2010-01-28 09:27:11 +03:00
/*
Unix SMB / CIFS implementation .
Database Glue between Samba and the KDC
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2005 - 2009
Copyright ( C ) Simo Sorce < idra @ samba . org > 2010
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 "includes.h"
# include "libcli/security/security.h"
2021-12-14 13:16:12 +03:00
# include "librpc/gen_ndr/ndr_security.h"
2010-01-28 09:27:11 +03:00
# include "auth/auth.h"
# include "auth/auth_sam.h"
2024-04-15 05:46:47 +03:00
# include "dsdb/gmsa/util.h"
2010-01-28 09:27:11 +03:00
# include "dsdb/samdb/samdb.h"
2024-05-21 02:14:50 +03:00
# include "dsdb/common/proto.h"
2010-02-16 07:49:29 +03:00
# include "dsdb/common/util.h"
2010-01-28 09:27:11 +03:00
# include "librpc/gen_ndr/ndr_drsblobs.h"
# include "param/param.h"
2018-09-20 05:24:11 +03:00
# include "param/secrets.h"
2024-04-15 05:46:47 +03:00
# include "lib/crypto/gkdi.h"
2010-01-28 09:27:11 +03:00
# include "../lib/crypto/md4.h"
2024-04-15 05:46:47 +03:00
# include "lib/util/memory.h"
2010-01-28 09:27:11 +03:00
# include "system/kerberos.h"
2010-06-24 03:40:16 +04:00
# include "auth/kerberos/kerberos.h"
2023-05-04 06:06:40 +03:00
# include "kdc/authn_policy_util.h"
2014-05-08 19:13:04 +04:00
# include "kdc/sdb.h"
2010-01-28 09:27:11 +03:00
# include "kdc/samba_kdc.h"
2011-03-19 02:43:50 +03:00
# include "kdc/db-glue.h"
2022-03-18 01:13:40 +03:00
# include "kdc/pac-glue.h"
2017-04-03 07:11:35 +03:00
# include "librpc/gen_ndr/ndr_irpc_c.h"
# include "lib/messaging/irpc.h"
2022-09-09 13:32:57 +03:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_KERBEROS
2020-08-07 23:27:39 +03:00
# undef strcasecmp
# undef strncasecmp
2010-01-28 09:27:11 +03:00
2011-03-04 18:12:29 +03:00
# define SAMBA_KVNO_GET_KRBTGT(kvno) \
( ( uint16_t ) ( ( ( uint32_t ) kvno ) > > 16 ) )
2022-02-16 16:11:10 +03:00
# define SAMBA_KVNO_GET_VALUE(kvno) \
( ( uint16_t ) ( ( ( uint32_t ) kvno ) & 0xFFFF ) )
2011-03-04 18:12:29 +03:00
# define SAMBA_KVNO_AND_KRBTGT(kvno, krbtgt) \
( ( krb5_kvno ) ( ( ( ( uint32_t ) kvno ) & 0xFFFF ) | \
( ( ( ( uint32_t ) krbtgt ) < < 16 ) & 0xFFFF0000 ) ) )
2010-01-28 09:27:11 +03:00
enum trust_direction {
UNKNOWN = 0 ,
INBOUND = LSA_TRUST_DIRECTION_INBOUND ,
OUTBOUND = LSA_TRUST_DIRECTION_OUTBOUND
} ;
static const char * trust_attrs [ ] = {
2018-02-01 13:10:14 +03:00
" securityIdentifier " ,
" flatName " ,
2010-01-28 09:27:11 +03:00
" trustPartner " ,
2018-02-01 13:10:14 +03:00
" trustAttributes " ,
" trustDirection " ,
" trustType " ,
" msDS-TrustForestTrustInfo " ,
2010-01-28 09:27:11 +03:00
" trustAuthIncoming " ,
" trustAuthOutgoing " ,
" whenCreated " ,
" msDS-SupportedEncryptionTypes " ,
NULL
} ;
2017-04-03 07:11:35 +03:00
/*
send a message to the drepl server telling it to initiate a
REPL_SECRET getncchanges extended op to fetch the users secrets
*/
static void auth_sam_trigger_repl_secret ( TALLOC_CTX * mem_ctx ,
struct imessaging_context * msg_ctx ,
struct tevent_context * event_ctx ,
struct ldb_dn * user_dn )
{
struct dcerpc_binding_handle * irpc_handle ;
struct drepl_trigger_repl_secret r ;
struct tevent_req * req ;
TALLOC_CTX * tmp_ctx ;
tmp_ctx = talloc_new ( mem_ctx ) ;
if ( tmp_ctx = = NULL ) {
return ;
}
irpc_handle = irpc_binding_handle_by_name ( tmp_ctx , msg_ctx ,
" dreplsrv " ,
& ndr_table_irpc ) ;
if ( irpc_handle = = NULL ) {
2023-07-21 02:44:05 +03:00
DBG_WARNING ( " Unable to get binding handle for dreplsrv \n " ) ;
2017-04-03 07:11:35 +03:00
TALLOC_FREE ( tmp_ctx ) ;
return ;
}
r . in . user_dn = ldb_dn_get_linearized ( user_dn ) ;
2023-08-10 06:53:23 +03:00
if ( r . in . user_dn = = NULL ) {
DBG_WARNING ( " Unable to get user DN \n " ) ;
TALLOC_FREE ( tmp_ctx ) ;
return ;
}
2017-04-03 07:11:35 +03:00
/*
* This seem to rely on the current IRPC implementation ,
* which delivers the message in the _send function .
*
* TODO : we need a ONE_WAY IRPC handle and register
* a callback and wait for it to be triggered !
*/
req = dcerpc_drepl_trigger_repl_secret_r_send ( tmp_ctx ,
event_ctx ,
irpc_handle ,
& r ) ;
/* we aren't interested in a reply */
talloc_free ( req ) ;
TALLOC_FREE ( tmp_ctx ) ;
}
2012-03-31 09:15:36 +04:00
2014-05-08 12:49:00 +04:00
static time_t ldb_msg_find_krb5time_ldap_time ( struct ldb_message * msg , const char * attr , time_t default_val )
2010-01-28 09:27:11 +03:00
{
2023-09-20 22:32:30 +03:00
const struct ldb_val * gentime = NULL ;
time_t t ;
int ret ;
2010-01-28 09:27:11 +03:00
2023-09-20 22:32:30 +03:00
gentime = ldb_msg_find_ldb_val ( msg , attr ) ;
ret = ldb_val_to_time ( gentime , & t ) ;
if ( ret ) {
2010-01-28 09:27:11 +03:00
return default_val ;
}
2023-09-20 22:32:30 +03:00
return t ;
2010-01-28 09:27:11 +03:00
}
2014-05-08 19:13:04 +04:00
static struct SDBFlags uf2SDBFlags ( krb5_context context , uint32_t userAccountControl , enum samba_kdc_ent_type ent_type )
2010-01-28 09:27:11 +03:00
{
2023-10-12 01:54:50 +03:00
struct SDBFlags flags = { } ;
2010-01-28 09:27:11 +03:00
/* we don't allow kadmin deletes */
flags . immutable = 1 ;
/* mark the principal as invalid to start with */
flags . invalid = 1 ;
flags . renewable = 1 ;
/* All accounts are servers, but this may be disabled again in the caller */
flags . server = 1 ;
/* Account types - clear the invalid bit if it turns out to be valid */
if ( userAccountControl & UF_NORMAL_ACCOUNT ) {
if ( ent_type = = SAMBA_KDC_ENT_TYPE_CLIENT | | ent_type = = SAMBA_KDC_ENT_TYPE_ANY ) {
flags . client = 1 ;
}
flags . invalid = 0 ;
}
if ( userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT ) {
if ( ent_type = = SAMBA_KDC_ENT_TYPE_CLIENT | | ent_type = = SAMBA_KDC_ENT_TYPE_ANY ) {
flags . client = 1 ;
}
flags . invalid = 0 ;
}
if ( userAccountControl & UF_WORKSTATION_TRUST_ACCOUNT ) {
if ( ent_type = = SAMBA_KDC_ENT_TYPE_CLIENT | | ent_type = = SAMBA_KDC_ENT_TYPE_ANY ) {
flags . client = 1 ;
}
flags . invalid = 0 ;
}
if ( userAccountControl & UF_SERVER_TRUST_ACCOUNT ) {
if ( ent_type = = SAMBA_KDC_ENT_TYPE_CLIENT | | ent_type = = SAMBA_KDC_ENT_TYPE_ANY ) {
flags . client = 1 ;
}
flags . invalid = 0 ;
}
/* Not permitted to act as a client if disabled */
if ( userAccountControl & UF_ACCOUNTDISABLE ) {
flags . client = 0 ;
}
if ( userAccountControl & UF_LOCKOUT ) {
2013-10-29 03:31:46 +04:00
flags . locked_out = 1 ;
2010-01-28 09:27:11 +03:00
}
/*
2022-04-29 03:11:35 +03:00
if ( userAccountControl & UF_PASSWD_NOTREQD ) {
2010-01-28 09:27:11 +03:00
flags . invalid = 1 ;
}
*/
/*
2022-04-29 03:11:35 +03:00
UF_PASSWD_CANT_CHANGE and UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED are irrelevant
2010-01-28 09:27:11 +03:00
*/
if ( userAccountControl & UF_TEMP_DUPLICATE_ACCOUNT ) {
flags . invalid = 1 ;
}
/* UF_DONT_EXPIRE_PASSWD and UF_USE_DES_KEY_ONLY handled in samba_kdc_message2entry() */
/*
if ( userAccountControl & UF_MNS_LOGON_ACCOUNT ) {
flags . invalid = 1 ;
}
*/
if ( userAccountControl & UF_SMARTCARD_REQUIRED ) {
flags . require_hwauth = 1 ;
}
if ( userAccountControl & UF_TRUSTED_FOR_DELEGATION ) {
flags . ok_as_delegate = 1 ;
}
2011-04-07 14:16:16 +04:00
if ( userAccountControl & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION ) {
/*
* this is confusing . . .
*
* UF_TRUSTED_FOR_DELEGATION
* = > ok_as_delegate
*
* and
*
* UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
* = > trusted_for_delegation
*/
flags . trusted_for_delegation = 1 ;
}
2010-01-28 09:27:11 +03:00
if ( ! ( userAccountControl & UF_NOT_DELEGATED ) ) {
flags . forwardable = 1 ;
flags . proxiable = 1 ;
}
if ( userAccountControl & UF_DONT_REQUIRE_PREAUTH ) {
flags . require_preauth = 0 ;
} else {
flags . require_preauth = 1 ;
2016-01-08 04:08:18 +03:00
}
2010-01-28 09:27:11 +03:00
2016-01-08 04:08:18 +03:00
if ( userAccountControl & UF_NO_AUTH_DATA_REQUIRED ) {
flags . no_auth_data_reqd = 1 ;
2010-01-28 09:27:11 +03:00
}
2016-01-08 04:08:18 +03:00
2010-01-28 09:27:11 +03:00
return flags ;
}
static int samba_kdc_entry_destructor ( struct samba_kdc_entry * p )
{
2022-03-22 19:04:22 +03:00
if ( p - > db_entry ! = NULL ) {
/*
* A sdb_entry still has a reference
*/
return - 1 ;
}
if ( p - > kdc_entry ! = NULL ) {
/*
* hdb_entry or krb5_db_entry still
* have a reference . . .
*/
return - 1 ;
}
2015-02-25 13:52:45 +03:00
return 0 ;
2010-01-28 09:27:11 +03:00
}
2016-09-08 11:50:58 +03:00
/*
* Sort keys in descending order of strength .
*
2023-09-04 07:52:57 +03:00
* Explanation from Greg Hudson :
2016-09-08 11:50:58 +03:00
*
* To encrypt tickets only the first returned key is used by the MIT KDC . The
* other keys just communicate support for session key enctypes , and aren ' t
* really used . The encryption key for the ticket enc part doesn ' t have
* to be of a type requested by the client . The session key enctype is chosen
* based on the client preference order , limited by the set of enctypes present
* in the server keys ( unless the string attribute is set on the server
* principal overriding that set ) .
*/
2022-02-08 17:08:57 +03:00
static int sdb_key_strength_priority ( krb5_enctype etype )
2016-09-08 11:50:58 +03:00
{
static const krb5_enctype etype_list [ ] = {
ENCTYPE_AES256_CTS_HMAC_SHA1_96 ,
ENCTYPE_AES128_CTS_HMAC_SHA1_96 ,
ENCTYPE_DES3_CBC_SHA1 ,
ENCTYPE_ARCFOUR_HMAC ,
ENCTYPE_DES_CBC_MD5 ,
ENCTYPE_DES_CBC_MD4 ,
ENCTYPE_DES_CBC_CRC ,
ENCTYPE_NULL
} ;
2022-02-08 17:08:57 +03:00
int i ;
2016-09-08 11:50:58 +03:00
2022-02-08 17:08:57 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( etype_list ) ; i + + ) {
if ( etype = = etype_list [ i ] ) {
break ;
}
2016-09-08 11:50:58 +03:00
}
2022-02-08 17:08:57 +03:00
return ARRAY_SIZE ( etype_list ) - i ;
}
2016-09-08 11:50:58 +03:00
2022-02-08 17:08:57 +03:00
static int sdb_key_strength_cmp ( const struct sdb_key * k1 , const struct sdb_key * k2 )
{
int p1 = sdb_key_strength_priority ( KRB5_KEY_TYPE ( & k1 - > key ) ) ;
int p2 = sdb_key_strength_priority ( KRB5_KEY_TYPE ( & k2 - > key ) ) ;
2016-09-08 11:50:58 +03:00
2022-02-08 17:08:57 +03:00
if ( p1 = = p2 ) {
return 0 ;
2016-09-08 11:50:58 +03:00
}
2022-02-08 17:08:57 +03:00
if ( p1 > p2 ) {
/*
* Higher priority comes first
*/
2016-09-08 11:50:58 +03:00
return - 1 ;
2022-02-08 17:08:57 +03:00
} else {
return 1 ;
2016-09-08 11:50:58 +03:00
}
2022-02-08 17:08:57 +03:00
}
2016-09-08 11:50:58 +03:00
2022-02-08 17:08:57 +03:00
static void samba_kdc_sort_keys ( struct sdb_keys * keys )
{
if ( keys = = NULL ) {
return ;
}
2016-09-08 11:50:58 +03:00
2022-02-08 17:08:57 +03:00
TYPESAFE_QSORT ( keys - > val , keys - > len , sdb_key_strength_cmp ) ;
2016-09-08 11:50:58 +03:00
}
2018-09-20 05:24:11 +03:00
int samba_kdc_set_fixed_keys ( krb5_context context ,
const struct ldb_val * secretbuffer ,
2022-03-22 23:47:53 +03:00
uint32_t supported_enctypes ,
2022-03-22 20:09:33 +03:00
struct sdb_keys * keys )
2018-09-20 05:24:11 +03:00
{
uint16_t allocated_keys = 0 ;
int ret ;
allocated_keys = 3 ;
2022-03-22 20:09:33 +03:00
keys - > len = 0 ;
keys - > val = calloc ( allocated_keys , sizeof ( struct sdb_key ) ) ;
if ( keys - > val = = NULL ) {
2018-09-20 05:24:11 +03:00
memset ( secretbuffer - > data , 0 , secretbuffer - > length ) ;
ret = ENOMEM ;
goto out ;
}
if ( supported_enctypes & ENC_HMAC_SHA1_96_AES256 ) {
struct sdb_key key = { } ;
ret = smb_krb5_keyblock_init_contents ( context ,
ENCTYPE_AES256_CTS_HMAC_SHA1_96 ,
secretbuffer - > data ,
MIN ( secretbuffer - > length , 32 ) ,
& key . key ) ;
if ( ret ) {
memset ( secretbuffer - > data , 0 , secretbuffer - > length ) ;
goto out ;
}
2022-03-22 20:09:33 +03:00
keys - > val [ keys - > len ] = key ;
keys - > len + + ;
2018-09-20 05:24:11 +03:00
}
if ( supported_enctypes & ENC_HMAC_SHA1_96_AES128 ) {
struct sdb_key key = { } ;
ret = smb_krb5_keyblock_init_contents ( context ,
ENCTYPE_AES128_CTS_HMAC_SHA1_96 ,
secretbuffer - > data ,
MIN ( secretbuffer - > length , 16 ) ,
& key . key ) ;
if ( ret ) {
memset ( secretbuffer - > data , 0 , secretbuffer - > length ) ;
goto out ;
}
2022-03-22 20:09:33 +03:00
keys - > val [ keys - > len ] = key ;
keys - > len + + ;
2018-09-20 05:24:11 +03:00
}
if ( supported_enctypes & ENC_RC4_HMAC_MD5 ) {
struct sdb_key key = { } ;
ret = smb_krb5_keyblock_init_contents ( context ,
ENCTYPE_ARCFOUR_HMAC ,
secretbuffer - > data ,
MIN ( secretbuffer - > length , 16 ) ,
& key . key ) ;
if ( ret ) {
memset ( secretbuffer - > data , 0 , secretbuffer - > length ) ;
goto out ;
}
2022-03-22 20:09:33 +03:00
keys - > val [ keys - > len ] = key ;
keys - > len + + ;
2018-09-20 05:24:11 +03:00
}
ret = 0 ;
out :
return ret ;
}
static int samba_kdc_set_random_keys ( krb5_context context ,
2022-03-22 23:47:53 +03:00
uint32_t supported_enctypes ,
struct sdb_keys * keys )
2018-09-20 05:24:11 +03:00
{
struct ldb_val secret_val ;
uint8_t secretbuffer [ 32 ] ;
/*
* Fake keys until we have a better way to reject
* non - pkinit requests .
*
* We just need to indicate which encryption types are
* supported .
*/
generate_secret_buffer ( secretbuffer , sizeof ( secretbuffer ) ) ;
secret_val = data_blob_const ( secretbuffer ,
sizeof ( secretbuffer ) ) ;
2022-03-22 23:47:53 +03:00
return samba_kdc_set_fixed_keys ( context ,
2018-09-20 05:24:11 +03:00
& secret_val ,
2022-03-22 23:47:53 +03:00
supported_enctypes ,
2022-03-22 20:11:44 +03:00
keys ) ;
2018-09-20 05:24:11 +03:00
}
2022-02-11 23:46:28 +03:00
struct samba_kdc_user_keys {
struct sdb_keys * skeys ;
uint32_t kvno ;
uint32_t * returned_kvno ;
uint32_t supported_enctypes ;
uint32_t * available_enctypes ;
const struct samr_Password * nthash ;
const char * salt_string ;
uint16_t num_pkeys ;
const struct package_PrimaryKerberosKey4 * pkeys ;
} ;
static krb5_error_code samba_kdc_fill_user_keys ( krb5_context context ,
struct samba_kdc_user_keys * p )
{
/*
* Make sure we ' ll never reveal DES keys
*/
2022-11-01 05:20:47 +03:00
uint32_t supported_enctypes = p - > supported_enctypes & = ~ ( ENC_CRC32 | ENC_RSA_MD5 ) ;
2022-02-11 23:46:28 +03:00
uint32_t _available_enctypes = 0 ;
uint32_t * available_enctypes = p - > available_enctypes ;
uint32_t _returned_kvno = 0 ;
uint32_t * returned_kvno = p - > returned_kvno ;
uint32_t num_pkeys = p - > num_pkeys ;
uint32_t allocated_keys = num_pkeys ;
uint32_t i ;
int ret ;
if ( available_enctypes = = NULL ) {
available_enctypes = & _available_enctypes ;
}
* available_enctypes = 0 ;
if ( returned_kvno = = NULL ) {
returned_kvno = & _returned_kvno ;
}
* returned_kvno = p - > kvno ;
if ( p - > nthash ! = NULL ) {
allocated_keys + = 1 ;
}
allocated_keys = MAX ( 1 , allocated_keys ) ;
/* allocate space to decode into */
p - > skeys - > len = 0 ;
p - > skeys - > val = calloc ( allocated_keys , sizeof ( struct sdb_key ) ) ;
if ( p - > skeys - > val = = NULL ) {
return ENOMEM ;
}
for ( i = 0 ; i < num_pkeys ; i + + ) {
struct sdb_key key = { } ;
uint32_t enctype_bit ;
if ( p - > pkeys [ i ] . value = = NULL ) {
continue ;
}
enctype_bit = kerberos_enctype_to_bitmap ( p - > pkeys [ i ] . keytype ) ;
if ( ! ( enctype_bit & supported_enctypes ) ) {
continue ;
}
if ( p - > salt_string ! = NULL ) {
DATA_BLOB salt ;
salt = data_blob_string_const ( p - > salt_string ) ;
key . salt = calloc ( 1 , sizeof ( * key . salt ) ) ;
if ( key . salt = = NULL ) {
ret = ENOMEM ;
goto fail ;
}
key . salt - > type = KRB5_PW_SALT ;
ret = smb_krb5_copy_data_contents ( & key . salt - > salt ,
salt . data ,
salt . length ) ;
if ( ret ) {
2023-09-05 05:17:14 +03:00
* key . salt = ( struct sdb_salt ) { } ;
2022-02-11 23:46:28 +03:00
sdb_key_free ( & key ) ;
goto fail ;
}
}
ret = smb_krb5_keyblock_init_contents ( context ,
p - > pkeys [ i ] . keytype ,
p - > pkeys [ i ] . value - > data ,
p - > pkeys [ i ] . value - > length ,
& key . key ) ;
if ( ret = = 0 ) {
p - > skeys - > val [ p - > skeys - > len + + ] = key ;
* available_enctypes | = enctype_bit ;
continue ;
}
ZERO_STRUCT ( key . key ) ;
sdb_key_free ( & key ) ;
if ( ret = = KRB5_PROG_ETYPE_NOSUPP ) {
DEBUG ( 2 , ( " Unsupported keytype ignored - type %u \n " ,
p - > pkeys [ i ] . keytype ) ) ;
ret = 0 ;
continue ;
}
goto fail ;
}
if ( p - > nthash ! = NULL & & ( supported_enctypes & ENC_RC4_HMAC_MD5 ) ) {
struct sdb_key key = { } ;
ret = smb_krb5_keyblock_init_contents ( context ,
ENCTYPE_ARCFOUR_HMAC ,
p - > nthash - > hash ,
sizeof ( p - > nthash - > hash ) ,
& key . key ) ;
if ( ret = = 0 ) {
p - > skeys - > val [ p - > skeys - > len + + ] = key ;
* available_enctypes | = ENC_RC4_HMAC_MD5 ;
} else if ( ret = = KRB5_PROG_ETYPE_NOSUPP ) {
DEBUG ( 2 , ( " Unsupported keytype ignored - type %u \n " ,
ENCTYPE_ARCFOUR_HMAC ) ) ;
ret = 0 ;
}
if ( ret ! = 0 ) {
goto fail ;
}
}
samba_kdc_sort_keys ( p - > skeys ) ;
return 0 ;
fail :
sdb_keys_free ( p - > skeys ) ;
return ret ;
}
2018-09-20 05:24:11 +03:00
2024-04-15 05:46:47 +03:00
static krb5_error_code samba_kdc_merge_keys ( struct sdb_keys * keys ,
struct sdb_keys * old_keys )
{
unsigned num_keys ;
unsigned num_old_keys ;
unsigned total_keys ;
unsigned j ;
struct sdb_key * skeys = NULL ;
if ( keys = = NULL | | old_keys = = NULL ) {
return EINVAL ;
}
num_keys = keys - > len ;
num_old_keys = old_keys - > len ;
total_keys = num_keys + num_old_keys ;
skeys = realloc ( keys - > val , total_keys * sizeof keys - > val [ 0 ] ) ;
if ( skeys = = NULL ) {
return ENOMEM ;
}
keys - > val = skeys ;
for ( j = 0 ; j < num_old_keys ; + + j ) {
keys - > val [ num_keys + j ] = old_keys - > val [ j ] ;
}
keys - > len = total_keys ;
old_keys - > len = 0 ;
SAFE_FREE ( old_keys - > val ) ;
return 0 ;
}
2022-03-08 12:49:31 +03:00
krb5_error_code samba_kdc_message2entry_keys ( krb5_context context ,
TALLOC_CTX * mem_ctx ,
2024-04-15 05:39:45 +03:00
struct ldb_context * ldb ,
2022-03-08 12:49:31 +03:00
const struct ldb_message * msg ,
bool is_krbtgt ,
bool is_rodc ,
uint32_t userAccountControl ,
enum samba_kdc_ent_type ent_type ,
unsigned flags ,
krb5_kvno requested_kvno ,
struct sdb_entry * entry ,
const uint32_t supported_enctypes_in ,
uint32_t * supported_enctypes_out )
2010-01-28 09:27:11 +03:00
{
krb5_error_code ret = 0 ;
enum ndr_err_code ndr_err ;
struct samr_Password * hash ;
2022-02-11 23:42:06 +03:00
unsigned int num_ntPwdHistory = 0 ;
struct samr_Password * ntPwdHistory = NULL ;
struct samr_Password * old_hash = NULL ;
struct samr_Password * older_hash = NULL ;
2010-01-28 09:27:11 +03:00
const struct ldb_val * sc_val ;
struct supplementalCredentialsBlob scb ;
struct supplementalCredentialsPackage * scpk = NULL ;
struct package_PrimaryKerberosBlob _pkb ;
struct package_PrimaryKerberosCtr4 * pkb4 = NULL ;
2022-02-16 16:11:10 +03:00
int krbtgt_number = 0 ;
uint32_t current_kvno ;
2022-02-11 23:42:06 +03:00
uint32_t old_kvno = 0 ;
uint32_t older_kvno = 0 ;
2022-02-16 16:11:10 +03:00
uint32_t returned_kvno = 0 ;
2010-04-12 01:17:15 +04:00
uint16_t i ;
2022-02-11 23:46:28 +03:00
struct samba_kdc_user_keys keys = { . num_pkeys = 0 , } ;
2022-02-11 23:42:06 +03:00
struct samba_kdc_user_keys old_keys = { . num_pkeys = 0 , } ;
struct samba_kdc_user_keys older_keys = { . num_pkeys = 0 , } ;
2022-02-11 23:46:28 +03:00
uint32_t available_enctypes = 0 ;
2022-03-23 03:07:29 +03:00
uint32_t supported_enctypes = supported_enctypes_in ;
2024-04-15 05:45:51 +03:00
const bool exporting_keytab = flags & SDB_F_ADMIN_DATA ;
2010-11-16 13:01:22 +03:00
2022-03-23 03:07:29 +03:00
* supported_enctypes_out = 0 ;
2022-02-02 07:08:41 +03:00
2010-10-01 23:25:26 +04:00
/* Is this the krbtgt or a RODC krbtgt */
2010-09-29 03:06:39 +04:00
if ( is_rodc ) {
2022-02-16 16:11:10 +03:00
krbtgt_number = ldb_msg_find_attr_as_int ( msg , " msDS-SecondaryKrbTgtNumber " , - 1 ) ;
2010-09-28 06:49:44 +04:00
2022-02-16 16:11:10 +03:00
if ( krbtgt_number = = - 1 ) {
return EINVAL ;
}
if ( krbtgt_number = = 0 ) {
2010-09-28 06:49:44 +04:00
return EINVAL ;
}
}
2023-10-11 16:58:22 +03:00
if ( flags & SDB_F_USER2USER_PRINCIPAL ) {
/*
* User2User uses the session key
* from the additional ticket ,
* so we just provide random keys
* here in order to make sure
* we never expose the user password
* keys .
*/
ret = samba_kdc_set_random_keys ( context ,
supported_enctypes ,
& entry - > keys ) ;
* supported_enctypes_out = supported_enctypes & ENC_ALL_TYPES ;
goto out ;
}
2016-06-02 18:47:12 +03:00
if ( ( ent_type = = SAMBA_KDC_ENT_TYPE_CLIENT )
& & ( userAccountControl & UF_SMARTCARD_REQUIRED ) ) {
2018-09-20 05:24:11 +03:00
ret = samba_kdc_set_random_keys ( context ,
2022-03-22 23:47:53 +03:00
supported_enctypes ,
& entry - > keys ) ;
2021-12-24 06:59:12 +03:00
2022-03-24 17:44:40 +03:00
* supported_enctypes_out = supported_enctypes & ENC_ALL_TYPES ;
2021-12-24 06:59:12 +03:00
2016-06-02 18:47:12 +03:00
goto out ;
}
2010-01-28 09:27:11 +03:00
2022-02-16 16:11:10 +03:00
current_kvno = ldb_msg_find_attr_as_int ( msg , " msDS-KeyVersionNumber " , 0 ) ;
2022-02-11 23:42:06 +03:00
if ( current_kvno > 1 ) {
old_kvno = current_kvno - 1 ;
}
if ( current_kvno > 2 ) {
older_kvno = current_kvno - 2 ;
}
2022-02-16 16:11:10 +03:00
if ( is_krbtgt ) {
/*
* Even for the main krbtgt account
* we have to strictly split the kvno into
* two 16 - bit parts and the upper 16 - bit
* need to be all zero , even if
* the msDS - KeyVersionNumber has a value
* larger than 65535.
*
* See https : //bugzilla.samba.org/show_bug.cgi?id=14951
*/
current_kvno = SAMBA_KVNO_GET_VALUE ( current_kvno ) ;
2022-02-11 23:42:06 +03:00
old_kvno = SAMBA_KVNO_GET_VALUE ( old_kvno ) ;
older_kvno = SAMBA_KVNO_GET_VALUE ( older_kvno ) ;
2019-07-19 14:22:48 +03:00
requested_kvno = SAMBA_KVNO_GET_VALUE ( requested_kvno ) ;
2010-09-28 06:49:44 +04:00
}
2010-01-28 09:27:11 +03:00
/* Get keys from the db */
hash = samdb_result_hash ( mem_ctx , msg , " unicodePwd " ) ;
2022-02-11 23:42:06 +03:00
num_ntPwdHistory = samdb_result_hashes ( mem_ctx , msg ,
" ntPwdHistory " ,
& ntPwdHistory ) ;
if ( num_ntPwdHistory > 1 ) {
old_hash = & ntPwdHistory [ 1 ] ;
}
if ( num_ntPwdHistory > 2 ) {
2024-04-11 08:17:54 +03:00
older_hash = & ntPwdHistory [ 2 ] ;
2022-02-11 23:42:06 +03:00
}
2010-01-28 09:27:11 +03:00
sc_val = ldb_msg_find_ldb_val ( msg , " supplementalCredentials " ) ;
/* supplementalCredentials if present */
if ( sc_val ) {
2010-05-09 19:20:01 +04:00
ndr_err = ndr_pull_struct_blob_all ( sc_val , mem_ctx , & scb ,
2010-01-28 09:27:11 +03:00
( ndr_pull_flags_fn_t ) ndr_pull_supplementalCredentialsBlob ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
ret = EINVAL ;
goto out ;
}
if ( scb . sub . signature ! = SUPPLEMENTAL_CREDENTIALS_SIGNATURE ) {
2016-06-03 17:34:08 +03:00
if ( scb . sub . num_packages ! = 0 ) {
NDR_PRINT_DEBUG ( supplementalCredentialsBlob , & scb ) ;
ret = EINVAL ;
goto out ;
}
2010-01-28 09:27:11 +03:00
}
for ( i = 0 ; i < scb . sub . num_packages ; i + + ) {
2023-09-05 05:17:36 +03:00
if ( scb . sub . packages [ i ] . name ! = NULL & &
strcmp ( " Primary:Kerberos-Newer-Keys " , scb . sub . packages [ i ] . name ) = = 0 )
{
2010-01-28 09:27:11 +03:00
scpk = & scb . sub . packages [ i ] ;
if ( ! scpk - > data | | ! scpk - > data [ 0 ] ) {
scpk = NULL ;
continue ;
}
break ;
}
}
}
/*
2022-02-08 16:25:53 +03:00
* Primary : Kerberos - Newer - Keys element
2010-01-28 09:27:11 +03:00
* of supplementalCredentials
2022-02-08 16:25:53 +03:00
*
* The legacy Primary : Kerberos only contains
* single DES keys , which are completely ignored
* now .
2010-01-28 09:27:11 +03:00
*/
if ( scpk ) {
DATA_BLOB blob ;
blob = strhex_to_data_blob ( mem_ctx , scpk - > data ) ;
if ( ! blob . data ) {
ret = ENOMEM ;
goto out ;
}
/* we cannot use ndr_pull_struct_blob_all() here, as w2k and w2k3 add padding bytes */
2010-05-09 19:20:01 +04:00
ndr_err = ndr_pull_struct_blob ( & blob , mem_ctx , & _pkb ,
2010-01-28 09:27:11 +03:00
( ndr_pull_flags_fn_t ) ndr_pull_package_PrimaryKerberosBlob ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
ret = EINVAL ;
krb5_set_error_message ( context , ret , " samba_kdc_message2entry_keys: could not parse package_PrimaryKerberosBlob " ) ;
krb5_warnx ( context , " samba_kdc_message2entry_keys: could not parse package_PrimaryKerberosBlob " ) ;
goto out ;
}
2022-02-08 16:25:53 +03:00
if ( _pkb . version ! = 4 ) {
2010-01-28 09:27:11 +03:00
ret = EINVAL ;
krb5_set_error_message ( context , ret , " samba_kdc_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4 " ) ;
krb5_warnx ( context , " samba_kdc_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4 " ) ;
goto out ;
}
2022-02-08 16:25:53 +03:00
pkb4 = & _pkb . ctr . ctr4 ;
2010-01-28 09:27:11 +03:00
}
2022-02-11 23:46:28 +03:00
keys = ( struct samba_kdc_user_keys ) {
. kvno = current_kvno ,
. supported_enctypes = supported_enctypes ,
. nthash = hash ,
. salt_string = pkb4 ! = NULL ? pkb4 - > salt . string : NULL ,
. num_pkeys = pkb4 ! = NULL ? pkb4 - > num_keys : 0 ,
. pkeys = pkb4 ! = NULL ? pkb4 - > keys : NULL ,
} ;
2010-01-28 09:27:11 +03:00
2022-02-11 23:42:06 +03:00
old_keys = ( struct samba_kdc_user_keys ) {
. kvno = old_kvno ,
. supported_enctypes = supported_enctypes ,
. nthash = old_hash ,
. salt_string = pkb4 ! = NULL ? pkb4 - > salt . string : NULL ,
. num_pkeys = pkb4 ! = NULL ? pkb4 - > num_old_keys : 0 ,
. pkeys = pkb4 ! = NULL ? pkb4 - > old_keys : NULL ,
} ;
older_keys = ( struct samba_kdc_user_keys ) {
. kvno = older_kvno ,
. supported_enctypes = supported_enctypes ,
. nthash = older_hash ,
. salt_string = pkb4 ! = NULL ? pkb4 - > salt . string : NULL ,
. num_pkeys = pkb4 ! = NULL ? pkb4 - > num_older_keys : 0 ,
. pkeys = pkb4 ! = NULL ? pkb4 - > older_keys : NULL ,
} ;
if ( flags & SDB_F_KVNO_SPECIFIED ) {
if ( requested_kvno = = keys . kvno ) {
/*
* The current kvno was requested ,
* so we return it .
*/
keys . skeys = & entry - > keys ;
keys . available_enctypes = & available_enctypes ;
keys . returned_kvno = & returned_kvno ;
} else if ( requested_kvno = = 0 ) {
/*
* don ' t return any keys
*/
} else if ( requested_kvno = = old_keys . kvno ) {
/*
* return the old keys as default keys
* with the requested kvno .
*/
old_keys . skeys = & entry - > keys ;
old_keys . available_enctypes = & available_enctypes ;
old_keys . returned_kvno = & returned_kvno ;
} else if ( requested_kvno = = older_keys . kvno ) {
/*
* return the older keys as default keys
* with the requested kvno .
*/
older_keys . skeys = & entry - > keys ;
older_keys . available_enctypes = & available_enctypes ;
older_keys . returned_kvno = & returned_kvno ;
} else {
/*
* don ' t return any keys
*/
}
} else {
bool include_history = false ;
if ( ( flags & SDB_F_GET_CLIENT ) & & ( flags & SDB_F_FOR_AS_REQ ) ) {
include_history = true ;
2024-04-15 05:45:51 +03:00
} else if ( exporting_keytab ) {
2022-02-11 23:42:06 +03:00
include_history = true ;
}
keys . skeys = & entry - > keys ;
keys . available_enctypes = & available_enctypes ;
keys . returned_kvno = & returned_kvno ;
if ( include_history & & old_keys . kvno ! = 0 ) {
old_keys . skeys = & entry - > old_keys ;
}
if ( include_history & & older_keys . kvno ! = 0 ) {
older_keys . skeys = & entry - > older_keys ;
}
}
2022-02-11 23:46:28 +03:00
if ( keys . skeys ! = NULL ) {
ret = samba_kdc_fill_user_keys ( context , & keys ) ;
if ( ret ! = 0 ) {
2010-01-28 09:27:11 +03:00
goto out ;
}
}
2022-02-11 23:42:06 +03:00
if ( old_keys . skeys ! = NULL ) {
ret = samba_kdc_fill_user_keys ( context , & old_keys ) ;
if ( ret ! = 0 ) {
goto out ;
}
2024-04-15 05:46:47 +03:00
if ( keys . skeys ! = NULL & & ! exporting_keytab ) {
bool is_gmsa ;
is_gmsa = dsdb_account_is_gmsa ( ldb , msg ) ;
if ( is_gmsa ) {
NTTIME current_time ;
bool gmsa_key_is_recent ;
bool ok ;
ok = dsdb_gmsa_current_time ( ldb , & current_time ) ;
if ( ! ok ) {
ret = EINVAL ;
goto out ;
}
gmsa_key_is_recent = samdb_gmsa_key_is_recent (
msg , current_time ) ;
if ( gmsa_key_is_recent ) {
/*
* As the current gMSA keys are less
* than five minutes old , the previous
* set of keys remains valid . The
* Heimdal KDC will try each of the
* current keys when decrypting a
* client ’ s PA ‐ DATA , so by merging the
* old set into the current set we can
* cause both sets to be considered for
* decryption .
*/
ret = samba_kdc_merge_keys (
keys . skeys , old_keys . skeys ) ;
if ( ret ) {
goto out ;
}
}
}
}
2022-02-11 23:42:06 +03:00
}
if ( older_keys . skeys ! = NULL ) {
ret = samba_kdc_fill_user_keys ( context , & older_keys ) ;
if ( ret ! = 0 ) {
goto out ;
}
}
2022-02-11 23:46:28 +03:00
* supported_enctypes_out | = available_enctypes ;
2010-01-28 09:27:11 +03:00
2022-02-16 16:11:10 +03:00
if ( is_krbtgt ) {
/*
* Even for the main krbtgt account
* we have to strictly split the kvno into
* two 16 - bit parts and the upper 16 - bit
* need to be all zero , even if
* the msDS - KeyVersionNumber has a value
* larger than 65535.
*
* See https : //bugzilla.samba.org/show_bug.cgi?id=14951
*/
returned_kvno = SAMBA_KVNO_AND_KRBTGT ( returned_kvno , krbtgt_number ) ;
}
entry - > kvno = returned_kvno ;
2022-02-11 23:46:28 +03:00
out :
2010-01-28 09:27:11 +03:00
return ret ;
}
2023-09-21 02:37:30 +03:00
static krb5_error_code is_principal_component_equal_impl ( krb5_context context ,
krb5_const_principal principal ,
unsigned int component ,
const char * string ,
bool do_strcasecmp ,
bool * eq )
2014-05-15 17:57:06 +04:00
{
const char * p ;
# if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
2023-09-21 02:22:51 +03:00
if ( component > = krb5_princ_size ( context , principal ) ) {
2023-09-21 02:37:30 +03:00
/* A non‐ existent component compares equal to no string. */
* eq = false ;
2023-09-21 02:22:51 +03:00
return 0 ;
}
2014-05-15 17:57:06 +04:00
p = krb5_principal_get_comp_string ( context , principal , component ) ;
if ( p = = NULL ) {
2023-09-21 02:22:51 +03:00
return ENOENT ;
2014-05-15 17:57:06 +04:00
}
2022-05-25 11:00:55 +03:00
if ( do_strcasecmp ) {
2023-09-21 02:37:30 +03:00
* eq = strcasecmp ( p , string ) = = 0 ;
2022-05-25 11:00:55 +03:00
} else {
2023-09-21 02:37:30 +03:00
* eq = strcmp ( p , string ) = = 0 ;
2022-05-25 11:00:55 +03:00
}
2023-09-21 02:22:51 +03:00
return 0 ;
2014-05-15 17:57:06 +04:00
# else
2022-05-25 11:00:55 +03:00
size_t len ;
2023-09-05 07:46:08 +03:00
krb5_data d ;
krb5_error_code ret = 0 ;
2023-09-21 03:01:27 +03:00
if ( component > INT_MAX ) {
return EINVAL ;
}
2014-05-15 17:57:06 +04:00
if ( component > = krb5_princ_size ( context , principal ) ) {
2023-09-21 02:37:30 +03:00
/* A non‐ existent component compares equal to no string. */
* eq = false ;
2023-09-21 02:22:51 +03:00
return 0 ;
2014-05-15 17:57:06 +04:00
}
2023-09-05 07:46:08 +03:00
ret = smb_krb5_princ_component ( context , principal , component , & d ) ;
if ( ret ) {
2023-09-21 02:22:51 +03:00
return ret ;
2014-05-15 17:57:06 +04:00
}
2023-09-05 07:46:08 +03:00
p = d . data ;
2022-05-25 11:00:55 +03:00
len = strlen ( string ) ;
2023-09-21 02:37:30 +03:00
if ( d . length ! = len ) {
* eq = false ;
2023-09-21 02:22:51 +03:00
return 0 ;
2022-05-25 11:00:55 +03:00
}
2014-05-15 17:57:06 +04:00
if ( do_strcasecmp ) {
2023-09-21 02:37:30 +03:00
* eq = strncasecmp ( p , string , len ) = = 0 ;
2014-05-15 17:57:06 +04:00
} else {
2023-09-21 02:37:30 +03:00
* eq = memcmp ( p , string , len ) = = 0 ;
2014-05-15 17:57:06 +04:00
}
2023-09-21 02:22:51 +03:00
return 0 ;
2022-05-25 11:00:55 +03:00
# endif
2014-05-15 17:57:06 +04:00
}
2023-09-21 02:37:30 +03:00
static krb5_error_code is_principal_component_equal_ignoring_case ( krb5_context context ,
krb5_const_principal principal ,
unsigned int component ,
const char * string ,
bool * eq )
2014-05-15 17:57:06 +04:00
{
2023-09-21 02:37:30 +03:00
return is_principal_component_equal_impl ( context ,
principal ,
component ,
string ,
true /* do_strcasecmp */ ,
eq ) ;
2014-05-15 17:57:06 +04:00
}
2023-09-21 02:37:30 +03:00
static krb5_error_code is_principal_component_equal ( krb5_context context ,
krb5_const_principal principal ,
unsigned int component ,
const char * string ,
bool * eq )
2014-05-15 17:57:06 +04:00
{
2023-09-21 02:37:30 +03:00
return is_principal_component_equal_impl ( context ,
principal ,
component ,
string ,
false /* do_strcasecmp */ ,
eq ) ;
2014-05-15 17:57:06 +04:00
}
2023-09-21 02:21:28 +03:00
static krb5_error_code is_kadmin_changepw ( krb5_context context ,
krb5_const_principal principal ,
bool * is_changepw )
2022-05-24 10:54:18 +03:00
{
2023-09-21 02:22:51 +03:00
krb5_error_code ret = 0 ;
2023-09-21 02:37:30 +03:00
bool eq = false ;
2023-09-21 02:21:28 +03:00
if ( krb5_princ_size ( context , principal ) ! = 2 ) {
* is_changepw = false ;
return 0 ;
}
2023-09-21 02:37:30 +03:00
ret = is_principal_component_equal ( context , principal , 0 , " kadmin " , & eq ) ;
2023-09-21 02:22:51 +03:00
if ( ret ) {
return ret ;
}
2023-09-21 02:37:30 +03:00
if ( ! eq ) {
2023-09-21 02:21:28 +03:00
* is_changepw = false ;
return 0 ;
}
2023-09-21 02:37:30 +03:00
ret = is_principal_component_equal ( context , principal , 1 , " changepw " , & eq ) ;
2023-09-21 02:22:51 +03:00
if ( ret ) {
return ret ;
}
2023-09-21 02:21:28 +03:00
2023-09-21 02:37:30 +03:00
* is_changepw = eq ;
2023-09-21 02:21:28 +03:00
return 0 ;
2022-05-24 10:54:18 +03:00
}
2022-05-18 07:56:01 +03:00
static krb5_error_code samba_kdc_get_entry_principal (
krb5_context context ,
struct samba_kdc_db_context * kdc_db_ctx ,
const char * samAccountName ,
enum samba_kdc_ent_type ent_type ,
unsigned flags ,
2022-05-18 07:56:01 +03:00
bool is_kadmin_changepw ,
2022-05-18 07:56:01 +03:00
krb5_const_principal in_princ ,
krb5_principal * out_princ )
{
struct loadparm_context * lp_ctx = kdc_db_ctx - > lp_ctx ;
2022-05-25 08:19:58 +03:00
krb5_error_code code = 0 ;
bool canon = flags & ( SDB_F_CANON | SDB_F_FORCE_CANON ) ;
2022-05-18 07:56:01 +03:00
/*
* If we are set to canonicalize , we get back the fixed UPPER
* case realm , and the real username ( ie matching LDAP
* samAccountName )
*
* Otherwise , if we are set to enterprise , we
* get back the whole principal as - sent
*
* Finally , if we are not set to canonicalize , we get back the
* fixed UPPER case realm , but the as - sent username
*/
2022-05-18 07:56:01 +03:00
/*
* We need to ensure that the kadmin / changepw principal isn ' t able to
* issue krbtgt tickets , even if canonicalization is turned on .
*/
if ( ! is_kadmin_changepw ) {
if ( ent_type = = SAMBA_KDC_ENT_TYPE_KRBTGT & & canon ) {
/*
2023-07-11 07:52:25 +03:00
* When requested to do so , ensure that both
* the realm values in the principal are set
2022-05-18 07:56:01 +03:00
* to the upper case , canonical realm
*/
code = smb_krb5_make_principal ( context ,
out_princ ,
lpcfg_realm ( lp_ctx ) ,
" krbtgt " ,
lpcfg_realm ( lp_ctx ) ,
NULL ) ;
if ( code ! = 0 ) {
return code ;
}
smb_krb5_principal_set_type ( context ,
* out_princ ,
KRB5_NT_SRV_INST ) ;
2022-05-18 07:56:01 +03:00
2022-05-18 07:56:01 +03:00
return 0 ;
}
2022-05-25 08:19:58 +03:00
2022-05-18 07:56:01 +03:00
if ( ( canon & & flags & ( SDB_F_FORCE_CANON | SDB_F_FOR_AS_REQ ) ) | |
( ent_type = = SAMBA_KDC_ENT_TYPE_ANY & & in_princ = = NULL ) ) {
/*
* SDB_F_CANON maps from the canonicalize flag in the
* packet , and has a different meaning between AS - REQ
* and TGS - REQ . We only change the principal in the
* AS - REQ case .
*
* The SDB_F_FORCE_CANON if for new MIT KDC code that
* wants the canonical name in all lookups , and takes
* care to canonicalize only when appropriate .
*/
code = smb_krb5_make_principal ( context ,
out_princ ,
lpcfg_realm ( lp_ctx ) ,
samAccountName ,
NULL ) ;
return code ;
}
2022-05-25 08:19:58 +03:00
}
2022-05-18 07:56:01 +03:00
2022-05-25 08:19:58 +03:00
/*
* For a krbtgt entry , this appears to be required regardless of the
* canonicalize flag from the client .
*/
code = krb5_copy_principal ( context , in_princ , out_princ ) ;
if ( code ! = 0 ) {
return code ;
2022-05-18 07:56:01 +03:00
}
2022-05-25 08:19:58 +03:00
/*
* While we have copied the client principal , tests show that Win2k3
* returns the ' corrected ' realm , not the client - specified realm . This
* code attempts to replace the client principal ' s realm with the one
* we determine from our records
*/
code = smb_krb5_principal_set_realm ( context ,
* out_princ ,
lpcfg_realm ( lp_ctx ) ) ;
return code ;
2022-05-18 07:56:01 +03:00
}
2010-01-28 09:27:11 +03:00
/*
* Construct an hdb_entry from a directory entry .
*/
static krb5_error_code samba_kdc_message2entry ( krb5_context context ,
2010-11-16 06:12:17 +03:00
struct samba_kdc_db_context * kdc_db_ctx ,
2015-02-25 13:54:52 +03:00
TALLOC_CTX * mem_ctx ,
krb5_const_principal principal ,
2010-11-16 06:12:17 +03:00
enum samba_kdc_ent_type ent_type ,
unsigned flags ,
2019-07-19 14:22:48 +03:00
krb5_kvno kvno ,
2010-11-16 06:12:17 +03:00
struct ldb_dn * realm_dn ,
struct ldb_message * msg ,
2022-03-23 05:36:31 +03:00
struct sdb_entry * entry )
2010-01-28 09:27:11 +03:00
{
2023-08-10 07:07:17 +03:00
TALLOC_CTX * tmp_ctx = NULL ;
2010-01-28 09:27:11 +03:00
struct loadparm_context * lp_ctx = kdc_db_ctx - > lp_ctx ;
2010-10-05 11:27:36 +04:00
uint32_t userAccountControl ;
2013-10-30 01:50:19 +04:00
uint32_t msDS_User_Account_Control_Computed ;
2010-01-28 09:27:11 +03:00
krb5_error_code ret = 0 ;
krb5_boolean is_computer = FALSE ;
struct samba_kdc_entry * p ;
NTTIME acct_expiry ;
NTSTATUS status ;
2022-02-02 07:08:41 +03:00
bool protected_user = false ;
2023-08-10 05:46:34 +03:00
struct dom_sid sid ;
2010-01-28 09:27:11 +03:00
uint32_t rid ;
2022-03-23 03:07:29 +03:00
bool is_krbtgt = false ;
2010-09-29 03:06:39 +04:00
bool is_rodc = false ;
2022-03-24 17:44:40 +03:00
bool force_rc4 = lpcfg_kdc_force_enable_rc4_weak_session_keys ( lp_ctx ) ;
2010-01-28 09:27:11 +03:00
struct ldb_message_element * objectclasses ;
2022-02-08 18:49:35 +03:00
struct ldb_val computer_val = data_blob_string_const ( " computer " ) ;
2023-12-22 02:09:45 +03:00
struct ldb_val gmsa_oc_val = data_blob_string_const ( " msDS-GroupManagedServiceAccount " ) ;
2022-11-30 11:05:51 +03:00
uint32_t config_default_supported_enctypes = lpcfg_kdc_default_domain_supported_enctypes ( lp_ctx ) ;
uint32_t default_supported_enctypes =
config_default_supported_enctypes ! = 0 ?
config_default_supported_enctypes :
ENC_RC4_HMAC_MD5 | ENC_HMAC_SHA1_96_AES256_SK ;
2022-03-23 03:07:29 +03:00
uint32_t supported_enctypes
= ldb_msg_find_attr_as_uint ( msg ,
" msDS-SupportedEncryptionTypes " ,
2022-11-01 05:20:47 +03:00
default_supported_enctypes ) ;
2022-03-24 17:44:40 +03:00
uint32_t pa_supported_enctypes ;
uint32_t supported_session_etypes ;
uint32_t available_enctypes = 0 ;
/*
2023-08-02 11:47:30 +03:00
* also legacy enctypes are announced ,
2022-03-24 17:44:40 +03:00
* but effectively restricted by kdc_enctypes
*/
uint32_t domain_enctypes = ENC_RC4_HMAC_MD5 | ENC_RSA_MD5 | ENC_CRC32 ;
2022-11-30 11:39:19 +03:00
uint32_t config_kdc_enctypes = lpcfg_kdc_supported_enctypes ( lp_ctx ) ;
uint32_t kdc_enctypes =
config_kdc_enctypes ! = 0 ?
config_kdc_enctypes :
ENC_ALL_TYPES ;
2010-01-28 09:27:11 +03:00
const char * samAccountName = ldb_msg_find_attr_as_string ( msg , " samAccountName " , NULL ) ;
2023-05-04 06:06:40 +03:00
const struct authn_kerberos_client_policy * authn_client_policy = NULL ;
const struct authn_server_policy * authn_server_policy = NULL ;
2023-10-11 16:58:22 +03:00
const bool user2user = ( flags & SDB_F_USER2USER_PRINCIPAL ) ;
2024-05-21 02:14:50 +03:00
int64_t lifetime_secs ;
int effective_lifetime_secs ;
2023-05-04 06:06:40 +03:00
2023-09-07 07:35:39 +03:00
* entry = ( struct sdb_entry ) { } ;
2022-02-08 18:50:14 +03:00
2023-08-10 07:07:17 +03:00
tmp_ctx = talloc_new ( mem_ctx ) ;
if ( tmp_ctx = = NULL ) {
return ENOMEM ;
}
2022-11-01 05:20:47 +03:00
if ( supported_enctypes = = 0 ) {
supported_enctypes = default_supported_enctypes ;
}
2022-03-24 17:44:40 +03:00
if ( dsdb_functional_level ( kdc_db_ctx - > samdb ) > = DS_DOMAIN_FUNCTION_2008 ) {
domain_enctypes | = ENC_HMAC_SHA1_96_AES128 | ENC_HMAC_SHA1_96_AES256 ;
}
2010-09-29 03:06:39 +04:00
if ( ldb_msg_find_element ( msg , " msDS-SecondaryKrbTgtNumber " ) ) {
is_rodc = true ;
}
2010-01-28 09:27:11 +03:00
if ( ! samAccountName ) {
ret = ENOENT ;
krb5_set_error_message ( context , ret , " samba_kdc_message2entry: no samAccountName present " ) ;
goto out ;
}
objectclasses = ldb_msg_find_element ( msg , " objectClass " ) ;
if ( objectclasses & & ldb_msg_find_val ( objectclasses , & computer_val ) ) {
is_computer = TRUE ;
}
2023-08-10 07:07:17 +03:00
p = talloc_zero ( tmp_ctx , struct samba_kdc_entry ) ;
2010-01-28 09:27:11 +03:00
if ( ! p ) {
ret = ENOMEM ;
goto out ;
}
2023-12-22 02:09:45 +03:00
if ( objectclasses & & ldb_msg_find_val ( objectclasses , & gmsa_oc_val ) ) {
p - > group_managed_service_account = true ;
}
2018-02-01 13:44:21 +03:00
p - > is_rodc = is_rodc ;
2010-01-28 09:27:11 +03:00
p - > kdc_db_ctx = kdc_db_ctx ;
p - > realm_dn = talloc_reference ( p , realm_dn ) ;
if ( ! p - > realm_dn ) {
ret = ENOMEM ;
goto out ;
}
2024-05-28 03:53:19 +03:00
p - > current_nttime = * kdc_db_ctx - > current_nttime_ull ;
2010-01-28 09:27:11 +03:00
talloc_set_destructor ( p , samba_kdc_entry_destructor ) ;
2022-03-23 05:25:12 +03:00
entry - > skdc_entry = p ;
2010-01-28 09:27:11 +03:00
userAccountControl = ldb_msg_find_attr_as_uint ( msg , " userAccountControl " , 0 ) ;
2013-10-30 01:50:19 +04:00
msDS_User_Account_Control_Computed
= ldb_msg_find_attr_as_uint ( msg ,
" msDS-User-Account-Control-Computed " ,
UF_ACCOUNTDISABLE ) ;
/*
* This brings in the lockout flag , block the account if not
* found . We need the weird UF_ACCOUNTDISABLE check because
* we do not want to fail open if the value is not returned ,
* but 0 is a valid value ( all OK )
*/
if ( msDS_User_Account_Control_Computed = = UF_ACCOUNTDISABLE ) {
ret = EINVAL ;
krb5_set_error_message ( context , ret , " samba_kdc_message2entry: "
" no msDS-User-Account-Control-Computed present " ) ;
goto out ;
} else {
userAccountControl | = msDS_User_Account_Control_Computed ;
}
2010-01-28 09:27:11 +03:00
2015-01-23 06:41:50 +03:00
if ( ent_type = = SAMBA_KDC_ENT_TYPE_KRBTGT ) {
2018-02-01 13:44:21 +03:00
p - > is_krbtgt = true ;
2010-01-28 09:27:11 +03:00
}
/* First try and figure out the flags based on the userAccountControl */
2022-03-23 05:25:12 +03:00
entry - > flags = uf2SDBFlags ( context , userAccountControl , ent_type ) ;
2010-01-28 09:27:11 +03:00
2021-06-23 02:35:01 +03:00
/*
* Take control of the returned principal here , rather than
* allowing the Heimdal code to do it as we have specific
* behaviour around the forced realm to honour
*/
2022-03-23 05:25:12 +03:00
entry - > flags . force_canonicalize = true ;
2021-06-23 02:35:01 +03:00
2023-10-11 16:58:22 +03:00
/*
* Windows 2008 seems to enforce this ( very sensible ) rule by
2010-01-28 09:27:11 +03:00
* default - don ' t allow offline attacks on a user ' s password
* by asking for a ticket to them as a service ( encrypted with
2023-10-11 16:58:22 +03:00
* their probably pathetically insecure password )
*
2023-10-17 01:21:00 +03:00
* But user2user avoids using the keys based on the password ,
2023-10-11 16:58:22 +03:00
* so we can allow it .
*/
2010-01-28 09:27:11 +03:00
2023-10-11 16:58:22 +03:00
if ( entry - > flags . server & & ! user2user
2010-07-16 08:32:42 +04:00
& & lpcfg_parm_bool ( lp_ctx , NULL , " kdc " , " require spn for service " , true ) ) {
2010-01-28 09:27:11 +03:00
if ( ! is_computer & & ! ldb_msg_find_attr_as_string ( msg , " servicePrincipalName " , NULL ) ) {
2022-03-23 05:25:12 +03:00
entry - > flags . server = 0 ;
2010-01-28 09:27:11 +03:00
}
}
2021-10-04 05:18:34 +03:00
/*
* We restrict a 3 - part SPN ending in my domain / realm to full
* domain controllers .
*
* This avoids any cases where ( eg ) a demoted DC still has
* these more restricted SPNs .
*/
if ( krb5_princ_size ( context , principal ) > 2 ) {
2023-08-25 02:28:07 +03:00
char * third_part = NULL ;
bool is_our_realm ;
bool is_dc ;
2023-09-21 01:41:05 +03:00
ret = smb_krb5_principal_get_comp_string ( tmp_ctx ,
context ,
principal ,
2 ,
& third_part ) ;
if ( ret ) {
2023-08-25 02:28:07 +03:00
krb5_set_error_message ( context , ret , " smb_krb5_principal_get_comp_string: out of memory " ) ;
goto out ;
}
is_our_realm = lpcfg_is_my_domain_or_realm ( lp_ctx ,
2021-10-04 05:18:34 +03:00
third_part ) ;
2023-08-25 02:28:07 +03:00
is_dc = userAccountControl &
2021-10-04 05:18:34 +03:00
( UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT ) ;
if ( is_our_realm & & ! is_dc ) {
2022-03-23 05:25:12 +03:00
entry - > flags . server = 0 ;
2021-10-04 05:18:34 +03:00
}
}
2015-01-30 02:31:29 +03:00
/*
* To give the correct type of error to the client , we must
* not just return the entry without . server set , we must
* pretend the principal does not exist . Otherwise we may
* return ERR_POLICY instead of
* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
*/
2022-03-23 05:25:12 +03:00
if ( ent_type = = SAMBA_KDC_ENT_TYPE_SERVER & & entry - > flags . server = = 0 ) {
2014-05-08 19:13:04 +04:00
ret = SDB_ERR_NOENTRY ;
2015-01-30 02:31:29 +03:00
krb5_set_error_message ( context , ret , " samba_kdc_message2entry: no servicePrincipalName present for this server, refusing with no-such-entry " ) ;
goto out ;
}
2014-05-08 19:13:04 +04:00
if ( flags & SDB_F_ADMIN_DATA ) {
2010-01-28 09:27:11 +03:00
/* These (created_by, modified_by) parts of the entry are not relevant for Samba4's use
2023-09-04 07:52:57 +03:00
* of the Heimdal KDC . They are stored in the traditional
2010-01-28 09:27:11 +03:00
* DB for audit purposes , and still form part of the structure
* we must return */
/* use 'whenCreated' */
2022-03-23 05:25:12 +03:00
entry - > created_by . time = ldb_msg_find_krb5time_ldap_time ( msg , " whenCreated " , 0 ) ;
2010-01-28 16:58:44 +03:00
/* use 'kadmin' for now (needed by mit_samba) */
2014-05-07 18:47:52 +04:00
ret = smb_krb5_make_principal ( context ,
2022-03-23 05:25:12 +03:00
& entry - > created_by . principal ,
2014-05-07 18:47:52 +04:00
lpcfg_realm ( lp_ctx ) , " kadmin " , NULL ) ;
2015-01-30 02:31:29 +03:00
if ( ret ) {
krb5_clear_error_message ( context ) ;
goto out ;
}
2010-01-28 09:27:11 +03:00
2023-08-10 05:37:34 +03:00
entry - > modified_by = calloc ( 1 , sizeof ( struct sdb_event ) ) ;
2022-03-23 05:25:12 +03:00
if ( entry - > modified_by = = NULL ) {
2010-01-28 09:27:11 +03:00
ret = ENOMEM ;
2023-08-10 05:37:34 +03:00
krb5_set_error_message ( context , ret , " calloc: out of memory " ) ;
2010-01-28 09:27:11 +03:00
goto out ;
}
/* use 'whenChanged' */
2022-03-23 05:25:12 +03:00
entry - > modified_by - > time = ldb_msg_find_krb5time_ldap_time ( msg , " whenChanged " , 0 ) ;
2010-01-28 16:58:44 +03:00
/* use 'kadmin' for now (needed by mit_samba) */
2014-05-07 18:47:52 +04:00
ret = smb_krb5_make_principal ( context ,
2022-03-23 05:25:12 +03:00
& entry - > modified_by - > principal ,
2014-05-07 18:47:52 +04:00
lpcfg_realm ( lp_ctx ) , " kadmin " , NULL ) ;
2015-01-30 02:31:29 +03:00
if ( ret ) {
krb5_clear_error_message ( context ) ;
goto out ;
}
2010-01-28 09:27:11 +03:00
}
/* The lack of password controls etc applies to krbtgt by
* virtue of being that particular RID */
2023-08-10 05:46:34 +03:00
ret = samdb_result_dom_sid_buf ( msg , " objectSid " , & sid ) ;
if ( ret ) {
goto out ;
}
status = dom_sid_split_rid ( NULL , & sid , NULL , & rid ) ;
2010-01-28 09:27:11 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
ret = EINVAL ;
goto out ;
}
if ( rid = = DOMAIN_RID_KRBTGT ) {
2014-05-08 12:10:49 +04:00
char * realm = NULL ;
2022-03-23 05:25:12 +03:00
entry - > valid_end = NULL ;
entry - > pw_end = NULL ;
2010-01-28 09:27:11 +03:00
2022-03-23 05:25:12 +03:00
entry - > flags . invalid = 0 ;
entry - > flags . server = 1 ;
2010-01-28 09:27:11 +03:00
2018-11-20 19:45:11 +03:00
realm = smb_krb5_principal_get_realm (
2023-08-10 07:07:17 +03:00
tmp_ctx , context , principal ) ;
2014-05-08 12:10:49 +04:00
if ( realm = = NULL ) {
ret = ENOMEM ;
goto out ;
}
2010-01-28 09:27:11 +03:00
/* Don't mark all requests for the krbtgt/realm as
* ' change password ' , as otherwise we could get into
2023-09-04 07:52:57 +03:00
* trouble , and not enforce the password expiry .
2010-01-28 09:27:11 +03:00
* Instead , only do it when request is for the kpasswd service */
2023-09-21 02:21:28 +03:00
if ( ent_type = = SAMBA_KDC_ENT_TYPE_SERVER ) {
bool is_changepw = false ;
ret = is_kadmin_changepw ( context , principal , & is_changepw ) ;
if ( ret ) {
goto out ;
}
if ( is_changepw & & lpcfg_is_my_domain_or_realm ( lp_ctx , realm ) ) {
entry - > flags . change_pw = 1 ;
}
2010-01-28 09:27:11 +03:00
}
2014-05-08 12:10:49 +04:00
2018-11-20 19:45:11 +03:00
TALLOC_FREE ( realm ) ;
2014-05-08 12:10:49 +04:00
2022-03-23 05:25:12 +03:00
entry - > flags . client = 0 ;
entry - > flags . forwardable = 1 ;
entry - > flags . ok_as_delegate = 1 ;
2010-09-29 03:06:39 +04:00
} else if ( is_rodc ) {
/* The RODC krbtgt account is like the main krbtgt,
* but it does not have a changepw or kadmin
* service */
2022-03-23 05:25:12 +03:00
entry - > valid_end = NULL ;
entry - > pw_end = NULL ;
2010-09-29 03:06:39 +04:00
/* Also don't allow the RODC krbtgt to be a client (it should not be needed) */
2022-03-23 05:25:12 +03:00
entry - > flags . client = 0 ;
entry - > flags . invalid = 0 ;
entry - > flags . server = 1 ;
entry - > flags . client = 0 ;
entry - > flags . forwardable = 1 ;
entry - > flags . ok_as_delegate = 0 ;
} else if ( entry - > flags . server & & ent_type = = SAMBA_KDC_ENT_TYPE_SERVER ) {
2010-01-28 09:27:11 +03:00
/* The account/password expiry only applies when the account is used as a
* client ( ie password login ) , not when used as a server */
/* Make very well sure we don't use this for a client,
* it could bypass the password restrictions */
2022-03-23 05:25:12 +03:00
entry - > flags . client = 0 ;
2010-01-28 09:27:11 +03:00
2022-03-23 05:25:12 +03:00
entry - > valid_end = NULL ;
entry - > pw_end = NULL ;
2010-01-28 09:27:11 +03:00
} else {
NTTIME must_change_time
2016-06-04 01:48:56 +03:00
= samdb_result_nttime ( msg ,
" msDS-UserPasswordExpiryTimeComputed " ,
0 ) ;
2010-01-28 09:27:11 +03:00
if ( must_change_time = = 0x7FFFFFFFFFFFFFFFULL ) {
2022-03-23 05:25:12 +03:00
entry - > pw_end = NULL ;
2010-01-28 09:27:11 +03:00
} else {
2022-03-23 05:25:12 +03:00
entry - > pw_end = malloc ( sizeof ( * entry - > pw_end ) ) ;
if ( entry - > pw_end = = NULL ) {
2010-01-28 09:27:11 +03:00
ret = ENOMEM ;
goto out ;
}
2022-03-23 05:25:12 +03:00
* entry - > pw_end = nt_time_to_unix ( must_change_time ) ;
2010-01-28 09:27:11 +03:00
}
acct_expiry = samdb_result_account_expires ( msg ) ;
if ( acct_expiry = = 0x7FFFFFFFFFFFFFFFULL ) {
2022-03-23 05:25:12 +03:00
entry - > valid_end = NULL ;
2010-01-28 09:27:11 +03:00
} else {
2022-03-23 05:25:12 +03:00
entry - > valid_end = malloc ( sizeof ( * entry - > valid_end ) ) ;
if ( entry - > valid_end = = NULL ) {
2010-01-28 09:27:11 +03:00
ret = ENOMEM ;
goto out ;
}
2022-03-23 05:25:12 +03:00
* entry - > valid_end = nt_time_to_unix ( acct_expiry ) ;
2010-01-28 09:27:11 +03:00
}
}
2022-05-18 07:56:01 +03:00
ret = samba_kdc_get_entry_principal ( context ,
kdc_db_ctx ,
samAccountName ,
ent_type ,
flags ,
2022-05-18 07:56:01 +03:00
entry - > flags . change_pw ,
2022-05-18 07:56:01 +03:00
principal ,
& entry - > principal ) ;
if ( ret ! = 0 ) {
krb5_clear_error_message ( context ) ;
goto out ;
}
2022-03-23 05:25:12 +03:00
entry - > valid_start = NULL ;
2010-01-28 09:27:11 +03:00
2022-03-23 05:25:12 +03:00
entry - > max_life = malloc ( sizeof ( * entry - > max_life ) ) ;
if ( entry - > max_life = = NULL ) {
2010-12-09 06:17:54 +03:00
ret = ENOMEM ;
goto out ;
}
2010-01-28 09:27:11 +03:00
2010-12-09 06:17:54 +03:00
if ( ent_type = = SAMBA_KDC_ENT_TYPE_SERVER ) {
2022-03-23 05:25:12 +03:00
* entry - > max_life = kdc_db_ctx - > policy . svc_tkt_lifetime ;
2010-12-09 06:17:54 +03:00
} else if ( ent_type = = SAMBA_KDC_ENT_TYPE_KRBTGT | | ent_type = = SAMBA_KDC_ENT_TYPE_CLIENT ) {
2022-03-23 05:25:12 +03:00
* entry - > max_life = kdc_db_ctx - > policy . usr_tkt_lifetime ;
2010-12-09 06:17:54 +03:00
} else {
2022-03-23 05:25:12 +03:00
* entry - > max_life = MIN ( kdc_db_ctx - > policy . svc_tkt_lifetime ,
2012-04-20 01:54:57 +04:00
kdc_db_ctx - > policy . usr_tkt_lifetime ) ;
2010-12-09 06:17:54 +03:00
}
2022-05-24 08:53:49 +03:00
if ( entry - > flags . change_pw ) {
/* Limit lifetime of kpasswd tickets to two minutes or less. */
* entry - > max_life = MIN ( * entry - > max_life , CHANGEPW_LIFETIME ) ;
}
2022-03-23 05:25:12 +03:00
entry - > max_renew = malloc ( sizeof ( * entry - > max_renew ) ) ;
if ( entry - > max_renew = = NULL ) {
2010-12-09 06:17:54 +03:00
ret = ENOMEM ;
goto out ;
}
2022-03-23 05:25:12 +03:00
* entry - > max_renew = kdc_db_ctx - > policy . renewal_lifetime ;
2010-01-28 09:27:11 +03:00
2023-05-04 06:06:40 +03:00
/*
* A principal acting as a client that is not being looked up as the
* principal of an armor ticket may have an authentication policy apply
* to it .
2023-06-20 03:57:27 +03:00
*
* We won ’ t get an authentication policy for the client of an S4U2Self
* or S4U2Proxy request . Those clients are looked up with
* SDB_F_FOR_TGS_REQ instead of with SDB_F_FOR_AS_REQ .
2023-05-04 06:06:40 +03:00
*/
if ( ent_type = = SAMBA_KDC_ENT_TYPE_CLIENT & &
( flags & SDB_F_FOR_AS_REQ ) & &
! ( flags & SDB_F_ARMOR_PRINCIPAL ) )
{
2023-08-10 07:07:17 +03:00
ret = authn_policy_kerberos_client ( kdc_db_ctx - > samdb , tmp_ctx , msg ,
2023-05-04 06:06:40 +03:00
& authn_client_policy ) ;
if ( ret ) {
goto out ;
}
}
/*
* A principal acting as a server may have an authentication policy
* apply to it .
*/
if ( ent_type = = SAMBA_KDC_ENT_TYPE_SERVER ) {
2023-08-10 07:07:17 +03:00
ret = authn_policy_server ( kdc_db_ctx - > samdb , tmp_ctx , msg ,
2023-05-04 06:06:40 +03:00
& authn_server_policy ) ;
if ( ret ) {
goto out ;
}
}
2024-05-21 02:14:50 +03:00
entry - > skdc_entry - > enforced_tgt_lifetime_nt_ticks = authn_policy_enforced_tgt_lifetime_raw ( authn_client_policy ) ;
lifetime_secs = entry - > skdc_entry - > enforced_tgt_lifetime_nt_ticks ;
effective_lifetime_secs = * entry - > max_life ;
2023-05-04 06:06:40 +03:00
2024-05-21 02:14:50 +03:00
if ( lifetime_secs ! = 0 ) {
2023-06-15 01:01:33 +03:00
lifetime_secs / = INT64_C ( 1000 ) * 1000 * 10 ;
lifetime_secs = MIN ( lifetime_secs , INT_MAX ) ;
lifetime_secs = MAX ( lifetime_secs , INT_MIN ) ;
2023-05-04 06:06:40 +03:00
2024-05-21 02:14:50 +03:00
effective_lifetime_secs = MIN ( effective_lifetime_secs ,
lifetime_secs ) ;
2023-05-04 06:06:40 +03:00
/*
* Set both lifetime and renewal time based only on the
* configured maximum lifetime — not on the configured renewal
* time . Yes , this is what Windows does .
*/
2024-05-21 02:14:50 +03:00
* entry - > max_life = effective_lifetime_secs ;
* entry - > max_renew = effective_lifetime_secs ;
2023-05-04 06:06:40 +03:00
}
2022-02-02 07:08:41 +03:00
if ( ent_type = = SAMBA_KDC_ENT_TYPE_CLIENT & & ( flags & SDB_F_FOR_AS_REQ ) ) {
int result ;
2023-10-11 07:07:02 +03:00
const struct auth_user_info_dc * user_info_dc = NULL ;
2022-02-02 07:08:41 +03:00
/*
* These protections only apply to clients , so servers in the
* Protected Users group may still have service tickets to them
* encrypted with RC4 . For accounts looked up as servers , note
* that ' msg ' does not contain the ' memberOf ' attribute for
* determining whether the account is a member of Protected
* Users .
*
* Additionally , Microsoft advises that accounts for services
* and computers should never be members of Protected Users , or
* they may fail to authenticate .
*/
2023-10-03 03:39:48 +03:00
ret = samba_kdc_get_user_info_from_db ( tmp_ctx ,
2023-10-03 04:53:17 +03:00
kdc_db_ctx - > samdb ,
2023-10-03 03:39:48 +03:00
p ,
msg ,
& user_info_dc ) ;
if ( ret ) {
2022-02-02 07:08:41 +03:00
goto out ;
}
result = dsdb_is_protected_user ( kdc_db_ctx - > samdb ,
user_info_dc - > sids ,
user_info_dc - > num_sids ) ;
if ( result = = - 1 ) {
ret = EINVAL ;
goto out ;
}
protected_user = result ;
2023-08-09 01:47:08 +03:00
if ( protected_user ) {
2022-03-23 05:25:12 +03:00
entry - > flags . forwardable = 0 ;
entry - > flags . proxiable = 0 ;
2023-08-09 01:47:08 +03:00
2024-05-21 02:14:50 +03:00
if ( lifetime_secs = = 0 ) {
2023-08-09 01:47:08 +03:00
/*
* If a TGT lifetime hasn ’ t been set , Protected
* Users enforces a four hour TGT lifetime .
*/
2024-05-21 02:14:50 +03:00
effective_lifetime_secs = 4 * 60 * 60 ;
* entry - > max_life = MIN ( * entry - > max_life , effective_lifetime_secs ) ;
* entry - > max_renew = MIN ( * entry - > max_renew , effective_lifetime_secs ) ;
2023-08-09 01:47:08 +03:00
}
2022-02-02 07:08:41 +03:00
}
}
2024-05-21 02:14:50 +03:00
if ( effective_lifetime_secs ! = lifetime_secs ) {
/*
* Since ‘ effective_lifetime_secs ’ has changed , update
* ‘ enforced_tgt_lifetime_nt_ticks ’ to match .
*/
entry - > skdc_entry - > enforced_tgt_lifetime_nt_ticks =
effective_lifetime_secs * ( INT64_C ( 1000 ) * 1000 * 10 ) ;
}
2022-03-23 03:07:29 +03:00
if ( rid = = DOMAIN_RID_KRBTGT | | is_rodc ) {
bool enable_fast ;
is_krbtgt = true ;
2022-03-24 17:44:40 +03:00
/*
* KDCs ( and KDCs on RODCs )
* ignore msDS - SupportedEncryptionTypes completely
* but support all supported enctypes by the domain .
*/
supported_enctypes = domain_enctypes ;
2022-11-01 05:20:47 +03:00
2022-03-23 03:07:29 +03:00
enable_fast = lpcfg_kdc_enable_fast ( kdc_db_ctx - > lp_ctx ) ;
if ( enable_fast ) {
supported_enctypes | = ENC_FAST_SUPPORTED ;
}
2022-12-02 00:49:20 +03:00
2023-03-20 06:58:47 +03:00
supported_enctypes | = ENC_CLAIMS_SUPPORTED ;
2023-03-29 00:56:22 +03:00
supported_enctypes | = ENC_COMPOUND_IDENTITY_SUPPORTED ;
2023-03-20 06:58:47 +03:00
2022-12-02 00:49:20 +03:00
/*
* Resource SID compression is enabled implicitly , unless
* disabled in msDS - SupportedEncryptionTypes .
*/
2022-03-23 03:07:29 +03:00
} else if ( userAccountControl & ( UF_PARTIAL_SECRETS_ACCOUNT | UF_SERVER_TRUST_ACCOUNT ) ) {
2022-03-24 17:44:40 +03:00
/*
* DCs and RODCs computer accounts take
* msDS - SupportedEncryptionTypes unmodified , but
* force all enctypes supported by the domain .
*/
supported_enctypes | = domain_enctypes ;
2022-11-01 05:20:47 +03:00
2022-03-23 03:07:29 +03:00
} else if ( ent_type = = SAMBA_KDC_ENT_TYPE_CLIENT | |
( ent_type = = SAMBA_KDC_ENT_TYPE_ANY ) ) {
2022-11-01 05:20:47 +03:00
/*
* for AS - REQ the client chooses the enc types it
2022-03-23 03:07:29 +03:00
* supports , and this will vary between computers a
2022-11-01 05:20:47 +03:00
* user logs in from . Therefore , so that we accept any
* of the client ' s keys for decrypting padata ,
* supported_enctypes should not restrict etype usage .
2022-03-23 03:07:29 +03:00
*
* likewise for ' any ' return as much as is supported ,
2022-11-01 05:20:47 +03:00
* to export into a keytab .
*/
supported_enctypes | = ENC_ALL_TYPES ;
2022-03-23 03:07:29 +03:00
}
/* If UF_USE_DES_KEY_ONLY has been set, then don't allow use of the newer enc types */
if ( userAccountControl & UF_USE_DES_KEY_ONLY ) {
2022-11-01 05:20:47 +03:00
supported_enctypes & = ~ ENC_ALL_TYPES ;
2024-08-16 11:24:12 +03:00
DBG_NOTICE ( " DES-only keys allowed on the account '%s', "
" most likely auth will fail through Kerberos \n " ,
samAccountName ) ;
2022-03-23 03:07:29 +03:00
}
if ( protected_user ) {
supported_enctypes & = ~ ENC_RC4_HMAC_MD5 ;
}
2022-03-24 17:44:40 +03:00
pa_supported_enctypes = supported_enctypes ;
supported_session_etypes = supported_enctypes ;
if ( supported_session_etypes & ENC_HMAC_SHA1_96_AES256_SK ) {
supported_session_etypes | = ENC_HMAC_SHA1_96_AES256 ;
supported_session_etypes | = ENC_HMAC_SHA1_96_AES128 ;
}
if ( force_rc4 ) {
supported_session_etypes | = ENC_RC4_HMAC_MD5 ;
}
/*
* now that we remembered what to announce in pa_supported_enctypes
* and normalized ENC_HMAC_SHA1_96_AES256_SK , we restrict the
* rest to the enc types the local kdc supports .
*/
supported_enctypes & = kdc_enctypes ;
supported_session_etypes & = kdc_enctypes ;
2010-01-28 09:27:11 +03:00
/* Get keys from the db */
2024-04-15 05:39:45 +03:00
ret = samba_kdc_message2entry_keys ( context , p ,
kdc_db_ctx - > samdb , msg ,
2022-03-23 03:07:29 +03:00
is_krbtgt , is_rodc ,
userAccountControl ,
2019-07-19 14:22:48 +03:00
ent_type , flags , kvno , entry ,
2022-03-23 03:07:29 +03:00
supported_enctypes ,
2022-03-24 17:44:40 +03:00
& available_enctypes ) ;
2010-01-28 09:27:11 +03:00
if ( ret ) {
2015-04-16 13:57:14 +03:00
/* Could be bogus data in the entry, or out of memory */
2010-01-28 09:27:11 +03:00
goto out ;
}
2022-03-24 17:44:40 +03:00
/*
* If we only have a nthash stored ,
* but a better session key would be
* available , we fallback to fetching the
* RC4_HMAC_MD5 , which implicitly also
* would allow an RC4_HMAC_MD5 session key .
* But only if the kdc actually supports
* RC4_HMAC_MD5 .
*/
if ( available_enctypes = = 0 & &
( supported_enctypes & ENC_RC4_HMAC_MD5 ) = = 0 & &
( supported_enctypes & ~ ENC_RC4_HMAC_MD5 ) ! = 0 & &
( kdc_enctypes & ENC_RC4_HMAC_MD5 ) ! = 0 )
{
supported_enctypes = ENC_RC4_HMAC_MD5 ;
2024-04-15 05:39:45 +03:00
ret = samba_kdc_message2entry_keys ( context , p ,
kdc_db_ctx - > samdb , msg ,
2022-03-24 17:44:40 +03:00
is_krbtgt , is_rodc ,
userAccountControl ,
ent_type , flags , kvno , entry ,
supported_enctypes ,
& available_enctypes ) ;
if ( ret ) {
/* Could be bogus data in the entry, or out of memory */
goto out ;
}
}
/*
* We need to support all session keys enctypes for
* all keys we provide
*/
supported_session_etypes | = available_enctypes ;
2022-11-01 05:20:47 +03:00
ret = sdb_entry_set_etypes ( entry ) ;
if ( ret ) {
goto out ;
}
if ( entry - > flags . server ) {
2022-03-24 17:44:40 +03:00
bool add_aes256 =
supported_session_etypes & KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 ;
bool add_aes128 =
supported_session_etypes & KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 ;
bool add_rc4 =
supported_session_etypes & ENC_RC4_HMAC_MD5 ;
2022-11-01 05:20:47 +03:00
ret = sdb_entry_set_session_etypes ( entry ,
2022-03-24 17:44:40 +03:00
add_aes256 ,
add_aes128 ,
add_rc4 ) ;
2022-11-01 05:20:47 +03:00
if ( ret ) {
goto out ;
}
}
if ( entry - > keys . len ! = 0 ) {
/*
* FIXME : Currently limited to Heimdal so as not to
* break MIT KDCs , for which no fix is available .
*/
# ifdef SAMBA4_USES_HEIMDAL
if ( is_krbtgt ) {
2022-03-23 00:13:54 +03:00
/*
2022-11-01 05:20:47 +03:00
* The krbtgt account , having no reason to
* issue tickets encrypted in weaker keys ,
* shall only make available its strongest
* key . All weaker keys are stripped out . This
* makes it impossible for an RC4 - encrypted
* TGT to be accepted when AES KDC keys exist .
*
* This controls the ticket key and so the PAC
* signature algorithms indirectly , preventing
* a weak KDC checksum from being accepted
* when we verify the signatures for an
* S4U2Proxy evidence ticket . As such , this is
* indispensable for addressing
* CVE - 2022 - 37966.
*
* Being strict here also provides protection
* against possible future attacks on weak
* keys .
2022-03-23 00:13:54 +03:00
*/
2024-05-09 04:16:50 +03:00
/*
* The krbtgt account is never a Group Managed Service
* Account , but a similar system might well be
* implemented as a means of having the krbtgt ’ s keys
* roll over automatically . In that case , thought might
* be given as to how this security measure — of
* stripping out weaker keys — would interact with key
* management .
*/
2022-11-01 05:20:47 +03:00
entry - > keys . len = 1 ;
if ( entry - > etypes ! = NULL ) {
2023-08-10 05:48:04 +03:00
entry - > etypes - > len = MIN ( entry - > etypes - > len , 1 ) ;
2022-11-01 05:20:47 +03:00
}
2022-12-04 23:05:39 +03:00
entry - > old_keys . len = MIN ( entry - > old_keys . len , 1 ) ;
entry - > older_keys . len = MIN ( entry - > older_keys . len , 1 ) ;
2022-03-23 00:13:54 +03:00
}
2022-11-01 05:20:47 +03:00
# endif
} else if ( kdc_db_ctx - > rodc ) {
/*
* We are on an RODC , but don ' t have keys for this
* account . Signal this to the caller
*/
auth_sam_trigger_repl_secret ( kdc_db_ctx ,
kdc_db_ctx - > msg_ctx ,
kdc_db_ctx - > ev_ctx ,
msg - > dn ) ;
2023-08-10 06:11:51 +03:00
ret = SDB_ERR_NOT_FOUND_HERE ;
goto out ;
2022-11-01 05:20:47 +03:00
} else {
2022-03-23 00:13:54 +03:00
/*
* oh , no password . Apparently ( comment in
* hdb - ldap . c ) this violates the ASN .1 , but this
* allows an entry with no keys ( yet ) .
*/
}
2010-01-28 09:27:11 +03:00
p - > msg = talloc_steal ( p , msg ) ;
2022-03-24 17:44:40 +03:00
p - > supported_enctypes = pa_supported_enctypes ;
2010-01-28 09:27:11 +03:00
2023-05-04 06:06:40 +03:00
p - > client_policy = talloc_steal ( p , authn_client_policy ) ;
p - > server_policy = talloc_steal ( p , authn_server_policy ) ;
2023-08-10 06:25:50 +03:00
talloc_steal ( kdc_db_ctx , p ) ;
2010-01-28 09:27:11 +03:00
out :
if ( ret ! = 0 ) {
/* This doesn't free ent itself, that is for the eventual caller to do */
2022-03-23 05:29:21 +03:00
sdb_entry_free ( entry ) ;
2010-01-28 09:27:11 +03:00
}
2023-08-10 07:07:17 +03:00
talloc_free ( tmp_ctx ) ;
2010-01-28 09:27:11 +03:00
return ret ;
}
2024-03-15 21:19:20 +03:00
struct samba_kdc_trust_keys {
struct sdb_keys * skeys ;
uint32_t kvno ;
uint32_t * returned_kvno ;
uint32_t supported_enctypes ;
uint32_t * available_enctypes ;
krb5_const_principal salt_principal ;
const struct AuthenticationInformationArray * auth_array ;
} ;
static krb5_error_code samba_kdc_fill_trust_keys ( krb5_context context ,
struct samba_kdc_trust_keys * p )
{
/*
* Make sure we ' ll never reveal DES keys
*/
uint32_t supported_enctypes = p - > supported_enctypes & = ~ ( ENC_CRC32 | ENC_RSA_MD5 ) ;
uint32_t _available_enctypes = 0 ;
uint32_t * available_enctypes = p - > available_enctypes ;
uint32_t _returned_kvno = 0 ;
uint32_t * returned_kvno = p - > returned_kvno ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
const struct AuthenticationInformationArray * aa = p - > auth_array ;
DATA_BLOB password_utf16 = { . length = 0 , } ;
DATA_BLOB password_utf8 = { . length = 0 , } ;
struct samr_Password _password_hash = { . hash = { 0 , } , } ;
const struct samr_Password * password_hash = NULL ;
uint32_t allocated_keys = 0 ;
uint32_t i ;
int ret ;
if ( available_enctypes = = NULL ) {
available_enctypes = & _available_enctypes ;
}
* available_enctypes = 0 ;
if ( returned_kvno = = NULL ) {
returned_kvno = & _returned_kvno ;
}
* returned_kvno = p - > kvno ;
for ( i = 0 ; i < aa - > count ; i + + ) {
if ( aa - > array [ i ] . AuthType = = TRUST_AUTH_TYPE_CLEAR ) {
const struct AuthInfoClear * clear =
& aa - > array [ i ] . AuthInfo . clear ;
bool ok ;
password_utf16 = data_blob_const ( clear - > password ,
clear - > size ) ;
if ( password_utf16 . length = = 0 ) {
break ;
}
if ( supported_enctypes & ENC_RC4_HMAC_MD5 ) {
mdfour ( _password_hash . hash ,
password_utf16 . data ,
password_utf16 . length ) ;
if ( password_hash = = NULL ) {
allocated_keys + = 1 ;
}
password_hash = & _password_hash ;
}
if ( ! ( supported_enctypes & ( ENC_HMAC_SHA1_96_AES128 | ENC_HMAC_SHA1_96_AES256 ) ) ) {
break ;
}
ok = convert_string_talloc ( frame ,
CH_UTF16MUNGED , CH_UTF8 ,
password_utf16 . data ,
password_utf16 . length ,
& password_utf8 . data ,
& password_utf8 . length ) ;
if ( ! ok ) {
krb5_clear_error_message ( context ) ;
ret = ENOMEM ;
goto fail ;
}
if ( supported_enctypes & ENC_HMAC_SHA1_96_AES128 ) {
allocated_keys + = 1 ;
}
if ( supported_enctypes & ENC_HMAC_SHA1_96_AES256 ) {
allocated_keys + = 1 ;
}
break ;
} else if ( aa - > array [ i ] . AuthType = = TRUST_AUTH_TYPE_NT4OWF ) {
const struct AuthInfoNT4Owf * nt4owf =
& aa - > array [ i ] . AuthInfo . nt4owf ;
if ( supported_enctypes & ENC_RC4_HMAC_MD5 ) {
password_hash = & nt4owf - > password ;
allocated_keys + = 1 ;
}
}
}
allocated_keys = MAX ( 1 , allocated_keys ) ;
/* allocate space to decode into */
p - > skeys - > len = 0 ;
p - > skeys - > val = calloc ( allocated_keys , sizeof ( struct sdb_key ) ) ;
if ( p - > skeys - > val = = NULL ) {
krb5_clear_error_message ( context ) ;
ret = ENOMEM ;
goto fail ;
}
if ( password_utf8 . length ! = 0 ) {
struct sdb_key key = { } ;
krb5_data salt ;
krb5_data cleartext_data ;
cleartext_data . data = discard_const_p ( char , password_utf8 . data ) ;
cleartext_data . length = password_utf8 . length ;
ret = smb_krb5_get_pw_salt ( context ,
p - > salt_principal ,
& salt ) ;
if ( ret ! = 0 ) {
goto fail ;
}
if ( supported_enctypes & ENC_HMAC_SHA1_96_AES256 ) {
key . salt = calloc ( 1 , sizeof ( * key . salt ) ) ;
if ( key . salt = = NULL ) {
smb_krb5_free_data_contents ( context , & salt ) ;
ret = ENOMEM ;
goto fail ;
}
key . salt - > type = KRB5_PW_SALT ;
ret = smb_krb5_copy_data_contents ( & key . salt - > salt ,
salt . data ,
salt . length ) ;
if ( ret ) {
* key . salt = ( struct sdb_salt ) { } ;
sdb_key_free ( & key ) ;
smb_krb5_free_data_contents ( context , & salt ) ;
goto fail ;
}
ret = smb_krb5_create_key_from_string ( context ,
p - > salt_principal ,
& salt ,
& cleartext_data ,
ENCTYPE_AES256_CTS_HMAC_SHA1_96 ,
& key . key ) ;
if ( ret = = 0 ) {
p - > skeys - > val [ p - > skeys - > len + + ] = key ;
* available_enctypes | = ENC_HMAC_SHA1_96_AES256 ;
} else if ( ret = = KRB5_PROG_ETYPE_NOSUPP ) {
DBG_NOTICE ( " Unsupported keytype ignored - type %u \n " ,
ENCTYPE_AES256_CTS_HMAC_SHA1_96 ) ;
ZERO_STRUCT ( key . key ) ;
sdb_key_free ( & key ) ;
ret = 0 ;
}
if ( ret ! = 0 ) {
ZERO_STRUCT ( key . key ) ;
sdb_key_free ( & key ) ;
smb_krb5_free_data_contents ( context , & salt ) ;
goto fail ;
}
}
if ( supported_enctypes & ENC_HMAC_SHA1_96_AES128 ) {
key . salt = calloc ( 1 , sizeof ( * key . salt ) ) ;
if ( key . salt = = NULL ) {
smb_krb5_free_data_contents ( context , & salt ) ;
ret = ENOMEM ;
goto fail ;
}
key . salt - > type = KRB5_PW_SALT ;
ret = smb_krb5_copy_data_contents ( & key . salt - > salt ,
salt . data ,
salt . length ) ;
if ( ret ) {
* key . salt = ( struct sdb_salt ) { } ;
sdb_key_free ( & key ) ;
smb_krb5_free_data_contents ( context , & salt ) ;
goto fail ;
}
ret = smb_krb5_create_key_from_string ( context ,
p - > salt_principal ,
& salt ,
& cleartext_data ,
ENCTYPE_AES128_CTS_HMAC_SHA1_96 ,
& key . key ) ;
if ( ret = = 0 ) {
p - > skeys - > val [ p - > skeys - > len + + ] = key ;
* available_enctypes | = ENC_HMAC_SHA1_96_AES128 ;
} else if ( ret = = KRB5_PROG_ETYPE_NOSUPP ) {
DBG_NOTICE ( " Unsupported keytype ignored - type %u \n " ,
ENCTYPE_AES128_CTS_HMAC_SHA1_96 ) ;
ZERO_STRUCT ( key . key ) ;
sdb_key_free ( & key ) ;
ret = 0 ;
}
if ( ret ! = 0 ) {
ZERO_STRUCT ( key . key ) ;
sdb_key_free ( & key ) ;
smb_krb5_free_data_contents ( context , & salt ) ;
goto fail ;
}
}
smb_krb5_free_data_contents ( context , & salt ) ;
}
if ( password_hash ! = NULL ) {
struct sdb_key key = { } ;
ret = smb_krb5_keyblock_init_contents ( context ,
ENCTYPE_ARCFOUR_HMAC ,
password_hash - > hash ,
sizeof ( password_hash - > hash ) ,
& key . key ) ;
if ( ret = = 0 ) {
p - > skeys - > val [ p - > skeys - > len + + ] = key ;
* available_enctypes | = ENC_RC4_HMAC_MD5 ;
} else if ( ret = = KRB5_PROG_ETYPE_NOSUPP ) {
DEBUG ( 2 , ( " Unsupported keytype ignored - type %u \n " ,
ENCTYPE_ARCFOUR_HMAC ) ) ;
ZERO_STRUCT ( key . key ) ;
sdb_key_free ( & key ) ;
ret = 0 ;
}
if ( ret ! = 0 ) {
ZERO_STRUCT ( key . key ) ;
sdb_key_free ( & key ) ;
goto fail ;
}
}
samba_kdc_sort_keys ( p - > skeys ) ;
return 0 ;
fail :
sdb_keys_free ( p - > skeys ) ;
TALLOC_FREE ( frame ) ;
return ret ;
}
2010-01-28 09:27:11 +03:00
/*
* Construct an hdb_entry from a directory entry .
2011-09-30 00:47:08 +04:00
* The kvno is what the remote client asked for
2010-01-28 09:27:11 +03:00
*/
static krb5_error_code samba_kdc_trust_message2entry ( krb5_context context ,
struct samba_kdc_db_context * kdc_db_ctx ,
2022-02-03 16:12:02 +03:00
TALLOC_CTX * mem_ctx ,
2010-01-28 09:27:11 +03:00
enum trust_direction direction ,
struct ldb_dn * realm_dn ,
2011-09-30 00:47:08 +04:00
unsigned flags ,
uint32_t kvno ,
2010-01-28 09:27:11 +03:00
struct ldb_message * msg ,
2022-03-23 05:36:31 +03:00
struct sdb_entry * entry )
2010-01-28 09:27:11 +03:00
{
2023-08-10 07:07:17 +03:00
TALLOC_CTX * tmp_ctx = NULL ;
2010-01-28 09:27:11 +03:00
struct loadparm_context * lp_ctx = kdc_db_ctx - > lp_ctx ;
2015-06-10 11:25:20 +03:00
const char * our_realm = lpcfg_realm ( lp_ctx ) ;
char * partner_realm = NULL ;
const char * realm = NULL ;
const char * krbtgt_realm = NULL ;
2010-01-28 09:27:11 +03:00
const struct ldb_val * password_val ;
struct trustAuthInOutBlob password_blob ;
struct samba_kdc_entry * p ;
2015-04-10 23:31:20 +03:00
bool use_previous = false ;
2024-03-15 21:23:46 +03:00
bool include_previous = false ;
2011-09-30 00:47:08 +04:00
uint32_t current_kvno ;
2015-04-10 23:31:20 +03:00
uint32_t previous_kvno ;
2024-03-15 21:19:20 +03:00
struct samba_kdc_trust_keys current_keys = { } ;
struct samba_kdc_trust_keys previous_keys = { } ;
2010-01-28 09:27:11 +03:00
enum ndr_err_code ndr_err ;
2018-02-01 13:10:14 +03:00
int ret ;
2010-04-12 01:17:15 +04:00
unsigned int i ;
2024-05-28 03:53:19 +03:00
NTTIME now = * kdc_db_ctx - > current_nttime_ull ;
NTTIME an_hour_ago , an_hour ;
2023-08-10 07:12:13 +03:00
bool prefer_current = false ;
2022-03-24 17:44:40 +03:00
bool force_rc4 = lpcfg_kdc_force_enable_rc4_weak_session_keys ( lp_ctx ) ;
2015-01-20 13:52:22 +03:00
uint32_t supported_enctypes = ENC_RC4_HMAC_MD5 ;
2022-03-24 17:44:40 +03:00
uint32_t pa_supported_enctypes ;
uint32_t supported_session_etypes ;
2022-11-30 11:39:19 +03:00
uint32_t config_kdc_enctypes = lpcfg_kdc_supported_enctypes ( lp_ctx ) ;
uint32_t kdc_enctypes =
config_kdc_enctypes ! = 0 ?
config_kdc_enctypes :
ENC_ALL_TYPES ;
2018-02-01 13:10:14 +03:00
struct lsa_TrustDomainInfoInfoEx * tdo = NULL ;
NTSTATUS status ;
2024-03-15 21:19:20 +03:00
uint32_t returned_kvno = 0 ;
2024-03-15 21:19:20 +03:00
uint32_t available_enctypes = 0 ;
2014-12-15 18:48:27 +03:00
2023-09-07 07:35:39 +03:00
* entry = ( struct sdb_entry ) { } ;
2022-02-08 18:50:14 +03:00
2023-08-10 07:07:17 +03:00
tmp_ctx = talloc_new ( mem_ctx ) ;
if ( tmp_ctx = = NULL ) {
return ENOMEM ;
}
2014-12-15 18:48:27 +03:00
if ( dsdb_functional_level ( kdc_db_ctx - > samdb ) > = DS_DOMAIN_FUNCTION_2008 ) {
2022-11-22 01:32:34 +03:00
/* If not told otherwise, Windows now assumes that trusts support AES. */
2014-12-15 18:48:27 +03:00
supported_enctypes = ldb_msg_find_attr_as_uint ( msg ,
" msDS-SupportedEncryptionTypes " ,
2022-11-22 01:32:34 +03:00
ENC_HMAC_SHA1_96_AES256 ) ;
2014-12-15 18:48:27 +03:00
}
2010-01-28 09:27:11 +03:00
2022-03-24 17:44:40 +03:00
pa_supported_enctypes = supported_enctypes ;
supported_session_etypes = supported_enctypes ;
if ( supported_session_etypes & ENC_HMAC_SHA1_96_AES256_SK ) {
supported_session_etypes | = ENC_HMAC_SHA1_96_AES256 ;
supported_session_etypes | = ENC_HMAC_SHA1_96_AES128 ;
}
if ( force_rc4 ) {
supported_session_etypes | = ENC_RC4_HMAC_MD5 ;
}
/*
* now that we remembered what to announce in pa_supported_enctypes
* and normalized ENC_HMAC_SHA1_96_AES256_SK , we restrict the
* rest to the enc types the local kdc supports .
*/
supported_enctypes & = kdc_enctypes ;
supported_session_etypes & = kdc_enctypes ;
2023-08-10 07:07:17 +03:00
status = dsdb_trust_parse_tdo_info ( tmp_ctx , msg , & tdo ) ;
2018-02-01 13:10:14 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
krb5_clear_error_message ( context ) ;
ret = ENOMEM ;
goto out ;
}
if ( ! ( tdo - > trust_direction & direction ) ) {
2015-06-10 11:25:20 +03:00
krb5_clear_error_message ( context ) ;
2014-05-08 19:13:04 +04:00
ret = SDB_ERR_NOENTRY ;
2015-06-10 11:25:20 +03:00
goto out ;
}
2018-02-01 13:10:14 +03:00
if ( tdo - > trust_type ! = LSA_TRUST_TYPE_UPLEVEL ) {
/*
* Only UPLEVEL domains support kerberos here ,
* as we don ' t support LSA_TRUST_TYPE_MIT .
*/
krb5_clear_error_message ( context ) ;
ret = SDB_ERR_NOENTRY ;
goto out ;
}
2018-02-01 13:06:10 +03:00
if ( tdo - > trust_attributes & LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION ) {
/*
* We don ' t support selective authentication yet .
*/
krb5_clear_error_message ( context ) ;
ret = SDB_ERR_NOENTRY ;
goto out ;
}
2018-02-01 13:10:14 +03:00
if ( tdo - > domain_name . string = = NULL ) {
2015-06-10 11:25:20 +03:00
krb5_clear_error_message ( context ) ;
2014-05-08 19:13:04 +04:00
ret = SDB_ERR_NOENTRY ;
2015-06-10 11:25:20 +03:00
goto out ;
}
2023-08-10 07:07:17 +03:00
partner_realm = strupper_talloc ( tmp_ctx , tdo - > domain_name . string ) ;
2015-06-10 11:25:20 +03:00
if ( partner_realm = = NULL ) {
krb5_clear_error_message ( context ) ;
ret = ENOMEM ;
goto out ;
}
2015-03-16 00:25:49 +03:00
if ( direction = = INBOUND ) {
2015-06-10 11:25:20 +03:00
realm = our_realm ;
krbtgt_realm = partner_realm ;
2015-03-16 00:25:49 +03:00
2015-06-10 11:25:20 +03:00
password_val = ldb_msg_find_ldb_val ( msg , " trustAuthIncoming " ) ;
2015-03-16 00:25:49 +03:00
} else { /* OUTBOUND */
2015-06-10 11:25:20 +03:00
realm = partner_realm ;
krbtgt_realm = our_realm ;
2015-03-16 00:25:49 +03:00
password_val = ldb_msg_find_ldb_val ( msg , " trustAuthOutgoing " ) ;
}
2015-06-10 11:25:20 +03:00
if ( password_val = = NULL ) {
2015-03-16 00:25:49 +03:00
krb5_clear_error_message ( context ) ;
2014-05-08 19:13:04 +04:00
ret = SDB_ERR_NOENTRY ;
2015-03-16 00:25:49 +03:00
goto out ;
}
2023-08-10 07:07:17 +03:00
ndr_err = ndr_pull_struct_blob ( password_val , tmp_ctx , & password_blob ,
2015-03-16 00:25:49 +03:00
( ndr_pull_flags_fn_t ) ndr_pull_trustAuthInOutBlob ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
krb5_clear_error_message ( context ) ;
ret = EINVAL ;
goto out ;
}
2023-08-10 07:07:17 +03:00
p = talloc_zero ( tmp_ctx , struct samba_kdc_entry ) ;
2010-01-28 09:27:11 +03:00
if ( ! p ) {
ret = ENOMEM ;
goto out ;
}
2018-02-01 13:44:21 +03:00
p - > is_trust = true ;
2010-01-28 09:27:11 +03:00
p - > kdc_db_ctx = kdc_db_ctx ;
p - > realm_dn = realm_dn ;
2022-03-24 17:44:40 +03:00
p - > supported_enctypes = pa_supported_enctypes ;
2024-05-28 03:53:19 +03:00
p - > current_nttime = * kdc_db_ctx - > current_nttime_ull ;
2010-01-28 09:27:11 +03:00
talloc_set_destructor ( p , samba_kdc_entry_destructor ) ;
2022-03-23 05:25:12 +03:00
entry - > skdc_entry = p ;
2010-01-28 09:27:11 +03:00
/* use 'whenCreated' */
2022-03-23 05:25:12 +03:00
entry - > created_by . time = ldb_msg_find_krb5time_ldap_time ( msg , " whenCreated " , 0 ) ;
2010-02-13 00:24:22 +03:00
/* use 'kadmin' for now (needed by mit_samba) */
2014-05-07 18:47:52 +04:00
ret = smb_krb5_make_principal ( context ,
2022-03-23 05:25:12 +03:00
& entry - > created_by . principal ,
2014-05-07 18:47:52 +04:00
realm , " kadmin " , NULL ) ;
2015-01-30 02:31:29 +03:00
if ( ret ) {
krb5_clear_error_message ( context ) ;
goto out ;
}
2010-01-28 09:27:11 +03:00
2014-12-15 18:48:27 +03:00
/*
2015-06-10 11:25:20 +03:00
* We always need to generate the canonicalized principal
* with the values of our database .
2014-12-15 18:48:27 +03:00
*/
2022-03-23 05:25:12 +03:00
ret = smb_krb5_make_principal ( context , & entry - > principal , realm ,
2015-06-10 11:25:20 +03:00
" krbtgt " , krbtgt_realm , NULL ) ;
2015-01-30 02:31:29 +03:00
if ( ret ) {
krb5_clear_error_message ( context ) ;
goto out ;
}
2022-03-23 05:25:12 +03:00
smb_krb5_principal_set_type ( context , entry - > principal ,
2015-06-10 11:25:20 +03:00
KRB5_NT_SRV_INST ) ;
2014-12-15 18:48:27 +03:00
2022-03-23 05:25:12 +03:00
entry - > valid_start = NULL ;
2010-01-28 09:27:11 +03:00
2011-09-30 00:47:08 +04:00
/* we need to work out if we are going to use the current or
* the previous password hash .
* We base this on the kvno the client passes in . If the kvno
* passed in is equal to the current kvno in our database then
* we use the current structure . If it is the current kvno - 1 ,
2023-09-04 07:52:57 +03:00
* then we use the previous substructure .
2011-09-16 05:15:27 +04:00
*/
2011-09-30 00:47:08 +04:00
2015-07-01 06:33:10 +03:00
/*
2023-08-10 07:12:13 +03:00
* Windows prefers the previous key for one hour .
2015-07-01 06:33:10 +03:00
*/
2024-05-28 03:53:19 +03:00
an_hour = INT64_C ( 1000 ) * 1000 * 10 * 3600 ;
/*
* While a ' now ' value of 0 is implausible , avoid this being a
* silly value in that case
*/
if ( now > an_hour ) {
an_hour_ago = now - an_hour ;
} else {
an_hour_ago = now ;
2015-07-01 06:33:10 +03:00
}
2011-09-30 00:47:08 +04:00
/* first work out the current kvno */
current_kvno = 0 ;
2010-01-28 09:27:11 +03:00
for ( i = 0 ; i < password_blob . count ; i + + ) {
2015-04-10 23:31:20 +03:00
struct AuthenticationInformation * a =
& password_blob . current . array [ i ] ;
2015-07-01 06:33:10 +03:00
if ( a - > LastUpdateTime < = an_hour_ago ) {
2023-08-10 07:12:13 +03:00
prefer_current = true ;
2015-07-01 06:33:10 +03:00
}
2015-04-10 23:31:20 +03:00
if ( a - > AuthType = = TRUST_AUTH_TYPE_VERSION ) {
current_kvno = a - > AuthInfo . version . version ;
}
}
if ( current_kvno = = 0 ) {
previous_kvno = 255 ;
} else {
previous_kvno = current_kvno - 1 ;
}
for ( i = 0 ; i < password_blob . count ; i + + ) {
struct AuthenticationInformation * a =
& password_blob . previous . array [ i ] ;
if ( a - > AuthType = = TRUST_AUTH_TYPE_VERSION ) {
previous_kvno = a - > AuthInfo . version . version ;
2010-01-28 09:27:11 +03:00
}
}
2011-09-30 00:47:08 +04:00
/* work out whether we will use the previous or current
password */
if ( password_blob . previous . count = = 0 ) {
/* there is no previous password */
use_previous = false ;
2014-05-08 19:13:04 +04:00
} else if ( ! ( flags & SDB_F_KVNO_SPECIFIED ) ) {
2015-04-10 23:31:20 +03:00
/*
2015-07-01 06:33:10 +03:00
* If not specified we use the lowest kvno
* for the first hour after an update .
2015-04-10 23:31:20 +03:00
*/
2023-08-10 07:12:13 +03:00
if ( prefer_current ) {
2015-07-01 06:33:10 +03:00
use_previous = false ;
} else if ( previous_kvno < current_kvno ) {
use_previous = true ;
} else {
use_previous = false ;
}
2024-03-15 21:23:46 +03:00
if ( flags & SDB_F_ADMIN_DATA ) {
/*
* let admin tool
* get to all keys
*/
use_previous = false ;
include_previous = true ;
}
2015-04-10 23:31:20 +03:00
} else if ( kvno = = current_kvno ) {
/*
* Exact match . . .
*/
2011-09-30 00:47:08 +04:00
use_previous = false ;
2015-04-10 23:31:20 +03:00
} else if ( kvno = = previous_kvno ) {
/*
* Exact match . . .
*/
2011-09-30 00:47:08 +04:00
use_previous = true ;
} else {
2015-04-10 23:31:20 +03:00
/*
* Fallback to the current one for anything else
*/
use_previous = false ;
2011-09-30 00:47:08 +04:00
}
2024-03-15 21:19:20 +03:00
current_keys = ( struct samba_kdc_trust_keys ) {
. kvno = current_kvno ,
. supported_enctypes = supported_enctypes ,
. salt_principal = entry - > principal ,
. auth_array = & password_blob . current ,
} ;
previous_keys = ( struct samba_kdc_trust_keys ) {
. kvno = previous_kvno ,
. supported_enctypes = supported_enctypes ,
. salt_principal = entry - > principal ,
. auth_array = & password_blob . previous ,
} ;
2011-09-30 00:47:08 +04:00
if ( use_previous ) {
2024-03-15 21:19:20 +03:00
/*
* return the old keys as default keys
* with the requested kvno .
*/
previous_keys . skeys = & entry - > keys ;
previous_keys . available_enctypes = & available_enctypes ;
previous_keys . returned_kvno = & returned_kvno ;
2011-09-30 00:47:08 +04:00
} else {
2024-03-15 21:19:20 +03:00
/*
* return the current keys as default keys
* with the requested kvno .
*/
current_keys . skeys = & entry - > keys ;
current_keys . available_enctypes = & available_enctypes ;
current_keys . returned_kvno = & returned_kvno ;
2024-03-15 21:23:46 +03:00
if ( include_previous ) {
/*
* return the old keys in addition .
*/
previous_keys . skeys = & entry - > old_keys ;
}
2011-09-30 00:47:08 +04:00
}
2024-03-15 21:19:20 +03:00
if ( current_keys . skeys ! = NULL ) {
ret = samba_kdc_fill_trust_keys ( context , & current_keys ) ;
if ( ret ! = 0 ) {
goto out ;
}
2011-09-30 00:47:08 +04:00
}
2024-03-15 21:19:20 +03:00
if ( previous_keys . skeys ! = NULL ) {
ret = samba_kdc_fill_trust_keys ( context , & previous_keys ) ;
if ( ret ! = 0 ) {
goto out ;
2010-01-28 09:27:11 +03:00
}
}
2024-03-15 21:19:20 +03:00
/* use the kvno the client specified, if available */
if ( flags & SDB_F_KVNO_SPECIFIED ) {
returned_kvno = kvno ;
}
2014-12-15 18:48:27 +03:00
/* Must have found a cleartext or MD4 password */
2024-03-15 21:19:20 +03:00
if ( entry - > keys . len = = 0 ) {
2023-07-21 02:44:05 +03:00
DBG_WARNING ( " no usable key found \n " ) ;
2014-12-15 18:48:27 +03:00
krb5_clear_error_message ( context ) ;
2014-05-08 19:13:04 +04:00
ret = SDB_ERR_NOENTRY ;
2014-12-15 18:48:27 +03:00
goto out ;
}
2010-01-28 09:27:11 +03:00
2023-10-12 01:54:50 +03:00
entry - > flags = ( struct SDBFlags ) { } ;
2022-03-23 05:25:12 +03:00
entry - > flags . immutable = 1 ;
entry - > flags . invalid = 0 ;
entry - > flags . server = 1 ;
entry - > flags . require_preauth = 1 ;
2010-01-28 09:27:11 +03:00
2022-03-23 05:25:12 +03:00
entry - > pw_end = NULL ;
2010-01-28 09:27:11 +03:00
2022-03-23 05:25:12 +03:00
entry - > max_life = NULL ;
2010-01-28 09:27:11 +03:00
2022-03-23 05:25:12 +03:00
entry - > max_renew = NULL ;
2010-01-28 09:27:11 +03:00
2020-01-14 15:16:02 +03:00
/* Match Windows behavior and allow forwardable flag in cross-realm. */
2022-03-23 05:25:12 +03:00
entry - > flags . forwardable = 1 ;
2020-01-14 15:16:02 +03:00
2024-03-15 21:19:20 +03:00
entry - > kvno = returned_kvno ;
2024-03-15 21:19:20 +03:00
/*
* We need to support all session keys enctypes for
* all keys we provide
*/
supported_session_etypes | = available_enctypes ;
2022-11-01 05:20:47 +03:00
ret = sdb_entry_set_etypes ( entry ) ;
if ( ret ) {
goto out ;
}
{
2022-03-24 17:44:40 +03:00
bool add_aes256 =
supported_session_etypes & KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 ;
bool add_aes128 =
supported_session_etypes & KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 ;
bool add_rc4 =
supported_session_etypes & ENC_RC4_HMAC_MD5 ;
2022-11-01 05:20:47 +03:00
ret = sdb_entry_set_session_etypes ( entry ,
2022-03-24 17:44:40 +03:00
add_aes256 ,
add_aes128 ,
add_rc4 ) ;
2022-11-01 05:20:47 +03:00
if ( ret ) {
goto out ;
}
}
2010-01-28 09:27:11 +03:00
p - > msg = talloc_steal ( p , msg ) ;
2023-08-10 06:25:50 +03:00
talloc_steal ( kdc_db_ctx , p ) ;
2010-01-28 09:27:11 +03:00
out :
2015-06-10 11:25:20 +03:00
TALLOC_FREE ( partner_realm ) ;
2010-01-28 09:27:11 +03:00
if ( ret ! = 0 ) {
/* This doesn't free ent itself, that is for the eventual caller to do */
2022-03-23 05:29:21 +03:00
sdb_entry_free ( entry ) ;
2010-01-28 09:27:11 +03:00
}
2023-08-10 07:07:17 +03:00
talloc_free ( tmp_ctx ) ;
2010-01-28 09:27:11 +03:00
return ret ;
}
static krb5_error_code samba_kdc_lookup_trust ( krb5_context context , struct ldb_context * ldb_ctx ,
TALLOC_CTX * mem_ctx ,
const char * realm ,
struct ldb_dn * realm_dn ,
struct ldb_message * * pmsg )
{
2014-08-15 07:00:25 +04:00
NTSTATUS status ;
2010-01-28 09:27:11 +03:00
const char * const * attrs = trust_attrs ;
2015-03-30 11:17:51 +03:00
status = dsdb_trust_search_tdo ( ldb_ctx , realm , realm ,
attrs , mem_ctx , pmsg ) ;
2014-08-15 07:00:25 +04:00
if ( NT_STATUS_IS_OK ( status ) ) {
return 0 ;
2015-03-30 11:17:51 +03:00
} else if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ) {
2014-05-08 19:13:04 +04:00
return SDB_ERR_NOENTRY ;
2014-08-15 07:00:25 +04:00
} else if ( NT_STATUS_EQUAL ( status , NT_STATUS_NO_MEMORY ) ) {
int ret = ENOMEM ;
2023-05-09 07:26:35 +03:00
krb5_set_error_message ( context , ret , " samba_kdc_lookup_trust: out of memory " ) ;
2014-08-15 07:00:25 +04:00
return ret ;
} else {
int ret = EINVAL ;
2023-05-09 07:26:35 +03:00
krb5_set_error_message ( context , ret , " samba_kdc_lookup_trust: %s " , nt_errstr ( status ) ) ;
2014-08-15 07:00:25 +04:00
return ret ;
2010-01-28 09:27:11 +03:00
}
}
static krb5_error_code samba_kdc_lookup_client ( krb5_context context ,
struct samba_kdc_db_context * kdc_db_ctx ,
TALLOC_CTX * mem_ctx ,
krb5_const_principal principal ,
const char * * attrs ,
2024-04-15 02:42:50 +03:00
const uint32_t dsdb_flags ,
2010-01-28 09:27:11 +03:00
struct ldb_dn * * realm_dn ,
2015-03-23 13:00:51 +03:00
struct ldb_message * * msg )
{
2010-01-28 09:27:11 +03:00
NTSTATUS nt_status ;
2015-03-23 13:00:51 +03:00
char * principal_string = NULL ;
2010-01-28 09:27:11 +03:00
2014-05-08 14:21:43 +04:00
if ( smb_krb5_principal_get_type ( context , principal ) = = KRB5_NT_ENTERPRISE_PRINCIPAL ) {
2023-09-21 01:41:05 +03:00
krb5_error_code ret = 0 ;
ret = smb_krb5_principal_get_comp_string ( mem_ctx , context ,
principal , 0 , & principal_string ) ;
if ( ret ) {
return ret ;
2014-12-17 07:02:53 +03:00
}
} else {
2015-03-23 13:00:51 +03:00
char * principal_string_m = NULL ;
2014-12-17 07:02:53 +03:00
krb5_error_code ret ;
2015-03-23 13:00:51 +03:00
ret = krb5_unparse_name ( context , principal , & principal_string_m ) ;
2014-12-17 07:02:53 +03:00
if ( ret ! = 0 ) {
return ret ;
}
2015-03-23 13:00:51 +03:00
principal_string = talloc_strdup ( mem_ctx , principal_string_m ) ;
SAFE_FREE ( principal_string_m ) ;
if ( principal_string = = NULL ) {
return ENOMEM ;
}
}
nt_status = sam_get_results_principal ( kdc_db_ctx - > samdb ,
2024-04-15 02:42:50 +03:00
mem_ctx , principal_string , attrs , dsdb_flags ,
2015-03-23 13:00:51 +03:00
realm_dn , msg ) ;
if ( NT_STATUS_EQUAL ( nt_status , NT_STATUS_NO_SUCH_USER ) ) {
krb5_principal fallback_principal = NULL ;
unsigned int num_comp ;
char * fallback_realm = NULL ;
char * fallback_account = NULL ;
krb5_error_code ret ;
ret = krb5_parse_name ( context , principal_string ,
& fallback_principal ) ;
TALLOC_FREE ( principal_string ) ;
if ( ret ! = 0 ) {
return ret ;
}
num_comp = krb5_princ_size ( context , fallback_principal ) ;
2018-11-20 19:45:11 +03:00
fallback_realm = smb_krb5_principal_get_realm (
mem_ctx , context , fallback_principal ) ;
2015-03-23 13:00:51 +03:00
if ( fallback_realm = = NULL ) {
krb5_free_principal ( context , fallback_principal ) ;
return ENOMEM ;
}
if ( num_comp = = 1 ) {
size_t len ;
2023-09-21 01:41:05 +03:00
ret = smb_krb5_principal_get_comp_string ( mem_ctx ,
context , fallback_principal , 0 , & fallback_account ) ;
if ( ret ) {
2015-03-23 13:00:51 +03:00
krb5_free_principal ( context , fallback_principal ) ;
2018-11-20 19:45:11 +03:00
TALLOC_FREE ( fallback_realm ) ;
2023-09-21 01:41:05 +03:00
return ret ;
2015-03-23 13:00:51 +03:00
}
len = strlen ( fallback_account ) ;
if ( len > = 2 & & fallback_account [ len - 1 ] = = ' $ ' ) {
TALLOC_FREE ( fallback_account ) ;
}
}
krb5_free_principal ( context , fallback_principal ) ;
fallback_principal = NULL ;
if ( fallback_account ! = NULL ) {
char * with_dollar ;
with_dollar = talloc_asprintf ( mem_ctx , " %s$ " ,
fallback_account ) ;
if ( with_dollar = = NULL ) {
2018-11-20 19:45:11 +03:00
TALLOC_FREE ( fallback_realm ) ;
2015-03-23 13:00:51 +03:00
return ENOMEM ;
}
TALLOC_FREE ( fallback_account ) ;
ret = smb_krb5_make_principal ( context ,
& fallback_principal ,
fallback_realm ,
with_dollar , NULL ) ;
TALLOC_FREE ( with_dollar ) ;
if ( ret ! = 0 ) {
2018-11-20 19:45:11 +03:00
TALLOC_FREE ( fallback_realm ) ;
2015-03-23 13:00:51 +03:00
return ret ;
}
}
2018-11-20 19:45:11 +03:00
TALLOC_FREE ( fallback_realm ) ;
2015-03-23 13:00:51 +03:00
if ( fallback_principal ! = NULL ) {
char * fallback_string = NULL ;
ret = krb5_unparse_name ( context ,
fallback_principal ,
& fallback_string ) ;
if ( ret ! = 0 ) {
krb5_free_principal ( context , fallback_principal ) ;
return ret ;
}
nt_status = sam_get_results_principal ( kdc_db_ctx - > samdb ,
mem_ctx ,
fallback_string ,
2024-04-15 02:42:50 +03:00
attrs , dsdb_flags ,
2015-03-23 13:00:51 +03:00
realm_dn , msg ) ;
SAFE_FREE ( fallback_string ) ;
}
krb5_free_principal ( context , fallback_principal ) ;
fallback_principal = NULL ;
2010-01-28 09:27:11 +03:00
}
2015-03-23 13:00:51 +03:00
TALLOC_FREE ( principal_string ) ;
2010-01-28 09:27:11 +03:00
if ( NT_STATUS_EQUAL ( nt_status , NT_STATUS_NO_SUCH_USER ) ) {
2014-05-08 19:13:04 +04:00
return SDB_ERR_NOENTRY ;
2010-01-28 09:27:11 +03:00
} else if ( NT_STATUS_EQUAL ( nt_status , NT_STATUS_NO_MEMORY ) ) {
return ENOMEM ;
} else if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
return EINVAL ;
}
2014-12-17 07:02:53 +03:00
return 0 ;
2010-01-28 09:27:11 +03:00
}
2024-05-21 02:14:50 +03:00
/* This is for the reset UF_SMARTCARD_REQUIRED password, but only in the expired case */
static void smartcard_random_pw_update ( TALLOC_CTX * mem_ctx ,
struct ldb_context * ldb ,
struct ldb_dn * dn )
{
int ret ;
NTSTATUS status = NT_STATUS_OK ;
/*
* The password_hash module expects these passwords to be
* null ‐ terminated , so we zero - initialise with { }
*/
uint8_t new_password [ 128 ] = { } ;
DATA_BLOB password_blob = { . data = new_password ,
. length = sizeof ( new_password ) } ;
/*
* This will be re - randomised in password_hash , but want this
* to be random in a failure case
*/
generate_random_buffer ( new_password , sizeof ( new_password ) - 2 ) ;
ret = ldb_transaction_start ( ldb ) ;
if ( ret ! = LDB_SUCCESS ) {
DBG_ERR ( " Transaction start for automated "
" password rotation "
" of soon-to-expire "
" underlying password on account %s with "
" UF_SMARTCARD_REQUIRED failed: %s \n " ,
ldb_dn_get_linearized ( dn ) ,
ldb_errstring ( ldb ) ) ;
return ;
}
status = samdb_set_password ( ldb ,
mem_ctx ,
dn ,
& password_blob ,
NULL ,
DSDB_PASSWORD_KDC_RESET_SMARTCARD_ACCOUNT_PASSWORD ,
NULL , NULL ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
ldb_transaction_cancel ( ldb ) ;
DBG_ERR ( " Automated password rotation "
" of soon-to-expire "
" underlying password on account %s with "
" UF_SMARTCARD_REQUIRED failed: %s \n " ,
ldb_dn_get_linearized ( dn ) ,
nt_errstr ( status ) ) ;
return ;
}
ret = ldb_transaction_commit ( ldb ) ;
if ( ret ! = LDB_SUCCESS ) {
DBG_ERR ( " Transaction commit for automated "
" password rotation "
" of soon-to-expire "
" underlying password on account %s with "
" UF_SMARTCARD_REQUIRED failed: %s \n " ,
ldb_dn_get_linearized ( dn ) ,
ldb_errstring ( ldb ) ) ;
}
}
2010-01-28 09:27:11 +03:00
static krb5_error_code samba_kdc_fetch_client ( krb5_context context ,
struct samba_kdc_db_context * kdc_db_ctx ,
TALLOC_CTX * mem_ctx ,
krb5_const_principal principal ,
2010-11-16 06:07:18 +03:00
unsigned flags ,
2019-07-19 14:22:48 +03:00
krb5_kvno kvno ,
2022-03-23 05:43:25 +03:00
struct sdb_entry * entry )
2022-03-23 05:36:31 +03:00
{
2010-01-28 09:27:11 +03:00
struct ldb_dn * realm_dn ;
krb5_error_code ret ;
struct ldb_message * msg = NULL ;
2024-05-21 02:14:50 +03:00
int tries = 0 ;
2024-05-09 07:24:31 +03:00
NTTIME pwd_last_set_last_loop = INT64_MAX ;
bool pwd_last_set_last_loop_set = false ;
2010-01-28 09:27:11 +03:00
2024-05-21 02:14:50 +03:00
/*
* We will try up to 3 times to rotate the expired or soon to
* expire password of a UF_SMARTCARD_REQUIRED account ,
* re - starting the search if we attempted a password change
* ( allowing the new secrets and expiry to be used ) .
*
* A failure to change the password is not fatal , as password
* changes are attempted before the ultimate expiry . This way
* the server will still process an AS - REQ with PKINIT until
* it ( later , in the KDC code ) finds the password has actually
* expired .
*/
while ( tries + + < = 2 ) {
2024-05-09 07:24:31 +03:00
NTTIME pwd_last_set_this_loop ;
2024-05-21 02:14:50 +03:00
uint32_t attr_flags_computed ;
/*
* When we look up the client , we also pre - rotate any expired
* passwords in the UF_SMARTCARD_REQUIRED case
*/
ret = samba_kdc_lookup_client ( context , kdc_db_ctx ,
mem_ctx , principal , user_attrs , DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS ,
& realm_dn , & msg ) ;
if ( ret ! = 0 ) {
return ret ;
}
ret = samba_kdc_message2entry ( context , kdc_db_ctx , mem_ctx ,
principal , SAMBA_KDC_ENT_TYPE_CLIENT ,
flags , kvno ,
realm_dn , msg , entry ) ;
if ( ret ! = 0 ) {
return ret ;
}
if ( ! ( flags & SDB_F_FOR_AS_REQ ) ) {
break ;
}
/* This is the check on UF_SMARTCARD_REQUIRED */
if ( ! ( entry - > flags . require_hwauth ) ) {
break ;
}
/*
* This check is also the configuration gate : the
* operational module will set a
* msDS - UserPasswordExpiryTimeComputed that in turn is
* represented here as NULL unless the
* expiry / auto - rotation of UF_SMARTCARD_REQUIRED
* accounts is enabled
*/
if ( entry - > pw_end = = NULL ) {
break ;
}
2024-05-09 07:24:31 +03:00
/*
* Find if the pwdLastSet has changed on an account
* that we are about to change the password for . If
* we have both seen it and it has changed already , go
* with that , even if it would fail the tests . As
* well as dealing with races , this will avoid a
* double - reset every loop if the TGT lifetime is
* longer than the expiry .
*/
pwd_last_set_this_loop =
ldb_msg_find_attr_as_int64 ( msg , " pwdLastSet " , INT64_MAX ) ;
if ( pwd_last_set_last_loop_set & &
pwd_last_set_last_loop ! = pwd_last_set_this_loop ) {
break ;
}
pwd_last_set_last_loop = pwd_last_set_this_loop ;
pwd_last_set_last_loop_set = true ;
2024-05-21 02:14:50 +03:00
attr_flags_computed
= ldb_msg_find_attr_as_uint ( msg ,
" msDS-User-Account-Control-Computed " ,
UF_PASSWORD_EXPIRED /* A safe if chaotic default */ ) ;
if ( attr_flags_computed & UF_PASSWORD_EXPIRED ) {
/* Already expired, keep processing */
} else {
/*
* Will expire soon , but not already expired .
*
* However we must first
* check if this is before the TGT is due to
* expire .
2024-05-17 08:34:36 +03:00
*
* Then we check if we are half - way
* though the password lifetime before we make
* a password rotation .
2024-05-21 02:14:50 +03:00
*/
NTTIME must_change_time
= samdb_result_nttime ( msg ,
" msDS-UserPasswordExpiryTimeComputed " ,
0 ) ;
2024-05-17 08:34:36 +03:00
NTTIME pw_lifetime = must_change_time - pwd_last_set_this_loop ;
NTTIME pw_halflife = pw_lifetime / 2 ;
2024-05-21 02:14:50 +03:00
if ( must_change_time
> entry - > skdc_entry - > enforced_tgt_lifetime_nt_ticks + entry - > skdc_entry - > current_nttime ) {
/* Password will not expire before TGT will */
break ;
}
2024-05-17 08:34:36 +03:00
if ( pwd_last_set_this_loop ! = 0
& & pwd_last_set_this_loop + pw_halflife > entry - > skdc_entry - > current_nttime ) {
/*
* Still in first half of password
* lifetime , no change per
* https : //lists.samba.org/archive/cifs-protocol/2024-May/004316.html
*/
break ;
}
2024-05-21 02:14:50 +03:00
/* Keep processing */
}
if ( kdc_db_ctx - > rodc ) {
/*
* Nothing we can do locally on an RODC . So
* we trigger pushing the user back to the
* full DC to ensure the PW is rotated .
*/
ret = SDB_ERR_NOT_FOUND_HERE ;
break ;
}
/*
* Reset PW to random value . All we can do is loop
* and hope we succeed again on failure , if we succeed
* then we will pass the tests above and break out of the loop
*
* We don ' t want to fail on error here as we might
* still be able to provide service to the client if
* the password is not yet actually expired . They may get
* better luck at another KDC or at a later AS - REQ .
*/
smartcard_random_pw_update ( mem_ctx , kdc_db_ctx - > samdb , entry - > skdc_entry - > msg - > dn ) ;
2010-01-28 09:27:11 +03:00
}
return ret ;
2024-05-21 02:14:50 +03:00
2010-01-28 09:27:11 +03:00
}
static krb5_error_code samba_kdc_fetch_krbtgt ( krb5_context context ,
2010-09-28 07:13:28 +04:00
struct samba_kdc_db_context * kdc_db_ctx ,
TALLOC_CTX * mem_ctx ,
krb5_const_principal principal ,
2010-11-16 06:07:18 +03:00
unsigned flags ,
2011-09-30 00:47:08 +04:00
uint32_t kvno ,
2022-03-23 05:43:25 +03:00
struct sdb_entry * entry )
2010-01-28 09:27:11 +03:00
{
2023-09-05 05:23:10 +03:00
TALLOC_CTX * tmp_ctx = NULL ;
2010-01-28 09:27:11 +03:00
struct loadparm_context * lp_ctx = kdc_db_ctx - > lp_ctx ;
2023-09-05 05:23:10 +03:00
krb5_error_code ret = 0 ;
2023-09-21 02:22:47 +03:00
int is_krbtgt ;
2010-01-28 09:27:11 +03:00
struct ldb_message * msg = NULL ;
struct ldb_dn * realm_dn = ldb_get_default_basedn ( kdc_db_ctx - > samdb ) ;
2018-11-20 19:45:11 +03:00
char * realm_from_princ ;
2023-09-05 05:23:10 +03:00
char * realm_princ_comp = NULL ;
tmp_ctx = talloc_new ( mem_ctx ) ;
if ( tmp_ctx = = NULL ) {
ret = ENOMEM ;
goto out ;
}
2014-05-08 12:10:49 +04:00
2018-11-20 19:45:11 +03:00
realm_from_princ = smb_krb5_principal_get_realm (
2023-09-05 05:23:10 +03:00
tmp_ctx , context , principal ) ;
2014-05-08 12:10:49 +04:00
if ( realm_from_princ = = NULL ) {
2018-11-20 19:45:11 +03:00
/* can't happen */
2023-09-05 05:23:10 +03:00
ret = SDB_ERR_NOENTRY ;
goto out ;
2014-05-08 12:10:49 +04:00
}
2023-09-21 02:22:47 +03:00
is_krbtgt = smb_krb5_principal_is_tgs ( context , principal ) ;
if ( is_krbtgt = = - 1 ) {
ret = ENOMEM ;
goto out ;
} else if ( ! is_krbtgt ) {
2010-01-28 09:27:11 +03:00
/* Not a krbtgt */
2023-09-05 05:23:10 +03:00
ret = SDB_ERR_NOENTRY ;
goto out ;
2010-01-28 09:27:11 +03:00
}
/* krbtgt case. Either us or a trusted realm */
2023-09-21 01:41:05 +03:00
ret = smb_krb5_principal_get_comp_string ( tmp_ctx , context , principal , 1 , & realm_princ_comp ) ;
if ( ret = = ENOENT ) {
/* OK. */
} else if ( ret ) {
goto out ;
}
2023-09-05 05:23:10 +03:00
2014-05-08 12:10:49 +04:00
if ( lpcfg_is_my_domain_or_realm ( lp_ctx , realm_from_princ )
2023-09-21 02:14:36 +03:00
& & ( realm_princ_comp = = NULL | | lpcfg_is_my_domain_or_realm ( lp_ctx , realm_princ_comp ) ) ) {
2010-09-28 07:13:28 +04:00
/* us, or someone quite like us */
2023-09-04 07:52:57 +03:00
/* Kludge, kludge, kludge. If the realm part of krbtgt/realm,
2010-01-28 09:27:11 +03:00
* is in our db , then direct the caller at our primary
* krbtgt */
int lret ;
2011-09-30 00:47:08 +04:00
unsigned int krbtgt_number ;
/* w2k8r2 sometimes gives us a kvno of 255 for inter-domain
trust tickets . We don ' t yet know what this means , but we do
seem to need to treat it as unspecified */
2023-10-17 10:18:28 +03:00
if ( flags & ( SDB_F_KVNO_SPECIFIED | SDB_F_RODC_NUMBER_SPECIFIED ) ) {
2011-09-30 00:47:08 +04:00
krbtgt_number = SAMBA_KVNO_GET_KRBTGT ( kvno ) ;
if ( kdc_db_ctx - > rodc ) {
if ( krbtgt_number ! = kdc_db_ctx - > my_krbtgt_number ) {
2023-09-05 05:23:10 +03:00
ret = SDB_ERR_NOT_FOUND_HERE ;
goto out ;
2011-09-30 00:47:08 +04:00
}
}
} else {
krbtgt_number = kdc_db_ctx - > my_krbtgt_number ;
}
2010-01-28 09:27:11 +03:00
2010-09-28 07:13:28 +04:00
if ( krbtgt_number = = kdc_db_ctx - > my_krbtgt_number ) {
2023-09-05 05:23:10 +03:00
lret = dsdb_search_one ( kdc_db_ctx - > samdb , tmp_ctx ,
2010-09-28 07:13:28 +04:00
& msg , kdc_db_ctx - > krbtgt_dn , LDB_SCOPE_BASE ,
2024-04-15 02:42:50 +03:00
krbtgt_attrs , DSDB_SEARCH_NO_GLOBAL_CATALOG | DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS ,
2010-09-28 07:13:28 +04:00
" (objectClass=user) " ) ;
} else {
/* We need to look up an RODC krbtgt (perhaps
* ours , if we are an RODC , perhaps another
* RODC if we are a read - write DC */
2023-09-05 05:23:10 +03:00
lret = dsdb_search_one ( kdc_db_ctx - > samdb , tmp_ctx ,
2010-09-28 07:13:28 +04:00
& msg , realm_dn , LDB_SCOPE_SUBTREE ,
krbtgt_attrs ,
2024-04-15 02:42:50 +03:00
DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG | DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS ,
2010-09-28 07:13:28 +04:00
" (&(objectClass=user)(msDS-SecondaryKrbTgtNumber=%u)) " , ( unsigned ) ( krbtgt_number ) ) ;
}
2010-01-28 09:27:11 +03:00
if ( lret = = LDB_ERR_NO_SUCH_OBJECT ) {
2023-05-09 07:28:22 +03:00
krb5_warnx ( context , " samba_kdc_fetch_krbtgt: could not find KRBTGT number %u in DB! " ,
2010-09-28 07:13:28 +04:00
( unsigned ) ( krbtgt_number ) ) ;
2014-05-08 19:13:04 +04:00
krb5_set_error_message ( context , SDB_ERR_NOENTRY ,
2023-05-09 07:28:22 +03:00
" samba_kdc_fetch_krbtgt: could not find KRBTGT number %u in DB! " ,
2010-09-28 07:13:28 +04:00
( unsigned ) ( krbtgt_number ) ) ;
2023-09-05 05:23:10 +03:00
ret = SDB_ERR_NOENTRY ;
goto out ;
2010-01-28 09:27:11 +03:00
} else if ( lret ! = LDB_SUCCESS ) {
2023-05-09 07:28:22 +03:00
krb5_warnx ( context , " samba_kdc_fetch_krbtgt: could not find KRBTGT number %u in DB! " ,
2010-09-28 07:13:28 +04:00
( unsigned ) ( krbtgt_number ) ) ;
2014-05-08 19:13:04 +04:00
krb5_set_error_message ( context , SDB_ERR_NOENTRY ,
2023-05-09 07:28:22 +03:00
" samba_kdc_fetch_krbtgt: could not find KRBTGT number %u in DB! " ,
2010-09-28 07:13:28 +04:00
( unsigned ) ( krbtgt_number ) ) ;
2023-09-05 05:23:10 +03:00
ret = SDB_ERR_NOENTRY ;
goto out ;
2010-01-28 09:27:11 +03:00
}
ret = samba_kdc_message2entry ( context , kdc_db_ctx , mem_ctx ,
2010-11-16 06:12:17 +03:00
principal , SAMBA_KDC_ENT_TYPE_KRBTGT ,
2019-07-19 14:22:48 +03:00
flags , kvno , realm_dn , msg , entry ) ;
2010-01-28 09:27:11 +03:00
if ( ret ! = 0 ) {
2023-05-09 07:28:22 +03:00
krb5_warnx ( context , " samba_kdc_fetch_krbtgt: self krbtgt message2entry failed " ) ;
2010-01-28 09:27:11 +03:00
}
} else {
enum trust_direction direction = UNKNOWN ;
2010-09-16 11:20:08 +04:00
const char * realm = NULL ;
2010-01-28 09:27:11 +03:00
/* Either an inbound or outbound trust */
2014-05-08 12:10:49 +04:00
if ( strcasecmp ( lpcfg_realm ( lp_ctx ) , realm_from_princ ) = = 0 ) {
2010-01-28 09:27:11 +03:00
/* look for inbound trust */
direction = INBOUND ;
2014-05-16 13:44:49 +04:00
realm = realm_princ_comp ;
2010-09-16 11:20:08 +04:00
} else {
2023-09-21 02:37:30 +03:00
bool eq = false ;
2023-09-21 02:22:51 +03:00
2023-09-21 02:37:30 +03:00
ret = is_principal_component_equal_ignoring_case ( context , principal , 1 , lpcfg_realm ( lp_ctx ) , & eq ) ;
2023-09-21 02:22:51 +03:00
if ( ret ) {
goto out ;
}
2023-09-21 02:37:30 +03:00
if ( eq ) {
2023-09-21 02:22:51 +03:00
/* look for outbound trust */
direction = OUTBOUND ;
realm = realm_from_princ ;
} else {
krb5_warnx ( context , " samba_kdc_fetch_krbtgt: not our realm for trusts ('%s', '%s') " ,
realm_from_princ ,
realm_princ_comp ) ;
krb5_set_error_message ( context , SDB_ERR_NOENTRY , " samba_kdc_fetch_krbtgt: not our realm for trusts ('%s', '%s') " ,
realm_from_princ ,
realm_princ_comp ) ;
ret = SDB_ERR_NOENTRY ;
goto out ;
}
2010-01-28 09:27:11 +03:00
}
/* Trusted domains are under CN=system */
ret = samba_kdc_lookup_trust ( context , kdc_db_ctx - > samdb ,
2023-09-05 05:23:10 +03:00
tmp_ctx ,
2010-01-28 09:27:11 +03:00
realm , realm_dn , & msg ) ;
if ( ret ! = 0 ) {
2023-05-09 07:28:22 +03:00
krb5_warnx ( context , " samba_kdc_fetch_krbtgt: could not find principal in DB " ) ;
krb5_set_error_message ( context , ret , " samba_kdc_fetch_krbtgt: could not find principal in DB " ) ;
2023-09-05 05:23:10 +03:00
goto out ;
2010-01-28 09:27:11 +03:00
}
ret = samba_kdc_trust_message2entry ( context , kdc_db_ctx , mem_ctx ,
2022-02-03 16:12:02 +03:00
direction ,
2022-03-23 05:36:31 +03:00
realm_dn , flags , kvno , msg , entry ) ;
2010-01-28 09:27:11 +03:00
if ( ret ! = 0 ) {
2023-05-09 07:28:22 +03:00
krb5_warnx ( context , " samba_kdc_fetch_krbtgt: trust_message2entry failed for %s " ,
2014-12-15 18:48:27 +03:00
ldb_dn_get_linearized ( msg - > dn ) ) ;
2023-05-09 07:28:22 +03:00
krb5_set_error_message ( context , ret , " samba_kdc_fetch_krbtgt: "
2014-12-15 18:48:27 +03:00
" trust_message2entry failed for %s " ,
ldb_dn_get_linearized ( msg - > dn ) ) ;
2010-01-28 09:27:11 +03:00
}
}
2023-09-05 05:23:10 +03:00
out :
talloc_free ( tmp_ctx ) ;
return ret ;
2010-01-28 09:27:11 +03:00
}
static krb5_error_code samba_kdc_lookup_server ( krb5_context context ,
2015-01-30 02:31:29 +03:00
struct samba_kdc_db_context * kdc_db_ctx ,
TALLOC_CTX * mem_ctx ,
krb5_const_principal principal ,
unsigned flags ,
struct ldb_dn * * realm_dn ,
struct ldb_message * * msg )
2010-01-28 09:27:11 +03:00
{
krb5_error_code ret ;
2015-01-30 02:31:29 +03:00
if ( ( smb_krb5_principal_get_type ( context , principal ) ! = KRB5_NT_ENTERPRISE_PRINCIPAL )
& & krb5_princ_size ( context , principal ) > = 2 ) {
2010-01-28 09:27:11 +03:00
/* 'normal server' case */
int ldb_ret ;
NTSTATUS nt_status ;
struct ldb_dn * user_dn ;
char * principal_string ;
ret = krb5_unparse_name_flags ( context , principal ,
KRB5_PRINCIPAL_UNPARSE_NO_REALM ,
& principal_string ) ;
if ( ret ! = 0 ) {
return ret ;
}
/* At this point we may find the host is known to be
* in a different realm , so we should generate a
* referral instead */
nt_status = crack_service_principal_name ( kdc_db_ctx - > samdb ,
mem_ctx , principal_string ,
& user_dn , realm_dn ) ;
free ( principal_string ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2014-05-08 19:13:04 +04:00
return SDB_ERR_NOENTRY ;
2010-01-28 09:27:11 +03:00
}
2010-02-16 07:49:29 +03:00
ldb_ret = dsdb_search_one ( kdc_db_ctx - > samdb ,
mem_ctx ,
msg , user_dn , LDB_SCOPE_BASE ,
2024-01-17 03:13:56 +03:00
server_attrs ,
2024-04-15 02:42:50 +03:00
DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG | DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS ,
2011-09-22 03:57:26 +04:00
" (objectClass=*) " ) ;
2010-01-28 09:27:11 +03:00
if ( ldb_ret ! = LDB_SUCCESS ) {
2014-05-08 19:13:04 +04:00
return SDB_ERR_NOENTRY ;
2010-01-28 09:27:11 +03:00
}
2015-01-30 02:31:29 +03:00
return 0 ;
2014-05-08 19:13:04 +04:00
} else if ( ! ( flags & SDB_F_FOR_AS_REQ )
2015-01-30 02:31:29 +03:00
& & smb_krb5_principal_get_type ( context , principal ) = = KRB5_NT_ENTERPRISE_PRINCIPAL ) {
/*
* The behaviour of accepting an
* KRB5_NT_ENTERPRISE_PRINCIPAL server principal
* containing a UPN only applies to TGS - REQ packets ,
* not AS - REQ packets .
*/
return samba_kdc_lookup_client ( context , kdc_db_ctx ,
2024-04-15 02:42:50 +03:00
mem_ctx , principal , server_attrs , DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS ,
2015-01-30 02:31:29 +03:00
realm_dn , msg ) ;
2010-01-28 09:27:11 +03:00
} else {
2015-01-30 02:31:29 +03:00
/*
* This case is for :
* - the AS - REQ , where we only accept
* samAccountName based lookups for the server , no
* matter if the name is an
* KRB5_NT_ENTERPRISE_PRINCIPAL or not
* - for the TGS - REQ when we are not given an
* KRB5_NT_ENTERPRISE_PRINCIPAL , which also must
* only lookup samAccountName based names .
*/
2010-01-28 09:27:11 +03:00
int lret ;
char * short_princ ;
2015-06-11 21:04:09 +03:00
krb5_principal enterprise_principal = NULL ;
krb5_const_principal used_principal = NULL ;
2015-03-23 13:00:51 +03:00
char * name1 = NULL ;
size_t len1 = 0 ;
char * filter = NULL ;
2015-01-30 02:31:29 +03:00
if ( smb_krb5_principal_get_type ( context , principal ) = = KRB5_NT_ENTERPRISE_PRINCIPAL ) {
2015-02-10 15:23:14 +03:00
char * str = NULL ;
2015-01-30 02:31:29 +03:00
/* Need to reparse the enterprise principal to find the real target */
2014-05-08 12:25:07 +04:00
if ( krb5_princ_size ( context , principal ) ! = 1 ) {
2015-01-30 02:31:29 +03:00
ret = KRB5_PARSE_MALFORMED ;
krb5_set_error_message ( context , ret , " samba_kdc_lookup_server: request for an "
" enterprise principal with wrong (%d) number of components " ,
2014-05-08 12:25:07 +04:00
krb5_princ_size ( context , principal ) ) ;
2015-01-30 02:31:29 +03:00
return ret ;
}
2023-09-21 01:41:05 +03:00
ret = smb_krb5_principal_get_comp_string ( mem_ctx , context , principal , 0 , & str ) ;
if ( ret ) {
2015-02-10 15:23:14 +03:00
return KRB5_PARSE_MALFORMED ;
}
ret = krb5_parse_name ( context , str ,
2015-06-11 21:04:09 +03:00
& enterprise_principal ) ;
2015-02-10 15:23:14 +03:00
talloc_free ( str ) ;
2015-01-30 02:31:29 +03:00
if ( ret ) {
return ret ;
}
2015-06-11 21:04:09 +03:00
used_principal = enterprise_principal ;
} else {
used_principal = principal ;
2015-01-30 02:31:29 +03:00
}
2010-01-28 09:27:11 +03:00
/* server as client principal case, but we must not lookup userPrincipalNames */
* realm_dn = ldb_get_default_basedn ( kdc_db_ctx - > samdb ) ;
2011-09-22 03:57:26 +04:00
/* TODO: Check if it is our realm, otherwise give referral */
2010-01-28 09:27:11 +03:00
2015-06-11 21:04:09 +03:00
ret = krb5_unparse_name_flags ( context , used_principal ,
2015-03-12 03:29:56 +03:00
KRB5_PRINCIPAL_UNPARSE_NO_REALM |
KRB5_PRINCIPAL_UNPARSE_DISPLAY ,
& short_princ ) ;
2015-06-11 21:04:09 +03:00
used_principal = NULL ;
krb5_free_principal ( context , enterprise_principal ) ;
enterprise_principal = NULL ;
2010-01-28 09:27:11 +03:00
if ( ret ! = 0 ) {
2023-05-09 07:28:22 +03:00
krb5_set_error_message ( context , ret , " samba_kdc_lookup_server: could not parse principal " ) ;
krb5_warnx ( context , " samba_kdc_lookup_server: could not parse principal " ) ;
2010-01-28 09:27:11 +03:00
return ret ;
}
2015-03-23 13:00:51 +03:00
name1 = ldb_binary_encode_string ( mem_ctx , short_princ ) ;
SAFE_FREE ( short_princ ) ;
if ( name1 = = NULL ) {
return ENOMEM ;
}
len1 = strlen ( name1 ) ;
if ( len1 > = 1 & & name1 [ len1 - 1 ] ! = ' $ ' ) {
filter = talloc_asprintf ( mem_ctx ,
" (&(objectClass=user)(|(samAccountName=%s)(samAccountName=%s$))) " ,
name1 , name1 ) ;
if ( filter = = NULL ) {
return ENOMEM ;
}
} else {
filter = talloc_asprintf ( mem_ctx ,
" (&(objectClass=user)(samAccountName=%s)) " ,
name1 ) ;
if ( filter = = NULL ) {
return ENOMEM ;
}
}
2010-02-16 07:49:29 +03:00
lret = dsdb_search_one ( kdc_db_ctx - > samdb , mem_ctx , msg ,
* realm_dn , LDB_SCOPE_SUBTREE ,
2024-01-17 03:13:56 +03:00
server_attrs ,
2024-04-15 02:42:50 +03:00
DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG | DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS ,
2015-03-23 13:00:51 +03:00
" %s " , filter ) ;
2010-01-28 09:27:11 +03:00
if ( lret = = LDB_ERR_NO_SUCH_OBJECT ) {
2023-07-21 02:44:05 +03:00
DBG_DEBUG ( " Failed to find an entry for %s filter:%s \n " ,
name1 , filter ) ;
2014-05-08 19:13:04 +04:00
return SDB_ERR_NOENTRY ;
2015-03-23 13:00:51 +03:00
}
if ( lret = = LDB_ERR_CONSTRAINT_VIOLATION ) {
2023-07-21 02:44:05 +03:00
DBG_DEBUG ( " Failed to find unique entry for %s filter:%s \n " ,
name1 , filter ) ;
2014-05-08 19:13:04 +04:00
return SDB_ERR_NOENTRY ;
2010-01-28 09:27:11 +03:00
}
if ( lret ! = LDB_SUCCESS ) {
2023-07-21 02:44:05 +03:00
DBG_ERR ( " Failed single search for %s - %s \n " ,
name1 , ldb_errstring ( kdc_db_ctx - > samdb ) ) ;
2014-05-08 19:13:04 +04:00
return SDB_ERR_NOENTRY ;
2010-01-28 09:27:11 +03:00
}
2015-01-30 02:31:29 +03:00
return 0 ;
2010-01-28 09:27:11 +03:00
}
2014-05-08 19:13:04 +04:00
return SDB_ERR_NOENTRY ;
2010-01-28 09:27:11 +03:00
}
2015-01-30 02:31:29 +03:00
2010-01-28 09:27:11 +03:00
static krb5_error_code samba_kdc_fetch_server ( krb5_context context ,
2010-11-16 06:07:18 +03:00
struct samba_kdc_db_context * kdc_db_ctx ,
TALLOC_CTX * mem_ctx ,
krb5_const_principal principal ,
unsigned flags ,
2019-07-19 14:22:48 +03:00
krb5_kvno kvno ,
2022-03-23 05:43:25 +03:00
struct sdb_entry * entry )
2010-01-28 09:27:11 +03:00
{
krb5_error_code ret ;
struct ldb_dn * realm_dn ;
struct ldb_message * msg ;
ret = samba_kdc_lookup_server ( context , kdc_db_ctx , mem_ctx , principal ,
2024-01-17 03:13:56 +03:00
flags , & realm_dn , & msg ) ;
2010-01-28 09:27:11 +03:00
if ( ret ! = 0 ) {
return ret ;
}
ret = samba_kdc_message2entry ( context , kdc_db_ctx , mem_ctx ,
2010-11-16 06:12:17 +03:00
principal , SAMBA_KDC_ENT_TYPE_SERVER ,
2019-07-19 14:22:48 +03:00
flags , kvno ,
2022-03-23 05:36:31 +03:00
realm_dn , msg , entry ) ;
2010-01-28 09:27:11 +03:00
if ( ret ! = 0 ) {
2022-01-18 11:24:44 +03:00
char * client_name = NULL ;
krb5_error_code code ;
code = krb5_unparse_name ( context , principal , & client_name ) ;
if ( code = = 0 ) {
krb5_warnx ( context ,
2023-05-09 07:28:22 +03:00
" samba_kdc_fetch_server: message2entry failed for "
2022-01-18 11:24:44 +03:00
" %s " ,
client_name ) ;
} else {
krb5_warnx ( context ,
2023-05-09 07:28:22 +03:00
" samba_kdc_fetch_server: message2entry and "
2022-01-18 11:24:44 +03:00
" krb5_unparse_name failed " ) ;
}
SAFE_FREE ( client_name ) ;
2010-01-28 09:27:11 +03:00
}
return ret ;
}
2015-02-10 16:43:01 +03:00
static krb5_error_code samba_kdc_lookup_realm ( krb5_context context ,
struct samba_kdc_db_context * kdc_db_ctx ,
krb5_const_principal principal ,
unsigned flags ,
2022-03-23 05:43:25 +03:00
struct sdb_entry * entry )
2015-02-10 16:43:01 +03:00
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
NTSTATUS status ;
krb5_error_code ret ;
bool check_realm = false ;
const char * realm = NULL ;
struct dsdb_trust_routing_table * trt = NULL ;
const struct lsa_TrustDomainInfoInfoEx * tdo = NULL ;
unsigned int num_comp ;
bool ok ;
char * upper = NULL ;
2023-09-07 07:35:39 +03:00
* entry = ( struct sdb_entry ) { } ;
2015-02-10 16:43:01 +03:00
num_comp = krb5_princ_size ( context , principal ) ;
2014-05-08 19:13:04 +04:00
if ( flags & SDB_F_GET_CLIENT ) {
if ( flags & SDB_F_FOR_AS_REQ ) {
2015-02-10 16:43:01 +03:00
check_realm = true ;
}
}
2014-05-08 19:13:04 +04:00
if ( flags & SDB_F_GET_SERVER ) {
if ( flags & SDB_F_FOR_TGS_REQ ) {
2015-02-10 16:43:01 +03:00
check_realm = true ;
}
}
if ( ! check_realm ) {
TALLOC_FREE ( frame ) ;
return 0 ;
}
2018-11-20 19:45:11 +03:00
realm = smb_krb5_principal_get_realm ( frame , context , principal ) ;
if ( realm = = NULL ) {
2015-02-10 16:43:01 +03:00
TALLOC_FREE ( frame ) ;
return ENOMEM ;
}
/*
* The requested realm needs to be our own
*/
2018-11-20 19:45:11 +03:00
ok = lpcfg_is_my_domain_or_realm ( kdc_db_ctx - > lp_ctx , realm ) ;
2015-02-10 16:43:01 +03:00
if ( ! ok ) {
/*
* The request is not for us . . .
*/
TALLOC_FREE ( frame ) ;
2014-05-08 19:13:04 +04:00
return SDB_ERR_NOENTRY ;
2015-02-10 16:43:01 +03:00
}
2015-07-24 18:48:08 +03:00
if ( smb_krb5_principal_get_type ( context , principal ) = = KRB5_NT_ENTERPRISE_PRINCIPAL ) {
2015-02-10 16:43:01 +03:00
char * principal_string = NULL ;
krb5_principal enterprise_principal = NULL ;
char * enterprise_realm = NULL ;
if ( num_comp ! = 1 ) {
TALLOC_FREE ( frame ) ;
2014-05-08 19:13:04 +04:00
return SDB_ERR_NOENTRY ;
2015-02-10 16:43:01 +03:00
}
2023-09-21 01:41:05 +03:00
ret = smb_krb5_principal_get_comp_string ( frame , context ,
principal , 0 , & principal_string ) ;
if ( ret ) {
2015-02-10 16:43:01 +03:00
TALLOC_FREE ( frame ) ;
2023-09-21 01:41:05 +03:00
return ret ;
2015-02-10 16:43:01 +03:00
}
ret = krb5_parse_name ( context , principal_string ,
& enterprise_principal ) ;
TALLOC_FREE ( principal_string ) ;
if ( ret ) {
TALLOC_FREE ( frame ) ;
return ret ;
}
2018-11-20 19:45:11 +03:00
enterprise_realm = smb_krb5_principal_get_realm (
frame , context , enterprise_principal ) ;
2015-02-10 16:43:01 +03:00
krb5_free_principal ( context , enterprise_principal ) ;
if ( enterprise_realm ! = NULL ) {
2018-11-20 19:45:11 +03:00
realm = enterprise_realm ;
2015-02-10 16:43:01 +03:00
}
}
2014-05-08 19:13:04 +04:00
if ( flags & SDB_F_GET_SERVER ) {
2023-09-21 02:37:30 +03:00
bool is_krbtgt = false ;
2023-09-21 02:22:51 +03:00
2023-09-21 02:37:30 +03:00
ret = is_principal_component_equal ( context , principal , 0 , KRB5_TGS_NAME , & is_krbtgt ) ;
2023-09-21 02:22:51 +03:00
if ( ret ) {
TALLOC_FREE ( frame ) ;
return ret ;
}
2023-09-21 02:37:30 +03:00
if ( is_krbtgt ) {
2015-02-10 16:43:01 +03:00
/*
* we need to search krbtgt / locally
*/
TALLOC_FREE ( frame ) ;
return 0 ;
}
/*
* We need to check the last component against the routing table .
*
* Note this works only with 2 or 3 component principals , e . g :
*
* servicePrincipalName : ldap / W2K8R2 - 219. bla . base
* servicePrincipalName : ldap / W2K8R2 - 219. bla . base / bla . base
* servicePrincipalName : ldap / W2K8R2 - 219. bla . base / ForestDnsZones . bla . base
* servicePrincipalName : ldap / W2K8R2 - 219. bla . base / DomainDnsZones . bla . base
*/
if ( num_comp = = 2 | | num_comp = = 3 ) {
2023-09-21 01:41:05 +03:00
char * service_realm = NULL ;
2015-02-10 16:43:01 +03:00
2023-09-21 01:41:05 +03:00
ret = smb_krb5_principal_get_comp_string ( frame ,
context ,
principal ,
num_comp - 1 ,
& service_realm ) ;
if ( ret ) {
TALLOC_FREE ( frame ) ;
return ret ;
} else {
realm = service_realm ;
}
2015-02-10 16:43:01 +03:00
}
}
ok = lpcfg_is_my_domain_or_realm ( kdc_db_ctx - > lp_ctx , realm ) ;
if ( ok ) {
/*
* skip the expensive routing lookup
*/
TALLOC_FREE ( frame ) ;
return 0 ;
}
status = dsdb_trust_routing_table_load ( kdc_db_ctx - > samdb ,
frame , & trt ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( frame ) ;
return EINVAL ;
}
tdo = dsdb_trust_routing_by_name ( trt , realm ) ;
if ( tdo = = NULL ) {
/*
* This principal has to be local
*/
TALLOC_FREE ( frame ) ;
return 0 ;
}
if ( tdo - > trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST ) {
/*
* TODO : handle the routing within the forest
*
* This should likely be handled in
* samba_kdc_message2entry ( ) in case we ' re
* a global catalog . We ' d need to check
* if realm_dn is our own domain and derive
* the dns domain name from realm_dn and check that
* against the routing table or fallback to
* the tdo we found here .
*
* But for now we don ' t support multiple domains
* in our forest correctly anyway .
*
* Just search in our local database .
*/
TALLOC_FREE ( frame ) ;
return 0 ;
}
ret = krb5_copy_principal ( context , principal ,
2022-03-23 05:43:25 +03:00
& entry - > principal ) ;
2015-02-10 16:43:01 +03:00
if ( ret ) {
TALLOC_FREE ( frame ) ;
return ret ;
}
upper = strupper_talloc ( frame , tdo - > domain_name . string ) ;
if ( upper = = NULL ) {
TALLOC_FREE ( frame ) ;
return ENOMEM ;
}
2015-07-24 18:48:08 +03:00
ret = smb_krb5_principal_set_realm ( context ,
2022-03-23 05:43:25 +03:00
entry - > principal ,
2015-07-24 18:48:08 +03:00
upper ) ;
2015-02-10 16:43:01 +03:00
if ( ret ) {
TALLOC_FREE ( frame ) ;
return ret ;
}
TALLOC_FREE ( frame ) ;
2014-05-08 19:13:04 +04:00
return SDB_ERR_WRONG_REALM ;
2015-02-10 16:43:01 +03:00
}
2010-01-28 09:27:11 +03:00
krb5_error_code samba_kdc_fetch ( krb5_context context ,
struct samba_kdc_db_context * kdc_db_ctx ,
krb5_const_principal principal ,
unsigned flags ,
2010-12-04 01:06:53 +03:00
krb5_kvno kvno ,
2022-03-23 05:43:25 +03:00
struct sdb_entry * entry )
2010-01-28 09:27:11 +03:00
{
2014-05-08 19:13:04 +04:00
krb5_error_code ret = SDB_ERR_NOENTRY ;
2010-09-28 07:13:28 +04:00
TALLOC_CTX * mem_ctx ;
2010-01-28 09:27:11 +03:00
2010-09-28 07:13:28 +04:00
mem_ctx = talloc_named ( kdc_db_ctx , 0 , " samba_kdc_fetch context " ) ;
2010-01-28 09:27:11 +03:00
if ( ! mem_ctx ) {
ret = ENOMEM ;
krb5_set_error_message ( context , ret , " samba_kdc_fetch: talloc_named() failed! " ) ;
return ret ;
}
2023-09-27 05:58:55 +03:00
ret = samba_kdc_lookup_realm ( context , kdc_db_ctx ,
2022-03-23 05:43:25 +03:00
principal , flags , entry ) ;
2015-02-10 16:43:01 +03:00
if ( ret ! = 0 ) {
goto done ;
}
2014-05-08 19:13:04 +04:00
ret = SDB_ERR_NOENTRY ;
2015-02-10 16:43:01 +03:00
2014-05-08 19:13:04 +04:00
if ( flags & SDB_F_GET_CLIENT ) {
2019-07-19 14:22:48 +03:00
ret = samba_kdc_fetch_client ( context , kdc_db_ctx , mem_ctx , principal , flags , kvno , entry ) ;
2014-05-08 19:13:04 +04:00
if ( ret ! = SDB_ERR_NOENTRY ) goto done ;
2010-01-28 09:27:11 +03:00
}
2014-05-08 19:13:04 +04:00
if ( flags & SDB_F_GET_SERVER ) {
2010-01-28 09:27:11 +03:00
/* krbtgt fits into this situation for trusted realms, and for resolving different versions of our own realm name */
2022-03-23 05:43:25 +03:00
ret = samba_kdc_fetch_krbtgt ( context , kdc_db_ctx , mem_ctx , principal , flags , kvno , entry ) ;
2014-05-08 19:13:04 +04:00
if ( ret ! = SDB_ERR_NOENTRY ) goto done ;
2010-01-28 09:27:11 +03:00
/* We return 'no entry' if it does not start with krbtgt/, so move to the common case quickly */
2019-07-19 14:22:48 +03:00
ret = samba_kdc_fetch_server ( context , kdc_db_ctx , mem_ctx , principal , flags , kvno , entry ) ;
2014-05-08 19:13:04 +04:00
if ( ret ! = SDB_ERR_NOENTRY ) goto done ;
2010-01-28 09:27:11 +03:00
}
2014-05-08 19:13:04 +04:00
if ( flags & SDB_F_GET_KRBTGT ) {
2022-03-23 05:43:25 +03:00
ret = samba_kdc_fetch_krbtgt ( context , kdc_db_ctx , mem_ctx , principal , flags , kvno , entry ) ;
2014-05-08 19:13:04 +04:00
if ( ret ! = SDB_ERR_NOENTRY ) goto done ;
2010-01-28 09:27:11 +03:00
}
done :
talloc_free ( mem_ctx ) ;
return ret ;
}
struct samba_kdc_seq {
2010-04-12 01:17:15 +04:00
unsigned int index ;
unsigned int count ;
2010-01-28 09:27:11 +03:00
struct ldb_message * * msgs ;
2022-02-03 16:14:06 +03:00
enum trust_direction trust_direction ;
unsigned int trust_index ;
unsigned int trust_count ;
struct ldb_message * * trust_msgs ;
2010-01-28 09:27:11 +03:00
struct ldb_dn * realm_dn ;
} ;
static krb5_error_code samba_kdc_seq ( krb5_context context ,
struct samba_kdc_db_context * kdc_db_ctx ,
2024-04-24 04:45:08 +03:00
const unsigned sdb_flags ,
2022-03-23 05:43:25 +03:00
struct sdb_entry * entry )
2010-01-28 09:27:11 +03:00
{
krb5_error_code ret ;
struct samba_kdc_seq * priv = kdc_db_ctx - > seq_ctx ;
2015-03-20 17:29:30 +03:00
const char * realm = lpcfg_realm ( kdc_db_ctx - > lp_ctx ) ;
struct ldb_message * msg = NULL ;
const char * sAMAccountName = NULL ;
krb5_principal principal = NULL ;
2010-01-28 09:27:11 +03:00
TALLOC_CTX * mem_ctx ;
if ( ! priv ) {
2014-05-08 19:13:04 +04:00
return SDB_ERR_NOENTRY ;
2010-01-28 09:27:11 +03:00
}
mem_ctx = talloc_named ( priv , 0 , " samba_kdc_seq context " ) ;
if ( ! mem_ctx ) {
ret = ENOMEM ;
krb5_set_error_message ( context , ret , " samba_kdc_seq: talloc_named() failed! " ) ;
2023-08-10 06:54:21 +03:00
goto out ;
2010-01-28 09:27:11 +03:00
}
2022-02-03 16:14:06 +03:00
if ( priv - > index = = priv - > count ) {
goto trusts ;
}
2015-03-20 17:29:30 +03:00
while ( priv - > index < priv - > count ) {
msg = priv - > msgs [ priv - > index + + ] ;
sAMAccountName = ldb_msg_find_attr_as_string ( msg , " sAMAccountName " , NULL ) ;
if ( sAMAccountName ! = NULL ) {
break ;
}
}
if ( sAMAccountName = = NULL ) {
2022-02-03 16:14:06 +03:00
/*
* This is not really possible ,
* but instead returning
* SDB_ERR_NOENTRY , we
* go on with trusts
*/
goto trusts ;
2015-03-20 17:29:30 +03:00
}
ret = smb_krb5_make_principal ( context , & principal ,
realm , sAMAccountName , NULL ) ;
if ( ret ! = 0 ) {
goto out ;
}
ret = samba_kdc_message2entry ( context , kdc_db_ctx , mem_ctx ,
principal , SAMBA_KDC_ENT_TYPE_ANY ,
2024-04-24 04:45:08 +03:00
sdb_flags | SDB_F_GET_ANY ,
2019-07-19 14:22:48 +03:00
0 /* kvno */ ,
2015-03-20 17:29:30 +03:00
priv - > realm_dn , msg , entry ) ;
2023-08-10 06:55:53 +03:00
krb5_free_principal ( context , principal ) ;
2015-03-20 17:29:30 +03:00
out :
2010-01-28 09:27:11 +03:00
if ( ret ! = 0 ) {
2010-04-17 22:08:15 +04:00
TALLOC_FREE ( priv ) ;
2010-01-28 09:27:11 +03:00
kdc_db_ctx - > seq_ctx = NULL ;
} else {
talloc_free ( mem_ctx ) ;
}
return ret ;
2022-02-03 16:14:06 +03:00
trusts :
while ( priv - > trust_index < priv - > trust_count ) {
enum trust_direction trust_direction = priv - > trust_direction ;
msg = priv - > trust_msgs [ priv - > trust_index ] ;
if ( trust_direction = = INBOUND ) {
/*
* This time we try INBOUND keys ,
* next time we ' ll do OUTBOUND
* for the same trust .
*/
priv - > trust_direction = OUTBOUND ;
/*
* samba_kdc_trust_message2entry ( )
* will likely steal msg from us ,
* so we need to make a copy for
* the first run with INBOUND ,
* and let it steal without
* a copy in the OUTBOUND run .
*/
msg = ldb_msg_copy ( priv - > trust_msgs , msg ) ;
if ( msg = = NULL ) {
return ENOMEM ;
}
} else {
/*
* This time we try OUTBOUND keys ,
* next time we ' ll do INBOUND for
* the next trust .
*/
priv - > trust_direction = INBOUND ;
priv - > trust_index + + ;
}
ret = samba_kdc_trust_message2entry ( context ,
kdc_db_ctx ,
mem_ctx ,
trust_direction ,
priv - > realm_dn ,
2024-04-24 04:45:08 +03:00
sdb_flags | SDB_F_GET_ANY ,
2022-02-03 16:14:06 +03:00
0 , /* kvno */
msg ,
entry ) ;
if ( ret = = SDB_ERR_NOENTRY ) {
continue ;
}
goto out ;
}
ret = SDB_ERR_NOENTRY ;
goto out ;
2010-01-28 09:27:11 +03:00
}
krb5_error_code samba_kdc_firstkey ( krb5_context context ,
struct samba_kdc_db_context * kdc_db_ctx ,
2024-04-24 04:45:08 +03:00
const unsigned sdb_flags ,
2022-03-23 05:43:25 +03:00
struct sdb_entry * entry )
2010-01-28 09:27:11 +03:00
{
struct ldb_context * ldb_ctx = kdc_db_ctx - > samdb ;
struct samba_kdc_seq * priv = kdc_db_ctx - > seq_ctx ;
char * realm ;
struct ldb_result * res = NULL ;
krb5_error_code ret ;
int lret ;
2022-02-03 16:14:06 +03:00
NTSTATUS status ;
2010-01-28 09:27:11 +03:00
if ( priv ) {
2010-04-17 22:08:15 +04:00
TALLOC_FREE ( priv ) ;
2010-01-28 09:27:11 +03:00
kdc_db_ctx - > seq_ctx = NULL ;
}
2022-02-03 16:14:06 +03:00
priv = talloc_zero ( kdc_db_ctx , struct samba_kdc_seq ) ;
2010-01-28 09:27:11 +03:00
if ( ! priv ) {
ret = ENOMEM ;
krb5_set_error_message ( context , ret , " talloc: out of memory " ) ;
return ret ;
}
priv - > realm_dn = ldb_get_default_basedn ( ldb_ctx ) ;
ret = krb5_get_default_realm ( context , & realm ) ;
if ( ret ! = 0 ) {
2010-04-17 22:08:15 +04:00
TALLOC_FREE ( priv ) ;
2010-01-28 09:27:11 +03:00
return ret ;
}
2014-02-21 08:13:47 +04:00
krb5_free_default_realm ( context , realm ) ;
2010-01-28 09:27:11 +03:00
2011-09-28 23:23:38 +04:00
lret = dsdb_search ( ldb_ctx , priv , & res ,
priv - > realm_dn , LDB_SCOPE_SUBTREE , user_attrs ,
2024-04-15 02:42:50 +03:00
DSDB_SEARCH_NO_GLOBAL_CATALOG | DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS ,
2011-09-28 23:23:38 +04:00
" (objectClass=user) " ) ;
2010-01-28 09:27:11 +03:00
if ( lret ! = LDB_SUCCESS ) {
2010-04-17 22:08:15 +04:00
TALLOC_FREE ( priv ) ;
2014-05-08 19:13:04 +04:00
return SDB_ERR_NOENTRY ;
2010-01-28 09:27:11 +03:00
}
priv - > count = res - > count ;
2022-02-03 16:14:06 +03:00
priv - > msgs = talloc_move ( priv , & res - > msgs ) ;
TALLOC_FREE ( res ) ;
status = dsdb_trust_search_tdos ( ldb_ctx ,
NULL , /* exclude */
trust_attrs ,
priv ,
& res ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " dsdb_trust_search_tdos() - %s \n " ,
nt_errstr ( status ) ) ;
TALLOC_FREE ( priv ) ;
return SDB_ERR_NOENTRY ;
}
priv - > trust_direction = INBOUND ;
priv - > trust_count = res - > count ;
priv - > trust_msgs = talloc_move ( priv , & res - > msgs ) ;
TALLOC_FREE ( res ) ;
2010-01-28 09:27:11 +03:00
kdc_db_ctx - > seq_ctx = priv ;
2024-04-24 04:45:08 +03:00
ret = samba_kdc_seq ( context , kdc_db_ctx , sdb_flags , entry ) ;
2010-01-28 09:27:11 +03:00
if ( ret ! = 0 ) {
2010-04-17 22:08:15 +04:00
TALLOC_FREE ( priv ) ;
2010-01-28 09:27:11 +03:00
kdc_db_ctx - > seq_ctx = NULL ;
}
return ret ;
}
krb5_error_code samba_kdc_nextkey ( krb5_context context ,
struct samba_kdc_db_context * kdc_db_ctx ,
2024-04-24 04:45:08 +03:00
const unsigned sdb_flags ,
2022-03-23 05:43:25 +03:00
struct sdb_entry * entry )
2010-01-28 09:27:11 +03:00
{
2024-04-24 04:45:08 +03:00
return samba_kdc_seq ( context , kdc_db_ctx , sdb_flags , entry ) ;
2010-01-28 09:27:11 +03:00
}
2010-03-27 15:11:06 +03:00
/* Check if a given entry may delegate or do s4u2self to this target principal
2010-01-28 09:27:11 +03:00
*
2021-10-07 22:29:51 +03:00
* The safest way to determine ' self ' is to check the DB record made at
* the time the principal was presented to the KDC .
2010-01-28 09:27:11 +03:00
*/
krb5_error_code
2016-01-08 04:08:18 +03:00
samba_kdc_check_client_matches_target_service ( krb5_context context ,
struct samba_kdc_entry * skdc_entry_client ,
struct samba_kdc_entry * skdc_entry_server_target )
2010-01-28 09:27:11 +03:00
{
struct dom_sid * orig_sid ;
struct dom_sid * target_sid ;
2021-10-07 22:29:51 +03:00
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2010-01-28 09:27:11 +03:00
2021-10-07 22:29:51 +03:00
orig_sid = samdb_result_dom_sid ( frame ,
skdc_entry_client - > msg ,
" objectSid " ) ;
target_sid = samdb_result_dom_sid ( frame ,
skdc_entry_server_target - > msg ,
" objectSid " ) ;
2010-01-28 09:27:11 +03:00
2021-10-07 22:29:51 +03:00
/*
* Allow delegation to the same record ( representing a
* principal ) , even if by a different name . The easy and safe
* way to prove this is by SID comparison
*/
2010-01-28 09:27:11 +03:00
if ( ! ( orig_sid & & target_sid & & dom_sid_equal ( orig_sid , target_sid ) ) ) {
2021-10-07 22:29:51 +03:00
talloc_free ( frame ) ;
2021-11-23 10:00:07 +03:00
return KRB5KRB_AP_ERR_BADMATCH ;
2010-01-28 09:27:11 +03:00
}
2021-10-07 22:29:51 +03:00
talloc_free ( frame ) ;
return 0 ;
2010-01-28 09:27:11 +03:00
}
2023-09-04 07:52:57 +03:00
/* Certificates printed by the Certificate Authority might have a
2010-01-28 09:27:11 +03:00
* slightly different form of the user principal name to that in the
* database . Allow a mismatch where they both refer to the same
* SID */
krb5_error_code
samba_kdc_check_pkinit_ms_upn_match ( krb5_context context ,
struct samba_kdc_db_context * kdc_db_ctx ,
2014-05-09 16:56:22 +04:00
struct samba_kdc_entry * skdc_entry ,
2010-01-28 09:27:11 +03:00
krb5_const_principal certificate_principal )
{
krb5_error_code ret ;
struct ldb_dn * realm_dn ;
struct ldb_message * msg ;
struct dom_sid * orig_sid ;
struct dom_sid * target_sid ;
const char * ms_upn_check_attrs [ ] = {
" objectSid " , NULL
} ;
TALLOC_CTX * mem_ctx = talloc_named ( kdc_db_ctx , 0 , " samba_kdc_check_pkinit_ms_upn_match " ) ;
if ( ! mem_ctx ) {
ret = ENOMEM ;
2023-05-09 07:28:22 +03:00
krb5_set_error_message ( context , ret , " samba_kdc_check_pkinit_ms_upn_match: talloc_named() failed! " ) ;
2010-01-28 09:27:11 +03:00
return ret ;
}
ret = samba_kdc_lookup_client ( context , kdc_db_ctx ,
2015-01-30 02:31:29 +03:00
mem_ctx , certificate_principal ,
2024-04-15 02:42:50 +03:00
ms_upn_check_attrs , 0 , & realm_dn , & msg ) ;
2010-01-28 09:27:11 +03:00
if ( ret ! = 0 ) {
talloc_free ( mem_ctx ) ;
return ret ;
}
2014-05-09 16:56:22 +04:00
orig_sid = samdb_result_dom_sid ( mem_ctx , skdc_entry - > msg , " objectSid " ) ;
2010-01-28 09:27:11 +03:00
target_sid = samdb_result_dom_sid ( mem_ctx , msg , " objectSid " ) ;
/* Consider these to be the same principal, even if by a different
* name . The easy and safe way to prove this is by SID
* comparison */
if ( ! ( orig_sid & & target_sid & & dom_sid_equal ( orig_sid , target_sid ) ) ) {
talloc_free ( mem_ctx ) ;
2018-10-23 07:33:46 +03:00
# if defined(KRB5KDC_ERR_CLIENT_NAME_MISMATCH) /* MIT */
2014-05-08 16:42:20 +04:00
return KRB5KDC_ERR_CLIENT_NAME_MISMATCH ;
2018-10-23 07:33:46 +03:00
# else /* Heimdal (where this is an enum) */
return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH ;
2014-05-08 16:42:20 +04:00
# endif
2010-01-28 09:27:11 +03:00
}
talloc_free ( mem_ctx ) ;
return ret ;
}
2011-04-07 13:16:55 +04:00
/*
* Check if a given entry may delegate to this target principal
* with S4U2Proxy .
*/
krb5_error_code
samba_kdc_check_s4u2proxy ( krb5_context context ,
struct samba_kdc_db_context * kdc_db_ctx ,
2014-05-09 16:58:08 +04:00
struct samba_kdc_entry * skdc_entry ,
2011-04-07 13:16:55 +04:00
krb5_const_principal target_principal )
{
2011-04-27 13:41:49 +04:00
krb5_error_code ret ;
char * tmp = NULL ;
const char * client_dn = NULL ;
const char * target_principal_name = NULL ;
struct ldb_message_element * el ;
struct ldb_val val ;
unsigned int i ;
bool found = false ;
TALLOC_CTX * mem_ctx = talloc_named ( kdc_db_ctx , 0 , " samba_kdc_check_s4u2proxy " ) ;
if ( ! mem_ctx ) {
ret = ENOMEM ;
krb5_set_error_message ( context , ret ,
" samba_kdc_check_s4u2proxy: "
" talloc_named() failed! " ) ;
return ret ;
}
2014-05-09 16:58:08 +04:00
client_dn = ldb_dn_get_linearized ( skdc_entry - > msg - > dn ) ;
2011-04-27 13:41:49 +04:00
if ( ! client_dn ) {
if ( errno = = 0 ) {
errno = ENOMEM ;
}
ret = errno ;
krb5_set_error_message ( context , ret ,
" samba_kdc_check_s4u2proxy: "
" ldb_dn_get_linearized() failed! " ) ;
2023-05-09 05:53:33 +03:00
talloc_free ( mem_ctx ) ;
2011-04-27 13:41:49 +04:00
return ret ;
}
2021-12-09 09:48:13 +03:00
el = ldb_msg_find_element ( skdc_entry - > msg , " msDS-AllowedToDelegateTo " ) ;
if ( el = = NULL ) {
2022-03-08 17:04:34 +03:00
ret = ENOENT ;
2021-12-09 09:48:13 +03:00
goto bad_option ;
}
2022-03-07 18:02:18 +03:00
SMB_ASSERT ( el - > num_values ! = 0 ) ;
2021-12-09 09:48:13 +03:00
/*
* This is the Microsoft forwardable flag behavior .
*
* If the proxy ( target ) principal is NULL , and we have any authorized
* delegation target , allow to forward .
*/
2022-03-07 18:02:18 +03:00
if ( target_principal = = NULL ) {
2023-05-09 05:53:33 +03:00
talloc_free ( mem_ctx ) ;
2021-12-09 09:48:13 +03:00
return 0 ;
}
2011-04-27 13:41:49 +04:00
/*
* The main heimdal code already checked that the target_principal
* belongs to the same realm as the client .
*
* So we just need the principal without the realm ,
* as that is what is configured in the " msDS-AllowedToDelegateTo "
* attribute .
*/
ret = krb5_unparse_name_flags ( context , target_principal ,
KRB5_PRINCIPAL_UNPARSE_NO_REALM , & tmp ) ;
if ( ret ) {
talloc_free ( mem_ctx ) ;
krb5_set_error_message ( context , ret ,
" samba_kdc_check_s4u2proxy: "
2023-08-25 02:29:24 +03:00
" krb5_unparse_name_flags() failed! " ) ;
2011-04-27 13:41:49 +04:00
return ret ;
}
2023-07-21 02:44:05 +03:00
DBG_DEBUG ( " client[%s] for target[%s] \n " ,
client_dn , tmp ) ;
2011-04-27 13:41:49 +04:00
target_principal_name = talloc_strdup ( mem_ctx , tmp ) ;
SAFE_FREE ( tmp ) ;
if ( target_principal_name = = NULL ) {
ret = ENOMEM ;
krb5_set_error_message ( context , ret ,
" samba_kdc_check_s4u2proxy: "
" talloc_strdup() failed! " ) ;
2023-05-09 05:53:33 +03:00
talloc_free ( mem_ctx ) ;
2011-04-27 13:41:49 +04:00
return ret ;
}
val = data_blob_string_const ( target_principal_name ) ;
for ( i = 0 ; i < el - > num_values ; i + + ) {
struct ldb_val * val1 = & val ;
struct ldb_val * val2 = & el - > values [ i ] ;
int cmp ;
if ( val1 - > length ! = val2 - > length ) {
continue ;
}
cmp = strncasecmp ( ( const char * ) val1 - > data ,
( const char * ) val2 - > data ,
val1 - > length ) ;
if ( cmp ! = 0 ) {
continue ;
}
found = true ;
break ;
}
if ( ! found ) {
2022-03-08 17:04:34 +03:00
ret = ENOENT ;
2011-04-27 13:41:49 +04:00
goto bad_option ;
}
2023-07-21 02:44:05 +03:00
DBG_DEBUG ( " client[%s] allowed target[%s] \n " ,
client_dn , target_principal_name ) ;
2011-04-27 13:41:49 +04:00
talloc_free ( mem_ctx ) ;
return 0 ;
bad_option :
krb5_set_error_message ( context , ret ,
" samba_kdc_check_s4u2proxy: client[%s] "
" not allowed for delegation to target[%s] " ,
client_dn ,
target_principal_name ) ;
talloc_free ( mem_ctx ) ;
2011-04-07 13:16:55 +04:00
return KRB5KDC_ERR_BADOPTION ;
}
2021-12-14 13:16:12 +03:00
/*
* This method is called for S4U2Proxy requests and implements the
* resource - based constrained delegation variant , which can support
* cross - realm delegation .
*/
krb5_error_code samba_kdc_check_s4u2proxy_rbcd (
krb5_context context ,
struct samba_kdc_db_context * kdc_db_ctx ,
krb5_const_principal client_principal ,
krb5_const_principal server_principal ,
2023-10-10 05:12:30 +03:00
const struct auth_user_info_dc * user_info_dc ,
2023-10-10 05:38:29 +03:00
const struct auth_user_info_dc * device_info_dc ,
const struct auth_claims auth_claims ,
2021-12-14 13:16:12 +03:00
struct samba_kdc_entry * proxy_skdc_entry )
{
krb5_error_code code ;
enum ndr_err_code ndr_err ;
char * client_name = NULL ;
char * server_name = NULL ;
const char * proxy_dn = NULL ;
const DATA_BLOB * data = NULL ;
struct security_descriptor * rbcd_security_descriptor = NULL ;
2023-05-09 05:41:30 +03:00
struct security_token * security_token = NULL ;
2023-06-20 07:50:18 +03:00
uint32_t session_info_flags =
AUTH_SESSION_INFO_DEFAULT_GROUPS |
2023-10-25 04:56:41 +03:00
AUTH_SESSION_INFO_DEVICE_DEFAULT_GROUPS |
2023-10-17 04:24:46 +03:00
AUTH_SESSION_INFO_SIMPLE_PRIVILEGES |
AUTH_SESSION_INFO_FORCE_COMPOUNDED_AUTHENTICATION ;
2022-05-02 01:50:33 +03:00
/*
* Testing shows that although Windows grants SEC_ADS_GENERIC_ALL access
* in security descriptors it creates for RBCD , its KDC only requires
* SEC_ADS_CONTROL_ACCESS for the access check to succeed .
*/
uint32_t access_desired = SEC_ADS_CONTROL_ACCESS ;
2021-12-14 13:16:12 +03:00
uint32_t access_granted = 0 ;
NTSTATUS nt_status ;
TALLOC_CTX * mem_ctx = NULL ;
mem_ctx = talloc_named ( kdc_db_ctx ,
0 ,
" samba_kdc_check_s4u2proxy_rbcd " ) ;
if ( mem_ctx = = NULL ) {
errno = ENOMEM ;
code = errno ;
return code ;
}
proxy_dn = ldb_dn_get_linearized ( proxy_skdc_entry - > msg - > dn ) ;
if ( proxy_dn = = NULL ) {
DBG_ERR ( " ldb_dn_get_linearized failed for proxy_dn! \n " ) ;
if ( errno = = 0 ) {
errno = ENOMEM ;
}
code = errno ;
goto out ;
}
rbcd_security_descriptor = talloc_zero ( mem_ctx ,
struct security_descriptor ) ;
if ( rbcd_security_descriptor = = NULL ) {
errno = ENOMEM ;
code = errno ;
goto out ;
}
code = krb5_unparse_name_flags ( context ,
client_principal ,
KRB5_PRINCIPAL_UNPARSE_DISPLAY ,
& client_name ) ;
if ( code ! = 0 ) {
DBG_ERR ( " Unable to parse client_principal! \n " ) ;
goto out ;
}
code = krb5_unparse_name_flags ( context ,
server_principal ,
KRB5_PRINCIPAL_UNPARSE_DISPLAY ,
& server_name ) ;
if ( code ! = 0 ) {
DBG_ERR ( " Unable to parse server_principal! \n " ) ;
goto out ;
}
DBG_INFO ( " Check delegation from client[%s] to server[%s] via "
" proxy[%s] \n " ,
client_name ,
server_name ,
proxy_dn ) ;
2022-12-12 00:50:01 +03:00
if ( ! ( user_info_dc - > info - > user_flags & NETLOGON_GUEST ) ) {
2021-12-14 13:16:12 +03:00
session_info_flags | = AUTH_SESSION_INFO_AUTHENTICATED ;
}
2023-10-25 05:10:48 +03:00
if ( device_info_dc ! = NULL & & ! ( device_info_dc - > info - > user_flags & NETLOGON_GUEST ) ) {
session_info_flags | = AUTH_SESSION_INFO_DEVICE_AUTHENTICATED ;
}
2023-05-09 05:41:30 +03:00
nt_status = auth_generate_security_token ( mem_ctx ,
kdc_db_ctx - > lp_ctx ,
kdc_db_ctx - > samdb ,
user_info_dc ,
2023-10-10 05:38:29 +03:00
device_info_dc ,
auth_claims ,
2023-05-09 05:41:30 +03:00
session_info_flags ,
& security_token ) ;
2021-12-14 13:16:12 +03:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
code = map_errno_from_nt_status ( nt_status ) ;
goto out ;
}
data = ldb_msg_find_ldb_val ( proxy_skdc_entry - > msg ,
" msDS-AllowedToActOnBehalfOfOtherIdentity " ) ;
if ( data = = NULL ) {
2023-06-23 12:51:47 +03:00
DBG_WARNING ( " Could not find security descriptor "
" msDS-AllowedToActOnBehalfOfOtherIdentity in "
" proxy[%s] \n " ,
proxy_dn ) ;
2021-12-14 13:16:12 +03:00
code = KRB5KDC_ERR_BADOPTION ;
goto out ;
}
ndr_err = ndr_pull_struct_blob (
data ,
mem_ctx ,
rbcd_security_descriptor ,
( ndr_pull_flags_fn_t ) ndr_pull_security_descriptor ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
errno = ndr_map_error2errno ( ndr_err ) ;
DBG_ERR ( " Failed to unmarshall "
" msDS-AllowedToActOnBehalfOfOtherIdentity "
" security descriptor of proxy[%s] \n " ,
proxy_dn ) ;
code = KRB5KDC_ERR_BADOPTION ;
goto out ;
}
if ( DEBUGLEVEL > = 10 ) {
2023-05-09 05:41:30 +03:00
NDR_PRINT_DEBUG ( security_token , security_token ) ;
2021-12-14 13:16:12 +03:00
NDR_PRINT_DEBUG ( security_descriptor , rbcd_security_descriptor ) ;
}
nt_status = sec_access_check_ds ( rbcd_security_descriptor ,
2023-05-09 05:41:30 +03:00
security_token ,
2021-12-14 13:16:12 +03:00
access_desired ,
& access_granted ,
NULL ,
NULL ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
DBG_WARNING ( " RBCD: sec_access_check_ds(access_desired=%#08x, "
" access_granted:%#08x) failed with: %s \n " ,
access_desired ,
access_granted ,
nt_errstr ( nt_status ) ) ;
code = KRB5KDC_ERR_BADOPTION ;
goto out ;
}
DBG_NOTICE ( " RBCD: Access granted for client[%s] \n " , client_name ) ;
code = 0 ;
out :
SAFE_FREE ( client_name ) ;
SAFE_FREE ( server_name ) ;
TALLOC_FREE ( mem_ctx ) ;
return code ;
}
2010-09-28 07:05:37 +04:00
NTSTATUS samba_kdc_setup_db_ctx ( TALLOC_CTX * mem_ctx , struct samba_kdc_base_context * base_ctx ,
struct samba_kdc_db_context * * kdc_db_ctx_out )
{
int ldb_ret ;
2023-08-25 02:30:27 +03:00
struct ldb_message * msg = NULL ;
struct samba_kdc_db_context * kdc_db_ctx = NULL ;
2024-05-28 03:53:19 +03:00
bool time_ok ;
2010-09-28 07:05:37 +04:00
/* The idea here is very simple. Using Kerberos to
2023-05-09 03:07:35 +03:00
* authenticate the KDC to the LDAP server is highly likely to
2010-09-28 07:05:37 +04:00
* be circular .
*
2023-08-02 11:47:30 +03:00
* In future we may set this up to use EXTERNAL and SSL
2010-09-28 07:05:37 +04:00
* certificates , for now it will almost certainly be NTLMSSP_SET_USERNAME
*/
kdc_db_ctx = talloc_zero ( mem_ctx , struct samba_kdc_db_context ) ;
if ( kdc_db_ctx = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
kdc_db_ctx - > ev_ctx = base_ctx - > ev_ctx ;
kdc_db_ctx - > lp_ctx = base_ctx - > lp_ctx ;
2017-02-21 04:07:54 +03:00
kdc_db_ctx - > msg_ctx = base_ctx - > msg_ctx ;
2010-09-28 07:05:37 +04:00
2024-05-28 03:53:19 +03:00
/* Copy over the pointer that will be updated with the time */
kdc_db_ctx - > current_nttime_ull = base_ctx - > current_nttime_ull ;
2012-04-20 01:54:57 +04:00
/* get default kdc policy */
2017-08-09 20:30:00 +03:00
lpcfg_default_kdc_policy ( mem_ctx ,
base_ctx - > lp_ctx ,
2012-04-20 01:54:57 +04:00
& kdc_db_ctx - > policy . svc_tkt_lifetime ,
& kdc_db_ctx - > policy . usr_tkt_lifetime ,
& kdc_db_ctx - > policy . renewal_lifetime ) ;
2010-12-09 06:17:54 +03:00
2023-12-19 05:58:49 +03:00
/* This is to allow "samba-tool domain exportkeytab to take a -H */
if ( base_ctx - > samdb ! = NULL ) {
/*
* Caller is responsible for lifetimes . In reality
* the whole thing is destroyed before leaving the
2024-05-30 02:43:04 +03:00
* function the samdb was passed into .
*
* We assume this DB is created from python and so
* can ' t be in the ldb_wrap cache .
2023-12-19 05:58:49 +03:00
*/
kdc_db_ctx - > samdb = base_ctx - > samdb ;
} else {
struct auth_session_info * session_info = NULL ;
session_info = system_session ( kdc_db_ctx - > lp_ctx ) ;
if ( session_info = = NULL ) {
talloc_free ( kdc_db_ctx ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
/* Setup the link to LDB */
kdc_db_ctx - > samdb = samdb_connect ( kdc_db_ctx ,
base_ctx - > ev_ctx ,
base_ctx - > lp_ctx ,
session_info ,
NULL ,
2024-05-30 02:43:04 +03:00
SAMBA_LDB_WRAP_CONNECT_FLAG_NO_SHARE_CONTEXT ) ;
2023-12-19 05:58:49 +03:00
if ( kdc_db_ctx - > samdb = = NULL ) {
DBG_WARNING ( " Cannot open samdb for KDC backend! \n " ) ;
talloc_free ( kdc_db_ctx ) ;
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO ;
}
2010-09-28 07:05:37 +04:00
}
2024-05-28 03:53:19 +03:00
/*
* Set the current time pointer , which will be updated before
* each packet ( Heimdal ) or fetch call ( MIT )
*/
time_ok = dsdb_gmsa_set_current_time ( kdc_db_ctx - > samdb , kdc_db_ctx - > current_nttime_ull ) ;
if ( ! time_ok ) {
talloc_free ( kdc_db_ctx ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
2010-09-28 07:05:37 +04:00
/* Find out our own krbtgt kvno */
ldb_ret = samdb_rodc ( kdc_db_ctx - > samdb , & kdc_db_ctx - > rodc ) ;
if ( ldb_ret ! = LDB_SUCCESS ) {
2023-07-21 02:44:05 +03:00
DBG_WARNING ( " Cannot determine if we are an RODC in KDC backend: %s \n " ,
ldb_errstring ( kdc_db_ctx - > samdb ) ) ;
2010-09-28 07:05:37 +04:00
talloc_free ( kdc_db_ctx ) ;
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO ;
}
if ( kdc_db_ctx - > rodc ) {
int my_krbtgt_number ;
const char * secondary_keytab [ ] = { " msDS-SecondaryKrbTgtNumber " , NULL } ;
2023-08-25 02:30:27 +03:00
struct ldb_dn * account_dn = NULL ;
2010-09-28 07:05:37 +04:00
struct ldb_dn * server_dn = samdb_server_dn ( kdc_db_ctx - > samdb , kdc_db_ctx ) ;
if ( ! server_dn ) {
2023-07-21 02:44:05 +03:00
DBG_WARNING ( " Cannot determine server DN in KDC backend: %s \n " ,
ldb_errstring ( kdc_db_ctx - > samdb ) ) ;
2010-09-28 07:05:37 +04:00
talloc_free ( kdc_db_ctx ) ;
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO ;
}
ldb_ret = samdb_reference_dn ( kdc_db_ctx - > samdb , kdc_db_ctx , server_dn ,
" serverReference " , & account_dn ) ;
if ( ldb_ret ! = LDB_SUCCESS ) {
2023-07-21 02:44:05 +03:00
DBG_WARNING ( " Cannot determine server account in KDC backend: %s \n " ,
ldb_errstring ( kdc_db_ctx - > samdb ) ) ;
2010-09-28 07:05:37 +04:00
talloc_free ( kdc_db_ctx ) ;
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO ;
}
ldb_ret = samdb_reference_dn ( kdc_db_ctx - > samdb , kdc_db_ctx , account_dn ,
" msDS-KrbTgtLink " , & kdc_db_ctx - > krbtgt_dn ) ;
talloc_free ( account_dn ) ;
if ( ldb_ret ! = LDB_SUCCESS ) {
2023-07-21 02:44:05 +03:00
DBG_WARNING ( " Cannot determine RODC krbtgt account in KDC backend: %s \n " ,
ldb_errstring ( kdc_db_ctx - > samdb ) ) ;
2010-09-28 07:05:37 +04:00
talloc_free ( kdc_db_ctx ) ;
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO ;
}
ldb_ret = dsdb_search_one ( kdc_db_ctx - > samdb , kdc_db_ctx ,
& msg , kdc_db_ctx - > krbtgt_dn , LDB_SCOPE_BASE ,
secondary_keytab ,
2011-09-28 23:23:38 +04:00
DSDB_SEARCH_NO_GLOBAL_CATALOG ,
2010-09-28 07:05:37 +04:00
" (&(objectClass=user)(msDS-SecondaryKrbTgtNumber=*)) " ) ;
if ( ldb_ret ! = LDB_SUCCESS ) {
2023-07-21 02:44:05 +03:00
DBG_WARNING ( " Cannot read krbtgt account %s in KDC backend to get msDS-SecondaryKrbTgtNumber: %s: %s \n " ,
ldb_dn_get_linearized ( kdc_db_ctx - > krbtgt_dn ) ,
ldb_errstring ( kdc_db_ctx - > samdb ) ,
ldb_strerror ( ldb_ret ) ) ;
2010-09-28 07:05:37 +04:00
talloc_free ( kdc_db_ctx ) ;
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO ;
}
my_krbtgt_number = ldb_msg_find_attr_as_int ( msg , " msDS-SecondaryKrbTgtNumber " , - 1 ) ;
if ( my_krbtgt_number = = - 1 ) {
2023-07-21 02:44:05 +03:00
DBG_WARNING ( " Cannot read msDS-SecondaryKrbTgtNumber from krbtgt account %s in KDC backend: got %d \n " ,
ldb_dn_get_linearized ( kdc_db_ctx - > krbtgt_dn ) ,
my_krbtgt_number ) ;
2010-09-28 07:05:37 +04:00
talloc_free ( kdc_db_ctx ) ;
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO ;
}
kdc_db_ctx - > my_krbtgt_number = my_krbtgt_number ;
} else {
kdc_db_ctx - > my_krbtgt_number = 0 ;
ldb_ret = dsdb_search_one ( kdc_db_ctx - > samdb , kdc_db_ctx ,
2011-08-19 11:31:13 +04:00
& msg ,
ldb_get_default_basedn ( kdc_db_ctx - > samdb ) ,
LDB_SCOPE_SUBTREE ,
2010-09-28 07:05:37 +04:00
krbtgt_attrs ,
2024-04-15 02:42:50 +03:00
DSDB_SEARCH_NO_GLOBAL_CATALOG | DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS ,
2010-09-28 07:05:37 +04:00
" (&(objectClass=user)(samAccountName=krbtgt)) " ) ;
if ( ldb_ret ! = LDB_SUCCESS ) {
2023-07-21 02:44:05 +03:00
DBG_WARNING ( " could not find own KRBTGT in DB: %s \n " , ldb_errstring ( kdc_db_ctx - > samdb ) ) ;
2010-09-28 07:05:37 +04:00
talloc_free ( kdc_db_ctx ) ;
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO ;
}
kdc_db_ctx - > krbtgt_dn = talloc_steal ( kdc_db_ctx , msg - > dn ) ;
kdc_db_ctx - > my_krbtgt_number = 0 ;
talloc_free ( msg ) ;
}
* kdc_db_ctx_out = kdc_db_ctx ;
return NT_STATUS_OK ;
2022-03-22 20:09:33 +03:00
}
2022-05-18 11:12:36 +03:00
krb5_error_code dsdb_extract_aes_256_key ( krb5_context context ,
TALLOC_CTX * mem_ctx ,
2024-04-15 05:39:45 +03:00
struct ldb_context * ldb ,
2022-05-18 11:12:36 +03:00
const struct ldb_message * msg ,
uint32_t user_account_control ,
const uint32_t * kvno ,
uint32_t * kvno_out ,
DATA_BLOB * aes_256_key ,
DATA_BLOB * salt )
{
krb5_error_code krb5_ret ;
uint32_t supported_enctypes ;
unsigned flags = SDB_F_GET_CLIENT ;
struct sdb_entry sentry = { } ;
if ( kvno ! = NULL ) {
flags | = SDB_F_KVNO_SPECIFIED ;
}
krb5_ret = samba_kdc_message2entry_keys ( context ,
mem_ctx ,
2024-04-15 05:39:45 +03:00
ldb ,
2022-05-18 11:12:36 +03:00
msg ,
false , /* is_krbtgt */
false , /* is_rodc */
user_account_control ,
SAMBA_KDC_ENT_TYPE_CLIENT ,
flags ,
( kvno ! = NULL ) ? * kvno : 0 ,
& sentry ,
ENC_HMAC_SHA1_96_AES256 ,
& supported_enctypes ) ;
if ( krb5_ret ! = 0 ) {
2023-08-11 01:25:08 +03:00
const char * krb5_err = krb5_get_error_message ( context , krb5_ret ) ;
2022-05-18 11:12:36 +03:00
DBG_ERR ( " Failed to parse supplementalCredentials "
" of %s with %s kvno using "
" ENCTYPE_HMAC_SHA1_96_AES256 "
" Kerberos Key: %s \n " ,
ldb_dn_get_linearized ( msg - > dn ) ,
( kvno ! = NULL ) ? " previous " : " current " ,
2023-08-11 01:25:08 +03:00
krb5_err ! = NULL ? krb5_err : " <unknown> " ) ;
krb5_free_error_message ( context , krb5_err ) ;
2022-05-18 11:12:36 +03:00
return krb5_ret ;
}
if ( ( supported_enctypes & ENC_HMAC_SHA1_96_AES256 ) = = 0 | |
sentry . keys . len ! = 1 ) {
DBG_INFO ( " Failed to find a ENCTYPE_HMAC_SHA1_96_AES256 "
" key in supplementalCredentials "
" of %s at KVNO %u (got %u keys, expected 1) \n " ,
ldb_dn_get_linearized ( msg - > dn ) ,
sentry . kvno ,
sentry . keys . len ) ;
sdb_entry_free ( & sentry ) ;
return ENOENT ;
}
if ( sentry . keys . val [ 0 ] . salt = = NULL ) {
DBG_INFO ( " Failed to find a salt in "
" supplementalCredentials "
" of %s at KVNO %u \n " ,
ldb_dn_get_linearized ( msg - > dn ) ,
sentry . kvno ) ;
sdb_entry_free ( & sentry ) ;
return ENOENT ;
}
if ( aes_256_key ! = NULL ) {
* aes_256_key = data_blob_talloc ( mem_ctx ,
KRB5_KEY_DATA ( & sentry . keys . val [ 0 ] . key ) ,
KRB5_KEY_LENGTH ( & sentry . keys . val [ 0 ] . key ) ) ;
if ( aes_256_key - > data = = NULL ) {
sdb_entry_free ( & sentry ) ;
return ENOMEM ;
}
talloc_keep_secret ( aes_256_key - > data ) ;
}
if ( salt ! = NULL ) {
* salt = data_blob_talloc ( mem_ctx ,
sentry . keys . val [ 0 ] . salt - > salt . data ,
sentry . keys . val [ 0 ] . salt - > salt . length ) ;
if ( salt - > data = = NULL ) {
sdb_entry_free ( & sentry ) ;
return ENOMEM ;
}
}
if ( kvno_out ! = NULL ) {
* kvno_out = sentry . kvno ;
}
sdb_entry_free ( & sentry ) ;
return 0 ;
}