xref: /illumos-gate/usr/src/lib/libsmbfs/smb/ntlm.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
1 /*
2  * Copyright (c) 2000-2001, Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: smb_crypt.c,v 1.13 2005/01/26 23:50:50 lindak Exp $
33  */
34 
35 /*
36  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
37  */
38 
39 /*
40  * NTLM support functions
41  *
42  * Some code from the driver: smb_smb.c, smb_crypt.c
43  */
44 
45 #include <sys/errno.h>
46 #include <sys/types.h>
47 #include <sys/md4.h>
48 #include <sys/md5.h>
49 
50 #include <ctype.h>
51 #include <stdlib.h>
52 #include <strings.h>
53 
54 #include <netsmb/smb_lib.h>
55 
56 #include "private.h"
57 #include "charsets.h"
58 #include "smb_crypt.h"
59 #include "ntlm.h"
60 
61 
62 /*
63  * ntlm_compute_lm_hash
64  *
65  * Compute an LM hash given a password
66  *
67  * Output:
68  *	hash: 16-byte "LanMan" (LM) hash.
69  * Inputs:
70  *	ucpw: User's password, upper-case UTF-8 string.
71  *
72  * Source: Implementing CIFS (Chris Hertel)
73  *
74  * P14 = UCPW padded to 14-bytes, or truncated (as needed)
75  * result = Encrypt(Key=P14, Data=MagicString)
76  */
77 int
78 ntlm_compute_lm_hash(uchar_t *hash, const char *pass)
79 {
80 	static const uchar_t M8[8] = "KGS!@#$%";
81 	uchar_t P14[14 + 1];
82 	int err;
83 	char *ucpw;
84 
85 	/* First, convert the p/w to upper case. */
86 	ucpw = utf8_str_toupper(pass);
87 	if (ucpw == NULL)
88 		return (ENOMEM);
89 
90 	/* Pad or truncate the upper-case P/W as needed. */
91 	bzero(P14, sizeof (P14));
92 	(void) strncpy((char *)P14, ucpw, 14);
93 
94 	/* Compute the hash. */
95 	err = smb_encrypt_DES(hash, NTLM_HASH_SZ,
96 	    P14, 14, M8, 8);
97 
98 	free(ucpw);
99 	return (err);
100 }
101 
102 /*
103  * ntlm_compute_nt_hash
104  *
105  * Compute an NT hash given a password in UTF-8.
106  *
107  * Output:
108  *	hash: 16-byte "NT" hash.
109  * Inputs:
110  *	upw: User's password, mixed-case UCS-2LE.
111  *	pwlen: Size (in bytes) of upw
112  */
113 int
114 ntlm_compute_nt_hash(uchar_t *hash, const char *pass)
115 {
116 	MD4_CTX ctx;
117 	uint16_t *unipw = NULL;
118 	int pwsz;
119 
120 	/* First, convert the password to unicode. */
121 	unipw = convert_utf8_to_leunicode(pass);
122 	if (unipw == NULL)
123 		return (ENOMEM);
124 	pwsz = unicode_strlen(unipw) << 1;
125 
126 	/* Compute the hash. */
127 	MD4Init(&ctx);
128 	MD4Update(&ctx, unipw, pwsz);
129 	MD4Final(hash, &ctx);
130 
131 	free(unipw);
132 	return (0);
133 }
134 
135 /*
136  * ntlm_v1_response
137  *
138  * Create an LM response from the given LM hash and challenge,
139  * or an NTLM repsonse from a given NTLM hash and challenge.
140  * Both response types are 24 bytes (NTLM_V1_RESP_SZ)
141  */
142 static int
143 ntlm_v1_response(uchar_t *resp,
144     const uchar_t *hash,
145     const uchar_t *chal, int clen)
146 {
147 	uchar_t S21[21];
148 	int err;
149 
150 	/*
151 	 * 14-byte LM Hash should be padded with 5 nul bytes to create
152 	 * a 21-byte string to be used in producing LM response
153 	 */
154 	bzero(&S21, sizeof (S21));
155 	bcopy(hash, S21, NTLM_HASH_SZ);
156 
157 	/* padded LM Hash -> LM Response */
158 	err = smb_encrypt_DES(resp, NTLM_V1_RESP_SZ,
159 	    S21, 21, chal, clen);
160 	return (err);
161 }
162 
163 /*
164  * Calculate an NTLMv1 session key (16 bytes).
165  */
166 static void
167 ntlm_v1_session_key(uchar_t *ssn_key, const uchar_t *nt_hash)
168 {
169 	MD4_CTX md4;
170 
171 	MD4Init(&md4);
172 	MD4Update(&md4, nt_hash, NTLM_HASH_SZ);
173 	MD4Final(ssn_key, &md4);
174 }
175 
176 /*
177  * Compute both the LM(v1) response and the NTLM(v1) response,
178  * and put them in the mbdata chains passed.  This allocates
179  * mbuf chains in the output args, which the caller frees.
180  */
181 int
182 ntlm_put_v1_responses(struct smb_ctx *ctx,
183 	struct mbdata *lm_mbp, struct mbdata *nt_mbp)
184 {
185 	uchar_t *lmresp, *ntresp;
186 	int err;
187 
188 	/* Get mbuf chain for the LM response. */
189 	if ((err = mb_init_sz(lm_mbp, NTLM_V1_RESP_SZ)) != 0)
190 		return (err);
191 
192 	/* Get mbuf chain for the NT response. */
193 	if ((err = mb_init_sz(nt_mbp, NTLM_V1_RESP_SZ)) != 0)
194 		return (err);
195 
196 	/*
197 	 * Compute the LM response, derived
198 	 * from the challenge and the ASCII
199 	 * password (if authflags allow).
200 	 */
201 	err = mb_fit(lm_mbp, NTLM_V1_RESP_SZ, (char **)&lmresp);
202 	if (err)
203 		return (err);
204 	bzero(lmresp, NTLM_V1_RESP_SZ);
205 	if (ctx->ct_authflags & SMB_AT_LM1) {
206 		/* They asked to send the LM hash too. */
207 		err = ntlm_v1_response(lmresp, ctx->ct_lmhash,
208 		    ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
209 		if (err)
210 			return (err);
211 	}
212 
213 	/*
214 	 * Compute the NTLM response, derived from
215 	 * the challenge and the NT hash.
216 	 */
217 	err = mb_fit(nt_mbp, NTLM_V1_RESP_SZ, (char **)&ntresp);
218 	if (err)
219 		return (err);
220 	bzero(ntresp, NTLM_V1_RESP_SZ);
221 	err = ntlm_v1_response(ntresp, ctx->ct_nthash,
222 	    ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
223 
224 	/*
225 	 * Compute the session key
226 	 */
227 	ntlm_v1_session_key(ctx->ct_ssn_key, ctx->ct_nthash);
228 
229 	return (err);
230 }
231 
232 /*
233  * A variation on HMAC-MD5 known as HMACT64 is used by Windows systems.
234  * The HMACT64() function is the same as the HMAC-MD5() except that
235  * it truncates the input key to 64 bytes rather than hashing it down
236  * to 16 bytes using the MD5() function.
237  *
238  * Output: digest (16-bytes)
239  */
240 static void
241 HMACT64(uchar_t *digest,
242     const uchar_t *key, size_t key_len,
243     const uchar_t *data, size_t data_len)
244 {
245 	MD5_CTX context;
246 	uchar_t k_ipad[64];	/* inner padding - key XORd with ipad */
247 	uchar_t k_opad[64];	/* outer padding - key XORd with opad */
248 	int i;
249 
250 	/* if key is longer than 64 bytes use only the first 64 bytes */
251 	if (key_len > 64)
252 		key_len = 64;
253 
254 	/*
255 	 * The HMAC-MD5 (and HMACT64) transform looks like:
256 	 *
257 	 * MD5(K XOR opad, MD5(K XOR ipad, data))
258 	 *
259 	 * where K is an n byte key
260 	 * ipad is the byte 0x36 repeated 64 times
261 	 * opad is the byte 0x5c repeated 64 times
262 	 * and data is the data being protected.
263 	 */
264 
265 	/* start out by storing key in pads */
266 	bzero(k_ipad, sizeof (k_ipad));
267 	bzero(k_opad, sizeof (k_opad));
268 	bcopy(key, k_ipad, key_len);
269 	bcopy(key, k_opad, key_len);
270 
271 	/* XOR key with ipad and opad values */
272 	for (i = 0; i < 64; i++) {
273 		k_ipad[i] ^= 0x36;
274 		k_opad[i] ^= 0x5c;
275 	}
276 
277 	/*
278 	 * perform inner MD5
279 	 */
280 	MD5Init(&context);			/* init context for 1st pass */
281 	MD5Update(&context, k_ipad, 64);	/* start with inner pad */
282 	MD5Update(&context, data, data_len);	/* then data of datagram */
283 	MD5Final(digest, &context);		/* finish up 1st pass */
284 
285 	/*
286 	 * perform outer MD5
287 	 */
288 	MD5Init(&context);			/* init context for 2nd pass */
289 	MD5Update(&context, k_opad, 64);	/* start with outer pad */
290 	MD5Update(&context, digest, 16);	/* then results of 1st hash */
291 	MD5Final(digest, &context);		/* finish up 2nd pass */
292 }
293 
294 
295 /*
296  * Compute an NTLMv2 hash given the NTLMv1 hash, the user name,
297  * and the destination (machine or domain name).
298  *
299  * Output:
300  *	v2hash: 16-byte NTLMv2 hash.
301  * Inputs:
302  *	v1hash: 16-byte NTLMv1 hash.
303  *	user: User name, UPPER-case UTF-8 string.
304  *	destination: Domain or server, MIXED-case UTF-8 string.
305  */
306 static int
307 ntlm_v2_hash(uchar_t *v2hash, const uchar_t *v1hash,
308     const char *user, const char *destination)
309 {
310 	int ulen, dlen;
311 	size_t ucs2len;
312 	uint16_t *ucs2data = NULL;
313 	char *utf8data = NULL;
314 	int err = ENOMEM;
315 
316 	/*
317 	 * v2hash = HMACT64(v1hash, 16, concat(upcase(user), dest))
318 	 * where "dest" is the domain or server name ("target name")
319 	 * Note: user name is converted to upper-case by the caller.
320 	 */
321 
322 	/* utf8data = concat(user, dest) */
323 	ulen = strlen(user);
324 	dlen = strlen(destination);
325 	utf8data = malloc(ulen + dlen + 1);
326 	if (utf8data == NULL)
327 		goto out;
328 	bcopy(user, utf8data, ulen);
329 	bcopy(destination, utf8data + ulen, dlen + 1);
330 
331 	/* Convert to UCS-2LE */
332 	ucs2data = convert_utf8_to_leunicode(utf8data);
333 	if (ucs2data == NULL)
334 		goto out;
335 	ucs2len = 2 * unicode_strlen(ucs2data);
336 
337 	HMACT64(v2hash, v1hash, NTLM_HASH_SZ,
338 	    (uchar_t *)ucs2data, ucs2len);
339 	err = 0;
340 out:
341 	if (ucs2data)
342 		free(ucs2data);
343 	if (utf8data)
344 		free(utf8data);
345 	return (err);
346 }
347 
348 /*
349  * Compute a partial LMv2 or NTLMv2 response (first 16-bytes).
350  * The full response is composed by the caller by
351  * appending the client_data to the returned hash.
352  *
353  * Output:
354  *	rhash: _partial_ LMv2/NTLMv2 response (first 16-bytes)
355  * Inputs:
356  *	v2hash: 16-byte NTLMv2 hash.
357  *	C8: Challenge from server (8 bytes)
358  *	client_data: client nonce (for LMv2) or the
359  *	  "blob" from ntlm_build_target_info (NTLMv2)
360  */
361 static int
362 ntlm_v2_resp_hash(uchar_t *rhash,
363     const uchar_t *v2hash, const uchar_t *C8,
364     const uchar_t *client_data, size_t cdlen)
365 {
366 	size_t dlen;
367 	uchar_t *data = NULL;
368 
369 	/* data = concat(C8, client_data) */
370 	dlen = 8 + cdlen;
371 	data = malloc(dlen);
372 	if (data == NULL)
373 		return (ENOMEM);
374 	bcopy(C8, data, 8);
375 	bcopy(client_data, data + 8, cdlen);
376 
377 	HMACT64(rhash, v2hash, NTLM_HASH_SZ, data, dlen);
378 
379 	free(data);
380 	return (0);
381 }
382 
383 /*
384  * Calculate an NTLMv2 session key (16 bytes).
385  */
386 static void
387 ntlm_v2_session_key(uchar_t *ssn_key,
388 	const uchar_t *v2hash,
389 	const uchar_t *ntresp)
390 {
391 
392 	/* session key uses only 1st 16 bytes of ntresp */
393 	HMACT64(ssn_key, v2hash, NTLM_HASH_SZ, ntresp, NTLM_HASH_SZ);
394 }
395 
396 
397 /*
398  * Compute both the LMv2 response and the NTLMv2 response,
399  * and put them in the mbdata chains passed.  This allocates
400  * mbuf chains in the output args, which the caller frees.
401  * Also computes the session key.
402  */
403 int
404 ntlm_put_v2_responses(struct smb_ctx *ctx, struct mbdata *ti_mbp,
405 	struct mbdata *lm_mbp, struct mbdata *nt_mbp)
406 {
407 	uchar_t *lmresp, *ntresp;
408 	int err;
409 	char *ucuser = NULL;	/* upper-case user name */
410 	uchar_t v2hash[NTLM_HASH_SZ];
411 	struct mbuf *tim = ti_mbp->mb_top;
412 
413 	if ((err = mb_init(lm_mbp)) != 0)
414 		return (err);
415 	if ((err = mb_init(nt_mbp)) != 0)
416 		return (err);
417 
418 	/*
419 	 * Convert the user name to upper-case, as
420 	 * that's what's used when computing LMv2
421 	 * and NTLMv2 responses.  Note that the
422 	 * domain name is NOT upper-cased!
423 	 */
424 	ucuser = utf8_str_toupper(ctx->ct_user);
425 	if (ucuser == NULL) {
426 		err = ENOMEM;
427 		goto out;
428 	}
429 
430 	/*
431 	 * Compute the NTLMv2 hash
432 	 */
433 	err = ntlm_v2_hash(v2hash, ctx->ct_nthash,
434 	    ucuser, ctx->ct_domain);
435 	if (err)
436 		goto out;
437 
438 	/*
439 	 * Compute the LMv2 response, derived from
440 	 * the v2hash, the server challenge, and
441 	 * the client nonce (random bits).
442 	 *
443 	 * We compose it from two parts:
444 	 *	1: 16-byte response hash
445 	 *	2: Client nonce
446 	 */
447 	lmresp = (uchar_t *)lm_mbp->mb_pos;
448 	mb_put_mem(lm_mbp, NULL, NTLM_HASH_SZ, MB_MSYSTEM);
449 	err = ntlm_v2_resp_hash(lmresp,
450 	    v2hash, ctx->ct_ntlm_chal,
451 	    ctx->ct_clnonce, NTLM_CHAL_SZ);
452 	if (err)
453 		goto out;
454 	mb_put_mem(lm_mbp, ctx->ct_clnonce, NTLM_CHAL_SZ, MB_MSYSTEM);
455 
456 	/*
457 	 * Compute the NTLMv2 response, derived
458 	 * from the server challenge and the
459 	 * "target info." blob passed in.
460 	 *
461 	 * Again composed from two parts:
462 	 *	1: 16-byte response hash
463 	 *	2: "target info." blob
464 	 */
465 	ntresp = (uchar_t *)nt_mbp->mb_pos;
466 	mb_put_mem(nt_mbp, NULL, NTLM_HASH_SZ, MB_MSYSTEM);
467 	err = ntlm_v2_resp_hash(ntresp,
468 	    v2hash, ctx->ct_ntlm_chal,
469 	    (uchar_t *)tim->m_data, tim->m_len);
470 	if (err)
471 		goto out;
472 	mb_put_mem(nt_mbp, tim->m_data, tim->m_len, MB_MSYSTEM);
473 
474 	/*
475 	 * Compute the session key
476 	 */
477 	ntlm_v2_session_key(ctx->ct_ssn_key, v2hash, ntresp);
478 
479 out:
480 	if (err) {
481 		mb_done(lm_mbp);
482 		mb_done(nt_mbp);
483 	}
484 	free(ucuser);
485 
486 	return (err);
487 }
488 
489 /*
490  * Helper for ntlm_build_target_info below.
491  * Put a name in the NTLMv2 "target info." blob.
492  */
493 static void
494 smb_put_blob_name(struct mbdata *mbp, char *name, int type)
495 {
496 	uint16_t *ucs = NULL;
497 	int nlen;
498 
499 	if (name)
500 		ucs = convert_utf8_to_leunicode(name);
501 	if (ucs)
502 		nlen = unicode_strlen(ucs);
503 	else
504 		nlen = 0;
505 
506 	nlen <<= 1;	/* length in bytes, without null. */
507 
508 	mb_put_uint16le(mbp, type);
509 	mb_put_uint16le(mbp, nlen);
510 	mb_put_mem(mbp, (char *)ucs, nlen, MB_MSYSTEM);
511 
512 	if (ucs)
513 		free(ucs);
514 }
515 
516 /*
517  * Build an NTLMv2 "target info." blob.  When called from NTLMSSP,
518  * the list of names comes from the Type 2 message.  Otherwise,
519  * we create the name list here.
520  */
521 int
522 ntlm_build_target_info(struct smb_ctx *ctx, struct mbuf *names,
523 	struct mbdata *mbp)
524 {
525 	struct timeval now;
526 	uint64_t nt_time;
527 
528 	char *ucdom = NULL;	/* user's domain */
529 	int err;
530 
531 	/* Get mbuf chain for the "target info". */
532 	if ((err = mb_init(mbp)) != 0)
533 		return (err);
534 
535 	/*
536 	 * Construct the client nonce by getting
537 	 * some random data from /dev/urandom
538 	 */
539 	err = smb_get_urandom(ctx->ct_clnonce, NTLM_CHAL_SZ);
540 	if (err)
541 		goto out;
542 
543 	/*
544 	 * Get the "NT time" for the target info header.
545 	 */
546 	(void) gettimeofday(&now, 0);
547 	smb_time_local2NT(&now, 0, &nt_time);
548 
549 	/*
550 	 * Build the "target info." block.
551 	 *
552 	 * Based on information at:
553 	 * http://davenport.sourceforge.net/ntlm.html#theNtlmv2Response
554 	 *
555 	 * First the fixed-size part.
556 	 */
557 	mb_put_uint32le(mbp, 0x101);	/* Blob signature */
558 	mb_put_uint32le(mbp, 0);		/* reserved */
559 	mb_put_uint64le(mbp, nt_time);	/* NT time stamp */
560 	mb_put_mem(mbp, ctx->ct_clnonce, NTLM_CHAL_SZ, MB_MSYSTEM);
561 	mb_put_uint32le(mbp, 0);		/* unknown */
562 
563 	/*
564 	 * Now put the list of names, either from the
565 	 * NTLMSSP Type 2 message or composed here.
566 	 */
567 	if (names) {
568 		err = mb_put_mem(mbp, names->m_data, names->m_len, MB_MSYSTEM);
569 	} else {
570 		/* Get upper-case names. */
571 		ucdom  = utf8_str_toupper(ctx->ct_domain);
572 		if (ucdom == NULL) {
573 			err = ENOMEM;
574 			goto out;
575 		}
576 		smb_put_blob_name(mbp, ucdom, NAMETYPE_DOMAIN_NB);
577 		smb_put_blob_name(mbp, NULL, NAMETYPE_EOL);
578 		/* OK, that's the whole "target info." blob! */
579 	}
580 	err = 0;
581 
582 out:
583 	free(ucdom);
584 	return (err);
585 }
586