[CIFS] SMB3 Signing enablement
authorSteve French <smfrench@gmail.com>
Thu, 27 Jun 2013 04:45:05 +0000 (23:45 -0500)
committerSteve French <smfrench@gmail.com>
Thu, 27 Jun 2013 04:45:05 +0000 (23:45 -0500)
SMB3 uses a much faster method of signing (which is also better in other ways),
AES-CMAC.  With the kernel now supporting AES-CMAC since last release, we
are overdue to allow SMB3 signing (today only CIFS and SMB2 and SMB2.1,
but not SMB3 and SMB3.1 can sign) - and we need this also for checking
secure negotation and also per-share encryption (two other new SMB3 features
which we need to implement).

This patch needs some work in a few areas - for example we need to
move signing for SMB2/SMB3 from per-socket to per-user (we may be able to
use the "nosharesock" mount option in the interim for the multiuser case),
and Shirish found a bug in the earlier authentication overhaul
(setting signing flags properly) - but those can be done in followon
patches.

Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Signed-off-by: Steve French <smfrench@gmail.com>
fs/cifs/Kconfig
fs/cifs/cifsencrypt.c
fs/cifs/cifsglob.h
fs/cifs/cifspdu.h
fs/cifs/cifsproto.h
fs/cifs/connect.c
fs/cifs/smb2glob.h
fs/cifs/smb2transport.c

index 2906ee2764082786520718e4e4a490b9f3211ac6..603f18a65c121e5586f903baff101a8637bdb852 100644 (file)
@@ -10,6 +10,7 @@ config CIFS
        select CRYPTO_ECB
        select CRYPTO_DES
        select CRYPTO_SHA256
+       select CRYPTO_CMAC
        help
          This is the client VFS module for the Common Internet File System
          (CIFS) protocol which is the successor to the Server Message Block
index 330875948f186926d26f632b765550f28940dab5..3d8bf941d1269ff3afa45baf1ed9f5aee8491431 100644 (file)
@@ -705,6 +705,9 @@ calc_seckey(struct cifs_ses *ses)
 void
 cifs_crypto_shash_release(struct TCP_Server_Info *server)
 {
+       if (server->secmech.cmacaes)
+               crypto_free_shash(server->secmech.cmacaes);
+
        if (server->secmech.hmacsha256)
                crypto_free_shash(server->secmech.hmacsha256);
 
@@ -714,6 +717,8 @@ cifs_crypto_shash_release(struct TCP_Server_Info *server)
        if (server->secmech.hmacmd5)
                crypto_free_shash(server->secmech.hmacmd5);
 
+       kfree(server->secmech.sdesccmacaes);
+
        kfree(server->secmech.sdeschmacsha256);
 
        kfree(server->secmech.sdeschmacmd5);
@@ -747,6 +752,13 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
                goto crypto_allocate_hmacsha256_fail;
        }
 
+       server->secmech.cmacaes = crypto_alloc_shash("cmac(aes)", 0, 0);
+       if (IS_ERR(server->secmech.cmacaes)) {
+               cifs_dbg(VFS, "could not allocate crypto cmac-aes");
+               rc = PTR_ERR(server->secmech.cmacaes);
+               goto crypto_allocate_cmacaes_fail;
+       }
+
        size = sizeof(struct shash_desc) +
                        crypto_shash_descsize(server->secmech.hmacmd5);
        server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
@@ -777,8 +789,22 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
        server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256;
        server->secmech.sdeschmacsha256->shash.flags = 0x0;
 
+       size = sizeof(struct shash_desc) +
+                       crypto_shash_descsize(server->secmech.cmacaes);
+       server->secmech.sdesccmacaes = kmalloc(size, GFP_KERNEL);
+       if (!server->secmech.sdesccmacaes) {
+               cifs_dbg(VFS, "%s: Can't alloc cmacaes\n", __func__);
+               rc = -ENOMEM;
+               goto crypto_allocate_cmacaes_sdesc_fail;
+       }
+       server->secmech.sdesccmacaes->shash.tfm = server->secmech.cmacaes;
+       server->secmech.sdesccmacaes->shash.flags = 0x0;
+
        return 0;
 
+crypto_allocate_cmacaes_sdesc_fail:
+       kfree(server->secmech.sdeschmacsha256);
+
 crypto_allocate_hmacsha256_sdesc_fail:
        kfree(server->secmech.sdescmd5);
 
