/* Unix SMB/CIFS implementation. Samba Active Directory authentication policy utility functions Copyright (C) Catalyst.Net Ltd 2023 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 "lib/replace/replace.h" #include "source4/kdc/authn_policy_util.h" #include "auth/authn_policy_impl.h" #include "lib/util/debug.h" #include "lib/util/samba_util.h" #include "libcli/security/security.h" #include "libcli/util/werror.h" #include "auth/common_auth.h" #include "source4/auth/session.h" #include "source4/dsdb/samdb/samdb.h" #include "source4/dsdb/samdb/ldb_modules/util.h" bool authn_policy_silos_and_policies_in_effect(struct ldb_context *samdb) { /* * Authentication Silos and Authentication Policies are not * honoured by Samba unless the DC is at FL 2012 R2. This is * to match Windows, which will offer these features as soon * as the DC is upgraded. */ const int functional_level = dsdb_dc_functional_level(samdb); return functional_level >= DS_DOMAIN_FUNCTION_2012_R2; } bool authn_policy_allowed_ntlm_network_auth_in_effect(struct ldb_context *samdb) { /* * The allowed NTLM network authentication Policies are not * honoured by Samba unless the DC is at FL2016. This * is to match Windows, which will enforce these restrictions * as soon as the DC is upgraded. */ const int functional_level = dsdb_dc_functional_level(samdb); return functional_level >= DS_DOMAIN_FUNCTION_2016; } /* * Depending on the type of the account, we need to refer to different * attributes of authentication silo objects. This structure keeps track of the * attributes to use for a certain account type. */ struct authn_silo_attrs { const char *policy; const char *attrs[]; }; /* * Depending on the type of the account, we need to refer to different * attributes of authentication policy objects. This structure keeps track of * the attributes to use for a certain account type. */ struct authn_policy_attrs { /* This applies at FL2016 and up. */ const char *allowed_ntlm_network_auth; /* The remainder apply at FL2012_R2 and up. */ const char *allowed_to_authenticate_from; const char *allowed_to_authenticate_to; const char *tgt_lifetime; const char *attrs[]; }; struct authn_attrs { const struct authn_silo_attrs *silo; const struct authn_policy_attrs *policy; }; /* * Get the authentication attributes that apply to an account of a certain * class. */ static const struct authn_attrs authn_policy_get_attrs(const struct ldb_message *msg) { const struct authn_attrs null_authn_attrs = { .silo = NULL, .policy = NULL, }; const struct ldb_message_element *objectclass_el = NULL; unsigned i; objectclass_el = ldb_msg_find_element(msg, "objectClass"); if (objectclass_el == NULL || objectclass_el->num_values == 0) { return null_authn_attrs; } /* * Iterate over the objectClasses, starting at the most-derived class. */ for (i = objectclass_el->num_values; i > 0; --i) { const struct ldb_val *objectclass_val = &objectclass_el->values[i - 1]; const char *objectclass = NULL; objectclass = (const char *)objectclass_val->data; if (objectclass == NULL) { continue; } #define COMMON_AUTHN_SILO_ATTRS \ "msDS-AuthNPolicySiloEnforced", \ "msDS-AuthNPolicySiloMembers", \ "name" #define COMMON_AUTHN_POLICY_ATTRS \ "msDS-AuthNPolicyEnforced", \ "msDS-StrongNTLMPolicy", \ "name" /* * See which of three classes this object is most closely * derived from. */ if (strcasecmp(objectclass, "user") == 0) { static const struct authn_silo_attrs user_authn_silo_attrs = { .policy = "msDS-UserAuthNPolicy", .attrs = { COMMON_AUTHN_SILO_ATTRS, "msDS-UserAuthNPolicy", NULL, }, }; static const struct authn_policy_attrs user_authn_policy_attrs = { .allowed_ntlm_network_auth = "msDS-UserAllowedNTLMNetworkAuthentication", .allowed_to_authenticate_from = "msDS-UserAllowedToAuthenticateFrom", .allowed_to_authenticate_to = "msDS-UserAllowedToAuthenticateTo", .tgt_lifetime = "msDS-UserTGTLifetime", .attrs = { COMMON_AUTHN_POLICY_ATTRS, "msDS-UserAllowedNTLMNetworkAuthentication", "msDS-UserAllowedToAuthenticateFrom", "msDS-UserAllowedToAuthenticateTo", "msDS-UserTGTLifetime", NULL, }, }; return (struct authn_attrs) { .silo = &user_authn_silo_attrs, .policy = &user_authn_policy_attrs, }; } if (strcasecmp(objectclass, "computer") == 0) { static const struct authn_silo_attrs computer_authn_silo_attrs = { .policy = "msDS-ComputerAuthNPolicy", .attrs = { COMMON_AUTHN_SILO_ATTRS, "msDS-ComputerAuthNPolicy", NULL, }, }; static const struct authn_policy_attrs computer_authn_policy_attrs = { .allowed_ntlm_network_auth = NULL, .allowed_to_authenticate_from = NULL, .allowed_to_authenticate_to = "msDS-ComputerAllowedToAuthenticateTo", .tgt_lifetime = "msDS-ComputerTGTLifetime", .attrs = { COMMON_AUTHN_POLICY_ATTRS, "msDS-ComputerAllowedToAuthenticateTo", "msDS-ComputerTGTLifetime", NULL, }, }; return (struct authn_attrs) { .silo = &computer_authn_silo_attrs, .policy = &computer_authn_policy_attrs, }; } if (strcasecmp(objectclass, "msDS-ManagedServiceAccount") == 0) { static const struct authn_silo_attrs service_authn_silo_attrs = { .policy = "msDS-ServiceAuthNPolicy", .attrs = { COMMON_AUTHN_SILO_ATTRS, "msDS-ServiceAuthNPolicy", NULL, }, }; static const struct authn_policy_attrs service_authn_policy_attrs = { .allowed_ntlm_network_auth = "msDS-ServiceAllowedNTLMNetworkAuthentication", .allowed_to_authenticate_from = "msDS-ServiceAllowedToAuthenticateFrom", .allowed_to_authenticate_to = "msDS-ServiceAllowedToAuthenticateTo", .tgt_lifetime = "msDS-ServiceTGTLifetime", .attrs = { COMMON_AUTHN_POLICY_ATTRS, "msDS-ServiceAllowedNTLMNetworkAuthentication", "msDS-ServiceAllowedToAuthenticateFrom", "msDS-ServiceAllowedToAuthenticateTo", "msDS-ServiceTGTLifetime", NULL, }, }; return (struct authn_attrs) { .silo = &service_authn_silo_attrs, .policy = &service_authn_policy_attrs, }; } } #undef COMMON_AUTHN_SILO_ATTRS #undef COMMON_AUTHN_POLICY_ATTRS /* No match — this object is not a user. */ return null_authn_attrs; } /* * Look up the silo assigned to an account. If one exists, returns its details * and whether it is enforced or not. ‘silo_attrs’ comprises the attributes to * include in the search result, the relevant set of which can differ depending * on the account’s objectClass. */ int authn_policy_get_assigned_silo(struct ldb_context *samdb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg, const char * const *silo_attrs, const struct ldb_message **silo_msg_out, bool *is_enforced) { TALLOC_CTX *tmp_ctx = NULL; int ret = 0; const struct ldb_message_element *authn_silo = NULL; struct ldb_dn *authn_silo_dn = NULL; struct ldb_message *authn_silo_msg = NULL; const struct ldb_message_element *members = NULL; const char *linearized_dn = NULL; struct ldb_val linearized_dn_val; *silo_msg_out = NULL; *is_enforced = true; if (!authn_policy_silos_and_policies_in_effect(samdb)) { return 0; } tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { ret = ENOMEM; goto out; } authn_silo = ldb_msg_find_element(msg, "msDS-AssignedAuthNPolicySilo"); /* Is the account assigned to a silo? */ if (authn_silo == NULL || !authn_silo->num_values) { goto out; } authn_silo_dn = ldb_dn_from_ldb_val(tmp_ctx, samdb, &authn_silo->values[0]); if (authn_silo_dn == NULL) { ret = ENOMEM; goto out; } ret = dsdb_search_one(samdb, tmp_ctx, &authn_silo_msg, authn_silo_dn, LDB_SCOPE_BASE, silo_attrs, 0, NULL); if (ret == LDB_ERR_NO_SUCH_OBJECT) { /* Not found. */ ret = 0; goto out; } if (ret) { goto out; } members = ldb_msg_find_element(authn_silo_msg, "msDS-AuthNPolicySiloMembers"); if (members == NULL) { goto out; } linearized_dn = ldb_dn_get_linearized(msg->dn); if (linearized_dn == NULL) { ret = ENOMEM; goto out; } linearized_dn_val = data_blob_string_const(linearized_dn); /* Is the account a member of the silo? */ if (!ldb_msg_find_val(members, &linearized_dn_val)) { goto out; } /* Is the silo actually enforced? */ *is_enforced = ldb_msg_find_attr_as_bool( authn_silo_msg, "msDS-AuthNPolicySiloEnforced", false); *silo_msg_out = talloc_move(mem_ctx, &authn_silo_msg); out: talloc_free(tmp_ctx); return ret; } /* * Look up the authentication policy assigned to an account, returning its * details if it exists. ‘authn_attrs’ specifies which attributes are relevant, * and should be chosen based on the account’s objectClass. */ static int samba_kdc_authn_policy_msg(struct ldb_context *samdb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg, const struct authn_attrs authn_attrs, struct ldb_message **authn_policy_msg_out, struct authn_policy *authn_policy_out) { TALLOC_CTX *tmp_ctx = NULL; int ret = 0; const struct ldb_message *authn_silo_msg = NULL; const struct ldb_message_element *authn_policy = NULL; const char *silo_name = NULL; const char *policy_name = NULL; struct ldb_dn *authn_policy_dn = NULL; struct ldb_message *authn_policy_msg = NULL; bool belongs_to_silo = false; bool is_enforced = true; *authn_policy_msg_out = NULL; *authn_policy_out = (struct authn_policy) {}; tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { ret = ENOMEM; goto out; } /* See whether the account is assigned to a silo. */ ret = authn_policy_get_assigned_silo(samdb, tmp_ctx, msg, authn_attrs.silo->attrs, &authn_silo_msg, &is_enforced); if (ret) { goto out; } if (authn_silo_msg != NULL) { belongs_to_silo = true; silo_name = ldb_msg_find_attr_as_string(authn_silo_msg, "name", NULL); /* Get the applicable authentication policy. */ authn_policy = ldb_msg_find_element( authn_silo_msg, authn_attrs.silo->policy); } else { /* * If no silo is assigned, take the policy that is directly * assigned to the account. */ authn_policy = ldb_msg_find_element(msg, "msDS-AssignedAuthNPolicy"); } if (authn_policy == NULL || !authn_policy->num_values) { /* No policy applies; we’re done. */ goto out; } authn_policy_dn = ldb_dn_from_ldb_val(tmp_ctx, samdb, &authn_policy->values[0]); if (authn_policy_dn == NULL) { ret = ENOMEM; goto out; } /* Look up the policy object. */ ret = dsdb_search_one(samdb, tmp_ctx, &authn_policy_msg, authn_policy_dn, LDB_SCOPE_BASE, authn_attrs.policy->attrs, 0, NULL); if (ret == LDB_ERR_NO_SUCH_OBJECT) { /* Not found. */ ret = 0; goto out; } if (ret) { goto out; } policy_name = ldb_msg_find_attr_as_string(authn_policy_msg, "name", NULL); if (!belongs_to_silo) { is_enforced = ldb_msg_find_attr_as_bool( authn_policy_msg, "msDS-AuthNPolicyEnforced", false); } authn_policy_out->silo_name = talloc_move(mem_ctx, &silo_name); authn_policy_out->policy_name = talloc_move(mem_ctx, &policy_name); authn_policy_out->enforced = is_enforced; *authn_policy_msg_out = talloc_move(mem_ctx, &authn_policy_msg); out: talloc_free(tmp_ctx); return ret; } /* * Reference an existing authentication policy onto a talloc context, returning * ‘true’ on success. */ static bool authn_policy_ref(TALLOC_CTX *mem_ctx, struct authn_policy *policy_out, const struct authn_policy *policy) { const char *silo_name = NULL; const char *policy_name = NULL; if (policy->silo_name != NULL) { silo_name = talloc_strdup(mem_ctx, policy->silo_name); if (silo_name == NULL) { return false; } } if (policy->policy_name != NULL) { policy_name = talloc_strdup(mem_ctx, policy->policy_name); if (policy_name == NULL) { /* * We can’t free ‘silo_name’ here, as it is declared * const. It will be freed with the parent context. */ return false; } } *policy_out = (struct authn_policy) { .silo_name = silo_name, .policy_name = policy_name, .enforced = policy->enforced, }; return true; } /* Create a structure containing auditing information. */ static NTSTATUS _authn_policy_audit_info(TALLOC_CTX *mem_ctx, const struct authn_policy *policy, const struct authn_int64_optional tgt_lifetime_raw, const struct auth_user_info_dc *client_info, const enum authn_audit_event event, const enum authn_audit_reason reason, const NTSTATUS policy_status, const char *location, struct authn_audit_info **audit_info_out) { struct authn_audit_info *audit_info = NULL; bool ok; if (audit_info_out == NULL) { return NT_STATUS_OK; } audit_info = talloc_zero(mem_ctx, struct authn_audit_info); if (audit_info == NULL) { return NT_STATUS_NO_MEMORY; } if (client_info != NULL) { /* * Keep a reference to the client’s user information so that it * is available to be logged later. */ audit_info->client_info = talloc_reference(audit_info, client_info); if (audit_info->client_info == NULL) { talloc_free(audit_info); return NT_STATUS_NO_MEMORY; } } if (policy != NULL) { audit_info->policy = talloc_zero(audit_info, struct authn_policy); if (audit_info->policy == NULL) { talloc_free(audit_info); return NT_STATUS_NO_MEMORY; } ok = authn_policy_ref(audit_info, audit_info->policy, policy); if (!ok) { talloc_free(audit_info); return NT_STATUS_NO_MEMORY; } } audit_info->event = event; audit_info->reason = reason; audit_info->policy_status = policy_status; audit_info->location = location; audit_info->tgt_lifetime_raw = tgt_lifetime_raw; *audit_info_out = audit_info; return NT_STATUS_OK; } /* Create a structure containing auditing information. */ #define authn_policy_audit_info( \ mem_ctx, \ policy, \ tgt_lifetime_raw, \ client_info, \ event, \ reason, \ policy_status, \ audit_info_out) \ _authn_policy_audit_info( \ mem_ctx, \ policy, \ tgt_lifetime_raw, \ client_info, \ event, \ reason, \ policy_status, \ __location__, \ audit_info_out) /* * Perform an access check against the security descriptor set in an * authentication policy. ‘client_info’ must be talloc-allocated so that we can * make a reference to it. */ static NTSTATUS _authn_policy_access_check(TALLOC_CTX *mem_ctx, struct ldb_context *samdb, struct loadparm_context* lp_ctx, const struct auth_user_info_dc *client_info, const struct auth_user_info_dc *device_info, const struct auth_claims auth_claims, const struct authn_policy *policy, const struct authn_int64_optional tgt_lifetime_raw, const enum authn_audit_event restriction_event, const struct authn_policy_flags authn_policy_flags, const DATA_BLOB *descriptor_blob, const char *location, struct authn_audit_info **audit_info_out) { TALLOC_CTX *tmp_ctx = NULL; NTSTATUS status = NT_STATUS_OK; NTSTATUS status2; enum ndr_err_code ndr_err; struct security_descriptor *descriptor = NULL; struct security_token *security_token = NULL; uint32_t session_info_flags = AUTH_SESSION_INFO_DEFAULT_GROUPS | AUTH_SESSION_INFO_SIMPLE_PRIVILEGES; const uint32_t access_desired = SEC_ADS_CONTROL_ACCESS; uint32_t access_granted; enum authn_audit_event event = restriction_event; enum authn_audit_reason reason = AUTHN_AUDIT_REASON_NONE; if (audit_info_out != NULL) { *audit_info_out = NULL; } tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } if (!(client_info->info->user_flags & NETLOGON_GUEST)) { session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED; } if (authn_policy_flags.force_compounded_authentication) { session_info_flags |= AUTH_SESSION_INFO_FORCE_COMPOUNDED_AUTHENTICATION; } descriptor = talloc(tmp_ctx, struct security_descriptor); if (descriptor == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } ndr_err = ndr_pull_struct_blob(descriptor_blob, tmp_ctx, descriptor, (ndr_pull_flags_fn_t)ndr_pull_security_descriptor); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DBG_ERR("Failed to unmarshall " "security descriptor for authentication policy: %s\n", nt_errstr(status)); reason = AUTHN_AUDIT_REASON_DESCRIPTOR_INVALID; goto out; } /* Require that the security descriptor has an owner set. */ if (descriptor->owner_sid == NULL) { status = NT_STATUS_INVALID_PARAMETER; reason = AUTHN_AUDIT_REASON_DESCRIPTOR_NO_OWNER; goto out; } status = auth_generate_security_token(tmp_ctx, lp_ctx, samdb, client_info, device_info, auth_claims, session_info_flags, &security_token); if (!NT_STATUS_IS_OK(status)) { reason = AUTHN_AUDIT_REASON_SECURITY_TOKEN_FAILURE; goto out; } status = sec_access_check_ds(descriptor, security_token, access_desired, &access_granted, NULL, NULL); if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { status = NT_STATUS_AUTHENTICATION_FIREWALL_FAILED; reason = AUTHN_AUDIT_REASON_ACCESS_DENIED; goto out; } if (!NT_STATUS_IS_OK(status)) { goto out; } event = AUTHN_AUDIT_EVENT_OK; out: /* * Create the structure with auditing information here while we have all * the relevant information to hand. It will contain references to * information regarding the client and the policy, to be consulted * after the referents have possibly been freed. */ status2 = _authn_policy_audit_info(mem_ctx, policy, tgt_lifetime_raw, client_info, event, reason, status, location, audit_info_out); if (!NT_STATUS_IS_OK(status2)) { status = status2; } else if (!authn_policy_is_enforced(policy)) { status = NT_STATUS_OK; } talloc_free(tmp_ctx); return status; } #define authn_policy_access_check(mem_ctx, \ samdb, \ lp_ctx, \ client_info, \ device_info, \ auth_claims, \ policy, \ tgt_lifetime_raw, \ restriction_event, \ authn_policy_flags, \ descriptor_blob, \ audit_info_out) \ _authn_policy_access_check(mem_ctx, \ samdb, \ lp_ctx, \ client_info, \ device_info, \ auth_claims, \ policy, \ tgt_lifetime_raw, \ restriction_event, \ authn_policy_flags, \ descriptor_blob, \ __location__, \ audit_info_out) /* Return an authentication policy moved onto a talloc context. */ static struct authn_policy authn_policy_move(TALLOC_CTX *mem_ctx, struct authn_policy *policy) { return (struct authn_policy) { .silo_name = talloc_move(mem_ctx, &policy->silo_name), .policy_name = talloc_move(mem_ctx, &policy->policy_name), .enforced = policy->enforced, }; } /* Authentication policies for Kerberos clients. */ /* * Get the applicable authentication policy for an account acting as a Kerberos * client. */ int authn_policy_kerberos_client(struct ldb_context *samdb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg, const struct authn_kerberos_client_policy **policy_out) { TALLOC_CTX *tmp_ctx = NULL; int ret = 0; struct authn_attrs authn_attrs; struct ldb_message *authn_policy_msg = NULL; struct authn_kerberos_client_policy *client_policy = NULL; struct authn_policy policy; *policy_out = NULL; if (!authn_policy_silos_and_policies_in_effect(samdb)) { return 0; } /* * Get the silo and policy attributes that apply to objects of this * account’s objectclass. */ authn_attrs = authn_policy_get_attrs(msg); if (authn_attrs.silo == NULL || authn_attrs.policy == NULL) { /* * No applicable silo or policy attributes (somehow). Either * this account isn’t derived from ‘user’, or the message is * missing an objectClass element. */ goto out; } if (authn_attrs.policy->allowed_to_authenticate_from == NULL && authn_attrs.policy->tgt_lifetime == NULL) { /* No relevant policy attributes apply. */ goto out; } tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { ret = ENOMEM; goto out; } ret = samba_kdc_authn_policy_msg(samdb, tmp_ctx, msg, authn_attrs, &authn_policy_msg, &policy); if (ret) { goto out; } if (authn_policy_msg == NULL) { /* No policy applies. */ goto out; } client_policy = talloc_zero(tmp_ctx, struct authn_kerberos_client_policy); if (client_policy == NULL) { ret = ENOMEM; goto out; } client_policy->policy = authn_policy_move(client_policy, &policy); if (authn_attrs.policy->allowed_to_authenticate_from != NULL) { const struct ldb_val *allowed_from = ldb_msg_find_ldb_val( authn_policy_msg, authn_attrs.policy->allowed_to_authenticate_from); if (allowed_from != NULL && allowed_from->data != NULL) { client_policy->allowed_to_authenticate_from = data_blob_const( talloc_steal(client_policy, allowed_from->data), allowed_from->length); } } if (authn_attrs.policy->tgt_lifetime != NULL) { client_policy->tgt_lifetime_raw = ldb_msg_find_attr_as_int64( authn_policy_msg, authn_attrs.policy->tgt_lifetime, 0); } *policy_out = talloc_move(mem_ctx, &client_policy); out: talloc_free(tmp_ctx); return ret; } /* Get device restrictions enforced by an authentication policy. */ static const DATA_BLOB *authn_policy_kerberos_device_restrictions(const struct authn_kerberos_client_policy *policy) { const DATA_BLOB *restrictions = NULL; if (policy == NULL) { return NULL; } restrictions = &policy->allowed_to_authenticate_from; if (restrictions->data == NULL) { return NULL; } return restrictions; } /* Return whether an authentication policy enforces device restrictions. */ bool authn_policy_device_restrictions_present(const struct authn_kerberos_client_policy *policy) { return authn_policy_kerberos_device_restrictions(policy) != NULL; } /* * Perform an access check for the device with which the client is * authenticating. ‘device_info’ must be talloc-allocated so that we can make a * reference to it. */ NTSTATUS authn_policy_authenticate_from_device(TALLOC_CTX *mem_ctx, struct ldb_context *samdb, struct loadparm_context* lp_ctx, const struct auth_user_info_dc *device_info, const struct auth_claims auth_claims, const struct authn_kerberos_client_policy *client_policy, struct authn_audit_info **client_audit_info_out) { NTSTATUS status = NT_STATUS_OK; const DATA_BLOB *restrictions = NULL; restrictions = authn_policy_kerberos_device_restrictions(client_policy); if (restrictions == NULL) { goto out; } status = authn_policy_access_check(mem_ctx, samdb, lp_ctx, device_info, /* The device itself has no device. */ NULL /* device_info */, auth_claims, &client_policy->policy, authn_int64_some(client_policy->tgt_lifetime_raw), AUTHN_AUDIT_EVENT_KERBEROS_DEVICE_RESTRICTION, (struct authn_policy_flags) {}, restrictions, client_audit_info_out); out: return status; } /* Authentication policies for NTLM clients. */ /* * Get the applicable authentication policy for an account acting as an NTLM * client. */ int authn_policy_ntlm_client(struct ldb_context *samdb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg, const struct authn_ntlm_client_policy **policy_out) { TALLOC_CTX *tmp_ctx = NULL; int ret = 0; struct authn_attrs authn_attrs; struct ldb_message *authn_policy_msg = NULL; struct authn_ntlm_client_policy *client_policy = NULL; struct authn_policy policy; *policy_out = NULL; if (!authn_policy_silos_and_policies_in_effect(samdb)) { return 0; } /* * Get the silo and policy attributes that apply to objects of this * account’s objectclass. */ authn_attrs = authn_policy_get_attrs(msg); if (authn_attrs.silo == NULL || authn_attrs.policy == NULL) { /* * No applicable silo or policy attributes (somehow). Either * this account isn’t derived from ‘user’, or the message is * missing an objectClass element. */ goto out; } if (authn_attrs.policy->allowed_to_authenticate_from == NULL && authn_attrs.policy->allowed_ntlm_network_auth == NULL) { /* No relevant policy attributes apply. */ goto out; } tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { ret = ENOMEM; goto out; } ret = samba_kdc_authn_policy_msg(samdb, tmp_ctx, msg, authn_attrs, &authn_policy_msg, &policy); if (ret) { goto out; } if (authn_policy_msg == NULL) { /* No policy applies. */ goto out; } client_policy = talloc_zero(tmp_ctx, struct authn_ntlm_client_policy); if (client_policy == NULL) { ret = ENOMEM; goto out; } client_policy->policy = authn_policy_move(client_policy, &policy); if (authn_attrs.policy->allowed_to_authenticate_from != NULL) { const struct ldb_val *allowed_from = ldb_msg_find_ldb_val( authn_policy_msg, authn_attrs.policy->allowed_to_authenticate_from); if (allowed_from != NULL && allowed_from->data != NULL) { client_policy->allowed_to_authenticate_from = data_blob_const( talloc_steal(client_policy, allowed_from->data), allowed_from->length); } } if (authn_attrs.policy->allowed_ntlm_network_auth != NULL && authn_policy_allowed_ntlm_network_auth_in_effect(samdb)) { client_policy->allowed_ntlm_network_auth = ldb_msg_find_attr_as_bool( authn_policy_msg, authn_attrs.policy->allowed_ntlm_network_auth, false); } *policy_out = talloc_move(mem_ctx, &client_policy); out: talloc_free(tmp_ctx); return ret; } /* Return whether an authentication policy enforces device restrictions. */ static bool authn_policy_ntlm_device_restrictions_present(const struct authn_ntlm_client_policy *policy) { if (policy == NULL) { return false; } return policy->allowed_to_authenticate_from.data != NULL; } /* Check whether the client is allowed to authenticate using NTLM. */ NTSTATUS authn_policy_ntlm_apply_device_restriction(TALLOC_CTX *mem_ctx, const struct authn_ntlm_client_policy *client_policy, struct authn_audit_info **client_audit_info_out) { NTSTATUS status; NTSTATUS status2; if (client_audit_info_out != NULL) { *client_audit_info_out = NULL; } if (client_policy == NULL) { return NT_STATUS_OK; } /* * Access control restrictions cannot be applied to NTLM. * * If NTLM authentication is disallowed and the policy enforces a device * restriction, deny the authentication. */ if (!authn_policy_ntlm_device_restrictions_present(client_policy)) { return authn_policy_audit_info(mem_ctx, &client_policy->policy, authn_int64_none() /* tgt_lifetime_raw */, NULL /* client_info */, AUTHN_AUDIT_EVENT_OK, AUTHN_AUDIT_REASON_NONE, NT_STATUS_OK, client_audit_info_out); } /* * (Although MS-APDS doesn’t state it, AllowedNTLMNetworkAuthentication * applies to interactive logons too.) */ if (client_policy->allowed_ntlm_network_auth) { return authn_policy_audit_info(mem_ctx, &client_policy->policy, authn_int64_none() /* tgt_lifetime_raw */, NULL /* client_info */, AUTHN_AUDIT_EVENT_OK, AUTHN_AUDIT_REASON_NONE, NT_STATUS_OK, client_audit_info_out); } status = NT_STATUS_ACCOUNT_RESTRICTION; status2 = authn_policy_audit_info(mem_ctx, &client_policy->policy, authn_int64_none() /* tgt_lifetime_raw */, NULL /* client_info */, AUTHN_AUDIT_EVENT_NTLM_DEVICE_RESTRICTION, AUTHN_AUDIT_REASON_NONE, status, client_audit_info_out); if (!NT_STATUS_IS_OK(status2)) { status = status2; } else if (!authn_policy_is_enforced(&client_policy->policy)) { status = NT_STATUS_OK; } return status; } /* Authentication policies for servers. */ /* * Get the applicable authentication policy for an account acting as a * server. */ int authn_policy_server(struct ldb_context *samdb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg, const struct authn_server_policy **policy_out) { TALLOC_CTX *tmp_ctx = NULL; int ret = 0; struct authn_attrs authn_attrs; struct ldb_message *authn_policy_msg = NULL; struct authn_server_policy *server_policy = NULL; struct authn_policy policy; *policy_out = NULL; if (!authn_policy_silos_and_policies_in_effect(samdb)) { return 0; } /* * Get the silo and policy attributes that apply to objects of this * account’s objectclass. */ authn_attrs = authn_policy_get_attrs(msg); if (authn_attrs.silo == NULL || authn_attrs.policy == NULL) { /* * No applicable silo or policy attributes (somehow). Either * this account isn’t derived from ‘user’, or the message is * missing an objectClass element. */ goto out; } if (authn_attrs.policy->allowed_to_authenticate_to == NULL) { /* The relevant policy attribute doesn’t apply. */ goto out; } tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { ret = ENOMEM; goto out; } ret = samba_kdc_authn_policy_msg(samdb, tmp_ctx, msg, authn_attrs, &authn_policy_msg, &policy); if (ret) { goto out; } if (authn_policy_msg == NULL) { /* No policy applies. */ goto out; } server_policy = talloc_zero(tmp_ctx, struct authn_server_policy); if (server_policy == NULL) { ret = ENOMEM; goto out; } server_policy->policy = authn_policy_move(server_policy, &policy); if (authn_attrs.policy->allowed_to_authenticate_to != NULL) { const struct ldb_val *allowed_to = ldb_msg_find_ldb_val( authn_policy_msg, authn_attrs.policy->allowed_to_authenticate_to); if (allowed_to != NULL && allowed_to->data != NULL) { server_policy->allowed_to_authenticate_to = data_blob_const( talloc_steal(server_policy, allowed_to->data), allowed_to->length); } } *policy_out = talloc_move(mem_ctx, &server_policy); out: talloc_free(tmp_ctx); return ret; } /* Get restrictions enforced by an authentication policy. */ static const DATA_BLOB *authn_policy_restrictions(const struct authn_server_policy *policy) { const DATA_BLOB *restrictions = NULL; if (policy == NULL) { return NULL; } restrictions = &policy->allowed_to_authenticate_to; if (restrictions->data == NULL) { return NULL; } return restrictions; } /* Return whether an authentication policy enforces restrictions. */ bool authn_policy_restrictions_present(const struct authn_server_policy *policy) { return authn_policy_restrictions(policy) != NULL; } /* * Perform an access check for the client attempting to authenticate to the * server. ‘user_info’ must be talloc-allocated so that we can make a reference * to it. */ NTSTATUS authn_policy_authenticate_to_service(TALLOC_CTX *mem_ctx, struct ldb_context *samdb, struct loadparm_context* lp_ctx, const enum authn_policy_auth_type auth_type, const struct auth_user_info_dc *user_info, const struct auth_user_info_dc *device_info, const struct auth_claims auth_claims, const struct authn_server_policy *server_policy, const struct authn_policy_flags authn_policy_flags, struct authn_audit_info **server_audit_info_out) { NTSTATUS status = NT_STATUS_OK; const DATA_BLOB *restrictions = NULL; enum authn_audit_event event; restrictions = authn_policy_restrictions(server_policy); if (restrictions == NULL) { return authn_server_policy_audit_info(mem_ctx, server_policy, user_info, AUTHN_AUDIT_EVENT_OK, AUTHN_AUDIT_REASON_NONE, NT_STATUS_OK, server_audit_info_out); } switch (auth_type) { case AUTHN_POLICY_AUTH_TYPE_KERBEROS: event = AUTHN_AUDIT_EVENT_KERBEROS_SERVER_RESTRICTION; break; case AUTHN_POLICY_AUTH_TYPE_NTLM: event = AUTHN_AUDIT_EVENT_NTLM_SERVER_RESTRICTION; break; default: return NT_STATUS_INVALID_PARAMETER_4; } status = authn_policy_access_check(mem_ctx, samdb, lp_ctx, user_info, device_info, auth_claims, &server_policy->policy, authn_int64_none() /* tgt_lifetime_raw */, event, authn_policy_flags, restrictions, server_audit_info_out); return status; } /* Create a structure containing auditing information. */ NTSTATUS _authn_kerberos_client_policy_audit_info( TALLOC_CTX *mem_ctx, const struct authn_kerberos_client_policy *client_policy, const struct auth_user_info_dc *client_info, const enum authn_audit_event event, const enum authn_audit_reason reason, const NTSTATUS policy_status, const char *location, struct authn_audit_info **audit_info_out) { const struct authn_policy *policy = NULL; struct authn_int64_optional tgt_lifetime_raw = authn_int64_none(); if (client_policy != NULL) { policy = &client_policy->policy; tgt_lifetime_raw = authn_int64_some(client_policy->tgt_lifetime_raw); } return _authn_policy_audit_info(mem_ctx, policy, tgt_lifetime_raw, client_info, event, reason, policy_status, location, audit_info_out); } /* Create a structure containing auditing information. */ NTSTATUS _authn_ntlm_client_policy_audit_info( TALLOC_CTX *mem_ctx, const struct authn_ntlm_client_policy *client_policy, const struct auth_user_info_dc *client_info, const enum authn_audit_event event, const enum authn_audit_reason reason, const NTSTATUS policy_status, const char *location, struct authn_audit_info **audit_info_out) { const struct authn_policy *policy = NULL; if (client_policy != NULL) { policy = &client_policy->policy; } return _authn_policy_audit_info(mem_ctx, policy, authn_int64_none() /* tgt_lifetime_raw */, client_info, event, reason, policy_status, location, audit_info_out); } /* Create a structure containing auditing information. */ NTSTATUS _authn_server_policy_audit_info( TALLOC_CTX *mem_ctx, const struct authn_server_policy *server_policy, const struct auth_user_info_dc *client_info, const enum authn_audit_event event, const enum authn_audit_reason reason, const NTSTATUS policy_status, const char *location, struct authn_audit_info **audit_info_out) { const struct authn_policy *policy = NULL; if (server_policy != NULL) { policy = &server_policy->policy; } return _authn_policy_audit_info(mem_ctx, policy, authn_int64_none() /* tgt_lifetime_raw */, client_info, event, reason, policy_status, location, audit_info_out); }