2001-10-11 11:42:52 +04:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
2001-10-12 08:49:42 +04:00
simple kerberos5 routines for active directory
2001-10-11 11:42:52 +04:00
Copyright ( C ) Andrew Tridgell 2001
2003-01-20 01:27:32 +03:00
Copyright ( C ) Luke Howard 2002
2001-10-11 11:42:52 +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
the Free Software Foundation ; either version 2 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 , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2001-11-29 02:54:07 +03:00
# ifdef HAVE_KRB5
2003-01-20 01:27:32 +03:00
2003-01-20 22:37:11 +03:00
# ifndef HAVE_KRB5_SET_REAL_TIME
2003-01-20 01:27:32 +03:00
/*
* This function is not in the Heimdal mainline .
*/
2003-01-21 16:33:31 +03:00
krb5_error_code krb5_set_real_time ( krb5_context context , int32_t seconds , int32_t microseconds )
2003-01-20 01:27:32 +03:00
{
krb5_error_code ret ;
int32_t sec , usec ;
ret = krb5_us_timeofday ( context , & sec , & usec ) ;
if ( ret )
return ret ;
context - > kdc_sec_offset = seconds - sec ;
context - > kdc_usec_offset = microseconds - usec ;
return 0 ;
}
# endif
# if defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) && !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
2003-01-21 16:33:31 +03:00
krb5_error_code krb5_set_default_tgs_ktypes ( krb5_context ctx , const krb5_enctype * enc )
2003-01-20 01:27:32 +03:00
{
return krb5_set_default_in_tkt_etypes ( ctx , enc ) ;
}
# endif
2003-01-21 09:23:49 +03:00
# if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
/* HEIMDAL */
2003-01-21 16:33:31 +03:00
void setup_kaddr ( krb5_address * pkaddr , struct sockaddr * paddr )
2003-01-21 09:23:49 +03:00
{
pkaddr - > addr_type = KRB5_ADDRESS_INET ;
pkaddr - > address . length = sizeof ( ( ( struct sockaddr_in * ) paddr ) - > sin_addr ) ;
pkaddr - > address . data = ( char * ) & ( ( ( struct sockaddr_in * ) paddr ) - > sin_addr ) ;
}
# elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
/* MIT */
2003-01-21 16:33:31 +03:00
void setup_kaddr ( krb5_address * pkaddr , struct sockaddr * paddr )
2003-01-21 09:23:49 +03:00
{
pkaddr - > addrtype = ADDRTYPE_INET ;
pkaddr - > length = sizeof ( ( ( struct sockaddr_in * ) paddr ) - > sin_addr ) ;
pkaddr - > contents = ( char * ) & ( ( ( struct sockaddr_in * ) paddr ) - > sin_addr ) ;
}
# else
2003-01-21 16:33:31 +03:00
__ERROR__XX__UNKNOWN_ADDRTYPE
2003-01-21 09:23:49 +03:00
# endif
2003-01-30 21:01:03 +03:00
# if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY)
int create_kerberos_key_from_string ( krb5_context context ,
krb5_principal host_princ ,
krb5_data * password ,
krb5_keyblock * key )
{
int ret ;
krb5_data salt ;
krb5_encrypt_block eblock ;
ret = krb5_principal2salt ( context , host_princ , & salt ) ;
if ( ret ) {
DEBUG ( 1 , ( " krb5_principal2salt failed (%s) \n " , error_message ( ret ) ) ) ;
return ret ;
}
krb5_use_enctype ( context , & eblock , ENCTYPE_DES_CBC_MD5 ) ;
return krb5_string_to_key ( context , & eblock , key , password , & salt ) ;
}
# elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
int create_kerberos_key_from_string ( krb5_context context ,
krb5_principal host_princ ,
krb5_data * password ,
krb5_keyblock * key )
{
int ret ;
krb5_salt salt ;
ret = krb5_get_pw_salt ( context , host_princ , & salt ) ;
if ( ret ) {
DEBUG ( 1 , ( " krb5_get_pw_salt failed (%s) \n " , error_message ( ret ) ) ) ;
return ret ;
}
return krb5_string_to_key_salt ( context , ENCTYPE_DES_CBC_MD5 , password - > data ,
salt , key ) ;
}
# else
__ERROR_XX_UNKNOWN_CREATE_KEY_FUNCTIONS
# endif
# if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
krb5_error_code krb5_auth_con_setuseruserkey ( krb5_context context ,
krb5_auth_context auth_context ,
krb5_keyblock * keyblock )
{
return krb5_auth_con_setkey ( context , auth_context , keyblock ) ;
}
# endif
void get_auth_data_from_tkt ( DATA_BLOB * auth_data , krb5_ticket * tkt )
{
# if defined(HAVE_KRB5_TKT_ENC_PART2)
if ( tkt - > enc_part2 )
* auth_data = data_blob ( tkt - > enc_part2 - > authorization_data [ 0 ] - > contents ,
tkt - > enc_part2 - > authorization_data [ 0 ] - > length ) ;
# else
if ( tkt - > ticket . authorization_data & & tkt - > ticket . authorization_data - > len )
* auth_data = data_blob ( tkt - > ticket . authorization_data - > val - > ad_data . data ,
tkt - > ticket . authorization_data - > val - > ad_data . length ) ;
# endif
}
krb5_const_principal get_principal_from_tkt ( krb5_ticket * tkt )
{
# if defined(HAVE_KRB5_TKT_ENC_PART2)
return tkt - > enc_part2 - > client ;
# else
return tkt - > client ;
# endif
}
# if !defined(HAVE_KRB5_LOCATE_KDC)
krb5_error_code krb5_locate_kdc ( krb5_context ctx , const krb5_data * realm , struct sockaddr * * addr_pp , int * naddrs , int get_masters )
{
krb5_krbhst_handle hnd ;
krb5_krbhst_info * hinfo ;
krb5_error_code rc ;
int num_kdcs , i ;
struct sockaddr * sa ;
* addr_pp = NULL ;
* naddrs = 0 ;
rc = krb5_krbhst_init ( ctx , realm - > data , KRB5_KRBHST_KDC , & hnd ) ;
if ( rc ) {
DEBUG ( 0 , ( " krb5_locate_kdc: krb5_krbhst_init failed (%s) \n " , error_message ( rc ) ) ) ;
return rc ;
}
for ( num_kdcs = 0 ; ( rc = krb5_krbhst_next ( ctx , hnd , & hinfo ) = = 0 ) ; num_kdcs + + )
;
krb5_krbhst_reset ( ctx , hnd ) ;
if ( ! num_kdcs ) {
DEBUG ( 0 , ( " krb5_locate_kdc: zero kdcs found ! \n " ) ) ;
krb5_krbhst_free ( ctx , hnd ) ;
return - 1 ;
}
sa = malloc ( sizeof ( struct sockaddr ) * num_kdcs ) ;
if ( ! sa ) {
DEBUG ( 0 , ( " krb5_locate_kdc: malloc failed \n " ) ) ;
krb5_krbhst_free ( ctx , hnd ) ;
naddrs = 0 ;
return - 1 ;
}
memset ( * addr_pp , ' \0 ' , sizeof ( struct sockaddr ) * num_kdcs ) ;
for ( i = 0 ; i < num_kdcs & & ( rc = krb5_krbhst_next ( ctx , hnd , & hinfo ) = = 0 ) ; i + + ) {
if ( hinfo - > ai - > ai_family = = AF_INET )
memcpy ( & sa [ i ] , hinfo - > ai - > ai_addr , sizeof ( struct sockaddr ) ) ;
}
krb5_krbhst_free ( ctx , hnd ) ;
* naddrs = num_kdcs ;
* addr_pp = sa ;
return 0 ;
}
# endif
2001-10-11 14:29:17 +04:00
/*
we can ' t use krb5_mk_req because w2k wants the service to be in a particular format
*/
2001-10-11 11:42:52 +04:00
static krb5_error_code krb5_mk_req2 ( krb5_context context ,
krb5_auth_context * auth_context ,
const krb5_flags ap_req_options ,
2001-10-22 00:51:27 +04:00
const char * principal ,
2001-10-11 11:42:52 +04:00
krb5_ccache ccache ,
krb5_data * outbuf )
{
2001-10-11 17:13:06 +04:00
krb5_error_code retval ;
krb5_principal server ;
krb5_creds * credsp ;
krb5_creds creds ;
krb5_data in_data ;
2001-10-22 00:51:27 +04:00
retval = krb5_parse_name ( context , principal , & server ) ;
2001-10-11 17:13:06 +04:00
if ( retval ) {
2001-10-22 00:51:27 +04:00
DEBUG ( 1 , ( " Failed to parse principal %s \n " , principal ) ) ;
2001-10-11 17:13:06 +04:00
return retval ;
}
/* obtain ticket & session key */
memset ( ( char * ) & creds , 0 , sizeof ( creds ) ) ;
2001-10-20 10:50:24 +04:00
if ( ( retval = krb5_copy_principal ( context , server , & creds . server ) ) ) {
DEBUG ( 1 , ( " krb5_copy_principal failed (%s) \n " ,
error_message ( retval ) ) ) ;
2001-10-11 17:13:06 +04:00
goto cleanup_princ ;
2001-10-20 10:50:24 +04:00
}
2001-10-11 17:13:06 +04:00
2001-10-20 10:50:24 +04:00
if ( ( retval = krb5_cc_get_principal ( context , ccache , & creds . client ) ) ) {
DEBUG ( 1 , ( " krb5_cc_get_principal failed (%s) \n " ,
error_message ( retval ) ) ) ;
2001-10-11 17:13:06 +04:00
goto cleanup_creds ;
2001-10-20 10:50:24 +04:00
}
2001-10-11 17:13:06 +04:00
if ( ( retval = krb5_get_credentials ( context , 0 ,
ccache , & creds , & credsp ) ) ) {
2001-12-19 12:53:30 +03:00
DEBUG ( 1 , ( " krb5_get_credentials failed for %s (%s) \n " ,
principal , error_message ( retval ) ) ) ;
2001-10-11 17:13:06 +04:00
goto cleanup_creds ;
}
2001-10-11 11:42:52 +04:00
2002-09-25 19:19:00 +04:00
/* cope with the ticket being in the future due to clock skew */
if ( ( unsigned ) credsp - > times . starttime > time ( NULL ) ) {
time_t t = time ( NULL ) ;
int time_offset = ( unsigned ) credsp - > times . starttime - t ;
DEBUG ( 4 , ( " Advancing clock by %d seconds to cope with clock skew \n " , time_offset ) ) ;
krb5_set_real_time ( context , t + time_offset + 1 , 0 ) ;
}
2001-10-11 17:13:06 +04:00
in_data . length = 0 ;
retval = krb5_mk_req_extended ( context , auth_context , ap_req_options ,
& in_data , credsp , outbuf ) ;
if ( retval ) {
2001-10-20 10:50:24 +04:00
DEBUG ( 1 , ( " krb5_mk_req_extended failed (%s) \n " ,
error_message ( retval ) ) ) ;
2001-10-11 17:13:06 +04:00
}
krb5_free_creds ( context , credsp ) ;
2001-10-11 11:42:52 +04:00
cleanup_creds :
2001-10-11 17:13:06 +04:00
krb5_free_cred_contents ( context , & creds ) ;
2001-10-11 11:42:52 +04:00
cleanup_princ :
2001-10-11 17:13:06 +04:00
krb5_free_principal ( context , server ) ;
2001-10-11 11:42:52 +04:00
2001-10-11 17:13:06 +04:00
return retval ;
2001-10-11 11:42:52 +04:00
}
/*
get a kerberos5 ticket for the given service
*/
2003-01-03 11:28:12 +03:00
DATA_BLOB krb5_get_ticket ( const char * principal , time_t time_offset )
2001-10-11 11:42:52 +04:00
{
krb5_error_code retval ;
2001-10-11 17:13:06 +04:00
krb5_data packet ;
2001-10-11 11:42:52 +04:00
krb5_ccache ccdef ;
krb5_context context ;
krb5_auth_context auth_context = NULL ;
DATA_BLOB ret ;
2002-09-25 19:19:00 +04:00
krb5_enctype enc_types [ ] = {
# ifdef ENCTYPE_ARCFOUR_HMAC
ENCTYPE_ARCFOUR_HMAC ,
# endif
ENCTYPE_DES_CBC_MD5 ,
2003-02-18 23:57:29 +03:00
ENCTYPE_DES_CBC_CRC ,
2002-09-25 19:19:00 +04:00
ENCTYPE_NULL } ;
2001-10-11 11:42:52 +04:00
retval = krb5_init_context ( & context ) ;
if ( retval ) {
2001-10-20 10:50:24 +04:00
DEBUG ( 1 , ( " krb5_init_context failed (%s) \n " ,
error_message ( retval ) ) ) ;
2001-10-11 11:42:52 +04:00
goto failed ;
}
2002-09-25 19:19:00 +04:00
if ( time_offset ! = 0 ) {
krb5_set_real_time ( context , time ( NULL ) + time_offset , 0 ) ;
}
2001-10-11 11:42:52 +04:00
if ( ( retval = krb5_cc_default ( context , & ccdef ) ) ) {
2001-10-20 10:50:24 +04:00
DEBUG ( 1 , ( " krb5_cc_default failed (%s) \n " ,
error_message ( retval ) ) ) ;
2001-10-11 11:42:52 +04:00
goto failed ;
}
2001-11-24 17:16:41 +03:00
if ( ( retval = krb5_set_default_tgs_ktypes ( context , enc_types ) ) ) {
DEBUG ( 1 , ( " krb5_set_default_tgs_ktypes failed (%s) \n " ,
error_message ( retval ) ) ) ;
goto failed ;
}
2001-10-11 11:42:52 +04:00
if ( ( retval = krb5_mk_req2 ( context ,
& auth_context ,
2001-10-18 14:26:06 +04:00
0 ,
2001-10-22 00:51:27 +04:00
principal ,
2001-10-11 17:13:06 +04:00
ccdef , & packet ) ) ) {
2001-10-11 11:42:52 +04:00
goto failed ;
}
ret = data_blob ( packet . data , packet . length ) ;
2001-10-16 16:02:18 +04:00
/* Hmm, heimdal dooesn't have this - what's the correct call? */
/* krb5_free_data_contents(context, &packet); */
2001-10-11 14:29:17 +04:00
krb5_free_context ( context ) ;
2001-10-11 11:42:52 +04:00
return ret ;
failed :
2002-11-15 20:57:25 +03:00
if ( context )
krb5_free_context ( context ) ;
2001-10-11 11:42:52 +04:00
return data_blob ( NULL , 0 ) ;
}
# else /* HAVE_KRB5 */
2001-10-12 08:49:42 +04:00
/* this saves a few linking headaches */
2003-01-04 12:06:46 +03:00
DATA_BLOB krb5_get_ticket ( const char * principal , time_t time_offset )
2001-10-12 08:49:42 +04:00
{
DEBUG ( 0 , ( " NO KERBEROS SUPPORT \n " ) ) ;
return data_blob ( NULL , 0 ) ;
}
2001-10-11 11:42:52 +04:00
# endif