diff options
Diffstat (limited to 'fs/ksmbd/auth.c')
-rw-r--r-- | fs/ksmbd/auth.c | 1206 |
1 files changed, 0 insertions, 1206 deletions
diff --git a/fs/ksmbd/auth.c b/fs/ksmbd/auth.c deleted file mode 100644 index df8fb076f6f1..000000000000 --- a/fs/ksmbd/auth.c +++ /dev/null @@ -1,1206 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org> - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/uaccess.h> -#include <linux/backing-dev.h> -#include <linux/writeback.h> -#include <linux/uio.h> -#include <linux/xattr.h> -#include <crypto/hash.h> -#include <crypto/aead.h> -#include <linux/random.h> -#include <linux/scatterlist.h> - -#include "auth.h" -#include "glob.h" - -#include <linux/fips.h> -#include <crypto/des.h> - -#include "server.h" -#include "smb_common.h" -#include "connection.h" -#include "mgmt/user_session.h" -#include "mgmt/user_config.h" -#include "crypto_ctx.h" -#include "transport_ipc.h" -#include "../smbfs_common/arc4.h" - -/* - * Fixed format data defining GSS header and fixed string - * "not_defined_in_RFC4178@please_ignore". - * So sec blob data in neg phase could be generated statically. - */ -static char NEGOTIATE_GSS_HEADER[AUTH_GSS_LENGTH] = { -#ifdef CONFIG_SMB_SERVER_KERBEROS5 - 0x60, 0x5e, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, - 0x05, 0x02, 0xa0, 0x54, 0x30, 0x52, 0xa0, 0x24, - 0x30, 0x22, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2a, - 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02, - 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, - 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, 0x30, 0x28, - 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, 0x74, 0x5f, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, - 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, 0x34, 0x31, - 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, 0x61, 0x73, - 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65 -#else - 0x60, 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, - 0x05, 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, - 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, - 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, - 0x30, 0x28, 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, - 0x74, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, - 0x34, 0x31, 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, - 0x61, 0x73, 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, - 0x72, 0x65 -#endif -}; - -void ksmbd_copy_gss_neg_header(void *buf) -{ - memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH); -} - -/** - * ksmbd_gen_sess_key() - function to generate session key - * @sess: session of connection - * @hash: source hash value to be used for find session key - * @hmac: source hmac value to be used for finding session key - * - */ -static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, - char *hmac) -{ - struct ksmbd_crypto_ctx *ctx; - int rc; - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - hash, - CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc); - goto out; - } - - rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc); - goto out; - } - - rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), - hmac, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) { - ksmbd_debug(AUTH, "Could not update with response error %d\n", rc); - goto out; - } - - rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc); - goto out; - } - -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, - char *ntlmv2_hash, char *dname) -{ - int ret, len, conv_len; - wchar_t *domain = NULL; - __le16 *uniname = NULL; - struct ksmbd_crypto_ctx *ctx; - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n"); - return -ENOMEM; - } - - ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - user_passkey(sess->user), - CIFS_ENCPWD_SIZE); - if (ret) { - ksmbd_debug(AUTH, "Could not set NT Hash as a key\n"); - goto out; - } - - ret = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (ret) { - ksmbd_debug(AUTH, "could not init hmacmd5\n"); - goto out; - } - - /* convert user_name to unicode */ - len = strlen(user_name(sess->user)); - uniname = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); - if (!uniname) { - ret = -ENOMEM; - goto out; - } - - conv_len = smb_strtoUTF16(uniname, user_name(sess->user), len, - conn->local_nls); - if (conv_len < 0 || conv_len > len) { - ret = -EINVAL; - goto out; - } - UniStrupr(uniname); - - ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), - (char *)uniname, - UNICODE_LEN(conv_len)); - if (ret) { - ksmbd_debug(AUTH, "Could not update with user\n"); - goto out; - } - - /* Convert domain name or conn name to unicode and uppercase */ - len = strlen(dname); - domain = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); - if (!domain) { - ret = -ENOMEM; - goto out; - } - - conv_len = smb_strtoUTF16((__le16 *)domain, dname, len, - conn->local_nls); - if (conv_len < 0 || conv_len > len) { - ret = -EINVAL; - goto out; - } - - ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), - (char *)domain, - UNICODE_LEN(conv_len)); - if (ret) { - ksmbd_debug(AUTH, "Could not update with domain\n"); - goto out; - } - - ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash); - if (ret) - ksmbd_debug(AUTH, "Could not generate md5 hash\n"); -out: - kfree(uniname); - kfree(domain); - ksmbd_release_crypto_ctx(ctx); - return ret; -} - -/** - * ksmbd_auth_ntlmv2() - NTLMv2 authentication handler - * @sess: session of connection - * @ntlmv2: NTLMv2 challenge response - * @blen: NTLMv2 blob length - * @domain_name: domain name - * - * Return: 0 on success, error number on error - */ -int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, - struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, - char *cryptkey) -{ - char ntlmv2_hash[CIFS_ENCPWD_SIZE]; - char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; - struct ksmbd_crypto_ctx *ctx = NULL; - char *construct = NULL; - int rc, len; - - rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name); - if (rc) { - ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); - goto out; - } - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - ntlmv2_hash, - CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n"); - goto out; - } - - rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (rc) { - ksmbd_debug(AUTH, "Could not init hmacmd5\n"); - goto out; - } - - len = CIFS_CRYPTO_KEY_SIZE + blen; - construct = kzalloc(len, GFP_KERNEL); - if (!construct) { - rc = -ENOMEM; - goto out; - } - - memcpy(construct, cryptkey, CIFS_CRYPTO_KEY_SIZE); - memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen); - - rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len); - if (rc) { - ksmbd_debug(AUTH, "Could not update with response\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp); - if (rc) { - ksmbd_debug(AUTH, "Could not generate md5 hash\n"); - goto out; - } - ksmbd_release_crypto_ctx(ctx); - ctx = NULL; - - rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp); - if (rc) { - ksmbd_debug(AUTH, "Could not generate sess key\n"); - goto out; - } - - if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0) - rc = -EINVAL; -out: - if (ctx) - ksmbd_release_crypto_ctx(ctx); - kfree(construct); - return rc; -} - -/** - * ksmbd_decode_ntlmssp_auth_blob() - helper function to construct - * authenticate blob - * @authblob: authenticate blob source pointer - * @usr: user details - * @sess: session of connection - * - * Return: 0 on success, error number on error - */ -int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, - int blob_len, struct ksmbd_conn *conn, - struct ksmbd_session *sess) -{ - char *domain_name; - unsigned int nt_off, dn_off; - unsigned short nt_len, dn_len; - int ret; - - if (blob_len < sizeof(struct authenticate_message)) { - ksmbd_debug(AUTH, "negotiate blob len %d too small\n", - blob_len); - return -EINVAL; - } - - if (memcmp(authblob->Signature, "NTLMSSP", 8)) { - ksmbd_debug(AUTH, "blob signature incorrect %s\n", - authblob->Signature); - return -EINVAL; - } - - nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset); - nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length); - dn_off = le32_to_cpu(authblob->DomainName.BufferOffset); - dn_len = le16_to_cpu(authblob->DomainName.Length); - - if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len || - nt_len < CIFS_ENCPWD_SIZE) - return -EINVAL; - - /* TODO : use domain name that imported from configuration file */ - domain_name = smb_strndup_from_utf16((const char *)authblob + dn_off, - dn_len, true, conn->local_nls); - if (IS_ERR(domain_name)) - return PTR_ERR(domain_name); - - /* process NTLMv2 authentication */ - ksmbd_debug(AUTH, "decode_ntlmssp_authenticate_blob dname%s\n", - domain_name); - ret = ksmbd_auth_ntlmv2(conn, sess, - (struct ntlmv2_resp *)((char *)authblob + nt_off), - nt_len - CIFS_ENCPWD_SIZE, - domain_name, conn->ntlmssp.cryptkey); - kfree(domain_name); - - /* The recovered secondary session key */ - if (conn->ntlmssp.client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) { - struct arc4_ctx *ctx_arc4; - unsigned int sess_key_off, sess_key_len; - - sess_key_off = le32_to_cpu(authblob->SessionKey.BufferOffset); - sess_key_len = le16_to_cpu(authblob->SessionKey.Length); - - if (blob_len < (u64)sess_key_off + sess_key_len) - return -EINVAL; - - ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); - if (!ctx_arc4) - return -ENOMEM; - - cifs_arc4_setkey(ctx_arc4, sess->sess_key, - SMB2_NTLMV2_SESSKEY_SIZE); - cifs_arc4_crypt(ctx_arc4, sess->sess_key, - (char *)authblob + sess_key_off, sess_key_len); - kfree_sensitive(ctx_arc4); - } - - return ret; -} - -/** - * ksmbd_decode_ntlmssp_neg_blob() - helper function to construct - * negotiate blob - * @negblob: negotiate blob source pointer - * @rsp: response header pointer to be updated - * @sess: session of connection - * - */ -int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, - int blob_len, struct ksmbd_conn *conn) -{ - if (blob_len < sizeof(struct negotiate_message)) { - ksmbd_debug(AUTH, "negotiate blob len %d too small\n", - blob_len); - return -EINVAL; - } - - if (memcmp(negblob->Signature, "NTLMSSP", 8)) { - ksmbd_debug(AUTH, "blob signature incorrect %s\n", - negblob->Signature); - return -EINVAL; - } - - conn->ntlmssp.client_flags = le32_to_cpu(negblob->NegotiateFlags); - return 0; -} - -/** - * ksmbd_build_ntlmssp_challenge_blob() - helper function to construct - * challenge blob - * @chgblob: challenge blob source pointer to initialize - * @rsp: response header pointer to be updated - * @sess: session of connection - * - */ -unsigned int -ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, - struct ksmbd_conn *conn) -{ - struct target_info *tinfo; - wchar_t *name; - __u8 *target_name; - unsigned int flags, blob_off, blob_len, type, target_info_len = 0; - int len, uni_len, conv_len; - int cflags = conn->ntlmssp.client_flags; - - memcpy(chgblob->Signature, NTLMSSP_SIGNATURE, 8); - chgblob->MessageType = NtLmChallenge; - - flags = NTLMSSP_NEGOTIATE_UNICODE | - NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_TARGET_TYPE_SERVER | - NTLMSSP_NEGOTIATE_TARGET_INFO; - - if (cflags & NTLMSSP_NEGOTIATE_SIGN) { - flags |= NTLMSSP_NEGOTIATE_SIGN; - flags |= cflags & (NTLMSSP_NEGOTIATE_128 | - NTLMSSP_NEGOTIATE_56); - } - - if (cflags & NTLMSSP_NEGOTIATE_SEAL && smb3_encryption_negotiated(conn)) - flags |= NTLMSSP_NEGOTIATE_SEAL; - - if (cflags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) - flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; - - if (cflags & NTLMSSP_REQUEST_TARGET) - flags |= NTLMSSP_REQUEST_TARGET; - - if (conn->use_spnego && - (cflags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) - flags |= NTLMSSP_NEGOTIATE_EXTENDED_SEC; - - if (cflags & NTLMSSP_NEGOTIATE_KEY_XCH) - flags |= NTLMSSP_NEGOTIATE_KEY_XCH; - - chgblob->NegotiateFlags = cpu_to_le32(flags); - len = strlen(ksmbd_netbios_name()); - name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL); - if (!name) - return -ENOMEM; - - conv_len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len, - conn->local_nls); - if (conv_len < 0 || conv_len > len) { - kfree(name); - return -EINVAL; - } - - uni_len = UNICODE_LEN(conv_len); - - blob_off = sizeof(struct challenge_message); - blob_len = blob_off + uni_len; - - chgblob->TargetName.Length = cpu_to_le16(uni_len); - chgblob->TargetName.MaximumLength = cpu_to_le16(uni_len); - chgblob->TargetName.BufferOffset = cpu_to_le32(blob_off); - - /* Initialize random conn challenge */ - get_random_bytes(conn->ntlmssp.cryptkey, sizeof(__u64)); - memcpy(chgblob->Challenge, conn->ntlmssp.cryptkey, - CIFS_CRYPTO_KEY_SIZE); - - /* Add Target Information to security buffer */ - chgblob->TargetInfoArray.BufferOffset = cpu_to_le32(blob_len); - - target_name = (__u8 *)chgblob + blob_off; - memcpy(target_name, name, uni_len); - tinfo = (struct target_info *)(target_name + uni_len); - - chgblob->TargetInfoArray.Length = 0; - /* Add target info list for NetBIOS/DNS settings */ - for (type = NTLMSSP_AV_NB_COMPUTER_NAME; - type <= NTLMSSP_AV_DNS_DOMAIN_NAME; type++) { - tinfo->Type = cpu_to_le16(type); - tinfo->Length = cpu_to_le16(uni_len); - memcpy(tinfo->Content, name, uni_len); - tinfo = (struct target_info *)((char *)tinfo + 4 + uni_len); - target_info_len += 4 + uni_len; - } - - /* Add terminator subblock */ - tinfo->Type = 0; - tinfo->Length = 0; - target_info_len += 4; - - chgblob->TargetInfoArray.Length = cpu_to_le16(target_info_len); - chgblob->TargetInfoArray.MaximumLength = cpu_to_le16(target_info_len); - blob_len += target_info_len; - kfree(name); - ksmbd_debug(AUTH, "NTLMSSP SecurityBufferLength %d\n", blob_len); - return blob_len; -} - -#ifdef CONFIG_SMB_SERVER_KERBEROS5 -int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len) -{ - struct ksmbd_spnego_authen_response *resp; - struct ksmbd_user *user = NULL; - int retval; - - resp = ksmbd_ipc_spnego_authen_request(in_blob, in_len); - if (!resp) { - ksmbd_debug(AUTH, "SPNEGO_AUTHEN_REQUEST failure\n"); - return -EINVAL; - } - - if (!(resp->login_response.status & KSMBD_USER_FLAG_OK)) { - ksmbd_debug(AUTH, "krb5 authentication failure\n"); - retval = -EPERM; - goto out; - } - - if (*out_len <= resp->spnego_blob_len) { - ksmbd_debug(AUTH, "buf len %d, but blob len %d\n", - *out_len, resp->spnego_blob_len); - retval = -EINVAL; - goto out; - } - - if (resp->session_key_len > sizeof(sess->sess_key)) { - ksmbd_debug(AUTH, "session key is too long\n"); - retval = -EINVAL; - goto out; - } - - user = ksmbd_alloc_user(&resp->login_response); - if (!user) { - ksmbd_debug(AUTH, "login failure\n"); - retval = -ENOMEM; - goto out; - } - sess->user = user; - - memcpy(sess->sess_key, resp->payload, resp->session_key_len); - memcpy(out_blob, resp->payload + resp->session_key_len, - resp->spnego_blob_len); - *out_len = resp->spnego_blob_len; - retval = 0; -out: - kvfree(resp); - return retval; -} -#else -int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len) -{ - return -EOPNOTSUPP; -} -#endif - -/** - * ksmbd_sign_smb2_pdu() - function to generate packet signing - * @conn: connection - * @key: signing key - * @iov: buffer iov array - * @n_vec: number of iovecs - * @sig: signature value generated for client request packet - * - */ -int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig) -{ - struct ksmbd_crypto_ctx *ctx; - int rc, i; - - ctx = ksmbd_crypto_ctx_find_hmacsha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), - key, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) - goto out; - - rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); - goto out; - } - - for (i = 0; i < n_vec; i++) { - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - iov[i].iov_base, - iov[i].iov_len); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc); - goto out; - } - } - - rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig); - if (rc) - ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc); -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -/** - * ksmbd_sign_smb3_pdu() - function to generate packet signing - * @conn: connection - * @key: signing key - * @iov: buffer iov array - * @n_vec: number of iovecs - * @sig: signature value generated for client request packet - * - */ -int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig) -{ - struct ksmbd_crypto_ctx *ctx; - int rc, i; - - ctx = ksmbd_crypto_ctx_find_cmacaes(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc cmac\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx), - key, - SMB2_CMACAES_SIZE); - if (rc) - goto out; - - rc = crypto_shash_init(CRYPTO_CMACAES(ctx)); - if (rc) { - ksmbd_debug(AUTH, "cmaces init error %d\n", rc); - goto out; - } - - for (i = 0; i < n_vec; i++) { - rc = crypto_shash_update(CRYPTO_CMACAES(ctx), - iov[i].iov_base, - iov[i].iov_len); - if (rc) { - ksmbd_debug(AUTH, "cmaces update error %d\n", rc); - goto out; - } - } - - rc = crypto_shash_final(CRYPTO_CMACAES(ctx), sig); - if (rc) - ksmbd_debug(AUTH, "cmaces generation error %d\n", rc); -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -struct derivation { - struct kvec label; - struct kvec context; - bool binding; -}; - -static int generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, - struct kvec label, struct kvec context, __u8 *key, - unsigned int key_size) -{ - unsigned char zero = 0x0; - __u8 i[4] = {0, 0, 0, 1}; - __u8 L128[4] = {0, 0, 0, 128}; - __u8 L256[4] = {0, 0, 1, 0}; - int rc; - unsigned char prfhash[SMB2_HMACSHA256_SIZE]; - unsigned char *hashptr = prfhash; - struct ksmbd_crypto_ctx *ctx; - - memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); - memset(key, 0x0, key_size); - - ctx = ksmbd_crypto_ctx_find_hmacsha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), - sess->sess_key, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) - goto smb3signkey_ret; - - rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - label.iov_base, - label.iov_len); - if (rc) { - ksmbd_debug(AUTH, "could not update with label\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1); - if (rc) { - ksmbd_debug(AUTH, "could not update with zero\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - context.iov_base, - context.iov_len); - if (rc) { - ksmbd_debug(AUTH, "could not update with context\n"); - goto smb3signkey_ret; - } - - if (key_size == SMB3_ENC_DEC_KEY_SIZE && - (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4); - else - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4); - if (rc) { - ksmbd_debug(AUTH, "could not update with L\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", - rc); - goto smb3signkey_ret; - } - - memcpy(key, hashptr, key_size); - -smb3signkey_ret: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -static int generate_smb3signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn, - const struct derivation *signing) -{ - int rc; - struct channel *chann; - char *key; - - chann = lookup_chann_list(sess, conn); - if (!chann) - return 0; - - if (conn->dialect >= SMB30_PROT_ID && signing->binding) - key = chann->smb3signingkey; - else - key = sess->smb3signingkey; - - rc = generate_key(conn, sess, signing->label, signing->context, key, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; - - if (!(conn->dialect >= SMB30_PROT_ID && signing->binding)) - memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE); - - ksmbd_debug(AUTH, "dumping generated AES signing keys\n"); - ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); - ksmbd_debug(AUTH, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); - ksmbd_debug(AUTH, "Signing Key %*ph\n", - SMB3_SIGN_KEY_SIZE, key); - return 0; -} - -int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn) -{ - struct derivation d; - - d.label.iov_base = "SMB2AESCMAC"; - d.label.iov_len = 12; - d.context.iov_base = "SmbSign"; - d.context.iov_len = 8; - d.binding = conn->binding; - - return generate_smb3signingkey(sess, conn, &d); -} - -int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn) -{ - struct derivation d; - - d.label.iov_base = "SMBSigningKey"; - d.label.iov_len = 14; - if (conn->binding) { - struct preauth_session *preauth_sess; - - preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); - if (!preauth_sess) - return -ENOENT; - d.context.iov_base = preauth_sess->Preauth_HashValue; - } else { - d.context.iov_base = sess->Preauth_HashValue; - } - d.context.iov_len = 64; - d.binding = conn->binding; - - return generate_smb3signingkey(sess, conn, &d); -} - -struct derivation_twin { - struct derivation encryption; - struct derivation decryption; -}; - -static int generate_smb3encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess, - const struct derivation_twin *ptwin) -{ - int rc; - - rc = generate_key(conn, sess, ptwin->encryption.label, - ptwin->encryption.context, sess->smb3encryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; - - rc = generate_key(conn, sess, ptwin->decryption.label, - ptwin->decryption.context, - sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; - - ksmbd_debug(AUTH, "dumping generated AES encryption keys\n"); - ksmbd_debug(AUTH, "Cipher type %d\n", conn->cipher_type); - ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); - ksmbd_debug(AUTH, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); - if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { - ksmbd_debug(AUTH, "ServerIn Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3encryptionkey); - ksmbd_debug(AUTH, "ServerOut Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3decryptionkey); - } else { - ksmbd_debug(AUTH, "ServerIn Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3encryptionkey); - ksmbd_debug(AUTH, "ServerOut Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey); - } - return 0; -} - -int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess) -{ - struct derivation_twin twin; - struct derivation *d; - - d = &twin.encryption; - d->label.iov_base = "SMB2AESCCM"; - d->label.iov_len = 11; - d->context.iov_base = "ServerOut"; - d->context.iov_len = 10; - - d = &twin.decryption; - d->label.iov_base = "SMB2AESCCM"; - d->label.iov_len = 11; - d->context.iov_base = "ServerIn "; - d->context.iov_len = 10; - - return generate_smb3encryptionkey(conn, sess, &twin); -} - -int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess) -{ - struct derivation_twin twin; - struct derivation *d; - - d = &twin.encryption; - d->label.iov_base = "SMBS2CCipherKey"; - d->label.iov_len = 16; - d->context.iov_base = sess->Preauth_HashValue; - d->context.iov_len = 64; - - d = &twin.decryption; - d->label.iov_base = "SMBC2SCipherKey"; - d->label.iov_len = 16; - d->context.iov_base = sess->Preauth_HashValue; - d->context.iov_len = 64; - - return generate_smb3encryptionkey(conn, sess, &twin); -} - -int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, - __u8 *pi_hash) -{ - int rc; - struct smb2_hdr *rcv_hdr = smb2_get_msg(buf); - char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; - int msg_size = get_rfc1002_len(buf); - struct ksmbd_crypto_ctx *ctx = NULL; - - if (conn->preauth_info->Preauth_HashId != - SMB2_PREAUTH_INTEGRITY_SHA512) - return -EINVAL; - - ctx = ksmbd_crypto_ctx_find_sha512(); - if (!ctx) { - ksmbd_debug(AUTH, "could not alloc sha512\n"); - return -ENOMEM; - } - - rc = crypto_shash_init(CRYPTO_SHA512(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init shashn"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); - goto out; - } -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, - __u8 *pi_hash) -{ - int rc; - struct ksmbd_crypto_ctx *ctx = NULL; - - ctx = ksmbd_crypto_ctx_find_sha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not alloc sha256\n"); - return -ENOMEM; - } - - rc = crypto_shash_init(CRYPTO_SHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init shashn"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA256(ctx), sd_buf, len); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_SHA256(ctx), pi_hash); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); - goto out; - } -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id, - int enc, u8 *key) -{ - struct ksmbd_session *sess; - u8 *ses_enc_key; - - if (enc) - sess = work->sess; - else - sess = ksmbd_session_lookup_all(work->conn, ses_id); - if (!sess) - return -EINVAL; - - ses_enc_key = enc ? sess->smb3encryptionkey : - sess->smb3decryptionkey; - memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); - - return 0; -} - -static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, - unsigned int buflen) -{ - void *addr; - - if (is_vmalloc_addr(buf)) - addr = vmalloc_to_page(buf); - else - addr = virt_to_page(buf); - sg_set_page(sg, addr, buflen, offset_in_page(buf)); -} - -static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, - u8 *sign) -{ - struct scatterlist *sg; - unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; - int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; - - if (!nvec) - return NULL; - - for (i = 0; i < nvec - 1; i++) { - unsigned long kaddr = (unsigned long)iov[i + 1].iov_base; - - if (is_vmalloc_addr(iov[i + 1].iov_base)) { - nr_entries[i] = ((kaddr + iov[i + 1].iov_len + - PAGE_SIZE - 1) >> PAGE_SHIFT) - - (kaddr >> PAGE_SHIFT); - } else { - nr_entries[i]++; - } - total_entries += nr_entries[i]; - } - - /* Add two entries for transform header and signature */ - total_entries += 2; - - sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL); - if (!sg) - return NULL; - - sg_init_table(sg, total_entries); - smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len); - for (i = 0; i < nvec - 1; i++) { - void *data = iov[i + 1].iov_base; - int len = iov[i + 1].iov_len; - - if (is_vmalloc_addr(data)) { - int j, offset = offset_in_page(data); - - for (j = 0; j < nr_entries[i]; j++) { - unsigned int bytes = PAGE_SIZE - offset; - - if (!len) - break; - - if (bytes > len) - bytes = len; - - sg_set_page(&sg[sg_idx++], - vmalloc_to_page(data), bytes, - offset_in_page(data)); - - data += bytes; - len -= bytes; - offset = 0; - } - } else { - sg_set_page(&sg[sg_idx++], virt_to_page(data), len, - offset_in_page(data)); - } - } - smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE); - return sg; -} - -int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, - unsigned int nvec, int enc) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_transform_hdr *tr_hdr = smb2_get_msg(iov[0].iov_base); - unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; - int rc; - struct scatterlist *sg; - u8 sign[SMB2_SIGNATURE_SIZE] = {}; - u8 key[SMB3_ENC_DEC_KEY_SIZE]; - struct aead_request *req; - char *iv; - unsigned int iv_len; - struct crypto_aead *tfm; - unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); - struct ksmbd_crypto_ctx *ctx; - - rc = ksmbd_get_encryption_key(work, - le64_to_cpu(tr_hdr->SessionId), - enc, - key); - if (rc) { - pr_err("Could not get %scryption key\n", enc ? "en" : "de"); - return rc; - } - - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) - ctx = ksmbd_crypto_ctx_find_gcm(); - else - ctx = ksmbd_crypto_ctx_find_ccm(); - if (!ctx) { - pr_err("crypto alloc failed\n"); - return -ENOMEM; - } - - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) - tfm = CRYPTO_GCM(ctx); - else - tfm = CRYPTO_CCM(ctx); - - if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) - rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); - else - rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); - if (rc) { - pr_err("Failed to set aead key %d\n", rc); - goto free_ctx; - } - - rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); - if (rc) { - pr_err("Failed to set authsize %d\n", rc); - goto free_ctx; - } - - req = aead_request_alloc(tfm, GFP_KERNEL); - if (!req) { - rc = -ENOMEM; - goto free_ctx; - } - - if (!enc) { - memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); - crypt_len += SMB2_SIGNATURE_SIZE; - } - - sg = ksmbd_init_sg(iov, nvec, sign); - if (!sg) { - pr_err("Failed to init sg\n"); - rc = -ENOMEM; - goto free_req; - } - - iv_len = crypto_aead_ivsize(tfm); - iv = kzalloc(iv_len, GFP_KERNEL); - if (!iv) { - rc = -ENOMEM; - goto free_sg; - } - - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { - memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); - } else { - iv[0] = 3; - memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); - } - - aead_request_set_crypt(req, sg, sg, crypt_len, iv); - aead_request_set_ad(req, assoc_data_len); - aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); - - if (enc) - rc = crypto_aead_encrypt(req); - else - rc = crypto_aead_decrypt(req); - if (rc) - goto free_iv; - - if (enc) - memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); - -free_iv: - kfree(iv); -free_sg: - kfree(sg); -free_req: - kfree(req); -free_ctx: - ksmbd_release_crypto_ctx(ctx); - return rc; -} |