1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/source4/kdc/ad_claims.c
Joseph Sutton 5e164cc2d6 s4:kdc: Move encode_claims_set() into the auth_session subsystem
Some functions in the auth_session subsystem will need to be able to
call encode_claims_set(). Moving said function lets them do that whilst
avoiding circular dependencies and additional public dependencies.

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2023-09-27 02:43:28 +00:00

1263 lines
31 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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 <http://www.gnu.org/licenses/>.
*/
#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"
#include "auth/session.h"
#undef strcasecmp
bool ad_claims_are_issued(struct ldb_context *samdb)
{
/*
* Claims arent 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 : "<unknown>",
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 : "<unknown>",
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 : "<unknown>",
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 : "<unknown>",
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 = talloc_strndup(mem_ctx,
(const char *)name.data,
name.length);
if (claim_entry->id == NULL) {
return ldb_oom(ldb);
}
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 whether 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 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,
struct CLAIMS_SET **claims_set_out)
{
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 = NULL;
struct CLAIMS_ARRAY *ad_sourced_constructed = NULL;
struct assigned_silo assigned_silo = new_assigned_silo();
*claims_set_out = NULL;
tmp_ctx = talloc_new(mem_ctx);
if (tmp_ctx == NULL) {
return ldb_oom(ldb);
}
claims_set = talloc_zero(tmp_ctx, struct CLAIMS_SET);
if (claims_set == NULL) {
talloc_free(tmp_ctx);
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(claims_set,
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(
claims_set->claims_arrays,
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 : "<NULL>");
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(claims_set,
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(
claims_set->claims_arrays,
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) {
*claims_set_out = talloc_steal(mem_ctx, claims_set);
}
talloc_free(tmp_ctx);
return LDB_SUCCESS;
}
int get_claims_set_for_principal(struct ldb_context *ldb,
TALLOC_CTX *mem_ctx,
const struct ldb_message *principal,
struct CLAIMS_SET **claims_set_out)
{
struct ldb_message_element *principal_class_el = NULL;
struct dsdb_schema *schema = NULL;
const struct dsdb_class *principal_class = NULL;
*claims_set_out = 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_set_out);
}
int get_claims_blob_for_principal(struct ldb_context *ldb,
TALLOC_CTX *mem_ctx,
const struct ldb_message *principal,
DATA_BLOB *claims_blob_out)
{
struct CLAIMS_SET *claims_set = NULL;
int ret;
NTSTATUS status;
*claims_blob_out = data_blob_null;
ret = get_claims_set_for_principal(ldb,
mem_ctx,
principal,
&claims_set);
if (ret) {
return ret;
}
if (claims_set == NULL) {
return LDB_SUCCESS;
}
/* Encode the claims ready to go into a PAC buffer. */
status = encode_claims_set(mem_ctx, claims_set, claims_blob_out);
if (!NT_STATUS_IS_OK(status)) {
ret = LDB_ERR_OPERATIONS_ERROR;
talloc_free(claims_set);
}
return ret;
}