]> git.ipfire.org Git - thirdparty/squid.git/blame - lib/ntlmauth/ntlmauth.cc
SourceFormat Enforcement
[thirdparty/squid.git] / lib / ntlmauth / ntlmauth.cc
CommitLineData
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 45void
09aabd84 46ntlm_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 */
82int
b13b5d03 83ntlm_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 */
115lstring
09aabd84 116ntlm_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 }
f207fe64
FC
146 *d = c;
147 ++d;
742a021b 148 ++rv.l;
75aa769b
AJ
149 }
150 } else {
151 /* ASCII/OEM string */
31e19066 152 char *sc = rv.str;
75aa769b 153
742a021b 154 for (; l>=0; ++sc, --l) {
75aa769b
AJ
155 if (*sc == '\0' || !xisprint(*sc)) {
156 fprintf(stderr, "ntlmssp: bad ascii: %04x\n", *sc);
157 return rv;
158 }
742a021b 159 ++rv.l;
75aa769b
AJ
160 }
161 }
94439e4e 162
163 return rv;
164}
165
dac46b89
AJ
166/**
167 * Adds something to the payload. The caller must guarrantee that
94439e4e 168 * there is enough space in the payload string to accommodate the
169 * added value.
170 * payload_length and hdr will be modified as a side-effect.
94439e4e 171 */
172void
75aa769b
AJ
173ntlm_add_to_payload(const ntlmhdr *packet_hdr,
174 char *payload,
175 int *payload_length,
176 strhdr * hdr,
177 const char *toadd,
178 const int toadd_length)
94439e4e 179{
94439e4e 180 int l = (*payload_length);
181 memcpy(payload + l, toadd, toadd_length);
182
f9576890 183 hdr->len = htole16(toadd_length);
184 hdr->maxlen = htole16(toadd_length);
75aa769b 185 hdr->offset = htole32(l + payload - (char*)packet_hdr);
94439e4e 186 (*payload_length) += toadd_length;
187}
188
75aa769b
AJ
189/* ************************************************************************* */
190/* Negotiate Packet functions */
191/* ************************************************************************* */
192
1dcf61eb 193// ?
75aa769b 194
75aa769b
AJ
195/* ************************************************************************* */
196/* Challenge Packet functions */
197/* ************************************************************************* */
198
632d8053 199/*
75aa769b
AJ
200 * Generates a challenge request nonce. The randomness of the 8 byte
201 * challenge strings can be guarenteed to be poor at best.
202 */
203void
204ntlm_make_nonce(char *nonce)
205{
206 static unsigned hash;
207 int i;
208 int r = (int) rand();
209 r = (hash ^ r) + r;
210
742a021b 211 for (i = 0; i < NTLM_NONCE_LEN; ++i) {
75aa769b
AJ
212 nonce[i] = r;
213 r = (r >> 2) ^ r;
214 }
215 hash = r;
216}
217
75aa769b
AJ
218/**
219 * Prepares a challenge packet to be sent to the client
220 * \note domain should be upper_case
221 */
222void
223ntlm_make_challenge(ntlm_challenge *ch,
224 const char *domain, const char *domain_controller_UNUSED,
225 const char *challenge_nonce, const int challenge_nonce_len,
09aabd84 226 const uint32_t flags)
94439e4e 227{
94439e4e 228 int pl = 0;
75aa769b
AJ
229 memset(ch, 0, sizeof(ntlm_challenge)); /* reset */
230 memcpy(ch->hdr.signature, "NTLMSSP", 8); /* set the signature */
231 ch->hdr.type = htole32(NTLM_CHALLENGE); /* this is a challenge */
232 if (domain != NULL) {
233 ntlm_add_to_payload(&ch->hdr, ch->payload, &pl, &ch->target, domain, strlen(domain));
234 }
235 ch->flags = htole32(flags);
236 ch->context_low = 0; /* check this out */
237 ch->context_high = 0;
238 memcpy(ch->challenge, challenge_nonce, challenge_nonce_len);
239}
240
241/* ************************************************************************* */
242/* Authenticate Packet functions */
243/* ************************************************************************* */
244
245/**
246 * Unpack the strings in an NTLM authentication response from client.
247 * The caller is responsible for initializing the user and domain buffers
248 * this function will only insert data if the packet contains any. Otherwise
249 * the buffers will be left untouched.
250 *
1dcf61eb
AJ
251 * \retval NTLM_ERR_NONE username present, maybe also domain.
252 * \retval NTLM_ERR_PROTOCOL packet type is not an authentication packet.
253 * \retval NTLM_ERR_LOGON no username.
254 * \retval NTLM_ERR_BLOB domain field is apparently larger than the packet.
75aa769b
AJ
255 */
256int
257ntlm_unpack_auth(const ntlm_authenticate *auth, char *user, char *domain, const int32_t size)
258{
75aa769b
AJ
259 lstring rv;
260
261 if (ntlm_validate_packet(&auth->hdr, NTLM_AUTHENTICATE)) {
31e19066 262 fprintf(stderr, "ntlm_unpack_auth: header check fails\n");
1dcf61eb 263 return NTLM_ERR_PROTOCOL;
75aa769b 264 }
31e19066
AJ
265 debug("ntlm_unpack_auth: size of %d\n", size);
266 debug("ntlm_unpack_auth: flg %08x\n", auth->flags);
267 debug("ntlm_unpack_auth: lmr o(%d) l(%d)\n", le32toh(auth->lmresponse.offset), auth->lmresponse.len);
268 debug("ntlm_unpack_auth: ntr o(%d) l(%d)\n", le32toh(auth->ntresponse.offset), auth->ntresponse.len);
269 debug("ntlm_unpack_auth: dom o(%d) l(%d)\n", le32toh(auth->domain.offset), auth->domain.len);
270 debug("ntlm_unpack_auth: usr o(%d) l(%d)\n", le32toh(auth->user.offset), auth->user.len);
271 debug("ntlm_unpack_auth: wst o(%d) l(%d)\n", le32toh(auth->workstation.offset), auth->workstation.len);
272 debug("ntlm_unpack_auth: key o(%d) l(%d)\n", le32toh(auth->sessionkey.offset), auth->sessionkey.len);
75aa769b
AJ
273
274 rv = ntlm_fetch_string(&auth->hdr, size, &auth->domain, auth->flags);
275 if (rv.l > 0) {
31e19066 276 memcpy(domain, rv.str, rv.l);
75aa769b 277 domain[rv.l] = '\0';
31e19066 278 debug("ntlm_unpack_auth: Domain '%s' (len=%d).\n", domain, rv.l);
75aa769b 279 }
ea205436
AJ
280 if (rv.l >= size) {
281 debug("ntlm_unpack_auth: Domain length %d too big for %d byte packet.\n", rv.l , size);
1dcf61eb 282 return NTLM_ERR_BLOB;
ea205436 283 }
75aa769b
AJ
284
285 rv = ntlm_fetch_string(&auth->hdr, size, &auth->user, auth->flags);
286 if (rv.l > 0) {
31e19066 287 memcpy(user, rv.str, rv.l);
75aa769b 288 user[rv.l] = '\0';
31e19066 289 debug("ntlm_unpack_auth: Username '%s' (len=%d).\n", user, rv.l);
75aa769b 290 } else
1dcf61eb 291 return NTLM_ERR_LOGON;
75aa769b 292
1dcf61eb 293 return NTLM_ERR_NONE;
94439e4e 294}