From 121e7b0e39478c5291100652ac92c263f406076b Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Thu, 22 Dec 2022 14:03:23 +0100 Subject: CVE-2022-38023 s4:rpc_server/netlogon: Move schannel and credentials check functions to librpc Will be used later by s3 netlogon server. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15240 Signed-off-by: Samuel Cabrero Reviewed-by: Andreas Schneider --- librpc/rpc/server/netlogon/schannel_util.c | 576 ++++++++++++++++++++++++++ librpc/rpc/server/netlogon/schannel_util.h | 54 +++ librpc/wscript_build | 12 + source4/rpc_server/netlogon/dcerpc_netlogon.c | 546 +----------------------- source4/rpc_server/wscript_build | 2 +- 5 files changed, 644 insertions(+), 546 deletions(-) create mode 100644 librpc/rpc/server/netlogon/schannel_util.c create mode 100644 librpc/rpc/server/netlogon/schannel_util.h diff --git a/librpc/rpc/server/netlogon/schannel_util.c b/librpc/rpc/server/netlogon/schannel_util.c new file mode 100644 index 00000000000..9b2a88a2628 --- /dev/null +++ b/librpc/rpc/server/netlogon/schannel_util.c @@ -0,0 +1,576 @@ +/* + Unix SMB/CIFS implementation. + + netlogon schannel utility functions + + Copyright (C) Andrew Bartlett 2004-2008 + Copyright (C) Stefan Metzmacher 2005 + Copyright (C) Matthias Dieter Wallnöfer 2009-2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "schannel_util.h" +#include "param/param.h" +#include "libcli/security/dom_sid.h" +#include "libcli/auth/schannel.h" +#include "librpc/rpc/dcesrv_core.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "lib/util/util_str_escape.h" + +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; + + bool seal_global_required; + bool seal_required; + bool seal_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; + int schannel = lpcfg_server_schannel(lp_ctx); + bool schannel_global_required = (schannel == true); + bool schannel_required = schannel_global_required; + const char *explicit_opt = NULL; + bool global_require_seal = lpcfg_server_schannel_require_seal(lp_ctx); + bool require_seal = global_require_seal; + const char *explicit_seal_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_seal_opt = lpcfg_get_parametric(lp_ctx, + NULL, + "server schannel require seal", + creds->account_name); + if (explicit_seal_opt != NULL) { + require_seal = lp_bool(explicit_seal_opt); + } + + /* + * 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; + + s->seal_global_required = global_require_seal; + s->seal_required = require_seal; + s->seal_explicitly_set = explicit_seal_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); + int CVE_2022_38023_warn_level = lpcfg_parm_int(lp_ctx, NULL, + "CVE_2022_38023", "warn_about_unused_debug_level", DBGLVL_ERR); + int CVE_2022_38023_error_level = lpcfg_parm_int(lp_ctx, NULL, + "CVE_2022_38023", "error_debug_level", DBGLVL_ERR); + TALLOC_CTX *frame = talloc_stackframe(); + unsigned int dbg_lvl = DBGLVL_DEBUG; + const char *opname = ""; + const char *reason = ""; + + if (opnum < ndr_table_netlogon.num_calls) { + opname = ndr_table_netlogon.calls[opnum].name; + } + + if (s->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { + if (s->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + reason = "WITH SEALED"; + } else if (s->auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) { + reason = "WITH SIGNED"; + } else { + reason = "WITH INVALID"; + dbg_lvl = DBGLVL_ERR; + s->result = NT_STATUS_INTERNAL_ERROR; + } + } else { + reason = "WITHOUT"; + } + + 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)/CVE-2022-38023: " + "%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 (s->auth_type == DCERPC_AUTH_TYPE_SCHANNEL && + s->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) + { + s->result = NT_STATUS_OK; + + if (s->schannel_explicitly_set && !s->schannel_required) { + dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_warn_level); + } else if (!s->schannel_required) { + dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO); + } + if (s->seal_explicitly_set && !s->seal_required) { + dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_warn_level); + } else if (!s->seal_required) { + dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO); + } + + DEBUG(dbg_lvl, ( + "CVE-2020-1472(ZeroLogon)/CVE-2022-38023: " + "%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))); + + 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", + log_escape(frame, creds->account_name), + log_escape(frame, creds->computer_name))); + } + + if (s->seal_explicitly_set && !s->seal_required) { + DEBUG(CVE_2022_38023_warn_level, ( + "CVE-2022-38023: " + "Option 'server schannel require seal:%s = no' not needed for '%s'!\n", + log_escape(frame, creds->account_name), + log_escape(frame, creds->computer_name))); + } + + TALLOC_FREE(frame); + return s->result; + } + + if (s->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { + if (s->seal_required) { + s->result = NT_STATUS_ACCESS_DENIED; + + if (s->seal_explicitly_set) { + dbg_lvl = DBGLVL_NOTICE; + } else { + dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_error_level); + } + if (s->schannel_explicitly_set && !s->schannel_required) { + dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_warn_level); + } + + DEBUG(dbg_lvl, ( + "CVE-2022-38023: " + "%s request (opnum[%u]) %s schannel from " + "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))); + if (s->seal_explicitly_set) { + D_NOTICE("CVE-2022-38023: Option " + "'server schannel require seal:%s = yes' " + "rejects access for client.\n", + log_escape(frame, creds->account_name)); + } else { + DEBUG(CVE_2020_1472_error_level, ( + "CVE-2022-38023: Check if option " + "'server schannel require seal:%s = no' " + "might be needed for a legacy client.\n", + log_escape(frame, creds->account_name))); + } + 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", + log_escape(frame, creds->account_name), + log_escape(frame, creds->computer_name))); + } + TALLOC_FREE(frame); + return s->result; + } + + s->result = NT_STATUS_OK; + + if (s->schannel_explicitly_set && !s->schannel_required) { + dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_warn_level); + } else if (!s->schannel_required) { + dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO); + } + if (s->seal_explicitly_set && !s->seal_required) { + dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO); + } else if (!s->seal_required) { + dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_error_level); + } + + 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))); + 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", + log_escape(frame, creds->account_name), + log_escape(frame, creds->computer_name))); + } + if (s->seal_explicitly_set && !s->seal_required) { + D_INFO("CVE-2022-38023: " + "Option 'server schannel require seal:%s = no' still needed for '%s'!\n", + log_escape(frame, creds->account_name), + log_escape(frame, creds->computer_name)); + } else if (!s->seal_required) { + /* + * admins should set + * server schannel require seal:COMPUTER$ = no + * in order to avoid the level 0 messages. + * Over time they can switch the global value + * to be strict. + */ + DEBUG(CVE_2022_38023_error_level, ( + "CVE-2022-38023: " + "Please use 'server schannel require seal:%s = no' " + "for '%s' to avoid this warning!\n", + log_escape(frame, creds->account_name), + log_escape(frame, creds->computer_name))); + } + + TALLOC_FREE(frame); + return s->result; + } + + if (s->seal_required) { + s->result = NT_STATUS_ACCESS_DENIED; + + if (s->seal_explicitly_set) { + dbg_lvl = MIN(dbg_lvl, DBGLVL_NOTICE); + } else { + dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_error_level); + } + if (!s->schannel_explicitly_set) { + dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_error_level); + } else if (s->schannel_required) { + dbg_lvl = MIN(dbg_lvl, DBGLVL_NOTICE); + } + + DEBUG(dbg_lvl, ( + "CVE-2020-1472(ZeroLogon)/CVE-2022-38023: " + "%s request (opnum[%u]) %s schannel from " + "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))); + if (s->seal_explicitly_set) { + D_NOTICE("CVE-2022-38023: Option " + "'server schannel require seal:%s = yes' " + "rejects access for client.\n", + log_escape(frame, creds->account_name)); + } else { + DEBUG(CVE_2022_38023_error_level, ( + "CVE-2022-38023: Check if option " + "'server schannel require seal:%s = no' " + "might be needed for a legacy client.\n", + log_escape(frame, creds->account_name))); + } + if (!s->schannel_explicitly_set) { + DEBUG(CVE_2020_1472_error_level, ( + "CVE-2020-1472(ZeroLogon): Check if option " + "'server require schannel:%s = no' " + "might be needed for a legacy client.\n", + log_escape(frame, creds->account_name))); + } else if (s->schannel_required) { + D_NOTICE("CVE-2022-38023: Option " + "'server require schannel:%s = yes' " + "also rejects access for client.\n", + log_escape(frame, creds->account_name)); + } + TALLOC_FREE(frame); + return s->result; + } + + if (s->schannel_required) { + s->result = NT_STATUS_ACCESS_DENIED; + + if (s->schannel_explicitly_set) { + dbg_lvl = MIN(dbg_lvl, DBGLVL_NOTICE); + } else { + dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_error_level); + } + if (!s->seal_explicitly_set) { + dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_error_level); + } + + DEBUG(dbg_lvl, ( + "CVE-2020-1472(ZeroLogon)/CVE-2022-38023: " + "%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))); + if (s->schannel_explicitly_set) { + D_NOTICE("CVE-2020-1472(ZeroLogon): Option " + "'server require schannel:%s = yes' " + "rejects access for client.\n", + log_escape(frame, creds->account_name)); + } else { + DEBUG(CVE_2020_1472_error_level, ( + "CVE-2020-1472(ZeroLogon): Check if option " + "'server require schannel:%s = no' " + "might be needed for a legacy client.\n", + log_escape(frame, creds->account_name))); + } + if (!s->seal_explicitly_set) { + DEBUG(CVE_2022_38023_error_level, ( + "CVE-2022-38023: Check if option " + "'server schannel require seal:%s = no' " + "might be needed for a legacy client.\n", + log_escape(frame, creds->account_name))); + } + TALLOC_FREE(frame); + return s->result; + } + + s->result = NT_STATUS_OK; + + if (s->seal_explicitly_set) { + dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO); + } else { + dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_error_level); + } + + if (s->schannel_explicitly_set) { + dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO); + } else { + dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_error_level); + } + + DEBUG(dbg_lvl, ( + "CVE-2020-1472(ZeroLogon)/CVE-2022-38023: " + "%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))); + + if (s->seal_explicitly_set) { + D_INFO("CVE-2022-38023: Option " + "'server schannel require seal:%s = no' " + "still needed for '%s'!\n", + log_escape(frame, creds->account_name), + log_escape(frame, creds->computer_name)); + } else { + /* + * admins should set + * server schannel require seal:COMPUTER$ = no + * in order to avoid the level 0 messages. + * Over time they can switch the global value + * to be strict. + */ + DEBUG(CVE_2022_38023_error_level, ( + "CVE-2022-38023: Please use " + "'server schannel require seal:%s = no' " + "for '%s' to avoid this warning!\n", + log_escape(frame, creds->account_name), + log_escape(frame, creds->computer_name))); + } + + if (s->schannel_explicitly_set) { + D_INFO("CVE-2020-1472(ZeroLogon): Option " + "'server require schannel:%s = no' " + "still needed for '%s'!\n", + log_escape(frame, creds->account_name), + log_escape(frame, creds->computer_name)); + } else { + /* + * admins should set + * server require schannel:COMPUTER$ = no + * in order to avoid the level 0 messages. + * Over time they can switch the global value + * to be strict. + */ + DEBUG(CVE_2020_1472_error_level, ( + "CVE-2020-1472(ZeroLogon): " + "Please use 'server require schannel:%s = no' " + "for '%s' to avoid this warning!\n", + log_escape(frame, creds->account_name), + log_escape(frame, creds->computer_name))); + } + + TALLOC_FREE(frame); + return s->result; +} + +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; +} + +/* + * NOTE: The following functions are nearly identical to the ones available in + * source3/rpc_server/srv_nelog_nt.c + * The reason we keep 2 copies is that they use different structures to + * represent the auth_info and the decrpc pipes. + */ +NTSTATUS dcesrv_netr_creds_server_step_check(struct dcesrv_call_state *dce_call, + TALLOC_CTX *mem_ctx, + const char *computer_name, + struct netr_Authenticator *received_authenticator, + struct netr_Authenticator *return_authenticator, + struct netlogon_creds_CredentialState **creds_out) +{ + NTSTATUS nt_status; + struct netlogon_creds_CredentialState *creds = NULL; + enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE; + enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE; + + dcesrv_call_auth_info(dce_call, &auth_type, &auth_level); + + nt_status = schannel_check_creds_state(mem_ctx, + dce_call->conn->dce_ctx->lp_ctx, + computer_name, + received_authenticator, + return_authenticator, + &creds); + if (!NT_STATUS_IS_OK(nt_status)) { + ZERO_STRUCTP(return_authenticator); + return nt_status; + } + + nt_status = dcesrv_netr_check_schannel(dce_call, + creds, + auth_type, + auth_level, + dce_call->pkt.u.request.opnum); + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(creds); + ZERO_STRUCTP(return_authenticator); + return nt_status; + } + + *creds_out = creds; + return NT_STATUS_OK; +} diff --git a/librpc/rpc/server/netlogon/schannel_util.h b/librpc/rpc/server/netlogon/schannel_util.h new file mode 100644 index 00000000000..561e2567e02 --- /dev/null +++ b/librpc/rpc/server/netlogon/schannel_util.h @@ -0,0 +1,54 @@ +/* + Unix SMB/CIFS implementation. + + netlogon schannel utility functions + + Copyright (C) Andrew Bartlett 2004-2008 + Copyright (C) Stefan Metzmacher 2005 + Copyright (C) Matthias Dieter Wallnöfer 2009-2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __LIBRPC_RPC_SERVER_NETLOGON_SCHANNEL_UTIL_H__ +#define __LIBRPC_RPC_SERVER_NETLOGON_SCHANNEL_UTIL_H__ + +#include "replace.h" +#include +#include "libcli/util/ntstatus.h" + +#define NETLOGON_SERVER_PIPE_STATE_MAGIC 0x4f555358 + +struct dcesrv_call_state; +struct netlogon_creds_CredentialState; +struct netr_Authenticator; +enum dcerpc_AuthType; +enum dcerpc_AuthLevel; + +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); + +NTSTATUS dcesrv_netr_creds_server_step_check( + struct dcesrv_call_state *dce_call, + TALLOC_CTX *mem_ctx, + const char *computer_name, + struct netr_Authenticator *received_authenticator, + struct netr_Authenticator *return_authenticator, + struct netlogon_creds_CredentialState **creds_out); + +#endif /* __LIBRPC_RPC_SERVER_NETLOGON_SCHANNEL_UTIL_H__ */ diff --git a/librpc/wscript_build b/librpc/wscript_build index 698ccddd093..eef57d47844 100644 --- a/librpc/wscript_build +++ b/librpc/wscript_build @@ -682,6 +682,18 @@ bld.SAMBA_LIBRARY('dcerpc-pkt-auth', ''', deps='dcerpc-binding gensec') +bld.SAMBA_SUBSYSTEM('DCERPC_SERVER_NETLOGON', + source=''' + rpc/server/netlogon/schannel_util.c + ''', + deps=''' + talloc + util_str_escape + samba-hostconfig + NDR_NETLOGON + dcerpc-server-core + ''') + bld.SAMBA_LIBRARY('dcerpc-server-core', source=''' rpc/dcesrv_core.c diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index aa24a539abf..314b469a718 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -42,6 +42,7 @@ #include "librpc/gen_ndr/ndr_irpc.h" #include "librpc/gen_ndr/ndr_winbind.h" #include "librpc/gen_ndr/ndr_winbind_c.h" +#include "librpc/rpc/server/netlogon/schannel_util.h" #include "lib/socket/netif.h" #include "lib/util/util_str_escape.h" #include "lib/param/loadparm.h" @@ -889,551 +890,6 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate2(struct dcesrv_call_state *dce_ca return dcesrv_netr_ServerAuthenticate3(dce_call, mem_ctx, &r3); } -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; - - bool seal_global_required; - bool seal_required; - bool seal_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; - int schannel = lpcfg_server_schannel(lp_ctx); - bool schannel_global_required = (schannel == true); - bool schannel_required = schannel_global_required; - const char *explicit_opt = NULL; - bool global_require_seal = lpcfg_server_schannel_require_seal(lp_ctx); - bool require_seal = global_require_seal; - const char *explicit_seal_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_seal_opt = lpcfg_get_parametric(lp_ctx, - NULL, - "server schannel require seal", - creds->account_name); - if (explicit_seal_opt != NULL) { - require_seal = lp_bool(explicit_seal_opt); - } - - /* - * 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; - - s->seal_global_required = global_require_seal; - s->seal_required = require_seal; - s->seal_explicitly_set = explicit_seal_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); - int CVE_2022_38023_warn_level = lpcfg_parm_int(lp_ctx, NULL, - "CVE_2022_38023", "warn_about_unused_debug_level", DBGLVL_ERR); - int CVE_2022_38023_error_level = lpcfg_parm_int(lp_ctx, NULL, - "CVE_2022_38023", "error_debug_level", DBGLVL_ERR); - TALLOC_CTX *frame = talloc_stackframe(); - unsigned int dbg_lvl = DBGLVL_DEBUG; - const char *opname = ""; - const char *reason = ""; - - if (opnum < ndr_table_netlogon.num_calls) { - opname = ndr_table_netlogon.calls[opnum].name; - } - - if (s->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { - if (s->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { - reason = "WITH SEALED"; - } else if (s->auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) { - reason = "WITH SIGNED"; - } else { - reason = "WITH INVALID"; - dbg_lvl = DBGLVL_ERR; - s->result = NT_STATUS_INTERNAL_ERROR; - } - } else { - reason = "WITHOUT"; - } - - 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)/CVE-2022-38023: " - "%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 (s->auth_type == DCERPC_AUTH_TYPE_SCHANNEL && - s->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) - { - s->result = NT_STATUS_OK; - - if (s->schannel_explicitly_set && !s->schannel_required) { - dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_warn_level); - } else if (!s->schannel_required) { - dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO); - } - if (s->seal_explicitly_set && !s->seal_required) { - dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_warn_level); - } else if (!s->seal_required) { - dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO); - } - - DEBUG(dbg_lvl, ( - "CVE-2020-1472(ZeroLogon)/CVE-2022-38023: " - "%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))); - - 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", - log_escape(frame, creds->account_name), - log_escape(frame, creds->computer_name))); - } - - if (s->seal_explicitly_set && !s->seal_required) { - DEBUG(CVE_2022_38023_warn_level, ( - "CVE-2022-38023: " - "Option 'server schannel require seal:%s = no' not needed for '%s'!\n", - log_escape(frame, creds->account_name), - log_escape(frame, creds->computer_name))); - } - - TALLOC_FREE(frame); - return s->result; - } - - if (s->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { - if (s->seal_required) { - s->result = NT_STATUS_ACCESS_DENIED; - - if (s->seal_explicitly_set) { - dbg_lvl = DBGLVL_NOTICE; - } else { - dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_error_level); - } - if (s->schannel_explicitly_set && !s->schannel_required) { - dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_warn_level); - } - - DEBUG(dbg_lvl, ( - "CVE-2022-38023: " - "%s request (opnum[%u]) %s schannel from " - "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))); - if (s->seal_explicitly_set) { - D_NOTICE("CVE-2022-38023: Option " - "'server schannel require seal:%s = yes' " - "rejects access for client.\n", - log_escape(frame, creds->account_name)); - } else { - DEBUG(CVE_2020_1472_error_level, ( - "CVE-2022-38023: Check if option " - "'server schannel require seal:%s = no' " - "might be needed for a legacy client.\n", - log_escape(frame, creds->account_name))); - } - 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", - log_escape(frame, creds->account_name), - log_escape(frame, creds->computer_name))); - } - TALLOC_FREE(frame); - return s->result; - } - - s->result = NT_STATUS_OK; - - if (s->schannel_explicitly_set && !s->schannel_required) { - dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_warn_level); - } else if (!s->schannel_required) { - dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO); - } - if (s->seal_explicitly_set && !s->seal_required) { - dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO); - } else if (!s->seal_required) { - dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_error_level); - } - - 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))); - 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", - log_escape(frame, creds->account_name), - log_escape(frame, creds->computer_name))); - } - if (s->seal_explicitly_set && !s->seal_required) { - D_INFO("CVE-2022-38023: " - "Option 'server schannel require seal:%s = no' still needed for '%s'!\n", - log_escape(frame, creds->account_name), - log_escape(frame, creds->computer_name)); - } else if (!s->seal_required) { - /* - * admins should set - * server schannel require seal:COMPUTER$ = no - * in order to avoid the level 0 messages. - * Over time they can switch the global value - * to be strict. - */ - DEBUG(CVE_2022_38023_error_level, ( - "CVE-2022-38023: " - "Please use 'server schannel require seal:%s = no' " - "for '%s' to avoid this warning!\n", - log_escape(frame, creds->account_name), - log_escape(frame, creds->computer_name))); - } - - TALLOC_FREE(frame); - return s->result; - } - - if (s->seal_required) { - s->result = NT_STATUS_ACCESS_DENIED; - - if (s->seal_explicitly_set) { - dbg_lvl = MIN(dbg_lvl, DBGLVL_NOTICE); - } else { - dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_error_level); - } - if (!s->schannel_explicitly_set) { - dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_error_level); - } else if (s->schannel_required) { - dbg_lvl = MIN(dbg_lvl, DBGLVL_NOTICE); - } - - DEBUG(dbg_lvl, ( - "CVE-2020-1472(ZeroLogon)/CVE-2022-38023: " - "%s request (opnum[%u]) %s schannel from " - "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))); - if (s->seal_explicitly_set) { - D_NOTICE("CVE-2022-38023: Option " - "'server schannel require seal:%s = yes' " - "rejects access for client.\n", - log_escape(frame, creds->account_name)); - } else { - DEBUG(CVE_2022_38023_error_level, ( - "CVE-2022-38023: Check if option " - "'server schannel require seal:%s = no' " - "might be needed for a legacy client.\n", - log_escape(frame, creds->account_name))); - } - if (!s->schannel_explicitly_set) { - DEBUG(CVE_2020_1472_error_level, ( - "CVE-2020-1472(ZeroLogon): Check if option " - "'server require schannel:%s = no' " - "might be needed for a legacy client.\n", - log_escape(frame, creds->account_name))); - } else if (s->schannel_required) { - D_NOTICE("CVE-2022-38023: Option " - "'server require schannel:%s = yes' " - "also rejects access for client.\n", - log_escape(frame, creds->account_name)); - } - TALLOC_FREE(frame); - return s->result; - } - - if (s->schannel_required) { - s->result = NT_STATUS_ACCESS_DENIED; - - if (s->schannel_explicitly_set) { - dbg_lvl = MIN(dbg_lvl, DBGLVL_NOTICE); - } else { - dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_error_level); - } - if (!s->seal_explicitly_set) { - dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_error_level); - } - - DEBUG(dbg_lvl, ( - "CVE-2020-1472(ZeroLogon)/CVE-2022-38023: " - "%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))); - if (s->schannel_explicitly_set) { - D_NOTICE("CVE-2020-1472(ZeroLogon): Option " - "'server require schannel:%s = yes' " - "rejects access for client.\n", - log_escape(frame, creds->account_name)); - } else { - DEBUG(CVE_2020_1472_error_level, ( - "CVE-2020-1472(ZeroLogon): Check if option " - "'server require schannel:%s = no' " - "might be needed for a legacy client.\n", - log_escape(frame, creds->account_name))); - } - if (!s->seal_explicitly_set) { - DEBUG(CVE_2022_38023_error_level, ( - "CVE-2022-38023: Check if option " - "'server schannel require seal:%s = no' " - "might be needed for a legacy client.\n", - log_escape(frame, creds->account_name))); - } - TALLOC_FREE(frame); - return s->result; - } - - s->result = NT_STATUS_OK; - - if (s->seal_explicitly_set) { - dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO); - } else { - dbg_lvl = MIN(dbg_lvl, CVE_2022_38023_error_level); - } - - if (s->schannel_explicitly_set) { - dbg_lvl = MIN(dbg_lvl, DBGLVL_INFO); - } else { - dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_error_level); - } - - DEBUG(dbg_lvl, ( - "CVE-2020-1472(ZeroLogon)/CVE-2022-38023: " - "%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))); - - if (s->seal_explicitly_set) { - D_INFO("CVE-2022-38023: Option " - "'server schannel require seal:%s = no' " - "still needed for '%s'!\n", - log_escape(frame, creds->account_name), - log_escape(frame, creds->computer_name)); - } else { - /* - * admins should set - * server schannel require seal:COMPUTER$ = no - * in order to avoid the level 0 messages. - * Over time they can switch the global value - * to be strict. - */ - DEBUG(CVE_2022_38023_error_level, ( - "CVE-2022-38023: Please use " - "'server schannel require seal:%s = no' " - "for '%s' to avoid this warning!\n", - log_escape(frame, creds->account_name), - log_escape(frame, creds->computer_name))); - } - - if (s->schannel_explicitly_set) { - D_INFO("CVE-2020-1472(ZeroLogon): Option " - "'server require schannel:%s = no' " - "still needed for '%s'!\n", - log_escape(frame, creds->account_name), - log_escape(frame, creds->computer_name)); - } else { - /* - * admins should set - * server require schannel:COMPUTER$ = no - * in order to avoid the level 0 messages. - * Over time they can switch the global value - * to be strict. - */ - DEBUG(CVE_2020_1472_error_level, ( - "CVE-2020-1472(ZeroLogon): " - "Please use 'server require schannel:%s = no' " - "for '%s' to avoid this warning!\n", - log_escape(frame, creds->account_name), - log_escape(frame, creds->computer_name))); - } - - 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; -} - -/* - * NOTE: The following functions are nearly identical to the ones available in - * source3/rpc_server/srv_nelog_nt.c - * The reason we keep 2 copies is that they use different structures to - * represent the auth_info and the decrpc pipes. - */ -static NTSTATUS dcesrv_netr_creds_server_step_check(struct dcesrv_call_state *dce_call, - TALLOC_CTX *mem_ctx, - const char *computer_name, - struct netr_Authenticator *received_authenticator, - struct netr_Authenticator *return_authenticator, - struct netlogon_creds_CredentialState **creds_out) -{ - NTSTATUS nt_status; - struct netlogon_creds_CredentialState *creds = NULL; - enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE; - enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE; - - dcesrv_call_auth_info(dce_call, &auth_type, &auth_level); - - nt_status = schannel_check_creds_state(mem_ctx, - dce_call->conn->dce_ctx->lp_ctx, - computer_name, - received_authenticator, - return_authenticator, - &creds); - if (!NT_STATUS_IS_OK(nt_status)) { - ZERO_STRUCTP(return_authenticator); - return nt_status; - } - - nt_status = dcesrv_netr_check_schannel(dce_call, - creds, - auth_type, - auth_level, - dce_call->pkt.u.request.opnum); - if (!NT_STATUS_IS_OK(nt_status)) { - TALLOC_FREE(creds); - ZERO_STRUCTP(return_authenticator); - return nt_status; - } - - *creds_out = creds; - return NT_STATUS_OK; -} - /* Change the machine account password for the currently connected client. Supplies only the NT#. diff --git a/source4/rpc_server/wscript_build b/source4/rpc_server/wscript_build index e7bb773d719..0e44a3c2bae 100644 --- a/source4/rpc_server/wscript_build +++ b/source4/rpc_server/wscript_build @@ -118,10 +118,10 @@ bld.SAMBA_MODULE('dcerpc_netlogon', samba-hostconfig DSDB_MODULE_HELPERS util_str_escape + DCERPC_SERVER_NETLOGON ''' ) - bld.SAMBA_MODULE('dcerpc_lsarpc', source='lsa/dcesrv_lsa.c lsa/lsa_init.c lsa/lsa_lookup.c', autoproto='lsa/proto.h', -- cgit v1.2.1