summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/cifsglob.h1
-rw-r--r--fs/cifs/smb2pdu.c102
-rw-r--r--fs/cifs/smb2pdu.h3
3 files changed, 106 insertions, 0 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 2282562e78a1..56fee0330afc 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -676,6 +676,7 @@ struct TCP_Server_Info {
unsigned int max_read;
unsigned int max_write;
#ifdef CONFIG_CIFS_SMB311
+ __le16 cipher_type;
/* save initital negprot hash */
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
#endif /* 3.1.1 */
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 6c2910e6c984..e01be06d588e 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -406,6 +406,99 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
*total_len += 4 + sizeof(struct smb2_preauth_neg_context)
+ sizeof(struct smb2_encryption_neg_context);
}
+
+static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt)
+{
+ unsigned int len = le16_to_cpu(ctxt->DataLength);
+
+ /* If invalid preauth context warn but use what we requested, SHA-512 */
+ if (len < MIN_PREAUTH_CTXT_DATA_LEN) {
+ printk_once(KERN_WARNING "server sent bad preauth context\n");
+ return;
+ }
+ if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1)
+ printk_once(KERN_WARNING "illegal SMB3 hash algorithm count\n");
+ if (ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512)
+ printk_once(KERN_WARNING "unknown SMB3 hash algorithm\n");
+}
+
+static int decode_encrypt_ctx(struct TCP_Server_Info *server,
+ struct smb2_encryption_neg_context *ctxt)
+{
+ unsigned int len = le16_to_cpu(ctxt->DataLength);
+
+ cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len);
+ if (len < MIN_ENCRYPT_CTXT_DATA_LEN) {
+ printk_once(KERN_WARNING "server sent bad crypto ctxt len\n");
+ return -EINVAL;
+ }
+
+ if (le16_to_cpu(ctxt->CipherCount) != 1) {
+ printk_once(KERN_WARNING "illegal SMB3.11 cipher count\n");
+ return -EINVAL;
+ }
+ cifs_dbg(FYI, "SMB311 cipher type:%d\n", le16_to_cpu(ctxt->Ciphers[0]));
+ if ((ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_CCM) &&
+ (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_GCM)) {
+ printk_once(KERN_WARNING "invalid SMB3.11 cipher returned\n");
+ return -EINVAL;
+ }
+ server->cipher_type = ctxt->Ciphers[0];
+ return 0;
+}
+
+static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp,
+ struct TCP_Server_Info *server)
+{
+ struct smb2_neg_context *pctx;
+ unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset);
+ unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount);
+ unsigned int len_of_smb = be32_to_cpu(rsp->hdr.smb2_buf_length);
+ unsigned int len_of_ctxts, i;
+ int rc = 0;
+
+ cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt);
+ if (len_of_smb <= offset) {
+ cifs_dbg(VFS, "Invalid response: negotiate context offset\n");
+ return -EINVAL;
+ }
+
+ len_of_ctxts = len_of_smb - offset;
+
+ for (i = 0; i < ctxt_cnt; i++) {
+ int clen;
+ /* check that offset is not beyond end of SMB */
+ if (len_of_ctxts == 0)
+ break;
+
+ if (len_of_ctxts < sizeof(struct smb2_neg_context))
+ break;
+
+ pctx = (struct smb2_neg_context *)(offset + 4 + (char *)rsp);
+ clen = le16_to_cpu(pctx->DataLength);
+ if (clen > len_of_ctxts)
+ break;
+
+ if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES)
+ decode_preauth_context(
+ (struct smb2_preauth_neg_context *)pctx);
+ else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES)
+ rc = decode_encrypt_ctx(server,
+ (struct smb2_encryption_neg_context *)pctx);
+ else
+ cifs_dbg(VFS, "unknown negcontext of type %d ignored\n",
+ le16_to_cpu(pctx->ContextType));
+
+ if (rc)
+ break;
+ /* offsets must be 8 byte aligned */
+ clen = (clen + 7) & ~0x7;
+ offset += clen + sizeof(struct smb2_neg_context);
+ len_of_ctxts -= clen;
+ }
+ return rc;
+}
+
#else
static void assemble_neg_contexts(struct smb2_negotiate_req *req,
unsigned int *total_len)
@@ -619,6 +712,15 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
else if (rc == 0)
rc = -EIO;
}
+
+#ifdef CONFIG_CIFS_SMB311
+ if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
+ if (rsp->NegotiateContextCount)
+ rc = smb311_decode_neg_context(rsp, server);
+ else
+ cifs_dbg(VFS, "Missing expected negotiate contexts\n");
+ }
+#endif /* CONFIG_CIFS_SMB311 */
neg_exit:
free_rsp_buf(resp_buftype, rsp);
return rc;
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 0e0a0af89e4d..6093e5142b2b 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -275,6 +275,7 @@ struct smb2_neg_context {
#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001)
#define SMB2_PREAUTH_HASH_SIZE 64
+#define MIN_PREAUTH_CTXT_DATA_LEN (SMB311_SALT_SIZE + 6)
struct smb2_preauth_neg_context {
__le16 ContextType; /* 1 */
__le16 DataLength;
@@ -289,6 +290,8 @@ struct smb2_preauth_neg_context {
#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001)
#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002)
+/* Min encrypt context data is one cipher so 2 bytes + 2 byte count field */
+#define MIN_ENCRYPT_CTXT_DATA_LEN 4
struct smb2_encryption_neg_context {
__le16 ContextType; /* 2 */
__le16 DataLength;