Print this page
12513 SMB 3.1.1 support for server

@@ -9,19 +9,20 @@
  * http://www.illumos.org/license/CDDL.
  */
 
 /*
  * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
- * Copyright 2019 RackTop Systems.
+ * Copyright 2020 RackTop Systems, Inc.
  */
 
 /*
  * Dispatch function for SMB2_NEGOTIATE
  */
 
 #include <smbsrv/smb2_kproto.h>
 #include <smbsrv/smb2.h>
+#include <sys/random.h>
 
 /*
  * Note from [MS-SMB2] Sec. 2.2.3:  Windows servers return
  * invalid parameter if the dialect count is greater than 64
  * This is here (and not in smb2.h) because this is technically

@@ -83,10 +84,11 @@
 static uint16_t smb2_versions[] = {
         0x202,  /* SMB 2.002 */
         0x210,  /* SMB 2.1 */
         0x300,  /* SMB 3.0 */
         0x302,  /* SMB 3.02 */
+        0x311,  /* SMB 3.11 */
 };
 static uint16_t smb2_nversions =
     sizeof (smb2_versions) / sizeof (smb2_versions[0]);
 
 static boolean_t

@@ -208,10 +210,279 @@
  * The caller always frees this request.
  *
  * Return value is 0 for success, and anything else will
  * terminate the reader thread (drop the connection).
  */
