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