ntlmauth.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /*
10  * Inspired by previous work by Andrew Doran <ad@interlude.eu.org>.
11  */
12 #include "squid.h"
13 
14 #include <cstring>
15 #include <ctime>
16 #include <random>
17 #if HAVE_STRINGS_H
18 #include <strings.h>
19 #endif
20 
21 #include "ntlmauth/ntlmauth.h"
22 #include "util.h" /* for base64-related stuff */
23 
24 /* ************************************************************************* */
25 /* DEBUG functions */
26 /* ************************************************************************* */
27 
29 void
30 ntlm_dump_ntlmssp_flags(uint32_t flags)
31 {
32  fprintf(stderr, "flags: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
33  (flags & NTLM_NEGOTIATE_UNICODE ? "Unicode " : ""),
34  (flags & NTLM_NEGOTIATE_ASCII ? "ASCII " : ""),
35  (flags & NTLM_NEGOTIATE_REQUEST_TARGET ? "ReqTgt " : ""),
36  (flags & NTLM_NEGOTIATE_REQUEST_SIGN ? "ReqSign " : ""),
37  (flags & NTLM_NEGOTIATE_REQUEST_SEAL ? "ReqSeal " : ""),
38  (flags & NTLM_NEGOTIATE_DATAGRAM_STYLE ? "Dgram " : ""),
39  (flags & NTLM_NEGOTIATE_USE_LM ? "UseLM " : ""),
40  (flags & NTLM_NEGOTIATE_USE_NETWARE ? "UseNW " : ""),
41  (flags & NTLM_NEGOTIATE_USE_NTLM ? "UseNTLM " : ""),
42  (flags & NTLM_NEGOTIATE_DOMAIN_SUPPLIED ? "HaveDomain " : ""),
43  (flags & NTLM_NEGOTIATE_WORKSTATION_SUPPLIED ? "HaveWKS " : ""),
44  (flags & NTLM_NEGOTIATE_THIS_IS_LOCAL_CALL ? "LocalCall " : ""),
45  (flags & NTLM_NEGOTIATE_ALWAYS_SIGN ? "AlwaysSign " : ""),
46  (flags & NTLM_CHALLENGE_TARGET_IS_DOMAIN ? "Tgt_is_domain" : ""),
47  (flags & NTLM_CHALLENGE_TARGET_IS_SERVER ? "Tgt_is_server " : ""),
48  (flags & NTLM_CHALLENGE_TARGET_IS_SHARE ? "Tgt_is_share " : ""),
49  (flags & NTLM_REQUEST_INIT_RESPONSE ? "Req_init_response " : ""),
50  (flags & NTLM_REQUEST_ACCEPT_RESPONSE ? "Req_accept_response " : ""),
51  (flags & NTLM_REQUEST_NON_NT_SESSION_KEY ? "Req_nonnt_sesskey " : "")
52  );
53 }
54 
55 /* ************************************************************************* */
56 /* Packet and Payload handling functions */
57 /* ************************************************************************* */
58 
67 ntlm_validate_packet(const ntlmhdr *hdr, const int32_t type)
68 {
69  /*
70  * Must be the correct security package and request type.
71  * The 8 bytes compared includes the ASCII 'NUL'.
72  */
73  if (memcmp(hdr->signature, "NTLMSSP", 8) != 0) {
74  fprintf(stderr, "ntlmCheckHeader: bad header signature\n");
75  return NtlmError::BlobError;
76  }
77  if (type == NTLM_ANY)
78  return NtlmError::None;
79 
80  if ((int32_t)le32toh(hdr->type) != type) {
81  /* don't report this error - it's ok as we do a if() around this function */
82  debug("ntlm_validate_packet: type is %d, wanted %d\n", le32toh(hdr->type), type);
84  }
85  return NtlmError::None;
86 }
87 
97 lstring
98 ntlm_fetch_string(const ntlmhdr *packet, const int32_t packet_size, const strhdr * str, const uint32_t flags)
99 {
100  static char buf[NTLM_MAX_FIELD_LENGTH];
101  lstring rv;
102  char *d;
103 
104  rv.str = nullptr;
105  rv.l = -1;
106 
107  int16_t l = le16toh(str->len);
108  int32_t o = le32toh(str->offset);
109  // debug("ntlm_fetch_string(plength=%d,l=%d,o=%d)\n",packet_size,l,o);
110 
111  if (l < 0 || l > NTLM_MAX_FIELD_LENGTH) {
112  debug("ntlm_fetch_string: insane string length (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
113  return rv;
114  }
115  else if (o <= 0 || o > packet_size) {
116  debug("ntlm_fetch_string: insane string offset (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
117  return rv;
118  }
119  else if (l > packet_size - o) {
120  debug("ntlm_fetch_string: truncated string data (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
121  return rv;
122  }
123 
124  rv.str = (char *)packet + o;
125  rv.l = 0;
126  if ((flags & NTLM_NEGOTIATE_ASCII) == 0) {
127  /* UNICODE string */
128  unsigned short *s = (unsigned short *)rv.str;
129  rv.str = d = buf;
130 
131  for (uint32_t len = (l>>1); len; ++s, --len) {
132  uint16_t c = le16toh(*s);
133  if (c > 254 || c == '\0') {
134  fprintf(stderr, "ntlmssp: bad unicode: %04x\n", c);
135  return rv;
136  }
137  *d = static_cast<char>(c&0xFF);
138  ++d;
139  ++rv.l;
140  }
141  } else {
142  /* ASCII/OEM string */
143  char *sc = rv.str;
144 
145  for (; l>=0; ++sc, --l) {
146  if (*sc == '\0' || !xisprint(*sc)) {
147  fprintf(stderr, "ntlmssp: bad ascii: %04x\n", *sc);
148  return rv;
149  }
150  ++rv.l;
151  }
152  }
153 
154  return rv;
155 }
156 
163 void
164 ntlm_add_to_payload(const ntlmhdr *packet_hdr,
165  char *payload,
166  int *payload_length,
167  strhdr * hdr,
168  const char *toadd,
169  const uint16_t toadd_length)
170 {
171  int l = (*payload_length);
172  memcpy(payload + l, toadd, toadd_length);
173 
174  hdr->len = htole16(toadd_length);
175  hdr->maxlen = htole16(toadd_length);
176  const off_t o = l + reinterpret_cast<const ntlmhdr *>(payload) - packet_hdr;
177  hdr->offset = htole32(o & 0xFFFFFFFF);
178  (*payload_length) += toadd_length;
179 }
180 
181 /* ************************************************************************* */
182 /* Negotiate Packet functions */
183 /* ************************************************************************* */
184 
185 // ?
186 
187 /* ************************************************************************* */
188 /* Challenge Packet functions */
189 /* ************************************************************************* */
190 
191 /*
192  * Generates a challenge request nonce.
193  */
194 void
195 ntlm_make_nonce(char *nonce)
196 {
197  static std::mt19937 mt(time(nullptr));
198  static std::uniform_int_distribution<uint8_t> dist;
199 
200  for (int i = 0; i < NTLM_NONCE_LEN; ++i)
201  nonce[i] = static_cast<char>(dist(mt) & 0xFF);
202 }
203 
208 void
210  const char *domain, const char *,
211  const char *challenge_nonce, const int challenge_nonce_len,
212  const uint32_t flags)
213 {
214  int pl = 0;
215  memset(ch, 0, sizeof(ntlm_challenge)); /* reset */
216  memcpy(ch->hdr.signature, "NTLMSSP", 8); /* set the signature */
217  ch->hdr.type = htole32(NTLM_CHALLENGE); /* this is a challenge */
218  if (domain != nullptr) {
219  // silently truncate the domain if it exceeds 2^16-1 bytes.
220  // NTLM packets normally expect 2^8 bytes of domain.
221  const uint16_t dlen = strlen(domain) & 0xFFFF;
222  ntlm_add_to_payload(&ch->hdr, ch->payload, &pl, &ch->target, domain, dlen);
223  }
224  ch->flags = htole32(flags);
225  ch->context_low = 0; /* check this out */
226  ch->context_high = 0;
227  memcpy(ch->challenge, challenge_nonce, challenge_nonce_len);
228 }
229 
230 /* ************************************************************************* */
231 /* Authenticate Packet functions */
232 /* ************************************************************************* */
233 
245 NtlmError
246 ntlm_unpack_auth(const ntlm_authenticate *auth, char *user, char *domain, const int32_t size)
247 {
248  lstring rv;
249 
251  fprintf(stderr, "ntlm_unpack_auth: header check fails\n");
253  }
254  debug("ntlm_unpack_auth: size of %d\n", size);
255  debug("ntlm_unpack_auth: flg %08x\n", auth->flags);
256  debug("ntlm_unpack_auth: lmr o(%d) l(%d)\n", le32toh(auth->lmresponse.offset), auth->lmresponse.len);
257  debug("ntlm_unpack_auth: ntr o(%d) l(%d)\n", le32toh(auth->ntresponse.offset), auth->ntresponse.len);
258  debug("ntlm_unpack_auth: dom o(%d) l(%d)\n", le32toh(auth->domain.offset), auth->domain.len);
259  debug("ntlm_unpack_auth: usr o(%d) l(%d)\n", le32toh(auth->user.offset), auth->user.len);
260  debug("ntlm_unpack_auth: wst o(%d) l(%d)\n", le32toh(auth->workstation.offset), auth->workstation.len);
261  debug("ntlm_unpack_auth: key o(%d) l(%d)\n", le32toh(auth->sessionkey.offset), auth->sessionkey.len);
262 
263  rv = ntlm_fetch_string(&auth->hdr, size, &auth->domain, auth->flags);
264  if (rv.l > 0) {
265  memcpy(domain, rv.str, rv.l);
266  domain[rv.l] = '\0';
267  debug("ntlm_unpack_auth: Domain '%s' (len=%d).\n", domain, rv.l);
268  }
269  if (rv.l >= size) {
270  debug("ntlm_unpack_auth: Domain length %d too big for %d byte packet.\n", rv.l, size);
271  return NtlmError::BlobError;
272  }
273 
274  rv = ntlm_fetch_string(&auth->hdr, size, &auth->user, auth->flags);
275  if (rv.l > 0) {
276  memcpy(user, rv.str, rv.l);
277  user[rv.l] = '\0';
278  debug("ntlm_unpack_auth: Username '%s' (len=%d).\n", user, rv.l);
279  } else
280  return NtlmError::LoginEror;
281 
282  return NtlmError::None;
283 }
284 
#define NTLM_NEGOTIATE_REQUEST_SIGN
Definition: ntlmauth.h:106
void ntlm_add_to_payload(const ntlmhdr *packet_hdr, char *payload, int *payload_length, strhdr *hdr, const char *toadd, const uint16_t toadd_length)
Definition: ntlmauth.cc:164
#define xisprint(x)
Definition: xis.h:22
void debug(const char *format,...)
Definition: debug.cc:19
char * str
Definition: ntlmauth.h:57
#define NTLM_NEGOTIATE_ALWAYS_SIGN
Definition: ntlmauth.h:115
char payload[256]
Definition: ntlmauth.h:145
#define NTLM_NEGOTIATE_DOMAIN_SUPPLIED
Definition: ntlmauth.h:112
#define NTLM_REQUEST_INIT_RESPONSE
Definition: ntlmauth.h:26
uint32_t context_high
Definition: ntlmauth.h:144
NtlmError
Definition: ntlmauth.h:31
#define le16toh(x)
#define htole16(x)
#define NTLM_NEGOTIATE_ASCII
Definition: ntlmauth.h:104
uint32_t flags
Definition: ntlmauth.h:141
#define NTLM_NEGOTIATE_WORKSTATION_SUPPLIED
Definition: ntlmauth.h:113
#define NTLM_NEGOTIATE_UNICODE
Definition: ntlmauth.h:103
#define NTLM_NEGOTIATE_USE_NTLM
Definition: ntlmauth.h:111
#define NTLM_CHALLENGE_TARGET_IS_SHARE
Definition: ntlmauth.h:135
uint32_t context_low
Definition: ntlmauth.h:143
int size
Definition: ModDevPoll.cc:69
#define htole32(x)
#define NTLM_NEGOTIATE_REQUEST_TARGET
Definition: ntlmauth.h:105
#define NTLM_AUTHENTICATE
Definition: ntlmauth.h:71
#define NTLM_NEGOTIATE_THIS_IS_LOCAL_CALL
Definition: ntlmauth.h:114
void ntlm_make_nonce(char *nonce)
Definition: ntlmauth.cc:195
u_char challenge[NTLM_NONCE_LEN]
Definition: ntlmauth.h:142
int16_t len
Definition: ntlmauth.h:49
void ntlm_dump_ntlmssp_flags(uint32_t flags)
Definition: ntlmauth.cc:30
int32_t l
Definition: ntlmauth.h:56
#define NTLM_NEGOTIATE_DATAGRAM_STYLE
Definition: ntlmauth.h:108
#define le32toh(x)
uint32_t flags
Definition: ntlmauth.h:177
#define NTLM_NEGOTIATE_USE_LM
Definition: ntlmauth.h:109
#define NTLM_CHALLENGE
Definition: ntlmauth.h:70
static int sc[16]
Definition: smbdes.c:121
strhdr workstation
Definition: ntlmauth.h:175
ntlmhdr hdr
Definition: ntlmauth.h:139
#define NTLM_CHALLENGE_TARGET_IS_SERVER
Definition: ntlmauth.h:134
#define NTLM_REQUEST_ACCEPT_RESPONSE
Definition: ntlmauth.h:27
strhdr target
Definition: ntlmauth.h:140
char signature[8]
Definition: ntlmauth.h:77
#define NTLM_MAX_FIELD_LENGTH
Definition: ntlmauth.h:18
int32_t type
Definition: ntlmauth.h:78
#define NTLM_ANY
Definition: ntlmauth.h:68
NtlmError ntlm_validate_packet(const ntlmhdr *hdr, const int32_t type)
Definition: ntlmauth.cc:67
NtlmError ntlm_unpack_auth(const ntlm_authenticate *auth, char *user, char *domain, const int32_t size)
Definition: ntlmauth.cc:246
int32_t offset
Definition: ntlmauth.h:51
#define NTLM_NEGOTIATE_REQUEST_SEAL
Definition: ntlmauth.h:107
lstring ntlm_fetch_string(const ntlmhdr *packet, const int32_t packet_size, const strhdr *str, const uint32_t flags)
Definition: ntlmauth.cc:98
int16_t maxlen
Definition: ntlmauth.h:50
void ntlm_make_challenge(ntlm_challenge *ch, const char *domain, const char *, const char *challenge_nonce, const int challenge_nonce_len, const uint32_t flags)
Definition: ntlmauth.cc:209
#define NTLM_CHALLENGE_TARGET_IS_DOMAIN
Definition: ntlmauth.h:133
#define NTLM_REQUEST_NON_NT_SESSION_KEY
Definition: ntlmauth.h:28
#define NTLM_NONCE_LEN
Definition: ntlmauth.h:130
#define NTLM_NEGOTIATE_USE_NETWARE
Definition: ntlmauth.h:110

 

Introduction

Documentation

Support

Miscellaneous