diff options
Diffstat (limited to '')
-rw-r--r-- | source4/kdc/authn_policy_util.c | 1316 |
1 files changed, 1316 insertions, 0 deletions
diff --git a/source4/kdc/authn_policy_util.c b/source4/kdc/authn_policy_util.c new file mode 100644 index 0000000..60de61a --- /dev/null +++ b/source4/kdc/authn_policy_util.c @@ -0,0 +1,1316 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#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_DEVICE_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 (device_info != NULL && !(device_info->info->user_flags & NETLOGON_GUEST)) { + session_info_flags |= AUTH_SESSION_INFO_DEVICE_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); +} |