ntlmauth.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2025 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  const auto c = static_cast<unsigned char>(*sc);
147  if (c == '\0' || !xisprint(c)) {
148  fprintf(stderr, "ntlmssp: bad ascii: %04x\n", c);
149  return rv;
150  }
151  ++rv.l;
152  }
153  }
154 
155  return rv;
156 }
157 
164 void
165 ntlm_add_to_payload(const ntlmhdr *packet_hdr,
166  char *payload,
167  int *payload_length,
168  strhdr * hdr,
169  const char *toadd,
170  const uint16_t toadd_length)
171 {
172  int l = (*payload_length);
173  memcpy(payload + l, toadd, toadd_length);
174 
175  hdr->len = htole16(toadd_length);
176  hdr->maxlen = htole16(toadd_length);
177  const off_t o = l + reinterpret_cast<const ntlmhdr *>(payload) - packet_hdr;
178  hdr->offset = htole32(o & 0xFFFFFFFF);
179  (*payload_length) += toadd_length;
180 }
181 
182 /* ************************************************************************* */
183 /* Negotiate Packet functions */
184 /* ************************************************************************* */
185 
186 // ?
187 
188 /* ************************************************************************* */
189 /* Challenge Packet functions */
190 /* ************************************************************************* */
191 
192 /*
193  * Generates a challenge request nonce.
194  */
195 void
196 ntlm_make_nonce(char *nonce)
197 {
198  static std::mt19937 mt(time(nullptr));
199  static std::uniform_int_distribution<uint8_t> dist;
200 
201  for (int i = 0; i < NTLM_NONCE_LEN; ++i)
202  nonce[i] = static_cast<char>(dist(mt) & 0xFF);
203 }
204 
209 void
211  const char *domain, const char *,
212  const char *challenge_nonce, const int challenge_nonce_len,
213  const uint32_t flags)
214 {
215  int pl = 0;
216  memset(ch, 0, sizeof(ntlm_challenge)); /* reset */
217  memcpy(ch->hdr.signature, "NTLMSSP", 8); /* set the signature */
218  ch->hdr.type = htole32(NTLM_CHALLENGE); /* this is a challenge */
219  if (domain != nullptr) {
220  // silently truncate the domain if it exceeds 2^16-1 bytes.
221  // NTLM packets normally expect 2^8 bytes of domain.
222  const uint16_t dlen = strlen(domain) & 0xFFFF;
223  ntlm_add_to_payload(&ch->hdr, ch->payload, &pl, &ch->target, domain, dlen);
224  }
225  ch->flags = htole32(flags);
226  ch->context_low = 0; /* check this out */
227  ch->context_high = 0;
228  memcpy(ch->challenge, challenge_nonce, challenge_nonce_len);
229 }
230 
231 /* ************************************************************************* */
232 /* Authenticate Packet functions */
233 /* ************************************************************************* */
234 
246 NtlmError
247 ntlm_unpack_auth(const ntlm_authenticate *auth, char *user, char *domain, const int32_t size)
248 {
249  lstring rv;
250 
252  fprintf(stderr, "ntlm_unpack_auth: header check fails\n");
254  }
255  debug("ntlm_unpack_auth: size of %d\n", size);
256  debug("ntlm_unpack_auth: flg %08x\n", le32toh(auth->flags));
257  debug("ntlm_unpack_auth: lmr o(%d) l(%d)\n", le32toh(auth->lmresponse.offset), auth->lmresponse.len);
258  debug("ntlm_unpack_auth: ntr o(%d) l(%d)\n", le32toh(auth->ntresponse.offset), auth->ntresponse.len);
259  debug("ntlm_unpack_auth: dom o(%d) l(%d)\n", le32toh(auth->domain.offset), auth->domain.len);
260  debug("ntlm_unpack_auth: usr o(%d) l(%d)\n", le32toh(auth->user.offset), auth->user.len);
261  debug("ntlm_unpack_auth: wst o(%d) l(%d)\n", le32toh(auth->workstation.offset), auth->workstation.len);
262  debug("ntlm_unpack_auth: key o(%d) l(%d)\n", le32toh(auth->sessionkey.offset), auth->sessionkey.len);
263 
264  rv = ntlm_fetch_string(&auth->hdr, size, &auth->domain, le32toh(auth->flags));
265  if (rv.l >= size) {
266  debug("ntlm_unpack_auth: Domain length %d too big for %d byte packet.\n", rv.l, size);
267  return NtlmError::BlobError;
268  }
269  if (rv.l > 0) {
270  const size_t dcopy = rv.l > (NTLM_MAX_FIELD_LENGTH - 1) ? (NTLM_MAX_FIELD_LENGTH - 1) : static_cast<size_t>(rv.l);
271  memcpy(domain, rv.str, dcopy);
272  domain[dcopy] = '\0';
273  debug("ntlm_unpack_auth: Domain '%s' (len=%zu).\n", domain, dcopy);
274  }
275 
276  rv = ntlm_fetch_string(&auth->hdr, size, &auth->user, le32toh(auth->flags));
277  if (rv.l <= 0)
278  return NtlmError::LoginEror;
279  if (rv.l >= size) {
280  debug("ntlm_unpack_auth: Username length %d too big for %d byte packet.\n", rv.l, size);
281  return NtlmError::BlobError;
282  }
283 
284  const size_t ucopy = rv.l > (NTLM_MAX_FIELD_LENGTH - 1) ? (NTLM_MAX_FIELD_LENGTH - 1) : static_cast<size_t>(rv.l);
285  memcpy(user, rv.str, ucopy);
286  user[ucopy] = '\0';
287  debug("ntlm_unpack_auth: Username '%s' (len=%zu).\n", user, ucopy);
288 
289  return NtlmError::None;
290 }
291 
#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:165
#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:70
#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:196
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
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:247
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:210
#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