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>
8 * * * * * * * * Legal stuff * * * * * * *
10 * (C) 2000 Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it>,
11 * inspired by previous work by Andrew Doran <ad@interlude.eu.org>.
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.
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.
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.
37 #include "ntlmauth/ntlmauth.h"
38 #include "util.h" /* for base64-related stuff */
40 /* ************************************************************************* */
42 /* ************************************************************************* */
44 /** Dumps NTLM flags to standard error for debugging purposes */
46 ntlm_dump_ntlmssp_flags(uint32_t flags
)
48 fprintf(stderr
, "flags: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
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 " : "")
71 /* ************************************************************************* */
72 /* Packet and Payload handling functions */
73 /* ************************************************************************* */
76 * Check the validity of a decoded NTLM packet.
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.
83 ntlm_validate_packet(const ntlmhdr
* hdr
, const int32_t type
)
86 * Must be the correct security package and request type.
87 * The 8 bytes compared includes the ASCII 'NUL'.
89 if (memcmp(hdr
->signature
, "NTLMSSP", 8) != 0) {
90 fprintf(stderr
, "ntlmCheckHeader: bad header signature\n");
96 if ((int32_t)le32toh(hdr
->type
) != type
) {
97 /* don't report this error - it's ok as we do a if() around this function */
98 debug("ntlm_validate_packet: type is %d, wanted %d\n", le32toh(hdr
->type
), type
);
99 return NTLM_ERR_PROTOCOL
;
101 return NTLM_ERR_NONE
;
104 #define lstring_zero(s) s.str=NULL; s.l=-1;
107 * Fetches a string from the authentication packet.
108 * The lstring data-part may point to inside the packet itself or a temporary static buffer.
109 * It's up to the user to memcpy() that if the value needs to
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).
113 * String may be either ASCII or UNICODE depending on whether flags contains NTLM_NEGOTIATE_ASCII
116 ntlm_fetch_string(const ntlmhdr
*packet
, const int32_t packet_size
, const strhdr
* str
, const uint32_t flags
)
118 int16_t l
; /* length */
119 int32_t o
; /* offset */
120 static char buf
[NTLM_MAX_FIELD_LENGTH
];
126 l
= le16toh(str
->len
);
127 o
= le32toh(str
->offset
);
128 // debug("ntlm_fetch_string(plength=%d,l=%d,o=%d)\n",packet_size,l,o);
130 if (l
< 0 || l
> NTLM_MAX_FIELD_LENGTH
|| o
+ l
> packet_size
|| o
== 0) {
131 debug("ntlm_fetch_string: insane data (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size
,l
,o
);
134 rv
.str
= (char *)packet
+ o
;
135 if ((flags
& NTLM_NEGOTIATE_ASCII
) == 0) {
137 unsigned short *s
= (unsigned short *)rv
.str
;
140 for (l
>>= 1; l
; ++s
, --l
) {
141 unsigned short c
= le16toh(*s
);
142 if (c
> 254 || c
== '\0') {
143 fprintf(stderr
, "ntlmssp: bad unicode: %04x\n", c
);
151 /* ASCII/OEM string */
154 for (; l
>=0; ++sc
, --l
) {
155 if (*sc
== '\0' || !xisprint(*sc
)) {
156 fprintf(stderr
, "ntlmssp: bad ascii: %04x\n", *sc
);
167 * Adds something to the payload. The caller must guarrantee that
168 * there is enough space in the payload string to accommodate the
170 * payload_length and hdr will be modified as a side-effect.
173 ntlm_add_to_payload(const ntlmhdr
*packet_hdr
,
178 const int toadd_length
)
180 int l
= (*payload_length
);
181 memcpy(payload
+ l
, toadd
, toadd_length
);
183 hdr
->len
= htole16(toadd_length
);
184 hdr
->maxlen
= htole16(toadd_length
);
185 hdr
->offset
= htole32(l
+ payload
- (char*)packet_hdr
);
186 (*payload_length
) += toadd_length
;
190 /* ************************************************************************* */
191 /* Negotiate Packet functions */
192 /* ************************************************************************* */
197 /* ************************************************************************* */
198 /* Challenge Packet functions */
199 /* ************************************************************************* */
202 * Generates a challenge request nonce. The randomness of the 8 byte
203 * challenge strings can be guarenteed to be poor at best.
206 ntlm_make_nonce(char *nonce
)
208 static unsigned hash
;
210 int r
= (int) rand();
213 for (i
= 0; i
< NTLM_NONCE_LEN
; ++i
) {
221 * Prepares a challenge packet to be sent to the client
222 * \note domain should be upper_case
225 ntlm_make_challenge(ntlm_challenge
*ch
,
226 const char *domain
, const char *domain_controller_UNUSED
,
227 const char *challenge_nonce
, const int challenge_nonce_len
,
228 const uint32_t flags
)
231 memset(ch
, 0, sizeof(ntlm_challenge
)); /* reset */
232 memcpy(ch
->hdr
.signature
, "NTLMSSP", 8); /* set the signature */
233 ch
->hdr
.type
= htole32(NTLM_CHALLENGE
); /* this is a challenge */
234 if (domain
!= NULL
) {
235 ntlm_add_to_payload(&ch
->hdr
, ch
->payload
, &pl
, &ch
->target
, domain
, strlen(domain
));
237 ch
->flags
= htole32(flags
);
238 ch
->context_low
= 0; /* check this out */
239 ch
->context_high
= 0;
240 memcpy(ch
->challenge
, challenge_nonce
, challenge_nonce_len
);
243 /* ************************************************************************* */
244 /* Authenticate Packet functions */
245 /* ************************************************************************* */
248 * Unpack the strings in an NTLM authentication response from client.
249 * The caller is responsible for initializing the user and domain buffers
250 * this function will only insert data if the packet contains any. Otherwise
251 * the buffers will be left untouched.
253 * \retval NTLM_ERR_NONE username present, maybe also domain.
254 * \retval NTLM_ERR_PROTOCOL packet type is not an authentication packet.
255 * \retval NTLM_ERR_LOGON no username.
256 * \retval NTLM_ERR_BLOB domain field is apparently larger than the packet.
259 ntlm_unpack_auth(const ntlm_authenticate
*auth
, char *user
, char *domain
, const int32_t size
)
263 if (ntlm_validate_packet(&auth
->hdr
, NTLM_AUTHENTICATE
)) {
264 fprintf(stderr
, "ntlm_unpack_auth: header check fails\n");
265 return NTLM_ERR_PROTOCOL
;
267 debug("ntlm_unpack_auth: size of %d\n", size
);
268 debug("ntlm_unpack_auth: flg %08x\n", auth
->flags
);
269 debug("ntlm_unpack_auth: lmr o(%d) l(%d)\n", le32toh(auth
->lmresponse
.offset
), auth
->lmresponse
.len
);
270 debug("ntlm_unpack_auth: ntr o(%d) l(%d)\n", le32toh(auth
->ntresponse
.offset
), auth
->ntresponse
.len
);
271 debug("ntlm_unpack_auth: dom o(%d) l(%d)\n", le32toh(auth
->domain
.offset
), auth
->domain
.len
);
272 debug("ntlm_unpack_auth: usr o(%d) l(%d)\n", le32toh(auth
->user
.offset
), auth
->user
.len
);
273 debug("ntlm_unpack_auth: wst o(%d) l(%d)\n", le32toh(auth
->workstation
.offset
), auth
->workstation
.len
);
274 debug("ntlm_unpack_auth: key o(%d) l(%d)\n", le32toh(auth
->sessionkey
.offset
), auth
->sessionkey
.len
);
276 rv
= ntlm_fetch_string(&auth
->hdr
, size
, &auth
->domain
, auth
->flags
);
278 memcpy(domain
, rv
.str
, rv
.l
);
280 debug("ntlm_unpack_auth: Domain '%s' (len=%d).\n", domain
, rv
.l
);
283 debug("ntlm_unpack_auth: Domain length %d too big for %d byte packet.\n", rv
.l
, size
);
284 return NTLM_ERR_BLOB
;
287 rv
= ntlm_fetch_string(&auth
->hdr
, size
, &auth
->user
, auth
->flags
);
289 memcpy(user
, rv
.str
, rv
.l
);
291 debug("ntlm_unpack_auth: Username '%s' (len=%d).\n", user
, rv
.l
);
293 return NTLM_ERR_LOGON
;
295 return NTLM_ERR_NONE
;