2009-12-23 23:17:16 +03:00
/*
2005-06-03 15:23:15 +04:00
Unix SMB / CIFS implementation .
KDC Server startup
2008-09-05 10:45:58 +04:00
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2005 - 2008
2005-06-03 15:23:15 +04:00
Copyright ( C ) Andrew Tridgell 2005
2005-10-14 10:12:05 +04:00
Copyright ( C ) Stefan Metzmacher 2005
2005-06-03 15:23:15 +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-06-03 15:23:15 +04:00
( at your option ) any later version .
2009-12-23 23:17:16 +03:00
2005-06-03 15:23:15 +04: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 .
2009-12-23 23:17:16 +03:00
2005-06-03 15:23:15 +04:00
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-06-03 15:23:15 +04:00
*/
# include "includes.h"
# include "smbd/service_task.h"
2006-03-07 15:08:58 +03:00
# include "smbd/service.h"
2005-10-14 10:12:05 +04:00
# include "smbd/service_stream.h"
2006-03-07 14:07:23 +03:00
# include "smbd/process_model.h"
2005-06-03 15:23:15 +04:00
# include "lib/events/events.h"
# include "lib/socket/socket.h"
2009-12-15 14:58:40 +03:00
# include "lib/tsocket/tsocket.h"
2010-01-07 14:23:33 +03:00
# include "libcli/util/tstream.h"
2005-06-03 18:32:10 +04:00
# include "system/network.h"
2008-10-11 23:31:42 +04:00
# include "../lib/util/dlinklist.h"
2005-07-19 13:27:20 +04:00
# include "lib/messaging/irpc.h"
2005-11-09 14:13:02 +03:00
# include "lib/stream/packet.h"
2006-02-04 02:19:00 +03:00
# include "librpc/gen_ndr/samr.h"
2008-09-03 09:30:17 +04:00
# include "librpc/gen_ndr/ndr_irpc.h"
# include "librpc/gen_ndr/ndr_krb5pac.h"
2006-08-17 17:37:04 +04:00
# include "lib/socket/netif.h"
2007-09-08 16:42:09 +04:00
# include "param/param.h"
2008-06-04 17:39:17 +04:00
# include "kdc/kdc.h"
2008-09-03 09:30:17 +04:00
# include "librpc/gen_ndr/ndr_misc.h"
2007-01-10 04:51:35 +03:00
2010-01-26 19:56:16 +03:00
extern struct krb5plugin_windc_ftable windc_plugin_table ;
2010-01-28 09:27:11 +03:00
extern struct hdb_method hdb_samba4 ;
2006-02-04 02:19:00 +03:00
2007-10-07 01:42:58 +04:00
typedef bool ( * kdc_process_fn_t ) ( struct kdc_server * kdc ,
2009-12-23 23:17:16 +03:00
TALLOC_CTX * mem_ctx ,
DATA_BLOB * input ,
2005-12-08 10:50:38 +03:00
DATA_BLOB * reply ,
2009-12-15 14:58:40 +03:00
struct tsocket_address * peer_addr ,
struct tsocket_address * my_addr ,
2006-11-07 09:59:56 +03:00
int datagram ) ;
2005-12-08 10:50:38 +03:00
2005-10-14 10:12:05 +04:00
/* hold information about one kdc socket */
struct kdc_socket {
struct kdc_server * kdc ;
2009-11-04 21:22:53 +03:00
struct tsocket_address * local_address ;
2005-12-08 10:50:38 +03:00
kdc_process_fn_t process ;
2005-10-14 10:12:05 +04:00
} ;
2009-11-04 21:22:53 +03:00
2005-10-14 10:12:05 +04:00
/*
state of an open tcp connection
*/
struct kdc_tcp_connection {
/* stream connection we belong to */
struct stream_connection * conn ;
/* the kdc_server the connection belongs to */
2009-11-04 21:22:53 +03:00
struct kdc_socket * kdc_socket ;
2005-10-14 10:12:05 +04:00
2010-01-07 14:23:33 +03:00
struct tstream_context * tstream ;
struct tevent_queue * send_queue ;
2005-10-14 10:12:05 +04:00
} ;
2005-06-29 17:55:09 +04:00
2005-10-14 10:12:05 +04:00
static void kdc_tcp_terminate_connection ( struct kdc_tcp_connection * kdcconn , const char * reason )
{
stream_terminate_connection ( kdcconn - > conn , reason ) ;
}
2010-01-07 14:23:33 +03:00
static void kdc_tcp_recv ( struct stream_connection * conn , uint16_t flags )
2005-11-09 14:13:02 +03:00
{
2009-02-02 12:30:03 +03:00
struct kdc_tcp_connection * kdcconn = talloc_get_type ( conn - > private_data ,
2005-11-09 14:13:02 +03:00
struct kdc_tcp_connection ) ;
2010-01-07 14:23:33 +03:00
/* this should never be triggered! */
kdc_tcp_terminate_connection ( kdcconn , " kdc_tcp_recv: called " ) ;
2005-11-09 14:13:02 +03:00
}
2005-10-14 10:12:05 +04:00
static void kdc_tcp_send ( struct stream_connection * conn , uint16_t flags )
{
2009-02-02 12:30:03 +03:00
struct kdc_tcp_connection * kdcconn = talloc_get_type ( conn - > private_data ,
2005-11-09 16:49:38 +03:00
struct kdc_tcp_connection ) ;
2010-01-07 14:23:33 +03:00
/* this should never be triggered! */
kdc_tcp_terminate_connection ( kdcconn , " kdc_tcp_send: called " ) ;
2005-10-14 10:12:05 +04:00
}
2005-10-21 05:25:55 +04:00
/**
Wrapper for krb5_kdc_process_krb5_request , converting to / from Samba
calling conventions
*/
2007-10-07 01:42:58 +04:00
static bool kdc_process ( struct kdc_server * kdc ,
2009-12-23 23:17:16 +03:00
TALLOC_CTX * mem_ctx ,
DATA_BLOB * input ,
2005-10-21 05:25:55 +04:00
DATA_BLOB * reply ,
2009-12-15 14:58:40 +03:00
struct tsocket_address * peer_addr ,
struct tsocket_address * my_addr ,
2006-11-07 09:59:56 +03:00
int datagram_reply )
2005-10-21 05:25:55 +04:00
{
2009-12-15 14:58:40 +03:00
int ret ;
char * pa ;
struct sockaddr_storage ss ;
2005-10-21 05:25:55 +04:00
krb5_data k5_reply ;
2006-11-07 09:59:56 +03:00
krb5_data_zero ( & k5_reply ) ;
2006-01-10 01:12:53 +03:00
2007-06-13 09:44:24 +04:00
krb5_kdc_update_time ( NULL ) ;
2009-12-15 14:58:40 +03:00
ret = tsocket_address_bsd_sockaddr ( peer_addr , ( struct sockaddr * ) & ss ,
sizeof ( struct sockaddr_storage ) ) ;
if ( ret < 0 ) {
return false ;
}
pa = tsocket_address_string ( peer_addr , mem_ctx ) ;
if ( pa = = NULL ) {
return false ;
}
DEBUG ( 10 , ( " Received KDC packet of length %lu from %s \n " ,
( long ) input - > length - 4 , pa ) ) ;
2005-10-21 05:25:55 +04:00
2009-12-23 23:17:16 +03:00
ret = krb5_kdc_process_krb5_request ( kdc - > smb_krb5_context - > krb5_context ,
2005-10-21 05:25:55 +04:00
kdc - > config ,
input - > data , input - > length ,
& k5_reply ,
2009-12-15 14:58:40 +03:00
pa ,
( struct sockaddr * ) & ss ,
2006-11-07 09:59:56 +03:00
datagram_reply ) ;
2005-10-21 05:25:55 +04:00
if ( ret = = - 1 ) {
* reply = data_blob ( NULL , 0 ) ;
2007-10-07 01:42:58 +04:00
return false ;
2005-10-21 05:25:55 +04:00
}
2006-11-07 09:59:56 +03:00
if ( k5_reply . length ) {
* reply = data_blob_talloc ( mem_ctx , k5_reply . data , k5_reply . length ) ;
2009-03-25 14:21:59 +03:00
krb5_data_free ( & k5_reply ) ;
2006-11-07 09:59:56 +03:00
} else {
2009-12-23 23:17:16 +03:00
* reply = data_blob ( NULL , 0 ) ;
2006-11-07 09:59:56 +03:00
}
2007-10-07 01:42:58 +04:00
return true ;
2005-10-21 05:25:55 +04:00
}
2010-01-07 14:23:33 +03:00
struct kdc_tcp_call {
struct kdc_tcp_connection * kdc_conn ;
DATA_BLOB in ;
DATA_BLOB out ;
uint8_t out_hdr [ 4 ] ;
struct iovec out_iov [ 2 ] ;
} ;
static void kdc_tcp_call_writev_done ( struct tevent_req * subreq ) ;
static void kdc_tcp_call_loop ( struct tevent_req * subreq )
{
struct kdc_tcp_connection * kdc_conn = tevent_req_callback_data ( subreq ,
struct kdc_tcp_connection ) ;
struct kdc_tcp_call * call ;
NTSTATUS status ;
bool ok ;
call = talloc ( kdc_conn , struct kdc_tcp_call ) ;
if ( call = = NULL ) {
kdc_tcp_terminate_connection ( kdc_conn , " kdc_tcp_call_loop: "
" no memory for kdc_tcp_call " ) ;
return ;
}
call - > kdc_conn = kdc_conn ;
status = tstream_read_pdu_blob_recv ( subreq ,
call ,
& call - > in ) ;
TALLOC_FREE ( subreq ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
const char * reason ;
reason = talloc_asprintf ( call , " kdc_tcp_call_loop: "
" tstream_read_pdu_blob_recv() - %s " ,
nt_errstr ( status ) ) ;
if ( ! reason ) {
reason = nt_errstr ( status ) ;
}
kdc_tcp_terminate_connection ( kdc_conn , reason ) ;
return ;
}
DEBUG ( 10 , ( " Received krb5 TCP packet of length %lu from %s \n " ,
( long ) call - > in . length ,
tsocket_address_string ( kdc_conn - > conn - > remote_address , call ) ) ) ;
/* skip length header */
call - > in . data + = 4 ;
call - > in . length - = 4 ;
/* Call krb5 */
ok = kdc_conn - > kdc_socket - > process ( kdc_conn - > kdc_socket - > kdc ,
call ,
& call - > in ,
& call - > out ,
kdc_conn - > conn - > remote_address ,
kdc_conn - > conn - > local_address ,
0 /* Stream */ ) ;
if ( ! ok ) {
kdc_tcp_terminate_connection ( kdc_conn ,
" kdc_tcp_call_loop: process function failed " ) ;
return ;
}
/* First add the length of the out buffer */
RSIVAL ( call - > out_hdr , 0 , call - > out . length ) ;
2010-06-26 21:43:51 +04:00
call - > out_iov [ 0 ] . iov_base = ( char * ) call - > out_hdr ;
2010-01-07 14:23:33 +03:00
call - > out_iov [ 0 ] . iov_len = 4 ;
2010-06-26 21:43:51 +04:00
call - > out_iov [ 1 ] . iov_base = ( char * ) call - > out . data ;
2010-01-07 14:23:33 +03:00
call - > out_iov [ 1 ] . iov_len = call - > out . length ;
subreq = tstream_writev_queue_send ( call ,
kdc_conn - > conn - > event . ctx ,
kdc_conn - > tstream ,
kdc_conn - > send_queue ,
call - > out_iov , 2 ) ;
if ( subreq = = NULL ) {
kdc_tcp_terminate_connection ( kdc_conn , " kdc_tcp_call_loop: "
" no memory for tstream_writev_queue_send " ) ;
return ;
}
tevent_req_set_callback ( subreq , kdc_tcp_call_writev_done , call ) ;
/*
* The krb5 tcp pdu ' s has the length as 4 byte ( initial_read_size ) ,
* packet_full_request_u32 provides the pdu length then .
*/
subreq = tstream_read_pdu_blob_send ( kdc_conn ,
kdc_conn - > conn - > event . ctx ,
kdc_conn - > tstream ,
4 , /* initial_read_size */
packet_full_request_u32 ,
kdc_conn ) ;
if ( subreq = = NULL ) {
kdc_tcp_terminate_connection ( kdc_conn , " kdc_tcp_call_loop: "
" no memory for tstream_read_pdu_blob_send " ) ;
return ;
}
tevent_req_set_callback ( subreq , kdc_tcp_call_loop , kdc_conn ) ;
}
static void kdc_tcp_call_writev_done ( struct tevent_req * subreq )
{
struct kdc_tcp_call * call = tevent_req_callback_data ( subreq ,
struct kdc_tcp_call ) ;
int sys_errno ;
int rc ;
rc = tstream_writev_queue_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( rc = = - 1 ) {
const char * reason ;
reason = talloc_asprintf ( call , " kdc_tcp_call_writev_done: "
" tstream_writev_queue_recv() - %d:%s " ,
sys_errno , strerror ( sys_errno ) ) ;
if ( ! reason ) {
reason = " kdc_tcp_call_writev_done: tstream_writev_queue_recv() failed " ;
}
kdc_tcp_terminate_connection ( call - > kdc_conn , reason ) ;
return ;
}
/* We don't care about errors */
talloc_free ( call ) ;
}
2005-10-21 05:25:55 +04:00
/*
called when we get a new connection
*/
2009-11-24 10:00:45 +03:00
static void kdc_tcp_accept ( struct stream_connection * conn )
2005-10-21 05:25:55 +04:00
{
2010-01-07 14:23:33 +03:00
struct kdc_socket * kdc_socket ;
struct kdc_tcp_connection * kdc_conn ;
struct tevent_req * subreq ;
int rc ;
kdc_conn = talloc_zero ( conn , struct kdc_tcp_connection ) ;
if ( kdc_conn = = NULL ) {
stream_terminate_connection ( conn ,
" kdc_tcp_accept: out of memory " ) ;
return ;
}
kdc_conn - > send_queue = tevent_queue_create ( conn , " kdc_tcp_accept " ) ;
if ( kdc_conn - > send_queue = = NULL ) {
stream_terminate_connection ( conn ,
" kdc_tcp_accept: out of memory " ) ;
return ;
}
kdc_socket = talloc_get_type ( conn - > private_data , struct kdc_socket ) ;
TALLOC_FREE ( conn - > event . fde ) ;
2005-10-21 05:25:55 +04:00
2010-02-26 12:35:01 +03:00
rc = tstream_bsd_existing_socket ( kdc_conn ,
2010-01-07 14:23:33 +03:00
socket_get_fd ( conn - > socket ) ,
& kdc_conn - > tstream ) ;
if ( rc < 0 ) {
stream_terminate_connection ( conn ,
" kdc_tcp_accept: out of memory " ) ;
2005-10-21 05:25:55 +04:00
return ;
}
2005-11-09 14:13:02 +03:00
2010-01-07 14:23:33 +03:00
kdc_conn - > conn = conn ;
kdc_conn - > kdc_socket = kdc_socket ;
conn - > private_data = kdc_conn ;
/*
* The krb5 tcp pdu ' s has the length as 4 byte ( initial_read_size ) ,
* packet_full_request_u32 provides the pdu length then .
*/
subreq = tstream_read_pdu_blob_send ( kdc_conn ,
kdc_conn - > conn - > event . ctx ,
kdc_conn - > tstream ,
4 , /* initial_read_size */
packet_full_request_u32 ,
kdc_conn ) ;
if ( subreq = = NULL ) {
kdc_tcp_terminate_connection ( kdc_conn , " kdc_tcp_accept: "
" no memory for tstream_read_pdu_blob_send " ) ;
2005-11-09 14:13:02 +03:00
return ;
}
2010-01-07 14:23:33 +03:00
tevent_req_set_callback ( subreq , kdc_tcp_call_loop , kdc_conn ) ;
2005-10-21 05:25:55 +04:00
}
2005-10-14 10:12:05 +04:00
static const struct stream_server_ops kdc_tcp_stream_ops = {
. name = " kdc_tcp " ,
. accept_connection = kdc_tcp_accept ,
2010-01-07 14:23:33 +03:00
. recv_handler = kdc_tcp_recv ,
2005-10-14 10:12:05 +04:00
. send_handler = kdc_tcp_send
} ;
2005-06-04 09:35:27 +04:00
2009-11-04 21:22:53 +03:00
/* hold information about one kdc/kpasswd udp socket */
struct kdc_udp_socket {
struct kdc_socket * kdc_socket ;
struct tdgram_context * dgram ;
struct tevent_queue * send_queue ;
} ;
struct kdc_udp_call {
struct tsocket_address * src ;
DATA_BLOB in ;
DATA_BLOB out ;
} ;
static void kdc_udp_call_sendto_done ( struct tevent_req * subreq ) ;
static void kdc_udp_call_loop ( struct tevent_req * subreq )
{
struct kdc_udp_socket * sock = tevent_req_callback_data ( subreq ,
struct kdc_udp_socket ) ;
struct kdc_udp_call * call ;
uint8_t * buf ;
ssize_t len ;
int sys_errno ;
2010-01-08 13:45:59 +03:00
bool ok ;
2009-11-04 21:22:53 +03:00
call = talloc ( sock , struct kdc_udp_call ) ;
if ( call = = NULL ) {
talloc_free ( call ) ;
goto done ;
}
len = tdgram_recvfrom_recv ( subreq , & sys_errno ,
call , & buf , & call - > src ) ;
TALLOC_FREE ( subreq ) ;
if ( len = = - 1 ) {
talloc_free ( call ) ;
goto done ;
}
call - > in . data = buf ;
call - > in . length = len ;
DEBUG ( 10 , ( " Received krb5 UDP packet of length %lu from %s \n " ,
( long ) call - > in . length ,
tsocket_address_string ( call - > src , call ) ) ) ;
/* Call krb5 */
2010-01-08 13:45:59 +03:00
ok = sock - > kdc_socket - > process ( sock - > kdc_socket - > kdc ,
call ,
& call - > in ,
& call - > out ,
call - > src ,
sock - > kdc_socket - > local_address ,
1 /* Datagram */ ) ;
if ( ! ok ) {
2009-11-04 21:22:53 +03:00
talloc_free ( call ) ;
goto done ;
}
subreq = tdgram_sendto_queue_send ( call ,
sock - > kdc_socket - > kdc - > task - > event_ctx ,
sock - > dgram ,
sock - > send_queue ,
call - > out . data ,
call - > out . length ,
call - > src ) ;
if ( subreq = = NULL ) {
talloc_free ( call ) ;
goto done ;
}
tevent_req_set_callback ( subreq , kdc_udp_call_sendto_done , call ) ;
done :
subreq = tdgram_recvfrom_send ( sock ,
sock - > kdc_socket - > kdc - > task - > event_ctx ,
sock - > dgram ) ;
if ( subreq = = NULL ) {
task_server_terminate ( sock - > kdc_socket - > kdc - > task ,
" no memory for tdgram_recvfrom_send " ,
true ) ;
return ;
}
tevent_req_set_callback ( subreq , kdc_udp_call_loop , sock ) ;
}
static void kdc_udp_call_sendto_done ( struct tevent_req * subreq )
{
struct kdc_udp_call * call = tevent_req_callback_data ( subreq ,
struct kdc_udp_call ) ;
ssize_t ret ;
int sys_errno ;
ret = tdgram_sendto_queue_recv ( subreq , & sys_errno ) ;
/* We don't care about errors */
talloc_free ( call ) ;
}
2005-06-03 15:23:15 +04:00
/*
start listening on the given address
*/
2009-11-24 07:28:11 +03:00
static NTSTATUS kdc_add_socket ( struct kdc_server * kdc ,
2009-11-20 17:47:40 +03:00
const struct model_ops * model_ops ,
2009-11-24 07:28:11 +03:00
const char * name ,
2009-11-20 17:47:40 +03:00
const char * address ,
2009-11-24 07:28:11 +03:00
uint16_t port ,
kdc_process_fn_t process )
2005-06-03 15:23:15 +04:00
{
2009-11-04 21:22:53 +03:00
struct kdc_socket * kdc_socket ;
struct kdc_udp_socket * kdc_udp_socket ;
struct tevent_req * udpsubreq ;
2005-06-03 15:23:15 +04:00
NTSTATUS status ;
2009-11-04 21:22:53 +03:00
int ret ;
2005-06-03 15:23:15 +04:00
kdc_socket = talloc ( kdc , struct kdc_socket ) ;
NT_STATUS_HAVE_NO_MEMORY ( kdc_socket ) ;
kdc_socket - > kdc = kdc ;
2009-11-24 07:28:11 +03:00
kdc_socket - > process = process ;
2005-06-03 15:23:15 +04:00
2009-11-04 21:22:53 +03:00
ret = tsocket_address_inet_from_strings ( kdc_socket , " ip " ,
address , port ,
& kdc_socket - > local_address ) ;
if ( ret ! = 0 ) {
status = map_nt_error_from_unix ( errno ) ;
2005-10-14 10:12:05 +04:00
return status ;
}
2009-12-23 23:17:16 +03:00
status = stream_setup_socket ( kdc - > task - > event_ctx ,
2009-11-20 17:47:40 +03:00
kdc - > task - > lp_ctx ,
2009-12-23 23:17:16 +03:00
model_ops ,
& kdc_tcp_stream_ops ,
" ip " , address , & port ,
2010-07-16 08:32:42 +04:00
lpcfg_socket_options ( kdc - > task - > lp_ctx ) ,
2009-11-24 10:00:45 +03:00
kdc_socket ) ;
2009-11-20 17:47:40 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " Failed to bind to %s:%u TCP - %s \n " ,
2009-11-24 07:28:11 +03:00
address , port , nt_errstr ( status ) ) ) ;
2009-11-20 17:47:40 +03:00
talloc_free ( kdc_socket ) ;
return status ;
}
2009-11-04 21:22:53 +03:00
kdc_udp_socket = talloc ( kdc_socket , struct kdc_udp_socket ) ;
NT_STATUS_HAVE_NO_MEMORY ( kdc_udp_socket ) ;
kdc_udp_socket - > kdc_socket = kdc_socket ;
ret = tdgram_inet_udp_socket ( kdc_socket - > local_address ,
NULL ,
kdc_udp_socket ,
& kdc_udp_socket - > dgram ) ;
if ( ret ! = 0 ) {
status = map_nt_error_from_unix ( errno ) ;
DEBUG ( 0 , ( " Failed to bind to %s:%u UDP - %s \n " ,
address , port , nt_errstr ( status ) ) ) ;
return status ;
}
kdc_udp_socket - > send_queue = tevent_queue_create ( kdc_udp_socket ,
" kdc_udp_send_queue " ) ;
NT_STATUS_HAVE_NO_MEMORY ( kdc_udp_socket - > send_queue ) ;
udpsubreq = tdgram_recvfrom_send ( kdc_udp_socket ,
kdc - > task - > event_ctx ,
kdc_udp_socket - > dgram ) ;
NT_STATUS_HAVE_NO_MEMORY ( udpsubreq ) ;
tevent_req_set_callback ( udpsubreq , kdc_udp_call_loop , kdc_udp_socket ) ;
2009-11-20 17:47:40 +03:00
return NT_STATUS_OK ;
}
2005-06-03 15:23:15 +04:00
/*
setup our listening sockets on the configured network interfaces
*/
2007-12-12 00:23:14 +03:00
static NTSTATUS kdc_startup_interfaces ( struct kdc_server * kdc , struct loadparm_context * lp_ctx ,
struct interface * ifaces )
2005-06-03 15:23:15 +04:00
{
2009-11-20 17:47:40 +03:00
const struct model_ops * model_ops ;
2007-12-12 00:23:14 +03:00
int num_interfaces ;
2005-06-03 15:23:15 +04:00
TALLOC_CTX * tmp_ctx = talloc_new ( kdc ) ;
NTSTATUS status ;
2006-02-04 02:19:00 +03:00
int i ;
2007-12-12 00:23:14 +03:00
2009-11-20 17:47:40 +03:00
/* within the kdc task we want to be a single process, so
ask for the single process model ops and pass these to the
stream_setup_socket ( ) call . */
model_ops = process_model_startup ( kdc - > task - > event_ctx , " single " ) ;
if ( ! model_ops ) {
DEBUG ( 0 , ( " Can't find 'single' process model_ops \n " ) ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
2007-12-12 00:23:14 +03:00
num_interfaces = iface_count ( ifaces ) ;
2009-12-23 23:17:16 +03:00
2006-02-04 02:19:00 +03:00
for ( i = 0 ; i < num_interfaces ; i + + ) {
2007-12-12 00:23:14 +03:00
const char * address = talloc_strdup ( tmp_ctx , iface_n_ip ( ifaces , i ) ) ;
2010-07-16 08:32:42 +04:00
uint16_t kdc_port = lpcfg_krb5_port ( lp_ctx ) ;
uint16_t kpasswd_port = lpcfg_kpasswd_port ( lp_ctx ) ;
2009-11-20 17:47:40 +03:00
if ( kdc_port ) {
2009-11-24 07:28:11 +03:00
status = kdc_add_socket ( kdc , model_ops ,
" kdc " , address , kdc_port ,
kdc_process ) ;
2009-11-20 17:47:40 +03:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
}
if ( kpasswd_port ) {
2009-11-24 07:28:11 +03:00
status = kdc_add_socket ( kdc , model_ops ,
" kpasswd " , address , kpasswd_port ,
kpasswdd_process ) ;
2009-11-20 17:47:40 +03:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
}
2006-02-04 02:19:00 +03:00
}
2005-06-03 15:23:15 +04:00
talloc_free ( tmp_ctx ) ;
return NT_STATUS_OK ;
}
2007-01-10 04:51:35 +03:00
2009-12-23 23:17:16 +03:00
static NTSTATUS kdc_check_generic_kerberos ( struct irpc_message * msg ,
2008-09-03 09:30:17 +04:00
struct kdc_check_generic_kerberos * r )
{
struct PAC_Validate pac_validate ;
DATA_BLOB srv_sig ;
struct PAC_SIGNATURE_DATA kdc_sig ;
2009-02-01 02:03:47 +03:00
struct kdc_server * kdc = talloc_get_type ( msg - > private_data , struct kdc_server ) ;
2008-09-03 09:30:17 +04:00
enum ndr_err_code ndr_err ;
krb5_enctype etype ;
int ret ;
hdb_entry_ex ent ;
krb5_principal principal ;
krb5_keyblock keyblock ;
Key * key ;
/* There is no reply to this request */
r - > out . generic_reply = data_blob ( NULL , 0 ) ;
2010-05-09 19:20:01 +04:00
ndr_err = ndr_pull_struct_blob ( & r - > in . generic_request , msg , & pac_validate ,
2008-09-03 09:30:17 +04:00
( ndr_pull_flags_fn_t ) ndr_pull_PAC_Validate ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2009-12-23 23:17:16 +03:00
2008-09-03 09:30:17 +04:00
if ( pac_validate . MessageType ! = 3 ) {
/* We don't implement any other message types - such as certificate validation - yet */
return NT_STATUS_INVALID_PARAMETER ;
}
2008-09-23 01:23:22 +04:00
2008-09-03 09:30:17 +04:00
if ( pac_validate . ChecksumAndSignature . length ! = ( pac_validate . ChecksumLength + pac_validate . SignatureLength )
| | pac_validate . ChecksumAndSignature . length < pac_validate . ChecksumLength
| | pac_validate . ChecksumAndSignature . length < pac_validate . SignatureLength ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2009-12-23 23:17:16 +03:00
srv_sig = data_blob_const ( pac_validate . ChecksumAndSignature . data ,
2008-09-03 09:30:17 +04:00
pac_validate . ChecksumLength ) ;
2009-12-23 23:17:16 +03:00
2008-09-03 09:30:17 +04:00
if ( pac_validate . SignatureType = = CKSUMTYPE_HMAC_MD5 ) {
etype = ETYPE_ARCFOUR_HMAC_MD5 ;
} else {
ret = krb5_cksumtype_to_enctype ( kdc - > smb_krb5_context - > krb5_context , pac_validate . SignatureType ,
& etype ) ;
if ( ret ! = 0 ) {
return NT_STATUS_LOGON_FAILURE ;
}
}
2009-12-23 23:17:16 +03:00
ret = krb5_make_principal ( kdc - > smb_krb5_context - > krb5_context , & principal ,
2010-07-16 08:32:42 +04:00
lpcfg_realm ( kdc - > task - > lp_ctx ) ,
" krbtgt " , lpcfg_realm ( kdc - > task - > lp_ctx ) ,
2008-09-03 09:30:17 +04:00
NULL ) ;
if ( ret ! = 0 ) {
return NT_STATUS_NO_MEMORY ;
}
2009-12-23 23:17:16 +03:00
ret = kdc - > config - > db [ 0 ] - > hdb_fetch ( kdc - > smb_krb5_context - > krb5_context ,
2008-09-03 09:30:17 +04:00
kdc - > config - > db [ 0 ] ,
principal ,
HDB_F_GET_KRBTGT | HDB_F_DECRYPT ,
& ent ) ;
2010-10-02 10:55:06 +04:00
2008-09-03 09:30:17 +04:00
if ( ret ! = 0 ) {
hdb_free_entry ( kdc - > smb_krb5_context - > krb5_context , & ent ) ;
krb5_free_principal ( kdc - > smb_krb5_context - > krb5_context , principal ) ;
2009-12-23 23:17:16 +03:00
2008-09-03 09:30:17 +04:00
return NT_STATUS_LOGON_FAILURE ;
}
2009-12-23 23:17:16 +03:00
2008-09-03 09:30:17 +04:00
ret = hdb_enctype2key ( kdc - > smb_krb5_context - > krb5_context , & ent . entry , etype , & key ) ;
if ( ret ! = 0 ) {
hdb_free_entry ( kdc - > smb_krb5_context - > krb5_context , & ent ) ;
krb5_free_principal ( kdc - > smb_krb5_context - > krb5_context , principal ) ;
return NT_STATUS_LOGON_FAILURE ;
}
keyblock = key - > key ;
2009-12-23 23:17:16 +03:00
2008-09-03 09:30:17 +04:00
kdc_sig . type = pac_validate . SignatureType ;
kdc_sig . signature = data_blob_const ( & pac_validate . ChecksumAndSignature . data [ pac_validate . ChecksumLength ] ,
pac_validate . SignatureLength ) ;
2009-12-23 23:17:16 +03:00
ret = check_pac_checksum ( msg , srv_sig , & kdc_sig ,
2008-09-03 09:30:17 +04:00
kdc - > smb_krb5_context - > krb5_context , & keyblock ) ;
hdb_free_entry ( kdc - > smb_krb5_context - > krb5_context , & ent ) ;
krb5_free_principal ( kdc - > smb_krb5_context - > krb5_context , principal ) ;
if ( ret ! = 0 ) {
return NT_STATUS_LOGON_FAILURE ;
}
2009-12-23 23:17:16 +03:00
2008-09-03 09:30:17 +04:00
return NT_STATUS_OK ;
}
2005-06-03 15:23:15 +04:00
/*
startup the kdc task
*/
static void kdc_task_init ( struct task_server * task )
{
struct kdc_server * kdc ;
NTSTATUS status ;
2005-06-03 18:32:10 +04:00
krb5_error_code ret ;
2007-12-12 00:23:14 +03:00
struct interface * ifaces ;
2005-06-03 15:23:15 +04:00
2010-07-16 08:32:42 +04:00
switch ( lpcfg_server_role ( task - > lp_ctx ) ) {
2006-02-04 02:19:00 +03:00
case ROLE_STANDALONE :
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " kdc: no KDC required in standalone configuration " , false ) ;
2006-02-04 02:19:00 +03:00
return ;
case ROLE_DOMAIN_MEMBER :
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " kdc: no KDC required in member server configuration " , false ) ;
2006-02-04 02:19:00 +03:00
return ;
2006-12-13 23:47:24 +03:00
case ROLE_DOMAIN_CONTROLLER :
2006-02-04 02:19:00 +03:00
/* Yes, we want a KDC */
break ;
}
2010-07-16 08:32:42 +04:00
load_interfaces ( task , lpcfg_interfaces ( task - > lp_ctx ) , & ifaces ) ;
2007-12-12 00:23:14 +03:00
if ( iface_count ( ifaces ) = = 0 ) {
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " kdc: no network interfaces configured " , false ) ;
2005-06-03 15:23:15 +04:00
return ;
}
2006-03-09 20:48:41 +03:00
task_server_set_title ( task , " task[kdc] " ) ;
2005-06-03 15:23:15 +04:00
kdc = talloc ( task , struct kdc_server ) ;
if ( kdc = = NULL ) {
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " kdc: out of memory " , true ) ;
2005-06-03 15:23:15 +04:00
return ;
}
kdc - > task = task ;
2005-06-03 18:32:10 +04:00
initialize_krb5_error_table ( ) ;
2007-12-02 22:56:26 +03:00
ret = smb_krb5_init_context ( kdc , task - > event_ctx , task - > lp_ctx , & kdc - > smb_krb5_context ) ;
2005-06-03 18:32:10 +04:00
if ( ret ) {
2009-12-23 23:17:16 +03:00
DEBUG ( 1 , ( " kdc_task_init: krb5_init_context failed (%s) \n " ,
2005-06-03 18:32:10 +04:00
error_message ( ret ) ) ) ;
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " kdc: krb5_init_context failed " , true ) ;
2009-12-23 23:17:16 +03:00
return ;
2005-06-03 18:32:10 +04:00
}
2005-09-28 06:22:31 +04:00
krb5_add_et_list ( kdc - > smb_krb5_context - > krb5_context , initialize_hdb_error_table_r ) ;
2009-12-23 23:17:16 +03:00
ret = krb5_kdc_get_config ( kdc - > smb_krb5_context - > krb5_context ,
2007-06-13 09:44:24 +04:00
& kdc - > config ) ;
2007-01-24 05:48:40 +03:00
if ( ret ) {
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " kdc: failed to get KDC configuration " , true ) ;
2007-01-24 05:48:40 +03:00
return ;
}
2009-12-23 23:17:16 +03:00
2005-06-04 15:17:05 +04:00
kdc - > config - > logf = kdc - > smb_krb5_context - > logf ;
2007-06-13 09:44:24 +04:00
kdc - > config - > db = talloc ( kdc , struct HDB * ) ;
2005-06-03 18:32:10 +04:00
if ( ! kdc - > config - > db ) {
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " kdc: out of memory " , true ) ;
2005-06-03 18:32:10 +04:00
return ;
}
kdc - > config - > num_db = 1 ;
2009-12-23 23:17:16 +03:00
2009-07-27 10:09:25 +04:00
/* Register hdb-samba4 hooks for use as a keytab */
2009-07-27 07:48:45 +04:00
2010-01-28 08:08:36 +03:00
kdc - > base_ctx = talloc_zero ( kdc , struct samba_kdc_base_context ) ;
if ( ! kdc - > base_ctx ) {
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " kdc: out of memory " , true ) ;
2009-12-23 23:17:16 +03:00
return ;
2009-07-27 10:09:25 +04:00
}
2010-01-28 08:08:36 +03:00
kdc - > base_ctx - > ev_ctx = task - > event_ctx ;
kdc - > base_ctx - > lp_ctx = task - > lp_ctx ;
status = hdb_samba4_create_kdc ( kdc - > base_ctx ,
kdc - > smb_krb5_context - > krb5_context ,
& kdc - > config - > db [ 0 ] ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
task_server_terminate ( task , " kdc: hdb_samba4_create_kdc (setup KDC database) failed " , true ) ;
return ;
}
2009-07-27 07:48:45 +04:00
2009-12-23 23:17:16 +03:00
ret = krb5_plugin_register ( kdc - > smb_krb5_context - > krb5_context ,
2008-09-24 23:53:10 +04:00
PLUGIN_TYPE_DATA , " hdb " ,
& hdb_samba4 ) ;
if ( ret ) {
2010-01-11 19:48:12 +03:00
task_server_terminate ( task , " kdc: failed to register hdb plugin " , true ) ;
2008-09-24 23:53:10 +04:00
return ;
}
2006-01-24 08:31:08 +03:00
ret = krb5_kt_register ( kdc - > smb_krb5_context - > krb5_context , & hdb_kt_ops ) ;
if ( ret ) {
2010-01-11 19:48:12 +03:00
task_server_terminate ( task , " kdc: failed to register keytab plugin " , true ) ;
2006-01-24 08:31:08 +03:00
return ;
}
2007-01-10 04:51:35 +03:00
2010-01-11 19:48:12 +03:00
/* Register WinDC hooks */
2009-12-23 23:17:16 +03:00
ret = krb5_plugin_register ( kdc - > smb_krb5_context - > krb5_context ,
2008-03-19 02:17:42 +03:00
PLUGIN_TYPE_DATA , " windc " ,
& windc_plugin_table ) ;
2007-06-13 09:44:24 +04:00
if ( ret ) {
2010-01-11 19:48:12 +03:00
task_server_terminate ( task , " kdc: failed to register windc plugin " , true ) ;
2007-06-13 09:44:24 +04:00
return ;
}
krb5_kdc_windc_init ( kdc - > smb_krb5_context - > krb5_context ) ;
2007-01-10 04:51:35 +03:00
2005-06-03 15:23:15 +04:00
/* start listening on the configured network interfaces */
2007-12-12 00:23:14 +03:00
status = kdc_startup_interfaces ( kdc , task - > lp_ctx , ifaces ) ;
2005-06-03 15:23:15 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " kdc failed to setup interfaces " , true ) ;
2005-06-03 15:23:15 +04:00
return ;
}
2005-07-19 13:27:20 +04:00
2009-12-23 23:17:16 +03:00
status = IRPC_REGISTER ( task - > msg_ctx , irpc , KDC_CHECK_GENERIC_KERBEROS ,
2008-09-03 09:30:17 +04:00
kdc_check_generic_kerberos , kdc ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-09-19 05:05:55 +04:00
task_server_terminate ( task , " nbtd failed to setup monitoring " , true ) ;
2008-09-03 09:30:17 +04:00
return ;
}
2005-07-19 13:27:20 +04:00
irpc_add_name ( task - > msg_ctx , " kdc_server " ) ;
2005-06-03 15:23:15 +04:00
}
/* called at smbd startup - register ourselves as a server service */
NTSTATUS server_service_kdc_init ( void )
{
2008-02-04 13:58:29 +03:00
return register_server_service ( " kdc " , kdc_task_init ) ;
2005-06-03 15:23:15 +04:00
}