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