#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"
bool global_reject_md5_client = lpcfg_reject_md5_clients(lp_ctx);
int schannel = lpcfg_server_schannel(lp_ctx);
bool schannel_global_required = (schannel == true);
+ bool global_require_seal = lpcfg_server_schannel_require_seal(lp_ctx);
static bool warned_global_nt4_once = false;
static bool warned_global_md5_once = false;
static bool warned_global_schannel_once = false;
+ static bool warned_global_seal_once = false;
if (global_allow_nt4_crypto && !warned_global_nt4_once) {
/*
warned_global_schannel_once = true;
}
+ if (!global_require_seal && !warned_global_seal_once) {
+ /*
+ * We want admins to notice their misconfiguration!
+ */
+ D_ERR("CVE-2022-38023 (and others): "
+ "Please configure 'server schannel require seal = yes' (the default), "
+ "See https://bugzilla.samba.org/show_bug.cgi?id=15240\n");
+ warned_global_seal_once = true;
+ }
+
return dcesrv_interface_bind_reject_connect(context, iface);
}
reject_des_client = !allow_nt4_crypto;
/*
- * If weak cryto is disabled, do not announce that we support RC4.
+ * If weak crypto is disabled, do not announce that we support RC4.
*/
if (lpcfg_weak_crypto(lp_ctx) == SAMBA_WEAK_CRYPTO_DISALLOWED) {
/* Without RC4 and DES we require AES */
NETLOGON_NEG_AUTHENTICATED_RPC;
/*
- * If weak cryto is disabled, do not announce that we support RC4.
+ * If weak crypto is disabled, do not announce that we support RC4.
*/
if (lpcfg_weak_crypto(dce_call->conn->dce_ctx->lp_ctx) ==
SAMBA_WEAK_CRYPTO_DISALLOWED) {
status,
lpcfg_workgroup(dce_call->conn->dce_ctx->lp_ctx),
trust_account_in_db,
- sid);
+ sid,
+ NULL /* client_audit_info */,
+ NULL /* server_audit_info */);
return status;
}
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 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;
- 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);
- unsigned int dbg_lvl = DBGLVL_DEBUG;
- const char *opname = "<unknown>";
- const char *reason = "<unknown>";
-
- if (opnum < ndr_table_netlogon.num_calls) {
- opname = ndr_table_netlogon.calls[opnum].name;
- }
-
- if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
- if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
- reason = "WITH SEALED";
- } else if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) {
- reason = "WITH SIGNED";
- } else {
- smb_panic("Schannel without SIGN/SEAL");
- }
- } 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 (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
- nt_status = NT_STATUS_OK;
-
- if (explicit_opt != NULL && !schannel_required) {
- dbg_lvl = MIN(dbg_lvl, CVE_2020_1472_warn_level);
- } else if (!schannel_required) {
- 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(nt_status)));
-
- if (explicit_opt != NULL && !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 nt_status;
- }
-
- if (schannel_required) {
- nt_status = NT_STATUS_ACCESS_DENIED;
-
- if (explicit_opt != NULL) {
- dbg_lvl = MIN(dbg_lvl, DBGLVL_NOTICE);
- } 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(nt_status)));
- if (explicit_opt != NULL) {
- 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)));
- }
- TALLOC_FREE(frame);
- return nt_status;
- }
-
- nt_status = NT_STATUS_OK;
-
- if (explicit_opt != NULL) {
- 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(nt_status)));
-
- if (explicit_opt != NULL) {
- 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 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#.
return NT_STATUS_OK;
}
- /* Until we get an implemetnation of these other packages */
+ /* Until we get an implementation of these other packages */
return NT_STATUS_INVALID_PARAMETER;
}
default:
case 2:
nt_status = auth_convert_user_info_dc_saminfo2(mem_ctx,
user_info_dc,
+ AUTH_INCLUDE_RESOURCE_GROUPS,
&sam2);
if (!NT_STATUS_IS_OK(nt_status)) {
r->out.result = nt_status;
case 3:
nt_status = auth_convert_user_info_dc_saminfo3(mem_ctx,
user_info_dc,
- &sam3);
+ AUTH_INCLUDE_RESOURCE_GROUPS,
+ &sam3, NULL);
if (!NT_STATUS_IS_OK(nt_status)) {
r->out.result = nt_status;
dcesrv_netr_LogonSamLogon_base_reply(state);
case 6:
nt_status = auth_convert_user_info_dc_saminfo6(mem_ctx,
user_info_dc,
- &sam6);
+ AUTH_INCLUDE_RESOURCE_GROUPS,
+ &sam6, NULL);
if (!NT_STATUS_IS_OK(nt_status)) {
r->out.result = nt_status;
dcesrv_netr_LogonSamLogon_base_reply(state);
}
/*
- * TODO: Should we also varify that only valid
+ * TODO: Should we also verify that only valid
* netbios name characters are used?
*/
}
struct netlogon_creds_CredentialState *creds;
NTSTATUS status;
+ switch (r->in.query_level) {
+ case 1:
+ break;
+ case 2:
+ /*
+ * Until we know the details behind KB5028166
+ * just return DCERPC_NCA_S_FAULT_INVALID_TAG
+ * like an unpatched Windows Server.
+ */
+ FALL_THROUGH;
+ default:
+ /*
+ * There would not be a way to marshall the
+ * the response. Which would mean our final
+ * ndr_push would fail an we would return
+ * an RPC-level fault with DCERPC_FAULT_BAD_STUB_DATA.
+ *
+ * But it's important to match a Windows server
+ * especially before KB5028166, see also our bug #15418
+ * Otherwise Windows client would stop talking to us.
+ */
+ DCESRV_FAULT(DCERPC_NCA_S_FAULT_INVALID_TAG);
+ }
+
status = dcesrv_netr_creds_server_step_check(dce_call,
mem_ctx,
r->in.computer_name,
}
NT_STATUS_NOT_OK_RETURN(status);
- if (r->in.query_level != 1) {
- return NT_STATUS_NOT_SUPPORTED;
- }
-
r->out.capabilities->server_capabilities = creds->negotiate_flags;
return NT_STATUS_OK;
frame);
local = tsocket_address_string(dce_call->conn->local_address,
frame);
- DBG_ERR(("Bad credentials - "
- "computer[%s] remote[%s] local[%s]\n"),
+ DBG_ERR("Bad credentials - "
+ "computer[%s] remote[%s] local[%s]\n",
log_escape(frame, r->in.computer_name),
remote,
local);
ZERO_STRUCTP(domain_info);
- /* Informations about the local and trusted domains */
+ /* Information about the local and trusted domains */
status = fill_our_one_domain_info(mem_ctx,
our_tdo,
const char *domain_name = NULL;
const char *pdc_ip;
bool different_domain = true;
+ bool force_remote_lookup = false;
uint32_t valid_flags;
+ uint32_t this_dc_valid_flags;
int dc_level;
ZERO_STRUCTP(r->out.info);
* ...
*/
- dc_level = dsdb_dc_functional_level(sam_ctx);
valid_flags = DSGETDC_VALID_FLAGS;
- if (dc_level >= DS_DOMAIN_FUNCTION_2012) {
- valid_flags |= DS_DIRECTORY_SERVICE_8_REQUIRED;
- }
- if (dc_level >= DS_DOMAIN_FUNCTION_2012_R2) {
- valid_flags |= DS_DIRECTORY_SERVICE_9_REQUIRED;
- }
- if (dc_level >= DS_DOMAIN_FUNCTION_2016) {
- valid_flags |= DS_DIRECTORY_SERVICE_10_REQUIRED;
- }
+
if (r->in.flags & ~valid_flags) {
/*
* TODO: add tests to prove this (maybe based on the
different_domain = false;
}
+ if (!different_domain) {
+ dc_level = dsdb_dc_functional_level(sam_ctx);
+
+ /*
+ * Do not return a local response if we do not support the
+ * functional level or feature (eg web services)
+ */
+ this_dc_valid_flags = valid_flags;
+
+ /* Samba does not implement this */
+ this_dc_valid_flags &= ~DS_WEB_SERVICE_REQUIRED;
+
+ if (dc_level < DS_DOMAIN_FUNCTION_2012) {
+ this_dc_valid_flags &= ~DS_DIRECTORY_SERVICE_8_REQUIRED;
+ }
+ if (dc_level < DS_DOMAIN_FUNCTION_2012_R2) {
+ this_dc_valid_flags &= ~DS_DIRECTORY_SERVICE_9_REQUIRED;
+ }
+ if (dc_level < DS_DOMAIN_FUNCTION_2016) {
+ this_dc_valid_flags &= ~DS_DIRECTORY_SERVICE_10_REQUIRED;
+ }
+ if (r->in.flags & ~this_dc_valid_flags) {
+ DBG_INFO("Forcing remote lookup to find another DC "
+ "in this domain %s with more features, "
+ "as this Samba DC is Functional level %d but flags are 0x08%x\n",
+ r->in.domain_name, dc_level, (unsigned int)r->in.flags);
+ force_remote_lookup = true;
+ }
+ }
+
/* Proof server site parameter "site_name" if it was specified */
server_site_name = samdb_server_site_name(sam_ctx, state);
W_ERROR_HAVE_NO_MEMORY(server_site_name);
- if (different_domain || (r->in.site_name != NULL &&
- (strcasecmp_m(r->in.site_name,
- server_site_name) != 0))) {
+ if (force_remote_lookup
+ || different_domain
+ || (r->in.site_name != NULL &&
+ (strcasecmp_m(r->in.site_name,
+ server_site_name) != 0))) {
struct dcerpc_binding_handle *irpc_handle = NULL;
struct tevent_req *subreq = NULL;
return WERR_INVALID_FLAGS;
}
- system_dn = samdb_search_dn(sam_ctx, mem_ctx,
- ldb_get_default_basedn(sam_ctx),
- "(&(objectClass=container)(cn=System))");
- if (!system_dn) {
- return WERR_GEN_FAILURE;
+ system_dn = samdb_system_container_dn(sam_ctx, mem_ctx);
+ if (system_dn == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
}
ret = gendb_search(sam_ctx, mem_ctx, system_dn,