s4-kdc: Move KDC packet handling functions to kdc-server.c
authorAndreas Schneider <asn@samba.org>
Fri, 10 Jun 2016 09:29:20 +0000 (11:29 +0200)
committerJeremy Allison <jra@samba.org>
Sun, 19 Jun 2016 01:31:32 +0000 (03:31 +0200)
Create an Kerberos implmentation independent KDC-SERVER subsystem so we
can use it to implement a kpasswd server with MIT Kerberos in future.

Signed-off-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Sun Jun 19 03:31:32 CEST 2016 on sn-devel-144

source4/kdc/kdc-heimdal.c
source4/kdc/kdc-server.c [new file with mode: 0644]
source4/kdc/kdc-server.h
source4/kdc/wscript_build

index 493b30b842539b36ae8cd9b7f3e1a6fb6f948bf3..be4507386bc568d1131ffa4381f07b931a9160fc 100644 (file)
 #include "includes.h"
 #include "smbd/process_model.h"
 #include "lib/tsocket/tsocket.h"
-#include "libcli/util/tstream.h"
 #include "lib/messaging/irpc.h"
 #include "librpc/gen_ndr/ndr_irpc.h"
 #include "librpc/gen_ndr/ndr_krb5pac.h"
-#include "lib/stream/packet.h"
 #include "lib/socket/netif.h"
 #include "param/param.h"
 #include "kdc/kdc-server.h"
@@ -45,78 +43,6 @@ NTSTATUS server_service_kdc_init(void);
 
 extern struct krb5plugin_windc_ftable windc_plugin_table;
 
