Print this page
12513 SMB 3.1.1 support for server

*** 9,27 **** * http://www.illumos.org/license/CDDL. */ /* * Copyright 2019 Nexenta Systems, Inc. All rights reserved. ! * Copyright 2019 RackTop Systems. */ /* * Dispatch function for SMB2_NEGOTIATE */ #include <smbsrv/smb2_kproto.h> #include <smbsrv/smb2.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 --- 9,28 ---- * http://www.illumos.org/license/CDDL. */ /* * Copyright 2019 Nexenta Systems, Inc. All rights reserved. ! * 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,92 **** --- 84,94 ---- 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,217 **** --- 210,488 ---- * 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,316 **** --- 578,592 ---- 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,333 **** --- 600,615 ---- 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,354 **** --- 627,638 ---- 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,386 **** --- 661,672 ---- * 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,411 **** --- 688,708 ---- 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,443 **** &sr->reply, "wwww#cllllTTwwl#c", 65, /* StructSize */ /* w */ s->srv_secmode, /* w */ version, /* w */ ! 0, /* reserved */ /* 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 */ sr->sr_cfg->skc_negtok_len, /* # */ sr->sr_cfg->skc_negtok); /* c */ /* 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()); --- 716,746 ---- &sr->reply, "wwww#cllllTTwwl#c", 65, /* StructSize */ /* w */ s->srv_secmode, /* w */ version, /* 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 */ ! 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());