mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
3afac3f8f7
get_claims_for_principal() is a new function that creates a claims blob for a principal based on attributes in the database. It's not hooked into the KDC yet, so this entails no change in behaviour. Constructed claims and certificate claims are not supported yet. Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
1094 lines
27 KiB
C
1094 lines
27 KiB
C
/*
|
|
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 "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
|
|
|
|
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 intepret 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 intepret 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 intepret 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(mem_ctx,
|
|
(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;
|
|
}
|
|
|
|
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,
|
|
struct ldb_dn *principal_dn,
|
|
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;
|
|
|
|
*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
|
|
* 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;
|
|
|
|
/*
|
|
* 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");
|
|
|
|
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,
|
|
};
|
|
}
|
|
}
|
|
|
|
if (ad_claims_count) {
|
|
struct ldb_result *principal_res = NULL;
|
|
const 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 = ldb_search(ldb, tmp_ctx, &principal_res,
|
|
principal_dn,
|
|
LDB_SCOPE_BASE,
|
|
ad_claim_attrs, NULL);
|
|
if (ret != LDB_SUCCESS) {
|
|
DBG_ERR("Failed to find principal %s to construct claims\n",
|
|
ldb_dn_get_linearized(principal_dn));
|
|
talloc_free(tmp_ctx);
|
|
return ret;
|
|
}
|
|
|
|
principal_msg = principal_res->msgs[0];
|
|
|
|
/*
|
|
* 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_res->msgs[0],
|
|
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,
|
|
struct ldb_dn *principal_dn,
|
|
DATA_BLOB *claims_blob)
|
|
{
|
|
struct ldb_result *principal_res = NULL;
|
|
static const char * const principal_attrs[] = {
|
|
"objectClass",
|
|
NULL
|
|
};
|
|
|
|
struct ldb_message_element *principal_class_el = NULL;
|
|
struct dsdb_schema *schema = NULL;
|
|
const struct dsdb_class *principal_class = NULL;
|
|
|
|
int ret;
|
|
|
|
*claims_blob = data_blob_null;
|
|
|
|
ret = ldb_search(ldb, mem_ctx, &principal_res,
|
|
principal_dn,
|
|
LDB_SCOPE_BASE,
|
|
principal_attrs, NULL);
|
|
if (ret != LDB_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
principal_class_el = ldb_msg_find_element(principal_res->msgs[0],
|
|
"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_dn,
|
|
principal_class->governsID_id,
|
|
claims_blob);
|
|
}
|