ntlmauth.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 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
29void
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
66int
67ntlm_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 NTLM_ERR_BLOB;
76 }
77 if (type == NTLM_ANY)
78 return NTLM_ERR_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);
83 return NTLM_ERR_PROTOCOL;
84 }
85 return NTLM_ERR_NONE;
86}
87
98ntlm_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 if (*sc == '\0' || !xisprint(*sc)) {
147 fprintf(stderr, "ntlmssp: bad ascii: %04x\n", *sc);
148 return rv;
149 }
150 ++rv.l;
151 }
152 }
153
154 return rv;
155}
156
163void
164ntlm_add_to_payload(const ntlmhdr *packet_hdr,
165 char *payload,
166 int *payload_length,
167 strhdr * hdr,
168 const char *toadd,
169 const uint16_t toadd_length)
170{
171 int l = (*payload_length);
172 memcpy(payload + l, toadd, toadd_length);
173
174 hdr->len = htole16(toadd_length);
175 hdr->maxlen = htole16(toadd_length);
176 const off_t o = l + reinterpret_cast<const ntlmhdr *>(payload) - packet_hdr;
177 hdr->offset = htole32(o & 0xFFFFFFFF);
178 (*payload_length) += toadd_length;
179}
180
181/* ************************************************************************* */
182/* Negotiate Packet functions */
183/* ************************************************************************* */
184
185// ?
186
187/* ************************************************************************* */
188/* Challenge Packet functions */
189/* ************************************************************************* */
190
191/*
192 * Generates a challenge request nonce.
193 */
194void
195ntlm_make_nonce(char *nonce)
196{
197 static std::mt19937 mt(time(nullptr));
198 static std::uniform_int_distribution<uint8_t> dist;
199
200 for (int i = 0; i < NTLM_NONCE_LEN; ++i)
201 nonce[i] = static_cast<char>(dist(mt) & 0xFF);
202}
203
208void
210 const char *domain, const char *,
211 const char *challenge_nonce, const int challenge_nonce_len,
212 const uint32_t flags)
213{
214 int pl = 0;
215 memset(ch, 0, sizeof(ntlm_challenge)); /* reset */
216 memcpy(ch->hdr.signature, "NTLMSSP", 8); /* set the signature */
217 ch->hdr.type = htole32(NTLM_CHALLENGE); /* this is a challenge */
218 if (domain != nullptr) {
219 // silently truncate the domain if it exceeds 2^16-1 bytes.
220 // NTLM packets normally expect 2^8 bytes of domain.
221 const uint16_t dlen = strlen(domain) & 0xFFFF;
222 ntlm_add_to_payload(&ch->hdr, ch->payload, &pl, &ch->target, domain, dlen);
223 }
224 ch->flags = htole32(flags);
225 ch->context_low = 0; /* check this out */
226 ch->context_high = 0;
227 memcpy(ch->challenge, challenge_nonce, challenge_nonce_len);
228}
229
230/* ************************************************************************* */
231/* Authenticate Packet functions */
232/* ************************************************************************* */
233
245int
246ntlm_unpack_auth(const ntlm_authenticate *auth, char *user, char *domain, const int32_t size)
247{
248 lstring rv;
249
251 fprintf(stderr, "ntlm_unpack_auth: header check fails\n");
252 return NTLM_ERR_PROTOCOL;
253 }
254 debug("ntlm_unpack_auth: size of %d\n", size);
255 debug("ntlm_unpack_auth: flg %08x\n", auth->flags);
256 debug("ntlm_unpack_auth: lmr o(%d) l(%d)\n", le32toh(auth->lmresponse.offset), auth->lmresponse.len);
257 debug("ntlm_unpack_auth: ntr o(%d) l(%d)\n", le32toh(auth->ntresponse.offset), auth->ntresponse.len);
258 debug("ntlm_unpack_auth: dom o(%d) l(%d)\n", le32toh(auth->domain.offset), auth->domain.len);
259 debug("ntlm_unpack_auth: usr o(%d) l(%d)\n", le32toh(auth->user.offset), auth->user.len);
260 debug("ntlm_unpack_auth: wst o(%d) l(%d)\n", le32toh(auth->workstation.offset), auth->workstation.len);
261 debug("ntlm_unpack_auth: key o(%d) l(%d)\n", le32toh(auth->sessionkey.offset), auth->sessionkey.len);
262
263 rv = ntlm_fetch_string(&auth->hdr, size, &auth->domain, auth->flags);
264 if (rv.l > 0) {
265 memcpy(domain, rv.str, rv.l);
266 domain[rv.l] = '\0';
267 debug("ntlm_unpack_auth: Domain '%s' (len=%d).\n", domain, rv.l);
268 }
269 if (rv.l >= size) {
270 debug("ntlm_unpack_auth: Domain length %d too big for %d byte packet.\n", rv.l, size);
271 return NTLM_ERR_BLOB;
272 }
273
274 rv = ntlm_fetch_string(&auth->hdr, size, &auth->user, auth->flags);
275 if (rv.l > 0) {
276 memcpy(user, rv.str, rv.l);
277 user[rv.l] = '\0';
278 debug("ntlm_unpack_auth: Username '%s' (len=%d).\n", user, rv.l);
279 } else
280 return NTLM_ERR_LOGON;
281
282 return NTLM_ERR_NONE;
283}
284
int size
Definition: ModDevPoll.cc:75
void debug(const char *format,...)
Definition: debug.cc:19
int ntlm_validate_packet(const ntlmhdr *hdr, const int32_t type)
Definition: ntlmauth.cc:67
int ntlm_unpack_auth(const ntlm_authenticate *auth, char *user, char *domain, const int32_t size)
Definition: ntlmauth.cc:246
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:209
void ntlm_dump_ntlmssp_flags(uint32_t flags)
Definition: ntlmauth.cc:30
lstring ntlm_fetch_string(const ntlmhdr *packet, const int32_t packet_size, const strhdr *str, const uint32_t flags)
Definition: ntlmauth.cc:98
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:164
void ntlm_make_nonce(char *nonce)
Definition: ntlmauth.cc:195
#define NTLM_REQUEST_ACCEPT_RESPONSE
Definition: ntlmauth.h:31
#define NTLM_MAX_FIELD_LENGTH
Definition: ntlmauth.h:22
#define NTLM_REQUEST_NON_NT_SESSION_KEY
Definition: ntlmauth.h:32
#define NTLM_NEGOTIATE_REQUEST_SEAL
Definition: ntlmauth.h:111
#define NTLM_AUTHENTICATE
Definition: ntlmauth.h:75
#define NTLM_NEGOTIATE_UNICODE
Definition: ntlmauth.h:107
#define NTLM_NEGOTIATE_DOMAIN_SUPPLIED
Definition: ntlmauth.h:116
#define NTLM_ERR_LOGON
Definition: ntlmauth.h:42
#define NTLM_NEGOTIATE_REQUEST_TARGET
Definition: ntlmauth.h:109
#define NTLM_ERR_NONE
Definition: ntlmauth.h:38
#define NTLM_NEGOTIATE_USE_NTLM
Definition: ntlmauth.h:115
#define NTLM_NEGOTIATE_USE_NETWARE
Definition: ntlmauth.h:114
#define NTLM_NEGOTIATE_REQUEST_SIGN
Definition: ntlmauth.h:110
#define NTLM_NEGOTIATE_DATAGRAM_STYLE
Definition: ntlmauth.h:112
#define NTLM_NEGOTIATE_USE_LM
Definition: ntlmauth.h:113
#define NTLM_CHALLENGE_TARGET_IS_SHARE
Definition: ntlmauth.h:139
#define NTLM_CHALLENGE
Definition: ntlmauth.h:74
#define NTLM_ERR_BLOB
Definition: ntlmauth.h:36
#define NTLM_NEGOTIATE_THIS_IS_LOCAL_CALL
Definition: ntlmauth.h:118
#define NTLM_ANY
Definition: ntlmauth.h:72
#define NTLM_NEGOTIATE_ASCII
Definition: ntlmauth.h:108
#define NTLM_CHALLENGE_TARGET_IS_SERVER
Definition: ntlmauth.h:138
#define NTLM_REQUEST_INIT_RESPONSE
Definition: ntlmauth.h:30
#define NTLM_NONCE_LEN
Definition: ntlmauth.h:134
#define NTLM_NEGOTIATE_WORKSTATION_SUPPLIED
Definition: ntlmauth.h:117
#define NTLM_CHALLENGE_TARGET_IS_DOMAIN
Definition: ntlmauth.h:137
#define NTLM_ERR_PROTOCOL
Definition: ntlmauth.h:41
#define NTLM_NEGOTIATE_ALWAYS_SIGN
Definition: ntlmauth.h:119
static int sc[16]
Definition: smbdes.c:121
char * str
Definition: ntlmauth.h:61
int32_t l
Definition: ntlmauth.h:60
uint32_t flags
Definition: ntlmauth.h:181
strhdr workstation
Definition: ntlmauth.h:179
u_char challenge[NTLM_NONCE_LEN]
Definition: ntlmauth.h:146
strhdr target
Definition: ntlmauth.h:144
uint32_t context_high
Definition: ntlmauth.h:148
ntlmhdr hdr
Definition: ntlmauth.h:143
uint32_t flags
Definition: ntlmauth.h:145
char payload[256]
Definition: ntlmauth.h:149
uint32_t context_low
Definition: ntlmauth.h:147
int32_t type
Definition: ntlmauth.h:82
char signature[8]
Definition: ntlmauth.h:81
int16_t maxlen
Definition: ntlmauth.h:54
int32_t offset
Definition: ntlmauth.h:55
int16_t len
Definition: ntlmauth.h:53
#define htole16(x)
#define le16toh(x)
#define htole32(x)
#define le32toh(x)
#define xisprint(x)
Definition: xis.h:22

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors