diff mbox

smb2: Enable signing in smb2

Message ID 1313609780-4606-1-git-send-email-shirishpargaonkar@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Shirish Pargaonkar Aug. 17, 2011, 7:36 p.m. UTC
From: Shirish Pargaonkar <shirishpargaonkar@gmail.com>


Enable signing in smb2.  For smb2, hmac-sha256 is used insted
of hmac-md5 used for cifs/smb.
Signature field in smb2 header is 16 bytes instead of 8 bytes.


Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
---
 fs/cifs/Kconfig         |    1 +
 fs/cifs/cifsencrypt.c   |   30 ++++++++-
 fs/cifs/cifsglob.h      |    2 +
 fs/cifs/smb2pdu.c       |   32 ++++++++-
 fs/cifs/smb2pdu.h       |    4 +
 fs/cifs/smb2transport.c |  175 +++++++++++++++++++++++++++++++++++++++++++++--
 6 files changed, 235 insertions(+), 9 deletions(-)

Comments

Pavel Shilovsky Aug. 21, 2011, 3:20 p.m. UTC | #1
2011/8/17  <shirishpargaonkar@gmail.com>:
> From: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
>
>
> Enable signing in smb2.  For smb2, hmac-sha256 is used insted
> of hmac-md5 used for cifs/smb.
> Signature field in smb2 header is 16 bytes instead of 8 bytes.
>
>
> Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
> ---
>  fs/cifs/Kconfig         |    1 +
>  fs/cifs/cifsencrypt.c   |   30 ++++++++-
>  fs/cifs/cifsglob.h      |    2 +
>  fs/cifs/smb2pdu.c       |   32 ++++++++-
>  fs/cifs/smb2pdu.h       |    4 +
>  fs/cifs/smb2transport.c |  175 +++++++++++++++++++++++++++++++++++++++++++++--
>  6 files changed, 235 insertions(+), 9 deletions(-)
>
> diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
> index f66cc16..ed5452e 100644
> --- a/fs/cifs/Kconfig
> +++ b/fs/cifs/Kconfig
> @@ -9,6 +9,7 @@ config CIFS
>        select CRYPTO_ARC4
>        select CRYPTO_ECB
>        select CRYPTO_DES
> +       select CRYPTO_SHA256
>        help
>          This is the client VFS module for the Common Internet File System
>          (CIFS) protocol which is the successor to the Server Message Block
> diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
> index e76bfeb..bb7c234 100644
> --- a/fs/cifs/cifsencrypt.c
> +++ b/fs/cifs/cifsencrypt.c
> @@ -774,12 +774,17 @@ calc_seckey(struct cifs_ses *ses)
>  void
>  cifs_crypto_shash_release(struct TCP_Server_Info *server)
>  {
> +       if (server->secmech.hmacsha256)
> +               crypto_free_shash(server->secmech.hmacsha256);
> +
>        if (server->secmech.md5)
>                crypto_free_shash(server->secmech.md5);
>
>        if (server->secmech.hmacmd5)
>                crypto_free_shash(server->secmech.hmacmd5);
>
> +       kfree(server->secmech.sdeschmacsha256);
> +
>        kfree(server->secmech.sdeschmacmd5);
>
>        kfree(server->secmech.sdescmd5);
> @@ -804,6 +809,13 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
>                goto crypto_allocate_md5_fail;
>        }
>
> +       server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0);
> +       if (IS_ERR(server->secmech.hmacsha256)) {
> +               cERROR(1, "could not allocate crypto hmacsha256\n");
> +               rc = PTR_ERR(server->secmech.hmacsha256);
> +               goto crypto_allocate_hmacsha256_fail;
> +       }
> +
>        size = sizeof(struct shash_desc) +
>                        crypto_shash_descsize(server->secmech.hmacmd5);
>        server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
> @@ -815,7 +827,6 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
>        server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5;
>        server->secmech.sdeschmacmd5->shash.flags = 0x0;
>
> -
>        size = sizeof(struct shash_desc) +
>                        crypto_shash_descsize(server->secmech.md5);
>        server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL);
> @@ -827,12 +838,29 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
>        server->secmech.sdescmd5->shash.tfm = server->secmech.md5;
>        server->secmech.sdescmd5->shash.flags = 0x0;
>
> +       size = sizeof(struct shash_desc) +
> +                       crypto_shash_descsize(server->secmech.hmacsha256);
> +       server->secmech.sdeschmacsha256 = kmalloc(size, GFP_KERNEL);
> +       if (!server->secmech.sdeschmacsha256) {
> +               cERROR(1, "%s: Can't alloc hmacsha256\n", __func__);
> +               rc = -ENOMEM;
> +               goto crypto_allocate_hmacsha256_sdesc_fail;
> +       }
> +       server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256;
> +       server->secmech.sdeschmacsha256->shash.flags = 0x0;
> +
>        return 0;
>
> +crypto_allocate_hmacsha256_sdesc_fail:
> +       kfree(server->secmech.sdescmd5);
> +
>  crypto_allocate_md5_sdesc_fail:
>        kfree(server->secmech.sdeschmacmd5);
>
>  crypto_allocate_hmacmd5_sdesc_fail:
> +       crypto_free_shash(server->secmech.hmacsha256);
> +
> +crypto_allocate_hmacsha256_fail:
>        crypto_free_shash(server->secmech.md5);
>
>  crypto_allocate_md5_fail:
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 13d8f4d..4c38b98 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -136,8 +136,10 @@ struct sdesc {
>  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 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 */
>  };
>
>  /* per smb session structure/fields */
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index dad29aa..17f5f22 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -657,6 +657,37 @@ SMB2_negotiate(unsigned int xid, struct cifs_ses *ses)
>                rc = -EIO;
>                goto neg_exit;
>        }
> +
> +       if ((sec_flags & CIFSSEC_MAY_SIGN) == 0) {
> +               /* MUST_SIGN already includes the MAY_SIGN FLAG
> +                  so if this is zero it means that signing is disabled */
> +               cFYI(1, "Signing disabled");
> +               if (ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
> +                       cERROR(1, "Server requires "
> +                                  "packet signing to be enabled in "
> +                                  "/proc/fs/cifs/SecurityFlags.");
> +                       rc = -EOPNOTSUPP;
> +               }
> +               ses->server->sec_mode &=
> +                       ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
> +       } else if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
> +               /* signing required */
> +               cFYI(1, "Must sign - sec_flags 0x%x", sec_flags);
> +               if ((ses->server->sec_mode & (SMB2_NEGOTIATE_SIGNING_ENABLED |
> +                               SMB2_NEGOTIATE_SIGNING_REQUIRED)) == 0) {
> +                       cERROR(1, "signing required but server lacks support");
> +                       rc = -EOPNOTSUPP;
> +               } else
> +                       ses->server->sec_mode
> +                               |= SECMODE_SIGN_REQUIRED;
> +       } else {
> +               /* signing optional ie CIFSSEC_MAY_SIGN */
> +               if ((ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED)
> +                                                                       == 0)
> +                       ses->server->sec_mode &=
> +                               ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
> +       }
> +

