xref: /onnv-gate/usr/src/lib/smbsrv/libsmb/common/smb_auth.c (revision 10966:37e5dcdf36d3)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
229832Samw@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
265331Samw #include <strings.h>
275331Samw #include <stdlib.h>
28*10966SJordan.Brown@Sun.COM #include <smbsrv/string.h>
295331Samw #include <smbsrv/libsmb.h>
305331Samw 
315331Samw extern void randomize(char *data, unsigned len);
325331Samw static uint64_t unix_micro_to_nt_time(struct timeval *unix_time);
335331Samw 
345331Samw /*
355331Samw  * smb_auth_qnd_unicode
365331Samw  *
375331Samw  * Quick and dirty unicode conversion!
385331Samw  * Returns the length of dst in bytes.
395331Samw  */
405331Samw int
smb_auth_qnd_unicode(smb_wchar_t * dst,const char * src,int length)41*10966SJordan.Brown@Sun.COM smb_auth_qnd_unicode(smb_wchar_t *dst, const char *src, int length)
425331Samw {
435331Samw 	int i;
44*10966SJordan.Brown@Sun.COM 	unsigned int count;
45*10966SJordan.Brown@Sun.COM 	smb_wchar_t new_char;
465331Samw 
47*10966SJordan.Brown@Sun.COM 	if ((count = oemtoucs(dst, src, length, OEM_CPG_1252)) == 0) {
485331Samw 		for (i = 0; i < length; ++i) {
49*10966SJordan.Brown@Sun.COM 			new_char = (smb_wchar_t)src[i] & 0xff;
505331Samw 			dst[i] = LE_IN16(&new_char);
515331Samw 		}
525331Samw 		dst[i] = 0;
535331Samw 		count = length;
545331Samw 	}
555331Samw 
56*10966SJordan.Brown@Sun.COM 	return (count * sizeof (smb_wchar_t));
575331Samw }
585331Samw 
595331Samw /*
605331Samw  * smb_auth_lmupr
615331Samw  *
625331Samw  * Converts the given LM password to all uppercase.
635331Samw  * The standard strupr cannot
645331Samw  * be used here because lm_pwd doesn't have to be
655331Samw  * nul terminated.
665331Samw  */
675331Samw static void
smb_auth_lmupr(unsigned char * lm_pwd)685331Samw smb_auth_lmupr(unsigned char *lm_pwd)
695331Samw {
705331Samw 	unsigned char *p = lm_pwd;
715331Samw 	int i;
725331Samw 
735331Samw 	for (i = 0; (*p) && (i < SMBAUTH_LM_PWD_SZ); i++) {
74*10966SJordan.Brown@Sun.COM 		if (smb_isascii(*p)) {
75*10966SJordan.Brown@Sun.COM 			*p = smb_toupper(*p);
765331Samw 			p++;
775331Samw 		}
785331Samw 	}
795331Samw }
805331Samw 
815331Samw /*
825331Samw  * smb_auth_lm_hash
835331Samw  *
845331Samw  * Source: Implementing CIFS (Chris Hertel)
855331Samw  *
865331Samw  * 1. The password, as entered by user, is either padded with nulls
875331Samw  *	  or trimmed to 14 bytes.
885331Samw  *    . Note that the 14-byte result string is not handled as a
895331Samw  *	    nul-terminated string.
905331Samw  *	  . The given password is OEM not Unicode
915331Samw  *
925331Samw  * 2. The 14-byte password is converted to all uppercase
935331Samw  *
945331Samw  * 3. The result is used as key to encrypt the KGS magic string to
955331Samw  *    make a 16-byte hash.
965331Samw  */
975331Samw int
smb_auth_lm_hash(const char * password,unsigned char * lm_hash)989832Samw@Sun.COM smb_auth_lm_hash(const char *password, unsigned char *lm_hash)
995331Samw {
1005331Samw 	unsigned char lm_pwd[SMBAUTH_LM_PWD_SZ];
1015331Samw 
1025331Samw 	bzero((void *)lm_pwd, SMBAUTH_LM_PWD_SZ);
1035331Samw 	(void) strncpy((char *)lm_pwd, password, SMBAUTH_LM_PWD_SZ);
1045331Samw 	smb_auth_lmupr(lm_pwd);
1055331Samw 
1065331Samw 	return (smb_auth_DES(lm_hash, SMBAUTH_HASH_SZ, lm_pwd,
1075331Samw 	    SMBAUTH_LM_PWD_SZ, (unsigned char *)SMBAUTH_LM_MAGIC_STR,
1085331Samw 	    sizeof (SMBAUTH_LM_MAGIC_STR)));
1095331Samw }
1105331Samw 
1115331Samw /*
1125331Samw  * smb_auth_lm_response
1135331Samw  *
1145331Samw  * Create a LM response from the given LM hash and challenge.
1155331Samw  *
1165331Samw  * Returns SMBAUTH_FAILURE if any problems occur, SMBAUTH_SUCCESS if
1175331Samw  * all goes well.
1185331Samw  */
1195331Samw static int
smb_auth_lm_response(unsigned char * hash,unsigned char * challenge,int clen,unsigned char * lm_rsp)1205331Samw smb_auth_lm_response(unsigned char *hash,
1215331Samw     unsigned char *challenge, int clen,
1225331Samw     unsigned char *lm_rsp)
1235331Samw {
1245331Samw 	unsigned char S21[21];
1255331Samw 
1265331Samw 	/*
1275331Samw 	 * 14-byte LM Hash should be padded with 5 nul bytes to create
1285331Samw 	 * a 21-byte string to be used in producing LM response
1295331Samw 	 */
1305331Samw 	bzero(&S21[SMBAUTH_HASH_SZ], 5);
1315331Samw 	bcopy(hash, S21, SMBAUTH_HASH_SZ);
1325331Samw 
1335331Samw 	/* padded LM Hash -> LM Response */
1345331Samw 	return (smb_auth_DES(lm_rsp, SMBAUTH_LM_RESP_SZ, S21, 21,
1355331Samw 	    challenge, clen));
1365331Samw }
1375331Samw 
1385331Samw /*
1395331Samw  * smb_auth_ntlm_hash
1405331Samw  *
1415331Samw  * Make NTLM Hash (using MD4) from the given password.
1425331Samw  * The result will contain a 16-byte NTLM hash.
1435331Samw  */
1445331Samw int
smb_auth_ntlm_hash(const char * password,unsigned char * hash)1459832Samw@Sun.COM smb_auth_ntlm_hash(const char *password, unsigned char *hash)
1465331Samw {
147*10966SJordan.Brown@Sun.COM 	smb_wchar_t *unicode_password;
1485331Samw 	int length;
1495331Samw 	int rc;
1505331Samw 
1515331Samw 	if (password == NULL || hash == NULL)
1525331Samw 		return (SMBAUTH_FAILURE);
1535331Samw 
1545331Samw 	length = strlen(password);
155*10966SJordan.Brown@Sun.COM 	unicode_password = (smb_wchar_t *)
156*10966SJordan.Brown@Sun.COM 	    malloc((length + 1) * sizeof (smb_wchar_t));
1575331Samw 
1585331Samw 	if (unicode_password == NULL)
1595331Samw 		return (SMBAUTH_FAILURE);
1605331Samw 
1615331Samw 	length = smb_auth_qnd_unicode(unicode_password, password, length);
1625331Samw 	rc = smb_auth_md4(hash, (unsigned char *)unicode_password, length);
1635331Samw 
1645331Samw 	free(unicode_password);
1655331Samw 	return (rc);
1665331Samw }
1675331Samw 
1685331Samw /*
1695331Samw  * smb_auth_ntlm_response
1705331Samw  *
1715331Samw  * Make LM/NTLM response from the given LM/NTLM Hash and given
1725331Samw  * challenge.
1735331Samw  */
1745331Samw static int
smb_auth_ntlm_response(unsigned char * hash,unsigned char * challenge,int clen,unsigned char * ntlm_rsp)1755331Samw smb_auth_ntlm_response(unsigned char *hash,
1765331Samw     unsigned char *challenge, int clen,
1775331Samw     unsigned char *ntlm_rsp)
1785331Samw {
1795331Samw 	unsigned char S21[21];
1805331Samw 
1815331Samw 	bcopy(hash, S21, SMBAUTH_HASH_SZ);
1825331Samw 	bzero(&S21[SMBAUTH_HASH_SZ], 5);
1835331Samw 	if (smb_auth_DES((unsigned char *)ntlm_rsp, SMBAUTH_LM_RESP_SZ,
1845331Samw 	    S21, 21, challenge, clen) == SMBAUTH_FAILURE)
1855331Samw 		return (0);
1865331Samw 	return (SMBAUTH_LM_RESP_SZ);
1875331Samw }
1885331Samw 
1895331Samw /*
1905331Samw  * smb_auth_gen_data_blob
1915331Samw  *
1925331Samw  * Fill the NTLMv2 data blob structure with information as described in
1935331Samw  * "Implementing CIFS, The Common Internet File System". (pg. 282)
1945331Samw  */
1955331Samw static void
smb_auth_gen_data_blob(smb_auth_data_blob_t * blob,char * ntdomain)1965331Samw smb_auth_gen_data_blob(smb_auth_data_blob_t *blob, char *ntdomain)
1975331Samw {
1985331Samw 	struct timeval now;
1995331Samw 
2005331Samw 	(void) memset(blob->ndb_signature, 1, 2);
2015331Samw 	(void) memset(&blob->ndb_signature[2], 0, 2);
2025331Samw 	(void) memset(blob->ndb_reserved, 0, sizeof (blob->ndb_reserved));
2035331Samw 
2045331Samw 	(void) gettimeofday(&now, 0);
2055331Samw 	blob->ndb_timestamp = unix_micro_to_nt_time(&now);
2065331Samw 	randomize((char *)blob->ndb_clnt_challenge,
2075331Samw 	    SMBAUTH_V2_CLNT_CHALLENGE_SZ);
2085331Samw 	(void) memset(blob->ndb_unknown, 0, sizeof (blob->ndb_unknown));
2095331Samw 	blob->ndb_names[0].nne_len = smb_auth_qnd_unicode(
2105331Samw 	    blob->ndb_names[0].nne_name, ntdomain, strlen(ntdomain));
2115331Samw 	blob->ndb_names[0].nne_type = SMBAUTH_NAME_TYPE_DOMAIN_NETBIOS;
2125331Samw 	blob->ndb_names[1].nne_len = 0;
2135331Samw 	blob->ndb_names[1].nne_type = SMBAUTH_NAME_TYPE_LIST_END;
2145331Samw 	*blob->ndb_names[1].nne_name = 0;
2155331Samw 	(void) memset(blob->ndb_unknown2, 0, sizeof (blob->ndb_unknown2));
2165331Samw }
2175331Samw 
2185331Samw /*
2195331Samw  * smb_auth_memcpy
2205331Samw  *
2215331Samw  * It increments the pointer to the destination buffer for the easy of
2225331Samw  * concatenation.
2235331Samw  */
2245331Samw static void
smb_auth_memcpy(unsigned char ** dstbuf,unsigned char * srcbuf,int srcbuf_len)2255331Samw smb_auth_memcpy(unsigned char **dstbuf,
2265331Samw 	unsigned char *srcbuf,
2275331Samw 	int srcbuf_len)
2285331Samw {
2295331Samw 	(void) memcpy(*dstbuf, srcbuf, srcbuf_len);
2305331Samw 	*dstbuf += srcbuf_len;
2315331Samw }
2325331Samw 
2335331Samw /*
2345331Samw  * smb_auth_blob_to_string
2355331Samw  *
2365331Samw  * Prepare the data blob string which will be used in NTLMv2 response
2375331Samw  * generation.
2385331Samw  *
2395331Samw  * Assumption: Caller must allocate big enough buffer to prevent buffer
2405331Samw  * overrun.
2415331Samw  *
2425331Samw  * Returns the len of the data blob string.
2435331Samw  */
2445331Samw static int
smb_auth_blob_to_string(smb_auth_data_blob_t * blob,unsigned char * data_blob)2455331Samw smb_auth_blob_to_string(smb_auth_data_blob_t *blob, unsigned char *data_blob)
2465331Samw {
2475331Samw 	unsigned char *bufp = data_blob;
2485331Samw 
2495331Samw 	smb_auth_memcpy(&bufp, blob->ndb_signature,
2505331Samw 	    sizeof (blob->ndb_signature));
2515331Samw 	smb_auth_memcpy(&bufp, blob->ndb_reserved,
2525331Samw 	    sizeof (blob->ndb_reserved));
2535331Samw 	smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_timestamp,
2545331Samw 	    sizeof (blob->ndb_timestamp));
2555331Samw 	smb_auth_memcpy(&bufp, blob->ndb_clnt_challenge,
2565331Samw 	    SMBAUTH_V2_CLNT_CHALLENGE_SZ);
2575331Samw 	smb_auth_memcpy(&bufp, blob->ndb_unknown, sizeof (blob->ndb_unknown));
2585331Samw 	smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[0].nne_type,
2595331Samw 	    sizeof (blob->ndb_names[0].nne_type));
2605331Samw 	smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[0].nne_len,
2615331Samw 	    sizeof (blob->ndb_names[0].nne_len));
2625331Samw 	smb_auth_memcpy(&bufp, (unsigned char *)blob->ndb_names[0].nne_name,
2635331Samw 	    blob->ndb_names[0].nne_len);
2645331Samw 	smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[1].nne_type,
2655331Samw 	    sizeof (blob->ndb_names[1].nne_type));
2665331Samw 	smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[1].nne_len,
2675331Samw 	    sizeof (blob->ndb_names[1].nne_len));
2685331Samw 	smb_auth_memcpy(&bufp, blob->ndb_unknown2, sizeof (blob->ndb_unknown2));
2695331Samw 
2705331Samw 	/*LINTED E_PTRDIFF_OVERFLOW*/
2715331Samw 	return (bufp - data_blob);
2725331Samw }
2735331Samw 
2745331Samw /*
2755331Samw  * smb_auth_ntlmv2_hash
2765331Samw  *
2775331Samw  * The NTLM v2 hash will be created from the given NTLM hash, username,
2785331Samw  * and the NETBIOS name of the domain.
2795331Samw  *
2805331Samw  * The NTLMv2 hash will be returned via the ntlmv2_hash parameter which
2815331Samw  * will be used in the calculation of the NTLMv2 and LMv2 responses.
2825331Samw  */
2836600Sas200622 int
smb_auth_ntlmv2_hash(unsigned char * ntlm_hash,char * username,char * ntdomain,unsigned char * ntlmv2_hash)2845331Samw smb_auth_ntlmv2_hash(unsigned char *ntlm_hash,
2857348SJose.Borrego@Sun.COM     char *username,
2867348SJose.Borrego@Sun.COM     char *ntdomain,
2877348SJose.Borrego@Sun.COM     unsigned char *ntlmv2_hash)
2885331Samw {
289*10966SJordan.Brown@Sun.COM 	smb_wchar_t *data;
2905331Samw 	int data_len;
2915331Samw 	unsigned char *buf;
2925331Samw 	int rc;
2935331Samw 
2945331Samw 	if (username == NULL || ntdomain == NULL)
2955331Samw 		return (SMBAUTH_FAILURE);
2965331Samw 
297*10966SJordan.Brown@Sun.COM 	(void) smb_strupr(username);
2985331Samw 
2995331Samw 	data_len = strlen(username) + strlen(ntdomain);
3005331Samw 	buf = (unsigned char *)malloc((data_len + 1) * sizeof (char));
3015331Samw 	if (buf == NULL)
3025331Samw 		return (SMBAUTH_FAILURE);
3035331Samw 
3045331Samw 	(void) snprintf((char *)buf, data_len + 1, "%s%s", username, ntdomain);
305*10966SJordan.Brown@Sun.COM 	data = (smb_wchar_t *)malloc((data_len + 1) * sizeof (smb_wchar_t));
3065331Samw 	if (data == NULL) {
3075331Samw 		free(buf);
3085331Samw 		return (SMBAUTH_FAILURE);
3095331Samw 	}
3105331Samw 
3115331Samw 	data_len = smb_auth_qnd_unicode(data, (char *)buf, data_len);
3125331Samw 	rc = SMBAUTH_HMACT64((unsigned char *)data, data_len, ntlm_hash,
3135331Samw 	    SMBAUTH_HASH_SZ, ntlmv2_hash);
3145331Samw 
3155331Samw 	free(buf);
3165331Samw 	free(data);
3175331Samw 	return (rc);
3185331Samw }
3195331Samw 
3205331Samw /*
3215331Samw  * smb_auth_v2_response
3225331Samw  *
3235331Samw  * Caculates either the LMv2 or NTLMv2 response.
3245331Samw  *
3255331Samw  * Same algorithm is used for calculating both LMv2 or NTLMv2 responses.
3265331Samw  * This routine will return NTLMv2 response if the data blob information
3275331Samw  * is passed in as the clnt_data. Otherwise, it will return LMv2 response
3285331Samw  * with the 8-byte client challenge(a.k.a blip) as the clnt_data.
3295331Samw  *
3305331Samw  * (LM/NTLM)v2 response is the hmac-md5 hash of the specified data
3315331Samw  * (server challenge + NTLMv2 data blob or LMv2 client challenge)
3325331Samw  * using the NTLMv2 hash as the key.
3335331Samw  *
3345331Samw  * Returns the size of the corresponding v2 response upon success.
3355331Samw  * Otherwise, returns -1 on error.
3365331Samw  */
3375331Samw static int
smb_auth_v2_response(unsigned char * hash,unsigned char * srv_challenge,int slen,unsigned char * clnt_data,int clen,unsigned char * v2_rsp)3385331Samw smb_auth_v2_response(
3395331Samw 	unsigned char *hash,
3405331Samw 	unsigned char *srv_challenge, int slen,
3415331Samw 	unsigned char *clnt_data, int clen,
3425331Samw 	unsigned char *v2_rsp)
3435331Samw {
3445331Samw 	unsigned char *hmac_data;
3455331Samw 
3465331Samw 	hmac_data = (unsigned char *)malloc((slen + clen) * sizeof (char));
3475331Samw 	if (!hmac_data) {
3485331Samw 		return (-1);
3495331Samw 	}
3505331Samw 
3515331Samw 	(void) memcpy(hmac_data, srv_challenge, slen);
3525331Samw 	(void) memcpy(&hmac_data[slen], clnt_data, clen);
3535331Samw 	if (SMBAUTH_HMACT64(hmac_data, slen + clen, (unsigned char *)hash,
3545331Samw 	    SMBAUTH_HASH_SZ, (unsigned char *)v2_rsp) != SMBAUTH_SUCCESS)
3555331Samw 		return (-1);
3565331Samw 	(void) memcpy(&v2_rsp[SMBAUTH_HASH_SZ], clnt_data, clen);
3575331Samw 
3585331Samw 	free(hmac_data);
3595331Samw 	return (SMBAUTH_HASH_SZ + clen);
3605331Samw }
3615331Samw 
3625331Samw /*
3635331Samw  * smb_auth_set_info
3645331Samw  *
3655331Samw  * Fill the smb_auth_info instance with either NTLM or NTLMv2 related
3665331Samw  * authentication information based on the LMCompatibilityLevel.
3675331Samw  *
3685331Samw  * If the LMCompatibilityLevel equals 2, the SMB Redirector will perform
3695331Samw  * NTLM challenge/response authentication which requires the NTLM hash and
3705331Samw  * NTLM response.
3715331Samw  *
3725331Samw  * If the LMCompatibilityLevel is 3 or above, the SMB Redirector will
3735331Samw  * perfrom NTLMv2 challenge/response authenticatoin which requires the
3745331Samw  * NTLM hash, NTLMv2 hash, NTLMv2 response and LMv2 response.
3755331Samw  *
3765331Samw  * Returns -1 on error. Otherwise, returns 0 upon success.
3775331Samw  */
3785331Samw int
smb_auth_set_info(char * username,char * password,unsigned char * ntlm_hash,char * domain,unsigned char * srv_challenge_key,int srv_challenge_len,int lmcomp_lvl,smb_auth_info_t * auth)3795331Samw smb_auth_set_info(char *username,
3805331Samw 	char *password,
3815331Samw 	unsigned char *ntlm_hash,
3825331Samw 	char *domain,
3835331Samw 	unsigned char *srv_challenge_key,
3845331Samw 	int srv_challenge_len,
3855331Samw 	int lmcomp_lvl,
3865331Samw 	smb_auth_info_t *auth)
3875331Samw {
3885331Samw 	unsigned short blob_len;
3895331Samw 	unsigned char blob_buf[SMBAUTH_BLOB_MAXLEN];
3905331Samw 	int rc;
3915772Sas200622 	char *uppercase_dom;
3925331Samw 
3935331Samw 	auth->lmcompatibility_lvl = lmcomp_lvl;
3945331Samw 	if (lmcomp_lvl == 2) {
3955331Samw 		auth->ci_len = 0;
3965331Samw 		*auth->ci = 0;
3975331Samw 		if (!ntlm_hash) {
3985331Samw 			if (smb_auth_ntlm_hash(password, auth->hash) !=
3995331Samw 			    SMBAUTH_SUCCESS)
4005331Samw 				return (-1);
4015331Samw 		} else {
4025331Samw 			(void) memcpy(auth->hash, ntlm_hash, SMBAUTH_HASH_SZ);
4035331Samw 		}
4045331Samw 
4055331Samw 		auth->cs_len = smb_auth_ntlm_response(auth->hash,
4065331Samw 		    srv_challenge_key, srv_challenge_len, auth->cs);
4075331Samw 	} else {
4085331Samw 		if (!ntlm_hash) {
4095331Samw 			if (smb_auth_ntlm_hash(password, auth->hash) !=
4105331Samw 			    SMBAUTH_SUCCESS)
4115331Samw 				return (-1);
4125331Samw 		} else {
4135331Samw 			(void) memcpy(auth->hash, ntlm_hash, SMBAUTH_HASH_SZ);
4145331Samw 		}
4155331Samw 
4165772Sas200622 		if (!domain)
4175772Sas200622 			return (-1);
4185772Sas200622 
4195772Sas200622 		if ((uppercase_dom = strdup(domain)) == NULL)
4205331Samw 			return (-1);
4215331Samw 
422*10966SJordan.Brown@Sun.COM 		(void) smb_strupr(uppercase_dom);
4235772Sas200622 
4245772Sas200622 		if (smb_auth_ntlmv2_hash(auth->hash, username,
4255772Sas200622 		    uppercase_dom, auth->hash_v2) != SMBAUTH_SUCCESS) {
4265772Sas200622 			free(uppercase_dom);
4275772Sas200622 			return (-1);
4285772Sas200622 		}
4295772Sas200622 
4305331Samw 		/* generate data blob */
4315772Sas200622 		smb_auth_gen_data_blob(&auth->data_blob, uppercase_dom);
4325772Sas200622 		free(uppercase_dom);
4335331Samw 		blob_len = smb_auth_blob_to_string(&auth->data_blob, blob_buf);
4345331Samw 
4355331Samw 		/* generate NTLMv2 response */
4365331Samw 		rc = smb_auth_v2_response(auth->hash_v2, srv_challenge_key,
4375331Samw 		    srv_challenge_len, blob_buf, blob_len, auth->cs);
4385331Samw 
4395331Samw 		if (rc < 0)
4405331Samw 			return (-1);
4415331Samw 
4425331Samw 		auth->cs_len = rc;
4435331Samw 
4445331Samw 		/* generate LMv2 response */
4455331Samw 		rc = smb_auth_v2_response(auth->hash_v2, srv_challenge_key,
4465331Samw 		    srv_challenge_len, auth->data_blob.ndb_clnt_challenge,
4475331Samw 		    SMBAUTH_V2_CLNT_CHALLENGE_SZ, auth->ci);
4485331Samw 
4495331Samw 		if (rc < 0)
4505331Samw 			return (-1);
4515331Samw 
4525331Samw 		auth->ci_len = rc;
4535331Samw 	}
4545331Samw 
4555331Samw 	return (0);
4565331Samw }
4575331Samw 
4585331Samw /*
4595331Samw  * smb_auth_gen_session_key
4605331Samw  *
4615331Samw  * Generate the NTLM user session key if LMCompatibilityLevel is 2 or
4625331Samw  * NTLMv2 user session key if LMCompatibilityLevel is 3 or above.
4635331Samw  *
4645331Samw  * NTLM_Session_Key = MD4(NTLM_Hash);
4655331Samw  *
4665331Samw  * NTLMv2_Session_Key = HMAC_MD5(NTLMv2Hash, 16, NTLMv2_HMAC, 16)
4675331Samw  *
4685331Samw  * Prior to calling this function, the auth instance should be set
4695331Samw  * via smb_auth_set_info().
4705331Samw  *
4715331Samw  * Returns the appropriate session key.
4725331Samw  */
4735331Samw int
smb_auth_gen_session_key(smb_auth_info_t * auth,unsigned char * session_key)4745331Samw smb_auth_gen_session_key(smb_auth_info_t *auth, unsigned char *session_key)
4755331Samw {
4765331Samw 	int rc;
4775331Samw 
4785331Samw 	if (auth->lmcompatibility_lvl == 2)
4795331Samw 		rc = smb_auth_md4(session_key, auth->hash, SMBAUTH_HASH_SZ);
4805331Samw 	else
4815331Samw 		rc = SMBAUTH_HMACT64((unsigned char *)auth->cs,
4825331Samw 		    SMBAUTH_HASH_SZ, (unsigned char *)auth->hash_v2,
4835331Samw 		    SMBAUTH_SESSION_KEY_SZ, session_key);
4845331Samw 
4855331Samw 	return (rc);
4865331Samw }
4875331Samw 
4885331Samw /* 100's of ns between 1/1/1970 and 1/1/1601 */
4895331Samw #define	NT_TIME_BIAS    (134774LL * 24LL * 60LL * 60LL * 10000000LL)
4905331Samw 
4915331Samw static uint64_t
unix_micro_to_nt_time(struct timeval * unix_time)4925331Samw unix_micro_to_nt_time(struct timeval *unix_time)
4935331Samw {
4945331Samw 	uint64_t nt_time;
4955331Samw 
4965331Samw 	nt_time = unix_time->tv_sec;
4975331Samw 	nt_time *= 10000000;  /* seconds to 100ns */
4985331Samw 	nt_time += unix_time->tv_usec * 10;
4995331Samw 	return (nt_time + NT_TIME_BIAS);
5005331Samw }
5015331Samw 
5025331Samw static boolean_t
smb_lm_password_ok(unsigned char * challenge,uint32_t clen,unsigned char * lm_hash,unsigned char * passwd)5035331Samw smb_lm_password_ok(
5045331Samw     unsigned char *challenge,
5055331Samw     uint32_t clen,
5065331Samw     unsigned char *lm_hash,
5075331Samw     unsigned char *passwd)
5085331Samw {
5095331Samw 	unsigned char lm_resp[SMBAUTH_LM_RESP_SZ];
5105331Samw 	int rc;
5115331Samw 
5125331Samw 	rc = smb_auth_lm_response(lm_hash, challenge, clen, lm_resp);
5135331Samw 	if (rc != SMBAUTH_SUCCESS)
5145331Samw 		return (B_FALSE);
5155331Samw 
5165331Samw 	return (bcmp(lm_resp, passwd, SMBAUTH_LM_RESP_SZ) == 0);
5175331Samw }
5185331Samw 
5195331Samw static boolean_t
smb_ntlm_password_ok(unsigned char * challenge,uint32_t clen,unsigned char * ntlm_hash,unsigned char * passwd,unsigned char * session_key)5205331Samw smb_ntlm_password_ok(
5215331Samw     unsigned char *challenge,
5225331Samw     uint32_t clen,
5235331Samw     unsigned char *ntlm_hash,
5247348SJose.Borrego@Sun.COM     unsigned char *passwd,
5257348SJose.Borrego@Sun.COM     unsigned char *session_key)
5265331Samw {
5275331Samw 	unsigned char ntlm_resp[SMBAUTH_LM_RESP_SZ];
5285331Samw 	int rc;
5297348SJose.Borrego@Sun.COM 	boolean_t ok;
5305331Samw 
5315331Samw 	rc = smb_auth_ntlm_response(ntlm_hash, challenge, clen, ntlm_resp);
5325331Samw 	if (rc != SMBAUTH_LM_RESP_SZ)
5335331Samw 		return (B_FALSE);
5345331Samw 
5357348SJose.Borrego@Sun.COM 	ok = (bcmp(ntlm_resp, passwd, SMBAUTH_LM_RESP_SZ) == 0);
5367348SJose.Borrego@Sun.COM 	if (ok && (session_key)) {
5377348SJose.Borrego@Sun.COM 		rc = smb_auth_md4(session_key, ntlm_hash, SMBAUTH_HASH_SZ);
5387348SJose.Borrego@Sun.COM 		if (rc != SMBAUTH_SUCCESS)
5397348SJose.Borrego@Sun.COM 			ok = B_FALSE;
5407348SJose.Borrego@Sun.COM 	}
5417348SJose.Borrego@Sun.COM 	return (ok);
5425331Samw }
5435331Samw 
5445331Samw static boolean_t
smb_ntlmv2_password_ok(unsigned char * challenge,uint32_t clen,unsigned char * ntlm_hash,unsigned char * passwd,int pwdlen,char * domain,char * username,uchar_t * session_key)5455331Samw smb_ntlmv2_password_ok(
5465331Samw     unsigned char *challenge,
5475331Samw     uint32_t clen,
5485331Samw     unsigned char *ntlm_hash,
5495331Samw     unsigned char *passwd,
5505331Samw     int pwdlen,
5515772Sas200622     char *domain,
5527348SJose.Borrego@Sun.COM     char *username,
5537348SJose.Borrego@Sun.COM     uchar_t *session_key)
5545331Samw {
5555331Samw 	unsigned char *clnt_blob;
5565331Samw 	int clnt_blob_len;
5575331Samw 	unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ];
5585331Samw 	unsigned char *ntlmv2_resp;
5595772Sas200622 	boolean_t ok = B_FALSE;
5605772Sas200622 	char *dest[3];
5615772Sas200622 	int i;
5627348SJose.Borrego@Sun.COM 	int rc;
5635331Samw 
5645331Samw 	clnt_blob_len = pwdlen - SMBAUTH_HASH_SZ;
5655331Samw 	clnt_blob = &passwd[SMBAUTH_HASH_SZ];
5665772Sas200622 	dest[0] = domain;
5675772Sas200622 	if ((dest[1] = strdup(domain)) == NULL)
5685772Sas200622 		return (B_FALSE);
569*10966SJordan.Brown@Sun.COM 	(void) smb_strupr(dest[1]);
5705772Sas200622 	dest[2] = "";
5715331Samw 
5725331Samw 	/*
5735331Samw 	 * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS"
5745331Samw 	 *
5755772Sas200622 	 * The NTLMv2 Hash is created from:
5765331Samw 	 * - NTLM hash
5775331Samw 	 * - user's username, and
5785331Samw 	 * - the name of the logon destination(i.e. the NetBIOS name of either
5795772Sas200622 	 *   the SMB server or NT Domain against which the user is trying to
5805331Samw 	 *   authenticate.
5815331Samw 	 *
5825772Sas200622 	 * Experiments show this is not exactly the case.
5835772Sas200622 	 * For Windows Server 2003, the domain name needs to be included and
5845772Sas200622 	 * converted to uppercase. For Vista, the domain name needs to be
5855772Sas200622 	 * included also, but leave the case alone.  And in some cases it needs
5865772Sas200622 	 * to be empty. All three variants are tried here.
5875331Samw 	 */
5885772Sas200622 
5895772Sas200622 	ntlmv2_resp = (unsigned char *)malloc(SMBAUTH_HASH_SZ + clnt_blob_len);
5905772Sas200622 	if (ntlmv2_resp == NULL) {
5915772Sas200622 		free(dest[1]);
5925331Samw 		return (B_FALSE);
5935331Samw 	}
5945331Samw 
5955772Sas200622 	for (i = 0; i < (sizeof (dest) / sizeof (char *)); i++) {
5965772Sas200622 		if (smb_auth_ntlmv2_hash(ntlm_hash, username, dest[i],
5975772Sas200622 		    ntlmv2_hash) != SMBAUTH_SUCCESS)
5985772Sas200622 			break;
5995331Samw 
6005772Sas200622 		if (smb_auth_v2_response(ntlmv2_hash, challenge,
6015772Sas200622 		    clen, clnt_blob, clnt_blob_len, ntlmv2_resp) < 0)
6025772Sas200622 			break;
6035772Sas200622 
6045772Sas200622 		ok = (bcmp(passwd, ntlmv2_resp, pwdlen) == 0);
6057348SJose.Borrego@Sun.COM 		if (ok && session_key) {
6067348SJose.Borrego@Sun.COM 			rc = SMBAUTH_HMACT64(ntlmv2_resp,
6077348SJose.Borrego@Sun.COM 			    SMBAUTH_HASH_SZ, ntlmv2_hash,
6087348SJose.Borrego@Sun.COM 			    SMBAUTH_SESSION_KEY_SZ, session_key);
6097348SJose.Borrego@Sun.COM 			if (rc != SMBAUTH_SUCCESS) {
6107348SJose.Borrego@Sun.COM 				ok = B_FALSE;
6117348SJose.Borrego@Sun.COM 			}
6125772Sas200622 			break;
6137348SJose.Borrego@Sun.COM 		}
6145331Samw 	}
6155331Samw 
6165772Sas200622 	free(dest[1]);
6175331Samw 	free(ntlmv2_resp);
6185331Samw 	return (ok);
6195331Samw }
6205331Samw 
6215772Sas200622 static boolean_t
smb_lmv2_password_ok(unsigned char * challenge,uint32_t clen,unsigned char * ntlm_hash,unsigned char * passwd,char * domain,char * username)6225331Samw smb_lmv2_password_ok(
6235331Samw     unsigned char *challenge,
6245331Samw     uint32_t clen,
6255331Samw     unsigned char *ntlm_hash,
6265331Samw     unsigned char *passwd,
6275772Sas200622     char *domain,
6285331Samw     char *username)
6295331Samw {
6305331Samw 	unsigned char *clnt_challenge;
6315331Samw 	unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ];
6325331Samw 	unsigned char lmv2_resp[SMBAUTH_LM_RESP_SZ];
6335772Sas200622 	boolean_t ok = B_FALSE;
6345772Sas200622 	char *dest[3];
6355772Sas200622 	int i;
6365331Samw 
6375331Samw 	clnt_challenge = &passwd[SMBAUTH_HASH_SZ];
6385772Sas200622 	dest[0] = domain;
6395772Sas200622 	if ((dest[1] = strdup(domain)) == NULL)
6405772Sas200622 		return (B_FALSE);
641*10966SJordan.Brown@Sun.COM 	(void) smb_strupr(dest[1]);
6425772Sas200622 	dest[2] = "";
6435331Samw 
6445331Samw 	/*
6455331Samw 	 * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS"
6465331Samw 	 *
6475331Samw 	 * The NTLMv2 Hash is created from:
6485331Samw 	 * - NTLM hash
6495331Samw 	 * - user's username, and
6505331Samw 	 * - the name of the logon destination(i.e. the NetBIOS name of either
6515331Samw 	 *   the SMB server or NT Domain against which the suer is trying to
6525331Samw 	 *   authenticate.
6535331Samw 	 *
6545772Sas200622 	 * Experiments show this is not exactly the case.
6555772Sas200622 	 * For Windows Server 2003, the domain name needs to be included and
6565772Sas200622 	 * converted to uppercase. For Vista, the domain name needs to be
6575772Sas200622 	 * included also, but leave the case alone.  And in some cases it needs
6585772Sas200622 	 * to be empty. All three variants are tried here.
6595331Samw 	 */
6605772Sas200622 
6615772Sas200622 	for (i = 0; i < (sizeof (dest) / sizeof (char *)); i++) {
6625772Sas200622 		if (smb_auth_ntlmv2_hash(ntlm_hash, username, dest[i],
6635772Sas200622 		    ntlmv2_hash) != SMBAUTH_SUCCESS)
6645772Sas200622 			break;
6655772Sas200622 
6665772Sas200622 		if (smb_auth_v2_response(ntlmv2_hash, challenge,
6675772Sas200622 		    clen, clnt_challenge, SMBAUTH_V2_CLNT_CHALLENGE_SZ,
6685772Sas200622 		    lmv2_resp) < 0)
6695772Sas200622 			break;
6705772Sas200622 
6715772Sas200622 		ok = (bcmp(passwd, lmv2_resp, SMBAUTH_LM_RESP_SZ) == 0);
6727348SJose.Borrego@Sun.COM 		if (ok)
6735772Sas200622 			break;
6745331Samw 	}
6755331Samw 
6765772Sas200622 	free(dest[1]);
6775772Sas200622 	return (ok);
6785331Samw }
6795331Samw 
6805331Samw /*
6815331Samw  * smb_auth_validate_lm
6825331Samw  *
6835331Samw  * Validates given LM/LMv2 client response, passed in passwd arg, against
6845331Samw  * stored user's password, passed in smbpw
6855331Samw  *
6865331Samw  * If LM level <=3 server accepts LM responses, otherwise LMv2
6875331Samw  */
6885331Samw boolean_t
smb_auth_validate_lm(unsigned char * challenge,uint32_t clen,smb_passwd_t * smbpw,unsigned char * passwd,int pwdlen,char * domain,char * username)6895331Samw smb_auth_validate_lm(
6905331Samw     unsigned char *challenge,
6915331Samw     uint32_t clen,
6925331Samw     smb_passwd_t *smbpw,
6935331Samw     unsigned char *passwd,
6945331Samw     int pwdlen,
6955772Sas200622     char *domain,
6965331Samw     char *username)
6975331Samw {
6985331Samw 	boolean_t ok = B_FALSE;
6995772Sas200622 	int64_t lmlevel;
7005331Samw 
7015331Samw 	if (pwdlen != SMBAUTH_LM_RESP_SZ)
7025331Samw 		return (B_FALSE);
7035331Samw 
7045772Sas200622 	if (smb_config_getnum(SMB_CI_LM_LEVEL, &lmlevel) != SMBD_SMF_OK)
7055772Sas200622 		return (B_FALSE);
7065331Samw 
7075331Samw 	if (lmlevel <= 3) {
7085331Samw 		ok = smb_lm_password_ok(challenge, clen, smbpw->pw_lmhash,
7095331Samw 		    passwd);
7105331Samw 	}
7115331Samw 
7125331Samw 	if (!ok)
7135331Samw 		ok = smb_lmv2_password_ok(challenge, clen, smbpw->pw_nthash,
7145772Sas200622 		    passwd, domain, username);
7155331Samw 
7165331Samw 	return (ok);
7175331Samw }
7185331Samw 
7195331Samw /*
7205331Samw  * smb_auth_validate_nt
7215331Samw  *
7225331Samw  * Validates given NTLM/NTLMv2 client response, passed in passwd arg, against
7235331Samw  * stored user's password, passed in smbpw
7245331Samw  *
7255331Samw  * If LM level <=4 server accepts NTLM/NTLMv2 responses, otherwise only NTLMv2
7265331Samw  */
7275331Samw boolean_t
smb_auth_validate_nt(unsigned char * challenge,uint32_t clen,smb_passwd_t * smbpw,unsigned char * passwd,int pwdlen,char * domain,char * username,uchar_t * session_key)7285331Samw smb_auth_validate_nt(
7295331Samw     unsigned char *challenge,
7305331Samw     uint32_t clen,
7315331Samw     smb_passwd_t *smbpw,
7325331Samw     unsigned char *passwd,
7335331Samw     int pwdlen,
7345772Sas200622     char *domain,
7357348SJose.Borrego@Sun.COM     char *username,
7367348SJose.Borrego@Sun.COM     uchar_t *session_key)
7375331Samw {
7385772Sas200622 	int64_t lmlevel;
7395331Samw 	boolean_t ok;
7405331Samw 
7415772Sas200622 	if (smb_config_getnum(SMB_CI_LM_LEVEL, &lmlevel) != SMBD_SMF_OK)
7425772Sas200622 		return (B_FALSE);
7435331Samw 
7445331Samw 	if ((lmlevel == 5) && (pwdlen <= SMBAUTH_LM_RESP_SZ))
7455331Samw 		return (B_FALSE);
7465331Samw 
7475331Samw 	if (pwdlen > SMBAUTH_LM_RESP_SZ)
7485331Samw 		ok = smb_ntlmv2_password_ok(challenge, clen,
7497348SJose.Borrego@Sun.COM 		    smbpw->pw_nthash, passwd, pwdlen,
7507348SJose.Borrego@Sun.COM 		    domain, username, session_key);
7515331Samw 	else
7525331Samw 		ok = smb_ntlm_password_ok(challenge, clen,
7537348SJose.Borrego@Sun.COM 		    smbpw->pw_nthash, passwd, session_key);
7545331Samw 
7555331Samw 	return (ok);
7565331Samw }
757