2023-03-02 23:17:39 +03:00
/*
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"
2023-03-28 05:10:50 +03:00
# include "source4/kdc/authn_policy_util.h"
2023-03-02 23:17:39 +03:00
# 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
2023-04-03 07:49:50 +03:00
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 ;
}
2023-03-02 23:17:39 +03:00
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 " ;
}
2023-04-03 07:56:56 +03:00
DBG_WARNING ( " Failed to interpret value %s as INT64 "
2023-03-02 23:17:39 +03:00
" 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 " ;
}
2023-04-03 07:56:56 +03:00
DBG_WARNING ( " Failed to interpret value %s as UINT64 "
2023-03-02 23:17:39 +03:00
" 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 " ;
}
2023-04-03 07:56:56 +03:00
DBG_WARNING ( " Failed to interpret value %s as BOOL "
2023-03-02 23:17:39 +03:00
" 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 ;
}
2023-04-03 04:07:30 +03:00
val = talloc_strndup ( claim - > values ,
2023-03-02 23:17:39 +03:00
( 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 ;
}
2023-03-28 05:10:50 +03:00
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 ;
}
2023-03-02 23:17:39 +03:00
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 ,
2023-05-17 02:55:16 +03:00
const struct ldb_message * principal ,
2023-03-02 23:17:39 +03:00
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 ;
2023-03-28 05:10:50 +03:00
struct assigned_silo assigned_silo = new_assigned_silo ( ) ;
2023-03-02 23:17:39 +03:00
* 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
2023-05-17 03:02:47 +03:00
* for a NULL marker ( so it can be passed as the attributes filter to an
* LDB search ) .
2023-03-02 23:17:39 +03:00
*/
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 ;
2023-03-28 05:10:50 +03:00
const char * claim_source = NULL ;
2023-03-02 23:17:39 +03:00
/*
* 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 " ) ;
2023-03-28 05:10:50 +03:00
claim_source = ldb_msg_find_attr_as_string ( res - > msgs [ i ] ,
" msDS-ClaimSource " ,
NULL ) ;
2023-03-02 23:17:39 +03:00
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 ,
} ;
2023-03-28 05:10:50 +03:00
} 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 ;
2023-03-02 23:17:39 +03:00
}
}
if ( ad_claims_count ) {
2023-05-17 03:02:47 +03:00
struct ldb_message * principal_msg = NULL ;
2023-03-02 23:17:39 +03:00
/* 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 ) ;
}
2023-05-17 03:02:47 +03:00
ret = dsdb_search_one ( ldb ,
tmp_ctx ,
& principal_msg ,
principal - > dn ,
LDB_SCOPE_BASE ,
ad_claim_attrs ,
0 ,
NULL ) ;
2023-03-02 23:17:39 +03:00
if ( ret ! = LDB_SUCCESS ) {
2023-05-17 02:55:16 +03:00
const char * dn = ldb_dn_get_linearized ( principal - > dn ) ;
2023-03-02 23:17:39 +03:00
DBG_ERR ( " Failed to find principal %s to construct claims \n " ,
2023-05-17 02:55:16 +03:00
dn ! = NULL ? dn : " <NULL> " ) ;
2023-03-02 23:17:39 +03:00
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. */
2023-05-17 03:02:47 +03:00
principal_attribute = ldb_msg_find_element ( principal_msg ,
2023-03-02 23:17:39 +03:00
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 ,
2023-05-17 02:55:16 +03:00
const struct ldb_message * principal ,
2023-03-02 23:17:39 +03:00
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 ;
2023-04-03 07:49:50 +03:00
if ( ! ad_claims_are_issued ( ldb ) ) {
return LDB_SUCCESS ;
}
2023-05-17 03:07:44 +03:00
principal_class_el = ldb_msg_find_element ( principal ,
2023-03-02 23:17:39 +03:00
" 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 ,
2023-05-17 02:55:16 +03:00
principal ,
2023-03-02 23:17:39 +03:00
principal_class - > governsID_id ,
claims_blob ) ;
}