--- old/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c	2020-04-15 01:43:37.348454016 +0000
+++ new/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c	2020-04-15 01:43:37.353469573 +0000
@@ -11,7 +11,7 @@
 
 /*
  * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
- * Copyright 2019 RackTop Systems.
+ * Copyright 2020 RackTop Systems, Inc.
  */
 
 /*
@@ -20,6 +20,7 @@
 
 #include <smbsrv/smb2_kproto.h>
 #include <smbsrv/smb2.h>
+#include <sys/random.h>
 
 /*
  * Note from [MS-SMB2] Sec. 2.2.3:  Windows servers return
@@ -85,6 +86,7 @@
 	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]);
@@ -210,6 +212,275 @@
  * 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)
 {
@@ -263,8 +534,8 @@
 	    version_cnt <= SMB2_NEGOTIATE_MAX_DIALECTS &&
 	    smb_mbc_decodef(&sr->command, "#w", version_cnt,
 	    cl_versions) != 0) {
-	    /* decode error; force an error below */
-	    version_cnt = 0;
+		/* decode error; force an error below */
+		version_cnt = 0;
 	}
 
 	DTRACE_SMB2_START(op__Negotiate, smb_request_t *, sr);
@@ -309,6 +580,11 @@
 		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. */
@@ -326,6 +602,12 @@
 		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);
@@ -347,6 +629,8 @@
 	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.
@@ -379,6 +663,8 @@
 	 */
 	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
@@ -404,6 +690,17 @@
 		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);
 		}
 	}
 
@@ -421,7 +718,7 @@
 	    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 */
@@ -432,10 +729,16 @@
 	    &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,