In this case we need to fix the check for in smb2_header_assemble from
if (tcon->ses->server->sec_mode &
                     SMB2_NEGOTIATE_SIGNING_REQUIRED)
to
if (tcon->ses->server->sec_mode & SECMODE_SIGN_REQUIRED)

to enable signing on the client. Without it we fail if the server
responses with signing 'enabled' but 'sign' mount option is specified.

>  #ifdef CONFIG_SMB2_ASN1  /* BB REMOVEME when updated asn1.c ready */
>        rc = decode_neg_token_init(security_blob, blob_length,
>                                   &ses->server->sec_type);
> @@ -929,7 +960,6 @@ SMB2_tcon(unsigned int xid, struct cifs_ses *ses,
>        pSMB2->hdr.smb2_buf_length =
>                cpu_to_be32(be32_to_cpu(pSMB2->hdr.smb2_buf_length)
>                            - 1 /* pad */ + unc_path_len);
> -
>        rc = smb2_sendrcv2(xid, ses, iov, 2, &resp_buftype /* ret */, &status,
>                           CIFS_STD_OP | CIFS_LOG_ERROR);
>        cFYI(1, "tcon buftype %d rc %d status %d", resp_buftype, rc, status);
> diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
> index 8dceea0..1d7bafc 100644
> --- a/fs/cifs/smb2pdu.h
> +++ b/fs/cifs/smb2pdu.h
> @@ -1057,4 +1057,8 @@ struct symlink_reparse_data_buf {
>        char pathbuffer[1];
>  } __attribute__((packed));
>
> +#define SMB2FLG_SECURITY_SIGNATURE (8)

We already have

#define SMB2_FLAGS_SIGNED                cpu_to_le32(0x00000008)

> +#define SMB2_SIGNATURE_SIZE (16)
> +#define SMB2_NTLMV2_SESSKEY_SIZE (16)
> +#define SMB2_HMACSHA256_SIZE (32)
>  #endif                         /* _SMB2PDU_H */
> diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
> index bc62e2d..a20bff8 100644
> --- a/fs/cifs/smb2transport.c
> +++ b/fs/cifs/smb2transport.c
> @@ -37,6 +37,169 @@
>
>  extern mempool_t *smb2_mid_poolp;
>
> +static int smb2_calc_signature(struct smb2_hdr *smb2_pdu,
> +                       struct TCP_Server_Info *server, char *signature)
> +{
> +       int rc;
> +       unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
> +       unsigned char *sigptr = smb2_signature;
> +
> +       memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
> +       memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
> +
> +       rc = crypto_shash_setkey(server->secmech.hmacsha256,
> +               server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
> +       if (rc) {
> +               cERROR(1, "%s: Could not update with response\n", __func__);
> +               return rc;
> +       }
> +
> +       rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
> +       if (rc) {
> +               cERROR(1, "%s: Could not init md5\n", __func__);
> +               return rc;
> +       }
> +
> +       rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
> +                               smb2_pdu->ProtocolId,
> +                               be32_to_cpu(smb2_pdu->smb2_buf_length));
> +       if (rc) {
> +               cERROR(1, "%s: Could not update with payload\n", __func__);
> +               return rc;
> +       }
> +
> +       rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
> +                                       sigptr);
> +       if (rc)
> +               cERROR(1, "%s: Could not generate sha256 hash\n", __func__);
> +
> +       memcpy(smb2_pdu->Signature, sigptr, SMB2_NTLMV2_SESSKEY_SIZE);
> +
> +       return rc;
> +}
> +
> +static int smb2_calc_signature2(const struct kvec *iov, int n_vec,
> +               struct TCP_Server_Info *server, struct smb2_hdr *smb2_pdu)
> +{
> +       int i, rc;
> +       unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
> +       unsigned char *sigptr = smb2_signature;
> +
> +       memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
> +       memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
> +
> +       rc = crypto_shash_setkey(server->secmech.hmacsha256,
> +               server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
> +       if (rc) {
> +               cERROR(1, "%s: Could not update with response\n", __func__);
> +               return rc;
> +       }
> +
> +       rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
> +       if (rc) {
> +               cERROR(1, "%s: Could not init md5\n", __func__);
> +               return rc;
> +       }
> +
> +       for (i = 0; i < n_vec; i++) {
> +               if (iov[i].iov_len == 0)
> +                       continue;
> +               if (iov[i].iov_base == NULL) {
> +                       cERROR(1, "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.sdeschmacsha256->shash,
> +                               iov[i].iov_base + 4, iov[i].iov_len - 4);
> +               } else {
> +                       rc =
> +                       crypto_shash_update(
> +                               &server->secmech.sdeschmacsha256->shash,
> +                               iov[i].iov_base, iov[i].iov_len);
> +               }
> +               if (rc) {
> +                       cERROR(1, "%s: Could not update with payload\n",
> +                                                       __func__);
> +                       return rc;
> +               }
> +       }
> +
> +       rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
> +                                       sigptr);
> +       if (rc)
> +               cERROR(1, "%s: Could not generate sha256 hash\n", __func__);
> +
> +       memcpy(smb2_pdu->Signature, sigptr, SMB2_NTLMV2_SESSKEY_SIZE);
> +
> +       return rc;
> +}
> +
> +/* must be called with server->srv_mutex held */
> +int smb2_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server)
> +{
> +       int rc = 0;
> +       struct smb2_hdr *smb2_pdu = iov[0].iov_base;
> +
> +       if (!(smb2_pdu->Flags & SMB2FLG_SECURITY_SIGNATURE) ||
> +           server->tcpStatus == CifsNeedNegotiate)
> +               return rc;
> +
> +       if (!server->session_estab) {
> +               strncpy(smb2_pdu->Signature, "BSRSPYL", 8);
> +               return rc;
> +       }
> +
> +       rc = smb2_calc_signature2(iov, n_vec, server, smb2_pdu);
> +
> +       return rc;
> +}
> +
> +int
> +smb2_verify_signature(struct smb2_hdr *smb2_pdu, struct TCP_Server_Info *server)
> +{
> +       unsigned int rc;
> +       char server_response_sig[16];
> +       char what_we_think_sig_should_be[20];
> +
> +       if ((smb2_pdu->Command == SMB2_NEGOTIATE) ||
> +                       (smb2_pdu->Command == SMB2_OPLOCK_BREAK) ||
> +                       (!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(smb2_pdu->Signature, "BSRSPYL ", 8) == 0)
> +               cFYI(1, "dummy signature received for smb command 0x%x",
> +                       smb2_pdu->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, smb2_pdu->Signature, 16);
> +
> +       memset(smb2_pdu->Signature, 0, 16);
> +
> +       rc = smb2_calc_signature(smb2_pdu, server, what_we_think_sig_should_be);
> +
> +       if (rc)
> +               return rc;
> +
> +/*     smb2_dump_mem("what we think it should be: ",
> +                     what_we_think_sig_should_be, 16); */
> +
> +       if (memcmp(server_response_sig, what_we_think_sig_should_be, 8))
> +               return -EACCES;
> +       else
> +               return 0;
> +
> +}
>  /*
>  * Set message id for the request. Should be called after wait_for_free_response
>  * and locking srv_mutex. iov array must have at least 1 element.
> @@ -292,18 +455,16 @@ int
>  smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
>                   unsigned int receive_len, bool log_error)
>  {
> +       int rc = 0;
>        struct smb2_hdr *buf = (struct smb2_hdr *)mid->resp_buf;
>
>        dump_smb2(buf, 80);
>        /* convert the length into a more usable form */
>        if ((receive_len > 24) &&
>            (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) {
> -               /* BB fixme */
> -               /*rc = smb2_verify_signature(mid->resp_buf,
> -                                       &ses->server->mac_signing_key);
> -               if (rc) {
> +               rc = smb2_verify_signature(mid->resp_buf, server);
> +               if (rc)
>                        cERROR(1, "Unexpected SMB signature");
> -               } */
>        }
>
>        return map_smb2_to_linux_error(buf, log_error);
> @@ -371,12 +532,12 @@ smb2_sendrcv2(const unsigned int xid, struct cifs_ses *ses,
>                wake_up(&ses->server->request_q);
>                return rc;
>        }
> -       /* rc = sign_smb2(iov, n_vec, ses->server); BB
> +       rc = smb2_sign_smb2(iov, n_vec, ses->server);
>        if (rc) {
>                mutex_unlock(&ses->server->srv_mutex);
>                cifs_small_buf_release(buf);
>                goto out;
> -       } */
> +       }

It seems we also need this for smb2_call_async too.

>
>        midQ->mid_state = MID_REQUEST_SUBMITTED;
>        cifs_in_send_inc(ses->server);
> --
> 1.6.0.2
>
>
Shirish Pargaonkar Aug. 21, 2011, 9:34 p.m. UTC | #2
On Sun, Aug 21, 2011 at 10:20 AM, Pavel Shilovsky <piastryyy@gmail.com> wrote:
> 2011/8/17  <shirishpargaonkar@gmail.com>:
>> From: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
>>
>>
>> Enable signing in smb2.  For smb2, hmac-sha256 is used insted
>> of hmac-md5 used for cifs/smb.
>> Signature field in smb2 header is 16 bytes instead of 8 bytes.
>>
>>
>> Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
>> ---
>>  fs/cifs/Kconfig         |    1 +
>>  fs/cifs/cifsencrypt.c   |   30 ++++++++-
>>  fs/cifs/cifsglob.h      |    2 +
>>  fs/cifs/smb2pdu.c       |   32 ++++++++-
>>  fs/cifs/smb2pdu.h       |    4 +
>>  fs/cifs/smb2transport.c |  175 +++++++++++++++++++++++++++++++++++++++++++++--
>>  6 files changed, 235 insertions(+), 9 deletions(-)
>>
>> diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
>> index f66cc16..ed5452e 100644
>> --- a/fs/cifs/Kconfig
>> +++ b/fs/cifs/Kconfig
>> @@ -9,6 +9,7 @@ config CIFS
>>        select CRYPTO_ARC4
>>        select CRYPTO_ECB
>>        select CRYPTO_DES
>> +       select CRYPTO_SHA256
>>        help
>>          This is the client VFS module for the Common Internet File System
>>          (CIFS) protocol which is the successor to the Server Message Block
>> diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
>> index e76bfeb..bb7c234 100644
>> --- a/fs/cifs/cifsencrypt.c
>> +++ b/fs/cifs/cifsencrypt.c
>> @@ -774,12 +774,17 @@ calc_seckey(struct cifs_ses *ses)
>>  void
>>  cifs_crypto_shash_release(struct TCP_Server_Info *server)
>>  {
>> +       if (server->secmech.hmacsha256)
>> +               crypto_free_shash(server->secmech.hmacsha256);
>> +
>>        if (server->secmech.md5)
>>                crypto_free_shash(server->secmech.md5);
>>
>>        if (server->secmech.hmacmd5)
>>                crypto_free_shash(server->secmech.hmacmd5);
>>
>> +       kfree(server->secmech.sdeschmacsha256);
>> +
>>        kfree(server->secmech.sdeschmacmd5);
>>
>>        kfree(server->secmech.sdescmd5);
>> @@ -804,6 +809,13 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
>>                goto crypto_allocate_md5_fail;
>>        }
>>
>> +       server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0);
>> +       if (IS_ERR(server->secmech.hmacsha256)) {
>> +               cERROR(1, "could not allocate crypto hmacsha256\n");
>> +               rc = PTR_ERR(server->secmech.hmacsha256);
>> +               goto crypto_allocate_hmacsha256_fail;
>> +       }
>> +
>>        size = sizeof(struct shash_desc) +
>>                        crypto_shash_descsize(server->secmech.hmacmd5);
>>        server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
>> @@ -815,7 +827,6 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
>>        server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5;
>>        server->secmech.sdeschmacmd5->shash.flags = 0x0;
>>
>> -
>>        size = sizeof(struct shash_desc) +
>>                        crypto_shash_descsize(server->secmech.md5);
>>        server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL);
>> @@ -827,12 +838,29 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
>>        server->secmech.sdescmd5->shash.tfm = server->secmech.md5;
>>        server->secmech.sdescmd5->shash.flags = 0x0;
>>
>> +       size = sizeof(struct shash_desc) +
>> +                       crypto_shash_descsize(server->secmech.hmacsha256);
>> +       server->secmech.sdeschmacsha256 = kmalloc(size, GFP_KERNEL);
>> +       if (!server->secmech.sdeschmacsha256) {
>> +               cERROR(1, "%s: Can't alloc hmacsha256\n", __func__);
>> +               rc = -ENOMEM;
>> +               goto crypto_allocate_hmacsha256_sdesc_fail;
>> +       }
>> +       server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256;
>> +       server->secmech.sdeschmacsha256->shash.flags = 0x0;
>> +
>>        return 0;
>>
>> +crypto_allocate_hmacsha256_sdesc_fail:
>> +       kfree(server->secmech.sdescmd5);
>> +
>>  crypto_allocate_md5_sdesc_fail:
>>        kfree(server->secmech.sdeschmacmd5);
>>
>>  crypto_allocate_hmacmd5_sdesc_fail:
>> +       crypto_free_shash(server->secmech.hmacsha256);
>> +
>> +crypto_allocate_hmacsha256_fail:
>>        crypto_free_shash(server->secmech.md5);
>>
>>  crypto_allocate_md5_fail:
>> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
>> index 13d8f4d..4c38b98 100644
>> --- a/fs/cifs/cifsglob.h
>> +++ b/fs/cifs/cifsglob.h
>> @@ -136,8 +136,10 @@ struct sdesc {
>>  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 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 */
>>  };
>>
>>  /* per smb session structure/fields */
>> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
>> index dad29aa..17f5f22 100644
>> --- a/fs/cifs/smb2pdu.c
>> +++ b/fs/cifs/smb2pdu.c
>> @@ -657,6 +657,37 @@ SMB2_negotiate(unsigned int xid, struct cifs_ses *ses)
>>                rc = -EIO;
>>                goto neg_exit;
>>        }
>> +
>> +       if ((sec_flags & CIFSSEC_MAY_SIGN) == 0) {
>> +               /* MUST_SIGN already includes the MAY_SIGN FLAG
>> +                  so if this is zero it means that signing is disabled */
>> +               cFYI(1, "Signing disabled");
>> +               if (ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
>> +                       cERROR(1, "Server requires "
>> +                                  "packet signing to be enabled in "
>> +                                  "/proc/fs/cifs/SecurityFlags.");
>> +                       rc = -EOPNOTSUPP;
>> +               }
>> +               ses->server->sec_mode &=
>> +                       ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
>> +       } else if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
>> +               /* signing required */
>> +               cFYI(1, "Must sign - sec_flags 0x%x", sec_flags);
>> +               if ((ses->server->sec_mode & (SMB2_NEGOTIATE_SIGNING_ENABLED |
>> +                               SMB2_NEGOTIATE_SIGNING_REQUIRED)) == 0) {
>> +                       cERROR(1, "signing required but server lacks support");
>> +                       rc = -EOPNOTSUPP;
>> +               } else
>> +                       ses->server->sec_mode
>> +                               |= SECMODE_SIGN_REQUIRED;
>> +       } else {
>> +               /* signing optional ie CIFSSEC_MAY_SIGN */
>> +               if ((ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED)
>> +                                                                       == 0)
>> +                       ses->server->sec_mode &=
>> +                               ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
>> +       }
>> +
>
> In this case we need to fix the check for in smb2_header_assemble from
> if (tcon->ses->server->sec_mode &
>                     SMB2_NEGOTIATE_SIGNING_REQUIRED)
> to
> if (tcon->ses->server->sec_mode & SECMODE_SIGN_REQUIRED)
>
> to enable signing on the client. Without it we fail if the server
> responses with signing 'enabled' but 'sign' mount option is specified.
>
>>  #ifdef CONFIG_SMB2_ASN1  /* BB REMOVEME when updated asn1.c ready */
>>        rc = decode_neg_token_init(security_blob, blob_length,
>>                                   &ses->server->sec_type);
>> @@ -929,7 +960,6 @@ SMB2_tcon(unsigned int xid, struct cifs_ses *ses,
>>        pSMB2->hdr.smb2_buf_length =
>>                cpu_to_be32(be32_to_cpu(pSMB2->hdr.smb2_buf_length)
>>                            - 1 /* pad */ + unc_path_len);
>> -
>>        rc = smb2_sendrcv2(xid, ses, iov, 2, &resp_buftype /* ret */, &status,
>>                           CIFS_STD_OP | CIFS_LOG_ERROR);
>>        cFYI(1, "tcon buftype %d rc %d status %d", resp_buftype, rc, status);
>> diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
>> index 8dceea0..1d7bafc 100644
>> --- a/fs/cifs/smb2pdu.h
>> +++ b/fs/cifs/smb2pdu.h
>> @@ -1057,4 +1057,8 @@ struct symlink_reparse_data_buf {
>>        char pathbuffer[1];
>>  } __attribute__((packed));
>>
>> +#define SMB2FLG_SECURITY_SIGNATURE (8)
>
> We already have
>
> #define SMB2_FLAGS_SIGNED                cpu_to_le32(0x00000008)
>
>> +#define SMB2_SIGNATURE_SIZE (16)
>> +#define SMB2_NTLMV2_SESSKEY_SIZE (16)
>> +#define SMB2_HMACSHA256_SIZE (32)
>>  #endif                         /* _SMB2PDU_H */
>> diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
>> index bc62e2d..a20bff8 100644
>> --- a/fs/cifs/smb2transport.c
>> +++ b/fs/cifs/smb2transport.c
>> @@ -37,6 +37,169 @@
>>
>>  extern mempool_t *smb2_mid_poolp;
>>
>> +static int smb2_calc_signature(struct smb2_hdr *smb2_pdu,
>> +                       struct TCP_Server_Info *server, char *signature)
>> +{
>> +       int rc;
>> +       unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
>> +       unsigned char *sigptr = smb2_signature;
>> +
>> +       memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
>> +       memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
>> +
>> +       rc = crypto_shash_setkey(server->secmech.hmacsha256,
>> +               server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
>> +       if (rc) {
>> +               cERROR(1, "%s: Could not update with response\n", __func__);
>> +               return rc;
>> +       }
>> +
>> +       rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
>> +       if (rc) {
>> +               cERROR(1, "%s: Could not init md5\n", __func__);
>> +               return rc;
>> +       }
>> +
>> +       rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
>> +                               smb2_pdu->ProtocolId,
>> +                               be32_to_cpu(smb2_pdu->smb2_buf_length));
>> +       if (rc) {
>> +               cERROR(1, "%s: Could not update with payload\n", __func__);
>> +               return rc;
>> +       }
>> +
>> +       rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
>> +                                       sigptr);
>> +       if (rc)
>> +               cERROR(1, "%s: Could not generate sha256 hash\n", __func__);
>> +
>> +       memcpy(smb2_pdu->Signature, sigptr, SMB2_NTLMV2_SESSKEY_SIZE);
>> +
>> +       return rc;
>> +}
>> +
>> +static int smb2_calc_signature2(const struct kvec *iov, int n_vec,
>> +               struct TCP_Server_Info *server, struct smb2_hdr *smb2_pdu)
>> +{
>> +       int i, rc;
>> +       unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
>> +       unsigned char *sigptr = smb2_signature;
>> +
>> +       memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
>> +       memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
>> +
>> +       rc = crypto_shash_setkey(server->secmech.hmacsha256,
>> +               server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
>> +       if (rc) {
>> +               cERROR(1, "%s: Could not update with response\n", __func__);
>> +               return rc;
>> +       }
>> +
>> +       rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
>> +       if (rc) {
>> +               cERROR(1, "%s: Could not init md5\n", __func__);
>> +               return rc;
>> +       }
>> +
>> +       for (i = 0; i < n_vec; i++) {
>> +               if (iov[i].iov_len == 0)
>> +                       continue;
>> +               if (iov[i].iov_base == NULL) {
>> +                       cERROR(1, "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.sdeschmacsha256->shash,
>> +                               iov[i].iov_base + 4, iov[i].iov_len - 4);
>> +               } else {
>> +                       rc =
>> +                       crypto_shash_update(
>> +                               &server->secmech.sdeschmacsha256->shash,
>> +                               iov[i].iov_base, iov[i].iov_len);
>> +               }
>> +               if (rc) {
>> +                       cERROR(1, "%s: Could not update with payload\n",
>> +                                                       __func__);
>> +                       return rc;
>> +               }
>> +       }
>> +
>> +       rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
>> +                                       sigptr);
>> +       if (rc)
>> +               cERROR(1, "%s: Could not generate sha256 hash\n", __func__);
>> +
>> +       memcpy(smb2_pdu->Signature, sigptr, SMB2_NTLMV2_SESSKEY_SIZE);
>> +
>> +       return rc;
>> +}
>> +
>> +/* must be called with server->srv_mutex held */
>> +int smb2_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server)
>> +{
>> +       int rc = 0;
>> +       struct smb2_hdr *smb2_pdu = iov[0].iov_base;
>> +
>> +       if (!(smb2_pdu->Flags & SMB2FLG_SECURITY_SIGNATURE) ||
>> +           server->tcpStatus == CifsNeedNegotiate)
>> +               return rc;
>> +
>> +       if (!server->session_estab) {
>> +               strncpy(smb2_pdu->Signature, "BSRSPYL", 8);
>> +               return rc;
>> +       }
>> +
>> +       rc = smb2_calc_signature2(iov, n_vec, server, smb2_pdu);
>> +
>> +       return rc;
>> +}
>> +
>> +int
>> +smb2_verify_signature(struct smb2_hdr *smb2_pdu, struct TCP_Server_Info *server)
>> +{
>> +       unsigned int rc;
>> +       char server_response_sig[16];
>> +       char what_we_think_sig_should_be[20];
>> +
>> +       if ((smb2_pdu->Command == SMB2_NEGOTIATE) ||
>> +                       (smb2_pdu->Command == SMB2_OPLOCK_BREAK) ||
>> +                       (!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(smb2_pdu->Signature, "BSRSPYL ", 8) == 0)
>> +               cFYI(1, "dummy signature received for smb command 0x%x",
>> +                       smb2_pdu->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, smb2_pdu->Signature, 16);
>> +
>> +       memset(smb2_pdu->Signature, 0, 16);
>> +
>> +       rc = smb2_calc_signature(smb2_pdu, server, what_we_think_sig_should_be);
>> +
>> +       if (rc)
>> +               return rc;
>> +
>> +/*     smb2_dump_mem("what we think it should be: ",
>> +                     what_we_think_sig_should_be, 16); */
>> +
>> +       if (memcmp(server_response_sig, what_we_think_sig_should_be, 8))
>> +               return -EACCES;
>> +       else
>> +               return 0;
>> +
>> +}
>>  /*
>>  * Set message id for the request. Should be called after wait_for_free_response
>>  * and locking srv_mutex. iov array must have at least 1 element.
>> @@ -292,18 +455,16 @@ int
>>  smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
>>                   unsigned int receive_len, bool log_error)
>>  {
>> +       int rc = 0;
>>        struct smb2_hdr *buf = (struct smb2_hdr *)mid->resp_buf;
>>
>>        dump_smb2(buf, 80);
>>        /* convert the length into a more usable form */
>>        if ((receive_len > 24) &&
>>            (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) {
>> -               /* BB fixme */
>> -               /*rc = smb2_verify_signature(mid->resp_buf,
>> -                                       &ses->server->mac_signing_key);
>> -               if (rc) {
>> +               rc = smb2_verify_signature(mid->resp_buf, server);
>> +               if (rc)
>>                        cERROR(1, "Unexpected SMB signature");
>> -               } */
>>        }
>>
>>        return map_smb2_to_linux_error(buf, log_error);
>> @@ -371,12 +532,12 @@ smb2_sendrcv2(const unsigned int xid, struct cifs_ses *ses,
>>                wake_up(&ses->server->request_q);
>>                return rc;
>>        }
>> -       /* rc = sign_smb2(iov, n_vec, ses->server); BB
>> +       rc = smb2_sign_smb2(iov, n_vec, ses->server);
>>        if (rc) {
>>                mutex_unlock(&ses->server->srv_mutex);
>>                cifs_small_buf_release(buf);
>>                goto out;
>> -       } */
>> +       }
>
> It seems we also need this for smb2_call_async too.
>
>>
>>        midQ->mid_state = MID_REQUEST_SUBMITTED;
>>        cifs_in_send_inc(ses->server);
>> --
>> 1.6.0.2
>>
>>
>
>
>
> --
> Best regards,
> Pavel Shilovsky.
>


OK. Understood. Thanks.

Regards,

Shirish
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index f66cc16..ed5452e 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -9,6 +9,7 @@  config CIFS
 	select CRYPTO_ARC4
 	select CRYPTO_ECB
 	select CRYPTO_DES
+	select CRYPTO_SHA256
 	help
 	  This is the client VFS module for the Common Internet File System
 	  (CIFS) protocol which is the successor to the Server Message Block
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index e76bfeb..bb7c234 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -774,12 +774,17 @@  calc_seckey(struct cifs_ses *ses)
 void
 cifs_crypto_shash_release(struct TCP_Server_Info *server)
 {
+	if (server->secmech.hmacsha256)
+		crypto_free_shash(server->secmech.hmacsha256);
+
 	if (server->secmech.md5)
 		crypto_free_shash(server->secmech.md5);
 
 	if (server->secmech.hmacmd5)
 		crypto_free_shash(server->secmech.hmacmd5);
 
+	kfree(server->secmech.sdeschmacsha256);
+
 	kfree(server->secmech.sdeschmacmd5);
 
 	kfree(server->secmech.sdescmd5);
@@ -804,6 +809,13 @@  cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
 		goto crypto_allocate_md5_fail;
 	}
 
+	server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0);
+	if (IS_ERR(server->secmech.hmacsha256)) {
+		cERROR(1, "could not allocate crypto hmacsha256\n");
+		rc = PTR_ERR(server->secmech.hmacsha256);
+		goto crypto_allocate_hmacsha256_fail;
+	}
+
 	size = sizeof(struct shash_desc) +
 			crypto_shash_descsize(server->secmech.hmacmd5);
 	server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
