xref: /titanic_51/usr/src/lib/smbsrv/libsmb/common/smb_auth.c (revision 613b28719c10e84c1202c1045df44d77767de21d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"@(#)smb_auth.c	1.5	08/07/08 SMI"
27 
28 #include <strings.h>
29 #include <stdlib.h>
30 #include <smbsrv/codepage.h>
31 #include <smbsrv/oem.h>
32 #include <smbsrv/ctype.h>
33 #include <smbsrv/crypt.h>
34 #include <smbsrv/libsmb.h>
35 
36 extern void randomize(char *data, unsigned len);
37 static uint64_t unix_micro_to_nt_time(struct timeval *unix_time);
38 
39 /*
40  * smb_auth_qnd_unicode
41  *
42  * Quick and dirty unicode conversion!
43  * Returns the length of dst in bytes.
44  */
45 int
46 smb_auth_qnd_unicode(mts_wchar_t *dst, char *src, int length)
47 {
48 	int i;
49 
50 	unsigned int cpid = oem_get_telnet_cpid();
51 	unsigned int count;
52 	mts_wchar_t new_char;
53 
54 	if ((count = oemstounicodes(dst, src, length, cpid)) == 0) {
55 		for (i = 0; i < length; ++i) {
56 			new_char = (mts_wchar_t)src[i] & 0xff;
57 			dst[i] = LE_IN16(&new_char);
58 		}
59 		dst[i] = 0;
60 		count = length;
61 	}
62 
63 	return (count * sizeof (mts_wchar_t));
64 }
65 
66 /*
67  * smb_auth_lmupr
68  *
69  * Converts the given LM password to all uppercase.
70  * The standard strupr cannot
71  * be used here because lm_pwd doesn't have to be
72  * nul terminated.
73  */
74 static void
75 smb_auth_lmupr(unsigned char *lm_pwd)
76 {
77 	unsigned char *p = lm_pwd;
78 	int i;
79 
80 	for (i = 0; (*p) && (i < SMBAUTH_LM_PWD_SZ); i++) {
81 		if (mts_isascii(*p)) {
82 			*p = codepage_toupper(*p);
83 			p++;
84 		}
85 	}
86 }
87 
88 /*
89  * smb_auth_lm_hash
90  *
91  * Source: Implementing CIFS (Chris Hertel)
92  *
93  * 1. The password, as entered by user, is either padded with nulls
94  *	  or trimmed to 14 bytes.
95  *    . Note that the 14-byte result string is not handled as a
96  *	    nul-terminated string.
97  *	  . The given password is OEM not Unicode
98  *
99  * 2. The 14-byte password is converted to all uppercase
100  *
101  * 3. The result is used as key to encrypt the KGS magic string to
102  *    make a 16-byte hash.
103  */
104 int
105 smb_auth_lm_hash(char *password, unsigned char *lm_hash)
106 {
107 	unsigned char lm_pwd[SMBAUTH_LM_PWD_SZ];
108 
109 	bzero((void *)lm_pwd, SMBAUTH_LM_PWD_SZ);
110 	(void) strncpy((char *)lm_pwd, password, SMBAUTH_LM_PWD_SZ);
111 	smb_auth_lmupr(lm_pwd);
112 
113 	return (smb_auth_DES(lm_hash, SMBAUTH_HASH_SZ, lm_pwd,
114 	    SMBAUTH_LM_PWD_SZ, (unsigned char *)SMBAUTH_LM_MAGIC_STR,
115 	    sizeof (SMBAUTH_LM_MAGIC_STR)));
116 }
117 
118 /*
119  * smb_auth_lm_response
120  *
121  * Create a LM response from the given LM hash and challenge.
122  *
123  * Returns SMBAUTH_FAILURE if any problems occur, SMBAUTH_SUCCESS if
124  * all goes well.
125  */
126 static int
127 smb_auth_lm_response(unsigned char *hash,
128     unsigned char *challenge, int clen,
129     unsigned char *lm_rsp)
130 {
131 	unsigned char S21[21];
132 
133 	/*
134 	 * 14-byte LM Hash should be padded with 5 nul bytes to create
135 	 * a 21-byte string to be used in producing LM response
136 	 */
137 	bzero(&S21[SMBAUTH_HASH_SZ], 5);
138 	bcopy(hash, S21, SMBAUTH_HASH_SZ);
139 
140 	/* padded LM Hash -> LM Response */
141 	return (smb_auth_DES(lm_rsp, SMBAUTH_LM_RESP_SZ, S21, 21,
142 	    challenge, clen));
143 }
144 
145 /*
146  * smb_auth_ntlm_hash
147  *
148  * Make NTLM Hash (using MD4) from the given password.
149  * The result will contain a 16-byte NTLM hash.
150  */
151 int
152 smb_auth_ntlm_hash(char *password, unsigned char *hash)
153 {
154 	mts_wchar_t *unicode_password;
155 	int length;
156 	int rc;
157 
158 	if (password == NULL || hash == NULL)
159 		return (SMBAUTH_FAILURE);
160 
161 	length = strlen(password);
162 	unicode_password = (mts_wchar_t *)
163 	    malloc((length + 1) * sizeof (mts_wchar_t));
164 
165 	if (unicode_password == NULL)
166 		return (SMBAUTH_FAILURE);
167 
168 	length = smb_auth_qnd_unicode(unicode_password, password, length);
169 	rc = smb_auth_md4(hash, (unsigned char *)unicode_password, length);
170 
171 	free(unicode_password);
172 	return (rc);
173 }
174 
175 /*
176  * smb_auth_ntlm_response
177  *
178  * Make LM/NTLM response from the given LM/NTLM Hash and given
179  * challenge.
180  */
181 static int
182 smb_auth_ntlm_response(unsigned char *hash,
183     unsigned char *challenge, int clen,
184     unsigned char *ntlm_rsp)
185 {
186 	unsigned char S21[21];
187 
188 	bcopy(hash, S21, SMBAUTH_HASH_SZ);
189 	bzero(&S21[SMBAUTH_HASH_SZ], 5);
190 	if (smb_auth_DES((unsigned char *)ntlm_rsp, SMBAUTH_LM_RESP_SZ,
191 	    S21, 21, challenge, clen) == SMBAUTH_FAILURE)
192 		return (0);
193 	return (SMBAUTH_LM_RESP_SZ);
194 }
195 
196 /*
197  * smb_auth_gen_data_blob
198  *
199  * Fill the NTLMv2 data blob structure with information as described in
200  * "Implementing CIFS, The Common Internet File System". (pg. 282)
201  */
202 static void
203 smb_auth_gen_data_blob(smb_auth_data_blob_t *blob, char *ntdomain)
204 {
205 	struct timeval now;
206 
207 	(void) memset(blob->ndb_signature, 1, 2);
208 	(void) memset(&blob->ndb_signature[2], 0, 2);
209 	(void) memset(blob->ndb_reserved, 0, sizeof (blob->ndb_reserved));
210 
211 	(void) gettimeofday(&now, 0);
212 	blob->ndb_timestamp = unix_micro_to_nt_time(&now);
213 	randomize((char *)blob->ndb_clnt_challenge,
214 	    SMBAUTH_V2_CLNT_CHALLENGE_SZ);
215 	(void) memset(blob->ndb_unknown, 0, sizeof (blob->ndb_unknown));
216 	blob->ndb_names[0].nne_len = smb_auth_qnd_unicode(
217 	    blob->ndb_names[0].nne_name, ntdomain, strlen(ntdomain));
218 	blob->ndb_names[0].nne_type = SMBAUTH_NAME_TYPE_DOMAIN_NETBIOS;
219 	blob->ndb_names[1].nne_len = 0;
220 	blob->ndb_names[1].nne_type = SMBAUTH_NAME_TYPE_LIST_END;
221 	*blob->ndb_names[1].nne_name = 0;
222 	(void) memset(blob->ndb_unknown2, 0, sizeof (blob->ndb_unknown2));
223 }
224 
225 /*
226  * smb_auth_memcpy
227  *
228  * It increments the pointer to the destination buffer for the easy of
229  * concatenation.
230  */
231 static void
232 smb_auth_memcpy(unsigned char **dstbuf,
233 	unsigned char *srcbuf,
234 	int srcbuf_len)
235 {
236 	(void) memcpy(*dstbuf, srcbuf, srcbuf_len);
237 	*dstbuf += srcbuf_len;
238 }
239 
240 /*
241  * smb_auth_blob_to_string
242  *
243  * Prepare the data blob string which will be used in NTLMv2 response
244  * generation.
245  *
246  * Assumption: Caller must allocate big enough buffer to prevent buffer
247  * overrun.
248  *
249  * Returns the len of the data blob string.
250  */
251 static int
252 smb_auth_blob_to_string(smb_auth_data_blob_t *blob, unsigned char *data_blob)
253 {
254 	unsigned char *bufp = data_blob;
255 
256 	smb_auth_memcpy(&bufp, blob->ndb_signature,
257 	    sizeof (blob->ndb_signature));
258 	smb_auth_memcpy(&bufp, blob->ndb_reserved,
259 	    sizeof (blob->ndb_reserved));
260 	smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_timestamp,
261 	    sizeof (blob->ndb_timestamp));
262 	smb_auth_memcpy(&bufp, blob->ndb_clnt_challenge,
263 	    SMBAUTH_V2_CLNT_CHALLENGE_SZ);
264 	smb_auth_memcpy(&bufp, blob->ndb_unknown, sizeof (blob->ndb_unknown));
265 	smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[0].nne_type,
266 	    sizeof (blob->ndb_names[0].nne_type));
267 	smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[0].nne_len,
268 	    sizeof (blob->ndb_names[0].nne_len));
269 	smb_auth_memcpy(&bufp, (unsigned char *)blob->ndb_names[0].nne_name,
270 	    blob->ndb_names[0].nne_len);
271 	smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[1].nne_type,
272 	    sizeof (blob->ndb_names[1].nne_type));
273 	smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[1].nne_len,
274 	    sizeof (blob->ndb_names[1].nne_len));
275 	smb_auth_memcpy(&bufp, blob->ndb_unknown2, sizeof (blob->ndb_unknown2));
276 
277 	/*LINTED E_PTRDIFF_OVERFLOW*/
278 	return (bufp - data_blob);
279 }
280 
281 /*
282  * smb_auth_ntlmv2_hash
283  *
284  * The NTLM v2 hash will be created from the given NTLM hash, username,
285  * and the NETBIOS name of the domain.
286  *
287  * The NTLMv2 hash will be returned via the ntlmv2_hash parameter which
288  * will be used in the calculation of the NTLMv2 and LMv2 responses.
289  */
290 int
291 smb_auth_ntlmv2_hash(unsigned char *ntlm_hash,
292     char *username,
293     char *ntdomain,
294     unsigned char *ntlmv2_hash)
295 {
296 	mts_wchar_t *data;
297 	int data_len;
298 	unsigned char *buf;
299 	int rc;
300 
301 	if (username == NULL || ntdomain == NULL)
302 		return (SMBAUTH_FAILURE);
303 
304 	(void) utf8_strupr(username);
305 
306 	data_len = strlen(username) + strlen(ntdomain);
307 	buf = (unsigned char *)malloc((data_len + 1) * sizeof (char));
308 	if (buf == NULL)
309 		return (SMBAUTH_FAILURE);
310 
311 	(void) snprintf((char *)buf, data_len + 1, "%s%s", username, ntdomain);
312 	data = (mts_wchar_t *)malloc((data_len + 1) * sizeof (mts_wchar_t));
313 	if (data == NULL) {
314 		free(buf);
315 		return (SMBAUTH_FAILURE);
316 	}
317 
318 	data_len = smb_auth_qnd_unicode(data, (char *)buf, data_len);
319 	rc = SMBAUTH_HMACT64((unsigned char *)data, data_len, ntlm_hash,
320 	    SMBAUTH_HASH_SZ, ntlmv2_hash);
321 
322 	free(buf);
323 	free(data);
324 	return (rc);
325 }
326 
327 /*
328  * smb_auth_v2_response
329  *
330  * Caculates either the LMv2 or NTLMv2 response.
331  *
332  * Same algorithm is used for calculating both LMv2 or NTLMv2 responses.
333  * This routine will return NTLMv2 response if the data blob information
334  * is passed in as the clnt_data. Otherwise, it will return LMv2 response
335  * with the 8-byte client challenge(a.k.a blip) as the clnt_data.
336  *
337  * (LM/NTLM)v2 response is the hmac-md5 hash of the specified data
338  * (server challenge + NTLMv2 data blob or LMv2 client challenge)
339  * using the NTLMv2 hash as the key.
340  *
341  * Returns the size of the corresponding v2 response upon success.
342  * Otherwise, returns -1 on error.
343  */
344 static int
345 smb_auth_v2_response(
346 	unsigned char *hash,
347 	unsigned char *srv_challenge, int slen,
348 	unsigned char *clnt_data, int clen,
349 	unsigned char *v2_rsp)
350 {
351 	unsigned char *hmac_data;
352 
353 	hmac_data = (unsigned char *)malloc((slen + clen) * sizeof (char));
354 	if (!hmac_data) {
355 		return (-1);
356 	}
357 
358 	(void) memcpy(hmac_data, srv_challenge, slen);
359 	(void) memcpy(&hmac_data[slen], clnt_data, clen);
360 	if (SMBAUTH_HMACT64(hmac_data, slen + clen, (unsigned char *)hash,
361 	    SMBAUTH_HASH_SZ, (unsigned char *)v2_rsp) != SMBAUTH_SUCCESS)
362 		return (-1);
363 	(void) memcpy(&v2_rsp[SMBAUTH_HASH_SZ], clnt_data, clen);
364 
365 	free(hmac_data);
366 	return (SMBAUTH_HASH_SZ + clen);
367 }
368 
369 /*
370  * smb_auth_set_info
371  *
372  * Fill the smb_auth_info instance with either NTLM or NTLMv2 related
373  * authentication information based on the LMCompatibilityLevel.
374  *
375  * If the LMCompatibilityLevel equals 2, the SMB Redirector will perform
376  * NTLM challenge/response authentication which requires the NTLM hash and
377  * NTLM response.
378  *
379  * If the LMCompatibilityLevel is 3 or above, the SMB Redirector will
380  * perfrom NTLMv2 challenge/response authenticatoin which requires the
381  * NTLM hash, NTLMv2 hash, NTLMv2 response and LMv2 response.
382  *
383  * Returns -1 on error. Otherwise, returns 0 upon success.
384  */
385 int
386 smb_auth_set_info(char *username,
387 	char *password,
388 	unsigned char *ntlm_hash,
389 	char *domain,
390 	unsigned char *srv_challenge_key,
391 	int srv_challenge_len,
392 	int lmcomp_lvl,
393 	smb_auth_info_t *auth)
394 {
395 	unsigned short blob_len;
396 	unsigned char blob_buf[SMBAUTH_BLOB_MAXLEN];
397 	int rc;
398 	char *uppercase_dom;
399 
400 	auth->lmcompatibility_lvl = lmcomp_lvl;
401 	if (lmcomp_lvl == 2) {
402 		auth->ci_len = 0;
403 		*auth->ci = 0;
404 		if (!ntlm_hash) {
405 			if (smb_auth_ntlm_hash(password, auth->hash) !=
406 			    SMBAUTH_SUCCESS)
407 				return (-1);
408 		} else {
409 			(void) memcpy(auth->hash, ntlm_hash, SMBAUTH_HASH_SZ);
410 		}
411 
412 		auth->cs_len = smb_auth_ntlm_response(auth->hash,
413 		    srv_challenge_key, srv_challenge_len, auth->cs);
414 	} else {
415 		if (!ntlm_hash) {
416 			if (smb_auth_ntlm_hash(password, auth->hash) !=
417 			    SMBAUTH_SUCCESS)
418 				return (-1);
419 		} else {
420 			(void) memcpy(auth->hash, ntlm_hash, SMBAUTH_HASH_SZ);
421 		}
422 
423 		if (!domain)
424 			return (-1);
425 
426 		if ((uppercase_dom = strdup(domain)) == NULL)
427 			return (-1);
428 
429 		(void) utf8_strupr(uppercase_dom);
430 
431 		if (smb_auth_ntlmv2_hash(auth->hash, username,
432 		    uppercase_dom, auth->hash_v2) != SMBAUTH_SUCCESS) {
433 			free(uppercase_dom);
434 			return (-1);
435 		}
436 
437 		/* generate data blob */
438 		smb_auth_gen_data_blob(&auth->data_blob, uppercase_dom);
439 		free(uppercase_dom);
440 		blob_len = smb_auth_blob_to_string(&auth->data_blob, blob_buf);
441 
442 		/* generate NTLMv2 response */
443 		rc = smb_auth_v2_response(auth->hash_v2, srv_challenge_key,
444 		    srv_challenge_len, blob_buf, blob_len, auth->cs);
445 
446 		if (rc < 0)
447 			return (-1);
448 
449 		auth->cs_len = rc;
450 
451 		/* generate LMv2 response */
452 		rc = smb_auth_v2_response(auth->hash_v2, srv_challenge_key,
453 		    srv_challenge_len, auth->data_blob.ndb_clnt_challenge,
454 		    SMBAUTH_V2_CLNT_CHALLENGE_SZ, auth->ci);
455 
456 		if (rc < 0)
457 			return (-1);
458 
459 		auth->ci_len = rc;
460 	}
461 
462 	return (0);
463 }
464 
465 /*
466  * smb_auth_gen_session_key
467  *
468  * Generate the NTLM user session key if LMCompatibilityLevel is 2 or
469  * NTLMv2 user session key if LMCompatibilityLevel is 3 or above.
470  *
471  * NTLM_Session_Key = MD4(NTLM_Hash);
472  *
473  * NTLMv2_Session_Key = HMAC_MD5(NTLMv2Hash, 16, NTLMv2_HMAC, 16)
474  *
475  * Prior to calling this function, the auth instance should be set
476  * via smb_auth_set_info().
477  *
478  * Returns the appropriate session key.
479  */
480 int
481 smb_auth_gen_session_key(smb_auth_info_t *auth, unsigned char *session_key)
482 {
483 	int rc;
484 
485 	if (auth->lmcompatibility_lvl == 2)
486 		rc = smb_auth_md4(session_key, auth->hash, SMBAUTH_HASH_SZ);
487 	else
488 		rc = SMBAUTH_HMACT64((unsigned char *)auth->cs,
489 		    SMBAUTH_HASH_SZ, (unsigned char *)auth->hash_v2,
490 		    SMBAUTH_SESSION_KEY_SZ, session_key);
491 
492 	return (rc);
493 }
494 
495 /* 100's of ns between 1/1/1970 and 1/1/1601 */
496 #define	NT_TIME_BIAS    (134774LL * 24LL * 60LL * 60LL * 10000000LL)
497 
498 static uint64_t
499 unix_micro_to_nt_time(struct timeval *unix_time)
500 {
501 	uint64_t nt_time;
502 
503 	nt_time = unix_time->tv_sec;
504 	nt_time *= 10000000;  /* seconds to 100ns */
505 	nt_time += unix_time->tv_usec * 10;
506 	return (nt_time + NT_TIME_BIAS);
507 }
508 
509 static boolean_t
510 smb_lm_password_ok(
511     unsigned char *challenge,
512     uint32_t clen,
513     unsigned char *lm_hash,
514     unsigned char *passwd)
515 {
516 	unsigned char lm_resp[SMBAUTH_LM_RESP_SZ];
517 	int rc;
518 
519 	rc = smb_auth_lm_response(lm_hash, challenge, clen, lm_resp);
520 	if (rc != SMBAUTH_SUCCESS)
521 		return (B_FALSE);
522 
523 	return (bcmp(lm_resp, passwd, SMBAUTH_LM_RESP_SZ) == 0);
524 }
525 
526 static boolean_t
527 smb_ntlm_password_ok(
528     unsigned char *challenge,
529     uint32_t clen,
530     unsigned char *ntlm_hash,
531     unsigned char *passwd,
532     unsigned char *session_key)
533 {
534 	unsigned char ntlm_resp[SMBAUTH_LM_RESP_SZ];
535 	int rc;
536 	boolean_t ok;
537 
538 	rc = smb_auth_ntlm_response(ntlm_hash, challenge, clen, ntlm_resp);
539 	if (rc != SMBAUTH_LM_RESP_SZ)
540 		return (B_FALSE);
541 
542 	ok = (bcmp(ntlm_resp, passwd, SMBAUTH_LM_RESP_SZ) == 0);
543 	if (ok && (session_key)) {
544 		rc = smb_auth_md4(session_key, ntlm_hash, SMBAUTH_HASH_SZ);
545 		if (rc != SMBAUTH_SUCCESS)
546 			ok = B_FALSE;
547 	}
548 	return (ok);
549 }
550 
551 static boolean_t
552 smb_ntlmv2_password_ok(
553     unsigned char *challenge,
554     uint32_t clen,
555     unsigned char *ntlm_hash,
556     unsigned char *passwd,
557     int pwdlen,
558     char *domain,
559     char *username,
560     uchar_t *session_key)
561 {
562 	unsigned char *clnt_blob;
563 	int clnt_blob_len;
564 	unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ];
565 	unsigned char *ntlmv2_resp;
566 	boolean_t ok = B_FALSE;
567 	char *dest[3];
568 	int i;
569 	int rc;
570 
571 	clnt_blob_len = pwdlen - SMBAUTH_HASH_SZ;
572 	clnt_blob = &passwd[SMBAUTH_HASH_SZ];
573 	dest[0] = domain;
574 	if ((dest[1] = strdup(domain)) == NULL)
575 		return (B_FALSE);
576 	(void) utf8_strupr(dest[1]);
577 	dest[2] = "";
578 
579 	/*
580 	 * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS"
581 	 *
582 	 * The NTLMv2 Hash is created from:
583 	 * - NTLM hash
584 	 * - user's username, and
585 	 * - the name of the logon destination(i.e. the NetBIOS name of either
586 	 *   the SMB server or NT Domain against which the user is trying to
587 	 *   authenticate.
588 	 *
589 	 * Experiments show this is not exactly the case.
590 	 * For Windows Server 2003, the domain name needs to be included and
591 	 * converted to uppercase. For Vista, the domain name needs to be
592 	 * included also, but leave the case alone.  And in some cases it needs
593 	 * to be empty. All three variants are tried here.
594 	 */
595 
596 	ntlmv2_resp = (unsigned char *)malloc(SMBAUTH_HASH_SZ + clnt_blob_len);
597 	if (ntlmv2_resp == NULL) {
598 		free(dest[1]);
599 		return (B_FALSE);
600 	}
601 
602 	for (i = 0; i < (sizeof (dest) / sizeof (char *)); i++) {
603 		if (smb_auth_ntlmv2_hash(ntlm_hash, username, dest[i],
604 		    ntlmv2_hash) != SMBAUTH_SUCCESS)
605 			break;
606 
607 		if (smb_auth_v2_response(ntlmv2_hash, challenge,
608 		    clen, clnt_blob, clnt_blob_len, ntlmv2_resp) < 0)
609 			break;
610 
611 		ok = (bcmp(passwd, ntlmv2_resp, pwdlen) == 0);
612 		if (ok && session_key) {
613 			rc = SMBAUTH_HMACT64(ntlmv2_resp,
614 			    SMBAUTH_HASH_SZ, ntlmv2_hash,
615 			    SMBAUTH_SESSION_KEY_SZ, session_key);
616 			if (rc != SMBAUTH_SUCCESS) {
617 				ok = B_FALSE;
618 			}
619 			break;
620 		}
621 	}
622 
623 	free(dest[1]);
624 	free(ntlmv2_resp);
625 	return (ok);
626 }
627 
628 static boolean_t
629 smb_lmv2_password_ok(
630     unsigned char *challenge,
631     uint32_t clen,
632     unsigned char *ntlm_hash,
633     unsigned char *passwd,
634     char *domain,
635     char *username)
636 {
637 	unsigned char *clnt_challenge;
638 	unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ];
639 	unsigned char lmv2_resp[SMBAUTH_LM_RESP_SZ];
640 	boolean_t ok = B_FALSE;
641 	char *dest[3];
642 	int i;
643 
644 	clnt_challenge = &passwd[SMBAUTH_HASH_SZ];
645 	dest[0] = domain;
646 	if ((dest[1] = strdup(domain)) == NULL)
647 		return (B_FALSE);
648 	(void) utf8_strupr(dest[1]);
649 	dest[2] = "";
650 
651 	/*
652 	 * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS"
653 	 *
654 	 * The NTLMv2 Hash is created from:
655 	 * - NTLM hash
656 	 * - user's username, and
657 	 * - the name of the logon destination(i.e. the NetBIOS name of either
658 	 *   the SMB server or NT Domain against which the suer is trying to
659 	 *   authenticate.
660 	 *
661 	 * Experiments show this is not exactly the case.
662 	 * For Windows Server 2003, the domain name needs to be included and
663 	 * converted to uppercase. For Vista, the domain name needs to be
664 	 * included also, but leave the case alone.  And in some cases it needs
665 	 * to be empty. All three variants are tried here.
666 	 */
667 
668 	for (i = 0; i < (sizeof (dest) / sizeof (char *)); i++) {
669 		if (smb_auth_ntlmv2_hash(ntlm_hash, username, dest[i],
670 		    ntlmv2_hash) != SMBAUTH_SUCCESS)
671 			break;
672 
673 		if (smb_auth_v2_response(ntlmv2_hash, challenge,
674 		    clen, clnt_challenge, SMBAUTH_V2_CLNT_CHALLENGE_SZ,
675 		    lmv2_resp) < 0)
676 			break;
677 
678 		ok = (bcmp(passwd, lmv2_resp, SMBAUTH_LM_RESP_SZ) == 0);
679 		if (ok)
680 			break;
681 	}
682 
683 	free(dest[1]);
684 	return (ok);
685 }
686 
687 /*
688  * smb_auth_validate_lm
689  *
690  * Validates given LM/LMv2 client response, passed in passwd arg, against
691  * stored user's password, passed in smbpw
692  *
693  * If LM level <=3 server accepts LM responses, otherwise LMv2
694  */
695 boolean_t
696 smb_auth_validate_lm(
697     unsigned char *challenge,
698     uint32_t clen,
699     smb_passwd_t *smbpw,
700     unsigned char *passwd,
701     int pwdlen,
702     char *domain,
703     char *username)
704 {
705 	boolean_t ok = B_FALSE;
706 	int64_t lmlevel;
707 
708 	if (pwdlen != SMBAUTH_LM_RESP_SZ)
709 		return (B_FALSE);
710 
711 	if (smb_config_getnum(SMB_CI_LM_LEVEL, &lmlevel) != SMBD_SMF_OK)
712 		return (B_FALSE);
713 
714 	if (lmlevel <= 3) {
715 		ok = smb_lm_password_ok(challenge, clen, smbpw->pw_lmhash,
716 		    passwd);
717 	}
718 
719 	if (!ok)
720 		ok = smb_lmv2_password_ok(challenge, clen, smbpw->pw_nthash,
721 		    passwd, domain, username);
722 
723 	return (ok);
724 }
725 
726 /*
727  * smb_auth_validate_nt
728  *
729  * Validates given NTLM/NTLMv2 client response, passed in passwd arg, against
730  * stored user's password, passed in smbpw
731  *
732  * If LM level <=4 server accepts NTLM/NTLMv2 responses, otherwise only NTLMv2
733  */
734 boolean_t
735 smb_auth_validate_nt(
736     unsigned char *challenge,
737     uint32_t clen,
738     smb_passwd_t *smbpw,
739     unsigned char *passwd,
740     int pwdlen,
741     char *domain,
742     char *username,
743     uchar_t *session_key)
744 {
745 	int64_t lmlevel;
746 	boolean_t ok;
747 
748 	if (smb_config_getnum(SMB_CI_LM_LEVEL, &lmlevel) != SMBD_SMF_OK)
749 		return (B_FALSE);
750 
751 	if ((lmlevel == 5) && (pwdlen <= SMBAUTH_LM_RESP_SZ))
752 		return (B_FALSE);
753 
754 	if (pwdlen > SMBAUTH_LM_RESP_SZ)
755 		ok = smb_ntlmv2_password_ok(challenge, clen,
756 		    smbpw->pw_nthash, passwd, pwdlen,
757 		    domain, username, session_key);
758 	else
759 		ok = smb_ntlm_password_ok(challenge, clen,
760 		    smbpw->pw_nthash, passwd, session_key);
761 
762 	return (ok);
763 }
764