could you fix the minor checkpatch nit? WARNING: Block comments use a trailing */ on a separate line #220: FILE: fs/cifs/smb2ops.c:4317: + * encrypted blob */ On Wed, Sep 28, 2022 at 8:57 PM Enzo Matsumiya <ematsumiya@xxxxxxx> wrote: > > Implement support for AES-GMAC message signing, as specified in > MS-SMB2 3.1.4.1 "Signing An Outgoing Message". > > The core function is smb311_calc_signature(), which will be used as the > ->calc_signature op when SIGNING_ALG_AES_GMAC has been negotiated with > the server. > > If "enable_negotiate_signing" is false (default) or if > SIGNING_ALG_AES_GMAC was not negotiated, use AES-CMAC. > > Changes: > - convert cifs_secmech sign/verify to unions, where .aead is for AES-GMAC > TFM and .shash for all the others signing algorithms > - init_sg(): rename to smb3_init_sg(), make it non-static, remove skip > variable. This makes it fit for both crypt_message() and > smb311_calc_signature() > - crypt_message(): advance the 20 bytes of rqst[0].rq_iov[0] that are > not part of the encrypted blob before calling smb3_init_sg(). Rewind > it back right after the call. (this was done in init_sg() via the > skip variable) > > Other smaller modifications: > - smb2_setup_request(): check if the request has a transform header as we > must not sign an encrypted message (MS-SMB2 3.2.4.1.1) > - smb2_verify_signature(): > * check if MessageId is 0xFFFFFFFFFFFFFFFF or if status is STATUS_PENDING > (MS-SMB2 3.2.5.1.3) > * remove extra call to zero the header signature as this is already > done by all ->calc_signature implementations > * remove useless call to check for "BSRSPYL" signature (SMB1 only), > and from smb2_sign_rqst() as well > > Signed-off-by: Enzo Matsumiya <ematsumiya@xxxxxxx> > --- > fs/cifs/cifsencrypt.c | 23 ++- > fs/cifs/cifsglob.h | 40 +++-- > fs/cifs/smb2ops.c | 36 ++--- > fs/cifs/smb2pdu.c | 82 +++++----- > fs/cifs/smb2proto.h | 9 +- > fs/cifs/smb2transport.c | 331 ++++++++++++++++++++++++++++++---------- > 6 files changed, 354 insertions(+), 167 deletions(-) > > diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c > index 4ae58ab29458..3a78efc45a23 100644 > --- a/fs/cifs/cifsencrypt.c > +++ b/fs/cifs/cifsencrypt.c > @@ -23,6 +23,7 @@ > #include <linux/fips.h> > #include "../smbfs_common/arc4.h" > #include <crypto/aead.h> > +#include "smb2proto.h" > > int __cifs_calc_signature(struct smb_rqst *rqst, > struct TCP_Server_Info *server, char *signature, > @@ -105,9 +106,9 @@ static int cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *se > return -EINVAL; > > if (verify) > - shash = server->secmech.verify; > + shash = server->secmech.verify.shash; > else > - shash = server->secmech.sign; > + shash = server->secmech.sign.shash; > > rc = crypto_shash_init(shash); > if (rc) { > @@ -703,16 +704,14 @@ calc_seckey(struct cifs_ses *ses) > void > cifs_crypto_secmech_release(struct TCP_Server_Info *server) > { > - cifs_free_hash(&server->secmech.sign); > - cifs_free_hash(&server->secmech.verify); > - > - if (server->secmech.enc) { > - crypto_free_aead(server->secmech.enc); > - server->secmech.enc = NULL; > + if (server->signing_algorithm == SIGNING_ALG_AES_GMAC) { > + smb3_crypto_aead_free(&server->secmech.sign.aead); > + smb3_crypto_aead_free(&server->secmech.verify.aead); > + } else { > + cifs_free_hash(&server->secmech.sign.shash); > + cifs_free_hash(&server->secmech.verify.shash); > } > > - if (server->secmech.dec) { > - crypto_free_aead(server->secmech.dec); > - server->secmech.dec = NULL; > - } > + smb3_crypto_aead_free(&server->secmech.enc); > + smb3_crypto_aead_free(&server->secmech.dec); > } > diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h > index 30b3fadb4b06..81a8eff06467 100644 > --- a/fs/cifs/cifsglob.h > +++ b/fs/cifs/cifsglob.h > @@ -153,24 +153,48 @@ struct session_key { > char *response; > }; > > +struct smb_rqst; > +struct TCP_Server_Info; > /** > * cifs_secmech - Crypto hashing related structure/fields, not specific to one mechanism > - * @sign: SHASH descriptor to allocate a hashing TFM for signing requests > - * @verify: SHASH descriptor to allocate a hashing TFM for verifying requests' signatures > + * @sign.shash: SHASH descriptor for signing TFM > + * @sign.aead: AEAD TFM for signing > + * @sign_wait: Completion struct for signing operations > + * @verify.shash: SHASH descriptor for verifying TFM > + * @verify.aead: AEAD TFM for verifying > + * @verify_wait: Completion struct for verifying operations > + * @calc_signature: Signature calculation function to be used. > * @enc: AEAD TFM for SMB3+ encryption > * @dec: AEAD TFM for SMB3+ decryption > * > - * @sign and @verify are allocated per-server, and the negotiated connection dialect will dictate > - * which algorithm to use: > + * @sign and @verify TFMs are allocated per-server, and the negotiated dialect will dictate which > + * algorithm to use: > * - MD5 for SMB1 > * - HMAC-SHA256 for SMB2 > * - AES-CMAC for SMB3 > + * - AES-GMAC for SMB3.1.1 > + * > + * The completion structs @sign_wait and @verify_wait are required so that we can serialize access > + * to the AEAD TFMs, since they're asynchronous by design. Using a stack-allocated structure could > + * cause concurrent access to the TFMs to overwrite the completion status of a previous operation. > * > - * @enc and @dec holds the encryption/decryption TFMs, where it'll be either AES-CCM or AES-GCM. > + * @enc and @dec holds the encryption/decryption TFMs, also allocated per server, where each will > + * be either AES-CCM or AES-GCM. > */ > struct cifs_secmech { > - struct shash_desc *sign; > - struct shash_desc *verify; > + union { > + struct shash_desc *shash; > + struct crypto_aead *aead; > + } sign; > + struct crypto_wait sign_wait; > + > + union { > + struct shash_desc *shash; > + struct crypto_aead *aead; > + } verify; > + struct crypto_wait verify_wait; > + > + int (*calc_signature)(struct smb_rqst *rqst, struct TCP_Server_Info *server, bool verify); > > struct crypto_aead *enc; > struct crypto_aead *dec; > @@ -445,8 +469,6 @@ struct smb_version_operations { > void (*new_lease_key)(struct cifs_fid *); > int (*generate_signingkey)(struct cifs_ses *ses, > struct TCP_Server_Info *server); > - int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *, > - bool allocate_crypto); > int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon, > struct cifsFileInfo *src_file); > int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon, > diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c > index 0aaad18e1ec8..22b40d181bba 100644 > --- a/fs/cifs/smb2ops.c > +++ b/fs/cifs/smb2ops.c > @@ -4234,21 +4234,14 @@ static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, > sg_set_page(sg, addr, buflen, offset_in_page(buf)); > } > > -/* Assumes the first rqst has a transform header as the first iov. > - * I.e. > - * rqst[0].rq_iov[0] is transform header > - * rqst[0].rq_iov[1+] data to be encrypted/decrypted > - * rqst[1+].rq_iov[0+] data to be encrypted/decrypted > - */ > -static struct scatterlist * > -init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign) > +struct scatterlist * > +smb3_init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign) > { > unsigned int sg_len; > struct scatterlist *sg; > unsigned int i; > unsigned int j; > unsigned int idx = 0; > - int skip; > > sg_len = 1; > for (i = 0; i < num_rqst; i++) > @@ -4261,15 +4254,10 @@ init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign) > sg_init_table(sg, sg_len); > for (i = 0; i < num_rqst; i++) { > for (j = 0; j < rqst[i].rq_nvec; j++) { > - /* > - * The first rqst has a transform header where the > - * first 20 bytes are not part of the encrypted blob > - */ > - skip = (i == 0) && (j == 0) ? 20 : 0; > smb2_sg_set_buf(&sg[idx++], > - rqst[i].rq_iov[j].iov_base + skip, > - rqst[i].rq_iov[j].iov_len - skip); > - } > + rqst[i].rq_iov[j].iov_base, > + rqst[i].rq_iov[j].iov_len); > + } > > for (j = 0; j < rqst[i].rq_npages; j++) { > unsigned int len, offset; > @@ -4325,7 +4313,15 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst, > crypt_len += SMB2_SIGNATURE_SIZE; > } > > - sg = init_sg(num_rqst, rqst, sign); > + /* Skip the first 20 bytes of the first iov of the first request as they're not part of the > + * encrypted blob */ > + rqst[0].rq_iov[0].iov_base += 20; > + rqst[0].rq_iov[0].iov_len -= 20; > + sg = smb3_init_sg(num_rqst, rqst, sign); > + /* Rewind those 20 bytes before going any further */ > + rqst[0].rq_iov[0].iov_base -= 20; > + rqst[0].rq_iov[0].iov_len += 20; > + > if (!sg) { > cifs_server_dbg(VFS, "%s: Failed to init sg\n", __func__); > rc = -ENOMEM; > @@ -5213,7 +5209,6 @@ struct smb_version_operations smb20_operations = { > .get_lease_key = smb2_get_lease_key, > .set_lease_key = smb2_set_lease_key, > .new_lease_key = smb2_new_lease_key, > - .calc_signature = smb2_calc_signature, > .is_read_op = smb2_is_read_op, > .set_oplock_level = smb2_set_oplock_level, > .create_lease_buf = smb2_create_lease_buf, > @@ -5314,7 +5309,6 @@ struct smb_version_operations smb21_operations = { > .get_lease_key = smb2_get_lease_key, > .set_lease_key = smb2_set_lease_key, > .new_lease_key = smb2_new_lease_key, > - .calc_signature = smb2_calc_signature, > .is_read_op = smb21_is_read_op, > .set_oplock_level = smb21_set_oplock_level, > .create_lease_buf = smb2_create_lease_buf, > @@ -5421,7 +5415,6 @@ struct smb_version_operations smb30_operations = { > .set_lease_key = smb2_set_lease_key, > .new_lease_key = smb2_new_lease_key, > .generate_signingkey = generate_smb30signingkey, > - .calc_signature = smb2_calc_signature, > .set_integrity = smb3_set_integrity, > .is_read_op = smb21_is_read_op, > .set_oplock_level = smb3_set_oplock_level, > @@ -5535,7 +5528,6 @@ struct smb_version_operations smb311_operations = { > .set_lease_key = smb2_set_lease_key, > .new_lease_key = smb2_new_lease_key, > .generate_signingkey = generate_smb311signingkey, > - .calc_signature = smb2_calc_signature, > .set_integrity = smb3_set_integrity, > .is_read_op = smb21_is_read_op, > .set_oplock_level = smb3_set_oplock_level, > diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c > index e5939c374c35..2c2bf28382bc 100644 > --- a/fs/cifs/smb2pdu.c > +++ b/fs/cifs/smb2pdu.c > @@ -460,7 +460,7 @@ static unsigned int > build_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt) > { > unsigned int ctxt_len = sizeof(struct smb2_signing_capabilities); > - unsigned short num_algs = 1; /* number of signing algorithms sent */ > + unsigned short num_algs = 2; /* number of signing algorithms sent */ > > pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; > /* > @@ -471,12 +471,18 @@ build_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt) > sizeof(struct smb2_neg_context) + > (num_algs * 2 /* sizeof u16 */), 8) * 8); > pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(num_algs); > - pneg_ctxt->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC); > + > + /* > + * Set AES-GMAC as preferred, but will fall back to AES-CMAC if server doesn't support it. > + * MS-SMB2 2.2.3.1.7 > + */ > + pneg_ctxt->SigningAlgorithms[0] = SIGNING_ALG_AES_GMAC_LE; > + pneg_ctxt->SigningAlgorithms[1] = SIGNING_ALG_AES_CMAC_LE; > + /* SMB 3.1.1 doesn't accept HMAC-SHA256, so no need to send it */ > > ctxt_len += 2 /* sizeof le16 */ * num_algs; > ctxt_len = DIV_ROUND_UP(ctxt_len, 8) * 8; > return ctxt_len; > - /* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */ > } > > static void > @@ -927,22 +933,6 @@ SMB2_negotiate(const unsigned int xid, > else > req->SecurityMode = 0; > > - if (req->SecurityMode) { > - /* > - * Allocate HMAC-SHA256 regardless of dialect requested, change to AES-CMAC later, > - * if SMB3+ is negotiated > - */ > - rc = cifs_alloc_hash("hmac(sha256)", &server->secmech.sign); > - if (rc) > - goto neg_exit; > - > - rc = cifs_alloc_hash("hmac(sha256)", &server->secmech.verify); > - if (rc) { > - cifs_free_hash(&server->secmech.sign); > - goto neg_exit; > - } > - } > - > req->Capabilities = cpu_to_le32(server->vals->req_capabilities); > if (ses->chan_max > 1) > req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); > @@ -1087,22 +1077,6 @@ SMB2_negotiate(const unsigned int xid, > rc = cifs_enable_signing(server, ses->sign); > if (rc) > goto neg_exit; > - > - if (server->sign && server->dialect >= SMB30_PROT_ID) { > - /* free HMAC-SHA256 allocated earlier for negprot */ > - cifs_free_hash(&server->secmech.sign); > - cifs_free_hash(&server->secmech.verify); > - rc = cifs_alloc_hash("cmac(aes)", &server->secmech.sign); > - if (rc) > - goto neg_exit; > - > - rc = cifs_alloc_hash("cmac(aes)", &server->secmech.verify); > - if (rc) { > - cifs_free_hash(&server->secmech.sign); > - goto neg_exit; > - } > - } > - > if (blob_length) { > rc = decode_negTokenInit(security_blob, blob_length, server); > if (rc == 1) > @@ -1112,12 +1086,35 @@ SMB2_negotiate(const unsigned int xid, > } > > if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { > + server->signing_algorithm = SIGNING_ALG_AES_CMAC; > + server->signing_negotiated = false; > + > if (rsp->NegotiateContextCount) > rc = smb311_decode_neg_context(rsp, server, > rsp_iov.iov_len); > else > cifs_server_dbg(VFS, "Missing expected negotiate contexts\n"); > + > + /* > + * Some servers will not send a SMB2_SIGNING_CAPABILITIES context response (*), > + * so use AES-CMAC signing algorithm as it is expected to be accepted. > + * See MS-SMB2 note <125> Section 3.2.4.2.2.2 > + */ > + if (!server->signing_negotiated) > + cifs_dbg(VFS, "signing capabilities were not negotiated, using " > + "AES-CMAC for message signing\n"); > + } else if (server->dialect >= SMB30_PROT_ID) { > + server->signing_algorithm = SIGNING_ALG_AES_CMAC; > + } else if (server->dialect >= SMB20_PROT_ID) { > + server->signing_algorithm = SIGNING_ALG_HMAC_SHA256; > } > + > + rc = smb2_init_secmechs(server); > + if (rc) { > + cifs_dbg(VFS, "Failed to initialize secmechs, rc=%d\n", rc); > + goto neg_exit; > + } > + > neg_exit: > free_rsp_buf(resp_buftype, rsp); > return rc; > @@ -1677,18 +1674,13 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) > goto out; > > if (ses->server->dialect < SMB30_PROT_ID) { > - rc = crypto_shash_setkey(server->secmech.sign->tfm, > - ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); > - if (rc) { > - cifs_dbg(VFS, "%s: Failed to set HMAC-SHA256 signing key, rc=%d\n", > - __func__, rc); > - goto out; > - } > - > - rc = crypto_shash_setkey(server->secmech.verify->tfm, > + rc = crypto_shash_setkey(server->secmech.sign.shash->tfm, > ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); > + if (!rc) > + rc = crypto_shash_setkey(server->secmech.verify.shash->tfm, > + ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); > if (rc) { > - cifs_dbg(VFS, "%s: Failed to set HMAC-SHA256 verify key, rc=%d\n", > + cifs_dbg(VFS, "%s: Failed to set HMAC-SHA256 signing keys, rc=%d\n", > __func__, rc); > goto out; > } > diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h > index 33af35b6e586..e4db59e6cee6 100644 > --- a/fs/cifs/smb2proto.h > +++ b/fs/cifs/smb2proto.h > @@ -14,6 +14,7 @@ > > struct statfs; > struct smb_rqst; > +struct crypto_aead; > > /* > ***************************************************************** > @@ -37,6 +38,7 @@ extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses, > struct smb_rqst *rqst); > extern struct mid_q_entry *smb2_setup_async_request( > struct TCP_Server_Info *server, struct smb_rqst *rqst); > +extern int smb2_init_secmechs(struct TCP_Server_Info *server); > extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server, > __u64 ses_id); > extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server, > @@ -44,6 +46,10 @@ extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server, > extern int smb2_calc_signature(struct smb_rqst *rqst, > struct TCP_Server_Info *server, > bool verify); > +extern int smb311_calc_signature(struct smb_rqst *rqst, > + struct TCP_Server_Info *server, > + bool verify); > +extern struct scatterlist *smb3_init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign); > extern void smb2_echo_request(struct work_struct *work); > extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); > extern bool smb2_is_valid_oplock_break(char *buffer, > @@ -99,7 +105,8 @@ extern int smb2_unlock_range(struct cifsFileInfo *cfile, > struct file_lock *flock, const unsigned int xid); > extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); > extern void smb2_reconnect_server(struct work_struct *work); > -extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); > +extern int smb3_crypto_aead_allocate(const char *name, struct crypto_aead **tfm); > +extern void smb3_crypto_aead_free(struct crypto_aead **tfm); > extern unsigned long smb_rqst_len(struct TCP_Server_Info *server, > struct smb_rqst *rqst); > extern void smb2_set_next_command(struct cifs_tcon *tcon, > diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c > index cf319fc25161..ded7144d1578 100644 > --- a/fs/cifs/smb2transport.c > +++ b/fs/cifs/smb2transport.c > @@ -26,6 +26,63 @@ > #include "smb2status.h" > #include "smb2glob.h" > > +int > +smb2_init_secmechs(struct TCP_Server_Info *server) > +{ > + int rc = 0; > + > + if (server->dialect >= SMB30_PROT_ID && > + (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) { > + if (server->cipher_type == SMB2_ENCRYPTION_AES128_GCM || > + server->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { > + rc = smb3_crypto_aead_allocate("gcm(aes)", &server->secmech.enc); > + if (!rc) > + rc = smb3_crypto_aead_allocate("gcm(aes)", &server->secmech.dec); > + } else { > + rc = smb3_crypto_aead_allocate("ccm(aes)", &server->secmech.enc); > + if (!rc) > + rc = smb3_crypto_aead_allocate("ccm(aes)", &server->secmech.dec); > + } > + > + if (rc) > + return rc; > + } > + > + if (server->signing_algorithm == SIGNING_ALG_AES_GMAC) { > + cifs_free_hash(&server->secmech.sign.shash); > + cifs_free_hash(&server->secmech.verify.shash); > + > + rc = smb3_crypto_aead_allocate("gcm(aes)", &server->secmech.sign.aead); > + if (!rc) > + rc = smb3_crypto_aead_allocate("gcm(aes)", &server->secmech.verify.aead); > + if (rc) { > + smb3_crypto_aead_free(&server->secmech.sign.aead); > + return rc; > + } > + > + server->secmech.calc_signature = smb311_calc_signature; > + } else { > + char *shash_alg; > + > + if (server->dialect >= SMB30_PROT_ID) > + shash_alg = "cmac(aes)"; > + else > + shash_alg = "hmac(sha256)"; > + > + rc = cifs_alloc_hash(shash_alg, &server->secmech.sign.shash); > + if (!rc) > + rc = cifs_alloc_hash(shash_alg, &server->secmech.verify.shash); > + if (rc) { > + cifs_free_hash(&server->secmech.sign.shash); > + return rc; > + } > + > + server->secmech.calc_signature = smb2_calc_signature; > + } > + > + return rc; > +} > + > static int > smb3_setup_keys(struct TCP_Server_Info *server, u8 *sign_key, u8 *enc_key, u8 *dec_key) > { > @@ -42,44 +99,51 @@ smb3_setup_keys(struct TCP_Server_Info *server, u8 *sign_key, u8 *enc_key, u8 *d > crypt_keylen = SMB3_GCM128_CRYPTKEY_SIZE; > > rc = crypto_aead_setkey(server->secmech.enc, enc_key, crypt_keylen); > + if (!rc) > + rc = crypto_aead_setkey(server->secmech.dec, dec_key, crypt_keylen); > if (rc) { > - cifs_server_dbg(VFS, "%s: Failed to set AEAD encryption key, rc=%d\n", > + cifs_server_dbg(VFS, "%s: Failed to set encryption/decryption key, rc=%d\n", > __func__, rc); > return rc; > } > > rc = crypto_aead_setauthsize(server->secmech.enc, SMB2_SIGNATURE_SIZE); > + if (!rc) > + rc = crypto_aead_setauthsize(server->secmech.dec, SMB2_SIGNATURE_SIZE); > if (rc) { > - cifs_server_dbg(VFS, "%s: Failed to set AEAD encryption authsize, rc=%d\n", > - __func__, rc); > - return rc; > - } > - > - rc = crypto_aead_setkey(server->secmech.dec, dec_key, crypt_keylen); > - if (rc) { > - cifs_server_dbg(VFS, "%s: Failed to set AEAD decryption key, rc=%d\n", > - __func__, rc); > - return rc; > - } > - > - rc = crypto_aead_setauthsize(server->secmech.dec, SMB2_SIGNATURE_SIZE); > - if (rc) { > - cifs_server_dbg(VFS, "%s: Failed to set AEAD decryption authsize, rc=%d\n", > + cifs_server_dbg(VFS, "%s: Failed to set encryption/decryption authsize, rc=%d\n", > __func__, rc); > return rc; > } > setup_sign: > - rc = crypto_shash_setkey(server->secmech.sign->tfm, sign_key, SMB3_SIGN_KEY_SIZE); > - if (rc) { > - cifs_server_dbg(VFS, "%s: Failed to set AES-CMAC signing key, rc=%d\n", > - __func__, rc); > - return rc; > - } > + if (server->signing_algorithm == SIGNING_ALG_AES_GMAC) { > + rc = crypto_aead_setkey(server->secmech.sign.aead, sign_key, SMB3_SIGN_KEY_SIZE); > + if (!rc) > + rc = crypto_aead_setkey(server->secmech.verify.aead, sign_key, > + SMB3_SIGN_KEY_SIZE); > + if (rc) { > + cifs_server_dbg(VFS, "%s: Failed to set AES-GMAC key, rc=%d\n", > + __func__, rc); > + return rc; > + } > > - rc = crypto_shash_setkey(server->secmech.verify->tfm, sign_key, SMB3_SIGN_KEY_SIZE); > - if (rc) > - cifs_server_dbg(VFS, "%s: Failed to set AES-CMAC verify key, rc=%d\n", > - __func__, rc); > + rc = crypto_aead_setauthsize(server->secmech.sign.aead, SMB2_SIGNATURE_SIZE); > + if (!rc) > + rc = crypto_aead_setauthsize(server->secmech.verify.aead, > + SMB2_SIGNATURE_SIZE); > + if (rc) > + cifs_server_dbg(VFS, "%s: Failed to set AES-GMAC authsize, rc=%d\n", > + __func__, rc); > + } else { > + rc = crypto_shash_setkey(server->secmech.sign.shash->tfm, sign_key, > + SMB3_SIGN_KEY_SIZE); > + if (!rc) > + rc = crypto_shash_setkey(server->secmech.verify.shash->tfm, sign_key, > + SMB3_SIGN_KEY_SIZE); > + if (rc) > + cifs_server_dbg(VFS, "%s: Failed to set %s signing key, rc=%d\n", __func__, > + crypto_shash_alg_name(server->secmech.sign.shash->tfm), rc); > + } > > return rc; > } > @@ -171,9 +235,9 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, bool > memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); > > if (verify) > - shash = server->secmech.verify; > + shash = server->secmech.verify.shash; > else > - shash = server->secmech.sign; > + shash = server->secmech.sign.shash; > > rc = crypto_shash_init(shash); > if (rc) { > @@ -360,10 +424,6 @@ generate_smb3signingkey(struct cifs_ses *ses, > if (rc) > goto out_zero_keys; > > - rc = smb3_crypto_aead_allocate(server); > - if (rc) > - goto out_zero_keys; > - > rc = smb3_setup_keys(ses->server, sign_key, enc_key, dec_key); > if (rc) > goto out_zero_keys; > @@ -471,6 +531,125 @@ generate_smb311signingkey(struct cifs_ses *ses, > return generate_smb3signingkey(ses, server, &triplet); > } > > +/* > + * This function implements AES-GMAC signing for SMB2 messages as described in MS-SMB2 > + * specification. This algorithm is only supported on SMB 3.1.1. > + * > + * Note: even though Microsoft mentions RFC4543 in MS-SMB2, the mechanism used _must_ be the "raw" > + * AES-128-GCM ("gcm(aes)"); RFC4543 is designed for IPsec and trying to use "rfc4543(gcm(aes)))" > + * will fail the signature computation. > + * > + * MS-SMB2 3.1.4.1 > + */ > +int > +smb311_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, bool verify) > +{ > + union { > + struct { > + /* for MessageId (8 bytes) */ > + __le64 mid; > + /* for role (client or server) and if SMB2 CANCEL (4 bytes) */ > + __le32 role; > + }; > + u8 buffer[12]; > + } __packed nonce; > + u8 sig[SMB2_SIGNATURE_SIZE] = { 0 }; > + struct aead_request *aead_req = NULL; > + struct crypto_aead *tfm = NULL; > + struct scatterlist *sg = NULL; > + unsigned long assoclen; > + struct smb2_hdr *shdr = NULL; > + struct crypto_wait *wait; > + unsigned int save_npages = 0; > + int rc = 0; > + > + if (verify) { > + wait = &server->secmech.verify_wait; > + tfm = server->secmech.verify.aead; > + } else { > + wait = &server->secmech.sign_wait; > + tfm = server->secmech.sign.aead; > + } > + > + if (completion_done(&wait->completion)) > + reinit_completion(&wait->completion); > + > + shdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base; > + > + memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE); > + memset(&nonce, 0, SMB3_AES_GCM_NONCE); > + > + /* note that nonce must always be little endian */ > + nonce.mid = shdr->MessageId; > + /* request is coming from the server, set LSB */ > + nonce.role |= shdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR; > + /* set penultimate LSB if SMB2_CANCEL command */ > + if (shdr->Command == SMB2_CANCEL) > + nonce.role |= cpu_to_le32(1UL << 1); > + > + aead_req = aead_request_alloc(tfm, GFP_KERNEL); > + if (!aead_req) { > + cifs_dbg(VFS, "%s: Failed to alloc AEAD request\n", __func__); > + return -ENOMEM; > + } > + > + /* skip page data if non-success error status, as it will compute an invalid signature */ > + if (shdr->Status != 0 && rqst->rq_npages > 0) { > + save_npages = rqst->rq_npages; > + rqst->rq_npages = 0; > + } > + > + assoclen = smb_rqst_len(server, rqst); > + > + sg = smb3_init_sg(1, rqst, sig); > + if (!sg) { > + cifs_dbg(VFS, "%s: Failed to init SG\n", __func__); > + goto out_free_req; > + } > + > + /* cryptlen == 0 because we're not encrypting anything */ > + aead_request_set_crypt(aead_req, sg, sg, 0, nonce.buffer); > + aead_request_set_ad(aead_req, assoclen); > + aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, wait); > + > + /* > + * Reminder: we must always use the encrypt function, as AES-GCM decrypt will internally > + * try to match the authentication codes, where we pass a zeroed buffer, and the operation > + * will fail with -EBADMSG (expectedly). > + * > + * Also note we can't use crypto_wait_req() here since it's not interruptible. > + */ > + rc = crypto_aead_encrypt(aead_req); > + if (!rc) > + goto out; > + > + if (rc == -EINPROGRESS || rc == -EBUSY) { > + rc = wait_for_completion_interruptible(&wait->completion); > + if (!rc) > + /* wait->err is set by crypto_req_done callback above */ > + rc = wait->err; > + } > + > + if (rc) { > + cifs_server_dbg(VFS, "%s: Failed to compute AES-GMAC signature, rc=%d\n", > + __func__, rc); > + goto out_free_sg; > + } > + > +out: > + memcpy(&shdr->Signature, sig, SMB2_SIGNATURE_SIZE); > +out_free_sg: > + kfree(sg); > +out_free_req: > + kfree(aead_req); > + > + /* restore rq_npages for further processing */ > + if (shdr->Status != 0 && save_npages > 0) > + rqst->rq_npages = save_npages; > + > + return rc; > +} > + > /* must be called with server->srv_mutex held */ > static int > smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) > @@ -497,12 +676,10 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) > return 0; > } > spin_unlock(&server->srv_lock); > - if (!is_binding && !server->session_estab) { > - strncpy(shdr->Signature, "BSRSPYL", 8); > + if (!is_binding && !server->session_estab) > return 0; > - } > > - rc = server->ops->calc_signature(rqst, server, false); > + rc = server->secmech.calc_signature(rqst, server, false); > > return rc; > } > @@ -518,6 +695,8 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) > if ((shdr->Command == SMB2_NEGOTIATE) || > (shdr->Command == SMB2_SESSION_SETUP) || > (shdr->Command == SMB2_OPLOCK_BREAK) || > + (shdr->MessageId == cpu_to_le64(0xFFFFFFFFFFFFFFFF)) || > + (shdr->Status == STATUS_PENDING) || > server->ignore_signature || > (!server->session_estab)) > return 0; > @@ -527,20 +706,13 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) > * 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); > + rc = server->secmech.calc_signature(rqst, server, true); > > if (rc) > return rc; > @@ -693,6 +865,8 @@ smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server, > int rc; > struct smb2_hdr *shdr = > (struct smb2_hdr *)rqst->rq_iov[0].iov_base; > + struct smb2_transform_hdr *trhdr = > + (struct smb2_transform_hdr *)rqst->rq_iov[0].iov_base; > struct mid_q_entry *mid; > > smb2_seq_num_into_buf(server, shdr); > @@ -703,11 +877,22 @@ smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server, > 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); > + /* > + * Client must not sign the request if it's encrypted. > + * > + * Note: we can't rely on SMB2_SESSION_FLAG_ENCRYPT_DATA or SMB2_GLOBAL_CAP_ENCRYPTION > + * here because they might be set, but not being actively used (e.g. not mounted with > + * "seal"), so just check if header is a transform header. > + * > + * MS-SMB2 3.2.4.1.1 > + */ > + if (trhdr->ProtocolId != SMB2_TRANSFORM_PROTO_NUM) { > + rc = smb2_sign_rqst(rqst, server); > + if (rc) { > + revert_current_mid_from_hdr(server, shdr); > + delete_mid(mid); > + return ERR_PTR(rc); > + } > } > > return mid; > @@ -748,39 +933,29 @@ smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) > } > > int > -smb3_crypto_aead_allocate(struct TCP_Server_Info *server) > +smb3_crypto_aead_allocate(const char *name, struct crypto_aead **tfm) > { > - struct crypto_aead *tfm; > + if (unlikely(!tfm)) > + return -EIO; > > - 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 (*tfm) > + return 0; > > - 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; > + *tfm = crypto_alloc_aead(name, CRYPTO_ALG_TYPE_AEAD, 0); > + if (IS_ERR(*tfm)) { > + cifs_dbg(VFS, "%s: Failed to alloc %s crypto TFM, rc=%ld\n", > + __func__, name, PTR_ERR(*tfm)); > + return PTR_ERR(*tfm); > } > > return 0; > } > + > +void smb3_crypto_aead_free(struct crypto_aead **tfm) > +{ > + if (!tfm || !*tfm) > + return; > + > + crypto_free_aead(*tfm); > + *tfm = NULL; > +} > -- > 2.35.3 > -- Thanks, Steve