]> git.ipfire.org Git - thirdparty/squid.git/blame - lib/ntlmauth/ntlmauth.cc
Fix libntlmauth build on current Fedora Rawhide (#1077)
[thirdparty/squid.git] / lib / ntlmauth / ntlmauth.cc
CommitLineData
9c89cd13 1/*
bf95c10a 2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
9c89cd13
AJ
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
94439e4e 9/*
9c89cd13 10 * Inspired by previous work by Andrew Doran <ad@interlude.eu.org>.
94439e4e 11 */
f7f3304a 12#include "squid.h"
94439e4e 13
074d6a40 14#include <cstring>
9b7c14a5 15#include <ctime>
85b08f12 16#include <random>
32d002cb 17#if HAVE_STRINGS_H
cf17b739 18#include <strings.h>
19#endif
20
7c16470c 21#include "ntlmauth/ntlmauth.h"
f53969cc 22#include "util.h" /* for base64-related stuff */
d434f297 23
75aa769b
AJ
24/* ************************************************************************* */
25/* DEBUG functions */
26/* ************************************************************************* */
27
dac46b89 28/** Dumps NTLM flags to standard error for debugging purposes */
94439e4e 29void
09aabd84 30ntlm_dump_ntlmssp_flags(uint32_t flags)
94439e4e 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",
1dcf61eb
AJ
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 " : "")
26ac0430 52 );
94439e4e 53}
75aa769b
AJ
54
55/* ************************************************************************* */
56/* Packet and Payload handling functions */
57/* ************************************************************************* */
58
59/**
1dcf61eb
AJ
60 * Check the validity of a decoded NTLM packet.
61 *
62 * \retval NTLM_ERR_NONE Packet is okay
63 * \retval NTLM_ERR_BLOB Packet is not even an NTLMSSP packet at all.
64 * \retval NTLM_ERR_PROTOCOL Packet is not the expected type.
75aa769b
AJ
65 */
66int
b13b5d03 67ntlm_validate_packet(const ntlmhdr * hdr, const int32_t type)
75aa769b
AJ
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");
1dcf61eb 75 return NTLM_ERR_BLOB;
75aa769b
AJ
76 }
77 if (type == NTLM_ANY)
1dcf61eb 78 return NTLM_ERR_NONE;
75aa769b 79
b13b5d03 80 if ((int32_t)le32toh(hdr->type) != type) {
75aa769b 81 /* don't report this error - it's ok as we do a if() around this function */
31e19066 82 debug("ntlm_validate_packet: type is %d, wanted %d\n", le32toh(hdr->type), type);
1dcf61eb 83 return NTLM_ERR_PROTOCOL;
75aa769b 84 }
1dcf61eb 85 return NTLM_ERR_NONE;
75aa769b 86}
94439e4e 87
dac46b89
AJ
88/**
89 * Fetches a string from the authentication packet.
75aa769b 90 * The lstring data-part may point to inside the packet itself or a temporary static buffer.
94439e4e 91 * It's up to the user to memcpy() that if the value needs to
dac46b89
AJ
92 * be used in any way that requires a tailing \0. (can check whether the
93 * value is there though, in that case lstring.length == -1).
75aa769b 94 *
1dcf61eb 95 * String may be either ASCII or UNICODE depending on whether flags contains NTLM_NEGOTIATE_ASCII
94439e4e 96 */
97lstring
09aabd84 98ntlm_fetch_string(const ntlmhdr *packet, const int32_t packet_size, const strhdr * str, const uint32_t flags)
94439e4e 99{
75aa769b 100 static char buf[NTLM_MAX_FIELD_LENGTH];
94439e4e 101 lstring rv;
31e19066 102 char *d;
94439e4e 103
ec419c3a
EV
104 rv.str = NULL;
105 rv.l = -1;
94439e4e 106
9c78b0e0
AJ
107 int16_t l = le16toh(str->len);
108 int32_t o = le32toh(str->offset);
31e19066 109 // debug("ntlm_fetch_string(plength=%d,l=%d,o=%d)\n",packet_size,l,o);
94439e4e 110
75aa769b 111 if (l < 0 || l > NTLM_MAX_FIELD_LENGTH || o + l > packet_size || o == 0) {
31e19066 112 debug("ntlm_fetch_string: insane data (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
26ac0430 113 return rv;
94439e4e 114 }
75aa769b 115 rv.str = (char *)packet + o;
ec419c3a 116 rv.l = 0;
1dcf61eb 117 if ((flags & NTLM_NEGOTIATE_ASCII) == 0) {
75aa769b 118 /* UNICODE string */
31e19066 119 unsigned short *s = (unsigned short *)rv.str;
75aa769b
AJ
120 rv.str = d = buf;
121
9c78b0e0
AJ
122 for (uint32_t len = (l>>1); len; ++s, --len) {
123 uint16_t c = le16toh(*s);
75aa769b
AJ
124 if (c > 254 || c == '\0') {
125 fprintf(stderr, "ntlmssp: bad unicode: %04x\n", c);
126 return rv;
127 }
9c78b0e0 128 *d = static_cast<char>(c&0xFF);
f207fe64 129 ++d;
742a021b 130 ++rv.l;
75aa769b
AJ
131 }
132 } else {
133 /* ASCII/OEM string */
31e19066 134 char *sc = rv.str;
75aa769b 135
742a021b 136 for (; l>=0; ++sc, --l) {
75aa769b
AJ
137 if (*sc == '\0' || !xisprint(*sc)) {
138 fprintf(stderr, "ntlmssp: bad ascii: %04x\n", *sc);
139 return rv;
140 }
742a021b 141 ++rv.l;
75aa769b
AJ
142 }
143 }
94439e4e 144
145 return rv;
146}
147
dac46b89 148/**
2f8abb64 149 * Adds something to the payload. The caller must guarantee that
94439e4e 150 * there is enough space in the payload string to accommodate the
151 * added value.
152 * payload_length and hdr will be modified as a side-effect.
94439e4e 153 */
154void
75aa769b
AJ
155ntlm_add_to_payload(const ntlmhdr *packet_hdr,
156 char *payload,
157 int *payload_length,
158 strhdr * hdr,
159 const char *toadd,
9c78b0e0 160 const uint16_t toadd_length)
94439e4e 161{
94439e4e 162 int l = (*payload_length);
163 memcpy(payload + l, toadd, toadd_length);
164
f9576890 165 hdr->len = htole16(toadd_length);
166 hdr->maxlen = htole16(toadd_length);
9c78b0e0
AJ
167 const off_t o = l + reinterpret_cast<const ntlmhdr *>(payload) - packet_hdr;
168 hdr->offset = htole32(o & 0xFFFFFFFF);
94439e4e 169 (*payload_length) += toadd_length;
170}
171
75aa769b
AJ
172/* ************************************************************************* */
173/* Negotiate Packet functions */
174/* ************************************************************************* */
175
1dcf61eb 176// ?
75aa769b 177
75aa769b
AJ
178/* ************************************************************************* */
179/* Challenge Packet functions */
180/* ************************************************************************* */
181
632d8053 182/*
85b08f12 183 * Generates a challenge request nonce.
75aa769b
AJ
184 */
185void
186ntlm_make_nonce(char *nonce)
187{
85b08f12 188 static std::mt19937 mt(time(0));
8ed8fa40 189 static xuniform_int_distribution<uint8_t> dist;
75aa769b 190
85b08f12
AJ
191 for (int i = 0; i < NTLM_NONCE_LEN; ++i)
192 nonce[i] = static_cast<char>(dist(mt) & 0xFF);
75aa769b
AJ
193}
194
75aa769b
AJ
195/**
196 * Prepares a challenge packet to be sent to the client
197 * \note domain should be upper_case
198 */
199void
200ntlm_make_challenge(ntlm_challenge *ch,
ced8def3 201 const char *domain, const char *,
75aa769b 202 const char *challenge_nonce, const int challenge_nonce_len,
09aabd84 203 const uint32_t flags)
94439e4e 204{
94439e4e 205 int pl = 0;
f53969cc
SM
206 memset(ch, 0, sizeof(ntlm_challenge)); /* reset */
207 memcpy(ch->hdr.signature, "NTLMSSP", 8); /* set the signature */
208 ch->hdr.type = htole32(NTLM_CHALLENGE); /* this is a challenge */
75aa769b 209 if (domain != NULL) {
9c78b0e0
AJ
210 // silently truncate the domain if it exceeds 2^16-1 bytes.
211 // NTLM packets normally expect 2^8 bytes of domain.
212 const uint16_t dlen = strlen(domain) & 0xFFFF;
213 ntlm_add_to_payload(&ch->hdr, ch->payload, &pl, &ch->target, domain, dlen);
75aa769b
AJ
214 }
215 ch->flags = htole32(flags);
f53969cc 216 ch->context_low = 0; /* check this out */
75aa769b
AJ
217 ch->context_high = 0;
218 memcpy(ch->challenge, challenge_nonce, challenge_nonce_len);
219}
220
221/* ************************************************************************* */
222/* Authenticate Packet functions */
223/* ************************************************************************* */
224
225/**
226 * Unpack the strings in an NTLM authentication response from client.
227 * The caller is responsible for initializing the user and domain buffers
228 * this function will only insert data if the packet contains any. Otherwise
229 * the buffers will be left untouched.
230 *
f53969cc
SM
231 * \retval NTLM_ERR_NONE username present, maybe also domain.
232 * \retval NTLM_ERR_PROTOCOL packet type is not an authentication packet.
233 * \retval NTLM_ERR_LOGON no username.
234 * \retval NTLM_ERR_BLOB domain field is apparently larger than the packet.
75aa769b
AJ
235 */
236int
237ntlm_unpack_auth(const ntlm_authenticate *auth, char *user, char *domain, const int32_t size)
238{
75aa769b
AJ
239 lstring rv;
240
241 if (ntlm_validate_packet(&auth->hdr, NTLM_AUTHENTICATE)) {
31e19066 242 fprintf(stderr, "ntlm_unpack_auth: header check fails\n");
1dcf61eb 243 return NTLM_ERR_PROTOCOL;
75aa769b 244 }
31e19066
AJ
245 debug("ntlm_unpack_auth: size of %d\n", size);
246 debug("ntlm_unpack_auth: flg %08x\n", auth->flags);
247 debug("ntlm_unpack_auth: lmr o(%d) l(%d)\n", le32toh(auth->lmresponse.offset), auth->lmresponse.len);
248 debug("ntlm_unpack_auth: ntr o(%d) l(%d)\n", le32toh(auth->ntresponse.offset), auth->ntresponse.len);
249 debug("ntlm_unpack_auth: dom o(%d) l(%d)\n", le32toh(auth->domain.offset), auth->domain.len);
250 debug("ntlm_unpack_auth: usr o(%d) l(%d)\n", le32toh(auth->user.offset), auth->user.len);
251 debug("ntlm_unpack_auth: wst o(%d) l(%d)\n", le32toh(auth->workstation.offset), auth->workstation.len);
252 debug("ntlm_unpack_auth: key o(%d) l(%d)\n", le32toh(auth->sessionkey.offset), auth->sessionkey.len);
75aa769b
AJ
253
254 rv = ntlm_fetch_string(&auth->hdr, size, &auth->domain, auth->flags);
255 if (rv.l > 0) {
31e19066 256 memcpy(domain, rv.str, rv.l);
75aa769b 257 domain[rv.l] = '\0';
31e19066 258 debug("ntlm_unpack_auth: Domain '%s' (len=%d).\n", domain, rv.l);
75aa769b 259 }
ea205436 260 if (rv.l >= size) {
9e167fa2 261 debug("ntlm_unpack_auth: Domain length %d too big for %d byte packet.\n", rv.l, size);
1dcf61eb 262 return NTLM_ERR_BLOB;
ea205436 263 }
75aa769b
AJ
264
265 rv = ntlm_fetch_string(&auth->hdr, size, &auth->user, auth->flags);
266 if (rv.l > 0) {
31e19066 267 memcpy(user, rv.str, rv.l);
75aa769b 268 user[rv.l] = '\0';
31e19066 269 debug("ntlm_unpack_auth: Username '%s' (len=%d).\n", user, rv.l);
75aa769b 270 } else
1dcf61eb 271 return NTLM_ERR_LOGON;
75aa769b 272
1dcf61eb 273 return NTLM_ERR_NONE;
94439e4e 274}
f53969cc 275