diff options
author | Stefan Metzmacher <metze@samba.org> | 2022-12-02 14:31:26 +0100 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2022-12-14 10:28:16 +0000 |
commit | 1d9c939ebaa711aba3f58860f2091b7b96e76690 (patch) | |
tree | 9cf94112a6da7ffdd079da5a28107748c8b59dd9 /source4 | |
parent | d04da3d7008d7bcd55eff70400d5834342925013 (diff) | |
download | samba-1d9c939ebaa711aba3f58860f2091b7b96e76690.tar.gz |
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)
Diffstat (limited to 'source4')
-rw-r--r-- | source4/rpc_server/netlogon/dcerpc_netlogon.c | 193 |
1 files changed, 153 insertions, 40 deletions
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index d5bca620b0d..624c8d40724 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; } |