SMB311: Improve checking of negotiate security contexts
authorSteve French <smfrench@gmail.com>
Mon, 9 Apr 2018 15:47:14 +0000 (10:47 -0500)
committerSteve French <smfrench@gmail.com>
Thu, 12 Apr 2018 21:54:06 +0000 (16:54 -0500)
SMB3.11 crypto and hash contexts were not being checked strictly enough.
Add parsing and validity checking for the security contexts in the SMB3.11
negotiate response.

Signed-off-by: Steve French <smfrench@gmail.com>
CC: Stable <stable@vger.kernel.org>
Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
fs/cifs/cifsglob.h
fs/cifs/smb2pdu.c
fs/cifs/smb2pdu.h

index 2282562e78a1fe5313faf3948e6cf2635926ab9f..56fee0330afc95656493df047649ea6dd51f2481 100644 (file)
@@ -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 */
index 6c2910e6c984ebd4ccf707a87d3b86ae51a32d47..e01be06d588ee1148109904cf057109bbddb6104 100644 (file)
@@ -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;
index 0e0a0af89e4d09a71a7ea9e2097ccd3862350bb6..6093e5142b2bc3792cf40afb7f7abbc02b60bbb4 100644 (file)
@@ -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;