From 14a0d742e33df4cfd2bd312407e86607a2ea6f79 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 fe422eb83fa1..c3960b53381a 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; } } @@ -6340,6 +6371,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) { @@ -6440,6 +6488,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, @@ -6449,6 +6501,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, @@ -6467,6 +6528,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, @@ -6476,6 +6541,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); @@ -6485,6 +6552,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) { /* @@ -6500,10 +6579,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); @@ -6535,6 +6610,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 @@ -6699,6 +6783,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 bf8638711ba5..4ce2338b4408 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -525,6 +525,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 673f5ec3e08e566f604cc85b1e621e65993625e3 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 4aadab21c4cf..1462e0f9a8d9 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 f0390f64a48f41b3f48191e01e1bdde571dfef03 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 89f5ac8b64afcccd4625ba809417f2fbcc6fa52e 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 cc112bdc4e894ddc0a87fef3aa9aa6b774f52f5c 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 268c059a8ed2..5a2b24b06a7d 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 32962253908f..68b178a52331 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, @@ -47,11 +48,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 70b9104d899edd1f633394af61181f20524b2d0a 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 a27ff8f5cdccbb3359457650f2bbf130fae1342d 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 cbdb0de67ede..1cb877567ac1 100644 --- a/source3/utils/status.c +++ b/source3/utils/status.c @@ -548,6 +548,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 cc6b0862e09652a7a3c412ea8c8d7aa236371949 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 1cb877567ac1..e68fd09f4971 100644 --- a/source3/utils/status.c +++ b/source3/utils/status.c @@ -482,9 +482,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); @@ -537,7 +557,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"; @@ -558,10 +580,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"; @@ -579,7 +607,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) { @@ -589,7 +621,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 22adac29adffb2d7080434c8a8a8e8a688149514 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 69023fcc50a1..f92721a2c187 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -549,6 +549,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 5a595313cd0f..886e6abced81 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -491,6 +491,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 e15a83d765a6..47bed6528ad2 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 5bd01c77e053..1fad6e54e366 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 a6ace21ed83a83578d2bb6591b603384275c5a0f 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 e68fd09f4971..38a534948c57 100644 --- a/source3/utils/status.c +++ b/source3/utils/status.c @@ -492,6 +492,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 { @@ -499,6 +501,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 { @@ -585,6 +589,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) || @@ -612,6 +621,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) { @@ -654,6 +668,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 { @@ -661,6 +677,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 { @@ -795,6 +813,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) || @@ -822,6 +845,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 79cb1dfe1e41..850fc67e5513 100644 --- a/source3/utils/status_json.c +++ b/source3/utils/status_json.c @@ -257,6 +257,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