2010-08-19 19:25:32 +04:00
/*
* GSSAPI Security Extensions
* Krb5 helpers
* Copyright ( C ) Simo Sorce 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 "smb_krb5.h"
# include "secrets.h"
2010-09-01 19:58:33 +04:00
# include "gse_krb5.h"
2012-07-23 06:47:01 +04:00
# include "lib/param/loadparm.h"
2010-08-19 19:25:32 +04:00
# ifdef HAVE_KRB5
static krb5_error_code flush_keytab ( krb5_context krbctx , krb5_keytab keytab )
{
krb5_error_code ret ;
2010-08-31 13:01:23 +04:00
krb5_kt_cursor kt_cursor ;
2010-08-19 19:25:32 +04:00
krb5_keytab_entry kt_entry ;
ZERO_STRUCT ( kt_entry ) ;
ret = krb5_kt_start_seq_get ( krbctx , keytab , & kt_cursor ) ;
if ( ret = = KRB5_KT_END | | ret = = ENOENT ) {
/* no entries */
return 0 ;
}
ret = krb5_kt_next_entry ( krbctx , keytab , & kt_entry , & kt_cursor ) ;
while ( ret = = 0 ) {
/* we need to close and reopen enumeration because we modify
* the keytab */
ret = krb5_kt_end_seq_get ( krbctx , keytab , & kt_cursor ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : krb5_kt_end_seq_get() "
" failed (%s) \n " , error_message ( ret ) ) ) ;
goto out ;
}
/* remove the entry */
ret = krb5_kt_remove_entry ( krbctx , keytab , & kt_entry ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : krb5_kt_remove_entry() "
" failed (%s) \n " , error_message ( ret ) ) ) ;
goto out ;
}
ret = smb_krb5_kt_free_entry ( krbctx , & kt_entry ) ;
ZERO_STRUCT ( kt_entry ) ;
/* now reopen */
ret = krb5_kt_start_seq_get ( krbctx , keytab , & kt_cursor ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : krb5_kt_start_seq() failed "
" (%s) \n " , error_message ( ret ) ) ) ;
goto out ;
}
ret = krb5_kt_next_entry ( krbctx , keytab ,
& kt_entry , & kt_cursor ) ;
}
if ( ret ! = KRB5_KT_END & & ret ! = ENOENT ) {
DEBUG ( 1 , ( __location__ " : flushing keytab we got [%s]! \n " ,
error_message ( ret ) ) ) ;
}
ret = 0 ;
out :
return ret ;
}
static krb5_error_code get_host_principal ( krb5_context krbctx ,
krb5_principal * host_princ )
{
krb5_error_code ret ;
char * host_princ_s = NULL ;
int err ;
2011-06-09 09:31:03 +04:00
err = asprintf ( & host_princ_s , " %s$@%s " , lp_netbios_name ( ) , lp_realm ( ) ) ;
2010-08-19 19:25:32 +04:00
if ( err = = - 1 ) {
return - 1 ;
}
2012-08-09 04:01:00 +04:00
if ( ! strlower_m ( host_princ_s ) ) {
SAFE_FREE ( host_princ_s ) ;
return - 1 ;
}
2010-08-19 19:25:32 +04:00
ret = smb_krb5_parse_name ( krbctx , host_princ_s , host_princ ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : smb_krb5_parse_name(%s) "
" failed (%s) \n " ,
host_princ_s , error_message ( ret ) ) ) ;
}
SAFE_FREE ( host_princ_s ) ;
return ret ;
}
static krb5_error_code fill_keytab_from_password ( krb5_context krbctx ,
krb5_keytab keytab ,
krb5_principal princ ,
krb5_kvno vno ,
krb5_data * password )
{
krb5_error_code ret ;
krb5_enctype * enctypes ;
krb5_keytab_entry kt_entry ;
unsigned int i ;
2010-08-31 13:04:40 +04:00
ret = get_kerberos_allowed_etypes ( krbctx , & enctypes ) ;
2010-08-19 19:25:32 +04:00
if ( ret ) {
DEBUG ( 1 , ( __location__
" : Can't determine permitted enctypes! \n " ) ) ;
return ret ;
}
for ( i = 0 ; enctypes [ i ] ; i + + ) {
krb5_keyblock * key = NULL ;
2010-09-03 00:07:00 +04:00
if ( ! ( key = SMB_MALLOC_P ( krb5_keyblock ) ) ) {
ret = ENOMEM ;
goto out ;
}
2010-08-19 19:25:32 +04:00
if ( create_kerberos_key_from_string ( krbctx , princ ,
password , key ,
enctypes [ i ] , false ) ) {
DEBUG ( 10 , ( " Failed to create key for enctype %d "
" (error: %s) \n " ,
enctypes [ i ] , error_message ( ret ) ) ) ;
2010-09-03 00:07:00 +04:00
SAFE_FREE ( key ) ;
2010-08-19 19:25:32 +04:00
continue ;
}
kt_entry . principal = princ ;
kt_entry . vno = vno ;
2010-09-03 00:09:19 +04:00
* ( KRB5_KT_KEY ( & kt_entry ) ) = * key ;
2010-08-19 19:25:32 +04:00
ret = krb5_kt_add_entry ( krbctx , keytab , & kt_entry ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : Failed to add entry to "
" keytab for enctype %d (error: %s) \n " ,
enctypes [ i ] , error_message ( ret ) ) ) ;
krb5_free_keyblock ( krbctx , key ) ;
goto out ;
}
krb5_free_keyblock ( krbctx , key ) ;
}
ret = 0 ;
out :
SAFE_FREE ( enctypes ) ;
return ret ;
}
# define SRV_MEM_KEYTAB_NAME "MEMORY:cifs_srv_keytab"
# define CLEARTEXT_PRIV_ENCTYPE -99
2012-01-20 14:51:59 +04:00
static krb5_error_code fill_mem_keytab_from_secrets ( krb5_context krbctx ,
krb5_keytab * keytab )
2010-08-19 19:25:32 +04:00
{
krb5_error_code ret ;
char * pwd = NULL ;
size_t pwd_len ;
2010-09-01 00:28:00 +04:00
krb5_kt_cursor kt_cursor ;
2010-08-19 19:25:32 +04:00
krb5_keytab_entry kt_entry ;
krb5_data password ;
krb5_principal princ = NULL ;
krb5_kvno kvno = 0 ; /* FIXME: fetch current vno from KDC ? */
char * pwd_old = NULL ;
if ( ! secrets_init ( ) ) {
DEBUG ( 1 , ( __location__ " : secrets_init failed \n " ) ) ;
return KRB5_CONFIG_CANTOPEN ;
}
pwd = secrets_fetch_machine_password ( lp_workgroup ( ) , NULL , NULL ) ;
if ( ! pwd ) {
DEBUG ( 2 , ( __location__ " : failed to fetch machine password \n " ) ) ;
return KRB5_LIBOS_CANTREADPWD ;
}
pwd_len = strlen ( pwd ) ;
ZERO_STRUCT ( kt_entry ) ;
2010-09-01 00:28:00 +04:00
ZERO_STRUCT ( kt_cursor ) ;
2010-08-19 19:25:32 +04:00
/* check if the keytab already has any entry */
ret = krb5_kt_start_seq_get ( krbctx , * keytab , & kt_cursor ) ;
if ( ret ! = KRB5_KT_END & & ret ! = ENOENT ) {
/* check if we have our special enctype used to hold
* the clear text password . If so , check it out so that
* we can verify if the keytab needs to be upgraded */
while ( ( ret = krb5_kt_next_entry ( krbctx , * keytab ,
& kt_entry , & kt_cursor ) ) = = 0 ) {
2010-09-01 00:27:32 +04:00
if ( smb_get_enctype_from_kt_entry ( & kt_entry ) = = CLEARTEXT_PRIV_ENCTYPE ) {
2010-08-19 19:25:32 +04:00
break ;
}
smb_krb5_kt_free_entry ( krbctx , & kt_entry ) ;
ZERO_STRUCT ( kt_entry ) ;
}
if ( ret ! = 0 & & ret ! = KRB5_KT_END & & ret ! = ENOENT ) {
/* Error parsing keytab */
DEBUG ( 1 , ( __location__ " : Failed to parse memory "
" keytab! \n " ) ) ;
goto out ;
}
if ( ret = = 0 ) {
/* found private entry,
* check if keytab is up to date */
2010-09-01 00:28:00 +04:00
if ( ( pwd_len = = KRB5_KEY_LENGTH ( KRB5_KT_KEY ( & kt_entry ) ) ) & &
( memcmp ( KRB5_KEY_DATA ( KRB5_KT_KEY ( & kt_entry ) ) ,
2010-08-19 19:25:32 +04:00
pwd , pwd_len ) = = 0 ) ) {
/* keytab is already up to date, return */
smb_krb5_kt_free_entry ( krbctx , & kt_entry ) ;
goto out ;
}
smb_krb5_kt_free_entry ( krbctx , & kt_entry ) ;
ZERO_STRUCT ( kt_entry ) ;
/* flush keytab, we need to regen it */
ret = flush_keytab ( krbctx , * keytab ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : Failed to flush "
" memory keytab! \n " ) ) ;
goto out ;
}
}
}
2010-09-01 00:28:00 +04:00
{
krb5_kt_cursor zero_csr ;
ZERO_STRUCT ( zero_csr ) ;
if ( ( memcmp ( & kt_cursor , & zero_csr , sizeof ( krb5_kt_cursor ) ) ! = 0 ) & & * keytab ) {
krb5_kt_end_seq_get ( krbctx , * keytab , & kt_cursor ) ;
}
}
2010-08-19 19:25:32 +04:00
/* keytab is not up to date, fill it up */
ret = get_host_principal ( krbctx , & princ ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : Failed to get host principal! \n " ) ) ;
goto out ;
}
password . data = pwd ;
password . length = pwd_len ;
ret = fill_keytab_from_password ( krbctx , * keytab ,
princ , kvno , & password ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : Failed to fill memory keytab! \n " ) ) ;
goto out ;
}
2011-04-06 03:10:13 +04:00
pwd_old = secrets_fetch_prev_machine_password ( lp_workgroup ( ) ) ;
2010-08-19 19:25:32 +04:00
if ( ! pwd_old ) {
DEBUG ( 10 , ( __location__ " : no prev machine password \n " ) ) ;
} else {
password . data = pwd_old ;
password . length = strlen ( pwd_old ) ;
ret = fill_keytab_from_password ( krbctx , * keytab ,
princ , kvno - 1 , & password ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__
" : Failed to fill memory keytab! \n " ) ) ;
goto out ;
}
}
/* add our private enctype + cleartext password so that we can
* update the keytab if secrets change later on */
ZERO_STRUCT ( kt_entry ) ;
kt_entry . principal = princ ;
kt_entry . vno = 0 ;
2010-09-01 00:28:00 +04:00
KRB5_KEY_TYPE ( KRB5_KT_KEY ( & kt_entry ) ) = CLEARTEXT_PRIV_ENCTYPE ;
KRB5_KEY_LENGTH ( KRB5_KT_KEY ( & kt_entry ) ) = pwd_len ;
KRB5_KEY_DATA ( KRB5_KT_KEY ( & kt_entry ) ) = ( uint8_t * ) pwd ;
2010-08-19 19:25:32 +04:00
ret = krb5_kt_add_entry ( krbctx , * keytab , & kt_entry ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : Failed to add entry to "
" keytab for private enctype (%d) (error: %s) \n " ,
CLEARTEXT_PRIV_ENCTYPE , error_message ( ret ) ) ) ;
goto out ;
}
ret = 0 ;
out :
SAFE_FREE ( pwd ) ;
SAFE_FREE ( pwd_old ) ;
2010-09-01 00:28:00 +04:00
{
krb5_kt_cursor zero_csr ;
ZERO_STRUCT ( zero_csr ) ;
if ( ( memcmp ( & kt_cursor , & zero_csr , sizeof ( krb5_kt_cursor ) ) ! = 0 ) & & * keytab ) {
krb5_kt_end_seq_get ( krbctx , * keytab , & kt_cursor ) ;
}
}
2010-08-19 19:25:32 +04:00
if ( princ ) {
krb5_free_principal ( krbctx , princ ) ;
}
return ret ;
}
2012-01-20 14:51:59 +04:00
static krb5_error_code fill_mem_keytab_from_system_keytab ( krb5_context krbctx ,
2012-01-20 12:31:55 +04:00
krb5_keytab * mkeytab )
2010-08-19 19:25:32 +04:00
{
2012-01-20 12:31:55 +04:00
krb5_error_code ret = 0 ;
krb5_keytab keytab = NULL ;
krb5_kt_cursor kt_cursor ;
krb5_keytab_entry kt_entry ;
char * valid_princ_formats [ 7 ] = { NULL , NULL , NULL ,
NULL , NULL , NULL , NULL } ;
char * entry_princ_s = NULL ;
fstring my_name , my_fqdn ;
int i ;
int err ;
/* Generate the list of principal names which we expect
* clients might want to use for authenticating to the file
* service . We allow name $ , { host , cifs } / { name , fqdn , name . REALM } . */
fstrcpy ( my_name , lp_netbios_name ( ) ) ;
my_fqdn [ 0 ] = ' \0 ' ;
name_to_fqdn ( my_fqdn , lp_netbios_name ( ) ) ;
err = asprintf ( & valid_princ_formats [ 0 ] ,
" %s$@%s " , my_name , lp_realm ( ) ) ;
if ( err = = - 1 ) {
ret = ENOMEM ;
goto out ;
}
err = asprintf ( & valid_princ_formats [ 1 ] ,
" host/%s@%s " , my_name , lp_realm ( ) ) ;
if ( err = = - 1 ) {
ret = ENOMEM ;
goto out ;
}
err = asprintf ( & valid_princ_formats [ 2 ] ,
" host/%s@%s " , my_fqdn , lp_realm ( ) ) ;
if ( err = = - 1 ) {
ret = ENOMEM ;
goto out ;
}
err = asprintf ( & valid_princ_formats [ 3 ] ,
" host/%s.%s@%s " , my_name , lp_realm ( ) , lp_realm ( ) ) ;
if ( err = = - 1 ) {
ret = ENOMEM ;
goto out ;
}
err = asprintf ( & valid_princ_formats [ 4 ] ,
" cifs/%s@%s " , my_name , lp_realm ( ) ) ;
if ( err = = - 1 ) {
ret = ENOMEM ;
goto out ;
}
err = asprintf ( & valid_princ_formats [ 5 ] ,
" cifs/%s@%s " , my_fqdn , lp_realm ( ) ) ;
if ( err = = - 1 ) {
ret = ENOMEM ;
goto out ;
}
err = asprintf ( & valid_princ_formats [ 6 ] ,
" cifs/%s.%s@%s " , my_name , lp_realm ( ) , lp_realm ( ) ) ;
if ( err = = - 1 ) {
ret = ENOMEM ;
goto out ;
}
ZERO_STRUCT ( kt_entry ) ;
ZERO_STRUCT ( kt_cursor ) ;
ret = smb_krb5_open_keytab ( krbctx , NULL , false , & keytab ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : smb_krb5_open_keytab failed (%s) \n " ,
error_message ( ret ) ) ) ;
goto out ;
}
/*
* Iterate through the keytab . For each key , if the principal
* name case - insensitively matches one of the allowed formats ,
* copy it to the memory keytab .
*/
ret = krb5_kt_start_seq_get ( krbctx , keytab , & kt_cursor ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : krb5_kt_start_seq_get failed (%s) \n " ,
error_message ( ret ) ) ) ;
goto out ;
}
while ( ( krb5_kt_next_entry ( krbctx , keytab ,
& kt_entry , & kt_cursor ) = = 0 ) ) {
ret = smb_krb5_unparse_name ( talloc_tos ( ) , krbctx ,
kt_entry . principal ,
& entry_princ_s ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : smb_krb5_unparse_name "
" failed (%s) \n " , error_message ( ret ) ) ) ;
goto out ;
}
for ( i = 0 ; i < ARRAY_SIZE ( valid_princ_formats ) ; i + + ) {
if ( ! strequal ( entry_princ_s , valid_princ_formats [ i ] ) ) {
continue ;
}
ret = krb5_kt_add_entry ( krbctx , * mkeytab , & kt_entry ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : smb_krb5_unparse_name "
" failed (%s) \n " , error_message ( ret ) ) ) ;
goto out ;
}
}
/* Free the name we parsed. */
TALLOC_FREE ( entry_princ_s ) ;
/* Free the entry we just read. */
smb_krb5_kt_free_entry ( krbctx , & kt_entry ) ;
ZERO_STRUCT ( kt_entry ) ;
}
krb5_kt_end_seq_get ( krbctx , keytab , & kt_cursor ) ;
ZERO_STRUCT ( kt_cursor ) ;
out :
for ( i = 0 ; i < ARRAY_SIZE ( valid_princ_formats ) ; i + + ) {
SAFE_FREE ( valid_princ_formats [ i ] ) ;
}
TALLOC_FREE ( entry_princ_s ) ;
{
krb5_keytab_entry zero_kt_entry ;
ZERO_STRUCT ( zero_kt_entry ) ;
if ( memcmp ( & zero_kt_entry , & kt_entry ,
sizeof ( krb5_keytab_entry ) ) ) {
smb_krb5_kt_free_entry ( krbctx , & kt_entry ) ;
}
}
{
krb5_kt_cursor zero_csr ;
ZERO_STRUCT ( zero_csr ) ;
if ( ( memcmp ( & kt_cursor , & zero_csr ,
sizeof ( krb5_kt_cursor ) ) ! = 0 ) & & keytab ) {
krb5_kt_end_seq_get ( krbctx , keytab , & kt_cursor ) ;
}
}
if ( keytab ) {
krb5_kt_close ( krbctx , keytab ) ;
}
return ret ;
}
static krb5_error_code fill_mem_keytab_from_dedicated_keytab ( krb5_context krbctx ,
krb5_keytab * mkeytab )
{
krb5_error_code ret = 0 ;
krb5_keytab keytab = NULL ;
krb5_kt_cursor kt_cursor ;
krb5_keytab_entry kt_entry ;
ret = smb_krb5_open_keytab ( krbctx , lp_dedicated_keytab_file ( ) ,
false , & keytab ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : smb_krb5_open_keytab failed (%s) \n " ,
error_message ( ret ) ) ) ;
2014-11-27 07:13:23 +03:00
return ret ;
2012-01-20 12:31:55 +04:00
}
/*
* Iterate through the keytab . For each key , if the principal
* name case - insensitively matches one of the allowed formats ,
* copy it to the memory keytab .
*/
ret = krb5_kt_start_seq_get ( krbctx , keytab , & kt_cursor ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : krb5_kt_start_seq_get failed (%s) \n " ,
error_message ( ret ) ) ) ;
goto out ;
}
while ( ( krb5_kt_next_entry ( krbctx , keytab ,
& kt_entry , & kt_cursor ) = = 0 ) ) {
ret = krb5_kt_add_entry ( krbctx , * mkeytab , & kt_entry ) ;
2014-11-27 07:13:23 +03:00
/* Free the entry we just read. */
smb_krb5_kt_free_entry ( krbctx , & kt_entry ) ;
2012-01-20 12:31:55 +04:00
if ( ret ) {
DEBUG ( 1 , ( __location__ " : smb_krb5_unparse_name "
" failed (%s) \n " , error_message ( ret ) ) ) ;
2014-11-27 07:13:23 +03:00
break ;
2012-01-20 12:31:55 +04:00
}
}
krb5_kt_end_seq_get ( krbctx , keytab , & kt_cursor ) ;
out :
2014-11-27 07:13:23 +03:00
krb5_kt_close ( krbctx , keytab ) ;
2012-01-20 12:31:55 +04:00
return ret ;
2010-08-19 19:25:32 +04:00
}
2010-09-01 19:58:33 +04:00
krb5_error_code gse_krb5_get_server_keytab ( krb5_context krbctx ,
2010-08-19 19:25:32 +04:00
krb5_keytab * keytab )
{
2012-01-20 14:50:20 +04:00
krb5_error_code ret = 0 ;
krb5_error_code ret1 = 0 ;
krb5_error_code ret2 = 0 ;
2010-08-19 19:25:32 +04:00
* keytab = NULL ;
2012-01-20 14:51:59 +04:00
/* create memory keytab */
ret = krb5_kt_resolve ( krbctx , SRV_MEM_KEYTAB_NAME , keytab ) ;
if ( ret ) {
DEBUG ( 1 , ( __location__ " : Failed to get memory "
" keytab! \n " ) ) ;
return ret ;
}
2010-08-19 19:25:32 +04:00
switch ( lp_kerberos_method ( ) ) {
default :
case KERBEROS_VERIFY_SECRETS :
2012-01-20 14:51:59 +04:00
ret = fill_mem_keytab_from_secrets ( krbctx , keytab ) ;
2010-08-19 19:25:32 +04:00
break ;
case KERBEROS_VERIFY_SYSTEM_KEYTAB :
2012-01-20 12:31:55 +04:00
ret = fill_mem_keytab_from_system_keytab ( krbctx , keytab ) ;
2010-08-19 19:25:32 +04:00
break ;
case KERBEROS_VERIFY_DEDICATED_KEYTAB :
/* just use whatever keytab is configured */
2012-01-20 12:31:55 +04:00
ret = fill_mem_keytab_from_dedicated_keytab ( krbctx , keytab ) ;
2010-08-19 19:25:32 +04:00
break ;
case KERBEROS_VERIFY_SECRETS_AND_KEYTAB :
2012-01-20 14:51:59 +04:00
ret1 = fill_mem_keytab_from_secrets ( krbctx , keytab ) ;
2012-01-20 14:50:20 +04:00
if ( ret1 ) {
2010-08-19 19:25:32 +04:00
DEBUG ( 3 , ( __location__ " : Warning! Unable to set mem "
" keytab from secrets! \n " ) ) ;
}
/* Now append system keytab keys too */
2012-01-20 12:31:55 +04:00
ret2 = fill_mem_keytab_from_system_keytab ( krbctx , keytab ) ;
2012-01-20 14:50:20 +04:00
if ( ret2 ) {
2010-08-19 19:25:32 +04:00
DEBUG ( 3 , ( __location__ " : Warning! Unable to set mem "
2012-01-20 14:50:20 +04:00
" keytab from system keytab! \n " ) ) ;
}
if ( ret1 = = 0 | | ret2 = = 0 ) {
ret = 0 ;
} else {
ret = ret1 ;
2010-08-19 19:25:32 +04:00
}
break ;
}
2012-01-20 14:51:59 +04:00
if ( ret ) {
krb5_kt_close ( krbctx , * keytab ) ;
* keytab = NULL ;
DEBUG ( 1 , ( " %s: Error! Unable to set mem keytab - %d \n " ,
__location__ , ret ) ) ;
}
2010-08-19 19:25:32 +04:00
return ret ;
}
# endif /* HAVE_KRB5 */