8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / smbsrv / libsmb / common / smb_auth.c
blob29918639e719dcaa50ae380041b111742111e8cf
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
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>
37 #define NTLM_CHAL_SZ SMBAUTH_CHAL_SZ /* challenge size */
40 * Compute the combined (server+client) challenge per. [MS-NLMP 3.3.1]
41 * MD5(concat(ServerChallenge,ClientChallenge))
43 void
44 smb_auth_ntlm2_mkchallenge(char *result,
45 const char *srv_chal, const char *clnt_chal)
47 MD5_CTX context;
48 uchar_t challenges[2 * NTLM_CHAL_SZ];
49 uchar_t digest[SMBAUTH_HASH_SZ];
52 * challenges = ConcatenationOf(ServerChallenge, ClientChallenge)
54 (void) memcpy(challenges, srv_chal, NTLM_CHAL_SZ);
55 (void) memcpy(challenges + NTLM_CHAL_SZ, clnt_chal, NTLM_CHAL_SZ);
58 * digest = MD5(challenges)
60 MD5Init(&context);
61 MD5Update(&context, challenges, sizeof (challenges));
62 MD5Final(digest, &context);
65 * result = digest[0..7]
67 (void) memcpy(result, digest, NTLM_CHAL_SZ);
70 void
71 smb_auth_ntlm2_kxkey(unsigned char *result, const char *srv_chal,
72 const char *clnt_chal, unsigned char *ssn_base_key)
74 uchar_t challenges[2 * NTLM_CHAL_SZ];
77 * challenges = ConcatenationOf(ServerChallenge, ClientChallenge)
79 (void) memcpy(challenges, srv_chal, NTLM_CHAL_SZ);
80 (void) memcpy(challenges + NTLM_CHAL_SZ, clnt_chal, NTLM_CHAL_SZ);
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);
89 * smb_auth_qnd_unicode
91 * Quick and dirty unicode conversion!
92 * Returns the length of dst in bytes.
94 int
95 smb_auth_qnd_unicode(smb_wchar_t *dst, const char *src, int length)
97 int i;
98 unsigned int count;
99 smb_wchar_t new_char;
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);
106 dst[i] = 0;
107 count = length;
110 return (count * sizeof (smb_wchar_t));
114 * smb_auth_lmupr
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.
121 static void
122 smb_auth_lmupr(unsigned char *lm_pwd)
124 unsigned char *p = lm_pwd;
125 int i;
127 for (i = 0; (*p) && (i < SMBAUTH_LM_PWD_SZ); i++) {
128 if (smb_isascii(*p)) {
129 *p = smb_toupper(*p);
130 p++;
136 * smb_auth_lm_hash
138 * Source: Implementing CIFS (Chris Hertel)
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
146 * 2. The 14-byte password is converted to all uppercase
148 * 3. The result is used as key to encrypt the KGS magic string to
149 * make a 16-byte hash.
152 smb_auth_lm_hash(const char *password, unsigned char *lm_hash)
154 unsigned char lm_pwd[SMBAUTH_LM_PWD_SZ];
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);
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)));
166 * smb_auth_lm_response
168 * Create a LM response from the given LM hash and challenge.
170 * Returns SMBAUTH_FAILURE if any problems occur, SMBAUTH_SUCCESS if
171 * all goes well.
173 static int
174 smb_auth_lm_response(unsigned char *hash,
175 unsigned char *challenge, /* NTLM_CHAL_SZ */
176 unsigned char *lm_rsp)
178 unsigned char S21[21];
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
184 bzero(&S21[SMBAUTH_HASH_SZ], 5);
185 bcopy(hash, S21, SMBAUTH_HASH_SZ);
187 /* padded LM Hash -> LM Response */
188 return (smb_auth_DES(lm_rsp, SMBAUTH_LM_RESP_SZ, S21, 21,
189 challenge, NTLM_CHAL_SZ));
193 * smb_auth_ntlm_hash
195 * Make NTLM Hash (using MD4) from the given password.
196 * The result will contain a 16-byte NTLM hash.
199 smb_auth_ntlm_hash(const char *password, unsigned char *hash)
201 smb_wchar_t *unicode_password;
202 int length, unicode_len;
203 int rc;
205 if (password == NULL || hash == NULL)
206 return (SMBAUTH_FAILURE);
208 length = strlen(password);
209 unicode_len = (length + 1) * sizeof (smb_wchar_t);
210 unicode_password = malloc(unicode_len);
212 if (unicode_password == NULL)
213 return (SMBAUTH_FAILURE);
215 length = smb_auth_qnd_unicode(unicode_password, password, length);
216 rc = smb_auth_md4(hash, (unsigned char *)unicode_password, length);
218 (void) memset(unicode_password, 0, unicode_len);
219 free(unicode_password);
221 return (rc);
225 * smb_auth_ntlm_response
227 * Make LM/NTLM response from the given LM/NTLM Hash and given
228 * challenge.
230 static int
231 smb_auth_ntlm_response(unsigned char *hash,
232 unsigned char *challenge, /* NTLM_CHAL_SZ */
233 unsigned char *ntlm_rsp)
235 unsigned char S21[21];
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);
246 * smb_auth_ntlmv2_hash
248 * The NTLM v2 hash will be created from the given NTLM hash, username,
249 * and the NETBIOS name of the domain.
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.
255 smb_auth_ntlmv2_hash(unsigned char *ntlm_hash,
256 char *username,
257 char *ntdomain,
258 unsigned char *ntlmv2_hash)
260 smb_wchar_t *data;
261 int data_len;
262 unsigned char *buf;
263 int rc;
265 if (username == NULL || ntdomain == NULL)
266 return (SMBAUTH_FAILURE);
268 (void) smb_strupr(username);
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);
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);
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);
286 free(buf);
287 free(data);
288 return (rc);
292 * smb_auth_v2_response
294 * Caculates either the LMv2 or NTLMv2 response.
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.
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.
305 * Returns the size of the corresponding v2 response upon success.
306 * Otherwise, returns -1 on error.
308 static int
309 smb_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)
315 unsigned char *hmac_data;
316 int slen = NTLM_CHAL_SZ;
318 hmac_data = malloc(NTLM_CHAL_SZ + clen);
319 if (!hmac_data) {
320 return (-1);
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);
330 free(hmac_data);
331 return (SMBAUTH_HASH_SZ + clen);
335 static boolean_t
336 smb_lm_password_ok(
337 unsigned char *challenge,
338 unsigned char *lm_hash,
339 unsigned char *lm_resp)
341 unsigned char ok_resp[SMBAUTH_LM_RESP_SZ];
342 int rc;
344 rc = smb_auth_lm_response(lm_hash, challenge, ok_resp);
345 if (rc != SMBAUTH_SUCCESS)
346 return (B_FALSE);
348 return (bcmp(ok_resp, lm_resp, SMBAUTH_LM_RESP_SZ) == 0);
351 static boolean_t
352 smb_ntlm_password_ok(
353 unsigned char *challenge,
354 unsigned char *ntlm_hash,
355 unsigned char *nt_resp,
356 unsigned char *session_key)
358 unsigned char ok_resp[SMBAUTH_LM_RESP_SZ];
359 int rc;
360 boolean_t ok;
362 rc = smb_auth_ntlm_response(ntlm_hash, challenge, ok_resp);
363 if (rc != SMBAUTH_LM_RESP_SZ)
364 return (B_FALSE);
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;
372 return (ok);
375 static boolean_t
376 smb_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)
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;
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] = "";
403 * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS"
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.
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.
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);
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;
430 if (smb_auth_v2_response(ntlmv2_hash, challenge,
431 clnt_blob, clnt_blob_len, ntlmv2_resp) < 0)
432 break;
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;
442 break;
446 free(dest[1]);
447 free(ntlmv2_resp);
448 return (ok);
451 static boolean_t
452 smb_lmv2_password_ok(
453 unsigned char *srv_challenge,
454 unsigned char *ntlm_hash,
455 unsigned char *passwd,
456 char *domain,
457 char *username)
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;
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] = "";
474 * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS"
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.
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.
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;
495 if (smb_auth_v2_response(ntlmv2_hash, srv_challenge,
496 clnt_challenge, SMBAUTH_CHAL_SZ,
497 lmv2_resp) < 0)
498 break;
500 ok = (bcmp(passwd, lmv2_resp, SMBAUTH_LM_RESP_SZ) == 0);
501 if (ok)
502 break;
505 free(dest[1]);
506 return (ok);
510 * smb_auth_validate
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).
517 boolean_t
518 smb_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)
530 int64_t lmlevel;
531 boolean_t ok = B_FALSE;
533 if (smb_config_getnum(SMB_CI_LM_LEVEL, &lmlevel) != SMBD_SMF_OK)
534 return (B_FALSE);
536 if (lmlevel > 5)
537 return (B_FALSE);
539 if (clen != NTLM_CHAL_SZ)
540 return (B_FALSE);
543 * Accept NTLMv2 at any LM level (0-5).
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);
553 if (lmlevel == 5)
554 return (B_FALSE);
557 * Accept NTLM at levels 0-4
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);
566 if (lmlevel == 4)
567 return (B_FALSE);
571 * Accept LM/LMv2 auth at levels 0-3
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);
586 return (B_FALSE);
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.
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'.
601 smb_gen_random_passwd(char *buf, size_t len)
603 const uchar_t start = ' ';
604 const uchar_t modulus = 'z' - ' ' + 1;
605 uchar_t t;
606 int i;
608 /* Last byte is the null. */
609 len--;
611 /* Temporarily put random data in the caller's buffer. */
612 randomize(buf, len);
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;
623 buf[len] = '\0';
625 return (0);