@@ -786,6 +812,9 @@ crypto_allocate_md5_sdesc_fail:
        kfree(server->secmech.sdeschmacmd5);
 
 crypto_allocate_hmacmd5_sdesc_fail:
+       crypto_free_shash(server->secmech.cmacaes);
+
+crypto_allocate_cmacaes_fail:
        crypto_free_shash(server->secmech.hmacsha256);
 
 crypto_allocate_hmacsha256_fail:
index 9a1e37aad3b8d49ed50f16bde2692f753b674468..2d0f524ebeee7484474e627e3b10494fb77c02a8 100644 (file)
@@ -125,9 +125,11 @@ struct cifs_secmech {
        struct crypto_shash *hmacmd5; /* hmac-md5 hash function */
        struct crypto_shash *md5; /* md5 hash function */
        struct crypto_shash *hmacsha256; /* hmac-sha256 hash function */
+       struct crypto_shash *cmacaes; /* block-cipher based MAC function */
        struct sdesc *sdeschmacmd5;  /* ctxt to generate ntlmv2 hash, CR1 */
        struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */
        struct sdesc *sdeschmacsha256;  /* ctxt to generate smb2 signature */
+       struct sdesc *sdesccmacaes;  /* ctxt to generate smb3 signature */
 };
 
 /* per smb session structure/fields */
@@ -538,6 +540,7 @@ struct TCP_Server_Info {
        int timeAdj;  /* Adjust for difference in server time zone in sec */
        __u64 CurrentMid;         /* multiplex id - rotating counter */
        char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */
+       char smb3signingkey[SMB3_SIGN_KEY_SIZE]; /* for signing smb3 packets */
        /* 16th byte of RFC1001 workstation name is always null */
        char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
        __u32 sequence_number; /* for signing, protected by srv_mutex */
index 7e8523c5c18e1b1a30c1571df96b3df06e54df5b..11ca24a8e054ef11472ba634472e6e8622008305 100644 (file)
  */
 #define CIFS_SESS_KEY_SIZE (16)
 
+/*
+ * Size of the smb3 signing key
+ */
+#define SMB3_SIGN_KEY_SIZE (16)
+
 #define CIFS_CLIENT_CHALLENGE_SIZE (8)
 #define CIFS_SERVER_CHALLENGE_SIZE (8)
 #define CIFS_HMAC_MD5_HASH_SIZE (16)
index a82b3c09888ba8a07c7b51c7517a7a4e0b2c5d86..ff669e7c5857bf404768502b1633ea69d5ecf3e2 100644 (file)
@@ -436,6 +436,7 @@ extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
 extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *);
 extern void cifs_crypto_shash_release(struct TCP_Server_Info *);
 extern int calc_seckey(struct cifs_ses *);
+extern int generate_smb3signingkey(struct TCP_Server_Info *);
 
 #ifdef CONFIG_CIFS_WEAK_PW_HASH
 extern int calc_lanman_hash(const char *password, const char *cryptkey,
index 53a1780cd0731c4eaf5c6bc56dc33e224d9d8b64..354ea7782dba3d67b5eeca37841ebbdfe9ccc410 100644 (file)
@@ -3841,6 +3841,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
                        server->sequence_number = 0x2;
                        server->session_estab = true;
                        ses->auth_key.response = NULL;
+                       generate_smb3signingkey(server);
                }
                mutex_unlock(&server->srv_mutex);
 
index 7c0e2143e775e04f183ccad271de61e73bc2edf3..c38350851b0883cd6074b04f5aa36398b2528e3c 100644 (file)
@@ -54,5 +54,7 @@
 #define SMB2_SIGNATURE_SIZE (16)
 #define SMB2_NTLMV2_SESSKEY_SIZE (16)
 #define SMB2_HMACSHA256_SIZE (32)
+#define SMB2_CMACAES_SIZE (16)
+#define SMB3_SIGNKEY_SIZE (16)
 
 #endif /* _SMB2_GLOB_H */
index c802ecfa770e0812edd995fac7ff98c4bc2e43f6..87563ee58d306942bf73ac50a2c596db6e8c423d 100644 (file)
@@ -116,11 +116,155 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
        return rc;
 }
 
