2010-07-30 16:34:53 -04:00
/*
2002-06-25 02:29:09 +00:00
Unix SMB / CIFS implementation .
kerberos utility library
Copyright ( C ) Andrew Tridgell 2001
Copyright ( C ) Remus Koos 2001
2010-07-30 16:34:53 -04:00
Copyright ( C ) Luke Howard 2003
2005-09-30 17:13:37 +00:00
Copyright ( C ) Guenther Deschner 2003 , 2005
2003-09-03 00:45:15 +00:00
Copyright ( C ) Jim McDonough ( jmcd @ us . ibm . com ) 2003
2005-09-30 17:13:37 +00:00
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2004 - 2005
2007-03-15 19:18:18 +00:00
Copyright ( C ) Jeremy Allison 2007
2010-07-30 16:34:53 -04:00
2002-06-25 02:29:09 +00: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 19:25:36 +00:00
the Free Software Foundation ; either version 3 of the License , or
2002-06-25 02:29:09 +00:00
( at your option ) any later version .
2010-07-30 16:34:53 -04:00
2002-06-25 02:29:09 +00:00
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 .
2010-07-30 16:34:53 -04:00
2002-06-25 02:29:09 +00:00
You should have received a copy of the GNU General Public License
2007-07-10 00:52:41 +00:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2002-06-25 02:29:09 +00:00
*/
# include "includes.h"
2009-11-27 15:52:57 +01:00
# include "smb_krb5.h"
2010-07-02 00:32:52 +02:00
# include "libads/kerberos_proto.h"
2010-08-05 02:25:37 +02:00
# include "secrets.h"
2010-08-02 23:12:16 +02:00
# include "../librpc/gen_ndr/krb5pac.h"
2002-06-25 02:29:09 +00:00
# ifdef HAVE_KRB5
2004-08-12 20:27:09 +00:00
# if !defined(HAVE_KRB5_PRINC_COMPONENT)
const krb5_data * krb5_princ_component ( krb5_context , krb5_principal , int ) ;
# endif
2009-01-15 17:02:41 -08:00
static bool ads_dedicated_keytab_verify_ticket ( krb5_context context ,
krb5_auth_context auth_context ,
const DATA_BLOB * ticket ,
krb5_ticket * * pp_tkt ,
krb5_keyblock * * keyblock ,
krb5_error_code * perr )
{
krb5_error_code ret = 0 ;
bool auth_ok = false ;
krb5_keytab keytab = NULL ;
krb5_keytab_entry kt_entry ;
krb5_ticket * dec_ticket = NULL ;
krb5_data packet ;
2009-02-03 14:53:58 +01:00
krb5_kvno kvno = 0 ;
krb5_enctype enctype ;
2009-01-15 17:02:41 -08:00
* pp_tkt = NULL ;
* keyblock = NULL ;
* perr = 0 ;
ZERO_STRUCT ( kt_entry ) ;
ret = smb_krb5_open_keytab ( context , lp_dedicated_keytab_file ( ) , true ,
& keytab ) ;
if ( ret ) {
DEBUG ( 1 , ( " smb_krb5_open_keytab failed (%s) \n " ,
error_message ( ret ) ) ) ;
goto out ;
}
packet . length = ticket - > length ;
packet . data = ( char * ) ticket - > data ;
ret = krb5_rd_req ( context , & auth_context , & packet , NULL , keytab ,
NULL , & dec_ticket ) ;
if ( ret ) {
DEBUG ( 0 , ( " krb5_rd_req failed (%s) \n " , error_message ( ret ) ) ) ;
goto out ;
}
2009-02-03 14:53:58 +01:00
# ifdef HAVE_ETYPE_IN_ENCRYPTEDDATA /* Heimdal */
enctype = dec_ticket - > ticket . key . keytype ;
# else /* MIT */
enctype = dec_ticket - > enc_part . enctype ;
kvno = dec_ticket - > enc_part . kvno ;
# endif
2009-01-15 17:02:41 -08:00
/* Get the key for checking the pac signature */
ret = krb5_kt_get_entry ( context , keytab , dec_ticket - > server ,
2009-02-03 14:53:58 +01:00
kvno , enctype , & kt_entry ) ;
2009-01-15 17:02:41 -08:00
if ( ret ) {
DEBUG ( 0 , ( " krb5_kt_get_entry failed (%s) \n " ,
error_message ( ret ) ) ) ;
goto out ;
}
2009-02-03 14:15:40 +01:00
ret = krb5_copy_keyblock ( context , KRB5_KT_KEY ( & kt_entry ) , keyblock ) ;
2009-01-15 17:02:41 -08:00
smb_krb5_kt_free_entry ( context , & kt_entry ) ;
if ( ret ) {
DEBUG ( 0 , ( " failed to copy key: %s \n " ,
error_message ( ret ) ) ) ;
goto out ;
}
auth_ok = true ;
* pp_tkt = dec_ticket ;
dec_ticket = NULL ;
out :
if ( dec_ticket )
krb5_free_ticket ( context , dec_ticket ) ;
if ( keytab )
krb5_kt_close ( context , keytab ) ;
* perr = ret ;
return auth_ok ;
}
2010-07-30 16:34:53 -04:00
/******************************************************************************
Try to verify a ticket using the system keytab . . . the system keytab has
kvno - 1 entries , so it ' s more like what microsoft does . . . see comment in
utils / net_ads . c in the ads_keytab_add_entry function for details .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-06-22 00:48:59 +00:00
2007-10-18 17:40:25 -07:00
static bool ads_keytab_verify_ticket ( krb5_context context ,
2007-03-15 19:18:18 +00:00
krb5_auth_context auth_context ,
const DATA_BLOB * ticket ,
krb5_ticket * * pp_tkt ,
krb5_keyblock * * keyblock ,
krb5_error_code * perr )
2004-06-22 00:48:59 +00:00
{
krb5_error_code ret = 0 ;
2007-10-18 17:40:25 -07:00
bool auth_ok = False ;
2004-06-22 00:48:59 +00:00
krb5_keytab keytab = NULL ;
2005-03-12 01:28:10 +00:00
krb5_kt_cursor kt_cursor ;
krb5_keytab_entry kt_entry ;
2010-07-30 16:34:53 -04:00
char * valid_princ_formats [ 7 ] = { NULL , NULL , NULL ,
NULL , NULL , NULL , NULL } ;
2005-03-12 01:28:10 +00:00
char * entry_princ_s = NULL ;
fstring my_name , my_fqdn ;
2004-10-30 01:32:05 +00:00
int i ;
2005-03-12 01:28:10 +00:00
int number_matched_principals = 0 ;
2007-03-15 19:18:18 +00:00
krb5_data packet ;
2010-07-30 16:34:53 -04:00
int err ;
2007-03-15 19:18:18 +00:00
* pp_tkt = NULL ;
* keyblock = NULL ;
* perr = 0 ;
2005-03-12 01:28:10 +00:00
/* 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 } . */
2011-06-09 15:31:03 +10:00
fstrcpy ( my_name , lp_netbios_name ( ) ) ;
2005-03-12 01:28:10 +00:00
my_fqdn [ 0 ] = ' \0 ' ;
2011-06-09 15:31:03 +10:00
name_to_fqdn ( my_fqdn , lp_netbios_name ( ) ) ;
2005-03-12 01:28:10 +00:00
2010-07-30 16:34:53 -04:00
err = asprintf ( & valid_princ_formats [ 0 ] ,
" %s$@%s " , my_name , lp_realm ( ) ) ;
if ( err = = - 1 ) {
2008-12-30 18:24:39 -08:00
goto out ;
}
2010-07-30 16:34:53 -04:00
err = asprintf ( & valid_princ_formats [ 1 ] ,
" host/%s@%s " , my_name , lp_realm ( ) ) ;
if ( err = = - 1 ) {
2008-12-30 18:24:39 -08:00
goto out ;
}
2010-07-30 16:34:53 -04:00
err = asprintf ( & valid_princ_formats [ 2 ] ,
" host/%s@%s " , my_fqdn , lp_realm ( ) ) ;
if ( err = = - 1 ) {
2008-12-30 18:24:39 -08:00
goto out ;
}
2010-07-30 16:34:53 -04:00
err = asprintf ( & valid_princ_formats [ 3 ] ,
" host/%s.%s@%s " , my_name , lp_realm ( ) , lp_realm ( ) ) ;
if ( err = = - 1 ) {
2008-12-30 18:24:39 -08:00
goto out ;
}
2010-07-30 16:34:53 -04:00
err = asprintf ( & valid_princ_formats [ 4 ] ,
" cifs/%s@%s " , my_name , lp_realm ( ) ) ;
if ( err = = - 1 ) {
2008-12-30 18:24:39 -08:00
goto out ;
}
2010-07-30 16:34:53 -04:00
err = asprintf ( & valid_princ_formats [ 5 ] ,
" cifs/%s@%s " , my_fqdn , lp_realm ( ) ) ;
if ( err = = - 1 ) {
2008-12-30 18:24:39 -08:00
goto out ;
}
2010-07-30 16:34:53 -04:00
err = asprintf ( & valid_princ_formats [ 6 ] ,
" cifs/%s.%s@%s " , my_name , lp_realm ( ) , lp_realm ( ) ) ;
if ( err = = - 1 ) {
2008-12-30 18:24:39 -08:00
goto out ;
}
2005-03-12 01:28:10 +00:00
ZERO_STRUCT ( kt_entry ) ;
ZERO_STRUCT ( kt_cursor ) ;
2004-06-24 20:37:23 +00:00
2007-06-29 08:58:03 +00:00
ret = smb_krb5_open_keytab ( context , NULL , False , & keytab ) ;
2004-06-22 00:48:59 +00:00
if ( ret ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 1 , ( __location__ " : smb_krb5_open_keytab failed (%s) \n " ,
error_message ( ret ) ) ) ;
2004-06-22 00:48:59 +00:00
goto out ;
}
2005-03-12 01:28:10 +00:00
/* Iterate through the keytab. For each key, if the principal
* name case - insensitively matches one of the allowed formats ,
* try verifying the ticket using that principal . */
2004-10-30 01:32:05 +00:00
2005-03-12 01:28:10 +00:00
ret = krb5_kt_start_seq_get ( context , keytab , & kt_cursor ) ;
if ( ret ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 1 , ( __location__ " : krb5_kt_start_seq_get failed (%s) \n " ,
error_message ( ret ) ) ) ;
2005-03-12 01:28:10 +00:00
goto out ;
}
2010-07-30 16:34:53 -04:00
while ( ! auth_ok & &
( krb5_kt_next_entry ( context , keytab ,
& kt_entry , & kt_cursor ) = = 0 ) ) {
ret = smb_krb5_unparse_name ( talloc_tos ( ) , context ,
kt_entry . principal ,
& entry_princ_s ) ;
2007-02-27 13:27:31 +00:00
if ( ret ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 1 , ( __location__ " : smb_krb5_unparse_name "
" failed (%s) \n " , error_message ( ret ) ) ) ;
2007-02-27 13:27:31 +00:00
goto out ;
}
2005-03-12 01:28:10 +00:00
2007-02-27 13:31:42 +00:00
for ( i = 0 ; i < ARRAY_SIZE ( valid_princ_formats ) ; i + + ) {
if ( ! strequal ( entry_princ_s , valid_princ_formats [ i ] ) ) {
continue ;
}
number_matched_principals + + ;
2007-03-15 19:18:18 +00:00
packet . length = ticket - > length ;
packet . data = ( char * ) ticket - > data ;
2007-02-27 13:31:42 +00:00
* pp_tkt = NULL ;
2010-07-30 16:34:53 -04:00
ret = krb5_rd_req_return_keyblock_from_keytab ( context ,
& auth_context , & packet ,
kt_entry . principal , keytab ,
NULL , pp_tkt , keyblock ) ;
2007-02-27 13:31:42 +00:00
if ( ret ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 10 , ( __location__ " : krb5_rd_req_return "
" _keyblock_from_keytab(%s) "
" failed: %s \n " , entry_princ_s ,
error_message ( ret ) ) ) ;
2007-02-27 13:31:42 +00:00
2010-07-30 16:34:53 -04:00
/* workaround for MIT:
2007-02-27 13:31:42 +00:00
* as krb5_ktfile_get_entry will explicitly
* close the krb5_keytab as soon as krb5_rd_req
2008-04-10 08:38:26 +02:00
* has successfully decrypted the ticket but the
2007-02-27 13:31:42 +00:00
* ticket is not valid yet ( due to clockskew )
* there is no point in querying more keytab
* entries - Guenther */
2010-07-30 16:34:53 -04:00
if ( ret = = KRB5KRB_AP_ERR_TKT_NYV | |
2007-03-15 19:18:18 +00:00
ret = = KRB5KRB_AP_ERR_TKT_EXPIRED | |
ret = = KRB5KRB_AP_ERR_SKEW ) {
2007-02-27 13:27:31 +00:00
break ;
2005-03-12 01:28:10 +00:00
}
2007-02-27 13:31:42 +00:00
} else {
2010-07-30 16:34:53 -04:00
DEBUG ( 3 , ( __location__ " : krb5_rd_req_return "
" _keyblock_from_keytab succeeded "
" for principal %s \n " ,
entry_princ_s ) ) ;
2007-02-27 13:31:42 +00:00
auth_ok = True ;
break ;
2005-03-12 01:28:10 +00:00
}
2007-02-27 13:27:31 +00:00
}
2005-03-12 01:28:10 +00:00
2007-02-27 13:27:31 +00:00
/* Free the name we parsed. */
2009-03-18 16:23:27 +11:00
TALLOC_FREE ( entry_princ_s ) ;
2005-03-12 01:28:10 +00:00
2007-02-27 13:27:31 +00:00
/* Free the entry we just read. */
smb_krb5_kt_free_entry ( context , & kt_entry ) ;
ZERO_STRUCT ( kt_entry ) ;
}
krb5_kt_end_seq_get ( context , keytab , & kt_cursor ) ;
2004-10-30 01:32:05 +00:00
2005-03-12 01:28:10 +00:00
ZERO_STRUCT ( kt_cursor ) ;
2004-10-30 01:32:05 +00:00
2010-07-30 16:34:53 -04:00
out :
2007-02-27 13:31:42 +00:00
for ( i = 0 ; i < ARRAY_SIZE ( valid_princ_formats ) ; i + + ) {
2005-09-13 21:26:25 +00:00
SAFE_FREE ( valid_princ_formats [ i ] ) ;
}
2010-07-30 16:34:53 -04:00
2005-03-12 01:28:10 +00:00
if ( ! auth_ok ) {
if ( ! number_matched_principals ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 3 , ( __location__ " : no keytab principals "
" matched expected file service name. \n " ) ) ;
2005-03-12 01:28:10 +00:00
} else {
2010-07-30 16:34:53 -04:00
DEBUG ( 3 , ( __location__ " : krb5_rd_req failed for "
" all %d matched keytab principals \n " ,
number_matched_principals ) ) ;
2005-03-12 01:28:10 +00:00
}
}
2004-10-30 01:32:05 +00:00
2006-04-24 15:57:54 +00:00
SAFE_FREE ( entry_princ_s ) ;
2004-06-22 00:48:59 +00:00
2005-03-12 01:28:10 +00:00
{
krb5_keytab_entry zero_kt_entry ;
ZERO_STRUCT ( zero_kt_entry ) ;
2010-07-30 16:34:53 -04:00
if ( memcmp ( & zero_kt_entry , & kt_entry ,
sizeof ( krb5_keytab_entry ) ) ) {
2005-03-12 01:28:10 +00:00
smb_krb5_kt_free_entry ( context , & kt_entry ) ;
2004-06-22 00:48:59 +00:00
}
}
2004-10-30 01:32:05 +00:00
2005-03-12 01:28:10 +00:00
{
krb5_kt_cursor zero_csr ;
ZERO_STRUCT ( zero_csr ) ;
2010-07-30 16:34:53 -04:00
if ( ( memcmp ( & kt_cursor , & zero_csr ,
sizeof ( krb5_kt_cursor ) ) ! = 0 ) & & keytab ) {
2005-03-12 01:28:10 +00:00
krb5_kt_end_seq_get ( context , keytab , & kt_cursor ) ;
}
2004-06-22 00:48:59 +00:00
}
if ( keytab ) {
krb5_kt_close ( context , keytab ) ;
}
2007-03-15 19:18:18 +00:00
* perr = ret ;
2004-06-22 00:48:59 +00:00
return auth_ok ;
}
2010-07-30 16:34:53 -04:00
/*****************************************************************************
2004-06-22 00:48:59 +00:00
Try to verify a ticket using the secrets . tdb .
2010-07-30 16:34:53 -04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-06-22 00:48:59 +00:00
2007-03-15 19:18:18 +00:00
static krb5_error_code ads_secrets_verify_ticket ( krb5_context context ,
krb5_auth_context auth_context ,
krb5_principal host_princ ,
const DATA_BLOB * ticket ,
krb5_ticket * * pp_tkt ,
krb5_keyblock * * keyblock ,
krb5_error_code * perr )
2004-06-22 00:48:59 +00:00
{
krb5_error_code ret = 0 ;
2007-10-18 17:40:25 -07:00
bool auth_ok = False ;
2010-05-21 11:57:29 +04:00
bool cont = true ;
2004-06-22 00:48:59 +00:00
char * password_s = NULL ;
2010-05-21 11:57:29 +04:00
/* Let's make some room for 2 password (old and new)*/
krb5_data passwords [ 2 ] ;
2010-07-30 16:34:53 -04:00
krb5_enctype enctypes [ ] = {
2007-06-13 20:49:20 +00:00
ENCTYPE_ARCFOUR_HMAC ,
2010-07-30 16:34:53 -04:00
ENCTYPE_DES_CBC_CRC ,
ENCTYPE_DES_CBC_MD5 ,
2007-06-13 20:49:20 +00:00
ENCTYPE_NULL
} ;
2007-03-15 19:18:18 +00:00
krb5_data packet ;
2010-05-21 11:57:29 +04:00
int i , j ;
2004-06-22 00:48:59 +00:00
2007-03-15 19:18:18 +00:00
* pp_tkt = NULL ;
* keyblock = NULL ;
* perr = 0 ;
2010-05-21 11:57:29 +04:00
ZERO_STRUCT ( passwords ) ;
2006-07-11 18:45:22 +00:00
2004-06-22 00:48:59 +00:00
if ( ! secrets_init ( ) ) {
DEBUG ( 1 , ( " ads_secrets_verify_ticket: secrets_init failed \n " ) ) ;
2007-03-15 19:18:18 +00:00
* perr = KRB5_CONFIG_CANTOPEN ;
2004-06-22 00:48:59 +00:00
return False ;
}
2010-07-30 16:34:53 -04:00
password_s = secrets_fetch_machine_password ( lp_workgroup ( ) ,
NULL , NULL ) ;
2004-06-22 00:48:59 +00:00
if ( ! password_s ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 1 , ( __location__ " : failed to fetch machine password \n " ) ) ;
2007-03-15 19:18:18 +00:00
* perr = KRB5_LIBOS_CANTREADPWD ;
2004-06-22 00:48:59 +00:00
return False ;
}
2010-05-21 11:57:29 +04:00
passwords [ 0 ] . data = password_s ;
passwords [ 0 ] . length = strlen ( password_s ) ;
password_s = secrets_fetch_prev_machine_password ( lp_workgroup ( ) ) ;
if ( password_s ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 10 , ( __location__ " : found previous password \n " ) ) ;
2010-05-21 11:57:29 +04:00
passwords [ 1 ] . data = password_s ;
passwords [ 1 ] . length = strlen ( password_s ) ;
}
2004-06-22 00:48:59 +00:00
/* CIFS doesn't use addresses in tickets. This would break NAT. JRA */
2007-03-15 19:18:18 +00:00
packet . length = ticket - > length ;
packet . data = ( char * ) ticket - > data ;
2004-06-22 00:48:59 +00:00
2010-07-30 16:34:53 -04:00
/* We need to setup a auth context with each possible encoding type
* in turn . */
2010-05-21 11:57:29 +04:00
for ( j = 0 ; j < 2 & & passwords [ j ] . length ; j + + ) {
2004-06-22 00:48:59 +00:00
2010-05-21 11:57:29 +04:00
for ( i = 0 ; enctypes [ i ] ; i + + ) {
krb5_keyblock * key = NULL ;
2004-06-22 00:48:59 +00:00
2010-05-21 11:57:29 +04:00
if ( ! ( key = SMB_MALLOC_P ( krb5_keyblock ) ) ) {
ret = ENOMEM ;
goto out ;
}
2004-06-22 00:48:59 +00:00
2010-07-30 16:34:53 -04:00
if ( create_kerberos_key_from_string ( context ,
host_princ , & passwords [ j ] ,
key , enctypes [ i ] , false ) ) {
2010-05-21 11:57:29 +04:00
SAFE_FREE ( key ) ;
continue ;
}
2010-07-30 16:34:53 -04:00
krb5_auth_con_setuseruserkey ( context ,
auth_context , key ) ;
2010-05-21 11:57:29 +04:00
2010-07-30 16:34:53 -04:00
if ( ! ( ret = krb5_rd_req ( context , & auth_context ,
& packet , NULL , NULL ,
NULL , pp_tkt ) ) ) {
DEBUG ( 10 , ( __location__ " : enc type [%u] "
" decrypted message ! \n " ,
( unsigned int ) enctypes [ i ] ) ) ;
2010-05-21 11:57:29 +04:00
auth_ok = True ;
cont = false ;
krb5_copy_keyblock ( context , key , keyblock ) ;
krb5_free_keyblock ( context , key ) ;
break ;
}
2006-03-20 10:05:51 +00:00
2010-05-21 11:57:29 +04:00
DEBUG ( ( ret ! = KRB5_BAD_ENCTYPE ) ? 3 : 10 ,
2010-07-30 16:34:53 -04:00
( __location__ " : enc type [%u] failed to "
" decrypt with error %s \n " ,
( unsigned int ) enctypes [ i ] ,
error_message ( ret ) ) ) ;
2010-05-21 11:57:29 +04:00
2010-07-30 16:34:53 -04:00
/* successfully decrypted but ticket is just not
* valid at the moment */
2010-05-21 11:57:29 +04:00
if ( ret = = KRB5KRB_AP_ERR_TKT_NYV | |
ret = = KRB5KRB_AP_ERR_TKT_EXPIRED | |
ret = = KRB5KRB_AP_ERR_SKEW ) {
krb5_free_keyblock ( context , key ) ;
cont = false ;
break ;
}
2005-09-30 17:13:37 +00:00
2007-07-27 09:22:43 +00:00
krb5_free_keyblock ( context , key ) ;
2010-05-21 11:57:29 +04:00
}
if ( ! cont ) {
/* If we found a valid pass then no need to try
* the next one or we have invalid ticket so no need
* to try next password */
2006-03-20 10:05:51 +00:00
break ;
}
2004-06-22 00:48:59 +00:00
}
out :
2010-05-21 11:57:29 +04:00
SAFE_FREE ( passwords [ 0 ] . data ) ;
SAFE_FREE ( passwords [ 1 ] . data ) ;
2007-03-15 19:18:18 +00:00
* perr = ret ;
2004-06-22 00:48:59 +00:00
return auth_ok ;
}
2010-07-30 16:34:53 -04:00
/*****************************************************************************
Verify an incoming ticket and parse out the principal name and
2004-06-22 00:48:59 +00:00
authorization_data if available .
2010-07-30 16:34:53 -04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-06-22 00:48:59 +00:00
2005-09-30 17:13:37 +00:00
NTSTATUS ads_verify_ticket ( TALLOC_CTX * mem_ctx ,
2007-03-15 19:18:18 +00:00
const char * realm ,
time_t time_offset ,
const DATA_BLOB * ticket ,
char * * principal ,
2010-05-06 12:45:14 +10:00
struct PAC_LOGON_INFO * * logon_info ,
2003-03-17 22:46:12 +00:00
DATA_BLOB * ap_rep ,
2007-06-13 20:49:20 +00:00
DATA_BLOB * session_key ,
2007-10-18 17:40:25 -07:00
bool use_replay_cache )
2002-06-25 02:29:09 +00:00
{
2003-07-12 00:27:22 +00:00
NTSTATUS sret = NT_STATUS_LOGON_FAILURE ;
2005-10-11 16:27:05 +00:00
NTSTATUS pac_ret ;
2005-09-30 17:13:37 +00:00
DATA_BLOB auth_data ;
2003-07-12 00:27:22 +00:00
krb5_context context = NULL ;
2002-06-25 02:29:09 +00:00
krb5_auth_context auth_context = NULL ;
krb5_data packet ;
krb5_ticket * tkt = NULL ;
2003-07-12 00:27:22 +00:00
krb5_rcache rcache = NULL ;
2005-09-30 17:13:37 +00:00
krb5_keyblock * keyblock = NULL ;
time_t authtime ;
2007-03-15 19:18:18 +00:00
krb5_error_code ret = 0 ;
2010-07-30 16:34:53 -04:00
int flags = 0 ;
2004-06-22 00:48:59 +00:00
krb5_principal host_princ = NULL ;
2005-09-30 17:13:37 +00:00
krb5_const_principal client_principal = NULL ;
2003-07-12 00:27:22 +00:00
char * host_princ_s = NULL ;
2007-10-18 17:40:25 -07:00
bool auth_ok = False ;
bool got_auth_data = False ;
2008-03-10 21:08:29 +01:00
struct named_mutex * mutex = NULL ;
2002-06-25 02:29:09 +00:00
2003-07-12 00:27:22 +00:00
ZERO_STRUCT ( packet ) ;
2005-09-30 17:13:37 +00:00
ZERO_STRUCT ( auth_data ) ;
2007-03-15 19:18:18 +00:00
* principal = NULL ;
2010-05-06 12:45:14 +10:00
* logon_info = NULL ;
2007-05-14 12:16:20 +00:00
* ap_rep = data_blob_null ;
* session_key = data_blob_null ;
2003-07-12 00:27:22 +00:00
2004-06-22 00:48:59 +00:00
initialize_krb5_error_table ( ) ;
2002-06-25 02:29:09 +00:00
ret = krb5_init_context ( & context ) ;
if ( ret ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 1 , ( __location__ " : krb5_init_context failed (%s) \n " ,
error_message ( ret ) ) ) ;
2002-06-25 02:29:09 +00:00
return NT_STATUS_LOGON_FAILURE ;
}
2006-05-09 19:02:26 +00:00
if ( time_offset ! = 0 ) {
krb5_set_real_time ( context , time ( NULL ) + time_offset , 0 ) ;
}
2003-08-14 21:07:49 +00:00
ret = krb5_set_default_realm ( context , realm ) ;
2002-06-25 02:29:09 +00:00
if ( ret ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 1 , ( __location__ " : krb5_set_default_realm "
" failed (%s) \n " , error_message ( ret ) ) ) ;
2003-07-12 00:27:22 +00:00
goto out ;
2002-06-25 02:29:09 +00:00
}
2003-07-12 00:27:22 +00:00
/* This whole process is far more complex than I would
2002-06-25 02:29:09 +00:00
like . We have to go through all this to allow us to store
the secret internally , instead of using / etc / krb5 . keytab */
2003-07-12 00:27:22 +00:00
2002-06-25 02:29:09 +00:00
ret = krb5_auth_con_init ( context , & auth_context ) ;
if ( ret ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 1 , ( __location__ " : krb5_auth_con_init failed (%s) \n " ,
error_message ( ret ) ) ) ;
2003-07-12 00:27:22 +00:00
goto out ;
2002-06-25 02:29:09 +00:00
}
2007-06-13 20:49:20 +00:00
krb5_auth_con_getflags ( context , auth_context , & flags ) ;
if ( ! use_replay_cache ) {
/* Disable default use of a replay cache */
flags & = ~ KRB5_AUTH_CONTEXT_DO_TIME ;
krb5_auth_con_setflags ( context , auth_context , flags ) ;
}
2011-06-09 15:31:03 +10:00
if ( asprintf ( & host_princ_s , " %s$ " , lp_netbios_name ( ) ) = = - 1 ) {
2007-03-15 19:18:18 +00:00
goto out ;
}
2004-11-02 02:21:26 +00:00
strlower_m ( host_princ_s ) ;
2006-04-24 15:57:54 +00:00
ret = smb_krb5_parse_name ( context , host_princ_s , & host_princ ) ;
2002-06-25 02:29:09 +00:00
if ( ret ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 1 , ( __location__ " : smb_krb5_parse_name(%s) "
" failed (%s) \n " , host_princ_s , error_message ( ret ) ) ) ;
2003-07-12 00:27:22 +00:00
goto out ;
}
2004-06-22 00:48:59 +00:00
2010-07-30 16:34:53 -04:00
if ( use_replay_cache ) {
/* Lock a mutex surrounding the replay as there is no
locking in the MIT krb5 code surrounding the replay
2007-06-13 20:49:20 +00:00
cache . . . */
2004-06-22 00:48:59 +00:00
2010-07-30 16:34:53 -04:00
mutex = grab_named_mutex ( talloc_tos ( ) ,
" replay cache mutex " , 10 ) ;
2008-03-10 21:08:29 +01:00
if ( mutex = = NULL ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 1 , ( __location__ " : unable to protect replay "
" cache with mutex. \n " ) ) ;
2007-06-13 20:49:20 +00:00
ret = KRB5_CC_IO ;
goto out ;
}
2004-06-22 00:48:59 +00:00
2010-07-30 16:34:53 -04:00
/* JRA. We must set the rcache here. This will prevent
2007-06-13 20:49:20 +00:00
replay attacks . */
2010-07-30 16:34:53 -04:00
ret = krb5_get_server_rcache (
context ,
krb5_princ_component ( context , host_princ , 0 ) ,
& rcache ) ;
2007-06-13 20:49:20 +00:00
if ( ret ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 1 , ( __location__ " : krb5_get_server_rcache "
" failed (%s) \n " , error_message ( ret ) ) ) ;
2007-06-13 20:49:20 +00:00
goto out ;
}
2003-07-12 00:27:22 +00:00
2007-06-13 20:49:20 +00:00
ret = krb5_auth_con_setrcache ( context , auth_context , rcache ) ;
if ( ret ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 1 , ( __location__ " : krb5_auth_con_setrcache "
" failed (%s) \n " , error_message ( ret ) ) ) ;
2007-06-13 20:49:20 +00:00
goto out ;
}
2003-07-12 00:27:22 +00:00
}
2009-01-15 17:02:41 -08:00
switch ( lp_kerberos_method ( ) ) {
default :
case KERBEROS_VERIFY_SECRETS :
auth_ok = ads_secrets_verify_ticket ( context , auth_context ,
host_princ , ticket , & tkt , & keyblock , & ret ) ;
break ;
case KERBEROS_VERIFY_SYSTEM_KEYTAB :
auth_ok = ads_keytab_verify_ticket ( context , auth_context ,
ticket , & tkt , & keyblock , & ret ) ;
break ;
case KERBEROS_VERIFY_DEDICATED_KEYTAB :
auth_ok = ads_dedicated_keytab_verify_ticket ( context ,
auth_context , ticket , & tkt , & keyblock , & ret ) ;
break ;
case KERBEROS_VERIFY_SECRETS_AND_KEYTAB :
/* First try secrets.tdb and fallback to the krb5.keytab if
necessary . This is the pre 3.4 behavior when
" use kerberos keytab " was true . */
auth_ok = ads_secrets_verify_ticket ( context , auth_context ,
host_princ , ticket , & tkt , & keyblock , & ret ) ;
if ( ! auth_ok ) {
/* Only fallback if we failed to decrypt the ticket */
if ( ret ! = KRB5KRB_AP_ERR_TKT_NYV & &
ret ! = KRB5KRB_AP_ERR_TKT_EXPIRED & &
ret ! = KRB5KRB_AP_ERR_SKEW ) {
auth_ok = ads_keytab_verify_ticket ( context ,
auth_context , ticket , & tkt , & keyblock ,
& ret ) ;
}
}
break ;
2007-06-13 20:49:20 +00:00
}
2003-07-29 21:32:36 +00:00
2010-07-30 16:34:53 -04:00
if ( use_replay_cache ) {
2008-03-10 21:08:29 +01:00
TALLOC_FREE ( mutex ) ;
2005-09-30 17:13:37 +00:00
#if 0
2007-06-13 20:49:20 +00:00
/* Heimdal leaks here, if we fix the leak, MIT crashes */
if ( rcache ) {
krb5_rc_close ( context , rcache ) ;
}
2005-09-30 17:13:37 +00:00
# endif
2010-07-30 16:34:53 -04:00
}
2005-09-30 17:13:37 +00:00
2003-02-19 15:48:12 +00:00
if ( ! auth_ok ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 3 , ( __location__ " : krb5_rd_req with auth "
" failed (%s) \n " , error_message ( ret ) ) ) ;
2007-03-15 19:18:18 +00:00
/* Try map the error return in case it's something like
* a clock skew error .
*/
sret = krb5_to_nt_status ( ret ) ;
2010-07-30 16:34:53 -04:00
if ( NT_STATUS_IS_OK ( sret ) | |
NT_STATUS_EQUAL ( sret , NT_STATUS_UNSUCCESSFUL ) ) {
2007-03-15 19:18:18 +00:00
sret = NT_STATUS_LOGON_FAILURE ;
}
2010-07-30 16:34:53 -04:00
DEBUG ( 10 , ( __location__ " : returning error %s \n " ,
nt_errstr ( sret ) ) ) ;
2003-07-12 00:27:22 +00:00
goto out ;
2010-07-30 16:34:53 -04:00
}
2006-03-23 17:32:21 +00:00
authtime = get_authtime_from_tkt ( tkt ) ;
client_principal = get_principal_from_tkt ( tkt ) ;
2002-06-25 02:29:09 +00:00
2003-03-17 22:46:12 +00:00
ret = krb5_mk_rep ( context , auth_context , & packet ) ;
if ( ret ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 3 , ( __location__ " : Failed to generate mutual "
" authentication reply (%s) \n " , error_message ( ret ) ) ) ;
2003-07-12 00:27:22 +00:00
goto out ;
2003-03-17 22:46:12 +00:00
}
* ap_rep = data_blob ( packet . data , packet . length ) ;
2007-03-15 19:18:18 +00:00
if ( packet . data ) {
kerberos_free_data_contents ( context , & packet ) ;
ZERO_STRUCT ( packet ) ;
}
2003-03-17 22:46:12 +00:00
2010-07-20 20:00:12 -04:00
get_krb5_smb_session_key ( mem_ctx , context ,
auth_context , session_key , true ) ;
2010-07-30 16:34:53 -04:00
dump_data_pw ( " SMB session key (from ticket) \n " ,
session_key - > data , session_key - > length ) ;
2003-03-17 22:46:12 +00:00
2003-02-19 15:48:12 +00:00
#if 0
file_save ( " /tmp/ticket.dat " , ticket - > data , ticket - > length ) ;
# endif
2010-07-30 16:34:53 -04:00
/* continue when no PAC is retrieved or we couldn't decode the PAC
2005-11-22 10:22:59 +00:00
( like accounts that have the UF_NO_AUTH_DATA_REQUIRED flag set , or
Kerberos tickets encrypted using a DES key ) - Guenther */
2002-06-25 02:29:09 +00:00
2005-09-30 17:13:37 +00:00
got_auth_data = get_auth_data_from_tkt ( mem_ctx , & auth_data , tkt ) ;
if ( ! got_auth_data ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 3 , ( __location__ " : did not retrieve auth data. "
" continuing without PAC \n " ) ) ;
2005-09-30 17:13:37 +00:00
}
2008-01-11 23:53:27 -08:00
if ( got_auth_data ) {
2010-05-06 12:45:14 +10:00
struct PAC_DATA * pac_data ;
2011-04-20 12:05:27 +10:00
pac_ret = kerberos_decode_pac ( mem_ctx , auth_data , context ,
NULL , keyblock , client_principal ,
authtime , & pac_data ) ;
2010-05-06 12:45:14 +10:00
data_blob_free ( & auth_data ) ;
2005-10-11 16:27:05 +00:00
if ( ! NT_STATUS_IS_OK ( pac_ret ) ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 3 , ( __location__ " : failed to decode "
" PAC_DATA: %s \n " , nt_errstr ( pac_ret ) ) ) ;
2010-05-06 12:45:14 +10:00
} else {
uint32_t i ;
2010-07-30 16:34:53 -04:00
for ( i = 0 ; i < pac_data - > num_buffers ; i + + ) {
2010-05-06 12:45:14 +10:00
if ( pac_data - > buffers [ i ] . type ! = PAC_TYPE_LOGON_INFO ) {
continue ;
}
* logon_info = pac_data - > buffers [ i ] . info - > logon_info . info ;
}
if ( ! * logon_info ) {
2010-07-30 16:34:53 -04:00
DEBUG ( 1 , ( " correctly decoded PAC but found "
" no logon_info! "
" This should not happen \n " ) ) ;
2010-05-06 12:45:14 +10:00
return NT_STATUS_INVALID_USER_BUFFER ;
}
2005-09-30 17:13:37 +00:00
}
2003-04-16 16:57:01 +00:00
}
2002-06-25 02:29:09 +00:00
#if 0
2005-09-30 17:13:37 +00:00
# if defined(HAVE_KRB5_TKT_ENC_PART2)
/* MIT */
2002-06-25 02:29:09 +00:00
if ( tkt - > enc_part2 ) {
2003-03-17 22:46:12 +00:00
file_save ( " /tmp/authdata.dat " ,
2002-06-25 02:29:09 +00:00
tkt - > enc_part2 - > authorization_data [ 0 ] - > contents ,
tkt - > enc_part2 - > authorization_data [ 0 ] - > length ) ;
2003-09-03 00:45:15 +00:00
}
2005-09-30 17:13:37 +00:00
# else
/* Heimdal */
if ( tkt - > ticket . authorization_data ) {
file_save ( " /tmp/authdata.dat " ,
tkt - > ticket . authorization_data - > val - > ad_data . data ,
tkt - > ticket . authorization_data - > val - > ad_data . length ) ;
}
# endif
2002-06-25 02:29:09 +00:00
# endif
2010-07-30 16:34:53 -04:00
ret = smb_krb5_unparse_name ( mem_ctx , context ,
client_principal , principal ) ;
if ( ret ) {
DEBUG ( 3 , ( __location__ " : smb_krb5_unparse_name "
" failed (%s) \n " , error_message ( ret ) ) ) ;
2003-07-12 00:27:22 +00:00
sret = NT_STATUS_LOGON_FAILURE ;
goto out ;
}
sret = NT_STATUS_OK ;
2010-07-30 16:34:53 -04:00
out :
2003-07-12 00:27:22 +00:00
2008-03-10 21:08:29 +01:00
TALLOC_FREE ( mutex ) ;
2004-02-03 03:23:18 +00:00
2004-06-22 00:48:59 +00:00
if ( ! NT_STATUS_IS_OK ( sret ) ) {
2005-09-30 17:13:37 +00:00
data_blob_free ( & auth_data ) ;
2004-06-22 00:48:59 +00:00
}
2003-07-12 00:27:22 +00:00
2004-06-22 00:48:59 +00:00
if ( ! NT_STATUS_IS_OK ( sret ) ) {
2003-03-17 22:46:12 +00:00
data_blob_free ( ap_rep ) ;
2004-06-22 00:48:59 +00:00
}
2003-07-12 00:27:22 +00:00
2004-06-22 00:48:59 +00:00
if ( host_princ ) {
2004-01-06 23:57:12 +00:00
krb5_free_principal ( context , host_princ ) ;
2004-06-22 00:48:59 +00:00
}
2004-01-06 23:57:12 +00:00
2005-09-30 17:13:37 +00:00
if ( keyblock ) {
krb5_free_keyblock ( context , keyblock ) ;
}
2004-06-22 00:48:59 +00:00
if ( tkt ! = NULL ) {
2003-08-25 09:13:20 +00:00
krb5_free_ticket ( context , tkt ) ;
2004-06-22 00:48:59 +00:00
}
2003-08-15 01:46:09 +00:00
SAFE_FREE ( host_princ_s ) ;
2003-07-12 00:27:22 +00:00
2004-06-22 00:48:59 +00:00
if ( auth_context ) {
2003-03-17 22:46:12 +00:00
krb5_auth_con_free ( context , auth_context ) ;
2004-06-22 00:48:59 +00:00
}
2002-06-25 02:29:09 +00:00
2004-06-22 00:48:59 +00:00
if ( context ) {
2003-07-12 00:27:22 +00:00
krb5_free_context ( context ) ;
2004-06-22 00:48:59 +00:00
}
2003-03-17 22:46:12 +00:00
2003-07-12 00:27:22 +00:00
return sret ;
2002-06-25 02:29:09 +00:00
}
2003-03-17 22:46:12 +00:00
# endif /* HAVE_KRB5 */