r14079: I just found the setproctitle library from alt linux:-)
[jelmer/samba4-debian.git] / source / kdc / kdc.c
index 9c27db6dc1a39d820af05c21817c105cb0a30a11..2fd5674f79456bcc0d2441b4001ce58a3e94f428 100644 (file)
 
 #include "includes.h"
 #include "smbd/service_task.h"
+#include "smbd/service.h"
 #include "smbd/service_stream.h"
+#include "smbd/process_model.h"
 #include "lib/events/events.h"
 #include "lib/socket/socket.h"
 #include "kdc/kdc.h"
 #include "system/network.h"
 #include "dlinklist.h"
 #include "lib/messaging/irpc.h"
+#include "lib/stream/packet.h"
+#include "librpc/gen_ndr/samr.h"
+#include "netif/netif.h"
 
 /* hold all the info needed to send a reply */
 struct kdc_reply {
        struct kdc_reply *next, *prev;
-       const char *dest_address;
-       int dest_port;
+       struct socket_address *dest;
        DATA_BLOB packet;
 };
 
-/*
-  top level context structure for the kdc server
-*/
-struct kdc_server {
-       struct task_server *task;
-       krb5_kdc_configuration *config;
-       struct smb_krb5_context *smb_krb5_context;
-};
+typedef BOOL (*kdc_process_fn_t)(struct kdc_server *kdc,
+                                TALLOC_CTX *mem_ctx, 
+                                DATA_BLOB *input, 
+                                DATA_BLOB *reply,
+                                struct socket_address *peer_addr, 
+                                struct socket_address *my_addr);
 
 /* hold information about one kdc socket */
 struct kdc_socket {
@@ -57,6 +59,8 @@ struct kdc_socket {
 
        /* a queue of outgoing replies that have been deferred */
        struct kdc_reply *send_queue;
+
+       kdc_process_fn_t process;
 };
 /*
   state of an open tcp connection
@@ -68,17 +72,9 @@ struct kdc_tcp_connection {
        /* the kdc_server the connection belongs to */
        struct kdc_server *kdc;
 
-       /* the partial data we've receiced yet */
-       DATA_BLOB partial;
-
-       /* the amount that we used yet from the partial buffer */
-       uint32_t partial_read;
+       struct packet_context *packet;
 
-       /* prevent loops when we use half async code, while processing a requuest */
-       BOOL processing;
-
-       /* a queue of outgoing replies that have been deferred */
-       struct data_blob_list_item *send_queue;
+       kdc_process_fn_t process;
 };
 
 /*
@@ -92,10 +88,13 @@ static void kdc_send_handler(struct kdc_socket *kdc_socket)
                size_t sendlen;
 
                status = socket_sendto(kdc_socket->sock, &rep->packet, &sendlen, 0,
-                                      rep->dest_address, rep->dest_port);
+                                      rep->dest);
                if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
                        break;
                }
+               if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_BUFFER_SIZE)) {
+                       /* Replace with a krb err, response to big */
+               }
                
                DLIST_REMOVE(kdc_socket->send_queue, rep);
                talloc_free(rep);
@@ -116,12 +115,10 @@ static void kdc_recv_handler(struct kdc_socket *kdc_socket)
        TALLOC_CTX *tmp_ctx = talloc_new(kdc_socket);
        DATA_BLOB blob;
        struct kdc_reply *rep;
-       krb5_data reply;
+       DATA_BLOB reply;
        size_t nread, dsize;
-       const char *src_addr;
-       int src_port;
-       struct sockaddr_in src_sock_addr;
-       struct ipv4_addr addr;
+       struct socket_address *src;
+       struct socket_address *my_addr;
        int ret;
 
        status = socket_pending(kdc_socket->sock, &dsize);
@@ -138,35 +135,30 @@ static void kdc_recv_handler(struct kdc_socket *kdc_socket)
        }
 
        status = socket_recvfrom(kdc_socket->sock, blob.data, blob.length, &nread, 0,
-                                &src_addr, &src_port);
+                                tmp_ctx, &src);
        if (!NT_STATUS_IS_OK(status)) {
                talloc_free(tmp_ctx);
                return;
        }
-       talloc_steal(tmp_ctx, src_addr);
        blob.length = nread;
        
