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"
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
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 ;
}
2018-02-16 19:52:01 +03:00
static bool fill_default_spns ( TALLOC_CTX * ctx , const char * machine_name ,
const char * my_fqdn , const char * spn ,
const char * * * spns )
{
char * psp1 , * psp2 ;
if ( * spns = = NULL ) {
* spns = talloc_zero_array ( ctx , const char * , 3 ) ;
2018-03-28 01:00:46 +03:00
if ( * spns = = NULL ) {
2018-02-16 19:52:01 +03:00
return false ;
}
}
psp1 = talloc_asprintf ( ctx ,
" %s/%s " ,
spn ,
machine_name ) ;
if ( psp1 = = NULL ) {
return false ;
}
if ( ! strlower_m ( & psp1 [ strlen ( spn ) + 1 ] ) ) {
return false ;
}
( * spns ) [ 0 ] = psp1 ;
psp2 = talloc_asprintf ( ctx ,
" %s/%s " ,
spn ,
my_fqdn ) ;
if ( psp2 = = NULL ) {
return false ;
}
if ( ! strlower_m ( & psp2 [ strlen ( spn ) + 1 ] ) ) {
return false ;
}
( * spns ) [ 1 ] = psp2 ;
return true ;
}
static bool ads_set_machine_account_spns ( TALLOC_CTX * ctx ,
ADS_STRUCT * ads ,
const char * service_or_spn ,
const char * my_fqdn )
{
const char * * spn_names = NULL ;
ADS_STATUS aderr ;
2018-01-29 21:38:05 +03:00
struct spn_struct * spn_struct = NULL ;
char * tmp = NULL ;
/* SPN should have '/' */
tmp = strchr_m ( service_or_spn , ' / ' ) ;
if ( tmp ! = NULL ) {
spn_struct = parse_spn ( ctx , service_or_spn ) ;
if ( spn_struct = = NULL ) {
return false ;
}
}
2018-02-16 19:52:01 +03:00
DBG_INFO ( " Attempting to add/update '%s' \n " , service_or_spn ) ;
2018-01-29 21:38:05 +03:00
if ( spn_struct ! = NULL ) {
spn_names = talloc_zero_array ( ctx , const char * , 2 ) ;
spn_names [ 0 ] = service_or_spn ;
} else {
bool ok ;
ok = fill_default_spns ( ctx ,
lp_netbios_name ( ) ,
my_fqdn ,
service_or_spn ,
& spn_names ) ;
if ( ! ok ) {
return false ;
}
2018-02-16 19:52:01 +03:00
}
aderr = ads_add_service_principal_names ( ads ,
lp_netbios_name ( ) ,
spn_names ) ;
if ( ! ADS_ERR_OK ( aderr ) ) {
DBG_WARNING ( " Failed to add service principal name. \n " ) ;
return false ;
}
return true ;
}
2018-01-29 21:30:33 +03:00
/*
* Create kerberos principal ( s ) from SPN or service name .
*/
static bool service_or_spn_to_kerberos_princ ( TALLOC_CTX * ctx ,
const char * service_or_spn ,
const char * my_fqdn ,
char * * p_princ_s ,
char * * p_short_princ_s )
{
char * princ_s = NULL ;
char * short_princ_s = NULL ;
const char * service = service_or_spn ;
const char * host = my_fqdn ;
struct spn_struct * spn_struct = NULL ;
char * tmp = NULL ;
bool ok = true ;
/* SPN should have '/' */
tmp = strchr_m ( service_or_spn , ' / ' ) ;
if ( tmp ! = NULL ) {
spn_struct = parse_spn ( ctx , service_or_spn ) ;
if ( spn_struct = = NULL ) {
ok = false ;
goto out ;
}
}
if ( spn_struct ! = NULL ) {
service = spn_struct - > serviceclass ;
host = spn_struct - > host ;
}
princ_s = talloc_asprintf ( ctx , " %s/%s@%s " ,
service ,
host , lp_realm ( ) ) ;
if ( princ_s = = NULL ) {
ok = false ;
goto out ;
}
if ( spn_struct = = NULL ) {
short_princ_s = talloc_asprintf ( ctx , " %s/%s@%s " ,
service , lp_netbios_name ( ) ,
lp_realm ( ) ) ;
if ( short_princ_s = = NULL ) {
ok = false ;
goto out ;
}
}
* p_princ_s = princ_s ;
* p_short_princ_s = short_princ_s ;
out :
return ok ;
}
2020-05-27 14:25:17 +03:00
static int add_kt_entry_etypes ( krb5_context context , TALLOC_CTX * tmpctx ,
ADS_STRUCT * ads , const char * salt_princ_s ,
krb5_keytab keytab , krb5_kvno kvno ,
const char * srvPrinc , const char * my_fqdn ,
krb5_data * password , bool update_ads )
2006-07-11 22:45:22 +04:00
{
krb5_error_code ret = 0 ;
2020-05-27 14:25:17 +03:00
char * princ_s = NULL ;
char * short_princ_s = NULL ;
krb5_enctype enctypes [ 4 ] = {
2011-12-15 21:12:41 +04:00
ENCTYPE_AES256_CTS_HMAC_SHA1_96 ,
2019-11-29 15:48:24 +03:00
ENCTYPE_AES128_CTS_HMAC_SHA1_96 ,
2010-08-18 12:33:32 +04:00
ENCTYPE_ARCFOUR_HMAC ,
0
} ;
2020-05-27 14:25:17 +03:00
size_t i ;
2006-07-11 22:45:22 +04:00
2010-08-18 12:33:32 +04:00
/* Construct our principal */
2006-07-11 22:45:22 +04:00
if ( strchr_m ( srvPrinc , ' @ ' ) ) {
/* It's a fully-named principal. */
2010-08-18 12:33:32 +04:00
princ_s = talloc_asprintf ( tmpctx , " %s " , srvPrinc ) ;
if ( ! princ_s ) {
2008-12-23 22:56:48 +03:00
ret = - 1 ;
goto out ;
}
2006-07-11 22:45:22 +04:00
} else if ( srvPrinc [ strlen ( srvPrinc ) - 1 ] = = ' $ ' ) {
/* It's the machine account, as used by smbclient clients. */
2010-08-18 12:33:32 +04:00
princ_s = talloc_asprintf ( tmpctx , " %s@%s " ,
srvPrinc , lp_realm ( ) ) ;
if ( ! princ_s ) {
2008-12-23 22:56:48 +03:00
ret = - 1 ;
goto out ;
}
2006-07-11 22:45:22 +04:00
} else {
/* It's a normal service principal. Add the SPN now so that we
* can obtain credentials for it and double - check the salt value
* used to generate the service ' s keys . */
2010-08-18 12:33:32 +04:00
2018-01-29 21:30:33 +03:00
if ( ! service_or_spn_to_kerberos_princ ( tmpctx ,
srvPrinc ,
my_fqdn ,
& princ_s ,
& short_princ_s ) ) {
2008-12-23 22:56:48 +03:00
ret = - 1 ;
goto out ;
}
2010-08-18 12:33:32 +04:00
/* According to http://support.microsoft.com/kb/326985/en-us,
certain principal names are automatically mapped to the
host / . . . principal in the AD account .
So only create these in the keytab , not in AD . - - jerry */
2018-02-08 20:33:08 +03:00
if ( update_ads & & ! strequal ( srvPrinc , " cifs " ) & &
2010-08-18 12:33:32 +04:00
! strequal ( srvPrinc , " host " ) ) {
2018-02-16 19:52:01 +03:00
if ( ! ads_set_machine_account_spns ( tmpctx ,
ads ,
srvPrinc ,
my_fqdn ) ) {
ret = - 1 ;
2006-07-11 22:45:22 +04:00
goto out ;
}
}
}
2016-02-29 18:21:56 +03:00
for ( i = 0 ; enctypes [ i ] ; i + + ) {
/* add the fqdn principal to the keytab */
ret = smb_krb5_kt_add_entry ( context ,
keytab ,
kvno ,
princ_s ,
salt_princ_s ,
enctypes [ i ] ,
2020-05-27 14:25:17 +03:00
password ,
2022-10-26 11:34:47 +03:00
false ) ; /* no_salt */
2010-08-18 12:33:32 +04:00
if ( ret ) {
2020-05-27 14:25:17 +03:00
DBG_WARNING ( " Failed to add entry to keytab \n " ) ;
2006-07-11 22:45:22 +04:00
goto out ;
}
2016-02-29 18:21:56 +03:00
/* add the short principal name if we have one */
if ( short_princ_s ) {
ret = smb_krb5_kt_add_entry ( context ,
keytab ,
kvno ,
short_princ_s ,
salt_princ_s ,
enctypes [ i ] ,
2020-05-27 14:25:17 +03:00
password ,
2022-10-26 11:34:47 +03:00
false ) ; /* no_salt */
2016-02-29 18:21:56 +03:00
if ( ret ) {
2020-05-27 14:25:17 +03:00
DBG_WARNING ( " Failed to add short entry to keytab \n " ) ;
2016-02-29 18:21:56 +03:00
goto out ;
}
}
2006-07-11 22:45:22 +04:00
}
2020-05-27 14:25:17 +03:00
out :
return ret ;
}
/**********************************************************************
Adds a single service principal , i . e . ' host ' to the system keytab
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int ads_keytab_add_entry ( ADS_STRUCT * ads , const char * srvPrinc , bool update_ads )
{
krb5_error_code ret = 0 ;
krb5_context context = NULL ;
krb5_keytab keytab = NULL ;
krb5_data password ;
krb5_kvno kvno ;
char * salt_princ_s = NULL ;
char * password_s = NULL ;
char * my_fqdn ;
TALLOC_CTX * tmpctx = NULL ;
2020-05-27 16:36:28 +03:00
char * * hostnames_array = NULL ;
size_t num_hostnames = 0 ;
2020-05-27 14:25:17 +03:00
ret = smb_krb5_init_context_common ( & context ) ;
if ( ret ) {
DBG_ERR ( " kerberos init context failed (%s) \n " ,
error_message ( ret ) ) ;
return - 1 ;
}
ret = ads_keytab_open ( context , & keytab ) ;
if ( ret ! = 0 ) {
goto out ;
}
/* retrieve the password */
if ( ! secrets_init ( ) ) {
DBG_WARNING ( " secrets_init failed \n " ) ;
ret = - 1 ;
goto out ;
}
password_s = secrets_fetch_machine_password ( lp_workgroup ( ) , NULL , NULL ) ;
if ( ! password_s ) {
DBG_WARNING ( " failed to fetch machine password \n " ) ;
ret = - 1 ;
goto out ;
}
ZERO_STRUCT ( password ) ;
password . data = password_s ;
password . length = strlen ( password_s ) ;
/* we need the dNSHostName value here */
tmpctx = talloc_init ( __location__ ) ;
if ( ! tmpctx ) {
DBG_ERR ( " talloc_init() failed! \n " ) ;
ret = - 1 ;
goto out ;
}
my_fqdn = ads_get_dnshostname ( ads , tmpctx , lp_netbios_name ( ) ) ;
if ( ! my_fqdn ) {
DBG_ERR ( " unable to determine machine account's dns name in "
" AD! \n " ) ;
ret = - 1 ;
goto out ;
}
/* make sure we have a single instance of a the computer account */
if ( ! ads_has_samaccountname ( ads , tmpctx , lp_netbios_name ( ) ) ) {
DBG_ERR ( " unable to determine machine account's short name in "
" AD! \n " ) ;
ret = - 1 ;
goto out ;
}
kvno = ( krb5_kvno ) ads_get_machine_kvno ( ads , lp_netbios_name ( ) ) ;
if ( kvno = = - 1 ) {
/* -1 indicates failure, everything else is OK */
DBG_WARNING ( " ads_get_machine_kvno failed to determine the "
" system's kvno. \n " ) ;
ret = - 1 ;
goto out ;
}
salt_princ_s = kerberos_secrets_fetch_salt_princ ( ) ;
if ( salt_princ_s = = NULL ) {
DBG_WARNING ( " kerberos_secrets_fetch_salt_princ() failed \n " ) ;
ret = - 1 ;
goto out ;
}
ret = add_kt_entry_etypes ( context , tmpctx , ads , salt_princ_s , keytab ,
kvno , srvPrinc , my_fqdn , & password ,
update_ads ) ;
if ( ret ! = 0 ) {
goto out ;
}
2006-07-11 22:45:22 +04:00
2020-05-27 16:36:28 +03:00
if ( ADS_ERR_OK ( ads_get_additional_dns_hostnames ( tmpctx , ads ,
lp_netbios_name ( ) ,
& hostnames_array ,
& num_hostnames ) ) ) {
size_t i ;
for ( i = 0 ; i < num_hostnames ; i + + ) {
ret = add_kt_entry_etypes ( context , tmpctx , ads ,
salt_princ_s , keytab ,
kvno , srvPrinc ,
hostnames_array [ i ] ,
& password , update_ads ) ;
if ( ret ! = 0 ) {
goto out ;
}
}
}
2006-07-11 22:45:22 +04:00
out :
2017-05-19 18:08:24 +03:00
SAFE_FREE ( salt_princ_s ) ;
2010-08-18 12:33:32 +04:00
TALLOC_FREE ( tmpctx ) ;
2004-06-18 03:07:20 +04:00
if ( keytab ) {
krb5_kt_close ( context , keytab ) ;
2004-06-18 01:39:51 +04:00
}
2004-06-18 03:07:20 +04:00
if ( context ) {
krb5_free_context ( context ) ;
2004-06-18 01:39:51 +04:00
}
2004-06-18 03:07:20 +04:00
return ( int ) ret ;
2004-06-18 01:39:51 +04:00
}
2022-10-26 12:36:01 +03:00
/**********************************************************************
Delete a single service principal , i . e . ' host ' from the system keytab
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int ads_keytab_delete_entry ( ADS_STRUCT * ads , const char * srvPrinc )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
krb5_error_code ret = 0 ;
krb5_context context = NULL ;
krb5_keytab keytab = NULL ;
char * princ_s = NULL ;
krb5_principal princ = NULL ;
char * short_princ_s = NULL ;
krb5_principal short_princ = NULL ;
bool ok ;
ret = smb_krb5_init_context_common ( & context ) ;
if ( ret ) {
DBG_ERR ( " kerberos init context failed (%s) \n " ,
error_message ( ret ) ) ;
goto out ;
}
ret = ads_keytab_open ( context , & keytab ) ;
if ( ret ! = 0 ) {
goto out ;
}
/* Construct our principal */
if ( strchr_m ( srvPrinc , ' @ ' ) ) {
/* It's a fully-named principal. */
princ_s = talloc_asprintf ( frame , " %s " , srvPrinc ) ;
if ( ! princ_s ) {
ret = - 1 ;
goto out ;
}
} else if ( srvPrinc [ strlen ( srvPrinc ) - 1 ] = = ' $ ' ) {
/* It's the machine account, as used by smbclient clients. */
princ_s = talloc_asprintf ( frame , " %s@%s " ,
srvPrinc , lp_realm ( ) ) ;
if ( ! princ_s ) {
ret = - 1 ;
goto out ;
}
} else {
/*
* It ' s a normal service principal .
*/
char * my_fqdn = NULL ;
char * tmp = NULL ;
/*
* SPN should have ' / ' otherwise we
* need to fallback and find our dnshostname
*/
tmp = strchr_m ( srvPrinc , ' / ' ) ;
if ( tmp = = NULL ) {
my_fqdn = ads_get_dnshostname ( ads , frame , lp_netbios_name ( ) ) ;
if ( ! my_fqdn ) {
DBG_ERR ( " unable to determine machine account's dns name in "
" AD! \n " ) ;
ret = - 1 ;
goto out ;
}
}
ok = service_or_spn_to_kerberos_princ ( frame ,
srvPrinc ,
my_fqdn ,
& princ_s ,
& short_princ_s ) ;
if ( ! ok ) {
ret = - 1 ;
goto out ;
}
}
ret = smb_krb5_parse_name ( context , princ_s , & princ ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : smb_krb5_parse_name(%s) "
" failed (%s) \n " , princ_s , error_message ( ret ) ) ) ;
goto out ;
}
if ( short_princ_s ! = NULL ) {
ret = smb_krb5_parse_name ( context , short_princ_s , & short_princ ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : smb_krb5_parse_name(%s) "
" failed (%s) \n " , short_princ_s , error_message ( ret ) ) ) ;
goto out ;
}
}
/* Seek and delete old keytab entries */
ret = smb_krb5_kt_seek_and_delete_old_entries ( context ,
keytab ,
false , /* keep_old_kvno */
- 1 ,
false , /* enctype_only */
ENCTYPE_NULL ,
princ_s ,
princ ,
false ) ; /* flush */
if ( ret ) {
goto out ;
}
if ( short_princ_s = = NULL ) {
goto out ;
}
/* Seek and delete old keytab entries */
ret = smb_krb5_kt_seek_and_delete_old_entries ( context ,
keytab ,
false , /* keep_old_kvno */
- 1 ,
false , /* enctype_only */
ENCTYPE_NULL ,
short_princ_s ,
short_princ ,
false ) ; /* flush */
if ( ret ) {
goto out ;
}
out :
if ( princ ) {
krb5_free_principal ( context , princ ) ;
}
if ( short_princ ) {
krb5_free_principal ( context , short_princ ) ;
}
if ( keytab ) {
krb5_kt_close ( context , keytab ) ;
}
if ( context ) {
krb5_free_context ( context ) ;
}
TALLOC_FREE ( frame ) ;
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 ;
}
2004-06-19 03:15:42 +04:00
/**********************************************************************
Adds all the required service principals to the system keytab .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-06-18 01:39:51 +04:00
int ads_keytab_create_default ( ADS_STRUCT * ads )
{
2004-06-19 03:15:42 +04:00
krb5_error_code ret = 0 ;
krb5_context context = NULL ;
krb5_keytab keytab = NULL ;
2015-03-04 12:09:51 +03:00
krb5_kt_cursor cursor = { 0 } ;
2015-03-04 12:09:18 +03:00
krb5_keytab_entry kt_entry = { 0 } ;
2004-06-18 01:39:51 +04:00
krb5_kvno kvno ;
2014-09-24 12:51:33 +04:00
size_t found = 0 ;
2006-07-11 22:45:22 +04:00
char * sam_account_name , * upn ;
2005-03-11 23:59:16 +03:00
char * * oldEntries = NULL , * princ_s [ 26 ] ;
2014-09-24 12:51:33 +04:00
TALLOC_CTX * frame ;
2010-08-18 14:09:27 +04:00
char * machine_name ;
2014-09-24 12:51:33 +04:00
char * * spn_array ;
size_t num_spns ;
size_t i ;
2018-01-12 17:22:34 +03:00
bool ok = false ;
2014-09-24 12:51:33 +04:00
ADS_STATUS status ;
2006-07-11 22:45:22 +04:00
2015-05-02 13:44:52 +03:00
ZERO_STRUCT ( kt_entry ) ;
ZERO_STRUCT ( cursor ) ;
2014-09-24 12:51:33 +04:00
frame = talloc_stackframe ( ) ;
if ( frame = = NULL ) {
ret = - 1 ;
goto done ;
}
status = ads_get_service_principal_names ( frame ,
ads ,
lp_netbios_name ( ) ,
& spn_array ,
& num_spns ) ;
if ( ! ADS_ERR_OK ( status ) ) {
ret = - 1 ;
goto done ;
2004-06-18 01:39:51 +04:00
}
2006-07-11 22:45:22 +04:00
2014-09-24 12:51:33 +04:00
for ( i = 0 ; i < num_spns ; i + + ) {
char * srv_princ ;
char * p ;
srv_princ = strlower_talloc ( frame , spn_array [ i ] ) ;
if ( srv_princ = = NULL ) {
ret = - 1 ;
goto done ;
}
p = strchr_m ( srv_princ , ' / ' ) ;
if ( p = = NULL ) {
continue ;
}
p [ 0 ] = ' \0 ' ;
/* Add the SPNs found on the DC */
2018-02-09 17:07:27 +03:00
ret = ads_keytab_add_entry ( ads , srv_princ , false ) ;
2014-09-24 12:51:33 +04:00
if ( ret ! = 0 ) {
DEBUG ( 1 , ( " ads_keytab_add_entry failed while "
" adding '%s' principal. \n " ,
spn_array [ i ] ) ) ;
goto done ;
}
}
2006-07-11 22:45:22 +04:00
2010-08-18 14:09:27 +04:00
#if 0 /* don't create the CIFS/... keytab entries since no one except smbd
really needs them and we will fall back to verifying against
secrets . tdb */
2018-02-09 17:07:27 +03:00
ret = ads_keytab_add_entry ( ads , " cifs " , false ) ) ;
2010-08-18 14:09:27 +04:00
if ( ret ! = 0 ) {
DEBUG ( 1 , ( __location__ " : ads_keytab_add_entry failed while "
" adding 'cifs'. \n " ) ) ;
2004-06-18 01:39:51 +04:00
return ret ;
}
2006-07-11 22:45:22 +04:00
# endif
2004-06-18 01:39:51 +04:00
2010-08-18 14:09:27 +04:00
memset ( princ_s , ' \0 ' , sizeof ( princ_s ) ) ;
2018-12-05 13:16:42 +03:00
ret = smb_krb5_init_context_common ( & context ) ;
2010-08-18 14:09:27 +04:00
if ( ret ) {
2018-12-05 13:16:42 +03:00
DBG_ERR ( " kerberos init context failed (%s) \n " ,
error_message ( ret ) ) ;
2010-08-18 14:09:27 +04:00
goto done ;
}
2007-01-03 00:29:09 +03:00
2014-09-24 12:51:33 +04:00
machine_name = talloc_strdup ( frame , lp_netbios_name ( ) ) ;
2010-08-18 14:09:27 +04:00
if ( ! machine_name ) {
ret = - 1 ;
goto done ;
2006-07-11 22:45:22 +04:00
}
2010-08-18 14:09:27 +04:00
/* now add the userPrincipalName and sAMAccountName entries */
2018-01-12 17:22:34 +03:00
ok = ads_has_samaccountname ( ads , frame , machine_name ) ;
if ( ! ok ) {
2010-08-18 14:09:27 +04:00
DEBUG ( 0 , ( __location__ " : unable to determine machine "
" account's name in AD! \n " ) ) ;
ret = - 1 ;
goto done ;
}
2007-01-03 00:29:09 +03:00
2018-01-12 17:22:34 +03:00
/*
* append ' $ ' to netbios name so ' ads_keytab_add_entry ' recognises
* it as a machine account rather than a service or Windows SPN .
*/
sam_account_name = talloc_asprintf ( frame , " %s$ " , machine_name ) ;
if ( sam_account_name = = NULL ) {
ret = - 1 ;
goto done ;
}
2010-08-18 14:09:27 +04:00
/* upper case the sAMAccountName to make it easier for apps to
know what case to use in the keytab file */
2012-08-09 02:35:28 +04:00
if ( ! strupper_m ( sam_account_name ) ) {
ret = - 1 ;
goto done ;
}
2007-01-03 00:29:09 +03:00
2018-02-09 17:07:27 +03:00
ret = ads_keytab_add_entry ( ads , sam_account_name , false ) ;
2010-08-18 14:09:27 +04:00
if ( ret ! = 0 ) {
DEBUG ( 1 , ( __location__ " : ads_keytab_add_entry() failed "
" while adding sAMAccountName (%s) \n " ,
sam_account_name ) ) ;
goto done ;
2006-07-11 22:45:22 +04:00
}
2010-08-18 14:09:27 +04:00
2006-07-11 22:45:22 +04:00
/* remember that not every machine account will have a upn */
2014-09-24 12:51:33 +04:00
upn = ads_get_upn ( ads , frame , machine_name ) ;
2010-08-18 14:09:27 +04:00
if ( upn ) {
2018-02-09 17:07:27 +03:00
ret = ads_keytab_add_entry ( ads , upn , false ) ;
2010-08-18 14:09:27 +04:00
if ( ret ! = 0 ) {
DEBUG ( 1 , ( __location__ " : ads_keytab_add_entry() "
" failed while adding UPN (%s) \n " , upn ) ) ;
goto done ;
2004-10-30 04:34:58 +04:00
}
}
2010-08-18 14:09:27 +04:00
/* Now loop through the keytab and update any other existing entries */
kvno = ( krb5_kvno ) ads_get_machine_kvno ( ads , machine_name ) ;
2014-09-24 12:51:33 +04:00
if ( kvno = = ( krb5_kvno ) - 1 ) {
2010-08-18 14:09:27 +04:00
DEBUG ( 1 , ( __location__ " : ads_get_machine_kvno() failed to "
" determine the system's kvno. \n " ) ) ;
goto done ;
2004-06-18 01:39:51 +04:00
}
2004-06-19 03:15:42 +04:00
2010-08-18 14:09:27 +04:00
DEBUG ( 3 , ( __location__ " : Searching for keytab entries to preserve "
" and update. \n " ) ) ;
2007-06-29 13:54:39 +04:00
2017-03-13 18:24:52 +03:00
ret = ads_keytab_open ( context , & keytab ) ;
if ( ret ! = 0 ) {
2007-06-29 13:58:11 +04:00
goto done ;
2004-06-18 01:39:51 +04:00
}
ret = krb5_kt_start_seq_get ( context , keytab , & cursor ) ;
if ( ret ! = KRB5_KT_END & & ret ! = ENOENT ) {
2010-08-18 14:09:27 +04:00
while ( ( ret = krb5_kt_next_entry ( context , keytab ,
& kt_entry , & cursor ) ) = = 0 ) {
2004-06-24 09:56:44 +04:00
smb_krb5_kt_free_entry ( context , & kt_entry ) ;
2004-06-19 03:15:42 +04:00
ZERO_STRUCT ( kt_entry ) ;
2004-06-18 01:39:51 +04:00
found + + ;
}
}
2004-06-19 03:15:42 +04:00
krb5_kt_end_seq_get ( context , keytab , & cursor ) ;
2004-06-24 23:48:52 +04:00
ZERO_STRUCT ( cursor ) ;
2004-06-18 01:39:51 +04:00
2004-06-19 03:15:42 +04:00
/*
2010-08-18 14:09:27 +04:00
* Hmmm . There is no " rewind " function for the keytab . This means we
* have a race condition where someone else could add entries after
* we ' ve counted them . Re - open asap to minimise the race . JRA .
2004-06-19 03:15:42 +04:00
*/
2014-09-24 12:51:33 +04:00
DEBUG ( 3 , ( __location__ " : Found %zd entries in the keytab. \n " , found ) ) ;
2004-06-18 01:39:51 +04:00
if ( ! found ) {
goto done ;
}
2010-08-18 14:09:27 +04:00
2014-11-12 19:21:05 +03:00
oldEntries = talloc_zero_array ( frame , char * , found + 1 ) ;
2004-06-18 01:39:51 +04:00
if ( ! oldEntries ) {
2010-08-18 14:09:27 +04:00
DEBUG ( 1 , ( __location__ " : Failed to allocate space to store "
" the old keytab entries (talloc failed?). \n " ) ) ;
2004-06-19 03:15:42 +04:00
ret = - 1 ;
goto done ;
2004-06-18 01:39:51 +04:00
}
ret = krb5_kt_start_seq_get ( context , keytab , & cursor ) ;
2010-08-18 14:09:27 +04:00
if ( ret = = KRB5_KT_END | | ret = = ENOENT ) {
krb5_kt_end_seq_get ( context , keytab , & cursor ) ;
ZERO_STRUCT ( cursor ) ;
goto done ;
}
2004-06-19 03:15:42 +04:00
2010-08-18 14:09:27 +04:00
while ( krb5_kt_next_entry ( context , keytab , & kt_entry , & cursor ) = = 0 ) {
if ( kt_entry . vno ! = kvno ) {
char * ktprinc = NULL ;
char * p ;
2004-10-30 04:34:58 +04:00
2010-08-18 14:09:27 +04:00
/* This returns a malloc'ed string in ktprinc. */
ret = smb_krb5_unparse_name ( oldEntries , context ,
kt_entry . principal ,
& ktprinc ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__
" : smb_krb5_unparse_name failed "
" (%s) \n " , error_message ( ret ) ) ) ;
goto done ;
}
/*
* From looking at the krb5 source they don ' t seem to
* take locale or mb strings into account .
* Maybe this is because they assume utf8 ?
* In this case we may need to convert from utf8 to
* mb charset here ? JRA .
*/
p = strchr_m ( ktprinc , ' @ ' ) ;
if ( p ) {
* p = ' \0 ' ;
}
p = strchr_m ( ktprinc , ' / ' ) ;
if ( p ) {
* p = ' \0 ' ;
}
for ( i = 0 ; i < found ; i + + ) {
if ( ! oldEntries [ i ] ) {
oldEntries [ i ] = ktprinc ;
break ;
2004-06-18 01:39:51 +04:00
}
2010-08-18 14:09:27 +04:00
if ( ! strcmp ( oldEntries [ i ] , ktprinc ) ) {
2009-03-18 08:23:27 +03:00
TALLOC_FREE ( ktprinc ) ;
2010-08-18 14:09:27 +04:00
break ;
2004-06-23 04:20:31 +04:00
}
2004-06-18 01:39:51 +04:00
}
2010-08-18 14:09:27 +04:00
if ( i = = found ) {
TALLOC_FREE ( ktprinc ) ;
}
2004-06-18 01:39:51 +04:00
}
2010-08-18 14:09:27 +04:00
smb_krb5_kt_free_entry ( context , & kt_entry ) ;
ZERO_STRUCT ( kt_entry ) ;
2004-06-18 01:39:51 +04:00
}
2015-05-02 13:44:53 +03:00
krb5_kt_end_seq_get ( context , keytab , & cursor ) ;
ZERO_STRUCT ( cursor ) ;
2010-08-18 14:09:27 +04:00
ret = 0 ;
for ( i = 0 ; oldEntries [ i ] ; i + + ) {
2018-02-09 17:07:27 +03:00
ret | = ads_keytab_add_entry ( ads , oldEntries [ i ] , false ) ;
2010-08-18 14:09:27 +04:00
TALLOC_FREE ( oldEntries [ i ] ) ;
}
2004-06-18 01:39:51 +04:00
done :
2009-03-18 08:23:27 +03:00
TALLOC_FREE ( oldEntries ) ;
2014-09-24 12:51:33 +04:00
TALLOC_FREE ( frame ) ;
2004-06-19 03:15:42 +04:00
2016-06-09 00:50:59 +03:00
if ( context ) {
2016-12-31 15:45:51 +03:00
if ( ! all_zero ( ( uint8_t * ) & kt_entry , sizeof ( kt_entry ) ) ) {
2004-06-24 09:56:44 +04:00
smb_krb5_kt_free_entry ( context , & kt_entry ) ;
2004-06-19 03:15:42 +04:00
}
2016-12-31 15:45:51 +03:00
if ( ! all_zero ( ( uint8_t * ) & cursor , sizeof ( cursor ) ) & & keytab ) {
2010-08-18 14:09:27 +04:00
krb5_kt_end_seq_get ( context , keytab , & cursor ) ;
2004-06-24 23:48:52 +04:00
}
2016-06-09 00:50:59 +03:00
if ( keytab ) {
krb5_kt_close ( context , keytab ) ;
}
2004-06-19 03:15:42 +04:00
krb5_free_context ( context ) ;
}
2004-06-18 01:39:51 +04:00
return ret ;
}
2007-04-23 12:40:54 +04:00
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
while ( 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 */