xref: /titanic_51/usr/src/lib/libsmbfs/smb/ntlm.c (revision 02d09e03eb27f3a2dc299de704e45dae5173f43f)
1613a2f6bSGordon Ross /*
2613a2f6bSGordon Ross  * Copyright (c) 2000-2001, Boris Popov
3613a2f6bSGordon Ross  * All rights reserved.
4613a2f6bSGordon Ross  *
5613a2f6bSGordon Ross  * Redistribution and use in source and binary forms, with or without
6613a2f6bSGordon Ross  * modification, are permitted provided that the following conditions
7613a2f6bSGordon Ross  * are met:
8613a2f6bSGordon Ross  * 1. Redistributions of source code must retain the above copyright
9613a2f6bSGordon Ross  *    notice, this list of conditions and the following disclaimer.
10613a2f6bSGordon Ross  * 2. Redistributions in binary form must reproduce the above copyright
11613a2f6bSGordon Ross  *    notice, this list of conditions and the following disclaimer in the
12613a2f6bSGordon Ross  *    documentation and/or other materials provided with the distribution.
13613a2f6bSGordon Ross  * 3. All advertising materials mentioning features or use of this software
14613a2f6bSGordon Ross  *    must display the following acknowledgement:
15613a2f6bSGordon Ross  *    This product includes software developed by Boris Popov.
16613a2f6bSGordon Ross  * 4. Neither the name of the author nor the names of any co-contributors
17613a2f6bSGordon Ross  *    may be used to endorse or promote products derived from this software
18613a2f6bSGordon Ross  *    without specific prior written permission.
19613a2f6bSGordon Ross  *
20613a2f6bSGordon Ross  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21613a2f6bSGordon Ross  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22613a2f6bSGordon Ross  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23613a2f6bSGordon Ross  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24613a2f6bSGordon Ross  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25613a2f6bSGordon Ross  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26613a2f6bSGordon Ross  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27613a2f6bSGordon Ross  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28613a2f6bSGordon Ross  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29613a2f6bSGordon Ross  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30613a2f6bSGordon Ross  * SUCH DAMAGE.
31613a2f6bSGordon Ross  *
32613a2f6bSGordon Ross  * $Id: smb_crypt.c,v 1.13 2005/01/26 23:50:50 lindak Exp $
33613a2f6bSGordon Ross  */
34613a2f6bSGordon Ross 
35613a2f6bSGordon Ross /*
36613a2f6bSGordon Ross  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
37613a2f6bSGordon Ross  * Use is subject to license terms.
38613a2f6bSGordon Ross  */
39613a2f6bSGordon Ross 
40613a2f6bSGordon Ross /*
41613a2f6bSGordon Ross  * NTLM support functions
42613a2f6bSGordon Ross  *
43613a2f6bSGordon Ross  * Some code from the driver: smb_smb.c, smb_crypt.c
44613a2f6bSGordon Ross  */
45613a2f6bSGordon Ross 
46613a2f6bSGordon Ross #include <sys/errno.h>
47613a2f6bSGordon Ross #include <sys/types.h>
48613a2f6bSGordon Ross #include <sys/md4.h>
49613a2f6bSGordon Ross #include <sys/md5.h>
50613a2f6bSGordon Ross 
51613a2f6bSGordon Ross #include <ctype.h>
52613a2f6bSGordon Ross #include <stdlib.h>
53613a2f6bSGordon Ross #include <strings.h>
54613a2f6bSGordon Ross 
55613a2f6bSGordon Ross #include <netsmb/smb_lib.h>
56613a2f6bSGordon Ross 
57613a2f6bSGordon Ross #include "private.h"
58613a2f6bSGordon Ross #include "charsets.h"
59613a2f6bSGordon Ross #include "smb_crypt.h"
60613a2f6bSGordon Ross #include "ntlm.h"
61613a2f6bSGordon Ross 
62613a2f6bSGordon Ross 
63613a2f6bSGordon Ross /*
64613a2f6bSGordon Ross  * ntlm_compute_lm_hash
65613a2f6bSGordon Ross  *
66613a2f6bSGordon Ross  * Compute an LM hash given a password
67613a2f6bSGordon Ross  *
68613a2f6bSGordon Ross  * Output:
69613a2f6bSGordon Ross  *	hash: 16-byte "LanMan" (LM) hash.
70613a2f6bSGordon Ross  * Inputs:
71613a2f6bSGordon Ross  *	ucpw: User's password, upper-case UTF-8 string.
72613a2f6bSGordon Ross  *
73613a2f6bSGordon Ross  * Source: Implementing CIFS (Chris Hertel)
74613a2f6bSGordon Ross  *
75613a2f6bSGordon Ross  * P14 = UCPW padded to 14-bytes, or truncated (as needed)
76613a2f6bSGordon Ross  * result = Encrypt(Key=P14, Data=MagicString)
77613a2f6bSGordon Ross  */
78613a2f6bSGordon Ross int
79613a2f6bSGordon Ross ntlm_compute_lm_hash(uchar_t *hash, const char *pass)
80613a2f6bSGordon Ross {
81613a2f6bSGordon Ross 	static const uchar_t M8[8] = "KGS!@#$%";
82613a2f6bSGordon Ross 	uchar_t P14[14 + 1];
83613a2f6bSGordon Ross 	int err;
84613a2f6bSGordon Ross 	char *ucpw;
85613a2f6bSGordon Ross 
86613a2f6bSGordon Ross 	/* First, convert the p/w to upper case. */
87613a2f6bSGordon Ross 	ucpw = utf8_str_toupper(pass);
88613a2f6bSGordon Ross 	if (ucpw == NULL)
89613a2f6bSGordon Ross 		return (ENOMEM);
90613a2f6bSGordon Ross 
91613a2f6bSGordon Ross 	/* Pad or truncate the upper-case P/W as needed. */
92613a2f6bSGordon Ross 	bzero(P14, sizeof (P14));
93613a2f6bSGordon Ross 	(void) strncpy((char *)P14, ucpw, 14);
94613a2f6bSGordon Ross 
95613a2f6bSGordon Ross 	/* Compute the hash. */
96613a2f6bSGordon Ross 	err = smb_encrypt_DES(hash, NTLM_HASH_SZ,
97613a2f6bSGordon Ross 	    P14, 14, M8, 8);
98613a2f6bSGordon Ross 
99613a2f6bSGordon Ross 	free(ucpw);
100613a2f6bSGordon Ross 	return (err);
101613a2f6bSGordon Ross }
102613a2f6bSGordon Ross 
103613a2f6bSGordon Ross /*
104613a2f6bSGordon Ross  * ntlm_compute_nt_hash
105613a2f6bSGordon Ross  *
106613a2f6bSGordon Ross  * Compute an NT hash given a password in UTF-8.
107613a2f6bSGordon Ross  *
108613a2f6bSGordon Ross  * Output:
109613a2f6bSGordon Ross  *	hash: 16-byte "NT" hash.
110613a2f6bSGordon Ross  * Inputs:
111613a2f6bSGordon Ross  *	upw: User's password, mixed-case UCS-2LE.
112613a2f6bSGordon Ross  *	pwlen: Size (in bytes) of upw
113613a2f6bSGordon Ross  */
114613a2f6bSGordon Ross int
115613a2f6bSGordon Ross ntlm_compute_nt_hash(uchar_t *hash, const char *pass)
116613a2f6bSGordon Ross {
117613a2f6bSGordon Ross 	MD4_CTX ctx;
118613a2f6bSGordon Ross 	uint16_t *unipw = NULL;
119613a2f6bSGordon Ross 	int pwsz;
120613a2f6bSGordon Ross 
121613a2f6bSGordon Ross 	/* First, convert the password to unicode. */
122613a2f6bSGordon Ross 	unipw = convert_utf8_to_leunicode(pass);
123613a2f6bSGordon Ross 	if (unipw == NULL)
124613a2f6bSGordon Ross 		return (ENOMEM);
125613a2f6bSGordon Ross 	pwsz = unicode_strlen(unipw) << 1;
126613a2f6bSGordon Ross 
127613a2f6bSGordon Ross 	/* Compute the hash. */
128613a2f6bSGordon Ross 	MD4Init(&ctx);
129613a2f6bSGordon Ross 	MD4Update(&ctx, unipw, pwsz);
130613a2f6bSGordon Ross 	MD4Final(hash, &ctx);
131613a2f6bSGordon Ross 
132613a2f6bSGordon Ross 	free(unipw);
133613a2f6bSGordon Ross 	return (0);
134613a2f6bSGordon Ross }
135613a2f6bSGordon Ross 
136613a2f6bSGordon Ross /*
137613a2f6bSGordon Ross  * ntlm_v1_response
138613a2f6bSGordon Ross  *
139613a2f6bSGordon Ross  * Create an LM response from the given LM hash and challenge,
140613a2f6bSGordon Ross  * or an NTLM repsonse from a given NTLM hash and challenge.
141613a2f6bSGordon Ross  * Both response types are 24 bytes (NTLM_V1_RESP_SZ)
142613a2f6bSGordon Ross  */
143613a2f6bSGordon Ross static int
144613a2f6bSGordon Ross ntlm_v1_response(uchar_t *resp,
145613a2f6bSGordon Ross     const uchar_t *hash,
146613a2f6bSGordon Ross     const uchar_t *chal, int clen)
147613a2f6bSGordon Ross {
148613a2f6bSGordon Ross 	uchar_t S21[21];
149613a2f6bSGordon Ross 	int err;
150613a2f6bSGordon Ross 
151613a2f6bSGordon Ross 	/*
152613a2f6bSGordon Ross 	 * 14-byte LM Hash should be padded with 5 nul bytes to create
153613a2f6bSGordon Ross 	 * a 21-byte string to be used in producing LM response
154613a2f6bSGordon Ross 	 */
155613a2f6bSGordon Ross 	bzero(&S21, sizeof (S21));
156613a2f6bSGordon Ross 	bcopy(hash, S21, NTLM_HASH_SZ);
157613a2f6bSGordon Ross 
158613a2f6bSGordon Ross 	/* padded LM Hash -> LM Response */
159613a2f6bSGordon Ross 	err = smb_encrypt_DES(resp, NTLM_V1_RESP_SZ,
160613a2f6bSGordon Ross 	    S21, 21, chal, clen);
161613a2f6bSGordon Ross 	return (err);
162613a2f6bSGordon Ross }
163613a2f6bSGordon Ross 
164613a2f6bSGordon Ross /*
165613a2f6bSGordon Ross  * Calculate an NTLMv1 session key (16 bytes).
166613a2f6bSGordon Ross  */
167613a2f6bSGordon Ross static void
168613a2f6bSGordon Ross ntlm_v1_session_key(uchar_t *ssn_key, const uchar_t *nt_hash)
169613a2f6bSGordon Ross {
170613a2f6bSGordon Ross 	MD4_CTX md4;
171613a2f6bSGordon Ross 
172613a2f6bSGordon Ross 	MD4Init(&md4);
173613a2f6bSGordon Ross 	MD4Update(&md4, nt_hash, NTLM_HASH_SZ);
174613a2f6bSGordon Ross 	MD4Final(ssn_key, &md4);
175613a2f6bSGordon Ross }
176613a2f6bSGordon Ross 
177613a2f6bSGordon Ross /*
178613a2f6bSGordon Ross  * Compute both the LM(v1) response and the NTLM(v1) response,
179613a2f6bSGordon Ross  * and put them in the mbdata chains passed.  This allocates
180613a2f6bSGordon Ross  * mbuf chains in the output args, which the caller frees.
181613a2f6bSGordon Ross  */
182613a2f6bSGordon Ross int
183613a2f6bSGordon Ross ntlm_put_v1_responses(struct smb_ctx *ctx,
184613a2f6bSGordon Ross 	struct mbdata *lm_mbp, struct mbdata *nt_mbp)
185613a2f6bSGordon Ross {
186613a2f6bSGordon Ross 	uchar_t *lmresp, *ntresp;
187613a2f6bSGordon Ross 	int err;
188613a2f6bSGordon Ross 
189613a2f6bSGordon Ross 	/* Get mbuf chain for the LM response. */
190*02d09e03SGordon Ross 	if ((err = mb_init_sz(lm_mbp, NTLM_V1_RESP_SZ)) != 0)
191613a2f6bSGordon Ross 		return (err);
192613a2f6bSGordon Ross 
193613a2f6bSGordon Ross 	/* Get mbuf chain for the NT response. */
194*02d09e03SGordon Ross 	if ((err = mb_init_sz(nt_mbp, NTLM_V1_RESP_SZ)) != 0)
195613a2f6bSGordon Ross 		return (err);
196613a2f6bSGordon Ross 
197613a2f6bSGordon Ross 	/*
198613a2f6bSGordon Ross 	 * Compute the LM response, derived
199613a2f6bSGordon Ross 	 * from the challenge and the ASCII
200613a2f6bSGordon Ross 	 * password (if authflags allow).
201613a2f6bSGordon Ross 	 */
202*02d09e03SGordon Ross 	err = mb_fit(lm_mbp, NTLM_V1_RESP_SZ, (char **)&lmresp);
203*02d09e03SGordon Ross 	if (err)
204*02d09e03SGordon Ross 		return (err);
205613a2f6bSGordon Ross 	bzero(lmresp, NTLM_V1_RESP_SZ);
206613a2f6bSGordon Ross 	if (ctx->ct_authflags & SMB_AT_LM1) {
207613a2f6bSGordon Ross 		/* They asked to send the LM hash too. */
208613a2f6bSGordon Ross 		err = ntlm_v1_response(lmresp, ctx->ct_lmhash,
209613a2f6bSGordon Ross 		    ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
210613a2f6bSGordon Ross 		if (err)
211613a2f6bSGordon Ross 			return (err);
212613a2f6bSGordon Ross 	}
213613a2f6bSGordon Ross 
214613a2f6bSGordon Ross 	/*
215613a2f6bSGordon Ross 	 * Compute the NTLM response, derived from
216613a2f6bSGordon Ross 	 * the challenge and the NT hash.
217613a2f6bSGordon Ross 	 */
218*02d09e03SGordon Ross 	err = mb_fit(nt_mbp, NTLM_V1_RESP_SZ, (char **)&ntresp);
219*02d09e03SGordon Ross 	if (err)
220*02d09e03SGordon Ross 		return (err);
221613a2f6bSGordon Ross 	bzero(ntresp, NTLM_V1_RESP_SZ);
222613a2f6bSGordon Ross 	err = ntlm_v1_response(ntresp, ctx->ct_nthash,
223613a2f6bSGordon Ross 	    ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
224613a2f6bSGordon Ross 
225613a2f6bSGordon Ross 	/*
226613a2f6bSGordon Ross 	 * Compute the session key
227613a2f6bSGordon Ross 	 */
228613a2f6bSGordon Ross 	ntlm_v1_session_key(ctx->ct_ssn_key, ctx->ct_nthash);
229613a2f6bSGordon Ross 
230613a2f6bSGordon Ross 	return (err);
231613a2f6bSGordon Ross }
232613a2f6bSGordon Ross 
233613a2f6bSGordon Ross /*
234613a2f6bSGordon Ross  * A variation on HMAC-MD5 known as HMACT64 is used by Windows systems.
235613a2f6bSGordon Ross  * The HMACT64() function is the same as the HMAC-MD5() except that
236613a2f6bSGordon Ross  * it truncates the input key to 64 bytes rather than hashing it down
237613a2f6bSGordon Ross  * to 16 bytes using the MD5() function.
238613a2f6bSGordon Ross  *
239613a2f6bSGordon Ross  * Output: digest (16-bytes)
240613a2f6bSGordon Ross  */
241613a2f6bSGordon Ross static void
242613a2f6bSGordon Ross HMACT64(uchar_t *digest,
243613a2f6bSGordon Ross     const uchar_t *key, size_t key_len,
244613a2f6bSGordon Ross     const uchar_t *data, size_t data_len)
245613a2f6bSGordon Ross {
246613a2f6bSGordon Ross 	MD5_CTX context;
247613a2f6bSGordon Ross 	uchar_t k_ipad[64];	/* inner padding - key XORd with ipad */
248613a2f6bSGordon Ross 	uchar_t k_opad[64];	/* outer padding - key XORd with opad */
249613a2f6bSGordon Ross 	int i;
250613a2f6bSGordon Ross 
251613a2f6bSGordon Ross 	/* if key is longer than 64 bytes use only the first 64 bytes */
252613a2f6bSGordon Ross 	if (key_len > 64)
253613a2f6bSGordon Ross 		key_len = 64;
254613a2f6bSGordon Ross 
255613a2f6bSGordon Ross 	/*
256613a2f6bSGordon Ross 	 * The HMAC-MD5 (and HMACT64) transform looks like:
257613a2f6bSGordon Ross 	 *
258613a2f6bSGordon Ross 	 * MD5(K XOR opad, MD5(K XOR ipad, data))
259613a2f6bSGordon Ross 	 *
260613a2f6bSGordon Ross 	 * where K is an n byte key
261613a2f6bSGordon Ross 	 * ipad is the byte 0x36 repeated 64 times
262613a2f6bSGordon Ross 	 * opad is the byte 0x5c repeated 64 times
263613a2f6bSGordon Ross 	 * and data is the data being protected.
264613a2f6bSGordon Ross 	 */
265613a2f6bSGordon Ross 
266613a2f6bSGordon Ross 	/* start out by storing key in pads */
267613a2f6bSGordon Ross 	bzero(k_ipad, sizeof (k_ipad));
268613a2f6bSGordon Ross 	bzero(k_opad, sizeof (k_opad));
269613a2f6bSGordon Ross 	bcopy(key, k_ipad, key_len);
270613a2f6bSGordon Ross 	bcopy(key, k_opad, key_len);
271613a2f6bSGordon Ross 
272613a2f6bSGordon Ross 	/* XOR key with ipad and opad values */
273613a2f6bSGordon Ross 	for (i = 0; i < 64; i++) {
274613a2f6bSGordon Ross 		k_ipad[i] ^= 0x36;
275613a2f6bSGordon Ross 		k_opad[i] ^= 0x5c;
276613a2f6bSGordon Ross 	}
277613a2f6bSGordon Ross 
278613a2f6bSGordon Ross 	/*
279613a2f6bSGordon Ross 	 * perform inner MD5
280613a2f6bSGordon Ross 	 */
281613a2f6bSGordon Ross 	MD5Init(&context);			/* init context for 1st pass */
282613a2f6bSGordon Ross 	MD5Update(&context, k_ipad, 64);	/* start with inner pad */
283613a2f6bSGordon Ross 	MD5Update(&context, data, data_len);	/* then data of datagram */
284613a2f6bSGordon Ross 	MD5Final(digest, &context);		/* finish up 1st pass */
285613a2f6bSGordon Ross 
286613a2f6bSGordon Ross 	/*
287613a2f6bSGordon Ross 	 * perform outer MD5
288613a2f6bSGordon Ross 	 */
289613a2f6bSGordon Ross 	MD5Init(&context);			/* init context for 2nd pass */
290613a2f6bSGordon Ross 	MD5Update(&context, k_opad, 64);	/* start with outer pad */
291613a2f6bSGordon Ross 	MD5Update(&context, digest, 16);	/* then results of 1st hash */
292613a2f6bSGordon Ross 	MD5Final(digest, &context);		/* finish up 2nd pass */
293613a2f6bSGordon Ross }
294613a2f6bSGordon Ross 
295613a2f6bSGordon Ross 
296613a2f6bSGordon Ross /*
297613a2f6bSGordon Ross  * Compute an NTLMv2 hash given the NTLMv1 hash, the user name,
298613a2f6bSGordon Ross  * and the destination (machine or domain name).
299613a2f6bSGordon Ross  *
300613a2f6bSGordon Ross  * Output:
301613a2f6bSGordon Ross  *	v2hash: 16-byte NTLMv2 hash.
302613a2f6bSGordon Ross  * Inputs:
303613a2f6bSGordon Ross  *	v1hash: 16-byte NTLMv1 hash.
304613a2f6bSGordon Ross  *	user: User name, UPPER-case UTF-8 string.
305613a2f6bSGordon Ross  *	destination: Domain or server, MIXED-case UTF-8 string.
306613a2f6bSGordon Ross  */
307613a2f6bSGordon Ross static int
308613a2f6bSGordon Ross ntlm_v2_hash(uchar_t *v2hash, const uchar_t *v1hash,
309613a2f6bSGordon Ross     const char *user, const char *destination)
310613a2f6bSGordon Ross {
311613a2f6bSGordon Ross 	int ulen, dlen;
312613a2f6bSGordon Ross 	size_t ucs2len;
313613a2f6bSGordon Ross 	uint16_t *ucs2data = NULL;
314613a2f6bSGordon Ross 	char *utf8data = NULL;
315613a2f6bSGordon Ross 	int err = ENOMEM;
316613a2f6bSGordon Ross 
317613a2f6bSGordon Ross 	/*
318613a2f6bSGordon Ross 	 * v2hash = HMACT64(v1hash, 16, concat(upcase(user), dest))
319613a2f6bSGordon Ross 	 * where "dest" is the domain or server name ("target name")
320613a2f6bSGordon Ross 	 * Note: user name is converted to upper-case by the caller.
321613a2f6bSGordon Ross 	 */
322613a2f6bSGordon Ross 
323613a2f6bSGordon Ross 	/* utf8data = concat(user, dest) */
324613a2f6bSGordon Ross 	ulen = strlen(user);
325613a2f6bSGordon Ross 	dlen = strlen(destination);
326613a2f6bSGordon Ross 	utf8data = malloc(ulen + dlen + 1);
327613a2f6bSGordon Ross 	if (utf8data == NULL)
328613a2f6bSGordon Ross 		goto out;
329613a2f6bSGordon Ross 	bcopy(user, utf8data, ulen);
330613a2f6bSGordon Ross 	bcopy(destination, utf8data + ulen, dlen + 1);
331613a2f6bSGordon Ross 
332613a2f6bSGordon Ross 	/* Convert to UCS-2LE */
333613a2f6bSGordon Ross 	ucs2data = convert_utf8_to_leunicode(utf8data);
334613a2f6bSGordon Ross 	if (ucs2data == NULL)
335613a2f6bSGordon Ross 		goto out;
336613a2f6bSGordon Ross 	ucs2len = 2 * unicode_strlen(ucs2data);
337613a2f6bSGordon Ross 
338613a2f6bSGordon Ross 	HMACT64(v2hash, v1hash, NTLM_HASH_SZ,
339613a2f6bSGordon Ross 	    (uchar_t *)ucs2data, ucs2len);
340613a2f6bSGordon Ross 	err = 0;
341613a2f6bSGordon Ross out:
342613a2f6bSGordon Ross 	if (ucs2data)
343613a2f6bSGordon Ross 		free(ucs2data);
344613a2f6bSGordon Ross 	if (utf8data)
345613a2f6bSGordon Ross 		free(utf8data);
346613a2f6bSGordon Ross 	return (err);
347613a2f6bSGordon Ross }
348613a2f6bSGordon Ross 
349613a2f6bSGordon Ross /*
350613a2f6bSGordon Ross  * Compute a partial LMv2 or NTLMv2 response (first 16-bytes).
351613a2f6bSGordon Ross  * The full response is composed by the caller by
352613a2f6bSGordon Ross  * appending the client_data to the returned hash.
353613a2f6bSGordon Ross  *
354613a2f6bSGordon Ross  * Output:
355613a2f6bSGordon Ross  *	rhash: _partial_ LMv2/NTLMv2 response (first 16-bytes)
356613a2f6bSGordon Ross  * Inputs:
357613a2f6bSGordon Ross  *	v2hash: 16-byte NTLMv2 hash.
358613a2f6bSGordon Ross  *	C8: Challenge from server (8 bytes)
359613a2f6bSGordon Ross  *	client_data: client nonce (for LMv2) or the
360613a2f6bSGordon Ross  *	  "blob" from ntlm_build_target_info (NTLMv2)
361613a2f6bSGordon Ross  */
362613a2f6bSGordon Ross static int
363613a2f6bSGordon Ross ntlm_v2_resp_hash(uchar_t *rhash,
364613a2f6bSGordon Ross     const uchar_t *v2hash, const uchar_t *C8,
365613a2f6bSGordon Ross     const uchar_t *client_data, size_t cdlen)
366613a2f6bSGordon Ross {
367613a2f6bSGordon Ross 	size_t dlen;
368613a2f6bSGordon Ross 	uchar_t *data = NULL;
369613a2f6bSGordon Ross 
370613a2f6bSGordon Ross 	/* data = concat(C8, client_data) */
371613a2f6bSGordon Ross 	dlen = 8 + cdlen;
372613a2f6bSGordon Ross 	data = malloc(dlen);
373613a2f6bSGordon Ross 	if (data == NULL)
374613a2f6bSGordon Ross 		return (ENOMEM);
375613a2f6bSGordon Ross 	bcopy(C8, data, 8);
376613a2f6bSGordon Ross 	bcopy(client_data, data + 8, cdlen);
377613a2f6bSGordon Ross 
378613a2f6bSGordon Ross 	HMACT64(rhash, v2hash, NTLM_HASH_SZ, data, dlen);
379613a2f6bSGordon Ross 
380613a2f6bSGordon Ross 	free(data);
381613a2f6bSGordon Ross 	return (0);
382613a2f6bSGordon Ross }
383613a2f6bSGordon Ross 
384613a2f6bSGordon Ross /*
385613a2f6bSGordon Ross  * Calculate an NTLMv2 session key (16 bytes).
386613a2f6bSGordon Ross  */
387613a2f6bSGordon Ross static void
388613a2f6bSGordon Ross ntlm_v2_session_key(uchar_t *ssn_key,
389613a2f6bSGordon Ross 	const uchar_t *v2hash,
390613a2f6bSGordon Ross 	const uchar_t *ntresp)
391613a2f6bSGordon Ross {
392613a2f6bSGordon Ross 
393613a2f6bSGordon Ross 	/* session key uses only 1st 16 bytes of ntresp */
394613a2f6bSGordon Ross 	HMACT64(ssn_key, v2hash, NTLM_HASH_SZ, ntresp, NTLM_HASH_SZ);
395613a2f6bSGordon Ross }
396613a2f6bSGordon Ross 
397613a2f6bSGordon Ross 
398613a2f6bSGordon Ross /*
399613a2f6bSGordon Ross  * Compute both the LMv2 response and the NTLMv2 response,
400613a2f6bSGordon Ross  * and put them in the mbdata chains passed.  This allocates
401613a2f6bSGordon Ross  * mbuf chains in the output args, which the caller frees.
402613a2f6bSGordon Ross  * Also computes the session key.
403613a2f6bSGordon Ross  */
404613a2f6bSGordon Ross int
405613a2f6bSGordon Ross ntlm_put_v2_responses(struct smb_ctx *ctx, struct mbdata *ti_mbp,
406613a2f6bSGordon Ross 	struct mbdata *lm_mbp, struct mbdata *nt_mbp)
407613a2f6bSGordon Ross {
408613a2f6bSGordon Ross 	uchar_t *lmresp, *ntresp;
409613a2f6bSGordon Ross 	int err;
410613a2f6bSGordon Ross 	char *ucdom = NULL;	/* user's domain */
411613a2f6bSGordon Ross 	char *ucuser = NULL;	/* account name */
412613a2f6bSGordon Ross 	uchar_t v2hash[NTLM_HASH_SZ];
413613a2f6bSGordon Ross 	struct mbuf *tim = ti_mbp->mb_top;
414613a2f6bSGordon Ross 
415*02d09e03SGordon Ross 	if ((err = mb_init(lm_mbp)) != 0)
416613a2f6bSGordon Ross 		return (err);
417*02d09e03SGordon Ross 	if ((err = mb_init(nt_mbp)) != 0)
418613a2f6bSGordon Ross 		return (err);
419613a2f6bSGordon Ross 
420613a2f6bSGordon Ross 	/*
421613a2f6bSGordon Ross 	 * Convert the user name to upper-case, as
422613a2f6bSGordon Ross 	 * that's what's used when computing LMv2
423613a2f6bSGordon Ross 	 * and NTLMv2 responses.  Also the domain.
424613a2f6bSGordon Ross 	 */
425613a2f6bSGordon Ross 	ucdom  = utf8_str_toupper(ctx->ct_domain);
426613a2f6bSGordon Ross 	ucuser = utf8_str_toupper(ctx->ct_user);
427613a2f6bSGordon Ross 	if (ucdom == NULL || ucuser == NULL) {
428613a2f6bSGordon Ross 		err = ENOMEM;
429613a2f6bSGordon Ross 		goto out;
430613a2f6bSGordon Ross 	}
431613a2f6bSGordon Ross 
432613a2f6bSGordon Ross 	/*
433613a2f6bSGordon Ross 	 * Compute the NTLMv2 hash (see above)
434613a2f6bSGordon Ross 	 * Needs upper-case user, domain.
435613a2f6bSGordon Ross 	 */
436613a2f6bSGordon Ross 	err = ntlm_v2_hash(v2hash, ctx->ct_nthash, ucuser, ucdom);
437613a2f6bSGordon Ross 	if (err)
438613a2f6bSGordon Ross 		goto out;
439613a2f6bSGordon Ross 
440613a2f6bSGordon Ross 	/*
441613a2f6bSGordon Ross 	 * Compute the LMv2 response, derived from
442613a2f6bSGordon Ross 	 * the v2hash, the server challenge, and
443613a2f6bSGordon Ross 	 * the client nonce (random bits).
444613a2f6bSGordon Ross 	 *
445613a2f6bSGordon Ross 	 * We compose it from two parts:
446613a2f6bSGordon Ross 	 *	1: 16-byte response hash
447613a2f6bSGordon Ross 	 *	2: Client nonce
448613a2f6bSGordon Ross 	 */
449613a2f6bSGordon Ross 	lmresp = (uchar_t *)lm_mbp->mb_pos;
450*02d09e03SGordon Ross 	mb_put_mem(lm_mbp, NULL, NTLM_HASH_SZ, MB_MSYSTEM);
451613a2f6bSGordon Ross 	err = ntlm_v2_resp_hash(lmresp,
452613a2f6bSGordon Ross 	    v2hash, ctx->ct_ntlm_chal,
453613a2f6bSGordon Ross 	    ctx->ct_clnonce, NTLM_CHAL_SZ);
454613a2f6bSGordon Ross 	if (err)
455613a2f6bSGordon Ross 		goto out;
456*02d09e03SGordon Ross 	mb_put_mem(lm_mbp, ctx->ct_clnonce, NTLM_CHAL_SZ, MB_MSYSTEM);
457613a2f6bSGordon Ross 
458613a2f6bSGordon Ross 	/*
459613a2f6bSGordon Ross 	 * Compute the NTLMv2 response, derived
460613a2f6bSGordon Ross 	 * from the server challenge and the
461613a2f6bSGordon Ross 	 * "target info." blob passed in.
462613a2f6bSGordon Ross 	 *
463613a2f6bSGordon Ross 	 * Again composed from two parts:
464613a2f6bSGordon Ross 	 *	1: 16-byte response hash
465613a2f6bSGordon Ross 	 *	2: "target info." blob
466613a2f6bSGordon Ross 	 */
467613a2f6bSGordon Ross 	ntresp = (uchar_t *)nt_mbp->mb_pos;
468*02d09e03SGordon Ross 	mb_put_mem(nt_mbp, NULL, NTLM_HASH_SZ, MB_MSYSTEM);
469613a2f6bSGordon Ross 	err = ntlm_v2_resp_hash(ntresp,
470613a2f6bSGordon Ross 	    v2hash, ctx->ct_ntlm_chal,
471613a2f6bSGordon Ross 	    (uchar_t *)tim->m_data, tim->m_len);
472613a2f6bSGordon Ross 	if (err)
473613a2f6bSGordon Ross 		goto out;
474*02d09e03SGordon Ross 	mb_put_mem(nt_mbp, tim->m_data, tim->m_len, MB_MSYSTEM);
475613a2f6bSGordon Ross 
476613a2f6bSGordon Ross 	/*
477613a2f6bSGordon Ross 	 * Compute the session key
478613a2f6bSGordon Ross 	 */
479613a2f6bSGordon Ross 	ntlm_v2_session_key(ctx->ct_ssn_key, v2hash, ntresp);
480613a2f6bSGordon Ross 
481613a2f6bSGordon Ross out:
482613a2f6bSGordon Ross 	if (err) {
483613a2f6bSGordon Ross 		mb_done(lm_mbp);
484613a2f6bSGordon Ross 		mb_done(nt_mbp);
485613a2f6bSGordon Ross 	}
486613a2f6bSGordon Ross 	free(ucdom);
487613a2f6bSGordon Ross 	free(ucuser);
488613a2f6bSGordon Ross 
489613a2f6bSGordon Ross 	return (err);
490613a2f6bSGordon Ross }
491613a2f6bSGordon Ross 
492613a2f6bSGordon Ross /*
493613a2f6bSGordon Ross  * Helper for ntlm_build_target_info below.
494613a2f6bSGordon Ross  * Put a name in the NTLMv2 "target info." blob.
495613a2f6bSGordon Ross  */
496613a2f6bSGordon Ross static void
497613a2f6bSGordon Ross smb_put_blob_name(struct mbdata *mbp, char *name, int type)
498613a2f6bSGordon Ross {
499613a2f6bSGordon Ross 	uint16_t *ucs = NULL;
500613a2f6bSGordon Ross 	int nlen;
501613a2f6bSGordon Ross 
502613a2f6bSGordon Ross 	if (name)
503613a2f6bSGordon Ross 		ucs = convert_utf8_to_leunicode(name);
504613a2f6bSGordon Ross 	if (ucs)
505613a2f6bSGordon Ross 		nlen = unicode_strlen(ucs);
506613a2f6bSGordon Ross 	else
507613a2f6bSGordon Ross 		nlen = 0;
508613a2f6bSGordon Ross 
509613a2f6bSGordon Ross 	nlen <<= 1;	/* length in bytes, without null. */
510613a2f6bSGordon Ross 
511613a2f6bSGordon Ross 	mb_put_uint16le(mbp, type);
512613a2f6bSGordon Ross 	mb_put_uint16le(mbp, nlen);
513*02d09e03SGordon Ross 	mb_put_mem(mbp, (char *)ucs, nlen, MB_MSYSTEM);
514613a2f6bSGordon Ross 
515613a2f6bSGordon Ross 	if (ucs)
516613a2f6bSGordon Ross 		free(ucs);
517613a2f6bSGordon Ross }
518613a2f6bSGordon Ross 
519613a2f6bSGordon Ross /*
520613a2f6bSGordon Ross  * Build an NTLMv2 "target info." blob.  When called from NTLMSSP,
521613a2f6bSGordon Ross  * the list of names comes from the Type 2 message.  Otherwise,
522613a2f6bSGordon Ross  * we create the name list here.
523613a2f6bSGordon Ross  */
524613a2f6bSGordon Ross int
525613a2f6bSGordon Ross ntlm_build_target_info(struct smb_ctx *ctx, struct mbuf *names,
526613a2f6bSGordon Ross 	struct mbdata *mbp)
527613a2f6bSGordon Ross {
528613a2f6bSGordon Ross 	struct timeval now;
529613a2f6bSGordon Ross 	uint64_t nt_time;
530613a2f6bSGordon Ross 
531613a2f6bSGordon Ross 	char *ucdom = NULL;	/* user's domain */
532613a2f6bSGordon Ross 	int err;
533613a2f6bSGordon Ross 
534613a2f6bSGordon Ross 	/* Get mbuf chain for the "target info". */
535*02d09e03SGordon Ross 	if ((err = mb_init(mbp)) != 0)
536613a2f6bSGordon Ross 		return (err);
537613a2f6bSGordon Ross 
538613a2f6bSGordon Ross 	/*
539613a2f6bSGordon Ross 	 * Construct the client nonce by getting
540613a2f6bSGordon Ross 	 * some random data from /dev/urandom
541613a2f6bSGordon Ross 	 */
542613a2f6bSGordon Ross 	err = smb_get_urandom(ctx->ct_clnonce, NTLM_CHAL_SZ);
543613a2f6bSGordon Ross 	if (err)
544613a2f6bSGordon Ross 		goto out;
545613a2f6bSGordon Ross 
546613a2f6bSGordon Ross 	/*
547613a2f6bSGordon Ross 	 * Get the "NT time" for the target info header.
548613a2f6bSGordon Ross 	 */
549613a2f6bSGordon Ross 	(void) gettimeofday(&now, 0);
550613a2f6bSGordon Ross 	smb_time_local2NT(&now, 0, &nt_time);
551613a2f6bSGordon Ross 
552613a2f6bSGordon Ross 	/*
553613a2f6bSGordon Ross 	 * Build the "target info." block.
554613a2f6bSGordon Ross 	 *
555613a2f6bSGordon Ross 	 * Based on information at:
556613a2f6bSGordon Ross 	 * http://davenport.sourceforge.net/ntlm.html#theNtlmv2Response
557613a2f6bSGordon Ross 	 *
558613a2f6bSGordon Ross 	 * First the fixed-size part.
559613a2f6bSGordon Ross 	 */
560613a2f6bSGordon Ross 	mb_put_uint32le(mbp, 0x101);	/* Blob signature */
561613a2f6bSGordon Ross 	mb_put_uint32le(mbp, 0);		/* reserved */
562613a2f6bSGordon Ross 	mb_put_uint64le(mbp, nt_time);	/* NT time stamp */
563*02d09e03SGordon Ross 	mb_put_mem(mbp, ctx->ct_clnonce, NTLM_CHAL_SZ, MB_MSYSTEM);
564613a2f6bSGordon Ross 	mb_put_uint32le(mbp, 0);		/* unknown */
565613a2f6bSGordon Ross 
566613a2f6bSGordon Ross 	/*
567613a2f6bSGordon Ross 	 * Now put the list of names, either from the
568613a2f6bSGordon Ross 	 * NTLMSSP Type 2 message or composed here.
569613a2f6bSGordon Ross 	 */
570613a2f6bSGordon Ross 	if (names) {
571*02d09e03SGordon Ross 		err = mb_put_mem(mbp, names->m_data, names->m_len, MB_MSYSTEM);
572613a2f6bSGordon Ross 	} else {
573613a2f6bSGordon Ross 		/* Get upper-case names. */
574613a2f6bSGordon Ross 		ucdom  = utf8_str_toupper(ctx->ct_domain);
575613a2f6bSGordon Ross 		if (ucdom == NULL) {
576613a2f6bSGordon Ross 			err = ENOMEM;
577613a2f6bSGordon Ross 			goto out;
578613a2f6bSGordon Ross 		}
579613a2f6bSGordon Ross 		smb_put_blob_name(mbp, ucdom, NAMETYPE_DOMAIN_NB);
580613a2f6bSGordon Ross 		smb_put_blob_name(mbp, NULL, NAMETYPE_EOL);
581613a2f6bSGordon Ross 		/* OK, that's the whole "target info." blob! */
582613a2f6bSGordon Ross 	}
583613a2f6bSGordon Ross 	err = 0;
584613a2f6bSGordon Ross 
585613a2f6bSGordon Ross out:
586613a2f6bSGordon Ross 	free(ucdom);
587613a2f6bSGordon Ross 	return (err);
588613a2f6bSGordon Ross }
589