-       DEBUG(2,("Received krb5 UDP packet of length %u from %s:%u\n", 
-                blob.length, src_addr, (uint16_t)src_port));
-       
-       /* TODO:  This really should be in a utility function somewhere */
-       ZERO_STRUCT(src_sock_addr);
-#ifdef HAVE_SOCK_SIN_LEN
-       src_sock_addr.sin_len         = sizeof(src_sock_addr);
-#endif
-       addr                     = interpret_addr2(src_addr);
-       src_sock_addr.sin_addr.s_addr = addr.addr;
-       src_sock_addr.sin_port        = htons(src_port);
-       src_sock_addr.sin_family      = PF_INET;
+       DEBUG(10,("Received krb5 UDP packet of length %lu from %s:%u\n", 
+                (long)blob.length, src->addr, (uint16_t)src->port));
        
+       my_addr = socket_get_my_addr(kdc_socket->sock, tmp_ctx);
+       if (!my_addr) {
+               talloc_free(tmp_ctx);
+               return;
+       }
+
+
        /* Call krb5 */
-       ret = krb5_kdc_process_krb5_request(kdc_socket->kdc->smb_krb5_context->krb5_context, 
-                                           kdc_socket->kdc->config,
-                                           blob.data, blob.length, 
-                                           &reply,
-                                           src_addr,
-                                           (struct sockaddr *)&src_sock_addr);
-       if (ret == -1) {
+       ret = kdc_socket->process(kdc_socket->kdc, 
+                                 tmp_ctx, 
+                                 &blob,  
+                                 &reply,
+                                 src, my_addr);
+       if (!ret) {
                talloc_free(tmp_ctx);
                return;
        }
@@ -174,14 +166,12 @@ static void kdc_recv_handler(struct kdc_socket *kdc_socket)
        /* queue a pending reply */
        rep = talloc(kdc_socket, struct kdc_reply);
        if (rep == NULL) {
-               krb5_data_free(&reply);
                talloc_free(tmp_ctx);
                return;
        }
-       rep->dest_address = talloc_steal(rep, src_addr);
-       rep->dest_port    = src_port;
-       rep->packet       = data_blob_talloc(rep, reply.data, reply.length);
-       krb5_data_free(&reply);
+       rep->dest         = talloc_steal(rep, src);
+       rep->packet       = reply;
+       talloc_steal(rep, reply.data);
 
        if (rep->packet.data == NULL) {
                talloc_free(rep);
@@ -215,155 +205,84 @@ static void kdc_tcp_terminate_connection(struct kdc_tcp_connection *kdcconn, con
 }
 
 /*
-  called when we get a new connection
+  receive a full packet on a KDC connection
 */
-static void kdc_tcp_accept(struct stream_connection *conn)
+static NTSTATUS kdc_tcp_recv(void *private, DATA_BLOB blob)
 {
-       struct kdc_server *kdc = talloc_get_type(conn->private, struct kdc_server);
-       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;
-       conn->private = kdcconn;
-}
-
-/*
-  receive some data on a winbind connection
-*/
-static void kdc_tcp_recv(struct stream_connection *conn, uint16_t flags)
-{
-       struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private, struct kdc_tcp_connection);
+       struct kdc_tcp_connection *kdcconn = talloc_get_type(private, 
+                                                            struct kdc_tcp_connection);
        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
        TALLOC_CTX *tmp_ctx = talloc_new(kdcconn);
-       struct data_blob_list_item *rep;
-       krb5_data reply;
-       size_t nread;
-       const char *src_addr;
-       int src_port;
-       struct sockaddr_in src_sock_addr;
-       struct ipv4_addr addr;
        int ret;
+       DATA_BLOB input, reply;
+       struct socket_address *src_addr;
+       struct socket_address *my_addr;
 
-       /* avoid recursion, because of half async code */
-       if (kdcconn->processing) {
-               EVENT_FD_NOT_READABLE(conn->event.fde);
-               return;
-       }
-
-       if (kdcconn->partial.length == 0) {
-               kdcconn->partial = data_blob_talloc(kdcconn, NULL, 4);
-               if (!kdcconn->partial.data) goto nomem;
+       talloc_steal(tmp_ctx, blob.data);
 
-               kdcconn->partial_read = 0;
+       src_addr = socket_get_peer_addr(kdcconn->conn->socket, tmp_ctx);
+       if (!src_addr) {
+               talloc_free(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       /* read in the packet length */
-       if (kdcconn->partial_read < 4) {
-               uint32_t packet_length;
-
-               status = socket_recv(conn->socket, 
-                                    kdcconn->partial.data + kdcconn->partial_read,
-                                    4 - kdcconn->partial_read,
-                                    &nread, 0);
-               if (NT_STATUS_IS_ERR(status)) goto failed;
-               if (!NT_STATUS_IS_OK(status)) return;
-
-               kdcconn->partial_read += nread;
-               if (kdcconn->partial_read != 4) return;
-
-               packet_length = RIVAL(kdcconn->partial.data, 0) + 4;
-
-               kdcconn->partial.data = talloc_realloc(kdcconn, kdcconn->partial.data, 
-                                                      uint8_t, packet_length);
-               if (!kdcconn->partial.data) goto nomem;
-
-               kdcconn->partial.length = packet_length;
+       my_addr = socket_get_my_addr(kdcconn->conn->socket, tmp_ctx);
+       if (!my_addr) {
+               talloc_free(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       /* read in the body */
-       status = socket_recv(conn->socket, 
-                            kdcconn->partial.data + kdcconn->partial_read,
-                            kdcconn->partial.length - kdcconn->partial_read,
-                            &nread, 0);
-       if (NT_STATUS_IS_ERR(status)) goto failed;
-       if (!NT_STATUS_IS_OK(status)) return;
-
-       kdcconn->partial_read += nread;
-       if (kdcconn->partial_read != kdcconn->partial.length) return;
-
-       /*
-        * we have parsed the request, so we can reset the kdcconn->partial_read,
-        * maybe we could also free kdcconn->partial, but for now we keep it,
-        * and overwrite it the next time
-        */
-       kdcconn->partial_read = 0;
-
-       src_addr = socket_get_peer_addr(kdcconn->conn->socket, tmp_ctx);
-       if (!src_addr) goto nomem;
-       src_port = socket_get_peer_port(kdcconn->conn->socket);
-
-       DEBUG(2,("Received krb5 TCP packet of length %u from %s:%u\n", 
-                kdcconn->partial.length - 4, src_addr, src_port));
-
-       /* TODO:  This really should be in a utility function somewhere */
-       ZERO_STRUCT(src_sock_addr);
-#ifdef HAVE_SOCK_SIN_LEN
-       src_sock_addr.sin_len           = sizeof(src_sock_addr);
-#endif
-       addr                            = interpret_addr2(src_addr);
-       src_sock_addr.sin_addr.s_addr   = addr.addr;
-       src_sock_addr.sin_port          = htons(src_port);
-       src_sock_addr.sin_family        = PF_INET;
-
        /* Call krb5 */
-       kdcconn->processing = True;
-       ret = krb5_kdc_process_krb5_request(kdcconn->kdc->smb_krb5_context->krb5_context, 
-                                           kdcconn->kdc->config,
-                                           kdcconn->partial.data + 4, kdcconn->partial.length - 4, 
-                                           &reply,
-                                           src_addr,
-                                           (struct sockaddr *)&src_sock_addr);
-       kdcconn->processing = False;
-       if (ret == -1) {
-               status = NT_STATUS_INTERNAL_ERROR;
-               goto failed;
+       input = data_blob_const(blob.data + 4, blob.length - 4); 
+
+       ret = kdcconn->process(kdcconn->kdc, 
+                              tmp_ctx,
+                              &input,
+                              &reply,
+                              src_addr,
+                              my_addr);
+       if (!ret) {
+               talloc_free(tmp_ctx);
+               return NT_STATUS_INTERNAL_ERROR;
        }
 
        /* and now encode the reply */
-       rep = talloc(kdcconn, struct data_blob_list_item);
-       if (!rep) {
-               krb5_data_free(&reply);
-               goto nomem;
-       }
-
-       rep->blob = data_blob_talloc(rep, NULL, reply.length + 4);
-       if (!rep->blob.data)  {
-               krb5_data_free(&reply);
-               goto nomem;
+       blob = data_blob_talloc(kdcconn, NULL, reply.length + 4);
+       if (!blob.data) {
+               talloc_free(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       RSIVAL(rep->blob.data, 0, reply.length);
-       memcpy(rep->blob.data + 4, reply.data, reply.length);   
-       krb5_data_free(&reply);
+       RSIVAL(blob.data, 0, reply.length);
+       memcpy(blob.data + 4, reply.data, reply.length);        
 
-       if (!kdcconn->send_queue) {
-               EVENT_FD_WRITEABLE(kdcconn->conn->event.fde);
+       status = packet_send(kdcconn->packet, blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(tmp_ctx);
+               return status;
        }
-       DLIST_ADD_END(kdcconn->send_queue, rep, struct data_blob_list_item *);
-
-       EVENT_FD_READABLE(kdcconn->conn->event.fde);
 
        /* the call isn't needed any more */
        talloc_free(tmp_ctx);
-       return;
-nomem:
-       status = NT_STATUS_NO_MEMORY;
-failed:
+       return NT_STATUS_OK;
+}
+
+/*
+  receive some data on a KDC connection
+*/
+static void kdc_tcp_recv_handler(struct stream_connection *conn, uint16_t flags)
+{
+       struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private, 
+                                                            struct kdc_tcp_connection);
+       packet_recv(kdcconn->packet);
+}
+
+/*
+  called on a tcp recv error
+*/
+static void kdc_tcp_recv_error(void *private, NTSTATUS status)
+{
+       struct kdc_tcp_connection *kdcconn = talloc_get_type(private, struct kdc_tcp_connection);
        kdc_tcp_terminate_connection(kdcconn, nt_errstr(status));
 }
 
@@ -372,36 +291,98 @@ failed:
 */
 static void kdc_tcp_send(struct stream_connection *conn, uint16_t flags)
 {
-       struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private, struct kdc_tcp_connection);
-       NTSTATUS status;
+       struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private, 
+                                                            struct kdc_tcp_connection);
+       packet_queue_run(kdcconn->packet);
+}
 
-       while (kdcconn->send_queue) {
-               struct data_blob_list_item *q = kdcconn->send_queue;
-               size_t sendlen;
+/**
+   Wrapper for krb5_kdc_process_krb5_request, converting to/from Samba
+   calling conventions
+*/
 
-               status = socket_send(conn->socket, &q->blob, &sendlen, 0);
-               if (NT_STATUS_IS_ERR(status)) goto failed;
-               if (!NT_STATUS_IS_OK(status)) return;
+static BOOL kdc_process(struct kdc_server *kdc,
+                       TALLOC_CTX *mem_ctx, 
+                       DATA_BLOB *input, 
+                       DATA_BLOB *reply,
+                       struct socket_address *peer_addr, 
+                       struct socket_address *my_addr)
+{
+       int ret;        
+       krb5_data k5_reply;
+
+       DEBUG(10,("Received KDC packet of length %lu from %s:%d\n", 
+                 (long)input->length - 4, peer_addr->addr, peer_addr->port));
+
+       ret = krb5_kdc_process_krb5_request(kdc->smb_krb5_context->krb5_context, 
+                                           kdc->config,
+                                           input->data, input->length,
+                                           &k5_reply,
+                                           peer_addr->addr,
+                                           peer_addr->sockaddr);
+       if (ret == -1) {
+               *reply = data_blob(NULL, 0);
+               return False;
+       }
+       *reply = data_blob_talloc(mem_ctx, k5_reply.data, k5_reply.length);
+       krb5_data_free(&k5_reply);
+       return True;
+}
 
-               q->blob.length -= sendlen;
-               q->blob.data   += sendlen;
+/*
+  called when we get a new connection
+*/
+static void kdc_tcp_generic_accept(struct stream_connection *conn, kdc_process_fn_t process_fn)
+{
+       struct kdc_server *kdc = talloc_get_type(conn->private, struct kdc_server);
+       struct kdc_tcp_connection *kdcconn;
 
-               if (q->blob.length == 0) {
-                       DLIST_REMOVE(kdcconn->send_queue, q);
-                       talloc_free(q);
-               }
+       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;
+       kdcconn->process = process_fn;
+       conn->private    = kdcconn;
+
+       kdcconn->packet = packet_init(kdcconn);
+       if (kdcconn->packet == NULL) {
+               kdc_tcp_terminate_connection(kdcconn, "kdc_tcp_accept: out of memory");
+               return;
        }
+       packet_set_private(kdcconn->packet, kdcconn);
+       packet_set_socket(kdcconn->packet, conn->socket);
+       packet_set_callback(kdcconn->packet, kdc_tcp_recv);
+       packet_set_full_request(kdcconn->packet, packet_full_request_u32);
+       packet_set_error_handler(kdcconn->packet, kdc_tcp_recv_error);
+       packet_set_event_context(kdcconn->packet, conn->event.ctx);
+       packet_set_fde(kdcconn->packet, conn->event.fde);
+       packet_set_serialise(kdcconn->packet);
+}
 
-       EVENT_FD_NOT_WRITEABLE(conn->event.fde);
-       return;
-failed:
-       kdc_tcp_terminate_connection(kdcconn, nt_errstr(status));
+static void kdc_tcp_accept(struct stream_connection *conn)
+{
+       kdc_tcp_generic_accept(conn, kdc_process);
 }
 
 static const struct stream_server_ops kdc_tcp_stream_ops = {
        .name                   = "kdc_tcp",
        .accept_connection      = kdc_tcp_accept,
-       .recv_handler           = kdc_tcp_recv,
+       .recv_handler           = kdc_tcp_recv_handler,
+       .send_handler           = kdc_tcp_send
+};
+
+static void kpasswdd_tcp_accept(struct stream_connection *conn)
+{
+       kdc_tcp_generic_accept(conn, kpasswdd_process);
+}
+
+static const struct stream_server_ops kpasswdd_tcp_stream_ops = {
+       .name                   = "kpasswdd_tcp",
+       .accept_connection      = kpasswdd_tcp_accept,
+       .recv_handler           = kdc_tcp_recv_handler,
        .send_handler           = kdc_tcp_send
 };
 
@@ -412,20 +393,33 @@ static NTSTATUS kdc_add_socket(struct kdc_server *kdc, const char *address)
 {
        const struct model_ops *model_ops;
        struct kdc_socket *kdc_socket;
+       struct kdc_socket *kpasswd_socket;
+       struct socket_address *kdc_address, *kpasswd_address;
        NTSTATUS status;
-       uint16_t port = lp_krb5_port();
+       uint16_t kdc_port = lp_krb5_port();
+       uint16_t kpasswd_port = lp_kpasswd_port();
 
        kdc_socket = talloc(kdc, struct kdc_socket);
        NT_STATUS_HAVE_NO_MEMORY(kdc_socket);
 
+       kpasswd_socket = talloc(kdc, struct kdc_socket);
+       NT_STATUS_HAVE_NO_MEMORY(kpasswd_socket);
+
        status = socket_create("ip", SOCKET_TYPE_DGRAM, &kdc_socket->sock, 0);
        if (!NT_STATUS_IS_OK(status)) {
                talloc_free(kdc_socket);
                return status;
        }
 
+       status = socket_create("ip", SOCKET_TYPE_DGRAM, &kpasswd_socket->sock, 0);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(kpasswd_socket);
+               return status;
+       }
+
        kdc_socket->kdc = kdc;
        kdc_socket->send_queue = NULL;
+       kdc_socket->process = kdc_process;
 
        talloc_steal(kdc_socket, kdc_socket->sock);
 
@@ -433,14 +427,40 @@ static NTSTATUS kdc_add_socket(struct kdc_server *kdc, const char *address)
                                       socket_get_fd(kdc_socket->sock), EVENT_FD_READ,
                                       kdc_socket_handler, kdc_socket);
 
-       status = socket_listen(kdc_socket->sock, address, port, 0, 0);
+       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);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0,("Failed to bind to %s:%d UDP - %s\n", 
-                        address, port, nt_errstr(status)));
+               DEBUG(0,("Failed to bind to %s:%d UDP for kdc - %s\n", 
+                        address, kdc_port, nt_errstr(status)));
                talloc_free(kdc_socket);
                return status;
        }
 
+       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);
+       
+       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);
+       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;
+       }
+
        /* 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. */
@@ -451,11 +471,22 @@ static NTSTATUS kdc_add_socket(struct kdc_server *kdc, const char *address)
                return NT_STATUS_INTERNAL_ERROR;
        }
 
-       status = stream_setup_socket(kdc->task->event_ctx, model_ops, &kdc_tcp_stream_ops, 
-                                    "ip", address, &port, kdc);
+       status = stream_setup_socket(kdc->task->event_ctx, model_ops, 
+                                    &kdc_tcp_stream_ops, 
+                                    "ip", address, &kdc_port, kdc);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
-                        address, port, nt_errstr(status)));
+                        address, kdc_port, nt_errstr(status)));
+               talloc_free(kdc_socket);
+               return status;
+       }
+
+       status = stream_setup_socket(kdc->task->event_ctx, model_ops, 
+                                    &kpasswdd_tcp_stream_ops, 
+                                    "ip", address, &kpasswd_port, kdc);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
+                        address, kpasswd_port, nt_errstr(status)));
                talloc_free(kdc_socket);
                return status;
        }