@@ -815,7 +827,6 @@  cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
 	server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5;
 	server->secmech.sdeschmacmd5->shash.flags = 0x0;
 
-
 	size = sizeof(struct shash_desc) +
 			crypto_shash_descsize(server->secmech.md5);
 	server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL);
@@ -827,12 +838,29 @@  cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
 	server->secmech.sdescmd5->shash.tfm = server->secmech.md5;
 	server->secmech.sdescmd5->shash.flags = 0x0;
 
+	size = sizeof(struct shash_desc) +
+			crypto_shash_descsize(server->secmech.hmacsha256);
+	server->secmech.sdeschmacsha256 = kmalloc(size, GFP_KERNEL);
+	if (!server->secmech.sdeschmacsha256) {
+		cERROR(1, "%s: Can't alloc hmacsha256\n", __func__);
+		rc = -ENOMEM;
+		goto crypto_allocate_hmacsha256_sdesc_fail;
+	}
+	server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256;
+	server->secmech.sdeschmacsha256->shash.flags = 0x0;
+
 	return 0;
 
+crypto_allocate_hmacsha256_sdesc_fail:
+	kfree(server->secmech.sdescmd5);
+
 crypto_allocate_md5_sdesc_fail:
 	kfree(server->secmech.sdeschmacmd5);
 
 crypto_allocate_hmacmd5_sdesc_fail:
