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 .
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-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"
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
2008-02-21 19:17:37 +03:00
/* Disgusting hack to get a mem_ctx and lp_ctx into the hdb plugin, when
* used as a keytab */
2007-01-10 04:51:35 +03:00
TALLOC_CTX * kdc_mem_ctx ;
2008-12-29 22:24:57 +03:00
struct tevent_context * kdc_ev_ctx ;
2008-02-21 19:17:37 +03:00
struct loadparm_context * kdc_lp_ctx ;
2006-02-04 02:19:00 +03:00
2005-10-14 10:12:05 +04:00
/* hold all the info needed to send a reply */
struct kdc_reply {
struct kdc_reply * next , * prev ;
2006-01-10 01:12:53 +03:00
struct socket_address * dest ;
2005-10-14 10:12:05 +04:00
DATA_BLOB packet ;
} ;
2005-06-29 17:55:09 +04:00
2007-10-07 01:42:58 +04:00
typedef bool ( * kdc_process_fn_t ) ( struct kdc_server * kdc ,
2005-12-08 10:50:38 +03:00
TALLOC_CTX * mem_ctx ,
DATA_BLOB * input ,
DATA_BLOB * reply ,
2006-01-10 01:12:53 +03:00
struct socket_address * peer_addr ,
2006-11-07 09:59:56 +03:00
struct socket_address * my_addr ,
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 socket_context * sock ;
struct kdc_server * kdc ;
2008-12-29 22:24:57 +03:00
struct tevent_fd * fde ;
2005-10-14 10:12:05 +04:00
/* a queue of outgoing replies that have been deferred */
struct kdc_reply * send_queue ;
2005-10-17 05:01:59 +04:00
2005-12-08 10:50:38 +03:00
kdc_process_fn_t process ;
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 */
struct kdc_server * kdc ;
2005-11-09 14:13:02 +03:00
struct packet_context * packet ;
2005-10-14 10:12:05 +04:00
2005-12-08 10:50:38 +03:00
kdc_process_fn_t process ;
2005-10-14 10:12:05 +04:00
} ;
2005-06-29 17:55:09 +04:00
2005-06-04 09:35:27 +04:00
/*
handle fd send events on a KDC socket
*/
static void kdc_send_handler ( struct kdc_socket * kdc_socket )
{
while ( kdc_socket - > send_queue ) {
struct kdc_reply * rep = kdc_socket - > send_queue ;
NTSTATUS status ;
size_t sendlen ;
2006-04-30 09:58:31 +04:00
status = socket_sendto ( kdc_socket - > sock , & rep - > packet , & sendlen ,
2006-01-10 01:12:53 +03:00
rep - > dest ) ;
2005-06-04 09:35:27 +04:00
if ( NT_STATUS_EQUAL ( status , STATUS_MORE_ENTRIES ) ) {
break ;
}
2005-11-07 05:19:19 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_INVALID_BUFFER_SIZE ) ) {
/* Replace with a krb err, response to big */
}
2005-06-04 09:35:27 +04:00
DLIST_REMOVE ( kdc_socket - > send_queue , rep ) ;
talloc_free ( rep ) ;
}
if ( kdc_socket - > send_queue = = NULL ) {
EVENT_FD_NOT_WRITEABLE ( kdc_socket - > fde ) ;
}
}
2005-06-03 15:23:15 +04:00
2005-06-04 05:40:30 +04:00
2005-06-04 09:35:27 +04:00
/*
handle fd recv events on a KDC socket
*/
static void kdc_recv_handler ( struct kdc_socket * kdc_socket )
2005-06-04 05:40:30 +04:00
{
NTSTATUS status ;
2005-06-04 09:35:27 +04:00
TALLOC_CTX * tmp_ctx = talloc_new ( kdc_socket ) ;
2005-06-04 05:40:30 +04:00
DATA_BLOB blob ;
2005-06-04 09:35:27 +04:00
struct kdc_reply * rep ;
2005-10-21 05:25:55 +04:00
DATA_BLOB reply ;
2005-06-04 05:40:30 +04:00
size_t nread , dsize ;
2006-01-10 01:12:53 +03:00
struct socket_address * src ;
struct socket_address * my_addr ;
2005-06-05 11:40:17 +04:00
int ret ;
2005-06-04 09:35:27 +04:00
2005-06-04 05:40:30 +04:00
status = socket_pending ( kdc_socket - > sock , & dsize ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( tmp_ctx ) ;
return ;
}
2005-06-04 09:35:27 +04:00
2006-05-23 09:14:06 +04:00
blob = data_blob_talloc ( tmp_ctx , NULL , dsize ) ;
2005-06-04 05:40:30 +04:00
if ( blob . data = = NULL ) {
2005-06-04 09:35:27 +04:00
/* hope this is a temporary low memory condition */
2005-06-04 05:40:30 +04:00
talloc_free ( tmp_ctx ) ;
return ;
}
2005-06-04 09:35:27 +04:00
2006-04-30 09:58:31 +04:00
status = socket_recvfrom ( kdc_socket - > sock , blob . data , blob . length , & nread ,
2006-01-10 01:12:53 +03:00
tmp_ctx , & src ) ;
2005-06-04 05:40:30 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( tmp_ctx ) ;
return ;
}
blob . length = nread ;
2006-01-03 01:00:40 +03:00
DEBUG ( 10 , ( " Received krb5 UDP packet of length %lu from %s:%u \n " ,
2006-01-10 01:12:53 +03:00
( long ) blob . length , src - > addr , ( uint16_t ) src - > port ) ) ;
2005-06-04 05:40:30 +04:00
2006-01-03 01:00:40 +03:00
my_addr = socket_get_my_addr ( kdc_socket - > sock , tmp_ctx ) ;
if ( ! my_addr ) {
talloc_free ( tmp_ctx ) ;
return ;
}
2005-06-04 05:40:30 +04:00
/* Call krb5 */
2005-10-21 05:25:55 +04:00
ret = kdc_socket - > process ( kdc_socket - > kdc ,
tmp_ctx ,
& blob ,
2005-10-17 05:01:59 +04:00
& reply ,
2006-11-07 09:59:56 +03:00
src , my_addr ,
1 /* Datagram */ ) ;
2005-10-21 05:25:55 +04:00
if ( ! ret ) {
2005-06-04 09:35:27 +04:00
talloc_free ( tmp_ctx ) ;
return ;
}
2005-06-04 05:40:30 +04:00
2005-06-04 09:35:27 +04:00
/* queue a pending reply */
rep = talloc ( kdc_socket , struct kdc_reply ) ;
if ( rep = = NULL ) {
talloc_free ( tmp_ctx ) ;
return ;
}
2006-01-10 01:12:53 +03:00
rep - > dest = talloc_steal ( rep , src ) ;
2005-10-21 05:25:55 +04:00
rep - > packet = reply ;
talloc_steal ( rep , reply . data ) ;
2005-06-12 15:31:57 +04:00
2005-06-04 09:35:27 +04:00
if ( rep - > packet . data = = NULL ) {
talloc_free ( rep ) ;
talloc_free ( tmp_ctx ) ;
return ;
2005-06-04 05:40:30 +04:00
}
2005-06-04 09:35:27 +04:00
DLIST_ADD_END ( kdc_socket - > send_queue , rep , struct kdc_reply * ) ;
EVENT_FD_WRITEABLE ( kdc_socket - > fde ) ;
2005-06-04 05:40:30 +04:00
talloc_free ( tmp_ctx ) ;
}
2005-06-03 15:23:15 +04:00
/*
2005-06-03 18:32:10 +04:00
handle fd events on a KDC socket
2005-06-03 15:23:15 +04:00
*/
2008-12-29 22:24:57 +03:00
static void kdc_socket_handler ( struct tevent_context * ev , struct tevent_fd * fde ,
2009-02-01 01:43:43 +03:00
uint16_t flags , void * private_data )
2005-06-03 15:23:15 +04:00
{
2009-02-01 01:43:43 +03:00
struct kdc_socket * kdc_socket = talloc_get_type ( private_data , struct kdc_socket ) ;
2005-06-03 15:23:15 +04:00
if ( flags & EVENT_FD_WRITE ) {
2005-06-04 09:35:27 +04:00
kdc_send_handler ( kdc_socket ) ;
2005-06-11 07:55:40 +04:00
}
if ( flags & EVENT_FD_READ ) {
2005-06-04 09:35:27 +04:00
kdc_recv_handler ( kdc_socket ) ;
2005-06-03 15:23:15 +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 ) ;
}
2005-11-09 14:13:02 +03:00
/*
receive a full packet on a KDC connection
*/
2009-02-01 01:43:43 +03:00
static NTSTATUS kdc_tcp_recv ( void * private_data , DATA_BLOB blob )
2005-11-09 14:13:02 +03:00
{
2009-02-01 01:43:43 +03:00
struct kdc_tcp_connection * kdcconn = talloc_get_type ( private_data ,
2005-11-10 03:36:53 +03:00
struct kdc_tcp_connection ) ;
2005-10-14 10:12:05 +04:00
NTSTATUS status = NT_STATUS_UNSUCCESSFUL ;
TALLOC_CTX * tmp_ctx = talloc_new ( kdcconn ) ;
int ret ;
2005-10-21 05:25:55 +04:00
DATA_BLOB input , reply ;
2006-01-10 01:12:53 +03:00
struct socket_address * src_addr ;
struct socket_address * my_addr ;
2005-10-14 10:12:05 +04:00
2005-11-09 14:13:02 +03:00
talloc_steal ( tmp_ctx , blob . data ) ;
2005-10-14 10:12:05 +04:00
src_addr = socket_get_peer_addr ( kdcconn - > conn - > socket , tmp_ctx ) ;
2006-01-03 01:00:40 +03:00
if ( ! src_addr ) {
talloc_free ( tmp_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
2005-10-14 10:12:05 +04:00
2006-01-03 01:00:40 +03:00
my_addr = socket_get_my_addr ( kdcconn - > conn - > socket , tmp_ctx ) ;
if ( ! my_addr ) {
talloc_free ( tmp_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
2005-10-14 10:12:05 +04:00
/* Call krb5 */
2005-11-09 14:13:02 +03:00
input = data_blob_const ( blob . data + 4 , blob . length - 4 ) ;
2005-10-21 05:25:55 +04:00
ret = kdcconn - > process ( kdcconn - > kdc ,
tmp_ctx ,
& input ,
2005-10-17 05:01:59 +04:00
& reply ,
2006-01-10 01:12:53 +03:00
src_addr ,
2006-11-07 09:59:56 +03:00
my_addr ,
0 /* Not datagram */ ) ;
2005-10-21 05:25:55 +04:00
if ( ! ret ) {
2006-01-03 01:00:40 +03:00
talloc_free ( tmp_ctx ) ;
return NT_STATUS_INTERNAL_ERROR ;
2005-10-14 10:12:05 +04:00
}
/* and now encode the reply */
2005-11-09 16:49:38 +03:00
blob = data_blob_talloc ( kdcconn , NULL , reply . length + 4 ) ;
if ( ! blob . data ) {
2006-01-03 01:00:40 +03:00
talloc_free ( tmp_ctx ) ;
return NT_STATUS_NO_MEMORY ;
2005-10-14 10:12:05 +04:00
}
2005-11-09 16:49:38 +03:00
RSIVAL ( blob . data , 0 , reply . length ) ;
memcpy ( blob . data + 4 , reply . data , reply . length ) ;
2005-10-14 10:12:05 +04:00
2005-11-09 16:49:38 +03:00
status = packet_send ( kdcconn - > packet , blob ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2006-01-03 01:00:40 +03:00
talloc_free ( tmp_ctx ) ;
return status ;
2005-10-14 10:12:05 +04:00
}
/* the call isn't needed any more */
talloc_free ( tmp_ctx ) ;
2005-11-09 14:13:02 +03:00
return NT_STATUS_OK ;
}
/*
receive some data on a KDC connection
*/
static void kdc_tcp_recv_handler ( 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 14:13:02 +03:00
struct kdc_tcp_connection ) ;
packet_recv ( kdcconn - > packet ) ;
}
/*
called on a tcp recv error
*/
2009-02-01 01:43:43 +03:00
static void kdc_tcp_recv_error ( void * private_data , NTSTATUS status )
2005-11-09 14:13:02 +03:00
{
2009-02-01 01:43:43 +03:00
struct kdc_tcp_connection * kdcconn = talloc_get_type ( private_data ,
struct kdc_tcp_connection ) ;
2005-11-09 14:13:02 +03:00
kdc_tcp_terminate_connection ( kdcconn , nt_errstr ( status ) ) ;
2005-10-14 10:12:05 +04:00
}
/*
called when we can write to a connection
*/
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 ) ;
packet_queue_run ( kdcconn - > packet ) ;
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 ,
2005-10-21 05:25:55 +04:00
TALLOC_CTX * mem_ctx ,
DATA_BLOB * input ,
DATA_BLOB * reply ,
2006-01-10 01:12:53 +03:00
struct socket_address * peer_addr ,
2006-11-07 09:59:56 +03:00
struct socket_address * my_addr ,
int datagram_reply )
2005-10-21 05:25:55 +04:00
{
int ret ;
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 ) ;
2006-01-10 01:12:53 +03:00
DEBUG ( 10 , ( " Received KDC packet of length %lu from %s:%d \n " ,
( long ) input - > length - 4 , peer_addr - > addr , peer_addr - > port ) ) ;
2005-10-21 05:25:55 +04:00
ret = krb5_kdc_process_krb5_request ( kdc - > smb_krb5_context - > krb5_context ,
kdc - > config ,
input - > data , input - > length ,
& k5_reply ,
2006-01-10 01:12:53 +03:00
peer_addr - > addr ,
2006-11-07 09:59:56 +03:00
peer_addr - > sockaddr ,
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 {
* reply = data_blob ( NULL , 0 ) ;
}
2007-10-07 01:42:58 +04:00
return true ;
2005-10-21 05:25:55 +04:00
}
/*
called when we get a new connection
*/
2005-12-08 10:50:38 +03:00
static void kdc_tcp_generic_accept ( struct stream_connection * conn , kdc_process_fn_t process_fn )
2005-10-21 05:25:55 +04:00
{
2009-02-02 12:30:03 +03:00
struct kdc_server * kdc = talloc_get_type ( conn - > private_data , struct kdc_server ) ;
2005-10-21 05:25:55 +04:00
struct kdc_tcp_connection * kdcconn ;
kdcconn = talloc_zero ( conn , struct kdc_tcp_connection ) ;
if ( ! kdcconn ) {
stream_terminate_connection ( conn , " kdc_tcp_accept: out of memory " ) ;
return ;
}
kdcconn - > conn = conn ;
kdcconn - > kdc = kdc ;
2005-12-08 10:50:38 +03:00
kdcconn - > process = process_fn ;
2009-02-02 12:30:03 +03:00
conn - > private_data = kdcconn ;
2005-11-09 14:13:02 +03:00
kdcconn - > packet = packet_init ( kdcconn ) ;
if ( kdcconn - > packet = = NULL ) {
2005-12-08 10:50:38 +03:00
kdc_tcp_terminate_connection ( kdcconn , " kdc_tcp_accept: out of memory " ) ;
2005-11-09 14:13:02 +03:00
return ;
}
packet_set_private ( kdcconn - > packet , kdcconn ) ;
packet_set_socket ( kdcconn - > packet , conn - > socket ) ;
packet_set_callback ( kdcconn - > packet , kdc_tcp_recv ) ;
2005-11-10 03:26:26 +03:00
packet_set_full_request ( kdcconn - > packet , packet_full_request_u32 ) ;
2005-11-09 14:13:02 +03:00
packet_set_error_handler ( kdcconn - > packet , kdc_tcp_recv_error ) ;
packet_set_event_context ( kdcconn - > packet , conn - > event . ctx ) ;
2005-11-14 06:45:57 +03:00
packet_set_fde ( kdcconn - > packet , conn - > event . fde ) ;
packet_set_serialise ( kdcconn - > packet ) ;
2005-10-21 05:25:55 +04:00
}
2005-12-08 10:50:38 +03:00
static void kdc_tcp_accept ( struct stream_connection * conn )
{
kdc_tcp_generic_accept ( conn , kdc_process ) ;
}
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 ,
2005-11-09 14:13:02 +03:00
. recv_handler = kdc_tcp_recv_handler ,
2005-10-14 10:12:05 +04:00
. send_handler = kdc_tcp_send
} ;
2005-06-04 09:35:27 +04:00
2005-12-08 10:50:38 +03:00
static void kpasswdd_tcp_accept ( struct stream_connection * conn )
2005-10-21 05:25:55 +04:00
{
2005-12-08 10:50:38 +03:00
kdc_tcp_generic_accept ( conn , kpasswdd_process ) ;
2005-10-21 05:25:55 +04:00
}
static const struct stream_server_ops kpasswdd_tcp_stream_ops = {
. name = " kpasswdd_tcp " ,
. accept_connection = kpasswdd_tcp_accept ,
2005-11-09 14:13:02 +03:00
. recv_handler = kdc_tcp_recv_handler ,
2005-10-21 05:25:55 +04:00
. send_handler = kdc_tcp_send
} ;
2005-06-03 15:23:15 +04:00
/*
start listening on the given address
*/
2007-09-07 19:35:18 +04:00
static NTSTATUS kdc_add_socket ( struct kdc_server * kdc , const char * address ,
uint16_t kdc_port , uint16_t kpasswd_port )
2005-06-03 15:23:15 +04:00
{
2005-10-14 10:12:05 +04:00
const struct model_ops * model_ops ;
2005-06-04 15:17:05 +04:00
struct kdc_socket * kdc_socket ;
2005-10-21 05:25:55 +04:00
struct kdc_socket * kpasswd_socket ;
2006-01-10 01:12:53 +03:00
struct socket_address * kdc_address , * kpasswd_address ;
2005-06-03 15:23:15 +04:00
NTSTATUS status ;
kdc_socket = talloc ( kdc , struct kdc_socket ) ;
NT_STATUS_HAVE_NO_MEMORY ( kdc_socket ) ;
2005-10-21 05:25:55 +04:00
kpasswd_socket = talloc ( kdc , struct kdc_socket ) ;
NT_STATUS_HAVE_NO_MEMORY ( kpasswd_socket ) ;
2005-06-03 15:23:15 +04:00
status = socket_create ( " ip " , SOCKET_TYPE_DGRAM , & kdc_socket - > sock , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( kdc_socket ) ;
return status ;
}
2005-10-21 05:25:55 +04:00
status = socket_create ( " ip " , SOCKET_TYPE_DGRAM , & kpasswd_socket - > sock , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( kpasswd_socket ) ;
return status ;
}
2005-06-03 15:23:15 +04:00
kdc_socket - > kdc = kdc ;
2005-06-04 09:35:27 +04:00
kdc_socket - > send_queue = NULL ;
2005-10-21 05:25:55 +04:00
kdc_socket - > process = kdc_process ;
2005-06-03 15:23:15 +04:00
talloc_steal ( kdc_socket , kdc_socket - > sock ) ;
kdc_socket - > fde = event_add_fd ( kdc - > task - > event_ctx , kdc ,
2005-06-04 09:35:27 +04:00
socket_get_fd ( kdc_socket - > sock ) , EVENT_FD_READ ,
2005-06-03 15:23:15 +04:00
kdc_socket_handler , kdc_socket ) ;
2006-01-10 01:12:53 +03:00
kdc_address = socket_address_from_strings ( kdc_socket , kdc_socket - > sock - > backend_name ,
address , kdc_port ) ;
NT_STATUS_HAVE_NO_MEMORY ( kdc_address ) ;
status = socket_listen ( kdc_socket - > sock , kdc_address , 0 , 0 ) ;
2005-10-14 10:12:05 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-10-21 05:25:55 +04:00
DEBUG ( 0 , ( " Failed to bind to %s:%d UDP for kdc - %s \n " ,
address , kdc_port , nt_errstr ( status ) ) ) ;
2005-10-14 10:12:05 +04:00
talloc_free ( kdc_socket ) ;
return status ;
}
2005-10-21 05:25:55 +04:00
kpasswd_socket - > kdc = kdc ;
kpasswd_socket - > send_queue = NULL ;
kpasswd_socket - > process = kpasswdd_process ;
talloc_steal ( kpasswd_socket , kpasswd_socket - > sock ) ;
kpasswd_socket - > fde = event_add_fd ( kdc - > task - > event_ctx , kdc ,
socket_get_fd ( kpasswd_socket - > sock ) , EVENT_FD_READ ,
kdc_socket_handler , kpasswd_socket ) ;
2006-01-10 01:12:53 +03:00
kpasswd_address = socket_address_from_strings ( kpasswd_socket , kpasswd_socket - > sock - > backend_name ,
address , kpasswd_port ) ;
NT_STATUS_HAVE_NO_MEMORY ( kpasswd_address ) ;
status = socket_listen ( kpasswd_socket - > sock , kpasswd_address , 0 , 0 ) ;
2005-10-21 05:25:55 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " Failed to bind to %s:%d UDP for kpasswd - %s \n " ,
address , kpasswd_port , nt_errstr ( status ) ) ) ;
talloc_free ( kpasswd_socket ) ;
return status ;
}
2005-10-14 10:12:05 +04: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 . */
2008-09-22 20:15:24 +04:00
model_ops = process_model_startup ( kdc - > task - > event_ctx , " single " ) ;
2005-10-14 10:12:05 +04:00
if ( ! model_ops ) {
DEBUG ( 0 , ( " Can't find 'single' process model_ops \n " ) ) ;
talloc_free ( kdc_socket ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
2008-01-06 04:03:43 +03:00
status = stream_setup_socket ( kdc - > task - > event_ctx ,
kdc - > task - > lp_ctx ,
model_ops ,
2005-10-21 05:25:55 +04:00
& kdc_tcp_stream_ops ,
2007-12-06 18:54:34 +03:00
" ip " , address , & kdc_port ,
lp_socket_options ( kdc - > task - > lp_ctx ) ,
kdc ) ;
2005-10-21 05:25:55 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " Failed to bind to %s:%u TCP - %s \n " ,
address , kdc_port , nt_errstr ( status ) ) ) ;
talloc_free ( kdc_socket ) ;
return status ;
}
2008-01-06 04:03:43 +03:00
status = stream_setup_socket ( kdc - > task - > event_ctx ,
kdc - > task - > lp_ctx ,
model_ops ,
2005-10-21 05:25:55 +04:00
& kpasswdd_tcp_stream_ops ,
2007-12-06 18:54:34 +03:00
" ip " , address , & kpasswd_port ,
lp_socket_options ( kdc - > task - > lp_ctx ) ,
kdc ) ;
2005-06-03 15:23:15 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-10-14 10:12:05 +04:00
DEBUG ( 0 , ( " Failed to bind to %s:%u TCP - %s \n " ,
2005-10-21 05:25:55 +04:00
address , kpasswd_port , nt_errstr ( status ) ) ) ;
2005-06-03 15:23:15 +04:00
talloc_free ( kdc_socket ) ;
return status ;
}
return NT_STATUS_OK ;
}
/*
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
{
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
num_interfaces = iface_count ( ifaces ) ;
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 ) ) ;
2007-12-02 21:04:33 +03:00
status = kdc_add_socket ( kdc , address , lp_krb5_port ( lp_ctx ) ,
lp_kpasswd_port ( lp_ctx ) ) ;
2006-02-04 02:19:00 +03:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
}
2005-06-03 15:23:15 +04:00
talloc_free ( tmp_ctx ) ;
return NT_STATUS_OK ;
}
2007-01-10 04:51:35 +03:00
static struct krb5plugin_windc_ftable windc_plugin_table = {
. minor_version = KRB5_WINDC_PLUGING_MINOR ,
. init = samba_kdc_plugin_init ,
. fini = samba_kdc_plugin_fini ,
. pac_generate = samba_kdc_get_pac ,
. pac_verify = samba_kdc_reget_pac ,
. client_access = samba_kdc_check_client_access ,
} ;
2008-09-03 09:30:17 +04:00
static NTSTATUS kdc_check_generic_kerberos ( struct irpc_message * msg ,
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 ) ;
ndr_err = ndr_pull_struct_blob ( & r - > in . generic_request , msg ,
lp_iconv_convenience ( kdc - > task - > lp_ctx ) ,
& pac_validate ,
( ndr_pull_flags_fn_t ) ndr_pull_PAC_Validate ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return NT_STATUS_INVALID_PARAMETER ;
}
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 ;
}
srv_sig = data_blob_const ( pac_validate . ChecksumAndSignature . data ,
pac_validate . ChecksumLength ) ;
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 ;
}
}
ret = krb5_make_principal ( kdc - > smb_krb5_context - > krb5_context , & principal ,
lp_realm ( kdc - > task - > lp_ctx ) ,
" krbtgt " , lp_realm ( kdc - > task - > lp_ctx ) ,
NULL ) ;
if ( ret ! = 0 ) {
return NT_STATUS_NO_MEMORY ;
}
ret = kdc - > config - > db [ 0 ] - > hdb_fetch ( kdc - > smb_krb5_context - > krb5_context ,
kdc - > config - > db [ 0 ] ,
principal ,
HDB_F_GET_KRBTGT | HDB_F_DECRYPT ,
& ent ) ;
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 ;
}
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 ;
kdc_sig . type = pac_validate . SignatureType ;
kdc_sig . signature = data_blob_const ( & pac_validate . ChecksumAndSignature . data [ pac_validate . ChecksumLength ] ,
pac_validate . SignatureLength ) ;
ret = check_pac_checksum ( msg , srv_sig , & kdc_sig ,
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 ;
}
return NT_STATUS_OK ;
}
2008-10-20 07:21:21 +04:00
static struct hdb_method hdb_samba4 = {
. interface_version = HDB_INTERFACE_VERSION ,
. prefix = " samba4: " ,
. create = hdb_samba4_create
} ;
2008-09-03 09:30:17 +04:00
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
2007-12-02 21:04:33 +03:00
switch ( lp_server_role ( task - > lp_ctx ) ) {
2006-02-04 02:19:00 +03:00
case ROLE_STANDALONE :
task_server_terminate ( task , " kdc: no KDC required in standalone configuration " ) ;
return ;
case ROLE_DOMAIN_MEMBER :
task_server_terminate ( task , " kdc: no KDC required in member server configuration " ) ;
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 ;
}
2007-12-12 00:23:20 +03:00
load_interfaces ( task , lp_interfaces ( task - > lp_ctx ) , & ifaces ) ;
2007-12-12 00:23:14 +03:00
if ( iface_count ( ifaces ) = = 0 ) {
2005-06-26 03:53:14 +04:00
task_server_terminate ( task , " kdc: no network interfaces configured " ) ;
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 ) {
2005-06-26 03:53:14 +04:00
task_server_terminate ( task , " kdc: out of memory " ) ;
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 ) {
DEBUG ( 1 , ( " kdc_task_init: krb5_init_context failed (%s) \n " ,
error_message ( ret ) ) ) ;
2005-06-26 03:53:14 +04:00
task_server_terminate ( task , " kdc: krb5_init_context failed " ) ;
2005-06-03 18:32:10 +04:00
return ;
}
2005-09-28 06:22:31 +04:00
krb5_add_et_list ( kdc - > smb_krb5_context - > krb5_context , initialize_hdb_error_table_r ) ;
2007-06-13 09:44:24 +04:00
ret = krb5_kdc_get_config ( kdc - > smb_krb5_context - > krb5_context ,
& kdc - > config ) ;
2007-01-24 05:48:40 +03:00
if ( ret ) {
2007-06-13 09:44:24 +04:00
task_server_terminate ( task , " kdc: failed to get KDC configuration " ) ;
2007-01-24 05:48:40 +03:00
return ;
}
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 ) {
2005-06-26 03:53:14 +04:00
task_server_terminate ( task , " kdc: out of memory " ) ;
2005-06-03 18:32:10 +04:00
return ;
}
kdc - > config - > num_db = 1 ;
2008-09-24 23:53:10 +04:00
status = kdc_hdb_samba4_create ( kdc , task - > event_ctx , task - > lp_ctx ,
2007-12-04 02:12:03 +03:00
kdc - > smb_krb5_context - > krb5_context ,
2006-01-24 08:31:08 +03:00
& kdc - > config - > db [ 0 ] , NULL ) ;
2005-12-11 11:31:46 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
task_server_terminate ( task , " kdc: hdb_ldb_create (setup KDC database) failed " ) ;
2005-06-03 18:32:10 +04:00
return ;
}
2005-06-03 15:23:15 +04:00
2008-09-24 23:53:10 +04:00
/* Register hdb-samba4 hooks */
ret = krb5_plugin_register ( kdc - > smb_krb5_context - > krb5_context ,
PLUGIN_TYPE_DATA , " hdb " ,
& hdb_samba4 ) ;
if ( ret ) {
task_server_terminate ( task , " kdc: failed to register hdb keytab " ) ;
return ;
}
2006-01-24 08:31:08 +03:00
ret = krb5_kt_register ( kdc - > smb_krb5_context - > krb5_context , & hdb_kt_ops ) ;
if ( ret ) {
task_server_terminate ( task , " kdc: failed to register hdb keytab " ) ;
return ;
}
2007-01-10 04:51:35 +03:00
2007-06-13 09:44:24 +04:00
/* Registar WinDC hooks */
2008-03-19 02:17:42 +03:00
ret = krb5_plugin_register ( kdc - > smb_krb5_context - > krb5_context ,
PLUGIN_TYPE_DATA , " windc " ,
& windc_plugin_table ) ;
2007-06-13 09:44:24 +04:00
if ( ret ) {
task_server_terminate ( task , " kdc: failed to register hdb keytab " ) ;
return ;
}
krb5_kdc_windc_init ( kdc - > smb_krb5_context - > krb5_context ) ;
2007-01-10 04:51:35 +03:00
2007-01-24 05:48:40 +03:00
kdc_mem_ctx = kdc - > smb_krb5_context ;
2008-12-29 11:37:02 +03:00
kdc_ev_ctx = task - > event_ctx ;
2008-02-21 19:17:37 +03:00
kdc_lp_ctx = task - > lp_ctx ;
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 ) ) {
2005-06-26 03:53:14 +04:00
task_server_terminate ( task , " kdc failed to setup interfaces " ) ;
2005-06-03 15:23:15 +04:00
return ;
}
2005-07-19 13:27:20 +04:00
2008-09-03 09:30:17 +04:00
status = IRPC_REGISTER ( task - > msg_ctx , irpc , KDC_CHECK_GENERIC_KERBEROS ,
kdc_check_generic_kerberos , kdc ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
task_server_terminate ( task , " nbtd failed to setup monitoring " ) ;
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
}