2007-08-24 19:50:12 +04:00
/*
2007-03-13 19:04:17 +03:00
Unix SMB / CIFS implementation .
kerberos locator plugin
Copyright ( C ) Guenther Deschner 2007
2007-08-24 19:50:12 +04:00
2007-03-13 19:04:17 +03: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
2007-03-13 19:04:17 +03:00
( at your option ) any later version .
2007-08-24 19:50:12 +04:00
2007-03-13 19:04:17 +03: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 .
2007-08-24 19:50:12 +04:00
2007-03-13 19:04:17 +03:00
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/>.
2007-03-13 19:04:17 +03:00
*/
# include "includes.h"
# if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H)
# include <krb5/locate_plugin.h>
static const char * get_service_from_locate_service_type ( enum locate_service_type svc )
{
switch ( svc ) {
case locate_service_kdc :
case locate_service_master_kdc :
return " 88 " ;
case locate_service_kadmin :
case locate_service_krb524 :
/* not supported */
return NULL ;
case locate_service_kpasswd :
return " 464 " ;
default :
break ;
}
return NULL ;
}
static const char * locate_service_type_name ( enum locate_service_type svc )
{
switch ( svc ) {
case locate_service_kdc :
return " locate_service_kdc " ;
case locate_service_master_kdc :
return " locate_service_master_kdc " ;
case locate_service_kadmin :
return " locate_service_kadmin " ;
case locate_service_krb524 :
return " locate_service_krb524 " ;
case locate_service_kpasswd :
return " locate_service_kpasswd " ;
default :
break ;
}
return NULL ;
}
static const char * socktype_name ( int socktype )
{
switch ( socktype ) {
case SOCK_STREAM :
return " SOCK_STREAM " ;
case SOCK_DGRAM :
return " SOCK_DGRAM " ;
default :
break ;
}
return " unknown " ;
}
static const char * family_name ( int family )
{
switch ( family ) {
case AF_UNSPEC :
return " AF_UNSPEC " ;
case AF_INET :
return " AF_INET " ;
case AF_INET6 :
return " AF_INET6 " ;
default :
break ;
}
return " unknown " ;
}
/**
* Check input parameters , return KRB5_PLUGIN_NO_HANDLE for unsupported ones
*
2007-08-24 19:50:12 +04:00
* @ param svc
2007-03-13 19:04:17 +03:00
* @ param realm string
* @ param socktype integer
* @ param family integer
*
* @ return integer .
*/
static int smb_krb5_locator_lookup_sanity_check ( enum locate_service_type svc ,
const char * realm ,
int socktype ,
int family )
{
if ( ! realm | | strlen ( realm ) = = 0 ) {
return EINVAL ;
}
switch ( svc ) {
case locate_service_kdc :
case locate_service_master_kdc :
case locate_service_kpasswd :
break ;
case locate_service_kadmin :
case locate_service_krb524 :
# ifdef KRB5_PLUGIN_NO_HANDLE
return KRB5_PLUGIN_NO_HANDLE ;
# else
return KRB5_KDC_UNREACH ; /* Heimdal */
# endif
default :
return EINVAL ;
}
switch ( family ) {
case AF_UNSPEC :
case AF_INET :
break ;
case AF_INET6 : /* not yet */
# ifdef KRB5_PLUGIN_NO_HANDLE
return KRB5_PLUGIN_NO_HANDLE ;
# else
return KRB5_KDC_UNREACH ; /* Heimdal */
# endif
default :
return EINVAL ;
}
switch ( socktype ) {
case SOCK_STREAM :
case SOCK_DGRAM :
case 0 : /* Heimdal uses that */
break ;
default :
return EINVAL ;
}
return 0 ;
}
/**
* Try to get addrinfo for a given host and call the krb5 callback
*
* @ param name string
* @ param service string
* @ param in struct addrinfo hint
* @ param cbfunc krb5 callback function
* @ param cbdata void pointer cbdata
*
* @ return krb5_error_code .
*/
2007-08-24 19:50:12 +04:00
static krb5_error_code smb_krb5_locator_call_cbfunc ( const char * name ,
2007-03-13 19:04:17 +03:00
const char * service ,
struct addrinfo * in ,
int ( * cbfunc ) ( void * , int , struct sockaddr * ) ,
void * cbdata )
{
struct addrinfo * out ;
int ret ;
int count = 3 ;
while ( count ) {
ret = getaddrinfo ( name , service , in , & out ) ;
if ( ret = = 0 ) {
break ;
}
if ( ret = = EAI_AGAIN ) {
count - - ;
continue ;
}
2007-08-24 19:50:12 +04:00
DEBUG ( 10 , ( " smb_krb5_locator_lookup: got ret: %s (%d) \n " ,
2007-03-13 19:04:17 +03:00
gai_strerror ( ret ) , ret ) ) ;
# ifdef KRB5_PLUGIN_NO_HANDLE
return KRB5_PLUGIN_NO_HANDLE ;
# else
return KRB5_KDC_UNREACH ; /* Heimdal */
# endif
}
ret = cbfunc ( cbdata , out - > ai_socktype , out - > ai_addr ) ;
if ( ret ) {
2007-08-24 19:50:12 +04:00
DEBUG ( 10 , ( " smb_krb5_locator_lookup: "
" failed to call callback: %s (%d) \n " ,
2007-03-13 19:04:17 +03:00
error_message ( ret ) , ret ) ) ;
}
freeaddrinfo ( out ) ;
return ret ;
}
/**
* PUBLIC INTERFACE : locate init
*
* @ param context krb5_context
* @ param privata_data pointer to private data pointer
*
* @ return krb5_error_code .
*/
2007-08-24 19:50:12 +04:00
krb5_error_code smb_krb5_locator_init ( krb5_context context ,
2007-03-13 19:04:17 +03:00
void * * private_data )
{
setup_logging ( " smb_krb5_locator " , True ) ;
load_case_tables ( ) ;
lp_load ( dyn_CONFIGFILE , True , False , False , True ) ;
DEBUG ( 10 , ( " smb_krb5_locator_init: called \n " ) ) ;
return 0 ;
}
/**
* PUBLIC INTERFACE : close locate
*
* @ param private_data pointer to private data
*
* @ return void .
*/
void smb_krb5_locator_close ( void * private_data )
{
DEBUG ( 10 , ( " smb_krb5_locator_close: called \n " ) ) ;
2007-03-16 18:48:07 +03:00
/* gfree_all(); */
2007-03-13 19:04:17 +03:00
}
/**
* PUBLIC INTERFACE : locate lookup
*
* @ param private_data pointer to private data
* @ param svc enum locate_service_type .
* @ param realm string
* @ param socktype integer
* @ param family integer
* @ param cbfunc callback function to send back entries
* @ param cbdata void pointer to cbdata
*
* @ return krb5_error_code .
*/
krb5_error_code smb_krb5_locator_lookup ( void * private_data ,
enum locate_service_type svc ,
const char * realm ,
int socktype ,
int family ,
int ( * cbfunc ) ( void * , int , struct sockaddr * ) ,
void * cbdata )
{
NTSTATUS status ;
krb5_error_code ret ;
char * sitename = NULL ;
struct ip_service * ip_list ;
int count = 0 ;
struct addrinfo aihints ;
char * saf_name = NULL ;
2007-08-24 19:50:12 +04:00
const char * service = get_service_from_locate_service_type ( svc ) ;
2007-03-13 19:04:17 +03:00
int i ;
DEBUG ( 10 , ( " smb_krb5_locator_lookup: called for \n " ) ) ;
2007-08-24 19:50:12 +04:00
DEBUGADD ( 10 , ( " \t svc: %s (%d), realm: %s \n " ,
2007-03-13 19:04:17 +03:00
locate_service_type_name ( svc ) , svc , realm ) ) ;
2007-08-24 19:50:12 +04:00
DEBUGADD ( 10 , ( " \t socktype: %s (%d), family: %s (%d) \n " ,
2007-03-13 19:04:17 +03:00
socktype_name ( socktype ) , socktype ,
family_name ( family ) , family ) ) ;
2007-08-24 19:50:12 +04:00
ret = smb_krb5_locator_lookup_sanity_check ( svc , realm , socktype ,
family ) ;
2007-03-13 19:04:17 +03:00
if ( ret ) {
2007-08-24 19:50:12 +04:00
DEBUG ( 10 , ( " smb_krb5_locator_lookup: returning ret: %s (%d) \n " ,
2007-03-13 19:04:17 +03:00
error_message ( ret ) , ret ) ) ;
return ret ;
}
/* first try to fetch from SAF cache */
saf_name = saf_fetch ( realm ) ;
if ( ! saf_name | | strlen ( saf_name ) = = 0 ) {
2007-08-24 19:50:12 +04:00
DEBUG ( 10 , ( " smb_krb5_locator_lookup: "
" no SAF name stored for %s \n " ,
2007-03-13 19:04:17 +03:00
realm ) ) ;
goto find_kdc ;
}
2007-08-24 19:50:12 +04:00
DEBUG ( 10 , ( " smb_krb5_locator_lookup: got %s for %s from SAF cache \n " ,
2007-03-13 19:04:17 +03:00
saf_name , realm ) ) ;
ZERO_STRUCT ( aihints ) ;
2007-08-24 19:50:12 +04:00
2007-03-13 19:04:17 +03:00
aihints . ai_family = family ;
aihints . ai_socktype = socktype ;
2007-08-24 19:50:12 +04:00
ret = smb_krb5_locator_call_cbfunc ( saf_name ,
service ,
& aihints ,
2007-03-13 19:04:17 +03:00
cbfunc , cbdata ) ;
if ( ret ) {
return ret ;
}
return 0 ;
find_kdc :
/* now try to find via site-aware DNS SRV query */
sitename = sitename_fetch ( realm ) ;
status = get_kdc_list ( realm , sitename , & ip_list , & count ) ;
/* if we didn't found any KDCs on our site go to the main list */
if ( NT_STATUS_IS_OK ( status ) & & sitename & & ( count = = 0 ) ) {
2007-03-16 18:48:07 +03:00
SAFE_FREE ( ip_list ) ;
2007-03-13 19:04:17 +03:00
SAFE_FREE ( sitename ) ;
status = get_kdc_list ( realm , NULL , & ip_list , & count ) ;
}
SAFE_FREE ( sitename ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " smb_krb5_locator_lookup: got %s (%s) \n " ,
2007-08-24 19:50:12 +04:00
nt_errstr ( status ) ,
2007-03-13 19:04:17 +03:00
error_message ( nt_status_to_krb5 ( status ) ) ) ) ;
# ifdef KRB5_PLUGIN_NO_HANDLE
return KRB5_PLUGIN_NO_HANDLE ;
# else
return KRB5_KDC_UNREACH ; /* Heimdal */
# endif
}
for ( i = 0 ; i < count ; i + + ) {
const char * host = NULL ;
const char * port = NULL ;
ZERO_STRUCT ( aihints ) ;
aihints . ai_family = family ;
aihints . ai_socktype = socktype ;
host = inet_ntoa ( ip_list [ i ] . ip ) ;
port = get_service_from_locate_service_type ( svc ) ;
ret = smb_krb5_locator_call_cbfunc ( host ,
port ,
2007-08-24 19:50:12 +04:00
& aihints ,
2007-03-13 19:04:17 +03:00
cbfunc , cbdata ) ;
if ( ret ) {
/* got error */
break ;
}
}
2007-03-16 18:48:07 +03:00
SAFE_FREE ( ip_list ) ;
2007-03-13 19:04:17 +03:00
return ret ;
}
# ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H
# define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */
# else
# define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */
# endif
const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = {
0 , /* version */
smb_krb5_locator_init ,
smb_krb5_locator_close ,
smb_krb5_locator_lookup ,
} ;
# endif