From 483ffb866dca057974809df827ef5ed8bfe821b3 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 14 May 2024 18:21:33 +0200 Subject: [PATCH 01/10] smbXcli_base: add hacks to test anonymous signing and encryption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUG: https://bugzilla.samba.org/show_bug.cgi?id=15412 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Günther Deschner (cherry picked from commit 14d6e2672126adee85997dc3d3c64607c987e8b9) --- libcli/smb/smbXcli_base.c | 104 ++++++++++++++++++++++++++++++++++++-- libcli/smb/smbXcli_base.h | 5 ++ 2 files changed, 104 insertions(+), 5 deletions(-) diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index a52a615857f0..87acddfc94f0 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -166,6 +166,13 @@ struct smb2cli_session { uint16_t channel_sequence; bool replay_active; bool require_signed_response; + + /* + * The following are just for torture tests + */ + bool anonymous_signing; + bool anonymous_encryption; + bool no_signing_disconnect; }; struct smbXcli_session { @@ -3999,6 +4006,9 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn, if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED) || NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) || + (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) && + session != NULL && + session->smb2->no_signing_disconnect) || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { /* * if the server returns @@ -4042,8 +4052,29 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn, /* * If the signing check fails, we disconnect * the connection. + * + * Unless + * smb2cli_session_torture_no_signing_disconnect + * was called in torture tests */ - return signing_status; + + if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + return signing_status; + } + + if (!NT_STATUS_EQUAL(status, signing_status)) { + return signing_status; + } + + if (session == NULL) { + return signing_status; + } + + if (!session->smb2->no_signing_disconnect) { + return signing_status; + } + + state->smb2.signing_skipped = true; } } @@ -6332,6 +6363,23 @@ void smb2cli_session_require_signed_response(struct smbXcli_session *session, session->smb2->require_signed_response = require_signed_response; } +void smb2cli_session_torture_anonymous_signing(struct smbXcli_session *session, + bool anonymous_signing) +{ + session->smb2->anonymous_signing = anonymous_signing; +} + +void smb2cli_session_torture_anonymous_encryption(struct smbXcli_session *session, + bool anonymous_encryption) +{ + session->smb2->anonymous_encryption = anonymous_encryption; +} + +void smb2cli_session_torture_no_signing_disconnect(struct smbXcli_session *session) +{ + session->smb2->no_signing_disconnect = true; +} + NTSTATUS smb2cli_session_update_preauth(struct smbXcli_session *session, const struct iovec *iov) { @@ -6432,6 +6480,10 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, conn->protocol, preauth_hash); + if (session->smb2->anonymous_encryption) { + goto skip_signing_key; + } + status = smb2_signing_key_sign_create(session->smb2, conn->smb2.server.sign_algo, &_session_key, @@ -6441,6 +6493,15 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, return status; } + if (session->smb2->anonymous_signing) { + /* + * skip encryption and application keys + */ + goto skip_application_key; + } + +skip_signing_key: + status = smb2_signing_key_cipher_create(session->smb2, conn->smb2.server.cipher, &_session_key, @@ -6459,6 +6520,10 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, return status; } + if (session->smb2->anonymous_encryption) { + goto skip_application_key; + } + status = smb2_signing_key_sign_create(session->smb2, conn->smb2.server.sign_algo, &_session_key, @@ -6468,6 +6533,8 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, return status; } +skip_application_key: + status = smb2_signing_key_copy(session, session->smb2->signing_key, &session->smb2_channel.signing_key); @@ -6477,6 +6544,18 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, check_signature = conn->mandatory_signing; + if (conn->protocol >= PROTOCOL_SMB3_11) { + check_signature = true; + } + + if (session->smb2->anonymous_signing) { + check_signature = false; + } + + if (session->smb2->anonymous_encryption) { + check_signature = false; + } + hdr_flags = IVAL(recv_iov[0].iov_base, SMB2_HDR_FLAGS); if (hdr_flags & SMB2_HDR_FLAG_SIGNED) { /* @@ -6492,10 +6571,6 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, check_signature = true; } - if (conn->protocol >= PROTOCOL_SMB3_11) { - check_signature = true; - } - if (check_signature) { status = smb2_signing_check_pdu(session->smb2_channel.signing_key, recv_iov, 3); @@ -6527,6 +6602,15 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, session->smb2->should_encrypt = false; } + if (session->smb2->anonymous_signing) { + session->smb2->should_sign = true; + } + + if (session->smb2->anonymous_encryption) { + session->smb2->should_encrypt = true; + session->smb2->should_sign = false; + } + /* * CCM and GCM algorithms must never have their * nonce wrap, or the security of the whole @@ -6698,6 +6782,16 @@ NTSTATUS smb2cli_session_set_channel_key(struct smbXcli_session *session, NTSTATUS smb2cli_session_encryption_on(struct smbXcli_session *session) { + if (session->smb2->anonymous_signing) { + return NT_STATUS_INVALID_PARAMETER_MIX; + } + + if (session->smb2->anonymous_encryption) { + SMB_ASSERT(session->smb2->should_encrypt); + SMB_ASSERT(!session->smb2->should_sign); + return NT_STATUS_OK; + } + if (!session->smb2->should_sign) { /* * We need required signing on the session diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h index 25ccd84b3360..69fa131a31d6 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -535,6 +535,11 @@ void smb2cli_session_start_replay(struct smbXcli_session *session); void smb2cli_session_stop_replay(struct smbXcli_session *session); void smb2cli_session_require_signed_response(struct smbXcli_session *session, bool require_signed_response); +void smb2cli_session_torture_anonymous_signing(struct smbXcli_session *session, + bool anonymous_signing); +void smb2cli_session_torture_anonymous_encryption(struct smbXcli_session *session, + bool anonymous_encryption); +void smb2cli_session_torture_no_signing_disconnect(struct smbXcli_session *session); NTSTATUS smb2cli_session_update_preauth(struct smbXcli_session *session, const struct iovec *iov); NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, -- 2.34.1 From e6161dee702d9010c2f17e603d744ab7e7d64ba6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 15 May 2024 10:51:42 +0200 Subject: [PATCH 02/10] s4:libcli/smb2: add hack to test anonymous signing and encryption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be used in torture tests. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15412 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Günther Deschner (cherry picked from commit 6a89615d78119c0bff2fb07bd0c62e4c31ea8441) --- source4/libcli/smb2/session.c | 16 +++++++++++----- source4/libcli/smb2/smb2.h | 2 ++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/source4/libcli/smb2/session.c b/source4/libcli/smb2/session.c index e94512d3d337..322a7bd6860a 100644 --- a/source4/libcli/smb2/session.c +++ b/source4/libcli/smb2/session.c @@ -385,7 +385,9 @@ static void smb2_session_setup_spnego_both_ready(struct tevent_req *req) return; } - if (cli_credentials_is_anonymous(state->credentials)) { + if (cli_credentials_is_anonymous(state->credentials) && + !state->session->anonymous_session_key) + { /* * Windows server does not set the * SMB2_SESSION_FLAG_IS_GUEST nor @@ -399,10 +401,14 @@ static void smb2_session_setup_spnego_both_ready(struct tevent_req *req) return; } - status = gensec_session_key(session->gensec, state, - &session_key); - if (tevent_req_nterror(req, status)) { - return; + if (state->session->forced_session_key.length != 0) { + session_key = state->session->forced_session_key; + } else { + status = gensec_session_key(session->gensec, state, + &session_key); + if (tevent_req_nterror(req, status)) { + return; + } } if (state->session_bind) { diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h index 88e651a396c6..1e2f1859fb4e 100644 --- a/source4/libcli/smb2/smb2.h +++ b/source4/libcli/smb2/smb2.h @@ -128,6 +128,8 @@ struct smb2_session { struct gensec_security *gensec; struct smbXcli_session *smbXcli; bool needs_bind; + bool anonymous_session_key; + DATA_BLOB forced_session_key; }; -- 2.34.1 From 9a70693203b32cd63c89af72fc1074e7c6bb90eb Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 15 May 2024 10:02:00 +0200 Subject: [PATCH 03/10] s4:torture/smb2: add smb2.session.anon-{encryption{1,2,},signing{1,2}} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These demonstrate how anonymous encryption and signing work. They pass against Windows 2022 as ad dc. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15412 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Günther Deschner (cherry picked from commit 6c5781b5f154857f1454f41133687fba8c4c9df9) --- selftest/knownfail.d/anon-encryption | 1 + source4/torture/smb2/session.c | 629 +++++++++++++++++++++++++++ 2 files changed, 630 insertions(+) create mode 100644 selftest/knownfail.d/anon-encryption diff --git a/selftest/knownfail.d/anon-encryption b/selftest/knownfail.d/anon-encryption new file mode 100644 index 000000000000..99d833c205a1 --- /dev/null +++ b/selftest/knownfail.d/anon-encryption @@ -0,0 +1 @@ +^samba3.smb2.session.*.anon-encryption2 diff --git a/source4/torture/smb2/session.c b/source4/torture/smb2/session.c index 823304f190f0..2a3d0e6e8536 100644 --- a/source4/torture/smb2/session.c +++ b/source4/torture/smb2/session.c @@ -5527,6 +5527,630 @@ static bool test_session_ntlmssp_bug14932(struct torture_context *tctx, struct s return ret; } +static bool test_session_anon_encryption1(struct torture_context *tctx, + struct smb2_tree *tree0) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = "IPC$"; + char *unc = NULL; + struct smb2_transport *transport0 = tree0->session->transport; + struct cli_credentials *anon_creds = NULL; + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_session *anon_session = NULL; + struct smb2_tree *anon_tree = NULL; + NTSTATUS status; + bool ok = true; + struct tevent_req *subreq = NULL; + uint32_t timeout_msec; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + torture_assert(tctx, unc != NULL, "talloc_asprintf"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon"); + ok = cli_credentials_set_smb_encryption(anon_creds, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options = transport0->options; + options.client_guid = GUID_random(); + options.only_negprot = true; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + anon_creds, + &anon_tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "smb2_connect failed"); + anon_session = anon_tree->session; + transport = anon_session->transport; + + anon_session->anonymous_session_key = true; + smb2cli_session_torture_anonymous_encryption(anon_session->smbXcli, true); + + status = smb2_session_setup_spnego(anon_session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok(tctx, status, + "smb2_session_setup_spnego failed"); + + ok = smbXcli_session_is_authenticated(anon_session->smbXcli); + torture_assert(tctx, !ok, "smbXcli_session_is_authenticated(anon) wrong"); + + /* + * The connection is still in ConstrainedConnection state... + * + * This will use encryption and causes a connection reset + */ + timeout_msec = transport->options.request_timeout * 1000; + subreq = smb2cli_tcon_send(tctx, + tctx->ev, + transport->conn, + timeout_msec, + anon_session->smbXcli, + anon_tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) { + status = NT_STATUS_CONNECTION_RESET; + } + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_CONNECTION_RESET, + "smb2cli_tcon_recv"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, !ok, "smbXcli_conn_is_connected still connected"); + + return true; +} + +static bool test_session_anon_encryption2(struct torture_context *tctx, + struct smb2_tree *tree0) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = "IPC$"; + char *unc = NULL; + struct smb2_transport *transport0 = tree0->session->transport; + struct cli_credentials *_creds = samba_cmdline_get_creds(); + struct cli_credentials *user_creds = NULL; + struct cli_credentials *anon_creds = NULL; + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_session *user_session = NULL; + struct smb2_tree *user_tree = NULL; + struct smb2_session *anon_session = NULL; + struct smb2_tree *anon_tree = NULL; + struct smb2_ioctl ioctl = { + .level = RAW_IOCTL_SMB2, + .in = { + .file = { + .handle = { + .data = { + [0] = UINT64_MAX, + [1] = UINT64_MAX, + }, + }, + }, + .function = FSCTL_QUERY_NETWORK_INTERFACE_INFO, + /* Windows client sets this to 64KiB */ + .max_output_response = 0x10000, + .flags = SMB2_IOCTL_FLAG_IS_FSCTL, + }, + }; + NTSTATUS status; + bool ok = true; + struct tevent_req *subreq = NULL; + uint32_t timeout_msec; + uint32_t caps = smb2cli_conn_server_capabilities(transport0->conn); + NTSTATUS expected_mc_status; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + if (caps & SMB2_CAP_MULTI_CHANNEL) { + expected_mc_status = NT_STATUS_OK; + } else { + expected_mc_status = NT_STATUS_FS_DRIVER_REQUIRED; + } + + unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + torture_assert(tctx, unc != NULL, "talloc_asprintf"); + + user_creds = cli_credentials_shallow_copy(tctx, _creds); + torture_assert(tctx, user_creds != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(user_creds, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon"); + ok = cli_credentials_set_smb_encryption(anon_creds, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options = transport0->options; + options.client_guid = GUID_random(); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + user_creds, + &user_tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "smb2_connect failed"); + user_session = user_tree->session; + transport = user_session->transport; + ok = smb2cli_tcon_is_encryption_on(user_tree->smbXcli); + torture_assert(tctx, ok, "smb2cli_tcon_is_encryption_on(user)"); + ok = smbXcli_session_is_authenticated(user_session->smbXcli); + torture_assert(tctx, ok, "smbXcli_session_is_authenticated(user)"); + + anon_session = smb2_session_init(transport, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx); + torture_assert(tctx, anon_session != NULL, "smb2_session_init(anon)"); + + anon_session->anonymous_session_key = true; + smb2cli_session_torture_anonymous_encryption(anon_session->smbXcli, true); + + status = smb2_session_setup_spnego(anon_session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok(tctx, status, + "smb2_session_setup_spnego failed"); + + ok = smb2cli_tcon_is_encryption_on(user_tree->smbXcli); + torture_assert(tctx, ok, "smb2cli_tcon_is_encryption_on(anon)"); + ok = smbXcli_session_is_authenticated(anon_session->smbXcli); + torture_assert(tctx, !ok, "smbXcli_session_is_authenticated(anon) wrong"); + + anon_tree = smb2_tree_init(anon_session, tctx, false); + torture_assert(tctx, anon_tree != NULL, "smb2_tree_init"); + + timeout_msec = transport->options.request_timeout * 1000; + subreq = smb2cli_tcon_send(tctx, + tctx->ev, + transport->conn, + timeout_msec, + anon_session->smbXcli, + anon_tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + torture_assert_ntstatus_ok(tctx, status, + "smb2cli_tcon_recv(anon)"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + ok = smb2cli_tcon_is_encryption_on(anon_tree->smbXcli); + torture_assert(tctx, ok, "smb2cli_tcon_is_encryption_on(anon)"); + ok = smbXcli_session_is_authenticated(anon_session->smbXcli); + torture_assert(tctx, !ok, "smbXcli_session_is_authenticated(anon) wrong"); + + status = smb2_ioctl(user_tree, tctx, &ioctl); + torture_assert_ntstatus_equal(tctx, status, expected_mc_status, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO user"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + status = smb2_ioctl(anon_tree, tctx, &ioctl); + torture_assert_ntstatus_equal(tctx, status, expected_mc_status, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO anonymous"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + status = smb2_ioctl(user_tree, tctx, &ioctl); + torture_assert_ntstatus_equal(tctx, status, expected_mc_status, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO user"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + status = smb2_ioctl(anon_tree, tctx, &ioctl); + torture_assert_ntstatus_equal(tctx, status, expected_mc_status, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO anonymous"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + return true; +} + +static bool test_session_anon_encryption3(struct torture_context *tctx, + struct smb2_tree *tree0) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = "IPC$"; + char *unc = NULL; + struct smb2_transport *transport0 = tree0->session->transport; + struct cli_credentials *_creds = samba_cmdline_get_creds(); + struct cli_credentials *user_creds = NULL; + struct cli_credentials *anon_creds = NULL; + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_session *user_session = NULL; + struct smb2_tree *user_tree = NULL; + struct smb2_session *anon_session = NULL; + struct smb2_tree *anon_tree = NULL; + NTSTATUS status; + bool ok = true; + struct tevent_req *subreq = NULL; + uint32_t timeout_msec; + uint8_t wrong_session_key[16] = { 0x1f, 0x2f, 0x3f, }; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + torture_assert(tctx, unc != NULL, "talloc_asprintf"); + + user_creds = cli_credentials_shallow_copy(tctx, _creds); + torture_assert(tctx, user_creds != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(user_creds, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon"); + ok = cli_credentials_set_smb_encryption(anon_creds, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options = transport0->options; + options.client_guid = GUID_random(); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + user_creds, + &user_tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "smb2_connect failed"); + user_session = user_tree->session; + transport = user_session->transport; + ok = smb2cli_tcon_is_encryption_on(user_tree->smbXcli); + torture_assert(tctx, ok, "smb2cli_tcon_is_encryption_on(user)"); + ok = smbXcli_session_is_authenticated(user_session->smbXcli); + torture_assert(tctx, ok, "smbXcli_session_is_authenticated(user)"); + + anon_session = smb2_session_init(transport, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx); + torture_assert(tctx, anon_session != NULL, "smb2_session_init(anon)"); + + anon_session->anonymous_session_key = true; + anon_session->forced_session_key = data_blob_const(wrong_session_key, + ARRAY_SIZE(wrong_session_key)); + smb2cli_session_torture_anonymous_encryption(anon_session->smbXcli, true); + + status = smb2_session_setup_spnego(anon_session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok(tctx, status, + "smb2_session_setup_spnego failed"); + + ok = smb2cli_tcon_is_encryption_on(user_tree->smbXcli); + torture_assert(tctx, ok, "smb2cli_tcon_is_encryption_on(anon)"); + ok = smbXcli_session_is_authenticated(anon_session->smbXcli); + torture_assert(tctx, !ok, "smbXcli_session_is_authenticated(anon) wrong"); + + anon_tree = smb2_tree_init(anon_session, tctx, false); + torture_assert(tctx, anon_tree != NULL, "smb2_tree_init"); + + timeout_msec = transport->options.request_timeout * 1000; + subreq = smb2cli_tcon_send(tctx, + tctx->ev, + transport->conn, + timeout_msec, + anon_session->smbXcli, + anon_tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) { + status = NT_STATUS_CONNECTION_RESET; + } + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_CONNECTION_RESET, + "smb2cli_tcon_recv"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, !ok, "smbXcli_conn_is_connected still connected"); + + return true; +} + +static bool test_session_anon_signing1(struct torture_context *tctx, + struct smb2_tree *tree0) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = "IPC$"; + char *unc = NULL; + struct smb2_transport *transport0 = tree0->session->transport; + struct cli_credentials *anon_creds = NULL; + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_session *anon_session = NULL; + struct smb2_tree *anon_tree = NULL; + NTSTATUS status; + bool ok = true; + struct tevent_req *subreq = NULL; + uint32_t timeout_msec; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + torture_assert(tctx, unc != NULL, "talloc_asprintf"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon"); + ok = cli_credentials_set_smb_signing(anon_creds, + SMB_SIGNING_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_signing"); + ok = cli_credentials_set_smb_ipc_signing(anon_creds, + SMB_SIGNING_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_ipc_signing"); + ok = cli_credentials_set_smb_encryption(anon_creds, + SMB_ENCRYPTION_OFF, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options = transport0->options; + options.client_guid = GUID_random(); + options.only_negprot = true; + options.signing = SMB_SIGNING_REQUIRED; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + anon_creds, + &anon_tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "smb2_connect failed"); + anon_session = anon_tree->session; + transport = anon_session->transport; + + anon_session->anonymous_session_key = true; + smb2cli_session_torture_anonymous_signing(anon_session->smbXcli, true); + + status = smb2_session_setup_spnego(anon_session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok(tctx, status, + "smb2_session_setup_spnego failed"); + + ok = smbXcli_session_is_authenticated(anon_session->smbXcli); + torture_assert(tctx, !ok, "smbXcli_session_is_authenticated(anon) wrong"); + + timeout_msec = transport->options.request_timeout * 1000; + subreq = smb2cli_tcon_send(tctx, + tctx->ev, + transport->conn, + timeout_msec, + anon_session->smbXcli, + anon_tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + torture_assert_ntstatus_ok(tctx, status, "smb2cli_tcon_recv"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + return true; +} + +static bool test_session_anon_signing2(struct torture_context *tctx, + struct smb2_tree *tree0) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = "IPC$"; + char *unc = NULL; + struct smb2_transport *transport0 = tree0->session->transport; + struct cli_credentials *anon_creds = NULL; + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_session *anon_session = NULL; + struct smb2_session *anon_session_nosign = NULL; + struct smb2_tree *anon_tree = NULL; + NTSTATUS status; + bool ok = true; + struct tevent_req *subreq = NULL; + uint32_t timeout_msec; + uint8_t wrong_session_key[16] = { 0x1f, 0x2f, 0x3f, }; + uint64_t session_id; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + torture_assert(tctx, unc != NULL, "talloc_asprintf"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon"); + ok = cli_credentials_set_smb_signing(anon_creds, + SMB_SIGNING_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_signing"); + ok = cli_credentials_set_smb_ipc_signing(anon_creds, + SMB_SIGNING_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_ipc_signing"); + ok = cli_credentials_set_smb_encryption(anon_creds, + SMB_ENCRYPTION_OFF, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options = transport0->options; + options.client_guid = GUID_random(); + options.only_negprot = true; + options.signing = SMB_SIGNING_REQUIRED; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + anon_creds, + &anon_tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "smb2_connect failed"); + anon_session = anon_tree->session; + transport = anon_session->transport; + + anon_session->anonymous_session_key = true; + anon_session->forced_session_key = data_blob_const(wrong_session_key, + ARRAY_SIZE(wrong_session_key)); + smb2cli_session_torture_anonymous_signing(anon_session->smbXcli, true); + smb2cli_session_torture_no_signing_disconnect(anon_session->smbXcli); + + status = smb2_session_setup_spnego(anon_session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok(tctx, status, + "smb2_session_setup_spnego failed"); + + ok = smbXcli_session_is_authenticated(anon_session->smbXcli); + torture_assert(tctx, !ok, "smbXcli_session_is_authenticated(anon) wrong"); + + /* + * create a new structure for the same session id, + * but without smb2.should_sign set. + */ + session_id = smb2cli_session_current_id(anon_session->smbXcli); + anon_session_nosign = smb2_session_init(transport, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx); + torture_assert(tctx, anon_session_nosign != NULL, "smb2_session_init(anon_nosign)"); + smb2cli_session_set_id_and_flags(anon_session_nosign->smbXcli, session_id, 0); + smb2cli_session_torture_no_signing_disconnect(anon_session_nosign->smbXcli); + + timeout_msec = transport->options.request_timeout * 1000; + subreq = smb2cli_tcon_send(tctx, + tctx->ev, + transport->conn, + timeout_msec, + anon_session->smbXcli, + anon_tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_ACCESS_DENIED, + "smb2cli_tcon_recv"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + subreq = smb2cli_tcon_send(tctx, + tctx->ev, + transport->conn, + timeout_msec, + anon_session_nosign->smbXcli, + anon_tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + torture_assert_ntstatus_ok(tctx, status, "smb2cli_tcon_recv"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + return true; +} + struct torture_suite *torture_smb2_session_init(TALLOC_CTX *ctx) { struct torture_suite *suite = @@ -5599,6 +6223,11 @@ struct torture_suite *torture_smb2_session_init(TALLOC_CTX *ctx) torture_suite_add_1smb2_test(suite, "encryption-aes-256-ccm", test_session_encryption_aes_256_ccm); torture_suite_add_1smb2_test(suite, "encryption-aes-256-gcm", test_session_encryption_aes_256_gcm); torture_suite_add_1smb2_test(suite, "ntlmssp_bug14932", test_session_ntlmssp_bug14932); + torture_suite_add_1smb2_test(suite, "anon-encryption1", test_session_anon_encryption1); + torture_suite_add_1smb2_test(suite, "anon-encryption2", test_session_anon_encryption2); + torture_suite_add_1smb2_test(suite, "anon-encryption3", test_session_anon_encryption3); + torture_suite_add_1smb2_test(suite, "anon-signing1", test_session_anon_signing1); + torture_suite_add_1smb2_test(suite, "anon-signing2", test_session_anon_signing2); suite->description = talloc_strdup(suite, "SMB2-SESSION tests"); -- 2.34.1 From c61313e54c6b597edc108d08d139855a3f3b1dbf Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 3 Jul 2023 15:05:59 +0200 Subject: [PATCH 04/10] s3:utils: remove unused signing_flags in connections_forall() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We never use the signing flags from the session, as the tcon has its own signing flags. https://bugzilla.samba.org/show_bug.cgi?id=15412 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Günther Deschner (cherry picked from commit a9f84593f44f15a19c4cdde1e7ad53cd5e03b4d9) --- source3/utils/conn_tdb.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source3/utils/conn_tdb.c b/source3/utils/conn_tdb.c index 3724bd424936..2689f71068ae 100644 --- a/source3/utils/conn_tdb.c +++ b/source3/utils/conn_tdb.c @@ -44,7 +44,6 @@ struct connections_forall_session { uint16_t cipher; uint16_t dialect; uint16_t signing; - uint8_t signing_flags; }; static int collect_sessions_fn(struct smbXsrv_session_global0 *global, @@ -69,7 +68,6 @@ static int collect_sessions_fn(struct smbXsrv_session_global0 *global, sess.cipher = global->channels[0].encryption_cipher; sess.signing = global->channels[0].signing_algo; sess.dialect = global->connection_dialect; - sess.signing_flags = global->signing_flags; status = dbwrap_store(state->session_by_pid, make_tdb_data((void*)&id, sizeof(id)), -- 2.34.1 From 8e83bae70254966dd2008318756de62b7ced465b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 3 Jul 2023 15:08:31 +0200 Subject: [PATCH 05/10] s3:lib: let sessionid_traverse_read() report if the session was authenticated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUG: https://bugzilla.samba.org/show_bug.cgi?id=15412 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Günther Deschner (cherry picked from commit 596a10d1079f5c4a954108c81efc862c22a11f28) --- source3/include/session.h | 1 + source3/lib/sessionid_tdb.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/source3/include/session.h b/source3/include/session.h index 40c25e514bb4..903208e9b0b7 100644 --- a/source3/include/session.h +++ b/source3/include/session.h @@ -39,6 +39,7 @@ struct sessionid { fstring ip_addr_str; time_t connect_start; uint16_t connection_dialect; + bool authenticated; uint8_t encryption_flags; uint16_t cipher; uint16_t signing; diff --git a/source3/lib/sessionid_tdb.c b/source3/lib/sessionid_tdb.c index 2376fd42a25e..54bb895849f3 100644 --- a/source3/lib/sessionid_tdb.c +++ b/source3/lib/sessionid_tdb.c @@ -24,6 +24,7 @@ #include "session.h" #include "util_tdb.h" #include "smbd/globals.h" +#include "../libcli/security/session.h" struct sessionid_traverse_read_state { int (*fn)(const char *key, struct sessionid *session, @@ -48,11 +49,18 @@ static int sessionid_traverse_read_fn(struct smbXsrv_session_global0 *global, }; if (session_info != NULL) { + enum security_user_level ul; + session.uid = session_info->unix_token->uid; session.gid = session_info->unix_token->gid; strncpy(session.username, session_info->unix_info->unix_name, sizeof(fstring)-1); + + ul = security_session_user_level(session_info, NULL); + if (ul >= SECURITY_USER) { + session.authenticated = true; + } } strncpy(session.remote_machine, -- 2.34.1 From 5e79e67858c3c5be4ca0856305141ba26f4cce8f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 3 Jul 2023 15:10:08 +0200 Subject: [PATCH 06/10] s3:utils: let connections_forall_read() report if the session was authenticated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUG: https://bugzilla.samba.org/show_bug.cgi?id=15412 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Günther Deschner (cherry picked from commit 5089d8550640f72b1e0373f8ac321378ccaa8bd5) --- source3/utils/conn_tdb.c | 10 ++++++++++ source3/utils/conn_tdb.h | 1 + 2 files changed, 11 insertions(+) diff --git a/source3/utils/conn_tdb.c b/source3/utils/conn_tdb.c index 2689f71068ae..3f4ef00ae4f1 100644 --- a/source3/utils/conn_tdb.c +++ b/source3/utils/conn_tdb.c @@ -27,6 +27,7 @@ #include "conn_tdb.h" #include "util_tdb.h" #include "lib/util/string_wrappers.h" +#include "../libcli/security/session.h" struct connections_forall_state { struct db_context *session_by_pid; @@ -44,6 +45,7 @@ struct connections_forall_session { uint16_t cipher; uint16_t dialect; uint16_t signing; + bool authenticated; }; static int collect_sessions_fn(struct smbXsrv_session_global0 *global, @@ -55,6 +57,7 @@ static int collect_sessions_fn(struct smbXsrv_session_global0 *global, uint32_t id = global->session_global_id; struct connections_forall_session sess; + enum security_user_level ul; if (global->auth_session_info == NULL) { sess.uid = -1; @@ -68,6 +71,12 @@ static int collect_sessions_fn(struct smbXsrv_session_global0 *global, sess.cipher = global->channels[0].encryption_cipher; sess.signing = global->channels[0].signing_algo; sess.dialect = global->connection_dialect; + ul = security_session_user_level(global->auth_session_info, NULL); + if (ul >= SECURITY_USER) { + sess.authenticated = true; + } else { + sess.authenticated = false; + } status = dbwrap_store(state->session_by_pid, make_tdb_data((void*)&id, sizeof(id)), @@ -132,6 +141,7 @@ static int traverse_tcon_fn(struct smbXsrv_tcon_global0 *global, data.dialect = sess.dialect; data.signing = sess.signing; data.signing_flags = global->signing_flags; + data.authenticated = sess.authenticated; state->count++; diff --git a/source3/utils/conn_tdb.h b/source3/utils/conn_tdb.h index 2a6e04e0a820..23a5e214ff2b 100644 --- a/source3/utils/conn_tdb.h +++ b/source3/utils/conn_tdb.h @@ -36,6 +36,7 @@ struct connections_data { uint16_t dialect; uint8_t signing_flags; uint16_t signing; + bool authenticated; }; /* The following definitions come from lib/conn_tdb.c */ -- 2.34.1 From 60a1de0c0524eab37636b94bfcdee9625c933e1f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 3 Jul 2023 15:12:38 +0200 Subject: [PATCH 07/10] s3:utils: let smbstatus also report AES-256 encryption types for tcons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already do that for sessions. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15412 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Günther Deschner (cherry picked from commit 8119fd6d6a49b869bd9e8ff653b500e194b070de) --- source3/utils/status.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source3/utils/status.c b/source3/utils/status.c index 4102b4156014..d66e310226b2 100644 --- a/source3/utils/status.c +++ b/source3/utils/status.c @@ -549,6 +549,12 @@ static int traverse_connections(const struct connections_data *crec, case SMB2_ENCRYPTION_AES128_GCM: encryption = "AES-128-GCM"; break; + case SMB2_ENCRYPTION_AES256_CCM: + encryption = "AES-256-CCM"; + break; + case SMB2_ENCRYPTION_AES256_GCM: + encryption = "AES-256-GCM"; + break; default: encryption = "???"; break; -- 2.34.1 From 29389e77554c68e20fb6d7908cf9d602b92519e1 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 3 Jul 2023 15:12:38 +0200 Subject: [PATCH 08/10] s3:utils: let smbstatus also report partial tcon signing/encryption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already do that for sessions and also for the json output, but it was missing in the non-json output for tcons. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15412 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Günther Deschner (cherry picked from commit 551756abd2c9e4922075bc3037db645355542363) --- source3/utils/status.c | 48 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/source3/utils/status.c b/source3/utils/status.c index d66e310226b2..d31a145451d6 100644 --- a/source3/utils/status.c +++ b/source3/utils/status.c @@ -483,9 +483,29 @@ static int traverse_connections_stdout(struct traverse_state *state, char *server_id, const char *machine, const char *timestr, - const char *encryption, - const char *signing) + const char *encryption_cipher, + enum crypto_degree encryption_degree, + const char *signing_cipher, + enum crypto_degree signing_degree) { + fstring encryption; + fstring signing; + + if (encryption_degree == CRYPTO_DEGREE_FULL) { + fstr_sprintf(encryption, "%s", encryption_cipher); + } else if (encryption_degree == CRYPTO_DEGREE_PARTIAL) { + fstr_sprintf(encryption, "partial(%s)", encryption_cipher); + } else { + fstr_sprintf(encryption, "-"); + } + if (signing_degree == CRYPTO_DEGREE_FULL) { + fstr_sprintf(signing, "%s", signing_cipher); + } else if (signing_degree == CRYPTO_DEGREE_PARTIAL) { + fstr_sprintf(signing, "partial(%s)", signing_cipher); + } else { + fstr_sprintf(signing, "-"); + } + d_printf("%-12s %-7s %-13s %-32s %-12s %-12s\n", servicename, server_id, machine, timestr, encryption, signing); @@ -538,7 +558,9 @@ static int traverse_connections(const struct connections_data *crec, return -1; } - if (smbXsrv_is_encrypted(crec->encryption_flags)) { + if (smbXsrv_is_encrypted(crec->encryption_flags) || + smbXsrv_is_partially_encrypted(crec->encryption_flags)) + { switch (crec->cipher) { case SMB_ENCRYPTION_GSSAPI: encryption = "GSSAPI"; @@ -559,10 +581,16 @@ static int traverse_connections(const struct connections_data *crec, encryption = "???"; break; } - encryption_degree = CRYPTO_DEGREE_FULL; + if (smbXsrv_is_encrypted(crec->encryption_flags)) { + encryption_degree = CRYPTO_DEGREE_FULL; + } else if (smbXsrv_is_partially_encrypted(crec->encryption_flags)) { + encryption_degree = CRYPTO_DEGREE_PARTIAL; + } } - if (smbXsrv_is_signed(crec->signing_flags)) { + if (smbXsrv_is_signed(crec->signing_flags) || + smbXsrv_is_partially_signed(crec->signing_flags)) + { switch (crec->signing) { case SMB2_SIGNING_MD5_SMB1: signing = "HMAC-MD5"; @@ -580,7 +608,11 @@ static int traverse_connections(const struct connections_data *crec, signing = "???"; break; } - signing_degree = CRYPTO_DEGREE_FULL; + if (smbXsrv_is_signed(crec->signing_flags)) { + signing_degree = CRYPTO_DEGREE_FULL; + } else if (smbXsrv_is_partially_signed(crec->signing_flags)) { + signing_degree = CRYPTO_DEGREE_PARTIAL; + } } if (!state->json_output) { @@ -590,7 +622,9 @@ static int traverse_connections(const struct connections_data *crec, crec->machine, timestr, encryption, - signing); + encryption_degree, + signing, + signing_degree); } else { result = traverse_connections_json(state, crec, -- 2.34.1 From bb070ed470a5315a366cc302f8e6988b9edcf457 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 30 Jun 2023 18:05:51 +0200 Subject: [PATCH 09/10] s3:smbd: allow anonymous encryption after one authenticated session setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have captures where a client tries smb3 encryption on an anonymous session, we used to allow that before commit da7dcc443f45d07d9963df9daae458fbdd991a47 was released with samba-4.15.0rc1. Testing against Windows Server 2022 revealed that anonymous signing is always allowed (with the session key derived from 16 zero bytes) and anonymous encryption is allowed after one authenticated session setup on the tcp connection. https://bugzilla.samba.org/show_bug.cgi?id=15412 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Günther Deschner (cherry picked from commit f3ddfb828e66738ca461c3284c423defb774547c) --- selftest/knownfail.d/anon-encryption | 1 - source3/smbd/globals.h | 5 +++++ source3/smbd/smb2_server.c | 11 +++++++++++ source3/smbd/smb2_sesssetup.c | 18 +++++++++++++++++- source3/smbd/smb2_tcon.c | 4 ++++ 5 files changed, 37 insertions(+), 2 deletions(-) delete mode 100644 selftest/knownfail.d/anon-encryption diff --git a/selftest/knownfail.d/anon-encryption b/selftest/knownfail.d/anon-encryption deleted file mode 100644 index 99d833c205a1..000000000000 --- a/selftest/knownfail.d/anon-encryption +++ /dev/null @@ -1 +0,0 @@ -^samba3.smb2.session.*.anon-encryption2 diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index 4928d1f4fb80..a954499f43c4 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -522,6 +522,11 @@ struct smbXsrv_connection { } smbtorture; bool signing_mandatory; + /* + * This is ConstrainedConnection in MS-SMB2, + * but with reversed value... + */ + bool got_authenticated_session; } smb2; }; diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index afb86abf2fc8..5b83d4d96522 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -494,6 +494,17 @@ static NTSTATUS smbd_smb2_inbuf_parse_compound(struct smbXsrv_connection *xconn, goto inval; } + if (!xconn->smb2.got_authenticated_session) { + D_INFO("Got SMB2_TRANSFORM header, " + "but not no authenticated session yet " + "client[%s] server[%s]\n", + tsocket_address_string( + xconn->remote_address, talloc_tos()), + tsocket_address_string( + xconn->local_address, talloc_tos())); + goto inval; + } + if (len < SMB2_TF_HDR_SIZE) { DEBUG(1, ("%d bytes left, expected at least %d\n", (int)len, SMB2_TF_HDR_SIZE)); diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index ac71e550264e..d3b21ead82be 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -271,6 +271,13 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session, x->global->signing_flags &= ~SMBXSRV_SIGNING_REQUIRED; /* we map anonymous to guest internally */ guest = true; + } else { + /* + * Remember we got one authenticated session on the connection + * in order to allow SMB3 decryption to happen + * (sadly even for future anonymous connections). + */ + xconn->smb2.got_authenticated_session = true; } if (guest && (x->global->encryption_flags & SMBXSRV_ENCRYPTION_REQUIRED)) { @@ -288,7 +295,10 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session, } x->global->signing_algo = xconn->smb2.server.sign_algo; x->global->encryption_cipher = xconn->smb2.server.cipher; - if (guest) { + if (*out_session_flags & SMB2_SESSION_FLAG_IS_GUEST) { + /* + * A fallback to guest can't do any encryption + */ x->global->encryption_cipher = SMB2_ENCRYPTION_NONE; } @@ -642,6 +652,12 @@ static NTSTATUS smbd_smb2_bind_auth_return(struct smbXsrv_session *session, return NT_STATUS_LOGON_FAILURE; } + /* + * Remember we got one authenticated session on the connection + * in order to allow SMB3 decryption to happen + */ + xconn->smb2.got_authenticated_session = true; + *out_session_id = session->global->session_wire_id; return NT_STATUS_OK; diff --git a/source3/smbd/smb2_tcon.c b/source3/smbd/smb2_tcon.c index b228036510aa..20d89670df18 100644 --- a/source3/smbd/smb2_tcon.c +++ b/source3/smbd/smb2_tcon.c @@ -331,6 +331,10 @@ static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req, } } + if (guest_session) { + /* make sure we don't ask for optional encryption */ + encryption_desired = false; + } if (encryption_desired) { encryption_flags |= SMBXSRV_ENCRYPTION_DESIRED; } -- 2.34.1 From 5877439e3680c7366a366bef9964d17408c6e8c8 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 3 Jul 2023 15:14:38 +0200 Subject: [PATCH 10/10] s3:utils: let smbstatus report anonymous signing/encryption explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should mark sessions/tcons with anonymous encryption or signing in a special way, as the value of it is void, all based on a session key with 16 zero bytes. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15412 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Günther Deschner Autobuild-User(master): Stefan Metzmacher Autobuild-Date(master): Thu May 23 13:37:09 UTC 2024 on atb-devel-224 (cherry picked from commit 5a54c9b28abb1464c84cb4be15a49718d8ae6795) --- source3/utils/status.c | 28 ++++++++++++++++++++++++++++ source3/utils/status.h | 1 + source3/utils/status_json.c | 2 ++ 3 files changed, 31 insertions(+) diff --git a/source3/utils/status.c b/source3/utils/status.c index d31a145451d6..02a5f6dbaba1 100644 --- a/source3/utils/status.c +++ b/source3/utils/status.c @@ -493,6 +493,8 @@ static int traverse_connections_stdout(struct traverse_state *state, if (encryption_degree == CRYPTO_DEGREE_FULL) { fstr_sprintf(encryption, "%s", encryption_cipher); + } else if (encryption_degree == CRYPTO_DEGREE_ANONYMOUS) { + fstr_sprintf(encryption, "anonymous(%s)", encryption_cipher); } else if (encryption_degree == CRYPTO_DEGREE_PARTIAL) { fstr_sprintf(encryption, "partial(%s)", encryption_cipher); } else { @@ -500,6 +502,8 @@ static int traverse_connections_stdout(struct traverse_state *state, } if (signing_degree == CRYPTO_DEGREE_FULL) { fstr_sprintf(signing, "%s", signing_cipher); + } else if (signing_degree == CRYPTO_DEGREE_ANONYMOUS) { + fstr_sprintf(signing, "anonymous(%s)", signing_cipher); } else if (signing_degree == CRYPTO_DEGREE_PARTIAL) { fstr_sprintf(signing, "partial(%s)", signing_cipher); } else { @@ -586,6 +590,11 @@ static int traverse_connections(const struct connections_data *crec, } else if (smbXsrv_is_partially_encrypted(crec->encryption_flags)) { encryption_degree = CRYPTO_DEGREE_PARTIAL; } + if (encryption_degree != CRYPTO_DEGREE_NONE && + !crec->authenticated) + { + encryption_degree = CRYPTO_DEGREE_ANONYMOUS; + } } if (smbXsrv_is_signed(crec->signing_flags) || @@ -613,6 +622,11 @@ static int traverse_connections(const struct connections_data *crec, } else if (smbXsrv_is_partially_signed(crec->signing_flags)) { signing_degree = CRYPTO_DEGREE_PARTIAL; } + if (signing_degree != CRYPTO_DEGREE_NONE && + !crec->authenticated) + { + signing_degree = CRYPTO_DEGREE_ANONYMOUS; + } } if (!state->json_output) { @@ -655,6 +669,8 @@ static int traverse_sessionid_stdout(struct traverse_state *state, if (encryption_degree == CRYPTO_DEGREE_FULL) { fstr_sprintf(encryption, "%s", encryption_cipher); + } else if (encryption_degree == CRYPTO_DEGREE_ANONYMOUS) { + fstr_sprintf(encryption, "anonymous(%s)", encryption_cipher); } else if (encryption_degree == CRYPTO_DEGREE_PARTIAL) { fstr_sprintf(encryption, "partial(%s)", encryption_cipher); } else { @@ -662,6 +678,8 @@ static int traverse_sessionid_stdout(struct traverse_state *state, } if (signing_degree == CRYPTO_DEGREE_FULL) { fstr_sprintf(signing, "%s", signing_cipher); + } else if (signing_degree == CRYPTO_DEGREE_ANONYMOUS) { + fstr_sprintf(signing, "anonymous(%s)", signing_cipher); } else if (signing_degree == CRYPTO_DEGREE_PARTIAL) { fstr_sprintf(signing, "partial(%s)", signing_cipher); } else { @@ -796,6 +814,11 @@ static int traverse_sessionid(const char *key, struct sessionid *session, } else if (smbXsrv_is_partially_encrypted(session->encryption_flags)) { encryption_degree = CRYPTO_DEGREE_PARTIAL; } + if (encryption_degree != CRYPTO_DEGREE_NONE && + !session->authenticated) + { + encryption_degree = CRYPTO_DEGREE_ANONYMOUS; + } } if (smbXsrv_is_signed(session->signing_flags) || @@ -823,6 +846,11 @@ static int traverse_sessionid(const char *key, struct sessionid *session, } else if (smbXsrv_is_partially_signed(session->signing_flags)) { signing_degree = CRYPTO_DEGREE_PARTIAL; } + if (signing_degree != CRYPTO_DEGREE_NONE && + !session->authenticated) + { + signing_degree = CRYPTO_DEGREE_ANONYMOUS; + } } diff --git a/source3/utils/status.h b/source3/utils/status.h index c08aba4c2624..6674f0db54fe 100644 --- a/source3/utils/status.h +++ b/source3/utils/status.h @@ -38,6 +38,7 @@ struct traverse_state { enum crypto_degree { CRYPTO_DEGREE_NONE, CRYPTO_DEGREE_PARTIAL, + CRYPTO_DEGREE_ANONYMOUS, CRYPTO_DEGREE_FULL }; diff --git a/source3/utils/status_json.c b/source3/utils/status_json.c index ee24a3b31d97..f558c91dec71 100644 --- a/source3/utils/status_json.c +++ b/source3/utils/status_json.c @@ -258,6 +258,8 @@ static int add_crypto_to_json(struct json_object *parent_json, if (degree == CRYPTO_DEGREE_NONE) { degree_str = "none"; + } else if (degree == CRYPTO_DEGREE_ANONYMOUS) { + degree_str = "anonymous"; } else if (degree == CRYPTO_DEGREE_PARTIAL) { degree_str = "partial"; } else { -- 2.34.1