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