2005-09-17 05:11:50 +04:00
/*
Unix SMB / CIFS implementation .
Wrapper for krb5_init_context
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2005
2005-09-28 05:09:10 +04:00
Copyright ( C ) Andrew Tridgell 2005
Copyright ( C ) Stefan Metzmacher 2004
2005-09-17 05:11:50 +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-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2005-09-17 05:11:50 +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 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2005-09-17 05:11:50 +04:00
*/
# include "includes.h"
# include "system/kerberos.h"
2006-11-07 09:59:56 +03:00
# include "heimdal/lib/krb5/krb5_locl.h"
2005-09-17 05:11:50 +04:00
# include "auth/kerberos/kerberos.h"
2005-09-28 05:09:10 +04:00
# include "lib/socket/socket.h"
2007-05-17 09:46:45 +04:00
# include "lib/stream/packet.h"
2006-03-11 11:11:33 +03:00
# include "system/network.h"
2005-09-28 05:09:10 +04:00
# include "lib/events/events.h"
2006-04-26 03:03:05 +04:00
# include "roken.h"
2005-11-09 13:17:05 +03:00
2005-09-28 05:09:10 +04:00
/*
context structure for operations on cldap packets
*/
struct smb_krb5_socket {
struct socket_context * sock ;
/* the fd event */
struct fd_event * fde ;
NTSTATUS status ;
2007-05-17 09:46:45 +04:00
DATA_BLOB request , reply ;
struct packet_context * packet ;
2005-09-28 05:09:10 +04:00
size_t partial_read ;
krb5_krbhst_info * hi ;
} ;
2005-09-17 05:11:50 +04:00
2006-05-24 11:32:17 +04:00
static int smb_krb5_context_destroy_1 ( struct smb_krb5_context * ctx )
2005-09-17 05:11:50 +04:00
{
krb5_free_context ( ctx - > krb5_context ) ;
return 0 ;
}
2006-05-24 11:32:17 +04:00
static int smb_krb5_context_destroy_2 ( struct smb_krb5_context * ctx )
2005-09-17 05:11:50 +04:00
{
/* Otherwise krb5_free_context will try and close what we have already free()ed */
krb5_set_warn_dest ( ctx - > krb5_context , NULL ) ;
krb5_closelog ( ctx - > krb5_context , ctx - > logf ) ;
2006-05-24 11:32:17 +04:00
smb_krb5_context_destroy_1 ( ctx ) ;
2005-09-17 05:11:50 +04:00
return 0 ;
}
/* We never close down the DEBUG system, and no need to unreference the use */
static void smb_krb5_debug_close ( void * private ) {
return ;
}
static void smb_krb5_debug_wrapper ( const char * timestr , const char * msg , void * private )
{
2006-11-07 09:59:56 +03:00
DEBUG ( 2 , ( " Kerberos: %s \n " , msg ) ) ;
2005-09-17 05:11:50 +04:00
}
2005-09-28 05:09:10 +04:00
/*
handle recv events on a smb_krb5 socket
*/
static void smb_krb5_socket_recv ( struct smb_krb5_socket * smb_krb5 )
{
TALLOC_CTX * tmp_ctx = talloc_new ( smb_krb5 ) ;
DATA_BLOB blob ;
size_t nread , dsize ;
2007-05-17 09:46:45 +04:00
smb_krb5 - > status = socket_pending ( smb_krb5 - > sock , & dsize ) ;
if ( ! NT_STATUS_IS_OK ( smb_krb5 - > status ) ) {
2005-09-28 05:09:10 +04:00
talloc_free ( tmp_ctx ) ;
return ;
}
2007-05-17 09:46:45 +04:00
blob = data_blob_talloc ( tmp_ctx , NULL , dsize ) ;
if ( blob . data = = NULL & & dsize ! = 0 ) {
smb_krb5 - > status = NT_STATUS_NO_MEMORY ;
talloc_free ( tmp_ctx ) ;
return ;
}
smb_krb5 - > status = socket_recv ( smb_krb5 - > sock , blob . data , blob . length , & nread ) ;
if ( ! NT_STATUS_IS_OK ( smb_krb5 - > status ) ) {
talloc_free ( tmp_ctx ) ;
return ;
}
blob . length = nread ;
if ( nread = = 0 ) {
smb_krb5 - > status = NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
talloc_free ( tmp_ctx ) ;
return ;
}
DEBUG ( 2 , ( " Received smb_krb5 packet of length %d \n " ,
( int ) blob . length ) ) ;
talloc_steal ( smb_krb5 , blob . data ) ;
smb_krb5 - > reply = blob ;
talloc_free ( tmp_ctx ) ;
}
static NTSTATUS smb_krb5_full_packet ( void * private , DATA_BLOB data )
{
struct smb_krb5_socket * smb_krb5 = talloc_get_type ( private , struct smb_krb5_socket ) ;
talloc_steal ( smb_krb5 , data . data ) ;
smb_krb5 - > reply = data ;
smb_krb5 - > reply . length - = 4 ;
smb_krb5 - > reply . data + = 4 ;
return NT_STATUS_OK ;
2005-09-28 05:09:10 +04:00
}
/*
handle request timeouts
*/
static void smb_krb5_request_timeout ( struct event_context * event_ctx ,
struct timed_event * te , struct timeval t ,
void * private )
{
struct smb_krb5_socket * smb_krb5 = talloc_get_type ( private , struct smb_krb5_socket ) ;
2005-10-14 10:06:18 +04:00
DEBUG ( 5 , ( " Timed out smb_krb5 packet \n " ) ) ;
2007-05-17 09:46:45 +04:00
smb_krb5 - > status = NT_STATUS_IO_TIMEOUT ;
}
static void smb_krb5_error_handler ( void * private , NTSTATUS status )
{
struct smb_krb5_socket * smb_krb5 = talloc_get_type ( private , struct smb_krb5_socket ) ;
smb_krb5 - > status = status ;
2005-09-28 05:09:10 +04:00
}
/*
handle send events on a smb_krb5 socket
*/
static void smb_krb5_socket_send ( struct smb_krb5_socket * smb_krb5 )
{
NTSTATUS status ;
size_t len ;
len = smb_krb5 - > request . length ;
2006-04-30 09:58:31 +04:00
status = socket_send ( smb_krb5 - > sock , & smb_krb5 - > request , & len ) ;
2005-09-28 05:09:10 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) return ;
EVENT_FD_READABLE ( smb_krb5 - > fde ) ;
EVENT_FD_NOT_WRITEABLE ( smb_krb5 - > fde ) ;
return ;
}
/*
handle fd events on a smb_krb5_socket
*/
static void smb_krb5_socket_handler ( struct event_context * ev , struct fd_event * fde ,
uint16_t flags , void * private )
{
struct smb_krb5_socket * smb_krb5 = talloc_get_type ( private , struct smb_krb5_socket ) ;
2007-05-17 09:46:45 +04:00
switch ( smb_krb5 - > hi - > proto ) {
case KRB5_KRBHST_UDP :
if ( flags & EVENT_FD_READ ) {
smb_krb5_socket_recv ( smb_krb5 ) ;
2007-05-18 09:47:33 +04:00
return ;
2007-05-17 09:46:45 +04:00
}
2007-05-18 09:47:33 +04:00
if ( flags & EVENT_FD_WRITE ) {
smb_krb5_socket_send ( smb_krb5 ) ;
return ;
}
/* not reached */
return ;
2007-05-17 09:46:45 +04:00
case KRB5_KRBHST_TCP :
if ( flags & EVENT_FD_READ ) {
packet_recv ( smb_krb5 - > packet ) ;
2007-05-18 09:47:33 +04:00
return ;
2007-05-17 09:46:45 +04:00
}
if ( flags & EVENT_FD_WRITE ) {
packet_queue_run ( smb_krb5 - > packet ) ;
2007-05-18 09:47:33 +04:00
return ;
2007-05-17 09:46:45 +04:00
}
2007-05-18 09:47:33 +04:00
/* not reached */
return ;
2007-05-17 09:46:45 +04:00
case KRB5_KRBHST_HTTP :
/* can't happen */
break ;
2005-09-28 05:09:10 +04:00
}
}
2006-11-07 09:59:56 +03:00
krb5_error_code smb_krb5_send_and_recv_func ( krb5_context context ,
void * data ,
krb5_krbhst_info * hi ,
const krb5_data * send_buf ,
krb5_data * recv_buf )
2005-09-28 05:09:10 +04:00
{
krb5_error_code ret ;
NTSTATUS status ;
2006-01-10 01:12:53 +03:00
struct socket_address * remote_addr ;
2005-09-28 05:09:10 +04:00
const char * name ;
struct addrinfo * ai , * a ;
struct smb_krb5_socket * smb_krb5 ;
struct event_context * ev = talloc_get_type ( data , struct event_context ) ;
DATA_BLOB send_blob = data_blob_const ( send_buf - > data , send_buf - > length ) ;
ret = krb5_krbhst_get_addrinfo ( context , hi , & ai ) ;
if ( ret ) {
return ret ;
}
for ( a = ai ; a ; a = ai - > ai_next ) {
smb_krb5 = talloc ( NULL , struct smb_krb5_socket ) ;
if ( ! smb_krb5 ) {
return ENOMEM ;
}
smb_krb5 - > hi = hi ;
switch ( a - > ai_family ) {
case PF_INET :
name = " ipv4 " ;
break ;
2007-04-30 15:27:41 +04:00
# ifdef HAVE_IPV6
2005-09-28 05:09:10 +04:00
case PF_INET6 :
name = " ipv6 " ;
break ;
2006-05-25 02:17:01 +04:00
# endif
2005-09-28 05:09:10 +04:00
default :
talloc_free ( smb_krb5 ) ;
return EINVAL ;
}
status = NT_STATUS_INVALID_PARAMETER ;
switch ( hi - > proto ) {
case KRB5_KRBHST_UDP :
2005-10-14 10:06:18 +04:00
if ( lp_parm_bool ( - 1 , " krb5 " , " udp " , True ) ) {
status = socket_create ( name , SOCKET_TYPE_DGRAM , & smb_krb5 - > sock , 0 ) ;
}
2006-03-13 09:56:31 +03:00
break ;
2005-09-28 05:09:10 +04:00
case KRB5_KRBHST_TCP :
2005-10-14 10:06:18 +04:00
if ( lp_parm_bool ( - 1 , " krb5 " , " tcp " , True ) ) {
status = socket_create ( name , SOCKET_TYPE_STREAM , & smb_krb5 - > sock , 0 ) ;
}
2006-03-13 09:56:31 +03:00
break ;
2005-09-28 05:09:10 +04:00
case KRB5_KRBHST_HTTP :
talloc_free ( smb_krb5 ) ;
return EINVAL ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( smb_krb5 ) ;
continue ;
}
talloc_steal ( smb_krb5 , smb_krb5 - > sock ) ;
2006-01-10 01:12:53 +03:00
remote_addr = socket_address_from_sockaddr ( smb_krb5 , a - > ai_addr , a - > ai_addrlen ) ;
if ( ! remote_addr ) {
2005-09-28 05:09:10 +04:00
talloc_free ( smb_krb5 ) ;
2006-01-10 01:12:53 +03:00
continue ;
2005-09-28 05:09:10 +04:00
}
2006-01-10 01:12:53 +03:00
status = socket_connect_ev ( smb_krb5 - > sock , NULL , remote_addr , 0 , ev ) ;
2005-09-28 05:09:10 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( smb_krb5 ) ;
continue ;
}
talloc_free ( remote_addr ) ;
2007-05-18 09:47:33 +04:00
/* Setup the FDE, start listening for read events
* from the start ( otherwise we may miss a socket
* drop ) and mark as AUTOCLOSE along with the fde */
/* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */
2007-05-17 09:46:45 +04:00
smb_krb5 - > fde = event_add_fd ( ev , smb_krb5 - > sock ,
2007-05-17 06:21:07 +04:00
socket_get_fd ( smb_krb5 - > sock ) ,
2007-05-18 09:47:33 +04:00
EVENT_FD_READ | EVENT_FD_AUTOCLOSE ,
2005-09-28 05:09:10 +04:00
smb_krb5_socket_handler , smb_krb5 ) ;
2007-05-17 06:21:07 +04:00
/* its now the job of the event layer to close the socket */
socket_set_flags ( smb_krb5 - > sock , SOCKET_FLAG_NOCLOSE ) ;
2005-09-28 05:09:10 +04:00
event_add_timed ( ev , smb_krb5 ,
timeval_current_ofs ( context - > kdc_timeout , 0 ) ,
smb_krb5_request_timeout , smb_krb5 ) ;
2007-05-17 09:46:45 +04:00
smb_krb5 - > status = NT_STATUS_OK ;
smb_krb5 - > reply = data_blob ( NULL , 0 ) ;
2005-09-28 05:09:10 +04:00
switch ( hi - > proto ) {
case KRB5_KRBHST_UDP :
2007-05-17 09:46:45 +04:00
EVENT_FD_WRITEABLE ( smb_krb5 - > fde ) ;
2005-09-28 05:09:10 +04:00
smb_krb5 - > request = send_blob ;
break ;
case KRB5_KRBHST_TCP :
2007-05-17 09:46:45 +04:00
smb_krb5 - > packet = packet_init ( smb_krb5 ) ;
if ( smb_krb5 - > packet = = NULL ) {
talloc_free ( smb_krb5 ) ;
return ENOMEM ;
}
packet_set_private ( smb_krb5 - > packet , smb_krb5 ) ;
packet_set_socket ( smb_krb5 - > packet , smb_krb5 - > sock ) ;
packet_set_callback ( smb_krb5 - > packet , smb_krb5_full_packet ) ;
packet_set_full_request ( smb_krb5 - > packet , packet_full_request_u32 ) ;
packet_set_error_handler ( smb_krb5 - > packet , smb_krb5_error_handler ) ;
packet_set_event_context ( smb_krb5 - > packet , ev ) ;
packet_set_fde ( smb_krb5 - > packet , smb_krb5 - > fde ) ;
2005-09-28 05:09:10 +04:00
smb_krb5 - > request = data_blob_talloc ( smb_krb5 , NULL , send_blob . length + 4 ) ;
RSIVAL ( smb_krb5 - > request . data , 0 , send_blob . length ) ;
memcpy ( smb_krb5 - > request . data + 4 , send_blob . data , send_blob . length ) ;
2007-05-17 09:46:45 +04:00
packet_send ( smb_krb5 - > packet , smb_krb5 - > request ) ;
2005-09-28 05:09:10 +04:00
break ;
case KRB5_KRBHST_HTTP :
talloc_free ( smb_krb5 ) ;
return EINVAL ;
}
2007-05-17 09:46:45 +04:00
while ( ( NT_STATUS_IS_OK ( smb_krb5 - > status ) ) & & ! smb_krb5 - > reply . length ) {
2005-09-28 05:09:10 +04:00
if ( event_loop_once ( ev ) ! = 0 ) {
talloc_free ( smb_krb5 ) ;
return EINVAL ;
}
}
2007-05-17 09:46:45 +04:00
if ( NT_STATUS_EQUAL ( smb_krb5 - > status , NT_STATUS_IO_TIMEOUT ) ) {
2005-09-28 05:09:10 +04:00
talloc_free ( smb_krb5 ) ;
continue ;
}
2007-05-17 09:46:45 +04:00
if ( ! NT_STATUS_IS_OK ( smb_krb5 - > status ) ) {
DEBUG ( 2 , ( " Error reading smb_krb5 reply packet: %s \n " , nt_errstr ( smb_krb5 - > status ) ) ) ;
2005-09-28 05:09:10 +04:00
talloc_free ( smb_krb5 ) ;
continue ;
}
ret = krb5_data_copy ( recv_buf , smb_krb5 - > reply . data , smb_krb5 - > reply . length ) ;
if ( ret ) {
talloc_free ( smb_krb5 ) ;
return ret ;
}
talloc_free ( smb_krb5 ) ;
break ;
}
if ( a ) {
return 0 ;
}
return KRB5_KDC_UNREACH ;
}
2006-03-05 20:50:47 +03:00
krb5_error_code smb_krb5_init_context ( void * parent_ctx ,
2007-05-17 12:47:04 +04:00
struct event_context * ev ,
2005-09-17 05:11:50 +04:00
struct smb_krb5_context * * smb_krb5_context )
{
krb5_error_code ret ;
TALLOC_CTX * tmp_ctx ;
2007-01-24 05:48:40 +03:00
char * * config_files ;
const char * config_file ;
2005-09-17 05:11:50 +04:00
initialize_krb5_error_table ( ) ;
tmp_ctx = talloc_new ( parent_ctx ) ;
* smb_krb5_context = talloc ( tmp_ctx , struct smb_krb5_context ) ;
if ( ! * smb_krb5_context | | ! tmp_ctx ) {
talloc_free ( tmp_ctx ) ;
return ENOMEM ;
}
ret = krb5_init_context ( & ( * smb_krb5_context ) - > krb5_context ) ;
if ( ret ) {
DEBUG ( 1 , ( " krb5_init_context failed (%s) \n " ,
error_message ( ret ) ) ) ;
2007-01-24 05:48:40 +03:00
talloc_free ( tmp_ctx ) ;
2005-09-17 05:11:50 +04:00
return ret ;
}
talloc_set_destructor ( * smb_krb5_context , smb_krb5_context_destroy_1 ) ;
2007-01-24 05:48:40 +03:00
config_file = config_path ( tmp_ctx , " krb5.conf " ) ;
if ( ! config_file ) {
talloc_free ( tmp_ctx ) ;
return ENOMEM ;
}
/* Use our local krb5.conf file by default */
ret = krb5_prepend_config_files_default ( config_file , & config_files ) ;
if ( ret ) {
DEBUG ( 1 , ( " krb5_prepend_config_files_default failed (%s) \n " ,
smb_get_krb5_error_message ( ( * smb_krb5_context ) - > krb5_context , ret , tmp_ctx ) ) ) ;
talloc_free ( tmp_ctx ) ;
return ret ;
}
ret = krb5_set_config_files ( ( * smb_krb5_context ) - > krb5_context ,
config_files ) ;
krb5_free_config_files ( config_files ) ;
if ( ret ) {
DEBUG ( 1 , ( " krb5_set_config_files failed (%s) \n " ,
smb_get_krb5_error_message ( ( * smb_krb5_context ) - > krb5_context , ret , tmp_ctx ) ) ) ;
talloc_free ( tmp_ctx ) ;
return ret ;
}
2005-09-17 05:11:50 +04:00
if ( lp_realm ( ) & & * lp_realm ( ) ) {
char * upper_realm = strupper_talloc ( tmp_ctx , lp_realm ( ) ) ;
if ( ! upper_realm ) {
DEBUG ( 1 , ( " gensec_krb5_start: could not uppercase realm: %s \n " , lp_realm ( ) ) ) ;
talloc_free ( tmp_ctx ) ;
return ENOMEM ;
}
2005-09-21 04:15:56 +04:00
ret = krb5_set_default_realm ( ( * smb_krb5_context ) - > krb5_context , upper_realm ) ;
2005-09-17 05:11:50 +04:00
if ( ret ) {
DEBUG ( 1 , ( " krb5_set_default_realm failed (%s) \n " ,
smb_get_krb5_error_message ( ( * smb_krb5_context ) - > krb5_context , ret , tmp_ctx ) ) ) ;
talloc_free ( tmp_ctx ) ;
return ret ;
}
}
/* TODO: Should we have a different name here? */
ret = krb5_initlog ( ( * smb_krb5_context ) - > krb5_context , " Samba " , & ( * smb_krb5_context ) - > logf ) ;
if ( ret ) {
DEBUG ( 1 , ( " krb5_initlog failed (%s) \n " ,
smb_get_krb5_error_message ( ( * smb_krb5_context ) - > krb5_context , ret , tmp_ctx ) ) ) ;
talloc_free ( tmp_ctx ) ;
return ret ;
}
talloc_set_destructor ( * smb_krb5_context , smb_krb5_context_destroy_2 ) ;
ret = krb5_addlog_func ( ( * smb_krb5_context ) - > krb5_context , ( * smb_krb5_context ) - > logf , 0 /* min */ , - 1 /* max */ ,
smb_krb5_debug_wrapper , smb_krb5_debug_close , NULL ) ;
if ( ret ) {
DEBUG ( 1 , ( " krb5_addlog_func failed (%s) \n " ,
smb_get_krb5_error_message ( ( * smb_krb5_context ) - > krb5_context , ret , tmp_ctx ) ) ) ;
talloc_free ( tmp_ctx ) ;
return ret ;
}
krb5_set_warn_dest ( ( * smb_krb5_context ) - > krb5_context , ( * smb_krb5_context ) - > logf ) ;
2005-09-28 05:09:10 +04:00
/* Set use of our socket lib */
2006-11-07 09:59:56 +03:00
ret = krb5_set_send_to_kdc_func ( ( * smb_krb5_context ) - > krb5_context ,
smb_krb5_send_and_recv_func ,
ev ) ;
2005-09-28 05:09:10 +04:00
if ( ret ) {
DEBUG ( 1 , ( " krb5_set_send_recv_func failed (%s) \n " ,
smb_get_krb5_error_message ( ( * smb_krb5_context ) - > krb5_context , ret , tmp_ctx ) ) ) ;
talloc_free ( tmp_ctx ) ;
return ret ;
}
2005-09-17 05:11:50 +04:00
talloc_steal ( parent_ctx , * smb_krb5_context ) ;
talloc_free ( tmp_ctx ) ;
2005-09-20 11:03:47 +04:00
/* Set options in kerberos */
2007-05-02 13:54:06 +04:00
krb5_set_dns_canonicalize_hostname ( ( * smb_krb5_context ) - > krb5_context ,
lp_parm_bool ( - 1 , " krb5 " , " set_dns_canonicalize " , false ) ) ;
2005-09-28 05:09:10 +04:00
2005-09-17 05:11:50 +04:00
return 0 ;
}