-static NTSTATUS kdc_proxy_unavailable_error(struct kdc_server *kdc,
-                                           TALLOC_CTX *mem_ctx,
-                                           DATA_BLOB *out)
-{
-       int kret;
-       krb5_data k5_error_blob;
-
-       kret = smb_krb5_mk_error(kdc->smb_krb5_context->krb5_context,
-                                KRB5KDC_ERR_SVC_UNAVAILABLE,
-                                NULL,
-                                NULL,
-                                &k5_error_blob);
-       if (kret != 0) {
-               DEBUG(2,(__location__ ": Unable to form krb5 error reply\n"));
-               return NT_STATUS_INTERNAL_ERROR;
-       }
-
-       *out = data_blob_talloc(mem_ctx, k5_error_blob.data, k5_error_blob.length);
-       kerberos_free_data_contents(kdc->smb_krb5_context->krb5_context,
-                                   &k5_error_blob);
-       if (!out->data) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       return NT_STATUS_OK;
-}
-
-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];
-};
-
-/*
-  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_socket *kdc_socket;
-
-       struct tstream_context *tstream;
-
-       struct tevent_queue *send_queue;
-};
-
-
-static void kdc_tcp_terminate_connection(struct kdc_tcp_connection *kdcconn, const char *reason)
-{
-       stream_terminate_connection(kdcconn->conn, reason);
-}
-
-static void kdc_tcp_recv(struct stream_connection *conn, uint16_t flags)
-{
-       struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private_data,
-                                                            struct kdc_tcp_connection);
-       /* this should never be triggered! */
-       kdc_tcp_terminate_connection(kdcconn, "kdc_tcp_recv: called");
-}
-
-static void kdc_tcp_send(struct stream_connection *conn, uint16_t flags)
-{
-       struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private_data,
-                                                            struct kdc_tcp_connection);
-       /* this should never be triggered! */
-       kdc_tcp_terminate_connection(kdcconn, "kdc_tcp_send: called");
-}
-
 /**
    Wrapper for krb5_kdc_process_krb5_request, converting to/from Samba
    calling conventions
@@ -180,519 +106,6 @@ static kdc_code kdc_process(struct kdc_server *kdc,
        return KDC_OK;
 }
 
-static void kdc_tcp_call_proxy_done(struct tevent_req *subreq);
-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;
-       kdc_code ret;
-
-       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 */
-       ret = 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 (ret == KDC_ERROR) {
-               kdc_tcp_terminate_connection(kdc_conn,
-                               "kdc_tcp_call_loop: process function failed");
-               return;
-       }
-
-       if (ret == KDC_PROXY_REQUEST) {
-               uint16_t port;
-
-               if (!kdc_conn->kdc_socket->kdc->am_rodc) {
-                       kdc_tcp_terminate_connection(kdc_conn,
-                                                    "kdc_tcp_call_loop: proxying requested when not RODC");
-                       return;
-               }
-               port = tsocket_address_inet_port(kdc_conn->conn->local_address);
-
-               subreq = kdc_tcp_proxy_send(call,
-                                           kdc_conn->conn->event.ctx,
-                                           kdc_conn->kdc_socket->kdc,
-                                           port,
-                                           call->in);
-               if (subreq == NULL) {
-                       kdc_tcp_terminate_connection(kdc_conn,
-                               "kdc_tcp_call_loop: kdc_tcp_proxy_send failed");
-                       return;
-               }
-               tevent_req_set_callback(subreq, kdc_tcp_call_proxy_done, call);
-               return;
-       }
-
-       /* First add the length of the out buffer */
-       RSIVAL(call->out_hdr, 0, call->out.length);
-       call->out_iov[0].iov_base = (char *) call->out_hdr;
-       call->out_iov[0].iov_len = 4;
-
-       call->out_iov[1].iov_base = (char *) call->out.data;
-       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_proxy_done(struct tevent_req *subreq)
-{
-       struct kdc_tcp_call *call = tevent_req_callback_data(subreq,
-                       struct kdc_tcp_call);
-       struct kdc_tcp_connection *kdc_conn = call->kdc_conn;
-       NTSTATUS status;
-
-       status = kdc_tcp_proxy_recv(subreq, call, &call->out);
-       TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               /* generate an error packet */
-               status = kdc_proxy_unavailable_error(kdc_conn->kdc_socket->kdc,
-                                                    call, &call->out);
-       }
-
-       if (!NT_STATUS_IS_OK(status)) {
-               const char *reason;
-
-               reason = talloc_asprintf(call, "kdc_tcp_call_proxy_done: "
-                                        "kdc_proxy_unavailable_error - %s",
-                                        nt_errstr(status));
-               if (!reason) {
-                       reason = "kdc_tcp_call_proxy_done: kdc_proxy_unavailable_error() failed";
-               }
-
-               kdc_tcp_terminate_connection(call->kdc_conn, reason);
-               return;
-       }
-
-       /* First add the length of the out buffer */
-       RSIVAL(call->out_hdr, 0, call->out.length);
-       call->out_iov[0].iov_base = (char *) call->out_hdr;
-       call->out_iov[0].iov_len = 4;
-
-       call->out_iov[1].iov_base = (char *) call->out.data;
-       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);
-}
-
-/*
-  called when we get a new connection
-*/
-static void kdc_tcp_accept(struct stream_connection *conn)
-{
-       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);
-
-       rc = tstream_bsd_existing_socket(kdc_conn,
-                       socket_get_fd(conn->socket),
-                       &kdc_conn->tstream);
-       if (rc < 0) {
-               stream_terminate_connection(conn,
-                               "kdc_tcp_accept: out of memory");
-               return;
-       }
-
-       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");
-               return;
-       }
-       tevent_req_set_callback(subreq, kdc_tcp_call_loop, kdc_conn);
-}
-
-static const struct stream_server_ops kdc_tcp_stream_ops = {
-       .name                   = "kdc_tcp",
-       .accept_connection      = kdc_tcp_accept,
-       .recv_handler           = kdc_tcp_recv,
-       .send_handler           = kdc_tcp_send
-};
-
-struct kdc_udp_call {
-       struct kdc_udp_socket *sock;
-       struct tsocket_address *src;
-       DATA_BLOB in;
-       DATA_BLOB out;
-};
-
-static void kdc_udp_call_proxy_done(struct tevent_req *subreq);
-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;
-       kdc_code ret;
-
-       call = talloc(sock, struct kdc_udp_call);
-       if (call == NULL) {
-               talloc_free(call);
-               goto done;
-       }
-       call->sock = sock;
-
-       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 */
-       ret = sock->kdc_socket->process(sock->kdc_socket->kdc,
-                                      call,
-                                      &call->in,
-                                      &call->out,
-                                      call->src,
-                                      sock->kdc_socket->local_address,
-                                      1 /* Datagram */);
-       if (ret == KDC_ERROR) {
-               talloc_free(call);
-               goto done;
-       }
-
-       if (ret == KDC_PROXY_REQUEST) {
-               uint16_t port;
-
-               if (!sock->kdc_socket->kdc->am_rodc) {
-                       DEBUG(0,("kdc_udp_call_loop: proxying requested when not RODC"));
-                       talloc_free(call);
-                       goto done;
-               }
-
-               port = tsocket_address_inet_port(sock->kdc_socket->local_address);
-
-               subreq = kdc_udp_proxy_send(call,
-                                           sock->kdc_socket->kdc->task->event_ctx,
-                                           sock->kdc_socket->kdc,
-                                           port,
-                                           call->in);
-               if (subreq == NULL) {
-                       talloc_free(call);
-                       goto done;
-               }
-               tevent_req_set_callback(subreq, kdc_udp_call_proxy_done, 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_proxy_done(struct tevent_req *subreq)
-{
-       struct kdc_udp_call *call =
-               tevent_req_callback_data(subreq,
-               struct kdc_udp_call);
-       NTSTATUS status;
-
-       status = kdc_udp_proxy_recv(subreq, call, &call->out);
-       TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               /* generate an error packet */
-               status = kdc_proxy_unavailable_error(call->sock->kdc_socket->kdc,
-                                                    call, &call->out);
-       }
-
-       if (!NT_STATUS_IS_OK(status)) {
-               talloc_free(call);
-               return;
-       }
-
-       subreq = tdgram_sendto_queue_send(call,
-                                         call->sock->kdc_socket->kdc->task->event_ctx,
-                                         call->sock->dgram,
-                                         call->sock->send_queue,
-                                         call->out.data,
-                                         call->out.length,
-                                         call->src);
-       if (subreq == NULL) {
-               talloc_free(call);
-               return;
-       }
-
-       tevent_req_set_callback(subreq, kdc_udp_call_sendto_done, call);
-}
-
-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);
-       int sys_errno;
-
-       tdgram_sendto_queue_recv(subreq, &sys_errno);
-
-       /* We don't care about errors */
-
-       talloc_free(call);
-}
-
-/*
-  start listening on the given address
-*/
-static NTSTATUS kdc_add_socket(struct kdc_server *kdc,
-                              const struct model_ops *model_ops,
-                              const char *name,
-                              const char *address,
-                              uint16_t port,
-                              kdc_process_fn_t process,
-                              bool udp_only)
-{
-       struct kdc_socket *kdc_socket;
-       struct kdc_udp_socket *kdc_udp_socket;
-       struct tevent_req *udpsubreq;
-       NTSTATUS status;
-       int ret;
-
-       kdc_socket = talloc(kdc, struct kdc_socket);
-       NT_STATUS_HAVE_NO_MEMORY(kdc_socket);
-
-       kdc_socket->kdc = kdc;
-       kdc_socket->process = process;
-
-       ret = tsocket_address_inet_from_strings(kdc_socket, "ip",
-                                               address, port,
-                                               &kdc_socket->local_address);
-       if (ret != 0) {
-               status = map_nt_error_from_unix_common(errno);
-               return status;
-       }
-
-       if (!udp_only) {
-               status = stream_setup_socket(kdc->task,
-                                            kdc->task->event_ctx,
-                                            kdc->task->lp_ctx,
-                                            model_ops,
-                                            &kdc_tcp_stream_ops,
-                                            "ip", address, &port,
-                                            lpcfg_socket_options(kdc->task->lp_ctx),
-                                            kdc_socket);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
-                                address, port, nt_errstr(status)));
-                       talloc_free(kdc_socket);
-                       return status;
-               }
-       }
-
-       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_common(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);
-
-       return NT_STATUS_OK;
-}
-
-
 /*
   setup our listening sockets on the configured network interfaces
 */
