xref: /illumos-gate/usr/src/lib/libsmbfs/smb/ntlm.c (revision 1033d75249caf2f4829363492fba5ce7044cd11f)
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  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
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  * Given a password, compute the LM hash.
67  * a.k.a. ResponseKeyLM in [MS-NLMP]
68  *
69  * Output:
70  *	hash: 16-byte "LanMan" (LM) hash (normally ctx->ct_lmhash)
71  * Inputs:
72  *	ucpw: User's password, upper-case UTF-8 string.
73  *
74  * Source: Implementing CIFS (Chris Hertel)
75  *
76  * P14 = UCPW padded to 14-bytes, or truncated (as needed)
77  * result = Encrypt(Key=P14, Data=MagicString)
78  */
79 int
80 ntlm_compute_lm_hash(uchar_t *hash, const char *pass)
81 {
82 	static const uchar_t M8[8] = "KGS!@#$%";
83 	uchar_t P14[14 + 1];
84 	int err;
85 	char *ucpw;
86 
87 	/* First, convert the p/w to upper case. */
88 	ucpw = utf8_str_toupper(pass);
89 	if (ucpw == NULL)
90 		return (ENOMEM);
91 
92 	/* Pad or truncate the upper-case P/W as needed. */
93 	bzero(P14, sizeof (P14));
94 	(void) strncpy((char *)P14, ucpw, 14);
95 
96 	/* Compute the hash. */
97 	err = smb_encrypt_DES(hash, NTLM_HASH_SZ,
98 	    P14, 14, M8, 8);
99 
100 	free(ucpw);
101 	return (err);
102 }
103 
104 /*
105  * ntlm_compute_nt_hash
106  *
107  * Given a password, compute the NT hash.
108  * a.k.a. the ResponseKeyNT in [MS-NLMP]
109  *
110  * Output:
111  *	hash: 16-byte "NT" hash (normally ctx->ct_nthash)
112  * Inputs:
113  *	upw: User's password, mixed-case UCS-2LE.
114  *	pwlen: Size (in bytes) of upw
115  */
116 int
117 ntlm_compute_nt_hash(uchar_t *hash, const char *pass)
118 {
119 	MD4_CTX ctx;
120 	uint16_t *unipw = NULL;
121 	int pwsz;
122 
123 	/* First, convert the password to unicode. */
124 	unipw = convert_utf8_to_leunicode(pass);
125 	if (unipw == NULL)
126 		return (ENOMEM);
127 	pwsz = unicode_strlen(unipw) << 1;
128 
129 	/* Compute the hash. */
130 	MD4Init(&ctx);
131 	MD4Update(&ctx, unipw, pwsz);
132 	MD4Final(hash, &ctx);
133 
134 	free(unipw);
135 	return (0);
136 }
137 
138 /*
139  * ntlm_v1_response
140  * a.k.a. DESL() in [MS-NLMP]
141  *
142  * Create an LM response from the given LM hash and challenge,
143  * or an NTLM repsonse from a given NTLM hash and challenge.
144  * Both response types are 24 bytes (NTLM_V1_RESP_SZ)
145  */
146 static int
147 ntlm_v1_response(uchar_t *resp,
148     const uchar_t *hash,
149     const uchar_t *chal, int clen)
150 {
151 	uchar_t S21[21];
152 	int err;
153 
154 	/*
155 	 * 14-byte LM Hash should be padded with 5 nul bytes to create
156 	 * a 21-byte string to be used in producing LM response
157 	 */
158 	bzero(&S21, sizeof (S21));
159 	bcopy(hash, S21, NTLM_HASH_SZ);
160 
161 	/* padded LM Hash -> LM Response */
162 	err = smb_encrypt_DES(resp, NTLM_V1_RESP_SZ,
163 	    S21, 21, chal, clen);
164 	return (err);
165 }
166 
167 /*
168  * Calculate an NTLMv1 session key (16 bytes).
169  */
170 static void
171 ntlm_v1_session_key(uchar_t *ssn_key, const uchar_t *nt_hash)
172 {
173 	MD4_CTX md4;
174 
175 	MD4Init(&md4);
176 	MD4Update(&md4, nt_hash, NTLM_HASH_SZ);
177 	MD4Final(ssn_key, &md4);
178 }
179 
180 /*
181  * Compute both the LM(v1) response and the NTLM(v1) response,
182  * and put them in the mbdata chains passed.  This allocates
183  * mbuf chains in the output args, which the caller frees.
184  */
185 int
186 ntlm_put_v1_responses(struct smb_ctx *ctx,
187 	struct mbdata *lm_mbp, struct mbdata *nt_mbp)
188 {
189 	uchar_t *lmresp, *ntresp;
190 	int err;
191 
192 	/* Get mbuf chain for the LM response. */
193 	if ((err = mb_init_sz(lm_mbp, NTLM_V1_RESP_SZ)) != 0)
194 		return (err);
195 
196 	/* Get mbuf chain for the NT response. */
197 	if ((err = mb_init_sz(nt_mbp, NTLM_V1_RESP_SZ)) != 0)
198 		return (err);
199 
200 	/*
201 	 * Compute the NTLM response, derived from
202 	 * the challenge and the NT hash (a.k.a ResponseKeyNT)
203 	 */
204 	err = mb_fit(nt_mbp, NTLM_V1_RESP_SZ, (char **)&ntresp);
205 	if (err)
206 		return (err);
207 	bzero(ntresp, NTLM_V1_RESP_SZ);
208 	err = ntlm_v1_response(ntresp, ctx->ct_nthash,
209 	    ctx->ct_srv_chal, NTLM_CHAL_SZ);
210 
211 	/*
212 	 * Compute the LM response, derived from
213 	 * the challenge and the ASCII password.
214 	 * Per. [MS-NLMP 3.3.1] if NoLmResponse,
215 	 * send the NT response for both NT+LM.
216 	 */
217 	err = mb_fit(lm_mbp, NTLM_V1_RESP_SZ, (char **)&lmresp);
218 	if (err)
219 		return (err);
220 	memcpy(lmresp, ntresp, NTLM_V1_RESP_SZ);
221 	if (ctx->ct_authflags & SMB_AT_LM1) {
222 		/* They asked to send the LM hash too. */
223 		err = ntlm_v1_response(lmresp, ctx->ct_lmhash,
224 		    ctx->ct_srv_chal, NTLM_CHAL_SZ);
225 		if (err)
226 			return (err);
227 	}
228 
229 	/*
230 	 * Compute the session key
231 	 */
232 	ntlm_v1_session_key(ctx->ct_ssn_key, ctx->ct_nthash);
233 
234 	return (err);
235 }
236 
237 /*
238  * Compute both the LM(v1x) response and the NTLM(v1x) response,
239  * and put them in the mbdata chains passed.  "v1x" here refers to
240  * NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY used with NTLMSSP,
241  * also known by its shorter alias NTLMSSP_NEGOTIATE_NTLM2.
242  * [MS-NLMP 3.3.1]
243  *
244  * This allocates mbuf chains in the output args (caller frees).
245  */
246 int
247 ntlm_put_v1x_responses(struct smb_ctx *ctx,
248 	struct mbdata *lm_mbp, struct mbdata *nt_mbp)
249 {
250 	MD5_CTX context;
251 	uchar_t challenges[2 * NTLM_CHAL_SZ];
252 	uchar_t digest[NTLM_HASH_SZ];
253 	uchar_t *lmresp, *ntresp;
254 	int err;
255 
256 	/* Get mbuf chain for the LM response. */
257 	if ((err = mb_init_sz(lm_mbp, NTLM_V1_RESP_SZ)) != 0)
258 		return (err);
259 
260 	/* Get mbuf chain for the NT response. */
261 	if ((err = mb_init_sz(nt_mbp, NTLM_V1_RESP_SZ)) != 0)
262 		return (err);
263 
264 	/*
265 	 * challenges = ConcatenationOf(ServerChallenge, ClientChallenge)
266 	 */
267 	memcpy(challenges, ctx->ct_srv_chal, NTLM_CHAL_SZ);
268 	memcpy(challenges + NTLM_CHAL_SZ, ctx->ct_clnonce, NTLM_CHAL_SZ);
269 
270 	/*
271 	 * digest = MD5(challenges)
272 	 */
273 	MD5Init(&context);
274 	MD5Update(&context, challenges, sizeof (challenges));
275 	MD5Final(digest, &context);
276 
277 	/*
278 	 * Compute the NTLM response, derived from the
279 	 * NT hash (a.k.a ResponseKeyNT) and the first
280 	 * 8 bytes of the MD5 digest of the challenges.
281 	 */
282 	err = mb_fit(nt_mbp, NTLM_V1_RESP_SZ, (char **)&ntresp);
283 	if (err)
284 		return (err);
285 	bzero(ntresp, NTLM_V1_RESP_SZ);
286 	err = ntlm_v1_response(ntresp, ctx->ct_nthash,
287 	    digest, NTLM_CHAL_SZ);
288 
289 	/*
290 	 * With "Extended Session Security", the LM response
291 	 * is simply the client challenge (nonce) padded out.
292 	 */
293 	err = mb_fit(lm_mbp, NTLM_V1_RESP_SZ, (char **)&lmresp);
294 	if (err)
295 		return (err);
296 	bzero(lmresp, NTLM_V1_RESP_SZ);
297 	memcpy(lmresp, ctx->ct_clnonce, NTLM_CHAL_SZ);
298 
299 	/*
300 	 * Compute the session key
301 	 */
302 	ntlm_v1_session_key(ctx->ct_ssn_key, ctx->ct_nthash);
303 
304 	return (err);
305 }
306 
307 /*
308  * A variation on HMAC-MD5 known as HMACT64 is used by Windows systems.
309  * The HMACT64() function is the same as the HMAC-MD5() except that
310  * it truncates the input key to 64 bytes rather than hashing it down
311  * to 16 bytes using the MD5() function.
312  *
313  * Output: digest (16-bytes)
314  */
315 static void
316 HMACT64(uchar_t *digest,
317     const uchar_t *key, size_t key_len,
318     const uchar_t *data, size_t data_len)
319 {
320 	MD5_CTX context;
321 	uchar_t k_ipad[64];	/* inner padding - key XORd with ipad */
322 	uchar_t k_opad[64];	/* outer padding - key XORd with opad */
323 	int i;
324 
325 	/* if key is longer than 64 bytes use only the first 64 bytes */
326 	if (key_len > 64)
327 		key_len = 64;
328 
329 	/*
330 	 * The HMAC-MD5 (and HMACT64) transform looks like:
331 	 *
332 	 * MD5(K XOR opad, MD5(K XOR ipad, data))
333 	 *
334 	 * where K is an n byte key
335 	 * ipad is the byte 0x36 repeated 64 times
336 	 * opad is the byte 0x5c repeated 64 times
337 	 * and data is the data being protected.
338 	 */
339 
340 	/* start out by storing key in pads */
341 	bzero(k_ipad, sizeof (k_ipad));
342 	bzero(k_opad, sizeof (k_opad));
343 	bcopy(key, k_ipad, key_len);
344 	bcopy(key, k_opad, key_len);
345 
346 	/* XOR key with ipad and opad values */
347 	for (i = 0; i < 64; i++) {
348 		k_ipad[i] ^= 0x36;
349 		k_opad[i] ^= 0x5c;
350 	}
351 
352 	/*
353 	 * perform inner MD5
354 	 */
355 	MD5Init(&context);			/* init context for 1st pass */
356 	MD5Update(&context, k_ipad, 64);	/* start with inner pad */
357 	MD5Update(&context, data, data_len);	/* then data of datagram */
358 	MD5Final(digest, &context);		/* finish up 1st pass */
359 
360 	/*
361 	 * perform outer MD5
362 	 */
363 	MD5Init(&context);			/* init context for 2nd pass */
364 	MD5Update(&context, k_opad, 64);	/* start with outer pad */
365 	MD5Update(&context, digest, 16);	/* then results of 1st hash */
366 	MD5Final(digest, &context);		/* finish up 2nd pass */
367 }
368 
369 
370 /*
371  * Compute an NTLMv2 hash given the NTLMv1 hash, the user name,
372  * and the destination (machine or domain name).
373  *
374  * Output:
375  *	v2hash: 16-byte NTLMv2 hash.
376  * Inputs:
377  *	v1hash: 16-byte NTLMv1 hash.
378  *	user: User name, UPPER-case UTF-8 string.
379  *	destination: Domain or server, MIXED-case UTF-8 string.
380  */
381 static int
382 ntlm_v2_hash(uchar_t *v2hash, const uchar_t *v1hash,
383     const char *user, const char *destination)
384 {
385 	int ulen, dlen;
386 	size_t ucs2len;
387 	uint16_t *ucs2data = NULL;
388 	char *utf8data = NULL;
389 	int err = ENOMEM;
390 
391 	/*
392 	 * v2hash = HMACT64(v1hash, 16, concat(upcase(user), dest))
393 	 * where "dest" is the domain or server name ("target name")
394 	 * Note: user name is converted to upper-case by the caller.
395 	 */
396 
397 	/* utf8data = concat(user, dest) */
398 	ulen = strlen(user);
399 	dlen = strlen(destination);
400 	utf8data = malloc(ulen + dlen + 1);
401 	if (utf8data == NULL)
402 		goto out;
403 	bcopy(user, utf8data, ulen);
404 	bcopy(destination, utf8data + ulen, dlen + 1);
405 
406 	/* Convert to UCS-2LE */
407 	ucs2data = convert_utf8_to_leunicode(utf8data);
408 	if (ucs2data == NULL)
409 		goto out;
410 	ucs2len = 2 * unicode_strlen(ucs2data);
411 
412 	HMACT64(v2hash, v1hash, NTLM_HASH_SZ,
413 	    (uchar_t *)ucs2data, ucs2len);
414 	err = 0;
415 out:
416 	if (ucs2data)
417 		free(ucs2data);
418 	if (utf8data)
419 		free(utf8data);
420 	return (err);
421 }
422 
423 /*
424  * Compute a partial LMv2 or NTLMv2 response (first 16-bytes).
425  * The full response is composed by the caller by
426  * appending the client_data to the returned hash.
427  *
428  * Output:
429  *	rhash: _partial_ LMv2/NTLMv2 response (first 16-bytes)
430  * Inputs:
431  *	v2hash: 16-byte NTLMv2 hash.
432  *	C8: Challenge from server (8 bytes)
433  *	client_data: client nonce (for LMv2) or the
434  *	  "blob" from ntlm_build_target_info (NTLMv2)
435  */
436 static int
437 ntlm_v2_resp_hash(uchar_t *rhash,
438     const uchar_t *v2hash, const uchar_t *C8,
439     const uchar_t *client_data, size_t cdlen)
440 {
441 	size_t dlen;
442 	uchar_t *data = NULL;
443 
444 	/* data = concat(C8, client_data) */
445 	dlen = 8 + cdlen;
446 	data = malloc(dlen);
447 	if (data == NULL)
448 		return (ENOMEM);
449 	bcopy(C8, data, 8);
450 	bcopy(client_data, data + 8, cdlen);
451 
452 	HMACT64(rhash, v2hash, NTLM_HASH_SZ, data, dlen);
453 
454 	free(data);
455 	return (0);
456 }
457 
458 /*
459  * Calculate an NTLMv2 session key (16 bytes).
460  */
461 static void
462 ntlm_v2_session_key(uchar_t *ssn_key,
463 	const uchar_t *v2hash,
464 	const uchar_t *ntresp)
465 {
466 
467 	/* session key uses only 1st 16 bytes of ntresp */
468 	HMACT64(ssn_key, v2hash, NTLM_HASH_SZ, ntresp, NTLM_HASH_SZ);
469 }
470 
471 
472 /*
473  * Compute both the LMv2 response and the NTLMv2 response,
474  * and put them in the mbdata chains passed.  This allocates
475  * mbuf chains in the output args, which the caller frees.
476  * Also computes the session key.
477  */
478 int
479 ntlm_put_v2_responses(struct smb_ctx *ctx, struct mbdata *ti_mbp,
480 	struct mbdata *lm_mbp, struct mbdata *nt_mbp)
481 {
482 	uchar_t *lmresp, *ntresp;
483 	int err;
484 	char *ucuser = NULL;	/* upper-case user name */
485 	uchar_t v2hash[NTLM_HASH_SZ];
486 	struct mbuf *tim = ti_mbp->mb_top;
487 
488 	/*
489 	 * Convert the user name to upper-case, as
490 	 * that's what's used when computing LMv2
491 	 * and NTLMv2 responses.  Note that the
492 	 * domain name is NOT upper-cased!
493 	 */
494 	if (ctx->ct_user[0] == '\0')
495 		return (EINVAL);
496 	ucuser = utf8_str_toupper(ctx->ct_user);
497 	if (ucuser == NULL)
498 		return (ENOMEM);
499 
500 	if ((err = mb_init(lm_mbp)) != 0)
501 		goto out;
502 	if ((err = mb_init(nt_mbp)) != 0)
503 		goto out;
504 
505 	/*
506 	 * Compute the NTLMv2 hash
507 	 */
508 	err = ntlm_v2_hash(v2hash, ctx->ct_nthash,
509 	    ucuser, ctx->ct_domain);
510 	if (err)
511 		goto out;
512 
513 	/*
514 	 * Compute the LMv2 response, derived from
515 	 * the v2hash, the server challenge, and
516 	 * the client nonce (random bits).
517 	 *
518 	 * We compose it from two parts:
519 	 *	1: 16-byte response hash
520 	 *	2: Client nonce
521 	 */
522 	lmresp = mb_reserve(lm_mbp, NTLM_HASH_SZ);
523 	err = ntlm_v2_resp_hash(lmresp,
524 	    v2hash, ctx->ct_srv_chal,
525 	    ctx->ct_clnonce, NTLM_CHAL_SZ);
526 	if (err)
527 		goto out;
528 	mb_put_mem(lm_mbp, ctx->ct_clnonce, NTLM_CHAL_SZ, MB_MSYSTEM);
529 
530 	/*
531 	 * Compute the NTLMv2 response, derived
532 	 * from the server challenge and the
533 	 * "target info." blob passed in.
534 	 *
535 	 * Again composed from two parts:
536 	 *	1: 16-byte response hash
537 	 *	2: "target info." blob
538 	 */
539 	ntresp = mb_reserve(nt_mbp, NTLM_HASH_SZ);
540 	err = ntlm_v2_resp_hash(ntresp,
541 	    v2hash, ctx->ct_srv_chal,
542 	    (uchar_t *)tim->m_data, tim->m_len);
543 	if (err)
544 		goto out;
545 	mb_put_mem(nt_mbp, tim->m_data, tim->m_len, MB_MSYSTEM);
546 
547 	/*
548 	 * Compute the session key
549 	 */
550 	ntlm_v2_session_key(ctx->ct_ssn_key, v2hash, ntresp);
551 
552 out:
553 	if (err) {
554 		mb_done(lm_mbp);
555 		mb_done(nt_mbp);
556 	}
557 	free(ucuser);
558 
559 	return (err);
560 }
561 
562 /*
563  * Helper for ntlm_build_target_info below.
564  * Put a name in the NTLMv2 "target info." blob.
565  */
566 static void
567 smb_put_blob_name(struct mbdata *mbp, char *name, int type)
568 {
569 	uint16_t *ucs = NULL;
570 	int nlen;
571 
572 	if (name)
573 		ucs = convert_utf8_to_leunicode(name);
574 	if (ucs)
575 		nlen = unicode_strlen(ucs);
576 	else
577 		nlen = 0;
578 
579 	nlen <<= 1;	/* length in bytes, without null. */
580 
581 	mb_put_uint16le(mbp, type);
582 	mb_put_uint16le(mbp, nlen);
583 	mb_put_mem(mbp, (char *)ucs, nlen, MB_MSYSTEM);
584 
585 	if (ucs)
586 		free(ucs);
587 }
588 
589 /*
590  * Build an NTLMv2 "target info." blob.  When called from NTLMSSP,
591  * the list of names comes from the Type 2 message.  Otherwise,
592  * we create the name list here.
593  */
594 int
595 ntlm_build_target_info(struct smb_ctx *ctx, struct mbuf *names,
596 	struct mbdata *mbp)
597 {
598 	struct timeval now;
599 	uint64_t nt_time;
600 
601 	char *ucdom = NULL;	/* user's domain */
602 	int err;
603 
604 	/* Get mbuf chain for the "target info". */
605 	if ((err = mb_init(mbp)) != 0)
606 		return (err);
607 
608 	/*
609 	 * Get the "NT time" for the target info header.
610 	 */
611 	(void) gettimeofday(&now, 0);
612 	smb_time_local2NT(&now, 0, &nt_time);
613 
614 	/*
615 	 * Build the "target info." block.
616 	 *
617 	 * Based on information at:
618 	 * http://davenport.sourceforge.net/ntlm.html#theNtlmv2Response
619 	 *
620 	 * First the fixed-size part.
621 	 */
622 	mb_put_uint32le(mbp, 0x101);	/* Blob signature */
623 	mb_put_uint32le(mbp, 0);		/* reserved */
624 	mb_put_uint64le(mbp, nt_time);	/* NT time stamp */
625 	mb_put_mem(mbp, ctx->ct_clnonce, NTLM_CHAL_SZ, MB_MSYSTEM);
626 	mb_put_uint32le(mbp, 0);		/* unknown */
627 
628 	/*
629 	 * Now put the list of names, either from the
630 	 * NTLMSSP Type 2 message or composed here.
631 	 */
632 	if (names) {
633 		err = mb_put_mem(mbp, names->m_data, names->m_len, MB_MSYSTEM);
634 	} else {
635 		/* Get upper-case names. */
636 		ucdom  = utf8_str_toupper(ctx->ct_domain);
637 		if (ucdom == NULL) {
638 			err = ENOMEM;
639 			goto out;
640 		}
641 		smb_put_blob_name(mbp, ucdom, NAMETYPE_DOMAIN_NB);
642 		smb_put_blob_name(mbp, NULL, NAMETYPE_EOL);
643 		/* OK, that's the whole "target info." blob! */
644 	}
645 	err = 0;
646 
647 out:
648 	free(ucdom);
649 	return (err);
650 }
651 
652 /*
653  * Build the MAC key (for SMB signing)
654  */
655 int
656 ntlm_build_mac_key(struct smb_ctx *ctx, struct mbdata *ntresp_mbp)
657 {
658 	struct mbuf *m;
659 	size_t len;
660 	char *p;
661 
662 	/*
663 	 * MAC_key = concat(session_key, nt_response)
664 	 */
665 	m = ntresp_mbp->mb_top;
666 	len = NTLM_HASH_SZ + m->m_len;
667 	if ((p = malloc(len)) == NULL)
668 		return (ENOMEM);
669 	ctx->ct_mackeylen = len;
670 	ctx->ct_mackey = p;
671 	memcpy(p, ctx->ct_ssn_key, NTLM_HASH_SZ);
672 	memcpy(p + NTLM_HASH_SZ, m->m_data, m->m_len);
673 
674 	return (0);
675 }
676 
677 /*
678  * Helper for ntlmssp_put_type3 - Build the "key exchange key"
679  * used when we have both NTLM(v1) and NTLMSSP_NEGOTIATE_NTLM2.
680  * HMAC_MD5(SessionBaseKey, concat(ServerChallenge, LmResponse[0..7]))
681  */
682 void
683 ntlm2_kxkey(struct smb_ctx *ctx, struct mbdata *lm_mbp, uchar_t *kxkey)
684 {
685 	uchar_t data[NTLM_HASH_SZ];
686 	uchar_t *p = mtod(lm_mbp->mb_top, uchar_t *);
687 
688 	/* concat(ServerChallenge, LmResponse[0..7]) */
689 	memcpy(data, ctx->ct_srv_chal, NTLM_CHAL_SZ);
690 	memcpy(data + NTLM_CHAL_SZ, p, NTLM_CHAL_SZ);
691 
692 	/* HMAC_MD5(SessionBaseKey, concat(...)) */
693 	HMACT64(kxkey, ctx->ct_ssn_key, NTLM_HASH_SZ,
694 	    data, NTLM_HASH_SZ);
695 }
696