+typedef struct smb31_preauth_integrity_caps_ctx {
+        uint16_t        hash_count;
+        uint16_t        salt_len;
+        char            *salt;
+} smb31_preauth_integrity_caps_ctx_t;
+
+typedef struct smb31_encryption_caps_ctx {
+        uint16_t        cipher_count;
+} smb31_encrypt_caps_ctx_t;
+
+enum smb3_neg_ctx_type {
+        SMB31_PREAUTH_INTEGRITY_CAPS = 1,
+        SMB31_ENCRYPTION_CAPS = 2
+};
+
+typedef struct smb3_negotiate_context {
+        uint16_t        type;
+        uint16_t        datalen;
+        uint32_t        _resrvd;
+        char            data[];
+} smb3_neg_ctx_t;
+
+typedef struct smb3_negotiate_context_info {
+        uint32_t        offset;
+        uint16_t        count;
+        uint16_t        _resrvd;
+} smb3_neg_ctx_info_t;
+
+#define NEG_CTX_INFO_OFFSET     (SMB2_HDR_SIZE + 28)
+#define STATUS_SMB_NO_PREAUTH_INEGRITY_HASH_OVERLAP     (0xC05D0000)
+
+#define STATUS_PREAUTH_HASH_OVERLAP \
+    STATUS_SMB_NO_PREAUTH_INEGRITY_HASH_OVERLAP
+
+/*
+ * This function should be called only for dialect >= 0x311
+ * Negotiate context list should contain exactly one
+ * smb31_preauth_INTEGRITY_CAPS context.
+ * Otherwise STATUS_INVALID_PARAMETER.
+ * It should contain at least 1 hash algorith what server does support.
+ * Otehrwise STATUS_SMB_NO_PREAUTH_INEGRITY_HASH_OVERLAP.
+ */
+static uint32_t
+smb31_decode_neg_ctxs(smb_request_t *sr, smb_session_t *s)
+{
+        uint32_t status = 0;
+        int found_preauth_ctx = 0;
+        boolean_t preauth_sha512_enabled = B_FALSE;
+        boolean_t encrypt_ccm_enabled = B_FALSE;
+        boolean_t encrypt_gcm_enabled = B_FALSE;
+        uint16_t cipher = sr->sr_server->sv_cfg.skc_encrypt_cipher;
+        smb3_neg_ctx_info_t     neg_ctx_info;
+        int cnt, i;
+        int rc;
+
+        sr->command.chain_offset = NEG_CTX_INFO_OFFSET;
+
+        rc = smb_mbc_decodef(&sr->command, "lw2.",
+            &neg_ctx_info.offset,       /* l */
+            &neg_ctx_info.count);       /* w */
+        if (rc != 0) {
+                status = NT_STATUS_INVALID_PARAMETER;
+                goto errout;
+        }
+
+        /*
+         * There should be exactly 1 SMB31_PREAUTH_INTEGRITY_CAPS negotiate ctx.
+         * SMB31_ENCRYPTION_CAPS is optional one.
+         */
+        cnt = neg_ctx_info.count;
+        if (cnt < 1) {
+                status = NT_STATUS_INVALID_PARAMETER;
+                goto errout;
+        }
+
+        sr->command.chain_offset = neg_ctx_info.offset;
+
+        /*
+         * Parse negotiate contexts.
+         */
+        for (i = 0; i < cnt; i++) {
+                smb3_neg_ctx_t neg_ctx;
+
+                sr->command.chain_offset =
+                    P2ROUNDUP(sr->command.chain_offset, 8);
+
+                rc = smb_mbc_decodef(
+                    &sr->command, "ww4.",
+                    &neg_ctx.type,      /* w */
+                    &neg_ctx.datalen);  /* w */
+                if (rc != 0) {
+                        status = NT_STATUS_INVALID_PARAMETER;
+                        goto errout;
+                }
+
+                if (neg_ctx.type == SMB31_PREAUTH_INTEGRITY_CAPS) {
+                        smb31_preauth_integrity_caps_ctx_t pic_ctx;
+
+                        if (found_preauth_ctx++ != 0) {
+                                status = NT_STATUS_INVALID_PARAMETER;
+                                goto errout;
+                        }
+
+                        rc = smb_mbc_decodef(
+                            &sr->command, "ww",
+                            &pic_ctx.hash_count,        /* w */
+                            &pic_ctx.salt_len);         /* w */
+                        if (rc != 0) {
+                                status = NT_STATUS_INVALID_PARAMETER;
+                                goto errout;
+                        }
+
+                        /*
+                         * In SMB 0x311 there should be exactly 1 preauth
+                         * negotiate context, and there should be exactly 1
+                         * hash value in the list - SHA512.
+                         */
+                        if (pic_ctx.hash_count != 1) {
+                                status = NT_STATUS_INVALID_PARAMETER;
+                                goto errout;
+                        }
+
+                        for (int k = 0; k < pic_ctx.hash_count; k++) {
+                                uint16_t hash_id;
+
+                                rc = smb_mbc_decodef(
+                                    &sr->command, "w",
+                                    &hash_id);    /* w */
+
+                                switch (hash_id) {
+                                case SMB3_HASH_SHA512:
+                                        preauth_sha512_enabled = B_TRUE;
+                                        break;
+                                default:
+                                        ;
+                                }
+                        }
+
+                        rc = smb_mbc_decodef(
+                            &sr->command, "#.",
+                            pic_ctx.salt_len);
+
+                } else if (neg_ctx.type == SMB31_ENCRYPTION_CAPS) {
+                        smb31_encrypt_caps_ctx_t enc_ctx;
+
+                        rc = smb_mbc_decodef(
+                            &sr->command, "w",
+                            &enc_ctx.cipher_count);     /* w */
+                        if (rc != 0) {
+                                status = NT_STATUS_INVALID_PARAMETER;
+                                goto errout;
+                        }
+
+                        for (int k = 0; k < enc_ctx.cipher_count; k++) {
+                                uint16_t cipher_id;
+
+                                rc = smb_mbc_decodef(
+                                    &sr->command, "w",
+                                    &cipher_id);        /* w */
+
+                                switch (cipher_id) {
+                                case SMB3_CIPHER_AES128_CCM:
+                                        encrypt_ccm_enabled = B_TRUE;
+                                        break;
+                                case SMB3_CIPHER_AES128_GCM:
+                                        encrypt_gcm_enabled = B_TRUE;
+                                        break;
+                                default:
+                                        ;
+                                }
+                        }
+                } else {
+                        /* Skip unsupported context */
+                        ASSERT0(smb_mbc_decodef(
+                            &sr->command, "#.",
+                            neg_ctx.datalen));
+                }
+        }
+
+        /* Not found mandatory SMB31_PREAUTH_INTEGRITY_CAPS ctx */
+        if (!found_preauth_ctx)
+                status = NT_STATUS_INVALID_PARAMETER;
+
+        if (!preauth_sha512_enabled) {
+                status = STATUS_PREAUTH_HASH_OVERLAP;
+                goto errout;
+        }
+
+        s->smb31_preauth_hashid = SMB3_HASH_SHA512;
+
+        switch (cipher) {
+        case SMB3_CIPHER_AES128_GCM:
+                if (encrypt_gcm_enabled) {
+                        s->smb31_enc_cipherid = cipher;
+                        break;
+                }
+                /* FALLTHROUGH */
+        case SMB3_CIPHER_AES128_CCM:
+                if (encrypt_ccm_enabled) {
+                        s->smb31_enc_cipherid = cipher;
+                        break;
+                }
+                /* FALLTHROUGH */
+        default:
+                s->smb31_enc_cipherid = 0;
+        }
+
+errout:
+        return (status);
+}
+
+#define SMB31_PREAUTH_CTX_SALT_LEN      32
+
+static int
+smb31_encode_neg_ctxs(smb_request_t *sr, smb_session_t *s)
+{
+        uint8_t salt[SMB31_PREAUTH_CTX_SALT_LEN];
+        uint16_t salt_len = SMB31_PREAUTH_CTX_SALT_LEN;
+        uint32_t preauth_ctx_len = 6 + salt_len;
+        uint32_t enc_ctx_len = 4;
+        uint32_t neg_ctx_off = SMB2_HDR_SIZE + 64 +
+            P2ROUNDUP(sr->sr_cfg->skc_negtok_len, 8);
+        uint32_t rc;
+
+        sr->reply.chain_offset = P2ROUNDUP(sr->reply.chain_offset, 8);
+
+        ASSERT3S(neg_ctx_off, ==, sr->reply.chain_offset);
+
+        (void) random_get_pseudo_bytes(salt, sizeof (salt));
+
+        rc = smb_mbc_encodef(
+            &sr->reply, "ww4.",
+            SMB31_PREAUTH_INTEGRITY_CAPS,
+            preauth_ctx_len
+            /* 4. */); /* reserverd */
+
+        ASSERT0(rc);
+        rc = smb_mbc_encodef(
+            &sr->reply, "www#c",
+            1,                          /* hash algo count */
+            salt_len,                   /* salt length */
+            s->smb31_preauth_hashid,    /* hash id */
+            salt_len,                   /* salt length */
+            salt);
+
+        /* aligned on 8-bytes boundary */
+        if (rc != 0 || s->smb31_enc_cipherid == 0) {
+                cmn_err(CE_NOTE, "Encryption is not supported");
+                return (rc);
+        }
+
+        if (sr->reply.chain_offset % 8 != 0) {
+                sr->reply.chain_offset = P2ROUNDUP(sr->reply.chain_offset, 8);
+        }
+
+        rc = smb_mbc_encodef(
+            &sr->reply, "ww4.",
+            SMB31_ENCRYPTION_CAPS,
+            enc_ctx_len
+            /* 4. */); /* reserverd */
+
+        rc = smb_mbc_encodef(
+            &sr->reply, "ww",
+            1,                          /* cipher count */
+            s->smb31_enc_cipherid);     /* encrypt. cipher id */
+
+        return (rc);
+}
+
 int
 smb2_newrq_negotiate(smb_request_t *sr)
 {
         smb_session_t *s = sr->session;
         int rc;

@@ -307,10 +578,15 @@
         best_version = smb2_find_best_dialect(s, cl_versions, version_cnt);
         if (best_version == 0) {
                 status = NT_STATUS_NOT_SUPPORTED;
                 goto errout;
         }
+        if (best_version >= 0x311) {
+                if ((status = smb31_decode_neg_ctxs(sr, s)) != 0)
+                        goto errout;
+        }
+
         s->dialect = best_version;
 
         /* Allow normal SMB2 requests now. */
         s->s_state = SMB_SESSION_STATE_NEGOTIATED;
         s->newrq_func = smb2sr_newrq;

@@ -324,10 +600,16 @@
 
         if (sr->smb2_status != 0)
                 smb2sr_put_error(sr, sr->smb2_status);
         (void) smb2_encode_header(sr, B_TRUE);
 
+        if (s->dialect >= SMB_VERS_3_11) {
+                ASSERT3U(s->smb31_preauth_hashid, !=, 0);
+                (void) smb31_preauth_sha512_calc(sr, &sr->reply,
+                    s->smb31_preauth_hashval);
+        }
+
         smb2_send_reply(sr);
 
         return (rc);
 }
 

