2 Unix SMB/CIFS implementation.
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2008
7 Copyright (C) Andrew Tridgell 2005
8 Copyright (C) Stefan Metzmacher 2005
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "smbd/service_task.h"
26 #include "smbd/service.h"
27 #include "smbd/service_stream.h"
28 #include "smbd/process_model.h"
29 #include "lib/events/events.h"
30 #include "lib/socket/socket.h"
31 #include "lib/tsocket/tsocket.h"
32 #include "libcli/util/tstream.h"
33 #include "system/network.h"
34 #include "../lib/util/dlinklist.h"
35 #include "lib/messaging/irpc.h"
36 #include "lib/stream/packet.h"
37 #include "librpc/gen_ndr/samr.h"
38 #include "librpc/gen_ndr/ndr_irpc.h"
39 #include "librpc/gen_ndr/ndr_krb5pac.h"
40 #include "lib/socket/netif.h"
41 #include "param/param.h"
43 #include "librpc/gen_ndr/ndr_misc.h"
46 /* Disgusting hack to get a mem_ctx and lp_ctx into the hdb plugin, when
48 TALLOC_CTX
*hdb_samba4_mem_ctx
;
49 struct tevent_context
*hdb_samba4_ev_ctx
;
50 struct loadparm_context
*hdb_samba4_lp_ctx
;
51 extern struct krb5plugin_windc_ftable windc_plugin_table
;
52 extern struct hdb_method hdb_samba4
;
54 typedef bool (*kdc_process_fn_t
)(struct kdc_server
*kdc
,
58 struct tsocket_address
*peer_addr
,
59 struct tsocket_address
*my_addr
,
62 /* hold information about one kdc socket */
64 struct kdc_server
*kdc
;
65 struct tsocket_address
*local_address
;
66 kdc_process_fn_t process
;
70 state of an open tcp connection
72 struct kdc_tcp_connection
{
73 /* stream connection we belong to */
74 struct stream_connection
*conn
;
76 /* the kdc_server the connection belongs to */
77 struct kdc_socket
*kdc_socket
;
79 struct tstream_context
*tstream
;
81 struct tevent_queue
*send_queue
;
84 static void kdc_tcp_terminate_connection(struct kdc_tcp_connection
*kdcconn
, const char *reason
)
86 stream_terminate_connection(kdcconn
->conn
, reason
);
89 static void kdc_tcp_recv(struct stream_connection
*conn
, uint16_t flags
)
91 struct kdc_tcp_connection
*kdcconn
= talloc_get_type(conn
->private_data
,
92 struct kdc_tcp_connection
);
93 /* this should never be triggered! */
94 kdc_tcp_terminate_connection(kdcconn
, "kdc_tcp_recv: called");
97 static void kdc_tcp_send(struct stream_connection
*conn
, uint16_t flags
)
99 struct kdc_tcp_connection
*kdcconn
= talloc_get_type(conn
->private_data
,
100 struct kdc_tcp_connection
);
101 /* this should never be triggered! */
102 kdc_tcp_terminate_connection(kdcconn
, "kdc_tcp_send: called");
106 Wrapper for krb5_kdc_process_krb5_request, converting to/from Samba
110 static bool kdc_process(struct kdc_server
*kdc
,
114 struct tsocket_address
*peer_addr
,
115 struct tsocket_address
*my_addr
,
120 struct sockaddr_storage ss
;
122 krb5_data_zero(&k5_reply
);
124 krb5_kdc_update_time(NULL
);
126 ret
= tsocket_address_bsd_sockaddr(peer_addr
, (struct sockaddr
*) &ss
,
127 sizeof(struct sockaddr_storage
));
131 pa
= tsocket_address_string(peer_addr
, mem_ctx
);
136 DEBUG(10,("Received KDC packet of length %lu from %s\n",
137 (long)input
->length
- 4, pa
));
139 ret
= krb5_kdc_process_krb5_request(kdc
->smb_krb5_context
->krb5_context
,
141 input
->data
, input
->length
,
144 (struct sockaddr
*) &ss
,
147 *reply
= data_blob(NULL
, 0);
150 if (k5_reply
.length
) {
151 *reply
= data_blob_talloc(mem_ctx
, k5_reply
.data
, k5_reply
.length
);
152 krb5_data_free(&k5_reply
);
154 *reply
= data_blob(NULL
, 0);
159 struct kdc_tcp_call
{
160 struct kdc_tcp_connection
*kdc_conn
;
164 struct iovec out_iov
[2];
167 static void kdc_tcp_call_writev_done(struct tevent_req
*subreq
);
169 static void kdc_tcp_call_loop(struct tevent_req
*subreq
)
171 struct kdc_tcp_connection
*kdc_conn
= tevent_req_callback_data(subreq
,
172 struct kdc_tcp_connection
);
173 struct kdc_tcp_call
*call
;
177 call
= talloc(kdc_conn
, struct kdc_tcp_call
);
179 kdc_tcp_terminate_connection(kdc_conn
, "kdc_tcp_call_loop: "
180 "no memory for kdc_tcp_call");
183 call
->kdc_conn
= kdc_conn
;
185 status
= tstream_read_pdu_blob_recv(subreq
,
189 if (!NT_STATUS_IS_OK(status
)) {
192 reason
= talloc_asprintf(call
, "kdc_tcp_call_loop: "
193 "tstream_read_pdu_blob_recv() - %s",
196 reason
= nt_errstr(status
);
199 kdc_tcp_terminate_connection(kdc_conn
, reason
);
203 DEBUG(10,("Received krb5 TCP packet of length %lu from %s\n",
204 (long) call
->in
.length
,
205 tsocket_address_string(kdc_conn
->conn
->remote_address
, call
)));
207 /* skip length header */
209 call
->in
.length
-= 4;
212 ok
= kdc_conn
->kdc_socket
->process(kdc_conn
->kdc_socket
->kdc
,
216 kdc_conn
->conn
->remote_address
,
217 kdc_conn
->conn
->local_address
,
220 kdc_tcp_terminate_connection(kdc_conn
,
221 "kdc_tcp_call_loop: process function failed");
225 /* First add the length of the out buffer */
226 RSIVAL(call
->out_hdr
, 0, call
->out
.length
);
227 call
->out_iov
[0].iov_base
= call
->out_hdr
;
228 call
->out_iov
[0].iov_len
= 4;
230 call
->out_iov
[1].iov_base
= call
->out
.data
;
231 call
->out_iov
[1].iov_len
= call
->out
.length
;
233 subreq
= tstream_writev_queue_send(call
,
234 kdc_conn
->conn
->event
.ctx
,
236 kdc_conn
->send_queue
,
238 if (subreq
== NULL
) {
239 kdc_tcp_terminate_connection(kdc_conn
, "kdc_tcp_call_loop: "
240 "no memory for tstream_writev_queue_send");
243 tevent_req_set_callback(subreq
, kdc_tcp_call_writev_done
, call
);
246 * The krb5 tcp pdu's has the length as 4 byte (initial_read_size),
247 * packet_full_request_u32 provides the pdu length then.
249 subreq
= tstream_read_pdu_blob_send(kdc_conn
,
250 kdc_conn
->conn
->event
.ctx
,
252 4, /* initial_read_size */
253 packet_full_request_u32
,
255 if (subreq
== NULL
) {
256 kdc_tcp_terminate_connection(kdc_conn
, "kdc_tcp_call_loop: "
257 "no memory for tstream_read_pdu_blob_send");
260 tevent_req_set_callback(subreq
, kdc_tcp_call_loop
, kdc_conn
);
263 static void kdc_tcp_call_writev_done(struct tevent_req
*subreq
)
265 struct kdc_tcp_call
*call
= tevent_req_callback_data(subreq
,
266 struct kdc_tcp_call
);
270 rc
= tstream_writev_queue_recv(subreq
, &sys_errno
);
275 reason
= talloc_asprintf(call
, "kdc_tcp_call_writev_done: "
276 "tstream_writev_queue_recv() - %d:%s",
277 sys_errno
, strerror(sys_errno
));
279 reason
= "kdc_tcp_call_writev_done: tstream_writev_queue_recv() failed";
282 kdc_tcp_terminate_connection(call
->kdc_conn
, reason
);
286 /* We don't care about errors */
292 called when we get a new connection
294 static void kdc_tcp_accept(struct stream_connection
*conn
)
296 struct kdc_socket
*kdc_socket
;
297 struct kdc_tcp_connection
*kdc_conn
;
298 struct tevent_req
*subreq
;
301 kdc_conn
= talloc_zero(conn
, struct kdc_tcp_connection
);
302 if (kdc_conn
== NULL
) {
303 stream_terminate_connection(conn
,
304 "kdc_tcp_accept: out of memory");
308 kdc_conn
->send_queue
= tevent_queue_create(conn
, "kdc_tcp_accept");
309 if (kdc_conn
->send_queue
== NULL
) {
310 stream_terminate_connection(conn
,
311 "kdc_tcp_accept: out of memory");
315 kdc_socket
= talloc_get_type(conn
->private_data
, struct kdc_socket
);
317 TALLOC_FREE(conn
->event
.fde
);
319 rc
= tstream_bsd_existing_socket(kdc_conn
->tstream
,
320 socket_get_fd(conn
->socket
),
323 stream_terminate_connection(conn
,
324 "kdc_tcp_accept: out of memory");
328 kdc_conn
->conn
= conn
;
329 kdc_conn
->kdc_socket
= kdc_socket
;
330 conn
->private_data
= kdc_conn
;
333 * The krb5 tcp pdu's has the length as 4 byte (initial_read_size),
334 * packet_full_request_u32 provides the pdu length then.
336 subreq
= tstream_read_pdu_blob_send(kdc_conn
,
337 kdc_conn
->conn
->event
.ctx
,
339 4, /* initial_read_size */
340 packet_full_request_u32
,
342 if (subreq
== NULL
) {
343 kdc_tcp_terminate_connection(kdc_conn
, "kdc_tcp_accept: "
344 "no memory for tstream_read_pdu_blob_send");
347 tevent_req_set_callback(subreq
, kdc_tcp_call_loop
, kdc_conn
);
350 static const struct stream_server_ops kdc_tcp_stream_ops
= {
352 .accept_connection
= kdc_tcp_accept
,
353 .recv_handler
= kdc_tcp_recv
,
354 .send_handler
= kdc_tcp_send
357 /* hold information about one kdc/kpasswd udp socket */
358 struct kdc_udp_socket
{
359 struct kdc_socket
*kdc_socket
;
360 struct tdgram_context
*dgram
;
361 struct tevent_queue
*send_queue
;
364 struct kdc_udp_call
{
365 struct tsocket_address
*src
;
370 static void kdc_udp_call_sendto_done(struct tevent_req
*subreq
);
372 static void kdc_udp_call_loop(struct tevent_req
*subreq
)
374 struct kdc_udp_socket
*sock
= tevent_req_callback_data(subreq
,
375 struct kdc_udp_socket
);
376 struct kdc_udp_call
*call
;
382 call
= talloc(sock
, struct kdc_udp_call
);
388 len
= tdgram_recvfrom_recv(subreq
, &sys_errno
,
389 call
, &buf
, &call
->src
);
397 call
->in
.length
= len
;
399 DEBUG(10,("Received krb5 UDP packet of length %lu from %s\n",
400 (long)call
->in
.length
,
401 tsocket_address_string(call
->src
, call
)));
404 ok
= sock
->kdc_socket
->process(sock
->kdc_socket
->kdc
,
409 sock
->kdc_socket
->local_address
,
416 subreq
= tdgram_sendto_queue_send(call
,
417 sock
->kdc_socket
->kdc
->task
->event_ctx
,
423 if (subreq
== NULL
) {
427 tevent_req_set_callback(subreq
, kdc_udp_call_sendto_done
, call
);
430 subreq
= tdgram_recvfrom_send(sock
,
431 sock
->kdc_socket
->kdc
->task
->event_ctx
,
433 if (subreq
== NULL
) {
434 task_server_terminate(sock
->kdc_socket
->kdc
->task
,
435 "no memory for tdgram_recvfrom_send",
439 tevent_req_set_callback(subreq
, kdc_udp_call_loop
, sock
);
442 static void kdc_udp_call_sendto_done(struct tevent_req
*subreq
)
444 struct kdc_udp_call
*call
= tevent_req_callback_data(subreq
,
445 struct kdc_udp_call
);
449 ret
= tdgram_sendto_queue_recv(subreq
, &sys_errno
);
451 /* We don't care about errors */
457 start listening on the given address
459 static NTSTATUS
kdc_add_socket(struct kdc_server
*kdc
,
460 const struct model_ops
*model_ops
,
464 kdc_process_fn_t process
)
466 struct kdc_socket
*kdc_socket
;
467 struct kdc_udp_socket
*kdc_udp_socket
;
468 struct tevent_req
*udpsubreq
;
472 kdc_socket
= talloc(kdc
, struct kdc_socket
);
473 NT_STATUS_HAVE_NO_MEMORY(kdc_socket
);
475 kdc_socket
->kdc
= kdc
;
476 kdc_socket
->process
= process
;
478 ret
= tsocket_address_inet_from_strings(kdc_socket
, "ip",
480 &kdc_socket
->local_address
);
482 status
= map_nt_error_from_unix(errno
);
486 status
= stream_setup_socket(kdc
->task
->event_ctx
,
490 "ip", address
, &port
,
491 lp_socket_options(kdc
->task
->lp_ctx
),
493 if (!NT_STATUS_IS_OK(status
)) {
494 DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
495 address
, port
, nt_errstr(status
)));
496 talloc_free(kdc_socket
);
500 kdc_udp_socket
= talloc(kdc_socket
, struct kdc_udp_socket
);
501 NT_STATUS_HAVE_NO_MEMORY(kdc_udp_socket
);
503 kdc_udp_socket
->kdc_socket
= kdc_socket
;
505 ret
= tdgram_inet_udp_socket(kdc_socket
->local_address
,
508 &kdc_udp_socket
->dgram
);
510 status
= map_nt_error_from_unix(errno
);
511 DEBUG(0,("Failed to bind to %s:%u UDP - %s\n",
512 address
, port
, nt_errstr(status
)));
516 kdc_udp_socket
->send_queue
= tevent_queue_create(kdc_udp_socket
,
517 "kdc_udp_send_queue");
518 NT_STATUS_HAVE_NO_MEMORY(kdc_udp_socket
->send_queue
);
520 udpsubreq
= tdgram_recvfrom_send(kdc_udp_socket
,
521 kdc
->task
->event_ctx
,
522 kdc_udp_socket
->dgram
);
523 NT_STATUS_HAVE_NO_MEMORY(udpsubreq
);
524 tevent_req_set_callback(udpsubreq
, kdc_udp_call_loop
, kdc_udp_socket
);
531 setup our listening sockets on the configured network interfaces
533 static NTSTATUS
kdc_startup_interfaces(struct kdc_server
*kdc
, struct loadparm_context
*lp_ctx
,
534 struct interface
*ifaces
)
536 const struct model_ops
*model_ops
;
538 TALLOC_CTX
*tmp_ctx
= talloc_new(kdc
);
542 /* within the kdc task we want to be a single process, so
543 ask for the single process model ops and pass these to the
544 stream_setup_socket() call. */
545 model_ops
= process_model_startup(kdc
->task
->event_ctx
, "single");
547 DEBUG(0,("Can't find 'single' process model_ops\n"));
548 return NT_STATUS_INTERNAL_ERROR
;
551 num_interfaces
= iface_count(ifaces
);
553 for (i
=0; i
<num_interfaces
; i
++) {
554 const char *address
= talloc_strdup(tmp_ctx
, iface_n_ip(ifaces
, i
));
555 uint16_t kdc_port
= lp_krb5_port(lp_ctx
);
556 uint16_t kpasswd_port
= lp_kpasswd_port(lp_ctx
);
559 status
= kdc_add_socket(kdc
, model_ops
,
560 "kdc", address
, kdc_port
,
562 NT_STATUS_NOT_OK_RETURN(status
);
566 status
= kdc_add_socket(kdc
, model_ops
,
567 "kpasswd", address
, kpasswd_port
,
569 NT_STATUS_NOT_OK_RETURN(status
);
573 talloc_free(tmp_ctx
);
579 static NTSTATUS
kdc_check_generic_kerberos(struct irpc_message
*msg
,
580 struct kdc_check_generic_kerberos
*r
)
582 struct PAC_Validate pac_validate
;
584 struct PAC_SIGNATURE_DATA kdc_sig
;
585 struct kdc_server
*kdc
= talloc_get_type(msg
->private_data
, struct kdc_server
);
586 enum ndr_err_code ndr_err
;
590 krb5_principal principal
;
591 krb5_keyblock keyblock
;
594 /* There is no reply to this request */
595 r
->out
.generic_reply
= data_blob(NULL
, 0);
597 ndr_err
= ndr_pull_struct_blob(&r
->in
.generic_request
, msg
,
598 lp_iconv_convenience(kdc
->task
->lp_ctx
),
600 (ndr_pull_flags_fn_t
)ndr_pull_PAC_Validate
);
601 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
602 return NT_STATUS_INVALID_PARAMETER
;
605 if (pac_validate
.MessageType
!= 3) {
606 /* We don't implement any other message types - such as certificate validation - yet */
607 return NT_STATUS_INVALID_PARAMETER
;
610 if (pac_validate
.ChecksumAndSignature
.length
!= (pac_validate
.ChecksumLength
+ pac_validate
.SignatureLength
)
611 || pac_validate
.ChecksumAndSignature
.length
< pac_validate
.ChecksumLength
612 || pac_validate
.ChecksumAndSignature
.length
< pac_validate
.SignatureLength
) {
613 return NT_STATUS_INVALID_PARAMETER
;
616 srv_sig
= data_blob_const(pac_validate
.ChecksumAndSignature
.data
,
617 pac_validate
.ChecksumLength
);
619 if (pac_validate
.SignatureType
== CKSUMTYPE_HMAC_MD5
) {
620 etype
= ETYPE_ARCFOUR_HMAC_MD5
;
622 ret
= krb5_cksumtype_to_enctype(kdc
->smb_krb5_context
->krb5_context
, pac_validate
.SignatureType
,
625 return NT_STATUS_LOGON_FAILURE
;
629 ret
= krb5_make_principal(kdc
->smb_krb5_context
->krb5_context
, &principal
,
630 lp_realm(kdc
->task
->lp_ctx
),
631 "krbtgt", lp_realm(kdc
->task
->lp_ctx
),
635 return NT_STATUS_NO_MEMORY
;
638 ret
= kdc
->config
->db
[0]->hdb_fetch(kdc
->smb_krb5_context
->krb5_context
,
641 HDB_F_GET_KRBTGT
| HDB_F_DECRYPT
,
645 hdb_free_entry(kdc
->smb_krb5_context
->krb5_context
, &ent
);
646 krb5_free_principal(kdc
->smb_krb5_context
->krb5_context
, principal
);
648 return NT_STATUS_LOGON_FAILURE
;
651 ret
= hdb_enctype2key(kdc
->smb_krb5_context
->krb5_context
, &ent
.entry
, etype
, &key
);
654 hdb_free_entry(kdc
->smb_krb5_context
->krb5_context
, &ent
);
655 krb5_free_principal(kdc
->smb_krb5_context
->krb5_context
, principal
);
656 return NT_STATUS_LOGON_FAILURE
;
661 kdc_sig
.type
= pac_validate
.SignatureType
;
662 kdc_sig
.signature
= data_blob_const(&pac_validate
.ChecksumAndSignature
.data
[pac_validate
.ChecksumLength
],
663 pac_validate
.SignatureLength
);
664 ret
= check_pac_checksum(msg
, srv_sig
, &kdc_sig
,
665 kdc
->smb_krb5_context
->krb5_context
, &keyblock
);
667 hdb_free_entry(kdc
->smb_krb5_context
->krb5_context
, &ent
);
668 krb5_free_principal(kdc
->smb_krb5_context
->krb5_context
, principal
);
671 return NT_STATUS_LOGON_FAILURE
;
681 static void kdc_task_init(struct task_server
*task
)
683 struct kdc_server
*kdc
;
686 struct interface
*ifaces
;
688 switch (lp_server_role(task
->lp_ctx
)) {
689 case ROLE_STANDALONE
:
690 task_server_terminate(task
, "kdc: no KDC required in standalone configuration", false);
692 case ROLE_DOMAIN_MEMBER
:
693 task_server_terminate(task
, "kdc: no KDC required in member server configuration", false);
695 case ROLE_DOMAIN_CONTROLLER
:
696 /* Yes, we want a KDC */
700 load_interfaces(task
, lp_interfaces(task
->lp_ctx
), &ifaces
);
702 if (iface_count(ifaces
) == 0) {
703 task_server_terminate(task
, "kdc: no network interfaces configured", false);
707 task_server_set_title(task
, "task[kdc]");
709 kdc
= talloc(task
, struct kdc_server
);
711 task_server_terminate(task
, "kdc: out of memory", true);
717 initialize_krb5_error_table();
719 ret
= smb_krb5_init_context(kdc
, task
->event_ctx
, task
->lp_ctx
, &kdc
->smb_krb5_context
);
721 DEBUG(1,("kdc_task_init: krb5_init_context failed (%s)\n",
722 error_message(ret
)));
723 task_server_terminate(task
, "kdc: krb5_init_context failed", true);
727 krb5_add_et_list(kdc
->smb_krb5_context
->krb5_context
, initialize_hdb_error_table_r
);
729 ret
= krb5_kdc_get_config(kdc
->smb_krb5_context
->krb5_context
,
732 task_server_terminate(task
, "kdc: failed to get KDC configuration", true);
736 kdc
->config
->logf
= kdc
->smb_krb5_context
->logf
;
737 kdc
->config
->db
= talloc(kdc
, struct HDB
*);
738 if (!kdc
->config
->db
) {
739 task_server_terminate(task
, "kdc: out of memory", true);
742 kdc
->config
->num_db
= 1;
744 /* Register hdb-samba4 hooks for use as a keytab */
746 kdc
->base_ctx
= talloc_zero(kdc
, struct samba_kdc_base_context
);
747 if (!kdc
->base_ctx
) {
748 task_server_terminate(task
, "kdc: out of memory", true);
752 kdc
->base_ctx
->ev_ctx
= task
->event_ctx
;
753 kdc
->base_ctx
->lp_ctx
= task
->lp_ctx
;
755 status
= hdb_samba4_create_kdc(kdc
->base_ctx
,
756 kdc
->smb_krb5_context
->krb5_context
,
757 &kdc
->config
->db
[0]);
758 if (!NT_STATUS_IS_OK(status
)) {
759 task_server_terminate(task
, "kdc: hdb_samba4_create_kdc (setup KDC database) failed", true);
763 ret
= krb5_plugin_register(kdc
->smb_krb5_context
->krb5_context
,
764 PLUGIN_TYPE_DATA
, "hdb",
767 task_server_terminate(task
, "kdc: failed to register hdb plugin", true);
771 ret
= krb5_kt_register(kdc
->smb_krb5_context
->krb5_context
, &hdb_kt_ops
);
773 task_server_terminate(task
, "kdc: failed to register keytab plugin", true);
777 /* Register WinDC hooks */
778 ret
= krb5_plugin_register(kdc
->smb_krb5_context
->krb5_context
,
779 PLUGIN_TYPE_DATA
, "windc",
780 &windc_plugin_table
);
782 task_server_terminate(task
, "kdc: failed to register windc plugin", true);
786 krb5_kdc_windc_init(kdc
->smb_krb5_context
->krb5_context
);
788 /* start listening on the configured network interfaces */
789 status
= kdc_startup_interfaces(kdc
, task
->lp_ctx
, ifaces
);
790 if (!NT_STATUS_IS_OK(status
)) {
791 task_server_terminate(task
, "kdc failed to setup interfaces", true);
795 status
= IRPC_REGISTER(task
->msg_ctx
, irpc
, KDC_CHECK_GENERIC_KERBEROS
,
796 kdc_check_generic_kerberos
, kdc
);
797 if (!NT_STATUS_IS_OK(status
)) {
798 task_server_terminate(task
, "nbtd failed to setup monitoring", true);
802 irpc_add_name(task
->msg_ctx
, "kdc_server");
806 /* called at smbd startup - register ourselves as a server service */
807 NTSTATUS
server_service_kdc_init(void)
809 return register_server_service("kdc", kdc_task_init
);