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 * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
26 */
27
28#include <strings.h>
29#include <stdlib.h>
30#include <syslog.h>
31#include <sys/md5.h>
32#include <smbsrv/string.h>
33#include <smbsrv/libsmb.h>
34#include <netsmb/spnego.h>	/* libsmbfs */
35#include <assert.h>
36
37#define	NTLM_CHAL_SZ	SMBAUTH_CHAL_SZ	/* challenge size */
38
39/*
40 * Compute the combined (server+client) challenge per. [MS-NLMP 3.3.1]
41 * MD5(concat(ServerChallenge,ClientChallenge))
42 */
43void
44smb_auth_ntlm2_mkchallenge(char *result,
45	const char *srv_chal, const char *clnt_chal)
46{
47	MD5_CTX context;
48	uchar_t challenges[2 * NTLM_CHAL_SZ];
49	uchar_t digest[SMBAUTH_HASH_SZ];
50
51	/*
52	 * challenges = ConcatenationOf(ServerChallenge, ClientChallenge)
53	 */
54	(void) memcpy(challenges, srv_chal, NTLM_CHAL_SZ);
55	(void) memcpy(challenges + NTLM_CHAL_SZ, clnt_chal, NTLM_CHAL_SZ);
56
57	/*
58	 * digest = MD5(challenges)
59	 */
60	MD5Init(&context);
61	MD5Update(&context, challenges, sizeof (challenges));
62	MD5Final(digest, &context);
63
64	/*
65	 * result = digest[0..7]
66	 */
67	(void) memcpy(result, digest, NTLM_CHAL_SZ);
68}
69
70void
71smb_auth_ntlm2_kxkey(unsigned char *result, const char *srv_chal,
72	const char *clnt_chal, unsigned char *ssn_base_key)
73{
74	uchar_t challenges[2 * NTLM_CHAL_SZ];
75
76	/*
77	 * challenges = ConcatenationOf(ServerChallenge, ClientChallenge)
78	 */
79	(void) memcpy(challenges, srv_chal, NTLM_CHAL_SZ);
80	(void) memcpy(challenges + NTLM_CHAL_SZ, clnt_chal, NTLM_CHAL_SZ);
81
82	/* HMAC_MD5(SessionBaseKey, concat(...)) */
83	/* SMBAUTH_HMACT64 args: D, Dsz, K, Ksz, digest */
84	(void) SMBAUTH_HMACT64(challenges, sizeof (challenges),
85	    ssn_base_key, SMBAUTH_HASH_SZ, result);
86}
87
88/*
89 * smb_auth_qnd_unicode
90 *
91 * Quick and dirty unicode conversion!
92 * Returns the length of dst in bytes.
93 */
94int
95smb_auth_qnd_unicode(smb_wchar_t *dst, const char *src, int length)
96{
97	int i;
98	unsigned int count;
99	smb_wchar_t new_char;
100
101	if ((count = oemtoucs(dst, src, length, OEM_CPG_1252)) == 0) {
102		for (i = 0; i < length; ++i) {
103			new_char = (smb_wchar_t)src[i] & 0xff;
104			dst[i] = LE_IN16(&new_char);
105		}
106		dst[i] = 0;
107		count = length;
108	}
109
110	return (count * sizeof (smb_wchar_t));
111}
112
113/*
114 * smb_auth_lmupr
115 *
116 * Converts the given LM password to all uppercase.
117 * The standard strupr cannot
118 * be used here because lm_pwd doesn't have to be
119 * nul terminated.
120 */
121static void
122smb_auth_lmupr(unsigned char *lm_pwd)
123{
124	unsigned char *p = lm_pwd;
125	int i;
126
127	for (i = 0; (*p) && (i < SMBAUTH_LM_PWD_SZ); i++) {
128		if (smb_isascii(*p)) {
129			*p = smb_toupper(*p);
130			p++;
131		}
132	}
133}
134
135/*
136 * smb_auth_lm_hash
137 *
138 * Source: Implementing CIFS (Chris Hertel)
139 *
140 * 1. The password, as entered by user, is either padded with nulls
141 *	  or trimmed to 14 bytes.
142 *    . Note that the 14-byte result string is not handled as a
143 *	    nul-terminated string.
144 *	  . The given password is OEM not Unicode
145 *
146 * 2. The 14-byte password is converted to all uppercase
147 *
148 * 3. The result is used as key to encrypt the KGS magic string to
149 *    make a 16-byte hash.
150 */
151int
152smb_auth_lm_hash(const char *password, unsigned char *lm_hash)
153{
154	unsigned char lm_pwd[SMBAUTH_LM_PWD_SZ];
155
156	bzero((void *)lm_pwd, SMBAUTH_LM_PWD_SZ);
157	(void) strncpy((char *)lm_pwd, password, SMBAUTH_LM_PWD_SZ);
158	smb_auth_lmupr(lm_pwd);
159
160	return (smb_auth_DES(lm_hash, SMBAUTH_HASH_SZ, lm_pwd,
161	    SMBAUTH_LM_PWD_SZ, (unsigned char *)SMBAUTH_LM_MAGIC_STR,
162	    sizeof (SMBAUTH_LM_MAGIC_STR)));
163}
164
165/*
166 * smb_auth_lm_response
167 *
168 * Create a LM response from the given LM hash and challenge.
169 *
170 * Returns SMBAUTH_FAILURE if any problems occur, SMBAUTH_SUCCESS if
171 * all goes well.
172 */
173static int
174smb_auth_lm_response(unsigned char *hash,
175    unsigned char *challenge, /* NTLM_CHAL_SZ */
176    unsigned char *lm_rsp)
177{
178	unsigned char S21[21];
179
180	/*
181	 * 14-byte LM Hash should be padded with 5 nul bytes to create
182	 * a 21-byte string to be used in producing LM response
183	 */
184	bzero(&S21[SMBAUTH_HASH_SZ], 5);
185	bcopy(hash, S21, SMBAUTH_HASH_SZ);
186
187	/* padded LM Hash -> LM Response */
188	return (smb_auth_DES(lm_rsp, SMBAUTH_LM_RESP_SZ, S21, 21,
189	    challenge, NTLM_CHAL_SZ));
190}
191
192/*
193 * smb_auth_ntlm_hash
194 *
195 * Make NTLM Hash (using MD4) from the given password.
196 * The result will contain a 16-byte NTLM hash.
197 */
198int
199smb_auth_ntlm_hash(const char *password, unsigned char *hash)
200{
201	smb_wchar_t *unicode_password;
202	int length, unicode_len;
203	int rc;
204
205	if (password == NULL || hash == NULL)
206		return (SMBAUTH_FAILURE);
207
208	length = strlen(password);
209	unicode_len = (length + 1) * sizeof (smb_wchar_t);
210	unicode_password = malloc(unicode_len);
211
212	if (unicode_password == NULL)
213		return (SMBAUTH_FAILURE);
214
215	length = smb_auth_qnd_unicode(unicode_password, password, length);
216	rc = smb_auth_md4(hash, (unsigned char *)unicode_password, length);
217
218	(void) memset(unicode_password, 0, unicode_len);
219	free(unicode_password);
220
221	return (rc);
222}
223
224/*
225 * smb_auth_ntlm_response
226 *
227 * Make LM/NTLM response from the given LM/NTLM Hash and given
228 * challenge.
229 */
230static int
231smb_auth_ntlm_response(unsigned char *hash,
232    unsigned char *challenge, /* NTLM_CHAL_SZ */
233    unsigned char *ntlm_rsp)
234{
235	unsigned char S21[21];
236
237	bcopy(hash, S21, SMBAUTH_HASH_SZ);
238	bzero(&S21[SMBAUTH_HASH_SZ], 5);
239	if (smb_auth_DES((unsigned char *)ntlm_rsp, SMBAUTH_LM_RESP_SZ,
240	    S21, 21, challenge, NTLM_CHAL_SZ) == SMBAUTH_FAILURE)
241		return (0);
242	return (SMBAUTH_LM_RESP_SZ);
243}
244
245/*
246 * smb_auth_ntlmv2_hash
247 *
248 * The NTLM v2 hash will be created from the given NTLM hash, username,
249 * and the NETBIOS name of the domain.
250 *
251 * The NTLMv2 hash will be returned via the ntlmv2_hash parameter which
252 * will be used in the calculation of the NTLMv2 and LMv2 responses.
253 */
254int
255smb_auth_ntlmv2_hash(unsigned char *ntlm_hash,
256    char *username,
257    char *ntdomain,
258    unsigned char *ntlmv2_hash)
259{
260	smb_wchar_t *data;
261	int data_len;
262	unsigned char *buf;
263	int rc;
264
265	if (username == NULL || ntdomain == NULL)
266		return (SMBAUTH_FAILURE);
267
268	(void) smb_strupr(username);
269
270	data_len = strlen(username) + strlen(ntdomain);
271	buf = (unsigned char *)malloc((data_len + 1) * sizeof (char));
272	if (buf == NULL)
273		return (SMBAUTH_FAILURE);
274
275	(void) snprintf((char *)buf, data_len + 1, "%s%s", username, ntdomain);
276	data = (smb_wchar_t *)malloc((data_len + 1) * sizeof (smb_wchar_t));
277	if (data == NULL) {
278		free(buf);
279		return (SMBAUTH_FAILURE);
280	}
281
282	data_len = smb_auth_qnd_unicode(data, (char *)buf, data_len);
283	rc = SMBAUTH_HMACT64((unsigned char *)data, data_len, ntlm_hash,
284	    SMBAUTH_HASH_SZ, ntlmv2_hash);
285
286	free(buf);
287	free(data);
288	return (rc);
289}
290
291/*
292 * smb_auth_v2_response
293 *
294 * Caculates either the LMv2 or NTLMv2 response.
295 *
296 * Same algorithm is used for calculating both LMv2 or NTLMv2 responses.
297 * This routine will return NTLMv2 response if the data blob information
298 * is passed in as the clnt_data. Otherwise, it will return LMv2 response
299 * with the 8-byte client challenge(a.k.a blip) as the clnt_data.
300 *
301 * (LM/NTLM)v2 response is the hmac-md5 hash of the specified data
302 * (server challenge + NTLMv2 data blob or LMv2 client challenge)
303 * using the NTLMv2 hash as the key.
304 *
305 * Returns the size of the corresponding v2 response upon success.
306 * Otherwise, returns -1 on error.
307 */
308static int
309smb_auth_v2_response(
310	unsigned char *hash,
311	unsigned char *srv_challenge, /* NTLM_CHAL_SZ */
312	unsigned char *clnt_data, int clen,
313	unsigned char *v2_rsp)
314{
315	unsigned char *hmac_data;
316	int slen = NTLM_CHAL_SZ;
317
318	hmac_data = malloc(NTLM_CHAL_SZ + clen);
319	if (!hmac_data) {
320		return (-1);
321	}
322
323	(void) memcpy(hmac_data, srv_challenge, slen);
324	(void) memcpy(&hmac_data[slen], clnt_data, clen);
325	if (SMBAUTH_HMACT64(hmac_data, slen + clen, (unsigned char *)hash,
326	    SMBAUTH_HASH_SZ, (unsigned char *)v2_rsp) != SMBAUTH_SUCCESS)
327		return (-1);
328	(void) memcpy(&v2_rsp[SMBAUTH_HASH_SZ], clnt_data, clen);
329
330	free(hmac_data);
331	return (SMBAUTH_HASH_SZ + clen);
332}
333
334
335static boolean_t
336smb_lm_password_ok(
337    unsigned char *challenge,
338    unsigned char *lm_hash,
339    unsigned char *lm_resp)
340{
341	unsigned char ok_resp[SMBAUTH_LM_RESP_SZ];
342	int rc;
343
344	rc = smb_auth_lm_response(lm_hash, challenge, ok_resp);
345	if (rc != SMBAUTH_SUCCESS)
346		return (B_FALSE);
347
348	return (bcmp(ok_resp, lm_resp, SMBAUTH_LM_RESP_SZ) == 0);
349}
350
351static boolean_t
352smb_ntlm_password_ok(
353    unsigned char *challenge,
354    unsigned char *ntlm_hash,
355    unsigned char *nt_resp,
356    unsigned char *session_key)
357{
358	unsigned char ok_resp[SMBAUTH_LM_RESP_SZ];
359	int rc;
360	boolean_t ok;
361
362	rc = smb_auth_ntlm_response(ntlm_hash, challenge, ok_resp);
363	if (rc != SMBAUTH_LM_RESP_SZ)
364		return (B_FALSE);
365
366	ok = (bcmp(ok_resp, nt_resp, SMBAUTH_LM_RESP_SZ) == 0);
367	if (ok && (session_key)) {
368		rc = smb_auth_md4(session_key, ntlm_hash, SMBAUTH_HASH_SZ);
369		if (rc != SMBAUTH_SUCCESS)
370			ok = B_FALSE;
371	}
372	return (ok);
373}
374
375static boolean_t
376smb_ntlmv2_password_ok(
377    unsigned char *challenge,
378    unsigned char *ntlm_hash,
379    unsigned char *passwd,
380    int pwdlen,
381    char *domain,
382    char *username,
383    uchar_t *session_key)
384{
385	unsigned char *clnt_blob;
386	int clnt_blob_len;
387	unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ];
388	unsigned char *ntlmv2_resp;
389	boolean_t ok = B_FALSE;
390	char *dest[3];
391	int i;
392	int rc;
393
394	clnt_blob_len = pwdlen - SMBAUTH_HASH_SZ;
395	clnt_blob = &passwd[SMBAUTH_HASH_SZ];
396	dest[0] = domain;
397	if ((dest[1] = strdup(domain)) == NULL)
398		return (B_FALSE);
399	(void) smb_strupr(dest[1]);
400	dest[2] = "";
401
402	/*
403	 * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS"
404	 *
405	 * The NTLMv2 Hash is created from:
406	 * - NTLM hash
407	 * - user's username, and
408	 * - the name of the logon destination(i.e. the NetBIOS name of either
409	 *   the SMB server or NT Domain against which the user is trying to
410	 *   authenticate.
411	 *
412	 * Experiments show this is not exactly the case.
413	 * For Windows Server 2003, the domain name needs to be included and
414	 * converted to uppercase. For Vista, the domain name needs to be
415	 * included also, but leave the case alone.  And in some cases it needs
416	 * to be empty. All three variants are tried here.
417	 */
418
419	ntlmv2_resp = (unsigned char *)malloc(SMBAUTH_HASH_SZ + clnt_blob_len);
420	if (ntlmv2_resp == NULL) {
421		free(dest[1]);
422		return (B_FALSE);
423	}
424
425	for (i = 0; i < (sizeof (dest) / sizeof (char *)); i++) {
426		if (smb_auth_ntlmv2_hash(ntlm_hash, username, dest[i],
427		    ntlmv2_hash) != SMBAUTH_SUCCESS)
428			break;
429
430		if (smb_auth_v2_response(ntlmv2_hash, challenge,
431		    clnt_blob, clnt_blob_len, ntlmv2_resp) < 0)
432			break;
433
434		ok = (bcmp(passwd, ntlmv2_resp, pwdlen) == 0);
435		if (ok && session_key) {
436			rc = SMBAUTH_HMACT64(ntlmv2_resp,
437			    SMBAUTH_HASH_SZ, ntlmv2_hash,
438			    SMBAUTH_SESSION_KEY_SZ, session_key);
439			if (rc != SMBAUTH_SUCCESS) {
440				ok = B_FALSE;
441			}
442			break;
443		}
444	}
445
446	free(dest[1]);
447	free(ntlmv2_resp);
448	return (ok);
449}
450
451static boolean_t
452smb_lmv2_password_ok(
453    unsigned char *srv_challenge,
454    unsigned char *ntlm_hash,
455    unsigned char *passwd,
456    char *domain,
457    char *username)
458{
459	unsigned char *clnt_challenge;
460	unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ];
461	unsigned char lmv2_resp[SMBAUTH_LM_RESP_SZ];
462	boolean_t ok = B_FALSE;
463	char *dest[3];
464	int i;
465
466	clnt_challenge = &passwd[SMBAUTH_HASH_SZ];
467	dest[0] = domain;
468	if ((dest[1] = strdup(domain)) == NULL)
469		return (B_FALSE);
470	(void) smb_strupr(dest[1]);
471	dest[2] = "";
472
473	/*
474	 * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS"
475	 *
476	 * The NTLMv2 Hash is created from:
477	 * - NTLM hash
478	 * - user's username, and
479	 * - the name of the logon destination(i.e. the NetBIOS name of either
480	 *   the SMB server or NT Domain against which the suer is trying to
481	 *   authenticate.
482	 *
483	 * Experiments show this is not exactly the case.
484	 * For Windows Server 2003, the domain name needs to be included and
485	 * converted to uppercase. For Vista, the domain name needs to be
486	 * included also, but leave the case alone.  And in some cases it needs
487	 * to be empty. All three variants are tried here.
488	 */
489
490	for (i = 0; i < (sizeof (dest) / sizeof (char *)); i++) {
491		if (smb_auth_ntlmv2_hash(ntlm_hash, username, dest[i],
492		    ntlmv2_hash) != SMBAUTH_SUCCESS)
493			break;
494
495		if (smb_auth_v2_response(ntlmv2_hash, srv_challenge,
496		    clnt_challenge, SMBAUTH_CHAL_SZ,
497		    lmv2_resp) < 0)
498			break;
499
500		ok = (bcmp(passwd, lmv2_resp, SMBAUTH_LM_RESP_SZ) == 0);
501		if (ok)
502			break;
503	}
504
505	free(dest[1]);
506	return (ok);
507}
508
509/*
510 * smb_auth_validate
511 *
512 * Validates given NTLMv2 (or NTLM, LMv2, LM) client responses against
513 * the stored user's password, passed in smbpw.  Try those in the order
514 * strongest to weakest, stopping at a point determined by the configured
515 * lmauth_level (LM Compatibility Level).
516 */
517boolean_t
518smb_auth_validate(
519    smb_passwd_t *smbpw,
520    char *domain,
521    char *username,
522    unsigned char *challenge,
523    uint_t clen,
524    unsigned char *nt_resp,
525    uint_t nt_len,
526    unsigned char *lm_resp,
527    uint_t lm_len,
528    uchar_t *session_key)
529{
530	int64_t lmlevel;
531	boolean_t ok = B_FALSE;
532
533	if (smb_config_getnum(SMB_CI_LM_LEVEL, &lmlevel) != SMBD_SMF_OK)
534		return (B_FALSE);
535
536	if (lmlevel > 5)
537		return (B_FALSE);
538
539	if (clen != NTLM_CHAL_SZ)
540		return (B_FALSE);
541
542	/*
543	 * Accept NTLMv2 at any LM level (0-5).
544	 */
545	if (nt_len > SMBAUTH_LM_RESP_SZ) {
546		ok = smb_ntlmv2_password_ok(challenge,
547		    smbpw->pw_nthash, nt_resp, nt_len,
548		    domain, username, session_key);
549		if (ok)
550			return (ok);
551	}
552
553	if (lmlevel == 5)
554		return (B_FALSE);
555
556	/*
557	 * Accept NTLM at levels 0-4
558	 */
559	if (nt_len == SMBAUTH_LM_RESP_SZ) {
560		ok = smb_ntlm_password_ok(challenge, smbpw->pw_nthash,
561		    nt_resp, session_key);
562		if (ok)
563			return (ok);
564	}
565
566	if (lmlevel == 4)
567		return (B_FALSE);
568
569
570	/*
571	 * Accept LM/LMv2 auth at levels 0-3
572	 */
573	if (lm_len != SMBAUTH_LM_RESP_SZ)
574		return (B_FALSE);
575	if (session_key)
576		(void) smb_auth_md4(session_key, smbpw->pw_nthash,
577		    SMBAUTH_HASH_SZ);
578	ok = smb_lmv2_password_ok(challenge, smbpw->pw_nthash,
579	    lm_resp, domain, username);
580	if (ok)
581		return (ok);
582	ok = smb_lm_password_ok(challenge, smbpw->pw_lmhash, lm_resp);
583	if (ok)
584		return (ok);
585
586	return (B_FALSE);
587}
588
589/*
590 * smb_gen_random_passwd(buf, len)
591 * Generate a random password of length len-1, and store it in buf,
592 * null terminated.  This is used as a machine account password,
593 * which we set when we join a domain.
594 *
595 * [MS-DISO] A machine password is an ASCII string of randomly chosen
596 * characters. Each character's ASCII code is between 32 and 122 inclusive.
597 * That's space through 'z'.
598 */
599
600int
601smb_gen_random_passwd(char *buf, size_t len)
602{
603	const uchar_t start = ' ';
604	const uchar_t modulus = 'z' - ' ' + 1;
605	uchar_t t;
606	int i;
607
608	/* Last byte is the null. */
609	len--;
610
611	/* Temporarily put random data in the caller's buffer. */
612	randomize(buf, len);
613
614	/* Convert the random data to printable characters. */
615	for (i = 0; i < len; i++) {
616		/* need unsigned math */
617		t = (uchar_t)buf[i];
618		t = (t % modulus) + start;
619		assert(' ' <= t && t <= 'z');
620		buf[i] = (char)t;
621	}
622
623	buf[len] = '\0';
624
625	return (0);
626}
627