diff --git a/source4/kdc/kdc-server.c b/source4/kdc/kdc-server.c
new file mode 100644 (file)
index 0000000..5b8463b
--- /dev/null
@@ -0,0 +1,615 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   KDC related functions
+
+   Copyright (c) 2005-2008 Andrew Bartlett <abartlet@samba.org>
+   Copyright (c) 2005      Andrew Tridgell <tridge@samba.org>
+   Copyright (c) 2005      Stefan Metzmacher <metze@samba.org>
+
+   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
+   the Free Software Foundation; either version 3 of the License, or
+   (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
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "param/param.h"
+#include "smbd/process_model.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+#include "kdc/kdc-server.h"
+#include "kdc/kdc-proxy.h"
+#include "lib/stream/packet.h"
+
+/*
+ * 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_socket *kdc_socket;
+
+       struct tstream_context *tstream;
+
+       struct tevent_queue *send_queue;
+};
+
+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];
+};
+
+struct kdc_udp_call {
+       struct kdc_udp_socket *sock;
+       struct tsocket_address *src;
+       DATA_BLOB in;
+       DATA_BLOB out;
+};
+
+static void kdc_udp_call_proxy_done(struct tevent_req *subreq);
+static void kdc_udp_call_sendto_done(struct tevent_req *subreq);
+
+static void kdc_tcp_call_writev_done(struct tevent_req *subreq);
+static void kdc_tcp_call_proxy_done(struct tevent_req *subreq);
+
+static void kdc_tcp_terminate_connection(struct kdc_tcp_connection *kdc_conn,
+                                        const char *reason)
+{
+       stream_terminate_connection(kdc_conn->conn, reason);
+}
+
+static NTSTATUS kdc_proxy_unavailable_error(struct kdc_server *kdc,
+                                           TALLOC_CTX *mem_ctx,
+                                           DATA_BLOB *out)
+{
+       krb5_error_code code;
+       krb5_data enc_error;
+
+       code = smb_krb5_mk_error(kdc->smb_krb5_context->krb5_context,
+                                KRB5KDC_ERR_SVC_UNAVAILABLE,
+                                NULL,
+                                NULL,
+                                &enc_error);
+       if (code != 0) {
+               DBG_WARNING("Unable to form krb5 error reply\n");
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       *out = data_blob_talloc(mem_ctx, enc_error.data, enc_error.length);
+       kerberos_free_data_contents(kdc->smb_krb5_context->krb5_context,
+                                   &enc_error);
+       if (!out->data) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       return NT_STATUS_OK;
+}
+
+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;
+       kdc_code ret;
+
+       call = talloc(sock, struct kdc_udp_call);
+       if (call == NULL) {
+               talloc_free(call);
+               goto done;
+       }
+       call->sock = sock;
+
+       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 */
+       ret = sock->kdc_socket->process(sock->kdc_socket->kdc,
+                                      call,
+                                      &call->in,
+                                      &call->out,
+                                      call->src,
+                                      sock->kdc_socket->local_address,
+                                      1 /* Datagram */);
+       if (ret == KDC_ERROR) {
+               talloc_free(call);
+               goto done;
+       }
+
+       if (ret == KDC_PROXY_REQUEST) {
+               uint16_t port;
+
+               if (!sock->kdc_socket->kdc->am_rodc) {
+                       DEBUG(0,("kdc_udp_call_loop: proxying requested when not RODC"));
+                       talloc_free(call);
+                       goto done;
+               }
+
+               port = tsocket_address_inet_port(sock->kdc_socket->local_address);
+
+               subreq = kdc_udp_proxy_send(call,
+                                           sock->kdc_socket->kdc->task->event_ctx,
+                                           sock->kdc_socket->kdc,
+                                           port,
+                                           call->in);
+               if (subreq == NULL) {
+                       talloc_free(call);
+                       goto done;
+               }
+               tevent_req_set_callback(subreq, kdc_udp_call_proxy_done, 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_proxy_done(struct tevent_req *subreq)
+{
+       struct kdc_udp_call *call =
+               tevent_req_callback_data(subreq,
+               struct kdc_udp_call);
+       NTSTATUS status;
+
+       status = kdc_udp_proxy_recv(subreq, call, &call->out);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               /* generate an error packet */
+               status = kdc_proxy_unavailable_error(call->sock->kdc_socket->kdc,
+                                                    call, &call->out);
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(call);
+               return;
+       }
+
+       subreq = tdgram_sendto_queue_send(call,
+                                         call->sock->kdc_socket->kdc->task->event_ctx,
+                                         call->sock->dgram,
+                                         call->sock->send_queue,
+                                         call->out.data,
+                                         call->out.length,
+                                         call->src);
+       if (subreq == NULL) {
+               talloc_free(call);
+               return;
+       }
+
+       tevent_req_set_callback(subreq, kdc_udp_call_sendto_done, call);
+}
+
+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);
+       int sys_errno;
+
+       tdgram_sendto_queue_recv(subreq, &sys_errno);
+
+       /* We don't care about errors */
+
+       talloc_free(call);
+}
+
+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;
+       kdc_code ret;
+
+       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 */
+       ret = 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 (ret == KDC_ERROR) {
+               kdc_tcp_terminate_connection(kdc_conn,
+                               "kdc_tcp_call_loop: process function failed");
+               return;
+       }
+
+       if (ret == KDC_PROXY_REQUEST) {
+               uint16_t port;
+
+               if (!kdc_conn->kdc_socket->kdc->am_rodc) {
+                       kdc_tcp_terminate_connection(kdc_conn,
+                                                    "kdc_tcp_call_loop: proxying requested when not RODC");
+                       return;
+               }
+               port = tsocket_address_inet_port(kdc_conn->conn->local_address);
+
+               subreq = kdc_tcp_proxy_send(call,
+                                           kdc_conn->conn->event.ctx,
+                                           kdc_conn->kdc_socket->kdc,
+                                           port,
+                                           call->in);
+               if (subreq == NULL) {
+                       kdc_tcp_terminate_connection(kdc_conn,
+                               "kdc_tcp_call_loop: kdc_tcp_proxy_send failed");
+                       return;
+               }
+               tevent_req_set_callback(subreq, kdc_tcp_call_proxy_done, call);
+               return;
+       }
+
+       /* First add the length of the out buffer */
+       RSIVAL(call->out_hdr, 0, call->out.length);
+       call->out_iov[0].iov_base = (char *) call->out_hdr;
+       call->out_iov[0].iov_len = 4;
+
+       call->out_iov[1].iov_base = (char *) call->out.data;
+       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_proxy_done(struct tevent_req *subreq)
+{
+       struct kdc_tcp_call *call = tevent_req_callback_data(subreq,
+                       struct kdc_tcp_call);
+       struct kdc_tcp_connection *kdc_conn = call->kdc_conn;
+       NTSTATUS status;
+
+       status = kdc_tcp_proxy_recv(subreq, call, &call->out);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               /* generate an error packet */
+               status = kdc_proxy_unavailable_error(kdc_conn->kdc_socket->kdc,
+                                                    call, &call->out);
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               const char *reason;
+
+               reason = talloc_asprintf(call, "kdc_tcp_call_proxy_done: "
+                                        "kdc_proxy_unavailable_error - %s",
+                                        nt_errstr(status));
+               if (!reason) {
+                       reason = "kdc_tcp_call_proxy_done: kdc_proxy_unavailable_error() failed";
+               }
+
+               kdc_tcp_terminate_connection(call->kdc_conn, reason);
+               return;
+       }
+
+       /* First add the length of the out buffer */
+       RSIVAL(call->out_hdr, 0, call->out.length);
+       call->out_iov[0].iov_base = (char *) call->out_hdr;
+       call->out_iov[0].iov_len = 4;
+
+       call->out_iov[1].iov_base = (char *) call->out.data;
+       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);
+}
+
+/*
+  called when we get a new connection
+*/
+static void kdc_tcp_accept(struct stream_connection *conn)
+{
+       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);
+
+       rc = tstream_bsd_existing_socket(kdc_conn,
+                       socket_get_fd(conn->socket),
+                       &kdc_conn->tstream);
+       if (rc < 0) {
+               stream_terminate_connection(conn,
+                               "kdc_tcp_accept: out of memory");
+               return;
+       }
+
+       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");
+               return;
+       }
+       tevent_req_set_callback(subreq, kdc_tcp_call_loop, kdc_conn);
+}
+
+static void kdc_tcp_recv(struct stream_connection *conn, uint16_t flags)
+{
+       struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private_data,
+                                                            struct kdc_tcp_connection);
+       /* this should never be triggered! */
+       kdc_tcp_terminate_connection(kdcconn, "kdc_tcp_recv: called");
+}
+
+static void kdc_tcp_send(struct stream_connection *conn, uint16_t flags)
+{
+       struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private_data,
+                                                            struct kdc_tcp_connection);
+       /* this should never be triggered! */
+       kdc_tcp_terminate_connection(kdcconn, "kdc_tcp_send: called");
+}
+
+static const struct stream_server_ops kdc_tcp_stream_ops = {
+       .name                   = "kdc_tcp",
+       .accept_connection      = kdc_tcp_accept,
+       .recv_handler           = kdc_tcp_recv,
+       .send_handler           = kdc_tcp_send
+};
+
+/*
+ * Start listening on the given address
+ */
+NTSTATUS kdc_add_socket(struct kdc_server *kdc,
+                       const struct model_ops *model_ops,
+                       const char *name,
+                       const char *address,
+                       uint16_t port,
+                       kdc_process_fn_t process,
+                       bool udp_only)
+{
+       struct kdc_socket *kdc_socket;
+       struct kdc_udp_socket *kdc_udp_socket;
+       struct tevent_req *udpsubreq;
+       NTSTATUS status;
+       int ret;
+
+       kdc_socket = talloc(kdc, struct kdc_socket);
+       NT_STATUS_HAVE_NO_MEMORY(kdc_socket);
+
+       kdc_socket->kdc = kdc;
+       kdc_socket->process = process;
+
+       ret = tsocket_address_inet_from_strings(kdc_socket, "ip",
+                                               address, port,
+                                               &kdc_socket->local_address);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               return status;
+       }
+
+       if (!udp_only) {
+               status = stream_setup_socket(kdc->task,
+                                            kdc->task->event_ctx,
+                                            kdc->task->lp_ctx,
+                                            model_ops,
+                                            &kdc_tcp_stream_ops,
+                                            "ip", address, &port,
+                                            lpcfg_socket_options(kdc->task->lp_ctx),
+                                            kdc_socket);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
+                                address, port, nt_errstr(status)));
+                       talloc_free(kdc_socket);
+                       return status;
+               }
+       }
+
+       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_common(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);
+
+       return NT_STATUS_OK;
+}
index 284a4ec8b40a47edb803eda49508b5e52662fc18..47e6c686fec462b79608ec54803110f76c9ca238 100644 (file)
 #define _KDC_SERVER_H
 
 #include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
 
 struct tsocket_address;
