Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 326271
b: refs/heads/master
c: 3c1bf7e
h: refs/heads/master
i:
  326269: d28d2b3
  326267: 081826c
  326263: a5882c7
  326255: 57b97fe
  326239: 69149e6
  326207: 3e79982
  326143: 789f872
v: v3
  • Loading branch information
Pavel Shilovsky authored and Steve French committed Sep 25, 2012
1 parent afea37a commit 2f5d709
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 29 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 009d344398bb3e844b31eb9e6a7860748c6f6dd3
refs/heads/master: 3c1bf7e48e9e463b65b1b90da4500a93dd2b27a7
1 change: 1 addition & 0 deletions trunk/fs/cifs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 29 additions & 1 deletion trunk/fs/cifs/cifsencrypt.c
Original file line number Diff line number Diff line change
Expand Up @@ -686,12 +686,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);
Expand All @@ -716,6 +721,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);
Expand All @@ -727,7 +739,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);
Expand All @@ -739,12 +750,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:
Expand Down
2 changes: 2 additions & 0 deletions trunk/fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,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 */
Expand Down
1 change: 1 addition & 0 deletions trunk/fs/cifs/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ extern char *cifs_compose_mount_options(const char *sb_mountdata,
extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer,
struct TCP_Server_Info *server);
extern void DeleteMidQEntry(struct mid_q_entry *midEntry);
extern void cifs_delete_mid(struct mid_q_entry *mid);
extern void cifs_wake_up_task(struct mid_q_entry *mid);
extern int cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
unsigned int nvec, mid_receive_t *receive,
Expand Down
4 changes: 4 additions & 0 deletions trunk/fs/cifs/smb2glob.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@
#define END_OF_CHAIN 4
#define RELATED_REQUEST 8

#define SMB2_SIGNATURE_SIZE (16)
#define SMB2_NTLMV2_SESSKEY_SIZE (16)
#define SMB2_HMACSHA256_SIZE (32)

#endif /* _SMB2_GLOB_H */
52 changes: 46 additions & 6 deletions trunk/fs/cifs/smb2pdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
/* BB how does SMB2 do case sensitive? */
/* if (tcon->nocase)
hdr->Flags |= SMBFLG_CASELESS; */
/* if (tcon->ses && tcon->ses->server &&
if (tcon->ses && tcon->ses->server &&
(tcon->ses->server->sec_mode & SECMODE_SIGN_REQUIRED))
hdr->Flags |= SMB2_FLAGS_SIGNED; */
hdr->Flags |= SMB2_FLAGS_SIGNED;
out:
pdu->StructureSize2 = cpu_to_le16(parmsize);
return;
Expand Down Expand Up @@ -441,6 +441,38 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
rc = -EIO;
goto neg_exit;
}

cFYI(1, "sec_flags 0x%x", sec_flags);
if (sec_flags & CIFSSEC_MUST_SIGN) {
cFYI(1, "Signing required");
if (!(server->sec_mode & (SMB2_NEGOTIATE_SIGNING_REQUIRED |
SMB2_NEGOTIATE_SIGNING_ENABLED))) {
cERROR(1, "signing required but server lacks support");
rc = -EOPNOTSUPP;
goto neg_exit;
}
server->sec_mode |= SECMODE_SIGN_REQUIRED;
} else if (sec_flags & CIFSSEC_MAY_SIGN) {
cFYI(1, "Signing optional");
if (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
cFYI(1, "Server requires signing");
server->sec_mode |= SECMODE_SIGN_REQUIRED;
} else {
server->sec_mode &=
~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
}
} else {
cFYI(1, "Signing disabled");
if (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
cERROR(1, "Server requires packet signing to be enabled"
" in /proc/fs/cifs/SecurityFlags.");
rc = -EOPNOTSUPP;
goto neg_exit;
}
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,
&server->sec_type);
Expand Down Expand Up @@ -669,6 +701,8 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)

/* since no tcon, smb2_init can not do this, so do here */
req->hdr.SessionId = ses->Suid;
if (server->sec_mode & SECMODE_SIGN_REQUIRED)
req->hdr.Flags |= SMB2_FLAGS_SIGNED;

rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0);
/*
Expand Down Expand Up @@ -1268,10 +1302,16 @@ smb2_readv_callback(struct mid_q_entry *mid)
case MID_RESPONSE_RECEIVED:
credits_received = le16_to_cpu(buf->CreditRequest);
/* result already set, check signature */
/* if (server->sec_mode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
if (smb2_verify_signature(mid->resp_buf, server))
cERROR(1, "Unexpected SMB signature"); */
if (server->sec_mode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
int rc;

rc = smb2_verify_signature2(rdata->iov, rdata->nr_iov,
server);
if (rc)
cERROR(1, "SMB signature verification returned "
"error = %d", rc);
}
/* FIXME: should this be counted toward the initiating task? */
task_io_account_read(rdata->bytes);
cifs_stats_bytes_read(tcon, rdata->bytes);
Expand Down
2 changes: 2 additions & 0 deletions trunk/fs/cifs/smb2proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr);
extern __le16 *cifs_convert_path_to_utf16(const char *from,
struct cifs_sb_info *cifs_sb);

extern int smb2_verify_signature2(struct kvec *, unsigned int,
struct TCP_Server_Info *);
extern int smb2_check_receive(struct mid_q_entry *mid,
struct TCP_Server_Info *server, bool log_error);
extern int smb2_setup_request(struct cifs_ses *ses, struct kvec *iov,
Expand Down
165 changes: 156 additions & 9 deletions trunk/fs/cifs/smb2transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,149 @@
#include "smb2proto.h"
#include "cifs_debug.h"
#include "smb2status.h"
#include "smb2glob.h"

static int
smb2_calc_signature2(const struct kvec *iov, int n_vec,
struct TCP_Server_Info *server)
{
int i, rc;
unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
unsigned char *sigptr = smb2_signature;
struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;

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_SIGNATURE_SIZE);

return rc;
}

/* must be called with server->srv_mutex held */
static 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 & SMB2_FLAGS_SIGNED) ||
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);

return rc;
}

int
smb2_verify_signature2(struct kvec *iov, unsigned int n_vec,
struct TCP_Server_Info *server)
{
unsigned int rc;
char server_response_sig[16];
struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;

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, SMB2_SIGNATURE_SIZE);

memset(smb2_pdu->Signature, 0, SMB2_SIGNATURE_SIZE);

mutex_lock(&server->srv_mutex);
rc = smb2_calc_signature2(iov, n_vec, server);
mutex_unlock(&server->srv_mutex);

if (rc)
return rc;

if (memcmp(server_response_sig, smb2_pdu->Signature,
SMB2_SIGNATURE_SIZE))
return -EACCES;
else
return 0;
}

static int
smb2_verify_signature(struct smb2_hdr *smb2_pdu, struct TCP_Server_Info *server)
{
struct kvec iov;

iov.iov_base = (char *)smb2_pdu;
iov.iov_len = get_rfc1002_length(smb2_pdu) + 4;
return smb2_verify_signature2(&iov, 1, server);
}

/*
* Set message id for the request. Should be called after wait_for_free_request
Expand Down Expand Up @@ -118,12 +261,15 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,

dump_smb(mid->resp_buf, min_t(u32, 80, len));
/* convert the length into a more usable form */
/* BB - uncomment with SMB2 signing implementation */
/* if ((len > 24) &&
if ((len > 24) &&
(server->sec_mode & (SECMODE_SIGN_REQUIRED|SECMODE_SIGN_ENABLED))) {
if (smb2_verify_signature(mid->resp_buf, server))
cERROR(1, "Unexpected SMB signature");
} */
int rc;

rc = smb2_verify_signature(mid->resp_buf, server);
if (rc)
cERROR(1, "SMB signature verification returned error = "
"%d", rc);
}

return map_smb2_to_linux_error(mid->resp_buf, log_error);
}
Expand All @@ -141,9 +287,9 @@ smb2_setup_request(struct cifs_ses *ses, struct kvec *iov,
rc = smb2_get_mid_entry(ses, hdr, &mid);
if (rc)
return rc;
/* rc = smb2_sign_smb2(iov, nvec, ses->server);
rc = smb2_sign_smb2(iov, nvec, ses->server);
if (rc)
delete_mid(mid); */
cifs_delete_mid(mid);
*ret_mid = mid;
return rc;
}
Expand All @@ -162,11 +308,12 @@ smb2_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov,
if (mid == NULL)
return -ENOMEM;

/* rc = smb2_sign_smb2(iov, nvec, server);
rc = smb2_sign_smb2(iov, nvec, server);
if (rc) {
DeleteMidQEntry(mid);
return rc;
}*/
}

*ret_mid = mid;
return rc;
}
Loading

0 comments on commit 2f5d709

Please sign in to comment.