]>
Commit | Line | Data |
---|---|---|
94439e4e | 1 | /* |
262a0e14 | 2 | * $Id$ |
94439e4e | 3 | * |
75aa769b AJ |
4 | * AUTHOR: Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> |
5 | * AUTHOR: Guido Serassio: <guido.serassio@acmeconsulting.it> | |
6 | * AUTHOR: Amos Jeffries <squid3@treenet.co.nz> | |
7 | * | |
94439e4e | 8 | * * * * * * * * Legal stuff * * * * * * * |
9 | * | |
10 | * (C) 2000 Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it>, | |
5d146f7d | 11 | * inspired by previous work by Andrew Doran <ad@interlude.eu.org>. |
94439e4e | 12 | * This program is free software; you can redistribute it and/or modify |
13 | * it under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
26ac0430 | 16 | * |
94439e4e | 17 | * This program is distributed in the hope that it will be useful, |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
26ac0430 | 21 | * |
94439e4e | 22 | * You should have received a copy of the GNU General Public License |
23 | * along with this program; if not, write to the Free Software | |
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
25 | * | |
26 | */ | |
27 | ||
f7f3304a | 28 | #include "squid.h" |
94439e4e | 29 | |
32d002cb | 30 | #if HAVE_STRING_H |
cf17b739 | 31 | #include <string.h> |
32 | #endif | |
32d002cb | 33 | #if HAVE_STRINGS_H |
cf17b739 | 34 | #include <strings.h> |
35 | #endif | |
36 | ||
7c16470c | 37 | #include "ntlmauth/ntlmauth.h" |
d434f297 | 38 | #include "util.h" /* for base64-related stuff */ |
39 | ||
75aa769b AJ |
40 | /* ************************************************************************* */ |
41 | /* DEBUG functions */ | |
42 | /* ************************************************************************* */ | |
43 | ||
dac46b89 | 44 | /** Dumps NTLM flags to standard error for debugging purposes */ |
94439e4e | 45 | void |
09aabd84 | 46 | ntlm_dump_ntlmssp_flags(uint32_t flags) |
94439e4e | 47 | { |
48 | fprintf(stderr, "flags: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", | |
1dcf61eb AJ |
49 | (flags & NTLM_NEGOTIATE_UNICODE ? "Unicode " : ""), |
50 | (flags & NTLM_NEGOTIATE_ASCII ? "ASCII " : ""), | |
51 | (flags & NTLM_NEGOTIATE_REQUEST_TARGET ? "ReqTgt " : ""), | |
52 | (flags & NTLM_NEGOTIATE_REQUEST_SIGN ? "ReqSign " : ""), | |
53 | (flags & NTLM_NEGOTIATE_REQUEST_SEAL ? "ReqSeal " : ""), | |
54 | (flags & NTLM_NEGOTIATE_DATAGRAM_STYLE ? "Dgram " : ""), | |
55 | (flags & NTLM_NEGOTIATE_USE_LM ? "UseLM " : ""), | |
56 | (flags & NTLM_NEGOTIATE_USE_NETWARE ? "UseNW " : ""), | |
57 | (flags & NTLM_NEGOTIATE_USE_NTLM ? "UseNTLM " : ""), | |
58 | (flags & NTLM_NEGOTIATE_DOMAIN_SUPPLIED ? "HaveDomain " : ""), | |
59 | (flags & NTLM_NEGOTIATE_WORKSTATION_SUPPLIED ? "HaveWKS " : ""), | |
60 | (flags & NTLM_NEGOTIATE_THIS_IS_LOCAL_CALL ? "LocalCall " : ""), | |
61 | (flags & NTLM_NEGOTIATE_ALWAYS_SIGN ? "AlwaysSign " : ""), | |
62 | (flags & NTLM_CHALLENGE_TARGET_IS_DOMAIN ? "Tgt_is_domain" : ""), | |
63 | (flags & NTLM_CHALLENGE_TARGET_IS_SERVER ? "Tgt_is_server " : ""), | |
64 | (flags & NTLM_CHALLENGE_TARGET_IS_SHARE ? "Tgt_is_share " : ""), | |
65 | (flags & NTLM_REQUEST_INIT_RESPONSE ? "Req_init_response " : ""), | |
66 | (flags & NTLM_REQUEST_ACCEPT_RESPONSE ? "Req_accept_response " : ""), | |
67 | (flags & NTLM_REQUEST_NON_NT_SESSION_KEY ? "Req_nonnt_sesskey " : "") | |
26ac0430 | 68 | ); |
94439e4e | 69 | } |
75aa769b AJ |
70 | |
71 | /* ************************************************************************* */ | |
72 | /* Packet and Payload handling functions */ | |
73 | /* ************************************************************************* */ | |
74 | ||
75 | /** | |
1dcf61eb AJ |
76 | * Check the validity of a decoded NTLM packet. |
77 | * | |
78 | * \retval NTLM_ERR_NONE Packet is okay | |
79 | * \retval NTLM_ERR_BLOB Packet is not even an NTLMSSP packet at all. | |
80 | * \retval NTLM_ERR_PROTOCOL Packet is not the expected type. | |
75aa769b AJ |
81 | */ |
82 | int | |
b13b5d03 | 83 | ntlm_validate_packet(const ntlmhdr * hdr, const int32_t type) |
75aa769b AJ |
84 | { |
85 | /* | |
86 | * Must be the correct security package and request type. | |
87 | * The 8 bytes compared includes the ASCII 'NUL'. | |
88 | */ | |
89 | if (memcmp(hdr->signature, "NTLMSSP", 8) != 0) { | |
90 | fprintf(stderr, "ntlmCheckHeader: bad header signature\n"); | |
1dcf61eb | 91 | return NTLM_ERR_BLOB; |
75aa769b AJ |
92 | } |
93 | if (type == NTLM_ANY) | |
1dcf61eb | 94 | return NTLM_ERR_NONE; |
75aa769b | 95 | |
b13b5d03 | 96 | if ((int32_t)le32toh(hdr->type) != type) { |
75aa769b | 97 | /* don't report this error - it's ok as we do a if() around this function */ |
31e19066 | 98 | debug("ntlm_validate_packet: type is %d, wanted %d\n", le32toh(hdr->type), type); |
1dcf61eb | 99 | return NTLM_ERR_PROTOCOL; |
75aa769b | 100 | } |
1dcf61eb | 101 | return NTLM_ERR_NONE; |
75aa769b | 102 | } |
94439e4e | 103 | |
104 | #define lstring_zero(s) s.str=NULL; s.l=-1; | |
105 | ||
dac46b89 AJ |
106 | /** |
107 | * Fetches a string from the authentication packet. | |
75aa769b | 108 | * The lstring data-part may point to inside the packet itself or a temporary static buffer. |
94439e4e | 109 | * It's up to the user to memcpy() that if the value needs to |
dac46b89 AJ |
110 | * be used in any way that requires a tailing \0. (can check whether the |
111 | * value is there though, in that case lstring.length == -1). | |
75aa769b | 112 | * |
1dcf61eb | 113 | * String may be either ASCII or UNICODE depending on whether flags contains NTLM_NEGOTIATE_ASCII |
94439e4e | 114 | */ |
115 | lstring | |
09aabd84 | 116 | ntlm_fetch_string(const ntlmhdr *packet, const int32_t packet_size, const strhdr * str, const uint32_t flags) |
94439e4e | 117 | { |
118 | int16_t l; /* length */ | |
119 | int32_t o; /* offset */ | |
75aa769b | 120 | static char buf[NTLM_MAX_FIELD_LENGTH]; |
94439e4e | 121 | lstring rv; |
31e19066 | 122 | char *d; |
94439e4e | 123 | |
124 | lstring_zero(rv); | |
125 | ||
f9576890 | 126 | l = le16toh(str->len); |
127 | o = le32toh(str->offset); | |
31e19066 | 128 | // debug("ntlm_fetch_string(plength=%d,l=%d,o=%d)\n",packet_size,l,o); |
94439e4e | 129 | |
75aa769b | 130 | if (l < 0 || l > NTLM_MAX_FIELD_LENGTH || o + l > packet_size || o == 0) { |
31e19066 | 131 | debug("ntlm_fetch_string: insane data (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o); |
26ac0430 | 132 | return rv; |
94439e4e | 133 | } |
75aa769b | 134 | rv.str = (char *)packet + o; |
1dcf61eb | 135 | if ((flags & NTLM_NEGOTIATE_ASCII) == 0) { |
75aa769b | 136 | /* UNICODE string */ |
31e19066 | 137 | unsigned short *s = (unsigned short *)rv.str; |
75aa769b AJ |
138 | rv.str = d = buf; |
139 | ||
742a021b | 140 | for (l >>= 1; l; ++s, --l) { |
31e19066 | 141 | unsigned short c = le16toh(*s); |
75aa769b AJ |
142 | if (c > 254 || c == '\0') { |
143 | fprintf(stderr, "ntlmssp: bad unicode: %04x\n", c); | |
144 | return rv; | |
145 | } | |
146 | *d++ = c; | |
742a021b | 147 | ++rv.l; |
75aa769b AJ |
148 | } |
149 | } else { | |
150 | /* ASCII/OEM string */ | |
31e19066 | 151 | char *sc = rv.str; |
75aa769b | 152 | |
742a021b | 153 | for (; l>=0; ++sc, --l) { |
75aa769b AJ |
154 | if (*sc == '\0' || !xisprint(*sc)) { |
155 | fprintf(stderr, "ntlmssp: bad ascii: %04x\n", *sc); | |
156 | return rv; | |
157 | } | |
742a021b | 158 | ++rv.l; |
75aa769b AJ |
159 | } |
160 | } | |
94439e4e | 161 | |
162 | return rv; | |
163 | } | |
164 | ||
dac46b89 AJ |
165 | /** |
166 | * Adds something to the payload. The caller must guarrantee that | |
94439e4e | 167 | * there is enough space in the payload string to accommodate the |
168 | * added value. | |
169 | * payload_length and hdr will be modified as a side-effect. | |
94439e4e | 170 | */ |
171 | void | |
75aa769b AJ |
172 | ntlm_add_to_payload(const ntlmhdr *packet_hdr, |
173 | char *payload, | |
174 | int *payload_length, | |
175 | strhdr * hdr, | |
176 | const char *toadd, | |
177 | const int toadd_length) | |
94439e4e | 178 | { |
94439e4e | 179 | int l = (*payload_length); |
180 | memcpy(payload + l, toadd, toadd_length); | |
181 | ||
f9576890 | 182 | hdr->len = htole16(toadd_length); |
183 | hdr->maxlen = htole16(toadd_length); | |
75aa769b | 184 | hdr->offset = htole32(l + payload - (char*)packet_hdr); |
94439e4e | 185 | (*payload_length) += toadd_length; |
186 | } | |
187 | ||
188 | ||
75aa769b AJ |
189 | /* ************************************************************************* */ |
190 | /* Negotiate Packet functions */ | |
191 | /* ************************************************************************* */ | |
192 | ||
1dcf61eb | 193 | // ? |
75aa769b AJ |
194 | |
195 | ||
196 | /* ************************************************************************* */ | |
197 | /* Challenge Packet functions */ | |
198 | /* ************************************************************************* */ | |
199 | ||
632d8053 | 200 | /* |
75aa769b AJ |
201 | * Generates a challenge request nonce. The randomness of the 8 byte |
202 | * challenge strings can be guarenteed to be poor at best. | |
203 | */ | |
204 | void | |
205 | ntlm_make_nonce(char *nonce) | |
206 | { | |
207 | static unsigned hash; | |
208 | int i; | |
209 | int r = (int) rand(); | |
210 | r = (hash ^ r) + r; | |
211 | ||
742a021b | 212 | for (i = 0; i < NTLM_NONCE_LEN; ++i) { |
75aa769b AJ |
213 | nonce[i] = r; |
214 | r = (r >> 2) ^ r; | |
215 | } | |
216 | hash = r; | |
217 | } | |
218 | ||
75aa769b AJ |
219 | /** |
220 | * Prepares a challenge packet to be sent to the client | |
221 | * \note domain should be upper_case | |
222 | */ | |
223 | void | |
224 | ntlm_make_challenge(ntlm_challenge *ch, | |
225 | const char *domain, const char *domain_controller_UNUSED, | |
226 | const char *challenge_nonce, const int challenge_nonce_len, | |
09aabd84 | 227 | const uint32_t flags) |
94439e4e | 228 | { |
94439e4e | 229 | int pl = 0; |
75aa769b AJ |
230 | memset(ch, 0, sizeof(ntlm_challenge)); /* reset */ |
231 | memcpy(ch->hdr.signature, "NTLMSSP", 8); /* set the signature */ | |
232 | ch->hdr.type = htole32(NTLM_CHALLENGE); /* this is a challenge */ | |
233 | if (domain != NULL) { | |
234 | ntlm_add_to_payload(&ch->hdr, ch->payload, &pl, &ch->target, domain, strlen(domain)); | |
235 | } | |
236 | ch->flags = htole32(flags); | |
237 | ch->context_low = 0; /* check this out */ | |
238 | ch->context_high = 0; | |
239 | memcpy(ch->challenge, challenge_nonce, challenge_nonce_len); | |
240 | } | |
241 | ||
242 | /* ************************************************************************* */ | |
243 | /* Authenticate Packet functions */ | |
244 | /* ************************************************************************* */ | |
245 | ||
246 | /** | |
247 | * Unpack the strings in an NTLM authentication response from client. | |
248 | * The caller is responsible for initializing the user and domain buffers | |
249 | * this function will only insert data if the packet contains any. Otherwise | |
250 | * the buffers will be left untouched. | |
251 | * | |
1dcf61eb AJ |
252 | * \retval NTLM_ERR_NONE username present, maybe also domain. |
253 | * \retval NTLM_ERR_PROTOCOL packet type is not an authentication packet. | |
254 | * \retval NTLM_ERR_LOGON no username. | |
255 | * \retval NTLM_ERR_BLOB domain field is apparently larger than the packet. | |
75aa769b AJ |
256 | */ |
257 | int | |
258 | ntlm_unpack_auth(const ntlm_authenticate *auth, char *user, char *domain, const int32_t size) | |
259 | { | |
75aa769b AJ |
260 | lstring rv; |
261 | ||
262 | if (ntlm_validate_packet(&auth->hdr, NTLM_AUTHENTICATE)) { | |
31e19066 | 263 | fprintf(stderr, "ntlm_unpack_auth: header check fails\n"); |
1dcf61eb | 264 | return NTLM_ERR_PROTOCOL; |
75aa769b | 265 | } |
31e19066 AJ |
266 | debug("ntlm_unpack_auth: size of %d\n", size); |
267 | debug("ntlm_unpack_auth: flg %08x\n", auth->flags); | |
268 | debug("ntlm_unpack_auth: lmr o(%d) l(%d)\n", le32toh(auth->lmresponse.offset), auth->lmresponse.len); | |
269 | debug("ntlm_unpack_auth: ntr o(%d) l(%d)\n", le32toh(auth->ntresponse.offset), auth->ntresponse.len); | |
270 | debug("ntlm_unpack_auth: dom o(%d) l(%d)\n", le32toh(auth->domain.offset), auth->domain.len); | |
271 | debug("ntlm_unpack_auth: usr o(%d) l(%d)\n", le32toh(auth->user.offset), auth->user.len); | |
272 | debug("ntlm_unpack_auth: wst o(%d) l(%d)\n", le32toh(auth->workstation.offset), auth->workstation.len); | |
273 | debug("ntlm_unpack_auth: key o(%d) l(%d)\n", le32toh(auth->sessionkey.offset), auth->sessionkey.len); | |
75aa769b AJ |
274 | |
275 | rv = ntlm_fetch_string(&auth->hdr, size, &auth->domain, auth->flags); | |
276 | if (rv.l > 0) { | |
31e19066 | 277 | memcpy(domain, rv.str, rv.l); |
75aa769b | 278 | domain[rv.l] = '\0'; |
31e19066 | 279 | debug("ntlm_unpack_auth: Domain '%s' (len=%d).\n", domain, rv.l); |
75aa769b | 280 | } |
ea205436 AJ |
281 | if (rv.l >= size) { |
282 | debug("ntlm_unpack_auth: Domain length %d too big for %d byte packet.\n", rv.l , size); | |
1dcf61eb | 283 | return NTLM_ERR_BLOB; |
ea205436 | 284 | } |
75aa769b AJ |
285 | |
286 | rv = ntlm_fetch_string(&auth->hdr, size, &auth->user, auth->flags); | |
287 | if (rv.l > 0) { | |
31e19066 | 288 | memcpy(user, rv.str, rv.l); |
75aa769b | 289 | user[rv.l] = '\0'; |
31e19066 | 290 | debug("ntlm_unpack_auth: Username '%s' (len=%d).\n", user, rv.l); |
75aa769b | 291 | } else |
1dcf61eb | 292 | return NTLM_ERR_LOGON; |
75aa769b | 293 | |
1dcf61eb | 294 | return NTLM_ERR_NONE; |
94439e4e | 295 | } |