@@ -472,20 +503,13 @@ static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc)
        int num_interfaces = iface_count();
        TALLOC_CTX *tmp_ctx = talloc_new(kdc);
        NTSTATUS status;
-
-       /* if we are allowing incoming packets from any address, then
-          we need to bind to the wildcard address */
-       if (!lp_bind_interfaces_only()) {
-               status = kdc_add_socket(kdc, "0.0.0.0");
+       
+       int i;
+       
+       for (i=0; i<num_interfaces; i++) {
+               const char *address = talloc_strdup(tmp_ctx, iface_n_ip(i));
+               status = kdc_add_socket(kdc, address);
                NT_STATUS_NOT_OK_RETURN(status);
-       } else {
-               int i;
-
-               for (i=0; i<num_interfaces; i++) {
-                       const char *address = talloc_strdup(tmp_ctx, iface_n_ip(i));
-                       status = kdc_add_socket(kdc, address);
-                       NT_STATUS_NOT_OK_RETURN(status);
-               }
        }
 
        talloc_free(tmp_ctx);
@@ -502,11 +526,26 @@ static void kdc_task_init(struct task_server *task)
        NTSTATUS status;
        krb5_error_code ret;
 
+       switch (lp_server_role()) {
+       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;
+       case ROLE_DOMAIN_PDC:
+       case ROLE_DOMAIN_BDC:
+               /* Yes, we want a KDC */
+               break;
+       }
+
        if (iface_count() == 0) {
                task_server_terminate(task, "kdc: no network interfaces configured");
                return;
        }
 
+       task_server_set_title(task, "task[kdc]");
+
        kdc = talloc(task, struct kdc_server);
        if (kdc == NULL) {
                task_server_terminate(task, "kdc: out of memory");
@@ -523,9 +562,6 @@ static void kdc_task_init(struct task_server *task)
        }
        krb5_kdc_default_config(kdc->config);
 
-       /* NAT and the like make this pointless, and painful */
-       kdc->config->check_ticket_addresses = FALSE;
-
        initialize_krb5_error_table();
 
        ret = smb_krb5_init_context(kdc, &kdc->smb_krb5_context);
@@ -546,15 +582,18 @@ static void kdc_task_init(struct task_server *task)
        }
        kdc->config->num_db = 1;
                
-       ret = hdb_ldb_create(kdc, kdc->smb_krb5_context->krb5_context, 
-                            &kdc->config->db[0], lp_sam_url());
-       if (ret != 0) {
-               DEBUG(1, ("kdc_task_init: hdb_ldb_create fails: %s\n", 
-                         smb_get_krb5_error_message(kdc->smb_krb5_context->krb5_context, ret, kdc))); 
-               task_server_terminate(task, "kdc: hdb_ldb_create failed");
+       status = kdc_hdb_ldb_create(kdc, kdc->smb_krb5_context->krb5_context, 
+                                   &kdc->config->db[0], NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               task_server_terminate(task, "kdc: hdb_ldb_create (setup KDC database) failed");
                return; 
        }
 
+       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;
+       }
        /* start listening on the configured network interfaces */
        status = kdc_startup_interfaces(kdc);
        if (!NT_STATUS_IS_OK(status)) {