xref: /onnv-gate/usr/src/lib/libsmbfs/smb/ntlm.c (revision 12140:3d7db9f327ef)
110023SGordon.Ross@Sun.COM /*
210023SGordon.Ross@Sun.COM  * Copyright (c) 2000-2001, Boris Popov
310023SGordon.Ross@Sun.COM  * All rights reserved.
410023SGordon.Ross@Sun.COM  *
510023SGordon.Ross@Sun.COM  * Redistribution and use in source and binary forms, with or without
610023SGordon.Ross@Sun.COM  * modification, are permitted provided that the following conditions
710023SGordon.Ross@Sun.COM  * are met:
810023SGordon.Ross@Sun.COM  * 1. Redistributions of source code must retain the above copyright
910023SGordon.Ross@Sun.COM  *    notice, this list of conditions and the following disclaimer.
1010023SGordon.Ross@Sun.COM  * 2. Redistributions in binary form must reproduce the above copyright
1110023SGordon.Ross@Sun.COM  *    notice, this list of conditions and the following disclaimer in the
1210023SGordon.Ross@Sun.COM  *    documentation and/or other materials provided with the distribution.
1310023SGordon.Ross@Sun.COM  * 3. All advertising materials mentioning features or use of this software
1410023SGordon.Ross@Sun.COM  *    must display the following acknowledgement:
1510023SGordon.Ross@Sun.COM  *    This product includes software developed by Boris Popov.
1610023SGordon.Ross@Sun.COM  * 4. Neither the name of the author nor the names of any co-contributors
1710023SGordon.Ross@Sun.COM  *    may be used to endorse or promote products derived from this software
1810023SGordon.Ross@Sun.COM  *    without specific prior written permission.
1910023SGordon.Ross@Sun.COM  *
2010023SGordon.Ross@Sun.COM  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2110023SGordon.Ross@Sun.COM  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2210023SGordon.Ross@Sun.COM  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2310023SGordon.Ross@Sun.COM  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2410023SGordon.Ross@Sun.COM  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2510023SGordon.Ross@Sun.COM  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2610023SGordon.Ross@Sun.COM  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2710023SGordon.Ross@Sun.COM  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2810023SGordon.Ross@Sun.COM  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2910023SGordon.Ross@Sun.COM  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3010023SGordon.Ross@Sun.COM  * SUCH DAMAGE.
3110023SGordon.Ross@Sun.COM  *
3210023SGordon.Ross@Sun.COM  * $Id: smb_crypt.c,v 1.13 2005/01/26 23:50:50 lindak Exp $
3310023SGordon.Ross@Sun.COM  */
3410023SGordon.Ross@Sun.COM 
3510023SGordon.Ross@Sun.COM /*
36*12140SGordon.Ross@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
3710023SGordon.Ross@Sun.COM  */
3810023SGordon.Ross@Sun.COM 
3910023SGordon.Ross@Sun.COM /*
4010023SGordon.Ross@Sun.COM  * NTLM support functions
4110023SGordon.Ross@Sun.COM  *
4210023SGordon.Ross@Sun.COM  * Some code from the driver: smb_smb.c, smb_crypt.c
4310023SGordon.Ross@Sun.COM  */
4410023SGordon.Ross@Sun.COM 
4510023SGordon.Ross@Sun.COM #include <sys/errno.h>
4610023SGordon.Ross@Sun.COM #include <sys/types.h>
4710023SGordon.Ross@Sun.COM #include <sys/md4.h>
4810023SGordon.Ross@Sun.COM #include <sys/md5.h>
4910023SGordon.Ross@Sun.COM 
5010023SGordon.Ross@Sun.COM #include <ctype.h>
5110023SGordon.Ross@Sun.COM #include <stdlib.h>
5210023SGordon.Ross@Sun.COM #include <strings.h>
5310023SGordon.Ross@Sun.COM 
5410023SGordon.Ross@Sun.COM #include <netsmb/smb_lib.h>
5510023SGordon.Ross@Sun.COM 
5610023SGordon.Ross@Sun.COM #include "private.h"
5710023SGordon.Ross@Sun.COM #include "charsets.h"
5810023SGordon.Ross@Sun.COM #include "smb_crypt.h"
5910023SGordon.Ross@Sun.COM #include "ntlm.h"
6010023SGordon.Ross@Sun.COM 
6110023SGordon.Ross@Sun.COM 
6210023SGordon.Ross@Sun.COM /*
6310023SGordon.Ross@Sun.COM  * ntlm_compute_lm_hash
6410023SGordon.Ross@Sun.COM  *
6510023SGordon.Ross@Sun.COM  * Compute an LM hash given a password
6610023SGordon.Ross@Sun.COM  *
6710023SGordon.Ross@Sun.COM  * Output:
6810023SGordon.Ross@Sun.COM  *	hash: 16-byte "LanMan" (LM) hash.
6910023SGordon.Ross@Sun.COM  * Inputs:
7010023SGordon.Ross@Sun.COM  *	ucpw: User's password, upper-case UTF-8 string.
7110023SGordon.Ross@Sun.COM  *
7210023SGordon.Ross@Sun.COM  * Source: Implementing CIFS (Chris Hertel)
7310023SGordon.Ross@Sun.COM  *
7410023SGordon.Ross@Sun.COM  * P14 = UCPW padded to 14-bytes, or truncated (as needed)
7510023SGordon.Ross@Sun.COM  * result = Encrypt(Key=P14, Data=MagicString)
7610023SGordon.Ross@Sun.COM  */
7710023SGordon.Ross@Sun.COM int
ntlm_compute_lm_hash(uchar_t * hash,const char * pass)7810023SGordon.Ross@Sun.COM ntlm_compute_lm_hash(uchar_t *hash, const char *pass)
7910023SGordon.Ross@Sun.COM {
8010023SGordon.Ross@Sun.COM 	static const uchar_t M8[8] = "KGS!@#$%";
8110023SGordon.Ross@Sun.COM 	uchar_t P14[14 + 1];
8210023SGordon.Ross@Sun.COM 	int err;
8310023SGordon.Ross@Sun.COM 	char *ucpw;
8410023SGordon.Ross@Sun.COM 
8510023SGordon.Ross@Sun.COM 	/* First, convert the p/w to upper case. */
8610023SGordon.Ross@Sun.COM 	ucpw = utf8_str_toupper(pass);
8710023SGordon.Ross@Sun.COM 	if (ucpw == NULL)
8810023SGordon.Ross@Sun.COM 		return (ENOMEM);
8910023SGordon.Ross@Sun.COM 
9010023SGordon.Ross@Sun.COM 	/* Pad or truncate the upper-case P/W as needed. */
9110023SGordon.Ross@Sun.COM 	bzero(P14, sizeof (P14));
9210023SGordon.Ross@Sun.COM 	(void) strncpy((char *)P14, ucpw, 14);
9310023SGordon.Ross@Sun.COM 
9410023SGordon.Ross@Sun.COM 	/* Compute the hash. */
9510023SGordon.Ross@Sun.COM 	err = smb_encrypt_DES(hash, NTLM_HASH_SZ,
9610023SGordon.Ross@Sun.COM 	    P14, 14, M8, 8);
9710023SGordon.Ross@Sun.COM 
9810023SGordon.Ross@Sun.COM 	free(ucpw);
9910023SGordon.Ross@Sun.COM 	return (err);
10010023SGordon.Ross@Sun.COM }
10110023SGordon.Ross@Sun.COM 
10210023SGordon.Ross@Sun.COM /*
10310023SGordon.Ross@Sun.COM  * ntlm_compute_nt_hash
10410023SGordon.Ross@Sun.COM  *
10510023SGordon.Ross@Sun.COM  * Compute an NT hash given a password in UTF-8.
10610023SGordon.Ross@Sun.COM  *
10710023SGordon.Ross@Sun.COM  * Output:
10810023SGordon.Ross@Sun.COM  *	hash: 16-byte "NT" hash.
10910023SGordon.Ross@Sun.COM  * Inputs:
11010023SGordon.Ross@Sun.COM  *	upw: User's password, mixed-case UCS-2LE.
11110023SGordon.Ross@Sun.COM  *	pwlen: Size (in bytes) of upw
11210023SGordon.Ross@Sun.COM  */
11310023SGordon.Ross@Sun.COM int
ntlm_compute_nt_hash(uchar_t * hash,const char * pass)11410023SGordon.Ross@Sun.COM ntlm_compute_nt_hash(uchar_t *hash, const char *pass)
11510023SGordon.Ross@Sun.COM {
11610023SGordon.Ross@Sun.COM 	MD4_CTX ctx;
11710023SGordon.Ross@Sun.COM 	uint16_t *unipw = NULL;
11810023SGordon.Ross@Sun.COM 	int pwsz;
11910023SGordon.Ross@Sun.COM 
12010023SGordon.Ross@Sun.COM 	/* First, convert the password to unicode. */
12110023SGordon.Ross@Sun.COM 	unipw = convert_utf8_to_leunicode(pass);
12210023SGordon.Ross@Sun.COM 	if (unipw == NULL)
12310023SGordon.Ross@Sun.COM 		return (ENOMEM);
12410023SGordon.Ross@Sun.COM 	pwsz = unicode_strlen(unipw) << 1;
12510023SGordon.Ross@Sun.COM 
12610023SGordon.Ross@Sun.COM 	/* Compute the hash. */
12710023SGordon.Ross@Sun.COM 	MD4Init(&ctx);
12810023SGordon.Ross@Sun.COM 	MD4Update(&ctx, unipw, pwsz);
12910023SGordon.Ross@Sun.COM 	MD4Final(hash, &ctx);
13010023SGordon.Ross@Sun.COM 
13110023SGordon.Ross@Sun.COM 	free(unipw);
13210023SGordon.Ross@Sun.COM 	return (0);
13310023SGordon.Ross@Sun.COM }
13410023SGordon.Ross@Sun.COM 
13510023SGordon.Ross@Sun.COM /*
13610023SGordon.Ross@Sun.COM  * ntlm_v1_response
13710023SGordon.Ross@Sun.COM  *
13810023SGordon.Ross@Sun.COM  * Create an LM response from the given LM hash and challenge,
13910023SGordon.Ross@Sun.COM  * or an NTLM repsonse from a given NTLM hash and challenge.
14010023SGordon.Ross@Sun.COM  * Both response types are 24 bytes (NTLM_V1_RESP_SZ)
14110023SGordon.Ross@Sun.COM  */
14210023SGordon.Ross@Sun.COM static int
ntlm_v1_response(uchar_t * resp,const uchar_t * hash,const uchar_t * chal,int clen)14310023SGordon.Ross@Sun.COM ntlm_v1_response(uchar_t *resp,
14410023SGordon.Ross@Sun.COM     const uchar_t *hash,
14510023SGordon.Ross@Sun.COM     const uchar_t *chal, int clen)
14610023SGordon.Ross@Sun.COM {
14710023SGordon.Ross@Sun.COM 	uchar_t S21[21];
14810023SGordon.Ross@Sun.COM 	int err;
14910023SGordon.Ross@Sun.COM 
15010023SGordon.Ross@Sun.COM 	/*
15110023SGordon.Ross@Sun.COM 	 * 14-byte LM Hash should be padded with 5 nul bytes to create
15210023SGordon.Ross@Sun.COM 	 * a 21-byte string to be used in producing LM response
15310023SGordon.Ross@Sun.COM 	 */
15410023SGordon.Ross@Sun.COM 	bzero(&S21, sizeof (S21));
15510023SGordon.Ross@Sun.COM 	bcopy(hash, S21, NTLM_HASH_SZ);
15610023SGordon.Ross@Sun.COM 
15710023SGordon.Ross@Sun.COM 	/* padded LM Hash -> LM Response */
15810023SGordon.Ross@Sun.COM 	err = smb_encrypt_DES(resp, NTLM_V1_RESP_SZ,
15910023SGordon.Ross@Sun.COM 	    S21, 21, chal, clen);
16010023SGordon.Ross@Sun.COM 	return (err);
16110023SGordon.Ross@Sun.COM }
16210023SGordon.Ross@Sun.COM 
16310023SGordon.Ross@Sun.COM /*
16410023SGordon.Ross@Sun.COM  * Calculate an NTLMv1 session key (16 bytes).
16510023SGordon.Ross@Sun.COM  */
16610023SGordon.Ross@Sun.COM static void
ntlm_v1_session_key(uchar_t * ssn_key,const uchar_t * nt_hash)16710023SGordon.Ross@Sun.COM ntlm_v1_session_key(uchar_t *ssn_key, const uchar_t *nt_hash)
16810023SGordon.Ross@Sun.COM {
16910023SGordon.Ross@Sun.COM 	MD4_CTX md4;
17010023SGordon.Ross@Sun.COM 
17110023SGordon.Ross@Sun.COM 	MD4Init(&md4);
17210023SGordon.Ross@Sun.COM 	MD4Update(&md4, nt_hash, NTLM_HASH_SZ);
17310023SGordon.Ross@Sun.COM 	MD4Final(ssn_key, &md4);
17410023SGordon.Ross@Sun.COM }
17510023SGordon.Ross@Sun.COM 
17610023SGordon.Ross@Sun.COM /*
17710023SGordon.Ross@Sun.COM  * Compute both the LM(v1) response and the NTLM(v1) response,
17810023SGordon.Ross@Sun.COM  * and put them in the mbdata chains passed.  This allocates
17910023SGordon.Ross@Sun.COM  * mbuf chains in the output args, which the caller frees.
18010023SGordon.Ross@Sun.COM  */
18110023SGordon.Ross@Sun.COM int
ntlm_put_v1_responses(struct smb_ctx * ctx,struct mbdata * lm_mbp,struct mbdata * nt_mbp)18210023SGordon.Ross@Sun.COM ntlm_put_v1_responses(struct smb_ctx *ctx,
18310023SGordon.Ross@Sun.COM 	struct mbdata *lm_mbp, struct mbdata *nt_mbp)
18410023SGordon.Ross@Sun.COM {
18510023SGordon.Ross@Sun.COM 	uchar_t *lmresp, *ntresp;
18610023SGordon.Ross@Sun.COM 	int err;
18710023SGordon.Ross@Sun.COM 
18810023SGordon.Ross@Sun.COM 	/* Get mbuf chain for the LM response. */
18911332SGordon.Ross@Sun.COM 	if ((err = mb_init_sz(lm_mbp, NTLM_V1_RESP_SZ)) != 0)
19010023SGordon.Ross@Sun.COM 		return (err);
19110023SGordon.Ross@Sun.COM 
19210023SGordon.Ross@Sun.COM 	/* Get mbuf chain for the NT response. */
19311332SGordon.Ross@Sun.COM 	if ((err = mb_init_sz(nt_mbp, NTLM_V1_RESP_SZ)) != 0)
19410023SGordon.Ross@Sun.COM 		return (err);
19510023SGordon.Ross@Sun.COM 
19610023SGordon.Ross@Sun.COM 	/*
19710023SGordon.Ross@Sun.COM 	 * Compute the LM response, derived
19810023SGordon.Ross@Sun.COM 	 * from the challenge and the ASCII
19910023SGordon.Ross@Sun.COM 	 * password (if authflags allow).
20010023SGordon.Ross@Sun.COM 	 */
20111332SGordon.Ross@Sun.COM 	err = mb_fit(lm_mbp, NTLM_V1_RESP_SZ, (char **)&lmresp);
20211332SGordon.Ross@Sun.COM 	if (err)
20311332SGordon.Ross@Sun.COM 		return (err);
20410023SGordon.Ross@Sun.COM 	bzero(lmresp, NTLM_V1_RESP_SZ);
20510023SGordon.Ross@Sun.COM 	if (ctx->ct_authflags & SMB_AT_LM1) {
20610023SGordon.Ross@Sun.COM 		/* They asked to send the LM hash too. */
20710023SGordon.Ross@Sun.COM 		err = ntlm_v1_response(lmresp, ctx->ct_lmhash,
20810023SGordon.Ross@Sun.COM 		    ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
20910023SGordon.Ross@Sun.COM 		if (err)
21010023SGordon.Ross@Sun.COM 			return (err);
21110023SGordon.Ross@Sun.COM 	}
21210023SGordon.Ross@Sun.COM 
21310023SGordon.Ross@Sun.COM 	/*
21410023SGordon.Ross@Sun.COM 	 * Compute the NTLM response, derived from
21510023SGordon.Ross@Sun.COM 	 * the challenge and the NT hash.
21610023SGordon.Ross@Sun.COM 	 */
21711332SGordon.Ross@Sun.COM 	err = mb_fit(nt_mbp, NTLM_V1_RESP_SZ, (char **)&ntresp);
21811332SGordon.Ross@Sun.COM 	if (err)
21911332SGordon.Ross@Sun.COM 		return (err);
22010023SGordon.Ross@Sun.COM 	bzero(ntresp, NTLM_V1_RESP_SZ);
22110023SGordon.Ross@Sun.COM 	err = ntlm_v1_response(ntresp, ctx->ct_nthash,
22210023SGordon.Ross@Sun.COM 	    ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
22310023SGordon.Ross@Sun.COM 
22410023SGordon.Ross@Sun.COM 	/*
22510023SGordon.Ross@Sun.COM 	 * Compute the session key
22610023SGordon.Ross@Sun.COM 	 */
22710023SGordon.Ross@Sun.COM 	ntlm_v1_session_key(ctx->ct_ssn_key, ctx->ct_nthash);
22810023SGordon.Ross@Sun.COM 
22910023SGordon.Ross@Sun.COM 	return (err);
23010023SGordon.Ross@Sun.COM }
23110023SGordon.Ross@Sun.COM 
23210023SGordon.Ross@Sun.COM /*
23310023SGordon.Ross@Sun.COM  * A variation on HMAC-MD5 known as HMACT64 is used by Windows systems.
23410023SGordon.Ross@Sun.COM  * The HMACT64() function is the same as the HMAC-MD5() except that
23510023SGordon.Ross@Sun.COM  * it truncates the input key to 64 bytes rather than hashing it down
23610023SGordon.Ross@Sun.COM  * to 16 bytes using the MD5() function.
23710023SGordon.Ross@Sun.COM  *
23810023SGordon.Ross@Sun.COM  * Output: digest (16-bytes)
23910023SGordon.Ross@Sun.COM  */
24010023SGordon.Ross@Sun.COM static void
HMACT64(uchar_t * digest,const uchar_t * key,size_t key_len,const uchar_t * data,size_t data_len)24110023SGordon.Ross@Sun.COM HMACT64(uchar_t *digest,
24210023SGordon.Ross@Sun.COM     const uchar_t *key, size_t key_len,
24310023SGordon.Ross@Sun.COM     const uchar_t *data, size_t data_len)
24410023SGordon.Ross@Sun.COM {
24510023SGordon.Ross@Sun.COM 	MD5_CTX context;
24610023SGordon.Ross@Sun.COM 	uchar_t k_ipad[64];	/* inner padding - key XORd with ipad */
24710023SGordon.Ross@Sun.COM 	uchar_t k_opad[64];	/* outer padding - key XORd with opad */
24810023SGordon.Ross@Sun.COM 	int i;
24910023SGordon.Ross@Sun.COM 
25010023SGordon.Ross@Sun.COM 	/* if key is longer than 64 bytes use only the first 64 bytes */
25110023SGordon.Ross@Sun.COM 	if (key_len > 64)
25210023SGordon.Ross@Sun.COM 		key_len = 64;
25310023SGordon.Ross@Sun.COM 
25410023SGordon.Ross@Sun.COM 	/*
25510023SGordon.Ross@Sun.COM 	 * The HMAC-MD5 (and HMACT64) transform looks like:
25610023SGordon.Ross@Sun.COM 	 *
25710023SGordon.Ross@Sun.COM 	 * MD5(K XOR opad, MD5(K XOR ipad, data))
25810023SGordon.Ross@Sun.COM 	 *
25910023SGordon.Ross@Sun.COM 	 * where K is an n byte key
26010023SGordon.Ross@Sun.COM 	 * ipad is the byte 0x36 repeated 64 times
26110023SGordon.Ross@Sun.COM 	 * opad is the byte 0x5c repeated 64 times
26210023SGordon.Ross@Sun.COM 	 * and data is the data being protected.
26310023SGordon.Ross@Sun.COM 	 */
26410023SGordon.Ross@Sun.COM 
26510023SGordon.Ross@Sun.COM 	/* start out by storing key in pads */
26610023SGordon.Ross@Sun.COM 	bzero(k_ipad, sizeof (k_ipad));
26710023SGordon.Ross@Sun.COM 	bzero(k_opad, sizeof (k_opad));
26810023SGordon.Ross@Sun.COM 	bcopy(key, k_ipad, key_len);
26910023SGordon.Ross@Sun.COM 	bcopy(key, k_opad, key_len);
27010023SGordon.Ross@Sun.COM 
27110023SGordon.Ross@Sun.COM 	/* XOR key with ipad and opad values */
27210023SGordon.Ross@Sun.COM 	for (i = 0; i < 64; i++) {
27310023SGordon.Ross@Sun.COM 		k_ipad[i] ^= 0x36;
27410023SGordon.Ross@Sun.COM 		k_opad[i] ^= 0x5c;
27510023SGordon.Ross@Sun.COM 	}
27610023SGordon.Ross@Sun.COM 
27710023SGordon.Ross@Sun.COM 	/*
27810023SGordon.Ross@Sun.COM 	 * perform inner MD5
27910023SGordon.Ross@Sun.COM 	 */
28010023SGordon.Ross@Sun.COM 	MD5Init(&context);			/* init context for 1st pass */
28110023SGordon.Ross@Sun.COM 	MD5Update(&context, k_ipad, 64);	/* start with inner pad */
28210023SGordon.Ross@Sun.COM 	MD5Update(&context, data, data_len);	/* then data of datagram */
28310023SGordon.Ross@Sun.COM 	MD5Final(digest, &context);		/* finish up 1st pass */
28410023SGordon.Ross@Sun.COM 
28510023SGordon.Ross@Sun.COM 	/*
28610023SGordon.Ross@Sun.COM 	 * perform outer MD5
28710023SGordon.Ross@Sun.COM 	 */
28810023SGordon.Ross@Sun.COM 	MD5Init(&context);			/* init context for 2nd pass */
28910023SGordon.Ross@Sun.COM 	MD5Update(&context, k_opad, 64);	/* start with outer pad */
29010023SGordon.Ross@Sun.COM 	MD5Update(&context, digest, 16);	/* then results of 1st hash */
29110023SGordon.Ross@Sun.COM 	MD5Final(digest, &context);		/* finish up 2nd pass */
29210023SGordon.Ross@Sun.COM }
29310023SGordon.Ross@Sun.COM 
29410023SGordon.Ross@Sun.COM 
29510023SGordon.Ross@Sun.COM /*
29610023SGordon.Ross@Sun.COM  * Compute an NTLMv2 hash given the NTLMv1 hash, the user name,
29710023SGordon.Ross@Sun.COM  * and the destination (machine or domain name).
29810023SGordon.Ross@Sun.COM  *
29910023SGordon.Ross@Sun.COM  * Output:
30010023SGordon.Ross@Sun.COM  *	v2hash: 16-byte NTLMv2 hash.
30110023SGordon.Ross@Sun.COM  * Inputs:
30210023SGordon.Ross@Sun.COM  *	v1hash: 16-byte NTLMv1 hash.
30310023SGordon.Ross@Sun.COM  *	user: User name, UPPER-case UTF-8 string.
30410023SGordon.Ross@Sun.COM  *	destination: Domain or server, MIXED-case UTF-8 string.
30510023SGordon.Ross@Sun.COM  */
30610023SGordon.Ross@Sun.COM static int
ntlm_v2_hash(uchar_t * v2hash,const uchar_t * v1hash,const char * user,const char * destination)30710023SGordon.Ross@Sun.COM ntlm_v2_hash(uchar_t *v2hash, const uchar_t *v1hash,
30810023SGordon.Ross@Sun.COM     const char *user, const char *destination)
30910023SGordon.Ross@Sun.COM {
31010023SGordon.Ross@Sun.COM 	int ulen, dlen;
31110023SGordon.Ross@Sun.COM 	size_t ucs2len;
31210023SGordon.Ross@Sun.COM 	uint16_t *ucs2data = NULL;
31310023SGordon.Ross@Sun.COM 	char *utf8data = NULL;
31410023SGordon.Ross@Sun.COM 	int err = ENOMEM;
31510023SGordon.Ross@Sun.COM 
31610023SGordon.Ross@Sun.COM 	/*
31710023SGordon.Ross@Sun.COM 	 * v2hash = HMACT64(v1hash, 16, concat(upcase(user), dest))
31810023SGordon.Ross@Sun.COM 	 * where "dest" is the domain or server name ("target name")
31910023SGordon.Ross@Sun.COM 	 * Note: user name is converted to upper-case by the caller.
32010023SGordon.Ross@Sun.COM 	 */
32110023SGordon.Ross@Sun.COM 
32210023SGordon.Ross@Sun.COM 	/* utf8data = concat(user, dest) */
32310023SGordon.Ross@Sun.COM 	ulen = strlen(user);
32410023SGordon.Ross@Sun.COM 	dlen = strlen(destination);
32510023SGordon.Ross@Sun.COM 	utf8data = malloc(ulen + dlen + 1);
32610023SGordon.Ross@Sun.COM 	if (utf8data == NULL)
32710023SGordon.Ross@Sun.COM 		goto out;
32810023SGordon.Ross@Sun.COM 	bcopy(user, utf8data, ulen);
32910023SGordon.Ross@Sun.COM 	bcopy(destination, utf8data + ulen, dlen + 1);
33010023SGordon.Ross@Sun.COM 
33110023SGordon.Ross@Sun.COM 	/* Convert to UCS-2LE */
33210023SGordon.Ross@Sun.COM 	ucs2data = convert_utf8_to_leunicode(utf8data);
33310023SGordon.Ross@Sun.COM 	if (ucs2data == NULL)
33410023SGordon.Ross@Sun.COM 		goto out;
33510023SGordon.Ross@Sun.COM 	ucs2len = 2 * unicode_strlen(ucs2data);
33610023SGordon.Ross@Sun.COM 
33710023SGordon.Ross@Sun.COM 	HMACT64(v2hash, v1hash, NTLM_HASH_SZ,
33810023SGordon.Ross@Sun.COM 	    (uchar_t *)ucs2data, ucs2len);
33910023SGordon.Ross@Sun.COM 	err = 0;
34010023SGordon.Ross@Sun.COM out:
34110023SGordon.Ross@Sun.COM 	if (ucs2data)
34210023SGordon.Ross@Sun.COM 		free(ucs2data);
34310023SGordon.Ross@Sun.COM 	if (utf8data)
34410023SGordon.Ross@Sun.COM 		free(utf8data);
34510023SGordon.Ross@Sun.COM 	return (err);
34610023SGordon.Ross@Sun.COM }
34710023SGordon.Ross@Sun.COM 
34810023SGordon.Ross@Sun.COM /*
34910023SGordon.Ross@Sun.COM  * Compute a partial LMv2 or NTLMv2 response (first 16-bytes).
35010023SGordon.Ross@Sun.COM  * The full response is composed by the caller by
35110023SGordon.Ross@Sun.COM  * appending the client_data to the returned hash.
35210023SGordon.Ross@Sun.COM  *
35310023SGordon.Ross@Sun.COM  * Output:
35410023SGordon.Ross@Sun.COM  *	rhash: _partial_ LMv2/NTLMv2 response (first 16-bytes)
35510023SGordon.Ross@Sun.COM  * Inputs:
35610023SGordon.Ross@Sun.COM  *	v2hash: 16-byte NTLMv2 hash.
35710023SGordon.Ross@Sun.COM  *	C8: Challenge from server (8 bytes)
35810023SGordon.Ross@Sun.COM  *	client_data: client nonce (for LMv2) or the
35910023SGordon.Ross@Sun.COM  *	  "blob" from ntlm_build_target_info (NTLMv2)
36010023SGordon.Ross@Sun.COM  */
36110023SGordon.Ross@Sun.COM static int
ntlm_v2_resp_hash(uchar_t * rhash,const uchar_t * v2hash,const uchar_t * C8,const uchar_t * client_data,size_t cdlen)36210023SGordon.Ross@Sun.COM ntlm_v2_resp_hash(uchar_t *rhash,
36310023SGordon.Ross@Sun.COM     const uchar_t *v2hash, const uchar_t *C8,
36410023SGordon.Ross@Sun.COM     const uchar_t *client_data, size_t cdlen)
36510023SGordon.Ross@Sun.COM {
36610023SGordon.Ross@Sun.COM 	size_t dlen;
36710023SGordon.Ross@Sun.COM 	uchar_t *data = NULL;
36810023SGordon.Ross@Sun.COM 
36910023SGordon.Ross@Sun.COM 	/* data = concat(C8, client_data) */
37010023SGordon.Ross@Sun.COM 	dlen = 8 + cdlen;
37110023SGordon.Ross@Sun.COM 	data = malloc(dlen);
37210023SGordon.Ross@Sun.COM 	if (data == NULL)
37310023SGordon.Ross@Sun.COM 		return (ENOMEM);
37410023SGordon.Ross@Sun.COM 	bcopy(C8, data, 8);
37510023SGordon.Ross@Sun.COM 	bcopy(client_data, data + 8, cdlen);
37610023SGordon.Ross@Sun.COM 
37710023SGordon.Ross@Sun.COM 	HMACT64(rhash, v2hash, NTLM_HASH_SZ, data, dlen);
37810023SGordon.Ross@Sun.COM 
37910023SGordon.Ross@Sun.COM 	free(data);
38010023SGordon.Ross@Sun.COM 	return (0);
38110023SGordon.Ross@Sun.COM }
38210023SGordon.Ross@Sun.COM 
38310023SGordon.Ross@Sun.COM /*
38410023SGordon.Ross@Sun.COM  * Calculate an NTLMv2 session key (16 bytes).
38510023SGordon.Ross@Sun.COM  */
38610023SGordon.Ross@Sun.COM static void
ntlm_v2_session_key(uchar_t * ssn_key,const uchar_t * v2hash,const uchar_t * ntresp)38710023SGordon.Ross@Sun.COM ntlm_v2_session_key(uchar_t *ssn_key,
38810023SGordon.Ross@Sun.COM 	const uchar_t *v2hash,
38910023SGordon.Ross@Sun.COM 	const uchar_t *ntresp)
39010023SGordon.Ross@Sun.COM {
39110023SGordon.Ross@Sun.COM 
39210023SGordon.Ross@Sun.COM 	/* session key uses only 1st 16 bytes of ntresp */
39310023SGordon.Ross@Sun.COM 	HMACT64(ssn_key, v2hash, NTLM_HASH_SZ, ntresp, NTLM_HASH_SZ);
39410023SGordon.Ross@Sun.COM }
39510023SGordon.Ross@Sun.COM 
39610023SGordon.Ross@Sun.COM 
39710023SGordon.Ross@Sun.COM /*
39810023SGordon.Ross@Sun.COM  * Compute both the LMv2 response and the NTLMv2 response,
39910023SGordon.Ross@Sun.COM  * and put them in the mbdata chains passed.  This allocates
40010023SGordon.Ross@Sun.COM  * mbuf chains in the output args, which the caller frees.
40110023SGordon.Ross@Sun.COM  * Also computes the session key.
40210023SGordon.Ross@Sun.COM  */
40310023SGordon.Ross@Sun.COM int
ntlm_put_v2_responses(struct smb_ctx * ctx,struct mbdata * ti_mbp,struct mbdata * lm_mbp,struct mbdata * nt_mbp)40410023SGordon.Ross@Sun.COM ntlm_put_v2_responses(struct smb_ctx *ctx, struct mbdata *ti_mbp,
40510023SGordon.Ross@Sun.COM 	struct mbdata *lm_mbp, struct mbdata *nt_mbp)
40610023SGordon.Ross@Sun.COM {
40710023SGordon.Ross@Sun.COM 	uchar_t *lmresp, *ntresp;
40810023SGordon.Ross@Sun.COM 	int err;
409*12140SGordon.Ross@Sun.COM 	char *ucuser = NULL;	/* upper-case user name */
41010023SGordon.Ross@Sun.COM 	uchar_t v2hash[NTLM_HASH_SZ];
41110023SGordon.Ross@Sun.COM 	struct mbuf *tim = ti_mbp->mb_top;
41210023SGordon.Ross@Sun.COM 
41311332SGordon.Ross@Sun.COM 	if ((err = mb_init(lm_mbp)) != 0)
41410023SGordon.Ross@Sun.COM 		return (err);
41511332SGordon.Ross@Sun.COM 	if ((err = mb_init(nt_mbp)) != 0)
41610023SGordon.Ross@Sun.COM 		return (err);
41710023SGordon.Ross@Sun.COM 
41810023SGordon.Ross@Sun.COM 	/*
41910023SGordon.Ross@Sun.COM 	 * Convert the user name to upper-case, as
42010023SGordon.Ross@Sun.COM 	 * that's what's used when computing LMv2
421*12140SGordon.Ross@Sun.COM 	 * and NTLMv2 responses.  Note that the
422*12140SGordon.Ross@Sun.COM 	 * domain name is NOT upper-cased!
42310023SGordon.Ross@Sun.COM 	 */
42410023SGordon.Ross@Sun.COM 	ucuser = utf8_str_toupper(ctx->ct_user);
425*12140SGordon.Ross@Sun.COM 	if (ucuser == NULL) {
42610023SGordon.Ross@Sun.COM 		err = ENOMEM;
42710023SGordon.Ross@Sun.COM 		goto out;
42810023SGordon.Ross@Sun.COM 	}
42910023SGordon.Ross@Sun.COM 
43010023SGordon.Ross@Sun.COM 	/*
431*12140SGordon.Ross@Sun.COM 	 * Compute the NTLMv2 hash
43210023SGordon.Ross@Sun.COM 	 */
433*12140SGordon.Ross@Sun.COM 	err = ntlm_v2_hash(v2hash, ctx->ct_nthash,
434*12140SGordon.Ross@Sun.COM 	    ucuser, ctx->ct_domain);
43510023SGordon.Ross@Sun.COM 	if (err)
43610023SGordon.Ross@Sun.COM 		goto out;
43710023SGordon.Ross@Sun.COM 
43810023SGordon.Ross@Sun.COM 	/*
43910023SGordon.Ross@Sun.COM 	 * Compute the LMv2 response, derived from
44010023SGordon.Ross@Sun.COM 	 * the v2hash, the server challenge, and
44110023SGordon.Ross@Sun.COM 	 * the client nonce (random bits).
44210023SGordon.Ross@Sun.COM 	 *
44310023SGordon.Ross@Sun.COM 	 * We compose it from two parts:
44410023SGordon.Ross@Sun.COM 	 *	1: 16-byte response hash
44510023SGordon.Ross@Sun.COM 	 *	2: Client nonce
44610023SGordon.Ross@Sun.COM 	 */
44710023SGordon.Ross@Sun.COM 	lmresp = (uchar_t *)lm_mbp->mb_pos;
44811332SGordon.Ross@Sun.COM 	mb_put_mem(lm_mbp, NULL, NTLM_HASH_SZ, MB_MSYSTEM);
44910023SGordon.Ross@Sun.COM 	err = ntlm_v2_resp_hash(lmresp,
45010023SGordon.Ross@Sun.COM 	    v2hash, ctx->ct_ntlm_chal,
45110023SGordon.Ross@Sun.COM 	    ctx->ct_clnonce, NTLM_CHAL_SZ);
45210023SGordon.Ross@Sun.COM 	if (err)
45310023SGordon.Ross@Sun.COM 		goto out;
45411332SGordon.Ross@Sun.COM 	mb_put_mem(lm_mbp, ctx->ct_clnonce, NTLM_CHAL_SZ, MB_MSYSTEM);
45510023SGordon.Ross@Sun.COM 
45610023SGordon.Ross@Sun.COM 	/*
45710023SGordon.Ross@Sun.COM 	 * Compute the NTLMv2 response, derived
45810023SGordon.Ross@Sun.COM 	 * from the server challenge and the
45910023SGordon.Ross@Sun.COM 	 * "target info." blob passed in.
46010023SGordon.Ross@Sun.COM 	 *
46110023SGordon.Ross@Sun.COM 	 * Again composed from two parts:
46210023SGordon.Ross@Sun.COM 	 *	1: 16-byte response hash
46310023SGordon.Ross@Sun.COM 	 *	2: "target info." blob
46410023SGordon.Ross@Sun.COM 	 */
46510023SGordon.Ross@Sun.COM 	ntresp = (uchar_t *)nt_mbp->mb_pos;
46611332SGordon.Ross@Sun.COM 	mb_put_mem(nt_mbp, NULL, NTLM_HASH_SZ, MB_MSYSTEM);
46710023SGordon.Ross@Sun.COM 	err = ntlm_v2_resp_hash(ntresp,
46810023SGordon.Ross@Sun.COM 	    v2hash, ctx->ct_ntlm_chal,
46910023SGordon.Ross@Sun.COM 	    (uchar_t *)tim->m_data, tim->m_len);
47010023SGordon.Ross@Sun.COM 	if (err)
47110023SGordon.Ross@Sun.COM 		goto out;
47211332SGordon.Ross@Sun.COM 	mb_put_mem(nt_mbp, tim->m_data, tim->m_len, MB_MSYSTEM);
47310023SGordon.Ross@Sun.COM 
47410023SGordon.Ross@Sun.COM 	/*
47510023SGordon.Ross@Sun.COM 	 * Compute the session key
47610023SGordon.Ross@Sun.COM 	 */
47710023SGordon.Ross@Sun.COM 	ntlm_v2_session_key(ctx->ct_ssn_key, v2hash, ntresp);
47810023SGordon.Ross@Sun.COM 
47910023SGordon.Ross@Sun.COM out:
48010023SGordon.Ross@Sun.COM 	if (err) {
48110023SGordon.Ross@Sun.COM 		mb_done(lm_mbp);
48210023SGordon.Ross@Sun.COM 		mb_done(nt_mbp);
48310023SGordon.Ross@Sun.COM 	}
48410023SGordon.Ross@Sun.COM 	free(ucuser);
48510023SGordon.Ross@Sun.COM 
48610023SGordon.Ross@Sun.COM 	return (err);
48710023SGordon.Ross@Sun.COM }
48810023SGordon.Ross@Sun.COM 
48910023SGordon.Ross@Sun.COM /*
49010023SGordon.Ross@Sun.COM  * Helper for ntlm_build_target_info below.
49110023SGordon.Ross@Sun.COM  * Put a name in the NTLMv2 "target info." blob.
49210023SGordon.Ross@Sun.COM  */
49310023SGordon.Ross@Sun.COM static void
smb_put_blob_name(struct mbdata * mbp,char * name,int type)49410023SGordon.Ross@Sun.COM smb_put_blob_name(struct mbdata *mbp, char *name, int type)
49510023SGordon.Ross@Sun.COM {
49610023SGordon.Ross@Sun.COM 	uint16_t *ucs = NULL;
49710023SGordon.Ross@Sun.COM 	int nlen;
49810023SGordon.Ross@Sun.COM 
49910023SGordon.Ross@Sun.COM 	if (name)
50010023SGordon.Ross@Sun.COM 		ucs = convert_utf8_to_leunicode(name);
50110023SGordon.Ross@Sun.COM 	if (ucs)
50210023SGordon.Ross@Sun.COM 		nlen = unicode_strlen(ucs);
50310023SGordon.Ross@Sun.COM 	else
50410023SGordon.Ross@Sun.COM 		nlen = 0;
50510023SGordon.Ross@Sun.COM 
50610023SGordon.Ross@Sun.COM 	nlen <<= 1;	/* length in bytes, without null. */
50710023SGordon.Ross@Sun.COM 
50810023SGordon.Ross@Sun.COM 	mb_put_uint16le(mbp, type);
50910023SGordon.Ross@Sun.COM 	mb_put_uint16le(mbp, nlen);
51011332SGordon.Ross@Sun.COM 	mb_put_mem(mbp, (char *)ucs, nlen, MB_MSYSTEM);
51110023SGordon.Ross@Sun.COM 
51210023SGordon.Ross@Sun.COM 	if (ucs)
51310023SGordon.Ross@Sun.COM 		free(ucs);
51410023SGordon.Ross@Sun.COM }
51510023SGordon.Ross@Sun.COM 
51610023SGordon.Ross@Sun.COM /*
51710023SGordon.Ross@Sun.COM  * Build an NTLMv2 "target info." blob.  When called from NTLMSSP,
51810023SGordon.Ross@Sun.COM  * the list of names comes from the Type 2 message.  Otherwise,
51910023SGordon.Ross@Sun.COM  * we create the name list here.
52010023SGordon.Ross@Sun.COM  */
52110023SGordon.Ross@Sun.COM int
ntlm_build_target_info(struct smb_ctx * ctx,struct mbuf * names,struct mbdata * mbp)52210023SGordon.Ross@Sun.COM ntlm_build_target_info(struct smb_ctx *ctx, struct mbuf *names,
52310023SGordon.Ross@Sun.COM 	struct mbdata *mbp)
52410023SGordon.Ross@Sun.COM {
52510023SGordon.Ross@Sun.COM 	struct timeval now;
52610023SGordon.Ross@Sun.COM 	uint64_t nt_time;
52710023SGordon.Ross@Sun.COM 
52810023SGordon.Ross@Sun.COM 	char *ucdom = NULL;	/* user's domain */
52910023SGordon.Ross@Sun.COM 	int err;
53010023SGordon.Ross@Sun.COM 
53110023SGordon.Ross@Sun.COM 	/* Get mbuf chain for the "target info". */
53211332SGordon.Ross@Sun.COM 	if ((err = mb_init(mbp)) != 0)
53310023SGordon.Ross@Sun.COM 		return (err);
53410023SGordon.Ross@Sun.COM 
53510023SGordon.Ross@Sun.COM 	/*
53610023SGordon.Ross@Sun.COM 	 * Construct the client nonce by getting
53710023SGordon.Ross@Sun.COM 	 * some random data from /dev/urandom
53810023SGordon.Ross@Sun.COM 	 */
53910023SGordon.Ross@Sun.COM 	err = smb_get_urandom(ctx->ct_clnonce, NTLM_CHAL_SZ);
54010023SGordon.Ross@Sun.COM 	if (err)
54110023SGordon.Ross@Sun.COM 		goto out;
54210023SGordon.Ross@Sun.COM 
54310023SGordon.Ross@Sun.COM 	/*
54410023SGordon.Ross@Sun.COM 	 * Get the "NT time" for the target info header.
54510023SGordon.Ross@Sun.COM 	 */
54610023SGordon.Ross@Sun.COM 	(void) gettimeofday(&now, 0);
54710023SGordon.Ross@Sun.COM 	smb_time_local2NT(&now, 0, &nt_time);
54810023SGordon.Ross@Sun.COM 
54910023SGordon.Ross@Sun.COM 	/*
55010023SGordon.Ross@Sun.COM 	 * Build the "target info." block.
55110023SGordon.Ross@Sun.COM 	 *
55210023SGordon.Ross@Sun.COM 	 * Based on information at:
55310023SGordon.Ross@Sun.COM 	 * http://davenport.sourceforge.net/ntlm.html#theNtlmv2Response
55410023SGordon.Ross@Sun.COM 	 *
55510023SGordon.Ross@Sun.COM 	 * First the fixed-size part.
55610023SGordon.Ross@Sun.COM 	 */
55710023SGordon.Ross@Sun.COM 	mb_put_uint32le(mbp, 0x101);	/* Blob signature */
55810023SGordon.Ross@Sun.COM 	mb_put_uint32le(mbp, 0);		/* reserved */
55910023SGordon.Ross@Sun.COM 	mb_put_uint64le(mbp, nt_time);	/* NT time stamp */
56011332SGordon.Ross@Sun.COM 	mb_put_mem(mbp, ctx->ct_clnonce, NTLM_CHAL_SZ, MB_MSYSTEM);
56110023SGordon.Ross@Sun.COM 	mb_put_uint32le(mbp, 0);		/* unknown */
56210023SGordon.Ross@Sun.COM 
56310023SGordon.Ross@Sun.COM 	/*
56410023SGordon.Ross@Sun.COM 	 * Now put the list of names, either from the
56510023SGordon.Ross@Sun.COM 	 * NTLMSSP Type 2 message or composed here.
56610023SGordon.Ross@Sun.COM 	 */
56710023SGordon.Ross@Sun.COM 	if (names) {
56811332SGordon.Ross@Sun.COM 		err = mb_put_mem(mbp, names->m_data, names->m_len, MB_MSYSTEM);
56910023SGordon.Ross@Sun.COM 	} else {
57010023SGordon.Ross@Sun.COM 		/* Get upper-case names. */
57110023SGordon.Ross@Sun.COM 		ucdom  = utf8_str_toupper(ctx->ct_domain);
57210023SGordon.Ross@Sun.COM 		if (ucdom == NULL) {
57310023SGordon.Ross@Sun.COM 			err = ENOMEM;
57410023SGordon.Ross@Sun.COM 			goto out;
57510023SGordon.Ross@Sun.COM 		}
57610023SGordon.Ross@Sun.COM 		smb_put_blob_name(mbp, ucdom, NAMETYPE_DOMAIN_NB);
57710023SGordon.Ross@Sun.COM 		smb_put_blob_name(mbp, NULL, NAMETYPE_EOL);
57810023SGordon.Ross@Sun.COM 		/* OK, that's the whole "target info." blob! */
57910023SGordon.Ross@Sun.COM 	}
58010023SGordon.Ross@Sun.COM 	err = 0;
58110023SGordon.Ross@Sun.COM 
58210023SGordon.Ross@Sun.COM out:
58310023SGordon.Ross@Sun.COM 	free(ucdom);
58410023SGordon.Ross@Sun.COM 	return (err);
58510023SGordon.Ross@Sun.COM }
586