smb_auth.c revision 2
2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <strings.h>
2N/A#include <stdlib.h>
2N/A#include <smbsrv/string.h>
2N/A#include <smbsrv/libsmb.h>
2N/A
2N/Aextern void randomize(char *data, unsigned len);
2N/Astatic uint64_t unix_micro_to_nt_time(struct timeval *unix_time);
2N/A
2N/A/*
2N/A * smb_auth_qnd_unicode
2N/A *
2N/A * Quick and dirty unicode conversion!
2N/A * Returns the length of dst in bytes.
2N/A */
2N/Aint
2N/Asmb_auth_qnd_unicode(smb_wchar_t *dst, const char *src, int length)
2N/A{
2N/A int i;
2N/A unsigned int count;
2N/A smb_wchar_t new_char;
2N/A
2N/A if ((count = oemtoucs(dst, src, length, OEM_CPG_1252)) == 0) {
2N/A for (i = 0; i < length; ++i) {
2N/A new_char = (smb_wchar_t)src[i] & 0xff;
2N/A dst[i] = LE_IN16(&new_char);
2N/A }
2N/A dst[i] = 0;
2N/A count = length;
2N/A }
2N/A
2N/A return (count * sizeof (smb_wchar_t));
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_lmupr
2N/A *
2N/A * Converts the given LM password to all uppercase.
2N/A * The standard strupr cannot
2N/A * be used here because lm_pwd doesn't have to be
2N/A * nul terminated.
2N/A */
2N/Astatic void
2N/Asmb_auth_lmupr(unsigned char *lm_pwd)
2N/A{
2N/A unsigned char *p = lm_pwd;
2N/A int i;
2N/A
2N/A for (i = 0; (*p) && (i < SMBAUTH_LM_PWD_SZ); i++) {
2N/A if (smb_isascii(*p)) {
2N/A *p = smb_toupper(*p);
2N/A p++;
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_lm_hash
2N/A *
2N/A * Source: Implementing CIFS (Chris Hertel)
2N/A *
2N/A * 1. The password, as entered by user, is either padded with nulls
2N/A * or trimmed to 14 bytes.
2N/A * . Note that the 14-byte result string is not handled as a
2N/A * nul-terminated string.
2N/A * . The given password is OEM not Unicode
2N/A *
2N/A * 2. The 14-byte password is converted to all uppercase
2N/A *
2N/A * 3. The result is used as key to encrypt the KGS magic string to
2N/A * make a 16-byte hash.
2N/A */
2N/Aint
2N/Asmb_auth_lm_hash(const char *password, unsigned char *lm_hash)
2N/A{
2N/A unsigned char lm_pwd[SMBAUTH_LM_PWD_SZ];
2N/A
2N/A bzero((void *)lm_pwd, SMBAUTH_LM_PWD_SZ);
2N/A (void) strncpy((char *)lm_pwd, password, SMBAUTH_LM_PWD_SZ);
2N/A smb_auth_lmupr(lm_pwd);
2N/A
2N/A return (smb_auth_DES(lm_hash, SMBAUTH_HASH_SZ, lm_pwd,
2N/A SMBAUTH_LM_PWD_SZ, (unsigned char *)SMBAUTH_LM_MAGIC_STR,
2N/A sizeof (SMBAUTH_LM_MAGIC_STR)));
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_lm_response
2N/A *
2N/A * Create a LM response from the given LM hash and challenge.
2N/A *
2N/A * Returns SMBAUTH_FAILURE if any problems occur, SMBAUTH_SUCCESS if
2N/A * all goes well.
2N/A */
2N/Astatic int
2N/Asmb_auth_lm_response(unsigned char *hash,
2N/A unsigned char *challenge, int clen,
2N/A unsigned char *lm_rsp)
2N/A{
2N/A unsigned char S21[21];
2N/A int rc;
2N/A
2N/A /*
2N/A * 14-byte LM Hash should be padded with 5 nul bytes to create
2N/A * a 21-byte string to be used in producing LM response
2N/A */
2N/A bzero(&S21[SMBAUTH_HASH_SZ], 5);
2N/A bcopy(hash, S21, SMBAUTH_HASH_SZ);
2N/A
2N/A /* padded LM Hash -> LM Response */
2N/A rc = smb_auth_DES(lm_rsp, SMBAUTH_RESP_SZ, S21, 21, challenge, clen);
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_ntlm_hash
2N/A *
2N/A * Make NTLM Hash (using MD4) from the given password.
2N/A * The result will contain a 16-byte NTLM hash.
2N/A */
2N/Aint
2N/Asmb_auth_ntlm_hash(const char *password, unsigned char *hash)
2N/A{
2N/A smb_wchar_t *unicode_password;
2N/A int length;
2N/A int rc;
2N/A
2N/A if (password == NULL || hash == NULL)
2N/A return (SMBAUTH_FAILURE);
2N/A
2N/A length = strlen(password);
2N/A unicode_password = (smb_wchar_t *)
2N/A malloc((length + 1) * sizeof (smb_wchar_t));
2N/A
2N/A if (unicode_password == NULL)
2N/A return (SMBAUTH_FAILURE);
2N/A
2N/A length = smb_auth_qnd_unicode(unicode_password, password, length);
2N/A rc = smb_auth_md4(hash, (unsigned char *)unicode_password, length);
2N/A
2N/A free(unicode_password);
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_ntlm_response
2N/A *
2N/A * Make LM/NTLM response from the given LM/NTLM Hash and given
2N/A * challenge.
2N/A */
2N/Astatic int
2N/Asmb_auth_ntlm_response(unsigned char *hash,
2N/A unsigned char *challenge, int clen,
2N/A unsigned char *ntlm_rsp)
2N/A{
2N/A unsigned char S21[21];
2N/A
2N/A bcopy(hash, S21, SMBAUTH_HASH_SZ);
2N/A bzero(&S21[SMBAUTH_HASH_SZ], 5);
2N/A if (smb_auth_DES((unsigned char *)ntlm_rsp, SMBAUTH_RESP_SZ,
2N/A S21, 21, challenge, clen) == SMBAUTH_FAILURE)
2N/A return (0);
2N/A return (SMBAUTH_RESP_SZ);
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_ntlm2_session_hash
2N/A *
2N/A * data = concat(challenge, client_nonce); [ntlm2 session nonce]
2N/A * hash = MD5(data);
2N/A * NTLM2 session hash = head(hash, 8);
2N/A *
2N/A * Returns SMBAUTH_SUCCESS if cryptology framework use was successful,
2N/A * Otherwise, returns SMBAUTH_FAILURE.
2N/A */
2N/Aint
2N/Asmb_auth_ntlm2_session_hash(
2N/A const unsigned char *nonce, int nlen, unsigned char *ntlm2_sess_hash)
2N/A{
2N/A uint8_t digest[MD_DIGEST_LEN];
2N/A
2N/A if (smb_auth_md5(digest, nonce, nlen) != SMBAUTH_SUCCESS)
2N/A return (SMBAUTH_FAILURE);
2N/A
2N/A bcopy(digest, ntlm2_sess_hash, SMBAUTH_SESSION_HASH_SZ);
2N/A return (SMBAUTH_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * ntlm2_sess_rsp = DES(data=ntlm2_sess_hash, key=ntlm_hash)
2N/A * On success, returns SMBAUTH_SUCCESS. Otherwise, returns SMBAUTH_FAILURE.
2N/A */
2N/Aint
2N/Asmb_auth_ntlm2_session_response(unsigned char *session_nonce, int slen,
2N/A const unsigned char *ntlm_hash, int hlen, unsigned char *ntlm2_sess_rsp,
2N/A int rlen)
2N/A{
2N/A unsigned char ntlm2_sess_hash[SMBAUTH_SESSION_HASH_SZ];
2N/A unsigned char S21[21];
2N/A
2N/A if (smb_auth_ntlm2_session_hash(session_nonce, slen,
2N/A ntlm2_sess_hash) == SMBAUTH_FAILURE)
2N/A return (SMBAUTH_FAILURE);
2N/A
2N/A bzero(S21, 21);
2N/A bcopy(ntlm_hash, S21, hlen);
2N/A
2N/A return (smb_auth_DES(ntlm2_sess_rsp, rlen, S21, 21, ntlm2_sess_hash,
2N/A SMBAUTH_SESSION_HASH_SZ));
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_gen_data_blob
2N/A *
2N/A * Fill the NTLMv2 data blob structure with information as described in
2N/A * "Implementing CIFS, The Common Internet File System". (pg. 282)
2N/A */
2N/Astatic void
2N/Asmb_auth_gen_data_blob(smb_auth_data_blob_t *blob, char *ntdomain)
2N/A{
2N/A struct timeval now;
2N/A
2N/A (void) memset(blob->ndb_signature, 1, 2);
2N/A (void) memset(&blob->ndb_signature[2], 0, 2);
2N/A (void) memset(blob->ndb_reserved, 0, sizeof (blob->ndb_reserved));
2N/A
2N/A (void) gettimeofday(&now, 0);
2N/A blob->ndb_timestamp = unix_micro_to_nt_time(&now);
2N/A randomize((char *)blob->ndb_clnt_challenge,
2N/A SMBAUTH_V2_CLNT_CHALLENGE_SZ);
2N/A (void) memset(blob->ndb_unknown, 0, sizeof (blob->ndb_unknown));
2N/A blob->ndb_names[0].nne_len = smb_auth_qnd_unicode(
2N/A blob->ndb_names[0].nne_name, ntdomain, strlen(ntdomain));
2N/A blob->ndb_names[0].nne_type = SMBAUTH_NAME_TYPE_DOMAIN_NETBIOS;
2N/A blob->ndb_names[1].nne_len = 0;
2N/A blob->ndb_names[1].nne_type = SMBAUTH_NAME_TYPE_LIST_END;
2N/A *blob->ndb_names[1].nne_name = 0;
2N/A (void) memset(blob->ndb_unknown2, 0, sizeof (blob->ndb_unknown2));
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_memcpy
2N/A *
2N/A * It increments the pointer to the destination buffer for the easy of
2N/A * concatenation.
2N/A */
2N/Astatic void
2N/Asmb_auth_memcpy(unsigned char **dstbuf,
2N/A unsigned char *srcbuf,
2N/A int srcbuf_len)
2N/A{
2N/A (void) memcpy(*dstbuf, srcbuf, srcbuf_len);
2N/A *dstbuf += srcbuf_len;
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_blob_to_string
2N/A *
2N/A * Prepare the data blob string which will be used in NTLMv2 response
2N/A * generation.
2N/A *
2N/A * Assumption: Caller must allocate big enough buffer to prevent buffer
2N/A * overrun.
2N/A *
2N/A * Returns the len of the data blob string.
2N/A */
2N/Astatic int
2N/Asmb_auth_blob_to_string(smb_auth_data_blob_t *blob, unsigned char *data_blob)
2N/A{
2N/A unsigned char *bufp = data_blob;
2N/A
2N/A smb_auth_memcpy(&bufp, blob->ndb_signature,
2N/A sizeof (blob->ndb_signature));
2N/A smb_auth_memcpy(&bufp, blob->ndb_reserved,
2N/A sizeof (blob->ndb_reserved));
2N/A smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_timestamp,
2N/A sizeof (blob->ndb_timestamp));
2N/A smb_auth_memcpy(&bufp, blob->ndb_clnt_challenge,
2N/A SMBAUTH_V2_CLNT_CHALLENGE_SZ);
2N/A smb_auth_memcpy(&bufp, blob->ndb_unknown, sizeof (blob->ndb_unknown));
2N/A smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[0].nne_type,
2N/A sizeof (blob->ndb_names[0].nne_type));
2N/A smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[0].nne_len,
2N/A sizeof (blob->ndb_names[0].nne_len));
2N/A smb_auth_memcpy(&bufp, (unsigned char *)blob->ndb_names[0].nne_name,
2N/A blob->ndb_names[0].nne_len);
2N/A smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[1].nne_type,
2N/A sizeof (blob->ndb_names[1].nne_type));
2N/A smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[1].nne_len,
2N/A sizeof (blob->ndb_names[1].nne_len));
2N/A smb_auth_memcpy(&bufp, blob->ndb_unknown2, sizeof (blob->ndb_unknown2));
2N/A
2N/A /*LINTED E_PTRDIFF_OVERFLOW*/
2N/A return (bufp - data_blob);
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_ntlmv2_hash
2N/A *
2N/A * The NTLM v2 hash will be created from the given NTLM hash, username,
2N/A * and the NETBIOS name of the domain.
2N/A *
2N/A * The NTLMv2 hash will be returned via the ntlmv2_hash parameter which
2N/A * will be used in the calculation of the NTLMv2 and LMv2 responses.
2N/A */
2N/Aint
2N/Asmb_auth_ntlmv2_hash(unsigned char *ntlm_hash,
2N/A char *username,
2N/A char *ntdomain,
2N/A unsigned char *ntlmv2_hash)
2N/A{
2N/A smb_wchar_t *data;
2N/A int data_len;
2N/A unsigned char *buf;
2N/A int rc;
2N/A
2N/A if (username == NULL || ntdomain == NULL)
2N/A return (SMBAUTH_FAILURE);
2N/A
2N/A (void) smb_strupr(username);
2N/A
2N/A data_len = strlen(username) + strlen(ntdomain);
2N/A buf = (unsigned char *)malloc((data_len + 1) * sizeof (char));
2N/A if (buf == NULL)
2N/A return (SMBAUTH_FAILURE);
2N/A
2N/A (void) snprintf((char *)buf, data_len + 1, "%s%s", username, ntdomain);
2N/A data = (smb_wchar_t *)malloc((data_len + 1) * sizeof (smb_wchar_t));
2N/A if (data == NULL) {
2N/A free(buf);
2N/A return (SMBAUTH_FAILURE);
2N/A }
2N/A
2N/A data_len = smb_auth_qnd_unicode(data, (char *)buf, data_len);
2N/A rc = SMBAUTH_HMACT64((unsigned char *)data, data_len, ntlm_hash,
2N/A SMBAUTH_HASH_SZ, ntlmv2_hash);
2N/A
2N/A free(buf);
2N/A free(data);
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_v2_response
2N/A *
2N/A * Caculates either the LMv2 or NTLMv2 response.
2N/A *
2N/A * Same algorithm is used for calculating both LMv2 or NTLMv2 responses.
2N/A * This routine will return NTLMv2 response if the data blob information
2N/A * is passed in as the clnt_data. Otherwise, it will return LMv2 response
2N/A * with the 8-byte client challenge(a.k.a blip) as the clnt_data.
2N/A *
2N/A * (LM/NTLM)v2 response is the hmac-md5 hash of the specified data
2N/A * (server challenge + NTLMv2 data blob or LMv2 client challenge)
2N/A * using the NTLMv2 hash as the key.
2N/A *
2N/A * Returns the size of the corresponding v2 response upon success.
2N/A * Otherwise, returns -1 on error.
2N/A */
2N/Astatic int
2N/Asmb_auth_v2_response(
2N/A unsigned char *hash,
2N/A unsigned char *srv_challenge, int slen,
2N/A unsigned char *clnt_data, int clen,
2N/A unsigned char *v2_rsp)
2N/A{
2N/A unsigned char *hmac_data;
2N/A
2N/A hmac_data = (unsigned char *)malloc((slen + clen) * sizeof (char));
2N/A if (!hmac_data) {
2N/A return (-1);
2N/A }
2N/A
2N/A (void) memcpy(hmac_data, srv_challenge, slen);
2N/A (void) memcpy(&hmac_data[slen], clnt_data, clen);
2N/A if (SMBAUTH_HMACT64(hmac_data, slen + clen, (unsigned char *)hash,
2N/A SMBAUTH_HASH_SZ, (unsigned char *)v2_rsp) != SMBAUTH_SUCCESS)
2N/A return (-1);
2N/A (void) memcpy(&v2_rsp[SMBAUTH_HASH_SZ], clnt_data, clen);
2N/A
2N/A free(hmac_data);
2N/A return (SMBAUTH_HASH_SZ + clen);
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_set_info
2N/A *
2N/A * Fill the smb_auth_info instance with either NTLM or NTLMv2 related
2N/A * authentication information based on the LMCompatibilityLevel.
2N/A *
2N/A * If the LMCompatibilityLevel equals 2, the SMB Redirector will perform
2N/A * NTLM challenge/response authentication which requires the NTLM hash and
2N/A * NTLM response.
2N/A *
2N/A * If the LMCompatibilityLevel is 3 or above, the SMB Redirector will
2N/A * perfrom NTLMv2 challenge/response authenticatoin which requires the
2N/A * NTLM hash, NTLMv2 hash, NTLMv2 response and LMv2 response.
2N/A *
2N/A * Returns -1 on error. Otherwise, returns 0 upon success.
2N/A */
2N/Aint
2N/Asmb_auth_set_info(char *username,
2N/A char *password,
2N/A unsigned char *ntlm_hash,
2N/A char *domain,
2N/A unsigned char *srv_challenge_key,
2N/A int srv_challenge_len,
2N/A int lmcomp_lvl,
2N/A smb_auth_info_t *auth)
2N/A{
2N/A unsigned short blob_len;
2N/A unsigned char blob_buf[SMBAUTH_BLOB_MAXLEN];
2N/A int rc;
2N/A char *uppercase_dom;
2N/A
2N/A auth->lmcompatibility_lvl = lmcomp_lvl;
2N/A if (lmcomp_lvl == 2) {
2N/A auth->ci_len = 0;
2N/A *auth->ci = 0;
2N/A if (!ntlm_hash) {
2N/A if (smb_auth_ntlm_hash(password, auth->hash) !=
2N/A SMBAUTH_SUCCESS)
2N/A return (-1);
2N/A } else {
2N/A (void) memcpy(auth->hash, ntlm_hash, SMBAUTH_HASH_SZ);
2N/A }
2N/A
2N/A auth->cs_len = smb_auth_ntlm_response(auth->hash,
2N/A srv_challenge_key, srv_challenge_len, auth->cs);
2N/A } else {
2N/A if (!ntlm_hash) {
2N/A if (smb_auth_ntlm_hash(password, auth->hash) !=
2N/A SMBAUTH_SUCCESS)
2N/A return (-1);
2N/A } else {
2N/A (void) memcpy(auth->hash, ntlm_hash, SMBAUTH_HASH_SZ);
2N/A }
2N/A
2N/A if (!domain)
2N/A return (-1);
2N/A
2N/A if ((uppercase_dom = strdup(domain)) == NULL)
2N/A return (-1);
2N/A
2N/A (void) smb_strupr(uppercase_dom);
2N/A
2N/A if (smb_auth_ntlmv2_hash(auth->hash, username,
2N/A uppercase_dom, auth->hash_v2) != SMBAUTH_SUCCESS) {
2N/A free(uppercase_dom);
2N/A return (-1);
2N/A }
2N/A
2N/A /* generate data blob */
2N/A smb_auth_gen_data_blob(&auth->data_blob, uppercase_dom);
2N/A free(uppercase_dom);
2N/A blob_len = smb_auth_blob_to_string(&auth->data_blob, blob_buf);
2N/A
2N/A /* generate NTLMv2 response */
2N/A rc = smb_auth_v2_response(auth->hash_v2, srv_challenge_key,
2N/A srv_challenge_len, blob_buf, blob_len, auth->cs);
2N/A
2N/A if (rc < 0)
2N/A return (-1);
2N/A
2N/A auth->cs_len = rc;
2N/A
2N/A /* generate LMv2 response */
2N/A rc = smb_auth_v2_response(auth->hash_v2, srv_challenge_key,
2N/A srv_challenge_len, auth->data_blob.ndb_clnt_challenge,
2N/A SMBAUTH_V2_CLNT_CHALLENGE_SZ, auth->ci);
2N/A
2N/A if (rc < 0)
2N/A return (-1);
2N/A
2N/A auth->ci_len = rc;
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_gen_session_key
2N/A *
2N/A * Generate the NTLM user session key if LMCompatibilityLevel is 2 or
2N/A * NTLMv2 user session key if LMCompatibilityLevel is 3 or above.
2N/A *
2N/A * NTLM_Session_Key = MD4(NTLM_Hash);
2N/A *
2N/A * NTLMv2_Session_Key = HMAC_MD5(NTLMv2Hash, 16, NTLMv2_HMAC, 16)
2N/A *
2N/A * Prior to calling this function, the auth instance should be set
2N/A * via smb_auth_set_info().
2N/A *
2N/A * Returns the appropriate session key.
2N/A */
2N/Aint
2N/Asmb_auth_gen_session_key(smb_auth_info_t *auth, unsigned char *session_key)
2N/A{
2N/A int rc;
2N/A
2N/A if (auth->lmcompatibility_lvl == 2)
2N/A rc = smb_auth_md4(session_key, auth->hash, SMBAUTH_HASH_SZ);
2N/A else
2N/A rc = SMBAUTH_HMACT64((unsigned char *)auth->cs,
2N/A SMBAUTH_HASH_SZ, (unsigned char *)auth->hash_v2,
2N/A SMBAUTH_SESSION_KEY_SZ, session_key);
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/* 100's of ns between 1/1/1970 and 1/1/1601 */
2N/A#define NT_TIME_BIAS (134774LL * 24LL * 60LL * 60LL * 10000000LL)
2N/A
2N/Astatic uint64_t
2N/Aunix_micro_to_nt_time(struct timeval *unix_time)
2N/A{
2N/A uint64_t nt_time;
2N/A
2N/A nt_time = unix_time->tv_sec;
2N/A nt_time *= 10000000; /* seconds to 100ns */
2N/A nt_time += unix_time->tv_usec * 10;
2N/A return (nt_time + NT_TIME_BIAS);
2N/A}
2N/A
2N/A/*
2N/A * Local Authentication
2N/A */
2N/Astatic boolean_t
2N/Asmb_lm_password_ok(
2N/A unsigned char *challenge,
2N/A uint32_t clen,
2N/A unsigned char *lm_hash,
2N/A unsigned char *passwd)
2N/A{
2N/A unsigned char lm_resp[SMBAUTH_RESP_SZ];
2N/A int rc;
2N/A
2N/A rc = smb_auth_lm_response(lm_hash, challenge, clen, lm_resp);
2N/A if (rc != SMBAUTH_SUCCESS)
2N/A return (B_FALSE);
2N/A
2N/A return (bcmp(lm_resp, passwd, SMBAUTH_RESP_SZ) == 0);
2N/A}
2N/A
2N/Astatic boolean_t
2N/Asmb_ntlm_password_ok(
2N/A unsigned char *challenge,
2N/A uint32_t clen,
2N/A unsigned char *ntlm_hash,
2N/A unsigned char *passwd,
2N/A unsigned char *session_key)
2N/A{
2N/A unsigned char ntlm_resp[SMBAUTH_RESP_SZ];
2N/A int rc;
2N/A boolean_t ok;
2N/A
2N/A rc = smb_auth_ntlm_response(ntlm_hash, challenge, clen, ntlm_resp);
2N/A if (rc != SMBAUTH_RESP_SZ)
2N/A return (B_FALSE);
2N/A
2N/A ok = (bcmp(ntlm_resp, passwd, SMBAUTH_RESP_SZ) == 0);
2N/A if (ok && (session_key)) {
2N/A rc = smb_auth_md4(session_key, ntlm_hash, SMBAUTH_HASH_SZ);
2N/A if (rc != SMBAUTH_SUCCESS)
2N/A ok = B_FALSE;
2N/A }
2N/A return (ok);
2N/A}
2N/A
2N/Astatic boolean_t
2N/Asmb_ntlmv2_password_ok(
2N/A unsigned char *challenge,
2N/A uint32_t clen,
2N/A unsigned char *ntlm_hash,
2N/A unsigned char *passwd,
2N/A int pwdlen,
2N/A char *domain,
2N/A char *username,
2N/A uchar_t *session_key)
2N/A{
2N/A unsigned char *clnt_blob;
2N/A int clnt_blob_len;
2N/A unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ];
2N/A unsigned char *ntlmv2_resp;
2N/A boolean_t ok = B_FALSE;
2N/A char *dest[3];
2N/A int i;
2N/A int rc;
2N/A
2N/A clnt_blob_len = pwdlen - SMBAUTH_HASH_SZ;
2N/A clnt_blob = &passwd[SMBAUTH_HASH_SZ];
2N/A dest[0] = domain;
2N/A if ((dest[1] = strdup(domain)) == NULL)
2N/A return (B_FALSE);
2N/A (void) smb_strupr(dest[1]);
2N/A dest[2] = "";
2N/A
2N/A /*
2N/A * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS"
2N/A *
2N/A * The NTLMv2 Hash is created from:
2N/A * - NTLM hash
2N/A * - user's username, and
2N/A * - the name of the logon destination(i.e. the NetBIOS name of either
2N/A * the SMB server or NT Domain against which the user is trying to
2N/A * authenticate.
2N/A *
2N/A * Experiments show this is not exactly the case.
2N/A * For Windows Server 2003, the domain name needs to be included and
2N/A * converted to uppercase. For Vista, the domain name needs to be
2N/A * included also, but leave the case alone. And in some cases it needs
2N/A * to be empty. All three variants are tried here.
2N/A */
2N/A
2N/A ntlmv2_resp = (unsigned char *)malloc(SMBAUTH_HASH_SZ + clnt_blob_len);
2N/A if (ntlmv2_resp == NULL) {
2N/A free(dest[1]);
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A for (i = 0; i < (sizeof (dest) / sizeof (char *)); i++) {
2N/A if (smb_auth_ntlmv2_hash(ntlm_hash, username, dest[i],
2N/A ntlmv2_hash) != SMBAUTH_SUCCESS)
2N/A break;
2N/A
2N/A if (smb_auth_v2_response(ntlmv2_hash, challenge,
2N/A clen, clnt_blob, clnt_blob_len, ntlmv2_resp) < 0)
2N/A break;
2N/A
2N/A ok = (bcmp(passwd, ntlmv2_resp, pwdlen) == 0);
2N/A if (ok && session_key) {
2N/A rc = SMBAUTH_HMACT64(ntlmv2_resp,
2N/A SMBAUTH_HASH_SZ, ntlmv2_hash,
2N/A SMBAUTH_SESSION_KEY_SZ, session_key);
2N/A if (rc != SMBAUTH_SUCCESS) {
2N/A ok = B_FALSE;
2N/A }
2N/A break;
2N/A }
2N/A }
2N/A
2N/A free(dest[1]);
2N/A free(ntlmv2_resp);
2N/A return (ok);
2N/A}
2N/A
2N/A/*
2N/A * NTLM2 Session Response User Session Key
2N/A *
2N/A * Used when NTLMv1 authentication is employed with NTLM2 session security.
2N/A * This key is derived from the NTLM2 session response information as follows:
2N/A * The HMAC-MD5 algorithm is applied to the session nonce, using the NTLM
2N/A * User Session Key as the key. The resulting 16-byte value is the NTLM2
2N/A * Session Response User Session Key.
2N/A */
2N/Aint
2N/Asmb_auth_ntlm2_usr_ssn_key(
2N/A const unsigned char *ntlm_hash, const unsigned char *sess_nonce,
2N/A unsigned char *result)
2N/A{
2N/A unsigned char ntlm_usr_sess_key[SMBAUTH_HASH_SZ];
2N/A
2N/A if (smb_auth_md4(ntlm_usr_sess_key, ntlm_hash, SMBAUTH_HASH_SZ)
2N/A != SMBAUTH_SUCCESS)
2N/A return (SMBAUTH_FAILURE);
2N/A
2N/A return (SMBAUTH_HMACT64(sess_nonce, SMBAUTH_SESSION_NONCE_SZ,
2N/A ntlm_usr_sess_key, SMBAUTH_SESSION_KEY_SZ, result));
2N/A
2N/A}
2N/A
2N/Astatic boolean_t
2N/Asmb_ntlm2_session_password_ok(
2N/A unsigned char *challenge,
2N/A uint32_t clen,
2N/A unsigned char *ntlm_hash,
2N/A unsigned char *passwd,
2N/A int pwdlen,
2N/A unsigned char *nonce,
2N/A int nlen,
2N/A uchar_t *session_key)
2N/A{
2N/A
2N/A unsigned char ntlm2_sess_resp[SMBAUTH_RESP_SZ];
2N/A unsigned char ntlm2_sess_nonce[SMBAUTH_SESSION_NONCE_SZ];
2N/A
2N/A bcopy(challenge, ntlm2_sess_nonce, clen);
2N/A bcopy(nonce, ntlm2_sess_nonce + clen, nlen);
2N/A
2N/A if (smb_auth_ntlm2_session_response(ntlm2_sess_nonce,
2N/A SMBAUTH_SESSION_NONCE_SZ, ntlm_hash, SMBAUTH_HASH_SZ,
2N/A ntlm2_sess_resp, SMBAUTH_RESP_SZ) != SMBAUTH_SUCCESS)
2N/A return (B_FALSE);
2N/A
2N/A if (smb_auth_ntlm2_usr_ssn_key(ntlm_hash, ntlm2_sess_nonce,
2N/A session_key) != SMBAUTH_SUCCESS)
2N/A return (B_FALSE);
2N/A
2N/A return (bcmp(passwd, ntlm2_sess_resp, pwdlen) == 0);
2N/A}
2N/A
2N/Astatic boolean_t
2N/Asmb_lmv2_password_ok(
2N/A unsigned char *challenge,
2N/A uint32_t clen,
2N/A unsigned char *ntlm_hash,
2N/A unsigned char *passwd,
2N/A char *domain,
2N/A char *username)
2N/A{
2N/A unsigned char *clnt_challenge;
2N/A unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ];
2N/A unsigned char lmv2_resp[SMBAUTH_RESP_SZ];
2N/A boolean_t ok = B_FALSE;
2N/A char *dest[3];
2N/A int i;
2N/A
2N/A clnt_challenge = &passwd[SMBAUTH_HASH_SZ];
2N/A dest[0] = domain;
2N/A if ((dest[1] = strdup(domain)) == NULL)
2N/A return (B_FALSE);
2N/A (void) smb_strupr(dest[1]);
2N/A dest[2] = "";
2N/A
2N/A /*
2N/A * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS"
2N/A *
2N/A * The NTLMv2 Hash is created from:
2N/A * - NTLM hash
2N/A * - user's username, and
2N/A * - the name of the logon destination(i.e. the NetBIOS name of either
2N/A * the SMB server or NT Domain against which the suer is trying to
2N/A * authenticate.
2N/A *
2N/A * Experiments show this is not exactly the case.
2N/A * For Windows Server 2003, the domain name needs to be included and
2N/A * converted to uppercase. For Vista, the domain name needs to be
2N/A * included also, but leave the case alone. And in some cases it needs
2N/A * to be empty. All three variants are tried here.
2N/A */
2N/A
2N/A for (i = 0; i < (sizeof (dest) / sizeof (char *)); i++) {
2N/A if (smb_auth_ntlmv2_hash(ntlm_hash, username, dest[i],
2N/A ntlmv2_hash) != SMBAUTH_SUCCESS)
2N/A break;
2N/A
2N/A if (smb_auth_v2_response(ntlmv2_hash, challenge,
2N/A clen, clnt_challenge, SMBAUTH_V2_CLNT_CHALLENGE_SZ,
2N/A lmv2_resp) < 0)
2N/A break;
2N/A
2N/A ok = (bcmp(passwd, lmv2_resp, SMBAUTH_RESP_SZ) == 0);
2N/A if (ok)
2N/A break;
2N/A }
2N/A
2N/A free(dest[1]);
2N/A return (ok);
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_validate_lm
2N/A *
2N/A * Validates given LM/LMv2 client response, passed in passwd arg, against
2N/A * stored user's password, passed in smbpw
2N/A *
2N/A * If LM level <=3 server accepts LM responses, otherwise LMv2
2N/A */
2N/Aboolean_t
2N/Asmb_auth_validate_lm(
2N/A unsigned char *challenge,
2N/A uint32_t clen,
2N/A smb_passwd_t *smbpw,
2N/A unsigned char *passwd,
2N/A int pwdlen,
2N/A char *domain,
2N/A char *username)
2N/A{
2N/A boolean_t ok = B_FALSE;
2N/A int64_t lmlevel;
2N/A
2N/A if (pwdlen != SMBAUTH_RESP_SZ)
2N/A return (B_FALSE);
2N/A
2N/A if (smb_config_getnum(SMB_CI_SVR_LM_LEVEL, &lmlevel) != SMBD_SMF_OK)
2N/A return (B_FALSE);
2N/A
2N/A if (lmlevel <= 3) {
2N/A ok = smb_lm_password_ok(challenge, clen, smbpw->pw_lmhash,
2N/A passwd);
2N/A }
2N/A
2N/A if (!ok)
2N/A ok = smb_lmv2_password_ok(challenge, clen, smbpw->pw_nthash,
2N/A passwd, domain, username);
2N/A
2N/A return (ok);
2N/A}
2N/A
2N/A/*
2N/A * smb_auth_validate_nt
2N/A *
2N/A * Validates given NTLM/NTLMv2/NTLM 2 session response sent by the client,
2N/A * passed in passwd arg, against stored user's password, passed in smbpw
2N/A *
2N/A * If LM level <=4 server accepts NTLM/NTLMv2 responses, otherwise only NTLMv2.
2N/A */
2N/Aboolean_t
2N/Asmb_auth_validate_nt(
2N/A unsigned char *challenge,
2N/A uint32_t clen,
2N/A smb_passwd_t *smbpw,
2N/A unsigned char *passwd,
2N/A int pwdlen,
2N/A char *domain,
2N/A char *username,
2N/A unsigned char *nonce,
2N/A int nlen,
2N/A uchar_t *session_key)
2N/A{
2N/A int64_t lmlevel;
2N/A boolean_t ok;
2N/A
2N/A if (smb_config_getnum(SMB_CI_SVR_LM_LEVEL, &lmlevel) != SMBD_SMF_OK)
2N/A return (B_FALSE);
2N/A
2N/A if ((lmlevel == 5) && (pwdlen <= SMBAUTH_RESP_SZ))
2N/A return (B_FALSE);
2N/A
2N/A if (pwdlen > SMBAUTH_RESP_SZ)
2N/A ok = smb_ntlmv2_password_ok(challenge, clen,
2N/A smbpw->pw_nthash, passwd, pwdlen,
2N/A domain, username, session_key);
2N/A else
2N/A ok = smb_ntlm_password_ok(challenge, clen,
2N/A smbpw->pw_nthash, passwd, session_key);
2N/A
2N/A if (!ok && (nonce != NULL))
2N/A ok = smb_ntlm2_session_password_ok(challenge, clen,
2N/A smbpw->pw_nthash, passwd, pwdlen, nonce, nlen,
2N/A session_key);
2N/A return (ok);
2N/A}