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