]> git.ipfire.org Git - thirdparty/squid.git/blob - lib/ntlmauth/ntlmauth.cc
SourceFormat Enforcement
[thirdparty/squid.git] / lib / ntlmauth / ntlmauth.cc
1 /*
2 * Copyright (C) 1996-2014 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 #if HAVE_STRINGS_H
16 #include <strings.h>
17 #endif
18
19 #include "ntlmauth/ntlmauth.h"
20 #include "util.h" /* for base64-related stuff */
21
22 /* ************************************************************************* */
23 /* DEBUG functions */
24 /* ************************************************************************* */
25
26 /** Dumps NTLM flags to standard error for debugging purposes */
27 void
28 ntlm_dump_ntlmssp_flags(uint32_t flags)
29 {
30 fprintf(stderr, "flags: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
31 (flags & NTLM_NEGOTIATE_UNICODE ? "Unicode " : ""),
32 (flags & NTLM_NEGOTIATE_ASCII ? "ASCII " : ""),
33 (flags & NTLM_NEGOTIATE_REQUEST_TARGET ? "ReqTgt " : ""),
34 (flags & NTLM_NEGOTIATE_REQUEST_SIGN ? "ReqSign " : ""),
35 (flags & NTLM_NEGOTIATE_REQUEST_SEAL ? "ReqSeal " : ""),
36 (flags & NTLM_NEGOTIATE_DATAGRAM_STYLE ? "Dgram " : ""),
37 (flags & NTLM_NEGOTIATE_USE_LM ? "UseLM " : ""),
38 (flags & NTLM_NEGOTIATE_USE_NETWARE ? "UseNW " : ""),
39 (flags & NTLM_NEGOTIATE_USE_NTLM ? "UseNTLM " : ""),
40 (flags & NTLM_NEGOTIATE_DOMAIN_SUPPLIED ? "HaveDomain " : ""),
41 (flags & NTLM_NEGOTIATE_WORKSTATION_SUPPLIED ? "HaveWKS " : ""),
42 (flags & NTLM_NEGOTIATE_THIS_IS_LOCAL_CALL ? "LocalCall " : ""),
43 (flags & NTLM_NEGOTIATE_ALWAYS_SIGN ? "AlwaysSign " : ""),
44 (flags & NTLM_CHALLENGE_TARGET_IS_DOMAIN ? "Tgt_is_domain" : ""),
45 (flags & NTLM_CHALLENGE_TARGET_IS_SERVER ? "Tgt_is_server " : ""),
46 (flags & NTLM_CHALLENGE_TARGET_IS_SHARE ? "Tgt_is_share " : ""),
47 (flags & NTLM_REQUEST_INIT_RESPONSE ? "Req_init_response " : ""),
48 (flags & NTLM_REQUEST_ACCEPT_RESPONSE ? "Req_accept_response " : ""),
49 (flags & NTLM_REQUEST_NON_NT_SESSION_KEY ? "Req_nonnt_sesskey " : "")
50 );
51 }
52
53 /* ************************************************************************* */
54 /* Packet and Payload handling functions */
55 /* ************************************************************************* */
56
57 /**
58 * Check the validity of a decoded NTLM packet.
59 *
60 * \retval NTLM_ERR_NONE Packet is okay
61 * \retval NTLM_ERR_BLOB Packet is not even an NTLMSSP packet at all.
62 * \retval NTLM_ERR_PROTOCOL Packet is not the expected type.
63 */
64 int
65 ntlm_validate_packet(const ntlmhdr * hdr, const int32_t type)
66 {
67 /*
68 * Must be the correct security package and request type.
69 * The 8 bytes compared includes the ASCII 'NUL'.
70 */
71 if (memcmp(hdr->signature, "NTLMSSP", 8) != 0) {
72 fprintf(stderr, "ntlmCheckHeader: bad header signature\n");
73 return NTLM_ERR_BLOB;
74 }
75 if (type == NTLM_ANY)
76 return NTLM_ERR_NONE;
77
78 if ((int32_t)le32toh(hdr->type) != type) {
79 /* don't report this error - it's ok as we do a if() around this function */
80 debug("ntlm_validate_packet: type is %d, wanted %d\n", le32toh(hdr->type), type);
81 return NTLM_ERR_PROTOCOL;
82 }
83 return NTLM_ERR_NONE;
84 }
85
86 /**
87 * Fetches a string from the authentication packet.
88 * The lstring data-part may point to inside the packet itself or a temporary static buffer.
89 * It's up to the user to memcpy() that if the value needs to
90 * be used in any way that requires a tailing \0. (can check whether the
91 * value is there though, in that case lstring.length == -1).
92 *
93 * String may be either ASCII or UNICODE depending on whether flags contains NTLM_NEGOTIATE_ASCII
94 */
95 lstring
96 ntlm_fetch_string(const ntlmhdr *packet, const int32_t packet_size, const strhdr * str, const uint32_t flags)
97 {
98 static char buf[NTLM_MAX_FIELD_LENGTH];
99 lstring rv;
100 char *d;
101
102 rv.str = NULL;
103 rv.l = -1;
104
105 int16_t l = le16toh(str->len);
106 int32_t o = le32toh(str->offset);
107 // debug("ntlm_fetch_string(plength=%d,l=%d,o=%d)\n",packet_size,l,o);
108
109 if (l < 0 || l > NTLM_MAX_FIELD_LENGTH || o + l > packet_size || o == 0) {
110 debug("ntlm_fetch_string: insane data (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
111 return rv;
112 }
113 rv.str = (char *)packet + o;
114 rv.l = 0;
115 if ((flags & NTLM_NEGOTIATE_ASCII) == 0) {
116 /* UNICODE string */
117 unsigned short *s = (unsigned short *)rv.str;
118 rv.str = d = buf;
119
120 for (uint32_t len = (l>>1); len; ++s, --len) {
121 uint16_t c = le16toh(*s);
122 if (c > 254 || c == '\0') {
123 fprintf(stderr, "ntlmssp: bad unicode: %04x\n", c);
124 return rv;
125 }
126 *d = static_cast<char>(c&0xFF);
127 ++d;
128 ++rv.l;
129 }
130 } else {
131 /* ASCII/OEM string */
132 char *sc = rv.str;
133
134 for (; l>=0; ++sc, --l) {
135 if (*sc == '\0' || !xisprint(*sc)) {
136 fprintf(stderr, "ntlmssp: bad ascii: %04x\n", *sc);
137 return rv;
138 }
139 ++rv.l;
140 }
141 }
142
143 return rv;
144 }
145
146 /**
147 * Adds something to the payload. The caller must guarrantee that
148 * there is enough space in the payload string to accommodate the
149 * added value.
150 * payload_length and hdr will be modified as a side-effect.
151 */
152 void
153 ntlm_add_to_payload(const ntlmhdr *packet_hdr,
154 char *payload,
155 int *payload_length,
156 strhdr * hdr,
157 const char *toadd,
158 const uint16_t toadd_length)
159 {
160 int l = (*payload_length);
161 memcpy(payload + l, toadd, toadd_length);
162
163 hdr->len = htole16(toadd_length);
164 hdr->maxlen = htole16(toadd_length);
165 const off_t o = l + reinterpret_cast<const ntlmhdr *>(payload) - packet_hdr;
166 hdr->offset = htole32(o & 0xFFFFFFFF);
167 (*payload_length) += toadd_length;
168 }
169
170 /* ************************************************************************* */
171 /* Negotiate Packet functions */
172 /* ************************************************************************* */
173
174 // ?
175
176 /* ************************************************************************* */
177 /* Challenge Packet functions */
178 /* ************************************************************************* */
179
180 /*
181 * Generates a challenge request nonce. The randomness of the 8 byte
182 * challenge strings can be guarenteed to be poor at best.
183 */
184 void
185 ntlm_make_nonce(char *nonce)
186 {
187 static unsigned hash;
188 uint32_t r = static_cast<uint32_t>(rand());
189 r = (hash ^ r) + r;
190
191 for (int i = 0; i < NTLM_NONCE_LEN; ++i) {
192 nonce[i] = static_cast<char>(r & 0xFF);
193 r = (r >> 2) ^ r;
194 }
195 hash = r;
196 }
197
198 /**
199 * Prepares a challenge packet to be sent to the client
200 * \note domain should be upper_case
201 */
202 void
203 ntlm_make_challenge(ntlm_challenge *ch,
204 const char *domain, const char *domain_controller_UNUSED,
205 const char *challenge_nonce, const int challenge_nonce_len,
206 const uint32_t flags)
207 {
208 int pl = 0;
209 memset(ch, 0, sizeof(ntlm_challenge)); /* reset */
210 memcpy(ch->hdr.signature, "NTLMSSP", 8); /* set the signature */
211 ch->hdr.type = htole32(NTLM_CHALLENGE); /* this is a challenge */
212 if (domain != NULL) {
213 // silently truncate the domain if it exceeds 2^16-1 bytes.
214 // NTLM packets normally expect 2^8 bytes of domain.
215 const uint16_t dlen = strlen(domain) & 0xFFFF;
216 ntlm_add_to_payload(&ch->hdr, ch->payload, &pl, &ch->target, domain, dlen);
217 }
218 ch->flags = htole32(flags);
219 ch->context_low = 0; /* check this out */
220 ch->context_high = 0;
221 memcpy(ch->challenge, challenge_nonce, challenge_nonce_len);
222 }
223
224 /* ************************************************************************* */
225 /* Authenticate Packet functions */
226 /* ************************************************************************* */
227
228 /**
229 * Unpack the strings in an NTLM authentication response from client.
230 * The caller is responsible for initializing the user and domain buffers
231 * this function will only insert data if the packet contains any. Otherwise
232 * the buffers will be left untouched.
233 *
234 * \retval NTLM_ERR_NONE username present, maybe also domain.
235 * \retval NTLM_ERR_PROTOCOL packet type is not an authentication packet.
236 * \retval NTLM_ERR_LOGON no username.
237 * \retval NTLM_ERR_BLOB domain field is apparently larger than the packet.
238 */
239 int
240 ntlm_unpack_auth(const ntlm_authenticate *auth, char *user, char *domain, const int32_t size)
241 {
242 lstring rv;
243
244 if (ntlm_validate_packet(&auth->hdr, NTLM_AUTHENTICATE)) {
245 fprintf(stderr, "ntlm_unpack_auth: header check fails\n");
246 return NTLM_ERR_PROTOCOL;
247 }
248 debug("ntlm_unpack_auth: size of %d\n", size);
249 debug("ntlm_unpack_auth: flg %08x\n", auth->flags);
250 debug("ntlm_unpack_auth: lmr o(%d) l(%d)\n", le32toh(auth->lmresponse.offset), auth->lmresponse.len);
251 debug("ntlm_unpack_auth: ntr o(%d) l(%d)\n", le32toh(auth->ntresponse.offset), auth->ntresponse.len);
252 debug("ntlm_unpack_auth: dom o(%d) l(%d)\n", le32toh(auth->domain.offset), auth->domain.len);
253 debug("ntlm_unpack_auth: usr o(%d) l(%d)\n", le32toh(auth->user.offset), auth->user.len);
254 debug("ntlm_unpack_auth: wst o(%d) l(%d)\n", le32toh(auth->workstation.offset), auth->workstation.len);
255 debug("ntlm_unpack_auth: key o(%d) l(%d)\n", le32toh(auth->sessionkey.offset), auth->sessionkey.len);
256
257 rv = ntlm_fetch_string(&auth->hdr, size, &auth->domain, auth->flags);
258 if (rv.l > 0) {
259 memcpy(domain, rv.str, rv.l);
260 domain[rv.l] = '\0';
261 debug("ntlm_unpack_auth: Domain '%s' (len=%d).\n", domain, rv.l);
262 }
263 if (rv.l >= size) {
264 debug("ntlm_unpack_auth: Domain length %d too big for %d byte packet.\n", rv.l , size);
265 return NTLM_ERR_BLOB;
266 }
267
268 rv = ntlm_fetch_string(&auth->hdr, size, &auth->user, auth->flags);
269 if (rv.l > 0) {
270 memcpy(user, rv.str, rv.l);
271 user[rv.l] = '\0';
272 debug("ntlm_unpack_auth: Username '%s' (len=%d).\n", user, rv.l);
273 } else
274 return NTLM_ERR_LOGON;
275
276 return NTLM_ERR_NONE;
277 }
278