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