/* Unix SMB/CIFS implementation. Samba Active Directory claims 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 "lib/util/debug.h" #include "lib/util/samba_util.h" #include "source4/kdc/ad_claims.h" #include "source4/kdc/authn_policy_util.h" #include "ldb_module.h" #include "libcli/security/security.h" #include "libcli/util/werror.h" #include "dsdb/samdb/samdb.h" #include "dsdb/samdb/ldb_modules/util.h" #include "librpc/gen_ndr/claims.h" #include "librpc/gen_ndr/ndr_claims.h" #include "librpc/gen_ndr/krb5pac.h" #include "librpc/gen_ndr/ndr_krb5pac.h" #include "lzxpress_huffman.h" #include "lib/util/binsearch.h" #undef strcasecmp bool ad_claims_are_issued(struct ldb_context *samdb) { /* * Claims aren’t issued by Samba unless the DC is at * FL2012. This is to match Windows, which will offer * this feature as soon as the DC is upgraded. */ const int functional_level = dsdb_dc_functional_level(samdb); return functional_level >= DS_DOMAIN_FUNCTION_2012; } static int acl_attr_cmp_fn(const char *a, const char * const *b) { return ldb_attr_cmp(a, *b); } /* * Add a single attribute to a list of attributes if it is not already * present. The list is maintained in case-insensitive sorted order. */ static int add_attr_unique(TALLOC_CTX *mem_ctx, const char **attrs, unsigned *ad_claim_attrs_count, const char *attr) { const unsigned count = *ad_claim_attrs_count; const char * const *exact = NULL; const char * const *next = NULL; BINARY_ARRAY_SEARCH_GTE(attrs, count, attr, acl_attr_cmp_fn, exact, next); if (exact != NULL) { /* The attribute is already present; there's nothing to do. */ return LDB_SUCCESS; } /* Make sure we don't overflow the array. */ SMB_ASSERT(count < talloc_array_length(attrs)); *ad_claim_attrs_count = count + 1; if (next == NULL) { /* Just add the new element on the end. */ attrs[count] = attr; } else { /* Shift all following elements over to make room. */ size_t next_idx = next - attrs; size_t bytes_to_move = (count - next_idx) * sizeof (attrs[0]); memmove(&attrs[next_idx + 1], &attrs[next_idx], bytes_to_move); attrs[next_idx] = attr; } return LDB_SUCCESS; } /* * Return true if a data_blob, interpreted as a string, is equal to another * string. This is more efficient than strcmp(), particularly when comparing * against a string constant. This assumes the data_blob's length does not * include the zero-terminator. */ static inline bool data_blob_equals_str(const DATA_BLOB val, const char *str) { size_t len = strlen(str); if (val.length != len) { return false; } return memcmp(val.data, str, len) == 0; } static int fill_claim_int64(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const struct ldb_message_element *principal_attribute, const struct ldb_val name, struct CLAIM_INT64 *claim) { uint32_t i; claim->value_count = 0; claim->values = talloc_array(mem_ctx, int64_t, principal_attribute->num_values); if (claim->values == NULL) { return ldb_oom(ldb); } for (i = 0; i < principal_attribute->num_values; ++i) { const struct ldb_val *value = &principal_attribute->values[i]; int ret = ldb_val_as_int64(value, &claim->values[i]); if (ret) { char buf[1024]; const char *reason = NULL; int err = strerror_r(ret, buf, sizeof(buf)); if (err == 0) { reason = buf; } else { reason = "Unknown error"; } DBG_WARNING("Failed to interpret value %s as INT64 " "while creating claim %s for attribute %s (%s); " "skipping value\n", (value->data != NULL) ? (const char *)value->data : "", name.data, principal_attribute->name, reason); continue; } ++claim->value_count; } /* Shrink the array to fit. */ claim->values = talloc_realloc(mem_ctx, claim->values, int64_t, claim->value_count); if (claim->value_count && claim->values == NULL) { return ldb_oom(ldb); } return LDB_SUCCESS; } static int fill_claim_uint64(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const struct ldb_message_element *principal_attribute, const struct ldb_val name, struct CLAIM_UINT64 *claim) { uint32_t i; claim->value_count = 0; claim->values = talloc_array(mem_ctx, uint64_t, principal_attribute->num_values); if (claim->values == NULL) { return ldb_oom(ldb); } for (i = 0; i < principal_attribute->num_values; ++i) { const struct ldb_val *value = &principal_attribute->values[i]; int ret = ldb_val_as_uint64(value, &claim->values[i]); if (ret) { char buf[1024]; const char *reason = NULL; int err = strerror_r(ret, buf, sizeof(buf)); if (err == 0) { reason = buf; } else { reason = "Unknown error"; } DBG_WARNING("Failed to interpret value %s as UINT64 " "while creating claim %s for attribute %s (%s); " "skipping value\n", (value->data != NULL) ? (const char *)value->data : "", name.data, principal_attribute->name, reason); continue; } ++claim->value_count; } /* Shrink the array to fit. */ claim->values = talloc_realloc(mem_ctx, claim->values, uint64_t, claim->value_count); if (claim->value_count && claim->values == NULL) { return ldb_oom(ldb); } return LDB_SUCCESS; } static int fill_claim_uint64_oid_syntax(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const struct dsdb_schema *schema, const struct ldb_message_element *principal_attribute, const struct ldb_val name, struct CLAIM_UINT64 *claim) { uint32_t i; claim->value_count = 0; claim->values = talloc_array(mem_ctx, uint64_t, principal_attribute->num_values); if (claim->values == NULL) { return ldb_oom(ldb); } for (i = 0; i < principal_attribute->num_values; ++i) { const struct dsdb_class *class_val = NULL; /* * OID values for objectClass * are presented in reverse * order. */ const struct ldb_val *display_name = &principal_attribute->values[ principal_attribute->num_values - 1 - i]; class_val = dsdb_class_by_lDAPDisplayName_ldb_val(schema, display_name); if (class_val == NULL) { DBG_WARNING("Failed to look up OID for value %s " "while creating claim %s for attribute %s; " "skipping value\n", (display_name->data != NULL) ? (const char *)display_name->data : "", name.data, principal_attribute->name); continue; } claim->values[i] = class_val->governsID_id; ++claim->value_count; } /* Shrink the array to fit. */ claim->values = talloc_realloc(mem_ctx, claim->values, uint64_t, claim->value_count); if (claim->value_count && claim->values == NULL) { return ldb_oom(ldb); } return LDB_SUCCESS; } static int fill_claim_boolean(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const struct ldb_message_element *principal_attribute, const struct ldb_val name, struct CLAIM_UINT64 *claim) { uint32_t i; claim->value_count = 0; claim->values = talloc_array(mem_ctx, uint64_t, principal_attribute->num_values); if (claim->values == NULL) { return ldb_oom(ldb); } for (i = 0; i < principal_attribute->num_values; ++i) { const struct ldb_val *value = &principal_attribute->values[i]; bool val = false; int ret = ldb_val_as_bool(value, &val); if (ret) { char buf[1024]; const char *reason = NULL; int err = strerror_r(ret, buf, sizeof(buf)); if (err == 0) { reason = buf; } else { reason = "Unknown error"; } DBG_WARNING("Failed to interpret value %s as BOOL " "while creating claim %s for attribute %s (%s); " "skipping value\n", (value->data != NULL) ? (const char *)value->data : "", name.data, principal_attribute->name, reason); continue; } claim->values[i] = val; ++claim->value_count; } /* Shrink the array to fit. */ claim->values = talloc_realloc(mem_ctx, claim->values, uint64_t, claim->value_count); if (claim->value_count && claim->values == NULL) { return ldb_oom(ldb); } return LDB_SUCCESS; } static int fill_claim_string(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const struct ldb_message_element *principal_attribute, struct CLAIM_STRING *claim) { uint32_t i; claim->value_count = 0; claim->values = talloc_array(mem_ctx, const char *, principal_attribute->num_values); if (claim->values == NULL) { return ldb_oom(ldb); } for (i = 0; i < principal_attribute->num_values; ++i) { const char *val = NULL; const struct ldb_val *v = &principal_attribute->values[i]; if (v == NULL || v->data == NULL) { continue; } val = talloc_strndup(claim->values, (const char *)v->data, v->length); if (val == NULL) { return ldb_oom(ldb); } claim->values[i] = val; ++claim->value_count; } /* Shrink the array to fit. */ claim->values = talloc_realloc(mem_ctx, claim->values, const char *, claim->value_count); if (claim->value_count && claim->values == NULL) { return ldb_oom(ldb); } return LDB_SUCCESS; } static int fill_claim_string_sec_desc_syntax(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const struct ldb_message_element *principal_attribute, struct CLAIM_STRING *claim) { TALLOC_CTX *tmp_ctx = NULL; const struct dom_sid *domain_sid = NULL; uint32_t i; claim->value_count = 0; claim->values = talloc_array(mem_ctx, const char *, principal_attribute->num_values); if (claim->values == NULL) { return ldb_oom(ldb); } domain_sid = samdb_domain_sid(ldb); if (domain_sid == NULL) { return ldb_oom(ldb); } tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { return ldb_oom(ldb); } for (i = 0; i < principal_attribute->num_values; ++i) { const struct ldb_val *v = &principal_attribute->values[i]; enum ndr_err_code ndr_err; struct security_descriptor desc = {}; const char *sddl = NULL; if (v == NULL || v->data == NULL) { continue; } ndr_err = ndr_pull_struct_blob(v, tmp_ctx, &desc, (ndr_pull_flags_fn_t)ndr_pull_security_descriptor); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err); DBG_ERR("security_descriptor pull failed: %s\n", nt_errstr(nt_status)); talloc_free(tmp_ctx); return ldb_operr(ldb); } sddl = sddl_encode(mem_ctx, &desc, domain_sid); if (sddl == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } claim->values[i] = sddl; ++claim->value_count; } talloc_free(tmp_ctx); /* Shrink the array to fit. */ claim->values = talloc_realloc(mem_ctx, claim->values, const char *, claim->value_count); if (claim->value_count && claim->values == NULL) { return ldb_oom(ldb); } return LDB_SUCCESS; } static int fill_claim_entry(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const struct dsdb_schema *schema, const struct ldb_message_element *principal_attribute, const struct ldb_val name, const DATA_BLOB syntax, enum CLAIM_TYPE claim_type, struct CLAIM_ENTRY *claim_entry) { claim_entry->id = (const char *)name.data; claim_entry->type = claim_type; switch (claim_type) { case CLAIM_TYPE_INT64: return fill_claim_int64(mem_ctx, ldb, principal_attribute, name, &claim_entry->values.claim_int64); case CLAIM_TYPE_UINT64: if (syntax.data != NULL && data_blob_equals_str(syntax, "2.5.5.2")) { return fill_claim_uint64_oid_syntax(mem_ctx, ldb, schema, principal_attribute, name, &claim_entry->values.claim_uint64); } else { return fill_claim_uint64(mem_ctx, ldb, principal_attribute, name, &claim_entry->values.claim_uint64); } case CLAIM_TYPE_BOOLEAN: return fill_claim_boolean(mem_ctx, ldb, principal_attribute, name, &claim_entry->values.claim_boolean); case CLAIM_TYPE_STRING: default: if (syntax.data != NULL && data_blob_equals_str(syntax, "2.5.5.15")) { return fill_claim_string_sec_desc_syntax(mem_ctx, ldb, principal_attribute, &claim_entry->values.claim_string); } else { return fill_claim_string(mem_ctx, ldb, principal_attribute, &claim_entry->values.claim_string); } } } /* * Determine wheter a claim applies to the most specific objectClass of the * principal. */ static int claim_applies_to_class(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const struct dsdb_schema *schema, const struct ldb_message *claim_msg, const uint32_t principal_class_id, bool *applies) { struct ldb_message_element *applies_to_class = NULL; unsigned i; applies_to_class = ldb_msg_find_element(claim_msg, "msDS-ClaimTypeAppliesToClass"); if (applies_to_class == NULL) { *applies = false; return LDB_SUCCESS; } for (i = 0; i < applies_to_class->num_values; ++i) { struct ldb_dn *class_dn = NULL; const struct dsdb_class *class_val = NULL; const struct ldb_val *class_rdn = NULL; class_dn = ldb_dn_from_ldb_val(mem_ctx, ldb, &applies_to_class->values[i]); if (class_dn == NULL) { return ldb_oom(ldb); } class_rdn = ldb_dn_get_rdn_val(class_dn); if (class_rdn == NULL) { TALLOC_FREE(class_dn); continue; } class_val = dsdb_class_by_cn_ldb_val(schema, class_rdn); TALLOC_FREE(class_dn); if (class_val == NULL) { continue; } if (class_val->governsID_id == principal_class_id) { *applies = true; return LDB_SUCCESS; } } *applies = false; return LDB_SUCCESS; } struct assigned_silo { const char *name; bool is_initialised; bool is_assigned; }; static struct assigned_silo new_assigned_silo(void) { return (struct assigned_silo) { .name = NULL, .is_initialised = false, .is_assigned = false, }; } static bool silo_is_maybe_assigned(struct assigned_silo silo) { return !silo.is_initialised || silo.is_assigned; } static int get_assigned_silo(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *principal, struct assigned_silo *assigned_silo) { TALLOC_CTX *tmp_ctx = NULL; int ret; const struct ldb_message *silo_msg = NULL; static const char * const silo_attrs[] = { "msDS-AuthNPolicySiloEnforced", "msDS-AuthNPolicySiloMembers", "name", NULL }; bool is_silo_enforced = false; const char *silo_name = NULL; if (assigned_silo->is_initialised) { return LDB_SUCCESS; } tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { return ldb_oom(ldb); } if (!authn_policy_silos_and_policies_in_effect(ldb)) { /* No assigned silo. */ assigned_silo->is_assigned = false; assigned_silo->is_initialised = true; talloc_free(tmp_ctx); return LDB_SUCCESS; } /* Check whether the user is assigned to an enforced silo. */ ret = authn_policy_get_assigned_silo(ldb, tmp_ctx, principal, silo_attrs, &silo_msg, &is_silo_enforced); if (ret) { talloc_free(tmp_ctx); return ret; } if (silo_msg == NULL || !is_silo_enforced) { /* No assigned silo. */ assigned_silo->is_assigned = false; assigned_silo->is_initialised = true; talloc_free(tmp_ctx); return LDB_SUCCESS; } /* The user does belong to a silo, so return the name of the silo. */ silo_name = ldb_msg_find_attr_as_string(silo_msg, "name", NULL); assigned_silo->name = talloc_steal(mem_ctx, silo_name); assigned_silo->is_assigned = true; assigned_silo->is_initialised = true; talloc_free(tmp_ctx); return LDB_SUCCESS; } static inline struct ldb_val talloc_steal_ldb_val(TALLOC_CTX *mem_ctx, struct ldb_val val) { val.data = talloc_steal(mem_ctx, val.data); return val; } static uint32_t claim_get_value_count(const struct CLAIM_ENTRY *claim) { switch (claim->type) { case CLAIM_TYPE_INT64: return claim->values.claim_int64.value_count; case CLAIM_TYPE_UINT64: return claim->values.claim_uint64.value_count; case CLAIM_TYPE_STRING: return claim->values.claim_string.value_count; case CLAIM_TYPE_BOOLEAN: return claim->values.claim_boolean.value_count; } smb_panic(__location__ ": unknown claim type"); return 0; } static int encode_claims_set(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct CLAIMS_SET *claims_set, DATA_BLOB *claims_blob) { TALLOC_CTX *tmp_ctx = NULL; enum ndr_err_code ndr_err; struct CLAIMS_SET_NDR *claims_set_info = NULL; struct CLAIMS_SET_METADATA *metadata = NULL; struct CLAIMS_SET_METADATA_NDR *metadata_ndr = NULL; tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { return ldb_oom(ldb); } metadata_ndr = talloc_zero(tmp_ctx, struct CLAIMS_SET_METADATA_NDR); if (metadata_ndr == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } metadata = talloc_zero(metadata_ndr, struct CLAIMS_SET_METADATA); if (metadata == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } claims_set_info = talloc_zero(metadata, struct CLAIMS_SET_NDR); if (claims_set_info == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } metadata_ndr->claims.metadata = metadata; metadata->claims_set = claims_set_info; metadata->compression_format = CLAIMS_COMPRESSION_FORMAT_XPRESS_HUFF; claims_set_info->claims.claims = claims_set; ndr_err = ndr_push_struct_blob(claims_blob, mem_ctx, metadata_ndr, (ndr_push_flags_fn_t)ndr_push_CLAIMS_SET_METADATA_NDR); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err); DBG_ERR("CLAIMS_SET_METADATA_NDR push failed: %s\n", nt_errstr(nt_status)); talloc_free(tmp_ctx); return ldb_operr(ldb); } talloc_free(tmp_ctx); return LDB_SUCCESS; } static bool is_schema_dn(struct ldb_dn *dn, struct ldb_dn *schema_dn) { if (ldb_dn_get_comp_num(dn) != (ldb_dn_get_comp_num(schema_dn) + 1)) { return false; } return ldb_dn_compare_base(schema_dn, dn) == 0; } static bool is_valid_claim_attribute_syntax(const DATA_BLOB source_syntax, uint64_t claim_value_type) { switch (claim_value_type) { case CLAIM_TYPE_STRING: if (data_blob_equals_str(source_syntax, "2.5.5.1")) { return true; } if (data_blob_equals_str(source_syntax, "2.5.5.12")) { return true; } if (data_blob_equals_str(source_syntax, "2.5.5.15")) { return true; } break; case CLAIM_TYPE_UINT64: if (data_blob_equals_str(source_syntax, "2.5.5.2")) { return true; } break; case CLAIM_TYPE_INT64: if (data_blob_equals_str(source_syntax, "2.5.5.9")) { return true; } if (data_blob_equals_str(source_syntax, "2.5.5.16")) { return true; } break; case CLAIM_TYPE_BOOLEAN: /* Note: MS-ADTS has a typo (2.2.5.8 instead of 2.5.5.8) */ if (data_blob_equals_str(source_syntax, "2.5.5.8")) { return true; } break; default: break; } return false; } static int get_all_claims(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *principal, uint32_t principal_class_id, DATA_BLOB *claims_blob) { TALLOC_CTX *tmp_ctx = NULL; const struct dsdb_schema *schema = NULL; struct ldb_dn *claim_config_container = NULL; struct ldb_dn *claim_types_child = NULL; struct ldb_dn *config_dn = ldb_get_config_basedn(ldb); struct ldb_dn *schema_dn = ldb_get_schema_basedn(ldb); bool ok; int ret; struct ldb_result *res = NULL; static const char * const attrs[] = { "Enabled", "msDS-ClaimAttributeSource", "msDS-ClaimSource", "msDS-ClaimSourceType", "msDS-ClaimTypeAppliesToClass", "msDS-ClaimValueType", "name", NULL }; const char **ad_claim_attrs = NULL; unsigned int ad_claim_attrs_count; struct ad_claim_info { struct ldb_val name; DATA_BLOB syntax; const char *attribute; enum CLAIM_TYPE claim_type; } *ad_claims = NULL; unsigned ad_claims_count; unsigned i; /* The structure which we'll use to build up the claims. */ struct CLAIMS_SET claims_set = {}; struct CLAIMS_ARRAY *ad_sourced_constructed = NULL; struct assigned_silo assigned_silo = new_assigned_silo(); *claims_blob = data_blob_null; tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { return ldb_oom(ldb); } schema = dsdb_get_schema(ldb, tmp_ctx); if (schema == NULL) { talloc_free(tmp_ctx); return ldb_operr(ldb); } /* Get the DN of the claims container. */ claim_config_container = ldb_dn_copy(tmp_ctx, config_dn); if (claim_config_container == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } claim_types_child = ldb_dn_new(tmp_ctx, ldb, "CN=Claim Types,CN=Claims Configuration,CN=Services"); if (claim_types_child == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } ok = ldb_dn_add_child(claim_config_container, claim_types_child); TALLOC_FREE(claim_types_child); if (!ok) { talloc_free(tmp_ctx); return ldb_operr(ldb); } /* Search for the claims container's children. */ ret = ldb_search(ldb, tmp_ctx, &res, claim_config_container, LDB_SCOPE_ONELEVEL, attrs, NULL); if (ret) { if (ret == LDB_ERR_NO_SUCH_OBJECT) { ret = LDB_SUCCESS; } talloc_free(tmp_ctx); return ret; } /* * Allocate enough space for all AD claim attributes, followed by space * for a NULL marker (so it can be passed as the attributes filter to an * LDB search). */ ad_claim_attrs = talloc_array(tmp_ctx, const char *, res->count + 1); if (ad_claim_attrs == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } ad_claims = talloc_array(tmp_ctx, struct ad_claim_info, res->count); if (ad_claims == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } ad_claims_count = ad_claim_attrs_count = 0; /* Loop through each child of the claims container. */ for (i = 0; i < res->count; ++i) { bool claim_applies = false; int enabled; uint64_t claim_value_type; const char *claim_source_type = NULL; const struct ldb_val *claim_attribute_source = NULL; const char *claim_source = NULL; /* * Does this claim apply to the most specific objectClass of the * principal? */ ret = claim_applies_to_class(tmp_ctx, ldb, schema, res->msgs[i], principal_class_id, &claim_applies); if (ret) { talloc_free(tmp_ctx); return ret; } if (!claim_applies) { /* If the claim doesn't apply, skip it. */ continue; } enabled = ldb_msg_find_attr_as_bool(res->msgs[i], "Enabled", 0); if (!enabled) { /* If the claim isn't enabled, skip it. */ continue; } claim_value_type = ldb_msg_find_attr_as_uint64(res->msgs[i], "msDS-ClaimValueType", 0); if (!claim_value_type) { continue; } claim_source_type = ldb_msg_find_attr_as_string(res->msgs[i], "msDS-ClaimSourceType", ""); /* Get the attribute used by the claim. */ claim_attribute_source = ldb_msg_find_ldb_val(res->msgs[i], "msDS-ClaimAttributeSource"); claim_source = ldb_msg_find_attr_as_string(res->msgs[i], "msDS-ClaimSource", NULL); if (strcasecmp(claim_source_type, "AD") == 0) { struct ldb_dn *claim_attribute_source_dn = NULL; const struct ldb_val *claim_attribute_source_rdn = NULL; const struct dsdb_attribute *claim_attribute_source_class = NULL; DATA_BLOB source_syntax; const char *attribute = NULL; const struct ldb_val *name = NULL; if (claim_attribute_source == NULL) { continue; } claim_attribute_source_dn = ldb_val_as_dn(ldb, tmp_ctx, claim_attribute_source); if (claim_attribute_source_dn == NULL) { talloc_free(tmp_ctx); return ldb_operr(ldb); } if (!is_schema_dn(claim_attribute_source_dn, schema_dn)) { /* This DN doesn't belong to the schema. */ continue; } claim_attribute_source_rdn = ldb_dn_get_rdn_val(claim_attribute_source_dn); if (claim_attribute_source_rdn == NULL) { /* No RDN, skip it. */ continue; } claim_attribute_source_class = dsdb_attribute_by_cn_ldb_val(schema, claim_attribute_source_rdn); claim_attribute_source_rdn = NULL; TALLOC_FREE(claim_attribute_source_dn); if (claim_attribute_source_class == NULL) { continue; } source_syntax = data_blob_string_const(claim_attribute_source_class->attributeSyntax_oid); if (source_syntax.data == NULL) { continue; } if (!is_valid_claim_attribute_syntax(source_syntax, claim_value_type)) { continue; } attribute = claim_attribute_source_class->lDAPDisplayName; if (attribute == NULL) { continue; } ret = add_attr_unique(tmp_ctx, ad_claim_attrs, &ad_claim_attrs_count, attribute); if (ret) { talloc_free(tmp_ctx); return ret; } name = ldb_msg_find_ldb_val(res->msgs[i], "name"); if (name == NULL) { name = &data_blob_null; } ad_claims[ad_claims_count++] = (struct ad_claim_info) { .name = *name, .syntax = source_syntax, .attribute = attribute, .claim_type = claim_value_type, }; } else if (silo_is_maybe_assigned(assigned_silo) && strcasecmp(claim_source_type, "Constructed") == 0) { const struct ldb_val *name = NULL; struct CLAIM_STRING *claim = NULL; struct CLAIM_ENTRY *claim_entry = NULL; const char *claim_value = NULL; if (claim_attribute_source != NULL) { continue; } if (claim_source != NULL) { continue; } name = ldb_msg_find_ldb_val(res->msgs[i], "name"); if (name == NULL || name->data == NULL) { continue; } /* Does the claim ID match exactly in case? */ if (strcmp((const char *)name->data, "ad://ext/AuthenticationSilo") != 0) { continue; } ret = get_assigned_silo(ldb, tmp_ctx, principal, &assigned_silo); if (ret) { talloc_free(tmp_ctx); return ret; } if (!assigned_silo.is_assigned) { continue; } if (ad_sourced_constructed == NULL) { claims_set.claims_arrays = talloc_realloc(tmp_ctx, claims_set.claims_arrays, struct CLAIMS_ARRAY, claims_set.claims_array_count + 1); if (claims_set.claims_arrays == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } ad_sourced_constructed = &claims_set.claims_arrays[claims_set.claims_array_count++]; *ad_sourced_constructed = (struct CLAIMS_ARRAY) { .claims_source_type = CLAIMS_SOURCE_TYPE_AD, }; } /* Add the claim to the array. */ ad_sourced_constructed->claim_entries = talloc_realloc( tmp_ctx, ad_sourced_constructed->claim_entries, struct CLAIM_ENTRY, ad_sourced_constructed->claims_count + 1); if (ad_sourced_constructed->claim_entries == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } claim_entry = &ad_sourced_constructed->claim_entries[ad_sourced_constructed->claims_count++]; /* Fill in the claim details and return the claim. */ claim_entry->id = "ad://ext/AuthenticationSilo"; claim_entry->type = CLAIM_TYPE_STRING; claim = &claim_entry->values.claim_string; claim->value_count = 1; claim->values = talloc_array(ad_sourced_constructed->claim_entries, const char *, claim->value_count); if (claim->values == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } claim_value = talloc_strdup(claim->values, assigned_silo.name); if (claim_value == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } claim->values[0] = claim_value; } } if (ad_claims_count) { struct ldb_message *principal_msg = NULL; /* Shrink the arrays to remove any unused space. */ ad_claim_attrs = talloc_realloc(tmp_ctx, ad_claim_attrs, const char *, ad_claim_attrs_count + 1); if (ad_claim_attrs == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } ad_claim_attrs[ad_claim_attrs_count] = NULL; ad_claims = talloc_realloc(tmp_ctx, ad_claims, struct ad_claim_info, ad_claims_count); if (ad_claims == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } ret = dsdb_search_one(ldb, tmp_ctx, &principal_msg, principal->dn, LDB_SCOPE_BASE, ad_claim_attrs, 0, NULL); if (ret != LDB_SUCCESS) { const char *dn = ldb_dn_get_linearized(principal->dn); DBG_ERR("Failed to find principal %s to construct claims\n", dn != NULL ? dn : ""); talloc_free(tmp_ctx); return ret; } /* * Ensure that only the attrs we asked for end up in the results * (it's fine if some are missing) */ SMB_ASSERT(principal_msg->num_elements <= ad_claim_attrs_count); for (i = 0; i < ad_claims_count; ++i) { const struct ldb_message_element *principal_attribute = NULL; struct CLAIM_ENTRY *claim_entry = NULL; uint32_t new_claims_array_count = claims_set.claims_array_count; /* Get the value of the claim attribute for the principal. */ principal_attribute = ldb_msg_find_element(principal_msg, ad_claims[i].attribute); if (principal_attribute == NULL) { continue; } /* Add the claim to the array. */ if (ad_sourced_constructed == NULL) { claims_set.claims_arrays = talloc_realloc(tmp_ctx, claims_set.claims_arrays, struct CLAIMS_ARRAY, new_claims_array_count + 1); if (claims_set.claims_arrays == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } ad_sourced_constructed = &claims_set.claims_arrays[new_claims_array_count++]; *ad_sourced_constructed = (struct CLAIMS_ARRAY) { .claims_source_type = CLAIMS_SOURCE_TYPE_AD, }; } ad_sourced_constructed->claim_entries = talloc_realloc( tmp_ctx, ad_sourced_constructed->claim_entries, struct CLAIM_ENTRY, ad_sourced_constructed->claims_count + 1); if (ad_sourced_constructed->claim_entries == NULL) { talloc_free(tmp_ctx); return ldb_oom(ldb); } claim_entry = &ad_sourced_constructed->claim_entries[ ad_sourced_constructed->claims_count]; ret = fill_claim_entry(ad_sourced_constructed->claim_entries, ldb, schema, principal_attribute, ad_claims[i].name, ad_claims[i].syntax, ad_claims[i].claim_type, claim_entry); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; } if (claim_get_value_count(claim_entry) > 0) { /* * If the claim contains values, add it to the * array(s). */ ++ad_sourced_constructed->claims_count; claims_set.claims_array_count = new_claims_array_count; } } } if (claims_set.claims_array_count == 0) { /* If we have no claims, we're done. */ talloc_free(tmp_ctx); return LDB_SUCCESS; } /* Encode the claims ready to go into a PAC buffer. */ ret = encode_claims_set(ldb, mem_ctx, &claims_set, claims_blob); talloc_free(tmp_ctx); return ret; } int get_claims_for_principal(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *principal, DATA_BLOB *claims_blob) { struct ldb_message_element *principal_class_el = NULL; struct dsdb_schema *schema = NULL; const struct dsdb_class *principal_class = NULL; *claims_blob = data_blob_null; if (!ad_claims_are_issued(ldb)) { return LDB_SUCCESS; } principal_class_el = ldb_msg_find_element(principal, "objectClass"); if (principal_class_el == NULL) { return ldb_operr(ldb); } schema = dsdb_get_schema(ldb, mem_ctx); if (schema == NULL) { return ldb_operr(ldb); } principal_class = dsdb_get_last_structural_class(schema, principal_class_el); if (principal_class == NULL) { return ldb_operr(ldb); } return get_all_claims(ldb, mem_ctx, principal, principal_class->governsID_id, claims_blob); }