]> git.karo-electronics.de Git - linux-beck.git/commitdiff
cifs: Start using per session key for smb2/3 for signature generation
authorShirish Pargaonkar <shirishpargaonkar@gmail.com>
Thu, 29 Aug 2013 13:35:11 +0000 (08:35 -0500)
committerSteve French <smfrench@gmail.com>
Sun, 8 Sep 2013 19:47:50 +0000 (14:47 -0500)
    Switch smb2 code to use per session session key and smb3 code to
    use per session signing key instead of per connection key to
    generate signatures.

    For that, we need to find a session to fetch the session key to
    generate signature to match for every request and response packet.

    We also forgo checking signature for a session setup response
    from the server.

Acked-by: Jeff Layton <jlayton@samba.org>
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Signed-off-by: Steve French <smfrench@gmail.com>
fs/cifs/cifsglob.h
fs/cifs/cifsproto.h
fs/cifs/smb2pdu.c
fs/cifs/smb2transport.c

index 20c7f8f4fba889d60c3087a3cf626e92108546e6..92798f74f0c316fd9cd84a8b12b1d87e0792cb6c 100644 (file)
@@ -366,8 +366,7 @@ struct smb_version_operations {
        void (*set_lease_key)(struct inode *, struct cifs_fid *fid);
        /* generate new lease key */
        void (*new_lease_key)(struct cifs_fid *fid);
-       /* The next two functions will need to be changed to per smb session */
-       void (*generate_signingkey)(struct TCP_Server_Info *server);
+       int (*generate_signingkey)(struct cifs_ses *);
        int (*calc_signature)(struct smb_rqst *rqst,
                                   struct TCP_Server_Info *server);
        int (*query_mf_symlink)(const unsigned char *path, char *pbuf,
@@ -548,7 +547,6 @@ 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 */
@@ -731,6 +729,7 @@ struct cifs_ses {
        bool need_reconnect:1; /* connection reset, uid now invalid */
 #ifdef CONFIG_CIFS_SMB2
        __u16 session_flags;
+       char smb3signingkey[SMB3_SIGN_KEY_SIZE]; /* for signing smb3 packets */
 #endif /* CONFIG_CIFS_SMB2 */
 };
 
index 5b05eb082a415ab0708179a644dc2c3733d1d0fd..b5ec2a268f560c77424744c55266480f25f3c8bb 100644 (file)
@@ -431,7 +431,7 @@ extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *);
 extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
 extern void cifs_crypto_shash_release(struct TCP_Server_Info *);
 extern int calc_seckey(struct cifs_ses *);
-extern void generate_smb3signingkey(struct TCP_Server_Info *);
+extern int generate_smb3signingkey(struct cifs_ses *);
 
 #ifdef CONFIG_CIFS_WEAK_PW_HASH
 extern int calc_lanman_hash(const char *password, const char *cryptkey,
index 3d010766f2671091cef71b36b38ba86588c1c2b5..0a945828271792cc7d07aeb4c92db6fb4406b2cd 100644 (file)
@@ -639,11 +639,20 @@ ssetup_exit:
 
        if (!rc) {
                mutex_lock(&server->srv_mutex);
+               if (server->sign && server->ops->generate_signingkey) {
+                       rc = server->ops->generate_signingkey(ses);
+                       kfree(ses->auth_key.response);
+                       ses->auth_key.response = NULL;
+                       if (rc) {
+                               cifs_dbg(FYI,
+                                       "SMB3 session key generation failed\n");
+                               mutex_unlock(&server->srv_mutex);
+                               goto keygen_exit;
+                       }
+               }
                if (!server->session_estab) {
                        server->sequence_number = 0x2;
                        server->session_estab = true;
-                       if (server->ops->generate_signingkey)
-                               server->ops->generate_signingkey(server);
                }
                mutex_unlock(&server->srv_mutex);
 
@@ -654,6 +663,7 @@ ssetup_exit:
                spin_unlock(&GlobalMid_Lock);
        }
 
+keygen_exit:
        if (!server->sign) {
                kfree(ses->auth_key.response);
                ses->auth_key.response = NULL;
index 4f2300d020c7e517a6fa89be5d955fa4b12753da..340abca3aa522f31490d0c483e76b60aa08d582a 100644 (file)
@@ -114,6 +114,23 @@ smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
        return 0;
 }
 
+static struct cifs_ses *
+smb2_find_smb_ses(struct smb2_hdr *smb2hdr, struct TCP_Server_Info *server)
+{
+       struct cifs_ses *ses;
+
+       spin_lock(&cifs_tcp_ses_lock);
+       list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+               if (ses->Suid != smb2hdr->SessionId)
+                       continue;
+               spin_unlock(&cifs_tcp_ses_lock);
+               return ses;
+       }
+       spin_unlock(&cifs_tcp_ses_lock);
+
+       return NULL;
+}
+
 
 int
 smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
@@ -124,6 +141,13 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
        struct kvec *iov = rqst->rq_iov;
        int n_vec = rqst->rq_nvec;
        struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
+       struct cifs_ses *ses;
+
+       ses = smb2_find_smb_ses(smb2_pdu, server);
+       if (!ses) {
+               cifs_dbg(VFS, "%s: Could not find session\n", __func__);
+               return 0;
+       }
 
        memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
        memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
@@ -135,7 +159,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
        }
 
        rc = crypto_shash_setkey(server->secmech.hmacsha256,
-               server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
+               ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
        if (rc) {
                cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
                return rc;
@@ -198,8 +222,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
        return rc;
 }
 
-void
-generate_smb3signingkey(struct TCP_Server_Info *server)
+int
+generate_smb3signingkey(struct cifs_ses *ses)
 {
        unsigned char zero = 0x0;
        __u8 i[4] = {0, 0, 0, 1};
@@ -209,90 +233,99 @@ generate_smb3signingkey(struct TCP_Server_Info *server)
        unsigned char *hashptr = prfhash;
 
        memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
-       memset(server->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE);
+       memset(ses->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE);
 
-       rc = smb3_crypto_shash_allocate(server);
+       rc = smb3_crypto_shash_allocate(ses->server);
        if (rc) {
                cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
                goto smb3signkey_ret;
        }
 
-       rc = crypto_shash_setkey(server->secmech.hmacsha256,
-               server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
+       rc = crypto_shash_setkey(ses->server->secmech.hmacsha256,
+               ses->auth_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);
+       rc = crypto_shash_init(&ses->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,
+       rc = crypto_shash_update(&ses->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,
+       rc = crypto_shash_update(&ses->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,
+       rc = crypto_shash_update(&ses->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,
+       rc = crypto_shash_update(&ses->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,
+       rc = crypto_shash_update(&ses->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,
+       rc = crypto_shash_final(&ses->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);
+       memcpy(ses->smb3signingkey, hashptr, SMB3_SIGNKEY_SIZE);
 
 smb3signkey_ret:
-       return;
+       return rc;
 }
 
 int
 smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
 {
-       int i, rc;
+       int i;
+       int rc = 0;
        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;
+       struct cifs_ses *ses;
+
+       ses = smb2_find_smb_ses(smb2_pdu, server);
+       if (!ses) {
+               cifs_dbg(VFS, "%s: Could not find session\n", __func__);
+               return 0;
+       }
 
        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);
+               ses->smb3signingkey, SMB2_CMACAES_SIZE);
+
        if (rc) {
                cifs_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
                return rc;
@@ -389,6 +422,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
        struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
 
        if ((smb2_pdu->Command == SMB2_NEGOTIATE) ||
+           (smb2_pdu->Command == SMB2_SESSION_SETUP) ||
            (smb2_pdu->Command == SMB2_OPLOCK_BREAK) ||
            (!server->session_estab))
                return 0;