@@ -345,10 +627,12 @@
         timestruc_t boot_tv, now_tv;
         smb_session_t *s = sr->session;
         int rc;
         uint32_t max_rwsize;
         uint16_t secmode;
+        uint16_t neg_ctx_cnt = 0;
+        uint32_t neg_ctx_off = 0;
 
         /*
          * Negotiation itself.  First the Security Mode.
          */
         secmode = SMB2_NEGOTIATE_SIGNING_ENABLED;

@@ -377,10 +661,12 @@
          * If the version is 0x2FF, we haven't completed negotiate.
          * Don't initialize until we have our final request.
          */
         if (version != 0x2FF)
                 smb2_sign_init_mech(s);
+        if (version >= 0x311)
+                smb31_preauth_init_mech(s);
 
         /*
          * [MS-SMB2] 3.3.5.4 Receiving an SMB2 NEGOTIATE Request
          *
          * The SMB2.x capabilities are returned without regard for

@@ -402,10 +688,21 @@
                 s->srv_cap = smb2srv_capabilities &
                     (SMB_2X_CAPS | s->capabilities);
                 if ((s->srv_cap & SMB2_CAP_ENCRYPTION) != 0 &&
                     smb3_encrypt_init_mech(s) != 0) {
                         s->srv_cap &= ~SMB2_CAP_ENCRYPTION;
+                        s->smb31_enc_cipherid = 0;
+                }
+
+                if (s->dialect >= SMB_VERS_3_11) {
+                        neg_ctx_cnt = s->smb31_enc_cipherid == 0 ? 1 : 2;
+                        neg_ctx_off = SMB2_HDR_SIZE + 64 +
+                            P2ROUNDUP(sr->sr_cfg->skc_negtok_len, 8);
+
+                        ASSERT3U(s->smb31_preauth_hashid, !=, 0);
+                        (void) smb31_preauth_sha512_calc(sr, &sr->command,
+                            s->smb31_preauth_hashval);
                 }
         }
 
         /*
          * See notes above smb2_max_rwsize, smb2_old_rwsize

@@ -419,25 +716,31 @@
             &sr->reply,
             "wwww#cllllTTwwl#c",
             65, /* StructSize */        /* w */
             s->srv_secmode,             /* w */
             version,                    /* w */
-            0, /* reserved */           /* w */
+            neg_ctx_cnt,                /* w */
             UUID_LEN,                   /* # */
             &s->s_cfg.skc_machine_uuid, /* c */
             s->srv_cap,                 /* l */
             smb2_max_trans,             /* l */
             max_rwsize,                 /* l */
             max_rwsize,                 /* l */
             &now_tv,                    /* T */
             &boot_tv,                   /* T */
             128, /* SecBufOff */        /* w */
             sr->sr_cfg->skc_negtok_len, /* w */
-            0,  /* reserved */          /* l */
+            neg_ctx_off,                /* l */
             sr->sr_cfg->skc_negtok_len, /* # */
             sr->sr_cfg->skc_negtok);    /* c */
 
+
+
+        if (neg_ctx_cnt) {
+                rc = smb31_encode_neg_ctxs(sr, s);
+        }
+
         /* smb2_send_reply(sr); in caller */
 
         (void) ksocket_setsockopt(s->sock, SOL_SOCKET,
             SO_SNDBUF, (const void *)&smb2_tcp_bufsize,
             sizeof (smb2_tcp_bufsize), CRED());