+	crypto_free_shash(server->secmech.hmacsha256);
+
+crypto_allocate_hmacsha256_fail:
 	crypto_free_shash(server->secmech.md5);
 
 crypto_allocate_md5_fail:
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 13d8f4d..4c38b98 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -136,8 +136,10 @@  struct sdesc {
 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 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 */
 };
 
 /* per smb session structure/fields */
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index dad29aa..17f5f22 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -657,6 +657,37 @@  SMB2_negotiate(unsigned int xid, struct cifs_ses *ses)
 		rc = -EIO;
 		goto neg_exit;
 	}
+
+	if ((sec_flags & CIFSSEC_MAY_SIGN) == 0) {
+		/* MUST_SIGN already includes the MAY_SIGN FLAG
+		   so if this is zero it means that signing is disabled */
+		cFYI(1, "Signing disabled");
+		if (ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+			cERROR(1, "Server requires "
+				   "packet signing to be enabled in "
+				   "/proc/fs/cifs/SecurityFlags.");
+			rc = -EOPNOTSUPP;
+		}
+		ses->server->sec_mode &=
+			~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
+	} else if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
+		/* signing required */
+		cFYI(1, "Must sign - sec_flags 0x%x", sec_flags);
+		if ((ses->server->sec_mode & (SMB2_NEGOTIATE_SIGNING_ENABLED |
+				SMB2_NEGOTIATE_SIGNING_REQUIRED)) == 0) {
+			cERROR(1, "signing required but server lacks support");
+			rc = -EOPNOTSUPP;
+		} else
+			ses->server->sec_mode
+				|= SECMODE_SIGN_REQUIRED;
+	} else {
+		/* signing optional ie CIFSSEC_MAY_SIGN */
+		if ((ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED)
+									== 0)
+			ses->server->sec_mode &=
+				~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
+	}
+
 #ifdef CONFIG_SMB2_ASN1  /* BB REMOVEME when updated asn1.c ready */
 	rc = decode_neg_token_init(security_blob, blob_length,
 				   &ses->server->sec_type);