+int
+generate_smb3signingkey(struct TCP_Server_Info *server)
+{
+       unsigned char zero = 0x0;
+       __u8 i[4] = {0, 0, 0, 1};
+       __u8 L[4] = {0, 0, 0, 128};
+       int rc = 0;
+       unsigned char prfhash[SMB2_HMACSHA256_SIZE];
+       unsigned char *hashptr = prfhash;
+
+       memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
+       memset(server->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE);
+
+       rc = crypto_shash_setkey(server->secmech.hmacsha256,
+               server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
+       if (rc) {
+               cifs_dbg(VFS, "%s: Could not set with session key\n", __func__);
+               goto smb3signkey_ret;
+       }
+
+       rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
+       if (rc) {
+               cifs_dbg(VFS, "%s: Could not init sign hmac\n", __func__);
+               goto smb3signkey_ret;
+       }
+
+       rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
+                               i, 4);
+       if (rc) {
+               cifs_dbg(VFS, "%s: Could not update with n\n", __func__);
+               goto smb3signkey_ret;
+       }
+
+       rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
+                               "SMB2AESCMAC", 12);
+       if (rc) {
+               cifs_dbg(VFS, "%s: Could not update with label\n", __func__);
+               goto smb3signkey_ret;
+       }
+
+       rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
+                               &zero, 1);
+       if (rc) {
+               cifs_dbg(VFS, "%s: Could not update with zero\n", __func__);
+               goto smb3signkey_ret;
+       }
+
+       rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
+                               "SmbSign", 8);
+       if (rc) {
+               cifs_dbg(VFS, "%s: Could not update with context\n", __func__);
+               goto smb3signkey_ret;
+       }
+
+       rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
+                               L, 4);
+       if (rc) {
+               cifs_dbg(VFS, "%s: Could not update with L\n", __func__);
+               goto smb3signkey_ret;
+       }
+
+       rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
+                               hashptr);
+       if (rc) {
+               cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__);
+               goto smb3signkey_ret;
+       }
+
+       memcpy(server->smb3signingkey, hashptr, SMB3_SIGNKEY_SIZE);
+
+smb3signkey_ret:
+       return rc;
+}
+
 int
 smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
 {
-       cifs_dbg(FYI, "smb3 signatures not supported yet\n");
-       return -EOPNOTSUPP;
+       int i, rc;
+       unsigned char smb3_signature[SMB2_CMACAES_SIZE];
+       unsigned char *sigptr = smb3_signature;
+       struct kvec *iov = rqst->rq_iov;
+       int n_vec = rqst->rq_nvec;
+       struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
+
+       memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
+       memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
+
+       rc = crypto_shash_setkey(server->secmech.cmacaes,
+               server->smb3signingkey, SMB2_CMACAES_SIZE);
+       if (rc) {
+               cifs_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
+               return rc;
+       }
+
+       rc = crypto_shash_init(&server->secmech.sdesccmacaes->shash);
+       if (rc) {
+               cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__);
+               return rc;
+       }
+
+       for (i = 0; i < n_vec; i++) {
+               if (iov[i].iov_len == 0)
+                       continue;
+               if (iov[i].iov_base == NULL) {
+                       cifs_dbg(VFS, "null iovec entry");
+                       return -EIO;
+               }
+               /*
+                * The first entry includes a length field (which does not get
+                * signed that occupies the first 4 bytes before the header).
+                */
+               if (i == 0) {
+                       if (iov[0].iov_len <= 8) /* cmd field at offset 9 */
+                               break; /* nothing to sign or corrupt header */
+                       rc =
+                       crypto_shash_update(
+                               &server->secmech.sdesccmacaes->shash,
+                               iov[i].iov_base + 4, iov[i].iov_len - 4);
+               } else {
+                       rc =
+                       crypto_shash_update(
+                               &server->secmech.sdesccmacaes->shash,
+                               iov[i].iov_base, iov[i].iov_len);
+               }
+               if (rc) {
+                       cifs_dbg(VFS, "%s: Couldn't update cmac aes with payload\n",
+                                                       __func__);
+                       return rc;
+               }
+       }
+
+       /* now hash over the rq_pages array */
+       for (i = 0; i < rqst->rq_npages; i++) {
+               struct kvec p_iov;
+
+               cifs_rqst_page_to_kvec(rqst, i, &p_iov);
+               crypto_shash_update(&server->secmech.sdesccmacaes->shash,
+                                       p_iov.iov_base, p_iov.iov_len);
+               kunmap(rqst->rq_pages[i]);
+       }
+
+       rc = crypto_shash_final(&server->secmech.sdesccmacaes->shash,
+                                               sigptr);
+       if (rc)
+               cifs_dbg(VFS, "%s: Could not generate cmac aes\n", __func__);
+
+       memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE);
+
+       return rc;
 }
 
 /* must be called with server->srv_mutex held */