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