diff options
author | Steve French <stfrench@microsoft.com> | 2023-05-22 04:46:30 +0300 |
---|---|---|
committer | Steve French <stfrench@microsoft.com> | 2023-05-25 00:29:21 +0300 |
commit | 38c8a9a52082579090e34c033d439ed2cd1a462d (patch) | |
tree | ca6c3bf5b2b912f32735c1aa0a045cd21a47a205 /fs/cifs/smb2transport.c | |
parent | cb8b02fd6343228966324528adf920bfb8b8e681 (diff) | |
download | linux-38c8a9a52082579090e34c033d439ed2cd1a462d.tar.xz |
smb: move client and server files to common directory fs/smb
Move CIFS/SMB3 related client and server files (cifs.ko and ksmbd.ko
and helper modules) to new fs/smb subdirectory:
fs/cifs --> fs/smb/client
fs/ksmbd --> fs/smb/server
fs/smbfs_common --> fs/smb/common
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/cifs/smb2transport.c')
-rw-r--r-- | fs/cifs/smb2transport.c | 934 |
1 files changed, 0 insertions, 934 deletions
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c deleted file mode 100644 index 790acf65a092..000000000000 --- a/fs/cifs/smb2transport.c +++ /dev/null @@ -1,934 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002, 2011 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Jeremy Allison (jra@samba.org) 2006 - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - */ - -#include <linux/fs.h> -#include <linux/list.h> -#include <linux/wait.h> -#include <linux/net.h> -#include <linux/delay.h> -#include <linux/uaccess.h> -#include <asm/processor.h> -#include <linux/mempool.h> -#include <linux/highmem.h> -#include <crypto/aead.h> -#include "cifsglob.h" -#include "cifsproto.h" -#include "smb2proto.h" -#include "cifs_debug.h" -#include "smb2status.h" -#include "smb2glob.h" - -static int -smb3_crypto_shash_allocate(struct TCP_Server_Info *server) -{ - struct cifs_secmech *p = &server->secmech; - int rc; - - rc = cifs_alloc_hash("hmac(sha256)", &p->hmacsha256); - if (rc) - goto err; - - rc = cifs_alloc_hash("cmac(aes)", &p->aes_cmac); - if (rc) - goto err; - - return 0; -err: - cifs_free_hash(&p->hmacsha256); - return rc; -} - -int -smb311_crypto_shash_allocate(struct TCP_Server_Info *server) -{ - struct cifs_secmech *p = &server->secmech; - int rc = 0; - - rc = cifs_alloc_hash("hmac(sha256)", &p->hmacsha256); - if (rc) - return rc; - - rc = cifs_alloc_hash("cmac(aes)", &p->aes_cmac); - if (rc) - goto err; - - rc = cifs_alloc_hash("sha512", &p->sha512); - if (rc) - goto err; - - return 0; - -err: - cifs_free_hash(&p->aes_cmac); - cifs_free_hash(&p->hmacsha256); - return rc; -} - - -static -int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) -{ - struct cifs_chan *chan; - struct TCP_Server_Info *pserver; - struct cifs_ses *ses = NULL; - int i; - int rc = 0; - bool is_binding = false; - - spin_lock(&cifs_tcp_ses_lock); - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - if (ses->Suid == ses_id) - goto found; - } - cifs_server_dbg(VFS, "%s: Could not find session 0x%llx\n", - __func__, ses_id); - rc = -ENOENT; - goto out; - -found: - spin_lock(&ses->ses_lock); - spin_lock(&ses->chan_lock); - - is_binding = (cifs_chan_needs_reconnect(ses, server) && - ses->ses_status == SES_GOOD); - if (is_binding) { - /* - * If we are in the process of binding a new channel - * to an existing session, use the master connection - * session key - */ - memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE); - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - goto out; - } - - /* - * Otherwise, use the channel key. - */ - - for (i = 0; i < ses->chan_count; i++) { - chan = ses->chans + i; - if (chan->server == server) { - memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE); - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - goto out; - } - } - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - - cifs_dbg(VFS, - "%s: Could not find channel signing key for session 0x%llx\n", - __func__, ses_id); - rc = -ENOENT; - -out: - spin_unlock(&cifs_tcp_ses_lock); - return rc; -} - -static struct cifs_ses * -smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id) -{ - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - if (ses->Suid != ses_id) - continue; - ++ses->ses_count; - return ses; - } - - return NULL; -} - -struct cifs_ses * -smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id) -{ - struct cifs_ses *ses; - - spin_lock(&cifs_tcp_ses_lock); - ses = smb2_find_smb_ses_unlocked(server, ses_id); - spin_unlock(&cifs_tcp_ses_lock); - - return ses; -} - -static struct cifs_tcon * -smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid) -{ - struct cifs_tcon *tcon; - - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - if (tcon->tid != tid) - continue; - ++tcon->tc_count; - return tcon; - } - - return NULL; -} - -/* - * Obtain tcon corresponding to the tid in the given - * cifs_ses - */ - -struct cifs_tcon * -smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid) -{ - struct cifs_ses *ses; - struct cifs_tcon *tcon; - - spin_lock(&cifs_tcp_ses_lock); - ses = smb2_find_smb_ses_unlocked(server, ses_id); - if (!ses) { - spin_unlock(&cifs_tcp_ses_lock); - return NULL; - } - tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid); - if (!tcon) { - cifs_put_smb_ses(ses); - spin_unlock(&cifs_tcp_ses_lock); - return NULL; - } - spin_unlock(&cifs_tcp_ses_lock); - /* tcon already has a ref to ses, so we don't need ses anymore */ - cifs_put_smb_ses(ses); - - return tcon; -} - -int -smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, - bool allocate_crypto) -{ - int rc; - unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; - unsigned char *sigptr = smb2_signature; - struct kvec *iov = rqst->rq_iov; - struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; - struct cifs_ses *ses; - struct shash_desc *shash = NULL; - struct smb_rqst drqst; - - ses = smb2_find_smb_ses(server, le64_to_cpu(shdr->SessionId)); - if (unlikely(!ses)) { - cifs_server_dbg(VFS, "%s: Could not find session\n", __func__); - return -ENOENT; - } - - memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE); - memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); - - if (allocate_crypto) { - rc = cifs_alloc_hash("hmac(sha256)", &shash); - if (rc) { - cifs_server_dbg(VFS, - "%s: sha256 alloc failed\n", __func__); - goto out; - } - } else { - shash = server->secmech.hmacsha256; - } - - rc = crypto_shash_setkey(shash->tfm, ses->auth_key.response, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) { - cifs_server_dbg(VFS, - "%s: Could not update with response\n", - __func__); - goto out; - } - - rc = crypto_shash_init(shash); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not init sha256", __func__); - goto out; - } - - /* - * For SMB2+, __cifs_calc_signature() expects to sign only the actual - * data, that is, iov[0] should not contain a rfc1002 length. - * - * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to - * __cifs_calc_signature(). - */ - drqst = *rqst; - if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { - rc = crypto_shash_update(shash, iov[0].iov_base, - iov[0].iov_len); - if (rc) { - cifs_server_dbg(VFS, - "%s: Could not update with payload\n", - __func__); - goto out; - } - drqst.rq_iov++; - drqst.rq_nvec--; - } - - rc = __cifs_calc_signature(&drqst, server, sigptr, shash); - if (!rc) - memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); - -out: - if (allocate_crypto) - cifs_free_hash(&shash); - if (ses) - cifs_put_smb_ses(ses); - return rc; -} - -static int generate_key(struct cifs_ses *ses, 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 = 0; - unsigned char prfhash[SMB2_HMACSHA256_SIZE]; - unsigned char *hashptr = prfhash; - struct TCP_Server_Info *server = ses->server; - - memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); - memset(key, 0x0, key_size); - - rc = smb3_crypto_shash_allocate(server); - if (rc) { - cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_setkey(server->secmech.hmacsha256->tfm, - ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not set with session key\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_init(server->secmech.hmacsha256); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not init sign hmac\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(server->secmech.hmacsha256, i, 4); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with n\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(server->secmech.hmacsha256, label.iov_base, label.iov_len); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with label\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(server->secmech.hmacsha256, &zero, 1); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with zero\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(server->secmech.hmacsha256, context.iov_base, context.iov_len); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with context\n", __func__); - goto smb3signkey_ret; - } - - if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) { - rc = crypto_shash_update(server->secmech.hmacsha256, L256, 4); - } else { - rc = crypto_shash_update(server->secmech.hmacsha256, L128, 4); - } - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with L\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_final(server->secmech.hmacsha256, hashptr); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); - goto smb3signkey_ret; - } - - memcpy(key, hashptr, key_size); - -smb3signkey_ret: - return rc; -} - -struct derivation { - struct kvec label; - struct kvec context; -}; - -struct derivation_triplet { - struct derivation signing; - struct derivation encryption; - struct derivation decryption; -}; - -static int -generate_smb3signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct derivation_triplet *ptriplet) -{ - int rc; - bool is_binding = false; - int chan_index = 0; - - spin_lock(&ses->ses_lock); - spin_lock(&ses->chan_lock); - is_binding = (cifs_chan_needs_reconnect(ses, server) && - ses->ses_status == SES_GOOD); - - chan_index = cifs_ses_get_chan_index(ses, server); - /* TODO: introduce ref counting for channels when the can be freed */ - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - - /* - * All channels use the same encryption/decryption keys but - * they have their own signing key. - * - * When we generate the keys, check if it is for a new channel - * (binding) in which case we only need to generate a signing - * key and store it in the channel as to not overwrite the - * master connection signing key stored in the session - */ - - if (is_binding) { - rc = generate_key(ses, ptriplet->signing.label, - ptriplet->signing.context, - ses->chans[chan_index].signkey, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; - } else { - rc = generate_key(ses, ptriplet->signing.label, - ptriplet->signing.context, - ses->smb3signingkey, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; - - /* safe to access primary channel, since it will never go away */ - spin_lock(&ses->chan_lock); - memcpy(ses->chans[chan_index].signkey, ses->smb3signingkey, - SMB3_SIGN_KEY_SIZE); - spin_unlock(&ses->chan_lock); - - rc = generate_key(ses, ptriplet->encryption.label, - ptriplet->encryption.context, - ses->smb3encryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - rc = generate_key(ses, ptriplet->decryption.label, - ptriplet->decryption.context, - ses->smb3decryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; - } - - if (rc) - return rc; - -#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS - cifs_dbg(VFS, "%s: dumping generated AES session keys\n", __func__); - /* - * The session id is opaque in terms of endianness, so we can't - * print it as a long long. we dump it as we got it on the wire - */ - cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid), - &ses->Suid); - cifs_dbg(VFS, "Cipher type %d\n", server->cipher_type); - cifs_dbg(VFS, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); - cifs_dbg(VFS, "Signing Key %*ph\n", - SMB3_SIGN_KEY_SIZE, ses->smb3signingkey); - if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) { - cifs_dbg(VFS, "ServerIn Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, ses->smb3encryptionkey); - cifs_dbg(VFS, "ServerOut Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, ses->smb3decryptionkey); - } else { - cifs_dbg(VFS, "ServerIn Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3encryptionkey); - cifs_dbg(VFS, "ServerOut Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3decryptionkey); - } -#endif - return rc; -} - -int -generate_smb30signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server) - -{ - struct derivation_triplet triplet; - struct derivation *d; - - d = &triplet.signing; - d->label.iov_base = "SMB2AESCMAC"; - d->label.iov_len = 12; - d->context.iov_base = "SmbSign"; - d->context.iov_len = 8; - - d = &triplet.encryption; - d->label.iov_base = "SMB2AESCCM"; - d->label.iov_len = 11; - d->context.iov_base = "ServerIn "; - d->context.iov_len = 10; - - d = &triplet.decryption; - d->label.iov_base = "SMB2AESCCM"; - d->label.iov_len = 11; - d->context.iov_base = "ServerOut"; - d->context.iov_len = 10; - - return generate_smb3signingkey(ses, server, &triplet); -} - -int -generate_smb311signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server) - -{ - struct derivation_triplet triplet; - struct derivation *d; - - d = &triplet.signing; - d->label.iov_base = "SMBSigningKey"; - d->label.iov_len = 14; - d->context.iov_base = ses->preauth_sha_hash; - d->context.iov_len = 64; - - d = &triplet.encryption; - d->label.iov_base = "SMBC2SCipherKey"; - d->label.iov_len = 16; - d->context.iov_base = ses->preauth_sha_hash; - d->context.iov_len = 64; - - d = &triplet.decryption; - d->label.iov_base = "SMBS2CCipherKey"; - d->label.iov_len = 16; - d->context.iov_base = ses->preauth_sha_hash; - d->context.iov_len = 64; - - return generate_smb3signingkey(ses, server, &triplet); -} - -int -smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, - bool allocate_crypto) -{ - int rc; - unsigned char smb3_signature[SMB2_CMACAES_SIZE]; - unsigned char *sigptr = smb3_signature; - struct kvec *iov = rqst->rq_iov; - struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; - struct shash_desc *shash = NULL; - struct smb_rqst drqst; - u8 key[SMB3_SIGN_KEY_SIZE]; - - rc = smb2_get_sign_key(le64_to_cpu(shdr->SessionId), server, key); - if (unlikely(rc)) { - cifs_server_dbg(VFS, "%s: Could not get signing key\n", __func__); - return rc; - } - - if (allocate_crypto) { - rc = cifs_alloc_hash("cmac(aes)", &shash); - if (rc) - return rc; - } else { - shash = server->secmech.aes_cmac; - } - - memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); - memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); - - rc = crypto_shash_setkey(shash->tfm, key, SMB2_CMACAES_SIZE); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); - goto out; - } - - /* - * we already allocate aes_cmac when we init smb3 signing key, - * so unlike smb2 case we do not have to check here if secmech are - * initialized - */ - rc = crypto_shash_init(shash); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__); - goto out; - } - - /* - * For SMB2+, __cifs_calc_signature() expects to sign only the actual - * data, that is, iov[0] should not contain a rfc1002 length. - * - * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to - * __cifs_calc_signature(). - */ - drqst = *rqst; - if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { - rc = crypto_shash_update(shash, iov[0].iov_base, - iov[0].iov_len); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with payload\n", - __func__); - goto out; - } - drqst.rq_iov++; - drqst.rq_nvec--; - } - - rc = __cifs_calc_signature(&drqst, server, sigptr, shash); - if (!rc) - memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); - -out: - if (allocate_crypto) - cifs_free_hash(&shash); - return rc; -} - -/* must be called with server->srv_mutex held */ -static int -smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) -{ - int rc = 0; - struct smb2_hdr *shdr; - struct smb2_sess_setup_req *ssr; - bool is_binding; - bool is_signed; - - shdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base; - ssr = (struct smb2_sess_setup_req *)shdr; - - is_binding = shdr->Command == SMB2_SESSION_SETUP && - (ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING); - is_signed = shdr->Flags & SMB2_FLAGS_SIGNED; - - if (!is_signed) - return 0; - spin_lock(&server->srv_lock); - if (server->ops->need_neg && - server->ops->need_neg(server)) { - spin_unlock(&server->srv_lock); - return 0; - } - spin_unlock(&server->srv_lock); - if (!is_binding && !server->session_estab) { - strncpy(shdr->Signature, "BSRSPYL", 8); - return 0; - } - - rc = server->ops->calc_signature(rqst, server, false); - - return rc; -} - -int -smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) -{ - unsigned int rc; - char server_response_sig[SMB2_SIGNATURE_SIZE]; - struct smb2_hdr *shdr = - (struct smb2_hdr *)rqst->rq_iov[0].iov_base; - - if ((shdr->Command == SMB2_NEGOTIATE) || - (shdr->Command == SMB2_SESSION_SETUP) || - (shdr->Command == SMB2_OPLOCK_BREAK) || - server->ignore_signature || - (!server->session_estab)) - return 0; - - /* - * BB what if signatures are supposed to be on for session but - * server does not send one? BB - */ - - /* Do not need to verify session setups with signature "BSRSPYL " */ - if (memcmp(shdr->Signature, "BSRSPYL ", 8) == 0) - cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", - shdr->Command); - - /* - * Save off the origiginal signature so we can modify the smb and check - * our calculated signature against what the server sent. - */ - memcpy(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE); - - memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE); - - rc = server->ops->calc_signature(rqst, server, true); - - if (rc) - return rc; - - if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE)) { - cifs_dbg(VFS, "sign fail cmd 0x%x message id 0x%llx\n", - shdr->Command, shdr->MessageId); - return -EACCES; - } else - return 0; -} - -/* - * Set message id for the request. Should be called after wait_for_free_request - * and when srv_mutex is held. - */ -static inline void -smb2_seq_num_into_buf(struct TCP_Server_Info *server, - struct smb2_hdr *shdr) -{ - unsigned int i, num = le16_to_cpu(shdr->CreditCharge); - - shdr->MessageId = get_next_mid64(server); - /* skip message numbers according to CreditCharge field */ - for (i = 1; i < num; i++) - get_next_mid(server); -} - -static struct mid_q_entry * -smb2_mid_entry_alloc(const struct smb2_hdr *shdr, - struct TCP_Server_Info *server) -{ - struct mid_q_entry *temp; - unsigned int credits = le16_to_cpu(shdr->CreditCharge); - - if (server == NULL) { - cifs_dbg(VFS, "Null TCP session in smb2_mid_entry_alloc\n"); - return NULL; - } - - temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); - memset(temp, 0, sizeof(struct mid_q_entry)); - kref_init(&temp->refcount); - temp->mid = le64_to_cpu(shdr->MessageId); - temp->credits = credits > 0 ? credits : 1; - temp->pid = current->pid; - temp->command = shdr->Command; /* Always LE */ - temp->when_alloc = jiffies; - temp->server = server; - - /* - * The default is for the mid to be synchronous, so the - * default callback just wakes up the current task. - */ - get_task_struct(current); - temp->creator = current; - temp->callback = cifs_wake_up_task; - temp->callback_data = current; - - atomic_inc(&mid_count); - temp->mid_state = MID_REQUEST_ALLOCATED; - trace_smb3_cmd_enter(le32_to_cpu(shdr->Id.SyncId.TreeId), - le64_to_cpu(shdr->SessionId), - le16_to_cpu(shdr->Command), temp->mid); - return temp; -} - -static int -smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server, - struct smb2_hdr *shdr, struct mid_q_entry **mid) -{ - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - - if (server->tcpStatus == CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - cifs_dbg(FYI, "tcp session dead - return to caller to retry\n"); - return -EAGAIN; - } - - if (server->tcpStatus == CifsNeedNegotiate && - shdr->Command != SMB2_NEGOTIATE) { - spin_unlock(&server->srv_lock); - return -EAGAIN; - } - spin_unlock(&server->srv_lock); - - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_NEW) { - if ((shdr->Command != SMB2_SESSION_SETUP) && - (shdr->Command != SMB2_NEGOTIATE)) { - spin_unlock(&ses->ses_lock); - return -EAGAIN; - } - /* else ok - we are setting up session */ - } - - if (ses->ses_status == SES_EXITING) { - if (shdr->Command != SMB2_LOGOFF) { - spin_unlock(&ses->ses_lock); - return -EAGAIN; - } - /* else ok - we are shutting down the session */ - } - spin_unlock(&ses->ses_lock); - - *mid = smb2_mid_entry_alloc(shdr, server); - if (*mid == NULL) - return -ENOMEM; - spin_lock(&server->mid_lock); - list_add_tail(&(*mid)->qhead, &server->pending_mid_q); - spin_unlock(&server->mid_lock); - - return 0; -} - -int -smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, - bool log_error) -{ - unsigned int len = mid->resp_buf_size; - struct kvec iov[1]; - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = 1 }; - - iov[0].iov_base = (char *)mid->resp_buf; - iov[0].iov_len = len; - - dump_smb(mid->resp_buf, min_t(u32, 80, len)); - /* convert the length into a more usable form */ - if (len > 24 && server->sign && !mid->decrypted) { - int rc; - - rc = smb2_verify_signature(&rqst, server); - if (rc) - cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", - rc); - } - - return map_smb2_to_linux_error(mid->resp_buf, log_error); -} - -struct mid_q_entry * -smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server, - struct smb_rqst *rqst) -{ - int rc; - struct smb2_hdr *shdr = - (struct smb2_hdr *)rqst->rq_iov[0].iov_base; - struct mid_q_entry *mid; - - smb2_seq_num_into_buf(server, shdr); - - rc = smb2_get_mid_entry(ses, server, shdr, &mid); - if (rc) { - revert_current_mid_from_hdr(server, shdr); - return ERR_PTR(rc); - } - - rc = smb2_sign_rqst(rqst, server); - if (rc) { - revert_current_mid_from_hdr(server, shdr); - delete_mid(mid); - return ERR_PTR(rc); - } - - return mid; -} - -struct mid_q_entry * -smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) -{ - int rc; - struct smb2_hdr *shdr = - (struct smb2_hdr *)rqst->rq_iov[0].iov_base; - struct mid_q_entry *mid; - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedNegotiate && - shdr->Command != SMB2_NEGOTIATE) { - spin_unlock(&server->srv_lock); - return ERR_PTR(-EAGAIN); - } - spin_unlock(&server->srv_lock); - - smb2_seq_num_into_buf(server, shdr); - - mid = smb2_mid_entry_alloc(shdr, server); - if (mid == NULL) { - revert_current_mid_from_hdr(server, shdr); - return ERR_PTR(-ENOMEM); - } - - rc = smb2_sign_rqst(rqst, server); - if (rc) { - revert_current_mid_from_hdr(server, shdr); - release_mid(mid); - return ERR_PTR(rc); - } - - return mid; -} - -int -smb3_crypto_aead_allocate(struct TCP_Server_Info *server) -{ - struct crypto_aead *tfm; - - if (!server->secmech.enc) { - if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - tfm = crypto_alloc_aead("gcm(aes)", 0, 0); - else - tfm = crypto_alloc_aead("ccm(aes)", 0, 0); - if (IS_ERR(tfm)) { - cifs_server_dbg(VFS, "%s: Failed alloc encrypt aead\n", - __func__); - return PTR_ERR(tfm); - } - server->secmech.enc = tfm; - } - - if (!server->secmech.dec) { - if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - tfm = crypto_alloc_aead("gcm(aes)", 0, 0); - else - tfm = crypto_alloc_aead("ccm(aes)", 0, 0); - if (IS_ERR(tfm)) { - crypto_free_aead(server->secmech.enc); - server->secmech.enc = NULL; - cifs_server_dbg(VFS, "%s: Failed to alloc decrypt aead\n", - __func__); - return PTR_ERR(tfm); - } - server->secmech.dec = tfm; - } - - return 0; -} |