2004-06-18 01:39:51 +04:00
/*
Unix SMB / CIFS implementation .
kerberos keytab utility library
Copyright ( C ) Andrew Tridgell 2001
Copyright ( C ) Remus Koos 2001
Copyright ( C ) Luke Howard 2003
Copyright ( C ) Jim McDonough ( jmcd @ us . ibm . com ) 2003
2007-06-29 13:42:14 +04:00
Copyright ( C ) Guenther Deschner 2003
2004-06-18 01:39:51 +04:00
Copyright ( C ) Rakesh Patel 2004
Copyright ( C ) Dan Perry 2004
2004-06-18 03:07:20 +04:00
Copyright ( C ) Jeremy Allison 2004
2006-07-11 22:45:22 +04:00
Copyright ( C ) Gerald Carter 2006
2004-06-18 01:39:51 +04:00
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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2004-06-18 01:39:51 +04:00
( 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
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-06-18 01:39:51 +04:00
*/
# include "includes.h"
2009-11-27 17:52:57 +03:00
# include "smb_krb5.h"
2010-07-02 02:32:52 +04:00
# include "ads.h"
2010-08-05 04:25:37 +04:00
# include "secrets.h"
2021-09-06 17:58:17 +03:00
# include "librpc/gen_ndr/ndr_secrets.h"
2004-06-18 01:39:51 +04:00
# ifdef HAVE_KRB5
2011-04-15 14:37:55 +04:00
# ifdef HAVE_ADS
2017-03-13 18:24:52 +03:00
/* This MAX_NAME_LEN is a constant defined in krb5.h */
# ifndef MAX_KEYTAB_NAME_LEN
# define MAX_KEYTAB_NAME_LEN 1100
# endif
2021-09-06 17:58:17 +03:00
enum spn_spec_type {
SPN_SPEC_DEFAULT ,
SPN_SPEC_SYNC ,
SPN_SPEC_FULL ,
SPN_SPEC_PREFIX
} ;
/* pw2kt_conf contains 1 parsed line from "sync machine password to keytab" */
struct pw2kt_conf {
enum spn_spec_type spn_spec ;
char * keytab ;
bool sync_etypes ;
bool sync_kvno ;
bool additional_dns_hostnames ;
bool netbios_aliases ;
bool machine_password ;
char * * spn_spec_array ;
size_t num_spn_spec ;
} ;
/* State used by pw2kt */
struct pw2kt_state {
/* Array of parsed lines from "sync machine password to keytab" */
struct pw2kt_conf * keytabs ;
size_t num_keytabs ;
bool sync_etypes ;
bool sync_kvno ;
bool sync_spns ;
/* These are from DC */
krb5_kvno ad_kvno ;
uint32_t ad_etypes ;
char * * ad_spn_array ;
size_t ad_num_spns ;
/* This is from secrets.db */
struct secrets_domain_info1 * info ;
} ;
/* State used by pw2kt_process_keytab */
struct pw2kt_process_state {
krb5_keytab keytab ;
krb5_context context ;
krb5_keytab_entry * array1 ;
krb5_keytab_entry * array2 ;
krb5_principal * princ_array ;
krb5_enctype * enctypes ;
krb5_enctype preferred_etype ;
} ;
static ADS_STATUS pw2kt_scan_add_spn ( TALLOC_CTX * ctx ,
const char * spn ,
struct pw2kt_conf * conf )
{
conf - > spn_spec_array = talloc_realloc ( ctx ,
conf - > spn_spec_array ,
char * ,
conf - > num_spn_spec + 1 ) ;
if ( conf - > spn_spec_array = = NULL ) {
return ADS_ERROR_NT ( NT_STATUS_NO_MEMORY ) ;
}
conf - > spn_spec_array [ conf - > num_spn_spec ] = talloc_strdup (
conf - > spn_spec_array , spn ) ;
if ( conf - > spn_spec_array [ conf - > num_spn_spec ] = = NULL ) {
return ADS_ERROR_NT ( NT_STATUS_NO_MEMORY ) ;
}
conf - > num_spn_spec + + ;
return ADS_SUCCESS ;
}
/*
* Parse the smb . conf and find out if it is needed to read from DC :
* - servicePrincipalNames
* - msDs - KeyVersionNumber
*/
static ADS_STATUS pw2kt_scan_line ( const char * line , struct pw2kt_state * state )
{
char * keytabname = NULL ;
char * spn_spec = NULL ;
char * spn_val = NULL ;
char * option = NULL ;
struct pw2kt_conf * conf = NULL ;
ADS_STATUS status ;
state - > keytabs = talloc_realloc ( state ,
state - > keytabs ,
struct pw2kt_conf ,
state - > num_keytabs + 1 ) ;
if ( state - > keytabs = = NULL ) {
return ADS_ERROR_NT ( NT_STATUS_NO_MEMORY ) ;
}
conf = & state - > keytabs [ state - > num_keytabs ] ;
state - > num_keytabs + + ;
keytabname = talloc_strdup ( state - > keytabs , line ) ;
if ( keytabname = = NULL ) {
return ADS_ERROR_NT ( NT_STATUS_NO_MEMORY ) ;
}
ZERO_STRUCT ( * conf ) ;
conf - > keytab = keytabname ;
spn_spec = strchr_m ( keytabname , ' : ' ) ;
if ( spn_spec = = NULL ) {
DBG_ERR ( " Invalid format! ':' expected in '%s' \n " , keytabname ) ;
return ADS_ERROR_NT ( NT_STATUS_INVALID_PARAMETER ) ;
}
* spn_spec + + = 0 ;
/* reverse match with strrchr_m() */
while ( ( option = strrchr_m ( spn_spec , ' : ' ) ) ! = NULL ) {
* option + + = 0 ;
if ( strequal ( option , " sync_kvno " ) ) {
conf - > sync_kvno = state - > sync_kvno = true ;
} else if ( strequal ( option , " sync_etypes " ) ) {
conf - > sync_etypes = state - > sync_etypes = true ;
} else if ( strequal ( option , " additional_dns_hostnames " ) ) {
conf - > additional_dns_hostnames = true ;
} else if ( strequal ( option , " netbios_aliases " ) ) {
conf - > netbios_aliases = true ;
} else if ( strequal ( option , " machine_password " ) ) {
conf - > machine_password = true ;
} else {
DBG_WARNING ( " Unknown option '%s'! \n " , option ) ;
return ADS_ERROR_NT ( NT_STATUS_INVALID_PARAMETER ) ;
}
}
spn_val = strchr_m ( spn_spec , ' = ' ) ;
if ( spn_val ! = NULL ) {
* spn_val + + = 0 ;
}
if ( strcmp ( spn_spec , " account_name " ) = = 0 ) {
conf - > spn_spec = SPN_SPEC_DEFAULT ;
} else if ( strcmp ( spn_spec , " sync_spns " ) = = 0 ) {
conf - > spn_spec = SPN_SPEC_SYNC ;
state - > sync_spns = true ;
} else if ( strcmp ( spn_spec , " spns " ) = = 0 | |
strcmp ( spn_spec , " spn_prefixes " ) = = 0 )
{
char * spn = NULL , * tmp = NULL ;
conf - > spn_spec = strcmp ( spn_spec , " spns " ) = = 0
? SPN_SPEC_FULL
: SPN_SPEC_PREFIX ;
conf - > num_spn_spec = 0 ;
spn = spn_val ;
while ( ( tmp = strchr_m ( spn , ' , ' ) ) ! = NULL ) {
* tmp + + = 0 ;
status = pw2kt_scan_add_spn ( state - > keytabs , spn , conf ) ;
if ( ! ADS_ERR_OK ( status ) ) {
return status ;
}
spn = tmp ;
}
/* Do not forget the last entry */
return pw2kt_scan_add_spn ( state - > keytabs , spn , conf ) ;
} else {
DBG_WARNING ( " Invalid SPN specifier: %s \n " , spn_spec ) ;
return ADS_ERROR_NT ( NT_STATUS_INVALID_PARAMETER ) ;
}
return ADS_SUCCESS ;
}
/*
* Fill struct pw2kt_state with defaults if " sync machine password to keytab "
* is missing in smb . conf
*/
static ADS_STATUS pw2kt_default_cfg ( const char * name , struct pw2kt_state * state )
{
char * keytabname = NULL ;
struct pw2kt_conf * conf = NULL ;
state - > keytabs = talloc_zero_array ( state - > keytabs ,
struct pw2kt_conf ,
1 ) ;
if ( state - > keytabs = = NULL ) {
return ADS_ERROR_NT ( NT_STATUS_NO_MEMORY ) ;
}
conf = & state - > keytabs [ 0 ] ;
state - > num_keytabs = 1 ;
keytabname = talloc_strdup ( state - > keytabs , name ) ;
if ( keytabname = = NULL ) {
return ADS_ERROR_NT ( NT_STATUS_NO_MEMORY ) ;
}
conf - > spn_spec = SPN_SPEC_SYNC ;
conf - > keytab = keytabname ;
conf - > machine_password = true ;
conf - > sync_kvno = state - > sync_kvno = true ;
state - > sync_spns = true ;
return ADS_SUCCESS ;
}
/*
* For the given principal add to the array entries created from all pw - > keys [ ]
*/
static krb5_error_code pw2kt_process_add_pw (
struct pw2kt_process_state * state2 ,
krb5_principal princ ,
krb5_kvno vno ,
struct secrets_domain_info1_password * pw )
{
uint16_t i ;
size_t len = talloc_array_length ( state2 - > array1 ) ;
for ( i = 0 ; i < pw - > num_keys ; i + + ) {
krb5_keytab_entry * kt_entry = NULL ;
krb5_keyblock * key = NULL ;
krb5_keytab_entry * tmp_a = NULL ;
if ( state2 - > preferred_etype ! = - 1 & &
state2 - > preferred_etype ! = pw - > keys [ i ] . keytype )
{
DBG_DEBUG ( " Skip enc type '%d'. \n " , pw - > keys [ i ] . keytype ) ;
continue ;
}
len + + ;
tmp_a = talloc_realloc ( state2 ,
state2 - > array1 ,
krb5_keytab_entry ,
len ) ;
if ( tmp_a = = NULL ) {
return ENOMEM ;
}
state2 - > array1 = tmp_a ;
kt_entry = & state2 - > array1 [ len - 1 ] ;
ZERO_STRUCT ( * kt_entry ) ;
kt_entry - > principal = princ ;
kt_entry - > vno = vno ;
key = KRB5_KT_KEY ( kt_entry ) ;
KRB5_KEY_TYPE ( key ) = pw - > keys [ i ] . keytype ;
KRB5_KEY_DATA ( key ) = pw - > keys [ i ] . value . data ;
KRB5_KEY_LENGTH ( key ) = pw - > keys [ i ] . value . length ;
}
return 0 ;
}
/*
* For the given principal add to the array entries based on password ,
* old_password , older_password and next_change - > password .
*/
static krb5_error_code pw2kt_process_add_info (
struct pw2kt_process_state * state2 ,
krb5_kvno kvno ,
const char * princs ,
struct secrets_domain_info1 * info )
{
krb5_error_code ret ;
krb5_principal princ = NULL ;
krb5_principal * a = NULL ;
size_t len ;
ret = smb_krb5_parse_name ( state2 - > context , princs , & princ ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Failed to parse principal: %s \n " , princs ) ;
return ret ;
}
len = talloc_array_length ( state2 - > princ_array ) ;
a = talloc_realloc ( state2 ,
state2 - > princ_array ,
krb5_principal ,
len + 1 ) ;
if ( a = = NULL ) {
( void ) krb5_free_principal ( state2 - > context , princ ) ;
return ENOMEM ;
}
a [ len ] = princ ;
state2 - > princ_array = a ;
# define ADD_PW(K, P) \
if ( info - > P ! = NULL ) { \
ret = pw2kt_process_add_pw ( state2 , princ , ( K ) , info - > P ) ; \
if ( ret ! = 0 ) { \
DBG_ERR ( " Failed adding %s keys for %s. \n " , \
# P, \
princs ) ; \
return ret ; \
} \
}
ADD_PW ( kvno , password ) ;
ADD_PW ( kvno - 1 , old_password ) ;
ADD_PW ( kvno - 2 , older_password ) ;
if ( info - > next_change ) {
ADD_PW ( kvno = = - 1 ? - 4 : kvno + 1 , next_change - > password ) ;
}
return ret ;
}
static int pw2kt_process_state_destructor ( struct pw2kt_process_state * state2 )
{
int i ;
size_t len2 = talloc_array_length ( state2 - > array2 ) ;
size_t len_p = talloc_array_length ( state2 - > princ_array ) ;
for ( i = 0 ; i < len2 ; i + + ) {
( void ) smb_krb5_kt_free_entry ( state2 - > context ,
& state2 - > array2 [ i ] ) ;
}
for ( i = 0 ; i < len_p ; i + + ) {
( void ) krb5_free_principal ( state2 - > context ,
state2 - > princ_array [ i ] ) ;
}
( void ) krb5_free_enctypes ( state2 - > context , state2 - > enctypes ) ;
return 0 ;
}
/* Read the whole keytab to krb5_keytab_entry array */
static krb5_error_code pw2kt_process_kt2ar ( struct pw2kt_process_state * state2 )
{
krb5_error_code ret = 0 , ret2 = 0 ;
krb5_kt_cursor cursor ;
krb5_keytab_entry * a = NULL ;
krb5_keytab_entry e ;
size_t num = 0 ;
ZERO_STRUCT ( cursor ) ;
ret = krb5_kt_start_seq_get ( state2 - > context , state2 - > keytab , & cursor ) ;
if ( ret ! = 0 ) {
if ( ret = = KRB5_KT_END | | ret = = ENOENT ) {
ret = 0 ;
}
return ret ;
}
for ( ; ; ) {
ret = samba_krb5_kt_next_entry ( state2 - > context ,
state2 - > keytab ,
& e ,
& cursor ) ;
if ( ret ! = 0 ) {
break ;
}
a = talloc_realloc ( state2 ,
state2 - > array2 ,
krb5_keytab_entry ,
num + 1 ) ;
if ( a = = NULL ) {
smb_krb5_kt_free_entry ( state2 - > context , & e ) ;
return ENOMEM ;
}
a [ num + + ] = e ;
state2 - > array2 = a ;
}
if ( ret = = KRB5_KT_END | | ret = = ENOENT ) {
ret = 0 ;
}
ret2 = krb5_kt_end_seq_get ( state2 - > context , state2 - > keytab , & cursor ) ;
return ret ! = 0 ? ret : ret2 ;
}
static ADS_STATUS pw2kt_process_keytab ( struct pw2kt_state * state ,
struct pw2kt_conf * keytabptr )
{
krb5_error_code ret = 0 ;
krb5_kvno kvno = - 1 ;
size_t i , j , len1 = 0 , len2 = 0 ;
char * princ_s = NULL ;
const char * * netbios_alias = NULL ;
const char * * addl_hostnames = NULL ;
size_t * index_array1 = NULL ;
size_t * index_array2 = NULL ;
struct pw2kt_process_state * state2 = NULL ;
if ( ! keytabptr - > machine_password ) {
DBG_ERR ( " No 'machine_password' option for '%s'. Skip it. \n " ,
keytabptr - > keytab ) ;
return ADS_SUCCESS ;
}
state2 = talloc_zero ( state , struct pw2kt_process_state ) ;
if ( state2 = = NULL ) {
return ADS_ERROR_NT ( NT_STATUS_NO_MEMORY ) ;
}
talloc_set_destructor ( state2 , pw2kt_process_state_destructor ) ;
ret = smb_krb5_init_context_common ( & state2 - > context ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Krb init context failed (%s) \n " , error_message ( ret ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
# define MATCH_ENCTYPE(TYPES, TYPE) \
( { \
int ei , result = 0 ; \
for ( ei = 0 ; ( TYPES ) [ ei ] ! = 0 ; ei + + ) { \
if ( ( uint32_t ) ( TYPES ) [ ei ] ! = ( TYPE ) ) { \
continue ; \
} \
result = 1 ; \
break ; \
} \
result ; \
} )
# define COMMON_ENCTYPE(ETYPE) \
MATCH_ENCTYPE ( ( state2 - > enctypes ) , ( ETYPE ) ) & & \
( ( state - > ad_etypes ) & ( ETYPE ) )
/*
* - 1 means there is no information about supported encryption types
* from DC and all encoding types will be added to the keytab .
*/
state2 - > preferred_etype = - 1 ;
/* Find the highest common enc type for AD and KRB5 lib */
if ( keytabptr - > sync_etypes ) {
ret = smb_krb5_get_allowed_etypes ( state2 - > context ,
& state2 - > enctypes ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Failed to get allowed enc types. \n " ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
if ( COMMON_ENCTYPE ( ENCTYPE_AES256_CTS_HMAC_SHA1_96 ) ) {
state2 - > preferred_etype =
ENCTYPE_AES256_CTS_HMAC_SHA1_96 ;
} else if ( COMMON_ENCTYPE ( ENCTYPE_AES128_CTS_HMAC_SHA1_96 ) ) {
state2 - > preferred_etype =
ENCTYPE_AES128_CTS_HMAC_SHA1_96 ;
} else if ( COMMON_ENCTYPE ( ENCTYPE_ARCFOUR_HMAC ) ) {
state2 - > preferred_etype = ENCTYPE_ARCFOUR_HMAC ;
} else {
DBG_ERR ( " No common enctype for AD and KRB5 lib. \n " ) ;
return ADS_ERROR_NT ( NT_STATUS_INVALID_PARAMETER ) ;
}
}
if ( keytabptr - > sync_kvno ) {
kvno = state - > ad_kvno ;
}
# define ADD_INFO(P) \
ret = pw2kt_process_add_info ( state2 , kvno , ( P ) , state - > info ) ; \
if ( ret ! = 0 ) { \
return ADS_ERROR_KRB5 ( ret ) ; \
}
/* Add ACCOUNTNAME$ entries */
switch ( keytabptr - > spn_spec ) {
case SPN_SPEC_DEFAULT :
ADD_INFO ( state - > info - > account_name ) ;
break ;
case SPN_SPEC_SYNC :
for ( i = 0 ; i < state - > ad_num_spns ; i + + ) {
ADD_INFO ( state - > ad_spn_array [ i ] ) ;
}
break ;
case SPN_SPEC_FULL :
for ( i = 0 ; i < keytabptr - > num_spn_spec ; i + + ) {
ADD_INFO ( keytabptr - > spn_spec_array [ i ] ) ;
}
break ;
case SPN_SPEC_PREFIX :
for ( i = 0 ; i < keytabptr - > num_spn_spec ; i + + ) {
princ_s = talloc_asprintf ( talloc_tos ( ) ,
" %s/%s@%s " ,
keytabptr - > spn_spec_array [ i ] ,
lp_netbios_name ( ) ,
lp_realm ( ) ) ;
if ( princ_s = = NULL ) {
return ADS_ERROR_KRB5 ( ENOMEM ) ;
}
ADD_INFO ( princ_s ) ;
if ( ! keytabptr - > netbios_aliases ) {
goto additional_dns_hostnames ;
}
for ( netbios_alias = lp_netbios_aliases ( ) ;
netbios_alias ! = NULL & & * netbios_alias ! = NULL ;
netbios_alias + + )
{
/* Add PREFIX/netbiosname@REALM */
princ_s = talloc_asprintf (
talloc_tos ( ) ,
" %s/%s@%s " ,
keytabptr - > spn_spec_array [ i ] ,
* netbios_alias ,
lp_realm ( ) ) ;
if ( princ_s = = NULL ) {
return ADS_ERROR_KRB5 ( ENOMEM ) ;
}
ADD_INFO ( princ_s ) ;
/* Add PREFIX/netbiosname.domainname@REALM */
princ_s = talloc_asprintf (
talloc_tos ( ) ,
" %s/%s.%s@%s " ,
keytabptr - > spn_spec_array [ i ] ,
* netbios_alias ,
lp_dnsdomain ( ) ,
lp_realm ( ) ) ;
if ( princ_s = = NULL ) {
return ADS_ERROR_KRB5 ( ENOMEM ) ;
}
ADD_INFO ( princ_s ) ;
}
additional_dns_hostnames :
if ( ! keytabptr - > additional_dns_hostnames ) {
continue ;
}
for ( addl_hostnames = lp_additional_dns_hostnames ( ) ;
addl_hostnames ! = NULL & & * addl_hostnames ! = NULL ;
addl_hostnames + + )
{
/* Add PREFIX/netbiosname@REALM */
princ_s = talloc_asprintf (
talloc_tos ( ) ,
" %s/%s@%s " ,
keytabptr - > spn_spec_array [ i ] ,
* addl_hostnames ,
lp_realm ( ) ) ;
if ( princ_s = = NULL ) {
return ADS_ERROR_KRB5 ( ENOMEM ) ;
}
ADD_INFO ( princ_s ) ;
}
}
break ;
default :
return ADS_ERROR_NT ( NT_STATUS_INVALID_PARAMETER ) ;
}
ret = smb_krb5_kt_open ( state2 - > context ,
keytabptr - > keytab ,
true ,
& state2 - > keytab ) ;
if ( ret ! = 0 ) {
return ADS_ERROR_KRB5 ( ret ) ;
}
/* The new entries are in array1. Read existing entries to array2. */
ret = pw2kt_process_kt2ar ( state2 ) ;
if ( ret ! = 0 ) {
return ADS_ERROR_KRB5 ( ret ) ;
}
len1 = talloc_array_length ( state2 - > array1 ) ;
len2 = talloc_array_length ( state2 - > array2 ) ;
if ( keytabptr - > sync_kvno ) {
goto sync_kvno ;
}
/* copy existing entries VNO -1, -2, -3, -4 to VNO -11, -12, -13, -14 */
for ( j = 0 ; j < len2 ; j + + ) {
krb5_keytab_entry e = state2 - > array2 [ j ] ;
/* vno type is 'krb5_kvno' which is 'unsigned int' */
if ( e . vno ! = - 1 & & e . vno ! = - 2 & & e . vno ! = - 3 & & e . vno ! = - 4 ) {
DBG_WARNING ( " Unexpected keytab entry with VNO = %d (it "
" should be -1, -2, -3, -4) in %s \n " ,
e . vno ,
keytabptr - > keytab ) ;
continue ;
}
e . vno = state2 - > array2 [ j ] . vno - 10 ;
ret = samba_krb5_kt_add_entry ( state2 - > context ,
state2 - > keytab ,
& e ) ;
if ( ret ! = 0 ) {
return ADS_ERROR_KRB5 ( ret ) ;
}
}
/* remove all old entries (they should have VNO -1, -2, -3, -4) */
for ( j = 0 ; j < len2 ; j + + ) {
krb5_keytab_entry e = state2 - > array2 [ j ] ;
if ( e . vno ! = - 1 & & e . vno ! = - 2 & & e . vno ! = - 3 & & e . vno ! = - 4 ) {
DBG_WARNING ( " Unexpected keytab entry with VNO = %d (it "
" should be -1, -2, -3, -4) in %s \n " ,
e . vno ,
keytabptr - > keytab ) ;
}
ret = samba_krb5_kt_remove_entry ( state2 - > context ,
state2 - > keytab ,
& state2 - > array2 [ j ] ) ;
if ( ret ! = 0 ) {
D_WARNING ( " Failed to remove keytab entry from %s \n " ,
keytabptr - > keytab ) ;
ret = 0 ; /* Be fault tolerant */
}
}
/* add new entries with VNO -1, -2, -3, -4 */
for ( i = 0 ; i < len1 ; i + + ) {
ret = samba_krb5_kt_add_entry ( state2 - > context ,
state2 - > keytab ,
& state2 - > array1 [ i ] ) ;
if ( ret ! = 0 ) {
return ADS_ERROR_KRB5 ( ret ) ;
}
}
/* remove entries with VNO -11, -12, -13, -14 */
for ( j = 0 ; j < len2 ; j + + ) {
krb5_keytab_entry e = state2 - > array2 [ j ] ;
e . vno = state2 - > array2 [ j ] . vno - 10 ;
ret = samba_krb5_kt_remove_entry ( state2 - > context ,
state2 - > keytab ,
& e ) ;
if ( ret ! = 0 ) {
D_WARNING ( " Failed to remove keytab entry from %s \n " ,
keytabptr - > keytab ) ;
ret = 0 ; /* Be fault tolerant */
}
}
ret = krb5_kt_close ( state2 - > context , state2 - > keytab ) ;
return ADS_ERROR_KRB5 ( ret ) ;
sync_kvno :
index_array1 = talloc_zero_array ( state2 , size_t , len1 ) ;
index_array2 = talloc_zero_array ( state2 , size_t , len2 ) ;
if ( index_array1 = = NULL | | index_array2 = = NULL ) {
return ADS_ERROR_KRB5 ( ENOMEM ) ;
}
/*
* Mark entries that are present in both arrays .
* These will not be added or removed .
*/
for ( i = 0 ; i < len1 ; i + + ) {
for ( j = 0 ; j < len2 ; j + + ) {
krb5_keytab_entry e2 = state2 - > array2 [ j ] ;
if ( smb_krb5_kt_compare (
state2 - > context ,
& state2 - > array1 [ i ] ,
e2 . principal ,
e2 . vno ,
KRB5_KEY_TYPE ( KRB5_KT_KEY ( & e2 ) )
) )
{
index_array1 [ i ] = 1 ;
index_array2 [ j ] = 1 ;
}
}
}
/* First add the new entries to the keytab.*/
for ( i = 0 ; i < len1 ; i + + ) {
if ( index_array1 [ i ] = = 0 ) {
ret = samba_krb5_kt_add_entry ( state2 - > context ,
state2 - > keytab ,
& state2 - > array1 [ i ] ) ;
if ( ret ! = 0 ) {
return ADS_ERROR_KRB5 ( ret ) ;
}
}
}
/* Now, remove the old entries from the keytab. */
for ( j = 0 ; j < len2 ; j + + ) {
if ( index_array2 [ j ] = = 0 ) {
ret = samba_krb5_kt_remove_entry ( state2 - > context ,
state2 - > keytab ,
& state2 - > array2 [ j ] ) ;
if ( ret ! = 0 ) {
D_WARNING ( " Failed to remove keytab entry from "
" %s \n " ,
keytabptr - > keytab ) ;
ret = 0 ; /* Be fault tolerant */
}
}
}
ret = krb5_kt_close ( state2 - > context , state2 - > keytab ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
static ADS_STATUS pw2kt_get_dc_info ( struct pw2kt_state * state )
{
ADS_STATUS status ;
LDAPMessage * res = NULL ;
int count ;
bool ok ;
TALLOC_CTX * tmp_ctx = talloc_stackframe ( ) ;
ADS_STRUCT * ads = ads_init (
tmp_ctx , lp_realm ( ) , lp_workgroup ( ) , NULL , ADS_SASL_SIGN ) ;
if ( ads = = NULL ) {
DBG_ERR ( " ads_init() failed \n " ) ;
TALLOC_FREE ( tmp_ctx ) ;
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
status = ads_connect_machine ( ads ) ;
if ( ! ADS_ERR_OK ( status ) ) {
DBG_ERR ( " Failed to refresh keytab, ads_connect() returned %s \n " ,
ads_errstr ( status ) ) ;
TALLOC_FREE ( tmp_ctx ) ;
return status ;
}
status = ads_find_machine_acct ( ads , & res , lp_netbios_name ( ) ) ;
if ( ! ADS_ERR_OK ( status ) ) {
TALLOC_FREE ( tmp_ctx ) ;
return status ;
}
count = ads_count_replies ( ads , res ) ;
if ( count ! = 1 ) {
status = ADS_ERROR ( LDAP_NO_SUCH_OBJECT ) ;
ads_msgfree ( ads , res ) ;
TALLOC_FREE ( tmp_ctx ) ;
return status ;
}
if ( state - > sync_etypes ) {
ok = ads_pull_uint32 ( ads ,
res ,
" msDS-SupportedEncryptionTypes " ,
& state - > ad_etypes ) ;
if ( ! ok ) {
DBG_WARNING ( " Failed to determine encryption types. \n " ) ;
ads_msgfree ( ads , res ) ;
TALLOC_FREE ( tmp_ctx ) ;
return ADS_ERROR_NT ( NT_STATUS_INTERNAL_ERROR ) ;
}
}
if ( state - > sync_kvno ) {
uint32_t kvno = - 1 ;
ok = ads_pull_uint32 ( ads , res , " msDS-KeyVersionNumber " , & kvno ) ;
if ( ! ok ) {
DBG_WARNING ( " Failed to determine the system's kvno. \n " ) ;
ads_msgfree ( ads , res ) ;
TALLOC_FREE ( tmp_ctx ) ;
return ADS_ERROR_NT ( NT_STATUS_INTERNAL_ERROR ) ;
}
state - > ad_kvno = ( krb5_kvno ) kvno ;
}
if ( state - > sync_spns ) {
state - > ad_spn_array = ads_pull_strings ( ads ,
state ,
res ,
" servicePrincipalName " ,
& state - > ad_num_spns ) ;
if ( state - > ad_spn_array = = NULL ) {
DBG_WARNING ( " Failed to determine SPNs. \n " ) ;
ads_msgfree ( ads , res ) ;
TALLOC_FREE ( tmp_ctx ) ;
return ADS_ERROR_NT ( NT_STATUS_INTERNAL_ERROR ) ;
}
}
ads_msgfree ( ads , res ) ;
TALLOC_FREE ( tmp_ctx ) ;
return status ;
}
static bool pw2kt_default_keytab_name ( char * name_str , size_t name_size )
{
char keytab_str [ MAX_KEYTAB_NAME_LEN ] = { 0 } ;
const char * keytab_name = NULL ;
krb5_context context = 0 ;
krb5_error_code ret ;
switch ( lp_kerberos_method ( ) ) {
case KERBEROS_VERIFY_SYSTEM_KEYTAB :
case KERBEROS_VERIFY_SECRETS_AND_KEYTAB :
ret = smb_krb5_init_context_common ( & context ) ;
if ( ret ) {
DBG_ERR ( " kerberos init context failed (%s) \n " ,
error_message ( ret ) ) ;
return false ;
}
ret = krb5_kt_default_name ( context ,
keytab_str ,
sizeof ( keytab_str ) - 2 ) ;
krb5_free_context ( context ) ;
if ( ret ! = 0 ) {
DBG_WARNING ( " Failed to get default keytab name \n " ) ;
return false ;
}
if ( strncmp ( keytab_str , " WRFILE: " , 7 ) = = 0 ) {
keytab_name = keytab_str + 7 ;
} else if ( strncmp ( keytab_str , " FILE: " , 5 ) = = 0 ) {
keytab_name = keytab_str + 5 ;
} else {
keytab_name = keytab_str ;
}
break ;
case KERBEROS_VERIFY_DEDICATED_KEYTAB :
keytab_name = lp_dedicated_keytab_file ( ) ;
break ;
default :
2024-08-16 17:37:32 +03:00
DBG_NOTICE ( " 'kerberos method' is 'secrets only' but "
" 'sync machine password to keytab' is not set "
" ==> no keytab will be generated. \n " ) ;
2021-09-06 17:58:17 +03:00
return false ;
}
if ( keytab_name = = NULL | | keytab_name [ 0 ] = = ' \0 ' ) {
DBG_ERR ( " Invalid keytab name \n " ) ;
return false ;
}
if ( strlen ( keytab_name ) + 1 > name_size ) {
DBG_ERR ( " Too long keytab name \n " ) ;
return false ;
}
( void ) strncpy ( name_str , keytab_name , name_size ) ;
return true ;
}
NTSTATUS sync_pw2keytabs ( void )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2024-02-28 15:30:30 +03:00
const struct loadparm_substitution * lp_sub =
loadparm_s3_global_substitution ( ) ;
2021-09-06 17:58:17 +03:00
struct pw2kt_state * state = NULL ;
const char * * line = NULL ;
const char * * lp_ptr = NULL ;
2024-02-28 15:30:30 +03:00
const char * pwsync_script = NULL ;
2021-09-06 17:58:17 +03:00
NTSTATUS status_nt ;
ADS_STATUS status_ads ;
int i ;
DBG_DEBUG ( " Syncing machine password from secrets to keytabs. \n " ) ;
if ( lp_server_role ( ) ! = ROLE_DOMAIN_MEMBER ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_OK ; /* nothing todo */
}
state = talloc_zero ( frame , struct pw2kt_state ) ;
if ( state = = NULL ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_NO_MEMORY ;
}
lp_ptr = lp_sync_machine_password_to_keytab ( ) ;
if ( lp_ptr = = NULL ) {
char name [ MAX_KEYTAB_NAME_LEN ] = { 0 } ;
bool ok = pw2kt_default_keytab_name ( name , sizeof ( name ) ) ;
if ( ! ok ) {
TALLOC_FREE ( frame ) ;
DBG_WARNING ( " No default keytab name. \n " ) ;
return NT_STATUS_OK ; /* nothing todo */
}
status_ads = pw2kt_default_cfg ( name , state ) ;
if ( ! ADS_ERR_OK ( status_ads ) ) {
DBG_WARNING ( " Cannot create default configuration. \n " ) ;
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
goto params_ready ;
}
2024-09-03 08:48:24 +03:00
if ( ( * lp_ptr ! = NULL ) & & strequal_m ( * lp_ptr , " disabled " ) ) {
DBG_DEBUG ( " 'sync machine password to keytab' is explicitly disabled. \n " ) ;
return NT_STATUS_OK ;
}
2021-09-06 17:58:17 +03:00
line = lp_ptr ;
while ( * line ) {
DBG_DEBUG ( " Scanning line: %s \n " , * line ) ;
status_ads = pw2kt_scan_line ( * line , state ) ;
if ( ! ADS_ERR_OK ( status_ads ) ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
line + + ;
}
params_ready :
if ( state - > sync_etypes | | state - > sync_kvno | | state - > sync_spns ) {
status_ads = pw2kt_get_dc_info ( state ) ;
if ( ! ADS_ERR_OK ( status_ads ) ) {
DBG_WARNING ( " cannot read from DC \n " ) ;
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
} else {
DBG_DEBUG ( " No 'sync_etypes', 'sync_kvno' and 'sync_spns' in "
" parameter 'sync machine password to keytab' => "
" no need to talk to DC. \n " ) ;
}
if ( ! secrets_init ( ) ) {
DBG_WARNING ( " secrets_init failed \n " ) ;
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
status_nt = secrets_fetch_or_upgrade_domain_info ( lp_workgroup ( ) ,
frame ,
& state - > info ) ;
if ( ! NT_STATUS_IS_OK ( status_nt ) ) {
DBG_WARNING ( " secrets_fetch_or_upgrade_domain_info(%s) - %s \n " ,
lp_workgroup ( ) ,
nt_errstr ( status_nt ) ) ;
TALLOC_FREE ( frame ) ;
return status_nt ;
}
for ( i = 0 ; i < state - > num_keytabs ; i + + ) {
status_ads = pw2kt_process_keytab ( state , & state - > keytabs [ i ] ) ;
if ( ! ADS_ERR_OK ( status_ads ) ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
}
2024-02-28 15:30:30 +03:00
pwsync_script = lp_sync_machine_password_script ( frame , lp_sub ) ;
if ( pwsync_script ! = NULL & & pwsync_script [ 0 ] ! = ' \0 ' ) {
int ret ;
DBG_DEBUG ( " Running script: '%s' \n . " , pwsync_script ) ;
ret = smbrun ( pwsync_script , NULL , NULL ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Script '%s' failed with: %d. \n " ,
pwsync_script ,
ret ) ;
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
}
2021-09-06 17:58:17 +03:00
TALLOC_FREE ( frame ) ;
return NT_STATUS_OK ;
}
2017-03-13 18:24:52 +03:00
static krb5_error_code ads_keytab_open ( krb5_context context ,
krb5_keytab * keytab )
{
char keytab_str [ MAX_KEYTAB_NAME_LEN ] = { 0 } ;
const char * keytab_name = NULL ;
krb5_error_code ret = 0 ;
switch ( lp_kerberos_method ( ) ) {
case KERBEROS_VERIFY_SYSTEM_KEYTAB :
case KERBEROS_VERIFY_SECRETS_AND_KEYTAB :
ret = krb5_kt_default_name ( context ,
keytab_str ,
sizeof ( keytab_str ) - 2 ) ;
if ( ret ! = 0 ) {
2023-08-07 07:36:27 +03:00
DBG_WARNING ( " Failed to get default keytab name \n " ) ;
2017-03-13 18:24:52 +03:00
goto out ;
}
keytab_name = keytab_str ;
break ;
case KERBEROS_VERIFY_DEDICATED_KEYTAB :
keytab_name = lp_dedicated_keytab_file ( ) ;
break ;
default :
DBG_ERR ( " Invalid kerberos method set (%d) \n " ,
lp_kerberos_method ( ) ) ;
ret = KRB5_KT_BADNAME ;
goto out ;
}
if ( keytab_name = = NULL | | keytab_name [ 0 ] = = ' \0 ' ) {
DBG_ERR ( " Invalid keytab name \n " ) ;
ret = KRB5_KT_BADNAME ;
goto out ;
}
ret = smb_krb5_kt_open ( context , keytab_name , true , keytab ) ;
if ( ret ! = 0 ) {
DBG_WARNING ( " smb_krb5_kt_open failed (%s) \n " ,
error_message ( ret ) ) ;
goto out ;
}
out :
return ret ;
}
2004-06-18 04:24:53 +04:00
/**********************************************************************
2004-06-19 03:15:42 +04:00
Flushes all entries from the system keytab .
2004-06-18 04:24:53 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-06-18 01:39:51 +04:00
int ads_keytab_flush ( ADS_STRUCT * ads )
{
2004-06-18 06:07:42 +04:00
krb5_error_code ret = 0 ;
krb5_context context = NULL ;
krb5_keytab keytab = NULL ;
2010-08-18 12:16:41 +04:00
ADS_STATUS aderr ;
2004-06-24 23:48:52 +04:00
2018-12-05 13:16:42 +03:00
ret = smb_krb5_init_context_common ( & context ) ;
2004-06-18 01:39:51 +04:00
if ( ret ) {
2018-12-05 13:16:42 +03:00
DBG_ERR ( " kerberos init context failed (%s) \n " ,
error_message ( ret ) ) ;
2004-06-18 01:39:51 +04:00
return ret ;
}
2007-06-29 12:56:35 +04:00
2017-03-13 18:24:52 +03:00
ret = ads_keytab_open ( context , & keytab ) ;
if ( ret ! = 0 ) {
2004-06-18 01:39:51 +04:00
goto out ;
}
2022-10-26 12:02:21 +03:00
/* Seek and delete all old keytab entries */
2016-02-29 19:31:56 +03:00
ret = smb_krb5_kt_seek_and_delete_old_entries ( context ,
keytab ,
2022-10-26 12:03:34 +03:00
false , /* keep_old_kvno */
2022-10-26 12:02:21 +03:00
- 1 ,
2022-10-26 12:03:34 +03:00
false , /* enctype_only */
2016-04-26 16:45:17 +03:00
ENCTYPE_NULL ,
2016-02-29 19:31:56 +03:00
NULL ,
NULL ,
2022-10-26 11:34:47 +03:00
true ) ; /* flush */
2010-08-18 12:16:41 +04:00
if ( ret ) {
goto out ;
2004-06-18 01:39:51 +04:00
}
2004-06-19 03:15:42 +04:00
2011-06-09 09:31:03 +04:00
aderr = ads_clear_service_principal_names ( ads , lp_netbios_name ( ) ) ;
2010-08-18 12:16:41 +04:00
if ( ! ADS_ERR_OK ( aderr ) ) {
DEBUG ( 1 , ( __location__ " : Error while clearing service "
" principal listings in LDAP. \n " ) ) ;
2018-11-21 12:59:31 +03:00
ret = - 1 ;
2004-06-18 01:39:51 +04:00
goto out ;
}
out :
2004-06-18 06:07:42 +04:00
if ( keytab ) {
krb5_kt_close ( context , keytab ) ;
}
if ( context ) {
krb5_free_context ( context ) ;
}
2004-06-18 01:39:51 +04:00
return ret ;
}
2011-04-15 14:37:55 +04:00
# endif /* HAVE_ADS */
2007-04-23 12:40:54 +04:00
/**********************************************************************
List system keytab .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-06-29 13:01:29 +04:00
int ads_keytab_list ( const char * keytab_name )
2007-04-23 12:40:54 +04:00
{
krb5_error_code ret = 0 ;
krb5_context context = NULL ;
krb5_keytab keytab = NULL ;
krb5_kt_cursor cursor ;
krb5_keytab_entry kt_entry ;
ZERO_STRUCT ( kt_entry ) ;
ZERO_STRUCT ( cursor ) ;
2018-12-05 13:16:42 +03:00
ret = smb_krb5_init_context_common ( & context ) ;
2007-04-23 12:40:54 +04:00
if ( ret ) {
2018-12-05 13:16:42 +03:00
DBG_ERR ( " kerberos init context failed (%s) \n " ,
error_message ( ret ) ) ;
2007-04-23 12:40:54 +04:00
return ret ;
}
2007-06-29 12:56:35 +04:00
2017-11-23 18:55:21 +03:00
if ( keytab_name = = NULL ) {
2018-02-03 09:07:24 +03:00
# ifdef HAVE_ADS
2017-11-23 18:55:21 +03:00
ret = ads_keytab_open ( context , & keytab ) ;
2018-02-03 09:07:24 +03:00
# else
ret = ENOENT ;
# endif
2017-11-23 18:55:21 +03:00
} else {
ret = smb_krb5_kt_open ( context , keytab_name , False , & keytab ) ;
}
2007-04-23 12:40:54 +04:00
if ( ret ) {
2016-08-29 12:03:51 +03:00
DEBUG ( 1 , ( " smb_krb5_kt_open failed (%s) \n " ,
2010-08-18 14:46:53 +04:00
error_message ( ret ) ) ) ;
2007-04-23 12:40:54 +04:00
goto out ;
}
ret = krb5_kt_start_seq_get ( context , keytab , & cursor ) ;
if ( ret ) {
2010-08-31 16:27:56 +04:00
ZERO_STRUCT ( cursor ) ;
2007-04-23 12:40:54 +04:00
goto out ;
}
2012-01-06 20:48:58 +04:00
printf ( " Vno Type Principal \n " ) ;
2007-04-23 12:40:54 +04:00
2023-12-07 19:49:07 +03:00
while ( samba_krb5_kt_next_entry ( context , keytab , & kt_entry , & cursor ) = =
0 )
{
2010-08-18 14:46:53 +04:00
2007-04-23 12:40:54 +04:00
char * princ_s = NULL ;
char * etype_s = NULL ;
krb5_enctype enctype = 0 ;
2010-08-18 14:46:53 +04:00
ret = smb_krb5_unparse_name ( talloc_tos ( ) , context ,
kt_entry . principal , & princ_s ) ;
2007-04-23 12:40:54 +04:00
if ( ret ) {
goto out ;
}
2016-08-29 10:17:37 +03:00
enctype = smb_krb5_kt_get_enctype_from_entry ( & kt_entry ) ;
2007-04-23 12:40:54 +04:00
ret = smb_krb5_enctype_to_string ( context , enctype , & etype_s ) ;
2010-08-18 14:46:53 +04:00
if ( ret & &
2021-07-02 10:14:18 +03:00
( asprintf ( & etype_s , " UNKNOWN: %d " , enctype ) = = - 1 ) ) {
2010-08-18 14:46:53 +04:00
TALLOC_FREE ( princ_s ) ;
goto out ;
2007-04-23 12:40:54 +04:00
}
2012-01-06 20:48:58 +04:00
printf ( " %3d %-43s %s \n " , kt_entry . vno , etype_s , princ_s ) ;
2007-04-23 12:40:54 +04:00
2009-03-18 08:23:27 +03:00
TALLOC_FREE ( princ_s ) ;
2007-04-23 12:40:54 +04:00
SAFE_FREE ( etype_s ) ;
ret = smb_krb5_kt_free_entry ( context , & kt_entry ) ;
if ( ret ) {
goto out ;
}
}
ret = krb5_kt_end_seq_get ( context , keytab , & cursor ) ;
if ( ret ) {
goto out ;
}
/* Ensure we don't double free. */
ZERO_STRUCT ( kt_entry ) ;
ZERO_STRUCT ( cursor ) ;
out :
2016-12-31 15:45:51 +03:00
if ( ! all_zero ( ( uint8_t * ) & kt_entry , sizeof ( kt_entry ) ) ) {
smb_krb5_kt_free_entry ( context , & kt_entry ) ;
2007-04-23 12:40:54 +04:00
}
2016-12-31 15:45:51 +03:00
if ( ! all_zero ( ( uint8_t * ) & cursor , sizeof ( cursor ) ) & & keytab ) {
krb5_kt_end_seq_get ( context , keytab , & cursor ) ;
2007-04-23 12:40:54 +04:00
}
if ( keytab ) {
krb5_kt_close ( context , keytab ) ;
}
if ( context ) {
krb5_free_context ( context ) ;
}
return ret ;
}
2004-06-18 01:39:51 +04:00
# endif /* HAVE_KRB5 */