306 lines
9.8 KiB
Diff
306 lines
9.8 KiB
Diff
From a29e855c113c5af16ea9d108c1ee0f869d5f0f3e Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 2 Dec 2022 14:31:26 +0100
|
|
Subject: [PATCH 27/30] CVE-2022-38023 s4:rpc_server/netlogon: add a per
|
|
connection cache to dcesrv_netr_check_schannel()
|
|
|
|
It's enough to warn the admin once per connection.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15240
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(cherry picked from commit 3c57608e1109c1d6e8bb8fbad2ef0b5d79d00e1a)
|
|
|
|
Conflict: NA
|
|
Reference: https://attachments.samba.org/attachment.cgi?id=17692
|
|
---
|
|
source4/rpc_server/netlogon/dcerpc_netlogon.c | 193 ++++++++++++++----
|
|
1 file changed, 153 insertions(+), 40 deletions(-)
|
|
|
|
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
|
|
index a9b16e5c42a3..45dcec133fb3 100644
|
|
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
|
|
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
|
|
@@ -877,23 +877,105 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate2(struct dcesrv_call_state *dce_ca
|
|
return dcesrv_netr_ServerAuthenticate3(dce_call, mem_ctx, &r3);
|
|
}
|
|
|
|
-static NTSTATUS dcesrv_netr_check_schannel(struct dcesrv_call_state *dce_call,
|
|
- const struct netlogon_creds_CredentialState *creds,
|
|
- enum dcerpc_AuthType auth_type,
|
|
- enum dcerpc_AuthLevel auth_level,
|
|
- uint16_t opnum)
|
|
+struct dcesrv_netr_check_schannel_state {
|
|
+ struct dom_sid account_sid;
|
|
+ enum dcerpc_AuthType auth_type;
|
|
+ enum dcerpc_AuthLevel auth_level;
|
|
+
|
|
+ bool schannel_global_required;
|
|
+ bool schannel_required;
|
|
+ bool schannel_explicitly_set;
|
|
+
|
|
+ NTSTATUS result;
|
|
+};
|
|
+
|
|
+static NTSTATUS dcesrv_netr_check_schannel_get_state(struct dcesrv_call_state *dce_call,
|
|
+ const struct netlogon_creds_CredentialState *creds,
|
|
+ enum dcerpc_AuthType auth_type,
|
|
+ enum dcerpc_AuthLevel auth_level,
|
|
+ struct dcesrv_netr_check_schannel_state **_s)
|
|
{
|
|
struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
|
|
- TALLOC_CTX *frame = talloc_stackframe();
|
|
- NTSTATUS nt_status;
|
|
int schannel = lpcfg_server_schannel(lp_ctx);
|
|
bool schannel_global_required = (schannel == true);
|
|
bool schannel_required = schannel_global_required;
|
|
const char *explicit_opt = NULL;
|
|
+#define DCESRV_NETR_CHECK_SCHANNEL_STATE_MAGIC (NETLOGON_SERVER_PIPE_STATE_MAGIC+1)
|
|
+ struct dcesrv_netr_check_schannel_state *s = NULL;
|
|
+ NTSTATUS status;
|
|
+
|
|
+ *_s = NULL;
|
|
+
|
|
+ s = dcesrv_iface_state_find_conn(dce_call,
|
|
+ DCESRV_NETR_CHECK_SCHANNEL_STATE_MAGIC,
|
|
+ struct dcesrv_netr_check_schannel_state);
|
|
+ if (s != NULL) {
|
|
+ if (!dom_sid_equal(&s->account_sid, creds->sid)) {
|
|
+ goto new_state;
|
|
+ }
|
|
+ if (s->auth_type != auth_type) {
|
|
+ goto new_state;
|
|
+ }
|
|
+ if (s->auth_level != auth_level) {
|
|
+ goto new_state;
|
|
+ }
|
|
+
|
|
+ *_s = s;
|
|
+ return NT_STATUS_OK;
|
|
+ }
|
|
+
|
|
+new_state:
|
|
+ TALLOC_FREE(s);
|
|
+ s = talloc_zero(dce_call,
|
|
+ struct dcesrv_netr_check_schannel_state);
|
|
+ if (s == NULL) {
|
|
+ return NT_STATUS_NO_MEMORY;
|
|
+ }
|
|
+
|
|
+ s->account_sid = *creds->sid;
|
|
+ s->auth_type = auth_type;
|
|
+ s->auth_level = auth_level;
|
|
+ s->result = NT_STATUS_MORE_PROCESSING_REQUIRED;
|
|
+
|
|
+ /*
|
|
+ * We don't use lpcfg_parm_bool(), as we
|
|
+ * need the explicit_opt pointer in order to
|
|
+ * adjust the debug messages.
|
|
+ */
|
|
+ explicit_opt = lpcfg_get_parametric(lp_ctx,
|
|
+ NULL,
|
|
+ "server require schannel",
|
|
+ creds->account_name);
|
|
+ if (explicit_opt != NULL) {
|
|
+ schannel_required = lp_bool(explicit_opt);
|
|
+ }
|
|
+
|
|
+ s->schannel_global_required = schannel_global_required;
|
|
+ s->schannel_required = schannel_required;
|
|
+ s->schannel_explicitly_set = explicit_opt != NULL;
|
|
+
|
|
+ status = dcesrv_iface_state_store_conn(dce_call,
|
|
+ DCESRV_NETR_CHECK_SCHANNEL_STATE_MAGIC,
|
|
+ s);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ *_s = s;
|
|
+ return NT_STATUS_OK;
|
|
+}
|
|
+
|
|
+static NTSTATUS dcesrv_netr_check_schannel_once(struct dcesrv_call_state *dce_call,
|
|
+ struct dcesrv_netr_check_schannel_state *s,
|
|
+ const struct netlogon_creds_CredentialState *creds,
|
|
+ uint16_t opnum)
|
|
+{
|
|
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
|
|
int CVE_2020_1472_warn_level = lpcfg_parm_int(lp_ctx, NULL,
|
|
"CVE_2020_1472", "warn_about_unused_debug_level", DBGLVL_ERR);
|
|
int CVE_2020_1472_error_level = lpcfg_parm_int(lp_ctx, NULL,
|
|
"CVE_2020_1472", "error_debug_level", DBGLVL_ERR);
|
|
+ TALLOC_CTX *frame = talloc_stackframe();
|
|
unsigned int dbg_lvl = DBGLVL_DEBUG;
|
|
const char *opname = "<unknown>";
|
|
const char *reason = "<unknown>";
|
|
@@ -902,37 +984,43 @@ static NTSTATUS dcesrv_netr_check_schannel(struct dcesrv_call_state *dce_call,
|
|
opname = ndr_table_netlogon.calls[opnum].name;
|
|
}
|
|
|
|
- if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
|
|
- if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
|
|
+ if (s->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
|
|
+ if (s->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
|
|
reason = "WITH SEALED";
|
|
- } else if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) {
|
|
+ } else if (s->auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) {
|
|
reason = "WITH SIGNED";
|
|
} else {
|
|
- smb_panic("Schannel without SIGN/SEAL");
|
|
+ reason = "WITH INVALID";
|
|
+ dbg_lvl = DBGLVL_ERR;
|
|
+ s->result = NT_STATUS_INTERNAL_ERROR;
|
|
}
|
|
} else {
|
|
reason = "WITHOUT";
|
|
}
|
|
|
|
- /*
|
|
- * We don't use lpcfg_parm_bool(), as we
|
|
- * need the explicit_opt pointer in order to
|
|
- * adjust the debug messages.
|
|
- */
|
|
- explicit_opt = lpcfg_get_parametric(lp_ctx,
|
|
- NULL,
|
|
- "server require schannel",
|
|
- creds->account_name);
|
|
- if (explicit_opt != NULL) {
|
|
- schannel_required = lp_bool(explicit_opt);
|
|
+ if (!NT_STATUS_EQUAL(s->result, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
|
+ if (!NT_STATUS_IS_OK(s->result)) {
|
|
+ dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO);
|
|
+ }
|
|
+
|
|
+ DEBUG(dbg_lvl, (
|
|
+ "CVE-2020-1472(ZeroLogon): "
|
|
+ "%s request (opnum[%u]) %s schannel from "
|
|
+ "client_account[%s] client_computer_name[%s] %s\n",
|
|
+ opname, opnum, reason,
|
|
+ log_escape(frame, creds->account_name),
|
|
+ log_escape(frame, creds->computer_name),
|
|
+ nt_errstr(s->result)));
|
|
+ TALLOC_FREE(frame);
|
|
+ return s->result;
|
|
}
|
|
|
|
- if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
|
|
- nt_status = NT_STATUS_OK;
|
|
+ if (s->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
|
|
+ s->result = NT_STATUS_OK;
|
|
|
|
- if (explicit_opt != NULL && !schannel_required) {
|
|
+ if (s->schannel_explicitly_set && !s->schannel_required) {
|
|
dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_warn_level);
|
|
- } else if (!schannel_required) {
|
|
+ } else if (!s->schannel_required) {
|
|
dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO);
|
|
}
|
|
|
|
@@ -943,9 +1031,8 @@ static NTSTATUS dcesrv_netr_check_schannel(struct dcesrv_call_state *dce_call,
|
|
opname, opnum, reason,
|
|
log_escape(frame, creds->account_name),
|
|
log_escape(frame, creds->computer_name),
|
|
- nt_errstr(nt_status)));
|
|
-
|
|
- if (explicit_opt != NULL && !schannel_required) {
|
|
+ nt_errstr(s->result)));
|
|
+ if (s->schannel_explicitly_set && !s->schannel_required) {
|
|
DEBUG(CVE_2020_1472_warn_level, (
|
|
"CVE-2020-1472(ZeroLogon): "
|
|
"Option 'server require schannel:%s = no' not needed for '%s'!\n",
|
|
@@ -954,13 +1041,13 @@ static NTSTATUS dcesrv_netr_check_schannel(struct dcesrv_call_state *dce_call,
|
|
}
|
|
|
|
TALLOC_FREE(frame);
|
|
- return nt_status;
|
|
+ return s->result;
|
|
}
|
|
|
|
- if (schannel_required) {
|
|
- nt_status = NT_STATUS_ACCESS_DENIED;
|
|
+ if (s->schannel_required) {
|
|
+ s->result = NT_STATUS_ACCESS_DENIED;
|
|
|
|
- if (explicit_opt != NULL) {
|
|
+ if (s->schannel_explicitly_set) {
|
|
dbg_lvl = MIN(dbg_lvl, DBGLVL_NOTICE);
|
|
} else {
|
|
dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_error_level);
|
|
@@ -973,8 +1060,8 @@ static NTSTATUS dcesrv_netr_check_schannel(struct dcesrv_call_state *dce_call,
|
|
opname, opnum, reason,
|
|
log_escape(frame, creds->account_name),
|
|
log_escape(frame, creds->computer_name),
|
|
- nt_errstr(nt_status)));
|
|
- if (explicit_opt != NULL) {
|
|
+ nt_errstr(s->result)));
|
|
+ if (s->schannel_explicitly_set) {
|
|
D_NOTICE("CVE-2020-1472(ZeroLogon): Option "
|
|
"'server require schannel:%s = yes' "
|
|
"rejects access for client.\n",
|
|
@@ -987,12 +1074,12 @@ static NTSTATUS dcesrv_netr_check_schannel(struct dcesrv_call_state *dce_call,
|
|
log_escape(frame, creds->account_name)));
|
|
}
|
|
TALLOC_FREE(frame);
|
|
- return nt_status;
|
|
+ return s->result;
|
|
}
|
|
|
|
- nt_status = NT_STATUS_OK;
|
|
+ s->result = NT_STATUS_OK;
|
|
|
|
- if (explicit_opt != NULL) {
|
|
+ if (s->schannel_explicitly_set) {
|
|
dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO);
|
|
} else {
|
|
dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_error_level);
|
|
@@ -1005,9 +1092,9 @@ static NTSTATUS dcesrv_netr_check_schannel(struct dcesrv_call_state *dce_call,
|
|
opname, opnum, reason,
|
|
log_escape(frame, creds->account_name),
|
|
log_escape(frame, creds->computer_name),
|
|
- nt_errstr(nt_status)));
|
|
+ nt_errstr(s->result)));
|
|
|
|
- if (explicit_opt != NULL) {
|
|
+ if (s->schannel_explicitly_set) {
|
|
D_INFO("CVE-2020-1472(ZeroLogon): Option "
|
|
"'server require schannel:%s = no' "
|
|
"still needed for '%s'!\n",
|
|
@@ -1030,6 +1117,32 @@ static NTSTATUS dcesrv_netr_check_schannel(struct dcesrv_call_state *dce_call,
|
|
}
|
|
|
|
TALLOC_FREE(frame);
|
|
+ return s->result;
|
|
+}
|
|
+
|
|
+static NTSTATUS dcesrv_netr_check_schannel(struct dcesrv_call_state *dce_call,
|
|
+ const struct netlogon_creds_CredentialState *creds,
|
|
+ enum dcerpc_AuthType auth_type,
|
|
+ enum dcerpc_AuthLevel auth_level,
|
|
+ uint16_t opnum)
|
|
+{
|
|
+ struct dcesrv_netr_check_schannel_state *s = NULL;
|
|
+ NTSTATUS status;
|
|
+
|
|
+ status = dcesrv_netr_check_schannel_get_state(dce_call,
|
|
+ creds,
|
|
+ auth_type,
|
|
+ auth_level,
|
|
+ &s);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ status = dcesrv_netr_check_schannel_once(dce_call, s, creds, opnum);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ return status;
|
|
+ }
|
|
+
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
--
|
|
2.34.1
|