@@ -929,7 +960,6 @@  SMB2_tcon(unsigned int xid, struct cifs_ses *ses,
 	pSMB2->hdr.smb2_buf_length =
 		cpu_to_be32(be32_to_cpu(pSMB2->hdr.smb2_buf_length)
 			    - 1 /* pad */ + unc_path_len);
-
 	rc = smb2_sendrcv2(xid, ses, iov, 2, &resp_buftype /* ret */, &status,
 			   CIFS_STD_OP | CIFS_LOG_ERROR);
 	cFYI(1, "tcon buftype %d rc %d status %d", resp_buftype, rc, status);
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 8dceea0..1d7bafc 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -1057,4 +1057,8 @@  struct symlink_reparse_data_buf {
 	char pathbuffer[1];
 } __attribute__((packed));
 
+#define SMB2FLG_SECURITY_SIGNATURE (8)
+#define SMB2_SIGNATURE_SIZE (16)
+#define SMB2_NTLMV2_SESSKEY_SIZE (16)
+#define SMB2_HMACSHA256_SIZE (32)
 #endif				/* _SMB2PDU_H */
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
index bc62e2d..a20bff8 100644
--- a/fs/cifs/smb2transport.c
+++ b/fs/cifs/smb2transport.c
@@ -37,6 +37,169 @@ 
 
 extern mempool_t *smb2_mid_poolp;
 
+static int smb2_calc_signature(struct smb2_hdr *smb2_pdu,
+			struct TCP_Server_Info *server, char *signature)
+{
+	int rc;
+	unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
+	unsigned char *sigptr = smb2_signature;
+
+	memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
+	memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
+
+	rc = crypto_shash_setkey(server->secmech.hmacsha256,
+		server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
+	if (rc) {
+		cERROR(1, "%s: Could not update with response\n", __func__);
+		return rc;
+	}
+
+	rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
+	if (rc) {
+		cERROR(1, "%s: Could not init md5\n", __func__);
+		return rc;
+	}
+
+	rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
+				smb2_pdu->ProtocolId,
+				be32_to_cpu(smb2_pdu->smb2_buf_length));
+	if (rc) {
+		cERROR(1, "%s: Could not update with payload\n", __func__);
+		return rc;
+	}
+
+	rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
+					sigptr);
+	if (rc)
+		cERROR(1, "%s: Could not generate sha256 hash\n", __func__);
+
+	memcpy(smb2_pdu->Signature, sigptr, SMB2_NTLMV2_SESSKEY_SIZE);
+
+	return rc;
+}
+
+static int smb2_calc_signature2(const struct kvec *iov, int n_vec,
+		struct TCP_Server_Info *server, struct smb2_hdr *smb2_pdu)
+{
+	int i, rc;
+	unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
+	unsigned char *sigptr = smb2_signature;
+
+	memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
+	memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
+
+	rc = crypto_shash_setkey(server->secmech.hmacsha256,
+		server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
+	if (rc) {
+		cERROR(1, "%s: Could not update with response\n", __func__);
+		return rc;
+	}
+
+	rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
+	if (rc) {
+		cERROR(1, "%s: Could not init md5\n", __func__);
+		return rc;
+	}
+
+	for (i = 0; i < n_vec; i++) {
+		if (iov[i].iov_len == 0)
+			continue;
+		if (iov[i].iov_base == NULL) {
+			cERROR(1, "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.sdeschmacsha256->shash,
+				iov[i].iov_base + 4, iov[i].iov_len - 4);
+		} else {
+			rc =
+			crypto_shash_update(
+				&server->secmech.sdeschmacsha256->shash,
+				iov[i].iov_base, iov[i].iov_len);
+		}
+		if (rc) {
+			cERROR(1, "%s: Could not update with payload\n",
+							__func__);
+			return rc;
+		}
+	}
+
+	rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
+					sigptr);
+	if (rc)
+		cERROR(1, "%s: Could not generate sha256 hash\n", __func__);
+
+	memcpy(smb2_pdu->Signature, sigptr, SMB2_NTLMV2_SESSKEY_SIZE);
+
+	return rc;
+}
+
+/* must be called with server->srv_mutex held */
+int smb2_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server)
+{
+	int rc = 0;
+	struct smb2_hdr *smb2_pdu = iov[0].iov_base;
+
+	if (!(smb2_pdu->Flags & SMB2FLG_SECURITY_SIGNATURE) ||
+	    server->tcpStatus == CifsNeedNegotiate)
+		return rc;
+
+	if (!server->session_estab) {
+		strncpy(smb2_pdu->Signature, "BSRSPYL", 8);
+		return rc;
+	}
+
+	rc = smb2_calc_signature2(iov, n_vec, server, smb2_pdu);
+
+	return rc;
+}
+
+int
+smb2_verify_signature(struct smb2_hdr *smb2_pdu, struct TCP_Server_Info *server)
+{
+	unsigned int rc;
+	char server_response_sig[16];
+	char what_we_think_sig_should_be[20];
+
+	if ((smb2_pdu->Command == SMB2_NEGOTIATE) ||
+			(smb2_pdu->Command == SMB2_OPLOCK_BREAK) ||
+			(!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(smb2_pdu->Signature, "BSRSPYL ", 8) == 0)
+		cFYI(1, "dummy signature received for smb command 0x%x",
+			smb2_pdu->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, smb2_pdu->Signature, 16);
+
+	memset(smb2_pdu->Signature, 0, 16);
+
+	rc = smb2_calc_signature(smb2_pdu, server, what_we_think_sig_should_be);
+
+	if (rc)
+		return rc;
+
+/*	smb2_dump_mem("what we think it should be: ",
+		      what_we_think_sig_should_be, 16); */
+
+	if (memcmp(server_response_sig, what_we_think_sig_should_be, 8))
+		return -EACCES;
+	else
+		return 0;
+
+}
 /*
  * Set message id for the request. Should be called after wait_for_free_response
  * and locking srv_mutex. iov array must have at least 1 element.
@@ -292,18 +455,16 @@  int
 smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
 		   unsigned int receive_len, bool log_error)
 {
+	int rc = 0;
 	struct smb2_hdr *buf = (struct smb2_hdr *)mid->resp_buf;
 
 	dump_smb2(buf, 80);
 	/* convert the length into a more usable form */
 	if ((receive_len > 24) &&
 	    (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) {
-		/* BB fixme */
-		/*rc = smb2_verify_signature(mid->resp_buf,
-					&ses->server->mac_signing_key);
-		if (rc) {
+		rc = smb2_verify_signature(mid->resp_buf, server);
+		if (rc)
 			cERROR(1, "Unexpected SMB signature");
-		} */
 	}
 
 	return map_smb2_to_linux_error(buf, log_error);
@@ -371,12 +532,12 @@  smb2_sendrcv2(const unsigned int xid, struct cifs_ses *ses,
 		wake_up(&ses->server->request_q);
 		return rc;
 	}
-	/* rc = sign_smb2(iov, n_vec, ses->server); BB
+	rc = smb2_sign_smb2(iov, n_vec, ses->server);
 	if (rc) {
 		mutex_unlock(&ses->server->srv_mutex);
 		cifs_small_buf_release(buf);
 		goto out;
-	} */
+	}
 
 	midQ->mid_state = MID_REQUEST_SUBMITTED;
 	cifs_in_send_inc(ses->server);