+struct model_ops;
 
 /*
  * Context structure for the kdc server
@@ -69,4 +71,12 @@ struct kdc_udp_socket {
        struct tevent_queue *send_queue;
 };
 
+NTSTATUS kdc_add_socket(struct kdc_server *kdc,
+                       const struct model_ops *model_ops,
+                       const char *name,
+                       const char *address,
+                       uint16_t port,
+                       kdc_process_fn_t process,
+                       bool udp_only);
+
 #endif /* _KDC_SERVER_H */
index c278efd25d12751335d6c34b3f2f7f0edeff97fb..230118cdd600bbac78c26c52c3e1f6372d0ee6bd 100755 (executable)
@@ -7,7 +7,7 @@ else:
     kdc_include = getattr(bld.env, "CPPPATH_KDC")
 
 bld.SAMBA_MODULE('service_kdc',
-                 source='kdc-heimdal.c kpasswd-heimdal.c kdc-proxy.c',
+                 source='kdc-heimdal.c kpasswd-heimdal.c',
                  subsystem='service',
                  init_function='server_service_kdc_init',
                  deps='''
@@ -15,12 +15,11 @@ bld.SAMBA_MODULE('service_kdc',
                       HDB_SAMBA4
                       WDC_SAMBA4
                       samba-hostconfig
-                      LIBTSOCKET
-                      LIBSAMBA_TSOCKET
                       com_err
                       samba_server_gensec
                       PAC_GLUE
                       KDC-GLUE
+                      KDC-SERVER
                       KPASSWD_GLUE
                  ''',
                  internal_module=False)
@@ -44,6 +43,17 @@ bld.SAMBA_LIBRARY('HDB_SAMBA4_PLUGIN',
                   enabled = (bld.CONFIG_SET("USING_SYSTEM_KRB5") and bld.CONFIG_SET("USING_SYSTEM_HDB"))
                   )
 
+bld.SAMBA_SUBSYSTEM('KDC-SERVER',
+                    source='kdc-server.c kdc-proxy.c',
+                    includes=kdc_include,
+                    deps='''
+                         krb5samba
+                         ldb
+                         LIBTSOCKET
+                         LIBSAMBA_TSOCKET
+                    ''',
+                    enabled=bld.CONFIG_SET('SAMBA4_USES_HEIMDAL'))
+
 bld.SAMBA_SUBSYSTEM('KDC-GLUE',
        source='kdc-glue.c',
         includes=kdc_include,