s4:kdc: split the kdc_udp_proxy() logic from the main kdc logic
authorStefan Metzmacher <metze@samba.org>
Wed, 9 Feb 2011 08:07:47 +0000 (09:07 +0100)
committerStefan Metzmacher <metze@samba.org>
Fri, 4 Mar 2011 20:19:05 +0000 (21:19 +0100)
By having kdc_udp_proxy_send/recv(), which just asks any writeable
dc for a reponse blob, we simplify the interaction between
client-local and local-writeable sockets.

This allows us to make kdc_udp_call and kdc_udp_socket private to
kdc.c again.

metze

source4/kdc/kdc-glue.h
source4/kdc/kdc.c
source4/kdc/proxy.c

index 75b6b988fe0e81edebbf9562c7f00d1b63a64e48..f9489b1fd16dcef635bedfbaa1d293f8ca78b1fe 100644 (file)
@@ -50,19 +50,6 @@ enum kdc_process_ret {
        KDC_PROCESS_FAILED,
        KDC_PROCESS_PROXY};
 
-struct kdc_udp_call {
-       struct tsocket_address *src;
-       DATA_BLOB in;
-       DATA_BLOB out;
-};
-
-/* hold information about one kdc/kpasswd udp socket */
-struct kdc_udp_socket {
-       struct kdc_socket *kdc_socket;
-       struct tdgram_context *dgram;
-       struct tevent_queue *send_queue;
-};
-
 struct kdc_tcp_call {
        struct kdc_tcp_connection *kdc_conn;
        DATA_BLOB in;
@@ -116,8 +103,14 @@ NTSTATUS hdb_samba4_create_kdc(struct samba_kdc_base_context *base_ctx,
                               krb5_context context, struct HDB **db);
 
 /* from proxy.c */
-void kdc_udp_proxy(struct kdc_server *kdc, struct kdc_udp_socket *sock,
-                  struct kdc_udp_call *call, uint16_t port);
+struct tevent_req *kdc_udp_proxy_send(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct kdc_server *kdc,
+                                     uint16_t port,
+                                     DATA_BLOB in);
+NTSTATUS kdc_udp_proxy_recv(struct tevent_req *req,
+                           TALLOC_CTX *mem_ctx,
+                           DATA_BLOB *out);
 
 void kdc_tcp_proxy(struct kdc_server *kdc, struct kdc_tcp_connection *kdc_conn,
                   struct kdc_tcp_call *call, uint16_t port);
index 249004323cfabe0332f3f47458a1df6a0704886b..05c1d9c40d527641ec2fdc8a5f2c8f3189a7513c 100644 (file)
@@ -345,6 +345,21 @@ static const struct stream_server_ops kdc_tcp_stream_ops = {
        .send_handler           = kdc_tcp_send
 };
 
+/* hold information about one kdc/kpasswd udp socket */
+struct kdc_udp_socket {
+       struct kdc_socket *kdc_socket;
+       struct tdgram_context *dgram;
+       struct tevent_queue *send_queue;
+};
+
+struct kdc_udp_call {
+       struct 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)
@@ -362,6 +377,7 @@ static void kdc_udp_call_loop(struct tevent_req *subreq)
                talloc_free(call);
                goto done;
        }
+       call->sock = sock;
 
        len = tdgram_recvfrom_recv(subreq, &sys_errno,
                                   call, &buf, &call->src);
@@ -392,13 +408,26 @@ static void kdc_udp_call_loop(struct tevent_req *subreq)
        }
 
        if (ret == KDC_PROCESS_PROXY) {
+               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;
                }
-               kdc_udp_proxy(sock->kdc_socket->kdc, sock, call,
-                             tsocket_address_inet_port(sock->kdc_socket->local_address));
+
+               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;
        }
 
@@ -428,6 +457,41 @@ done:
        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,
index 6179bf18ab18c961543c8d3ddc839348c50e592d..a11f253b26bf0c9c115d1dccaec3ac9b5ee534a5 100644 (file)
@@ -5,6 +5,7 @@
 
    Copyright (C) Andrew Tridgell       2010
    Copyright (C) Andrew Bartlett        2010
+   Copyright (C) Stefan Metzmacher      2011
 
    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
@@ -24,6 +25,7 @@
 #include "smbd/process_model.h"
 #include "lib/tsocket/tsocket.h"
 #include "libcli/util/tstream.h"
+#include "lib/util/tevent_ntstatus.h"
 #include "lib/stream/packet.h"
 #include "kdc/kdc-glue.h"
 #include "dsdb/samdb/samdb.h"
@@ -75,287 +77,252 @@ static WERROR kdc_proxy_get_writeable_dcs(struct kdc_server *kdc, TALLOC_CTX *me
 
 
 struct kdc_udp_proxy_state {
-       struct kdc_udp_call *call;
-       struct kdc_udp_socket *sock;
+       struct tevent_context *ev;
        struct kdc_server *kdc;
+       uint16_t port;
+       DATA_BLOB in;
+       DATA_BLOB out;
        char **proxy_list;
        uint32_t next_proxy;
-       const char *proxy_ip;
-       uint16_t port;
+       struct {
+               struct nbt_name name;
+               const char *ip;
+               struct tdgram_context *dgram;
+       } proxy;
 };
 
 
-static void kdc_udp_next_proxy(struct kdc_udp_proxy_state *state);
+static void kdc_udp_next_proxy(struct tevent_req *req);
 
-/*
-  called when the send of the call to the proxy is complete
-  this is used to get an errors from the sendto()
- */
-static void kdc_udp_proxy_sendto_done(struct tevent_req *req)
+struct tevent_req *kdc_udp_proxy_send(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct kdc_server *kdc,
+                                     uint16_t port,
+                                     DATA_BLOB in)
 {
-       struct kdc_udp_proxy_state *state = tevent_req_callback_data(req,
-                                                                    struct kdc_udp_proxy_state);
-       ssize_t ret;
-       int sys_errno;
-
-       ret = tdgram_sendto_queue_recv(req, &sys_errno);
-       talloc_free(req);
+       struct tevent_req *req;
+       struct kdc_udp_proxy_state *state;
+       WERROR werr;
 
-       if (ret == -1) {
-               DEBUG(4,("kdc_udp_proxy: sendto for %s gave %d : %s\n",
-                        state->proxy_ip, sys_errno, strerror(sys_errno)));
-               kdc_udp_next_proxy(state);
+       req = tevent_req_create(mem_ctx, &state,
+                               struct kdc_udp_proxy_state);
+       if (req == NULL) {
+               return NULL;
        }
-}
+       state->ev = ev;
+       state->kdc  = kdc;
+       state->port = port;
+       state->in = in;
 
-/*
-  called when the send of the reply to the client is complete
-  this is used to get an errors from the sendto()
- */
-static void kdc_udp_proxy_reply_done(struct tevent_req *req)
-{
-       struct kdc_udp_proxy_state *state = tevent_req_callback_data(req,
-                                                                    struct kdc_udp_proxy_state);
-       ssize_t ret;
-       int sys_errno;
+       werr = kdc_proxy_get_writeable_dcs(kdc, state, &state->proxy_list);
+       if (!W_ERROR_IS_OK(werr)) {
+               NTSTATUS status = werror_to_ntstatus(werr);
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
 
-       ret = tdgram_sendto_queue_recv(req, &sys_errno);
-       if (ret == -1) {
-               DEBUG(3,("kdc_udp_proxy: reply sendto gave %d : %s\n",
-                        sys_errno, strerror(sys_errno)));
+       kdc_udp_next_proxy(req);
+       if (!tevent_req_is_in_progress(req)) {
+               return tevent_req_post(req, ev);
        }
 
-       /* all done - we can destroy the proxy state */
-       talloc_free(req);
-       talloc_free(state);
+       return req;
 }
 
+static void kdc_udp_proxy_resolve_done(struct composite_context *csubreq);
 
 /*
-  called when the proxy replies
+  try the next proxy in the list
  */
-static void kdc_udp_proxy_reply(struct tevent_req *req)
+static void kdc_udp_next_proxy(struct tevent_req *req)
 {
-       struct kdc_udp_proxy_state *state = tevent_req_callback_data(req,
-                                                                    struct kdc_udp_proxy_state);
-       int sys_errno;
-       uint8_t *buf;
-       struct tsocket_address *src;
-       ssize_t len;
+       struct kdc_udp_proxy_state *state =
+               tevent_req_data(req,
+               struct kdc_udp_proxy_state);
+       const char *proxy_dnsname = state->proxy_list[state->next_proxy];
+       struct composite_context *csubreq;
 
-       len = tdgram_recvfrom_recv(req, &sys_errno,
-                                  state, &buf, &src);
-       talloc_free(req);
-       if (len == -1) {
-               DEBUG(4,("kdc_udp_proxy: reply from %s gave %d : %s\n",
-                        state->proxy_ip, sys_errno, strerror(sys_errno)));
-               kdc_udp_next_proxy(state);
+       if (proxy_dnsname == NULL) {
+               tevent_req_nterror(req, NT_STATUS_NO_LOGON_SERVERS);
                return;
        }
 
-       state->call->out.length = len;
-       state->call->out.data = buf;
+       state->next_proxy++;
 
-       /* TODO: check the reply came from the right IP? */
+       /* make sure we close the socket of the last try */
+       TALLOC_FREE(state->proxy.dgram);
+       ZERO_STRUCT(state->proxy);
 
-       req = tdgram_sendto_queue_send(state,
-                                      state->kdc->task->event_ctx,
-                                      state->sock->dgram,
-                                      state->sock->send_queue,
-                                      state->call->out.data,
-                                      state->call->out.length,
-                                      state->call->src);
-       if (req == NULL) {
-               kdc_udp_next_proxy(state);
+       make_nbt_name(&state->proxy.name, proxy_dnsname, 0);
+
+       csubreq = resolve_name_ex_send(lpcfg_resolve_context(state->kdc->task->lp_ctx),
+                                      state,
+                                      RESOLVE_NAME_FLAG_FORCE_DNS,
+                                      0,
+                                      &state->proxy.name,
+                                      state->ev);
+       if (tevent_req_nomem(csubreq, req)) {
                return;
        }
-
-       tevent_req_set_callback(req, kdc_udp_proxy_reply_done, state);
+       csubreq->async.fn = kdc_udp_proxy_resolve_done;
+       csubreq->async.private_data = req;
 }
 
+static void kdc_udp_proxy_sendto_done(struct tevent_req *subreq);
+static void kdc_udp_proxy_recvfrom_done(struct tevent_req *subreq);
 
-/*
-  called when we've resolved the name of a proxy
- */
-static void kdc_udp_proxy_resolve_done(struct composite_context *c)
+static void kdc_udp_proxy_resolve_done(struct composite_context *csubreq)
 {
-       struct kdc_udp_proxy_state *state;
+       struct tevent_req *req =
+               talloc_get_type_abort(csubreq->async.private_data,
+               struct tevent_req);
+       struct kdc_udp_proxy_state *state =
+               tevent_req_data(req,
+               struct kdc_udp_proxy_state);
        NTSTATUS status;
-       struct tevent_req *req;
+       struct tevent_req *subreq;
        struct tsocket_address *local_addr, *proxy_addr;
        int ret;
-       struct tdgram_context *dgram;
-       struct tevent_queue *send_queue;
-
-       state = talloc_get_type(c->async.private_data, struct kdc_udp_proxy_state);
 
-       status = resolve_name_recv(c, state, &state->proxy_ip);
+       status = resolve_name_recv(csubreq, state, &state->proxy.ip);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0,("Unable to resolve proxy\n"));
-               kdc_udp_next_proxy(state);
+               DEBUG(0,("Unable to resolve proxy[%s] - %s\n",
+                       state->proxy.name.name, nt_errstr(status)));
+               kdc_udp_next_proxy(req);
                return;
        }
 
        /* get an address for us to use locally */
        ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0, &local_addr);
        if (ret != 0) {
-               kdc_udp_next_proxy(state);
+               kdc_udp_next_proxy(req);
                return;
        }
 
        ret = tsocket_address_inet_from_strings(state, "ip",
-                                               state->proxy_ip, state->port, &proxy_addr);
+                                               state->proxy.ip,
+                                               state->port,
+                                               &proxy_addr);
        if (ret != 0) {
-               kdc_udp_next_proxy(state);
+               kdc_udp_next_proxy(req);
                return;
        }
 
        /* create a socket for us to work on */
-       ret = tdgram_inet_udp_socket(local_addr, proxy_addr, state, &dgram);
+       ret = tdgram_inet_udp_socket(local_addr, proxy_addr,
+                                    state, &state->proxy.dgram);
        if (ret != 0) {
-               kdc_udp_next_proxy(state);
-               return;
-       }
-
-       send_queue = tevent_queue_create(state, "kdc_udp_proxy");
-       if (send_queue == NULL) {
-               kdc_udp_next_proxy(state);
+               kdc_udp_next_proxy(req);
                return;
        }
 
-       req = tdgram_sendto_queue_send(state,
-                                      state->kdc->task->event_ctx,
-                                      dgram,
-                                      send_queue,
-                                      state->call->in.data,
-                                      state->call->in.length,
-                                      proxy_addr);
-       if (req == NULL) {
-               kdc_udp_next_proxy(state);
+       subreq = tdgram_sendto_send(state,
+                                   state->ev,
+                                   state->proxy.dgram,
+                                   state->in.data,
+                                   state->in.length,
+                                   NULL);
+       if (tevent_req_nomem(subreq, req)) {
                return;
        }
-
-       tevent_req_set_callback(req, kdc_udp_proxy_sendto_done, state);
+       tevent_req_set_callback(subreq, kdc_udp_proxy_sendto_done, req);
 
        /* setup to receive the reply from the proxy */
-       req = tdgram_recvfrom_send(state, state->kdc->task->event_ctx, dgram);
-       if (req == NULL) {
-               kdc_udp_next_proxy(state);
+       subreq = tdgram_recvfrom_send(state, state->ev, state->proxy.dgram);
+       if (tevent_req_nomem(subreq, req)) {
                return;
        }
-
-       tevent_req_set_callback(req, kdc_udp_proxy_reply, state);
-
-       tevent_req_set_endtime(req, state->kdc->task->event_ctx,
+       tevent_req_set_callback(subreq, kdc_udp_proxy_recvfrom_done, req);
+       tevent_req_set_endtime(subreq, state->ev,
                               timeval_current_ofs(state->kdc->proxy_timeout, 0));
 
-       DEBUG(4,("kdc_udp_proxy: proxying request to %s\n", state->proxy_ip));
+       DEBUG(4,("kdc_udp_proxy: proxying request to %s[%s]\n",
+                state->proxy.name.name, state->proxy.ip));
 }
 
-
 /*
-  called when our proxies are not available
+  called when the send of the call to the proxy is complete
+  this is used to get an errors from the sendto()
  */
-static void kdc_udp_proxy_unavailable(struct kdc_udp_proxy_state *state)
+static void kdc_udp_proxy_sendto_done(struct tevent_req *subreq)
 {
-       int kret;
-       krb5_data k5_error_blob;
-       struct tevent_req *req;
-
-       kret = krb5_mk_error(state->kdc->smb_krb5_context->krb5_context,
-                            KRB5KDC_ERR_SVC_UNAVAILABLE, NULL, NULL,
-                            NULL, NULL, NULL, NULL, &k5_error_blob);
-       if (kret != 0) {
-               DEBUG(2,(__location__ ": Unable to form krb5 error reply\n"));
-               talloc_free(state);
-               return;
-       }
-
-       state->call->out = data_blob_talloc(state, k5_error_blob.data, k5_error_blob.length);
-       krb5_data_free(&k5_error_blob);
-       if (!state->call->out.data) {
-               talloc_free(state);
-               return;
-       }
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct kdc_udp_proxy_state *state =
+               tevent_req_data(req,
+               struct kdc_udp_proxy_state);
+       ssize_t ret;
+       int sys_errno;
 
-       req = tdgram_sendto_queue_send(state,
-                                      state->kdc->task->event_ctx,
-                                      state->sock->dgram,
-                                      state->sock->send_queue,
-                                      state->call->out.data,
-                                      state->call->out.length,
-                                      state->call->src);
-       if (!req) {
-               talloc_free(state);
-               return;
+       ret = tdgram_sendto_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               DEBUG(4,("kdc_udp_proxy: sendto for %s[%s] gave %d : %s\n",
+                        state->proxy.name.name, state->proxy.ip,
+                        sys_errno, strerror(sys_errno)));
+               kdc_udp_next_proxy(req);
        }
-
-       tevent_req_set_callback(req, kdc_udp_proxy_reply_done, state);
 }
 
 /*
-  try the next proxy in the list
+  called when the proxy replies
  */
-static void kdc_udp_next_proxy(struct kdc_udp_proxy_state *state)
+static void kdc_udp_proxy_recvfrom_done(struct tevent_req *subreq)
 {
-       const char *proxy_dnsname = state->proxy_list[state->next_proxy];
-       struct nbt_name name;
-       struct composite_context *c;
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct kdc_udp_proxy_state *state =
+               tevent_req_data(req,
+               struct kdc_udp_proxy_state);
+       int sys_errno;
+       uint8_t *buf;
+       ssize_t len;
 
-       if (proxy_dnsname == NULL) {
-               kdc_udp_proxy_unavailable(state);
+       len = tdgram_recvfrom_recv(subreq, &sys_errno,
+                                  state, &buf, NULL);
+       TALLOC_FREE(subreq);
+       if (len == -1) {
+               DEBUG(4,("kdc_udp_proxy: reply from %s[%s] gave %d : %s\n",
+                        state->proxy.name.name, state->proxy.ip,
+                        sys_errno, strerror(sys_errno)));
+               kdc_udp_next_proxy(req);
                return;
        }
 
-       state->next_proxy++;
+       /*
+        * Check the reply came from the right IP?
+        * As we use connected udp sockets, that should not be needed...
+        */
 
-       make_nbt_name(&name, proxy_dnsname, 0);
+       state->out.length = len;
+       state->out.data = buf;
 
-       c = resolve_name_ex_send(lpcfg_resolve_context(state->kdc->task->lp_ctx),
-                                state,
-                                RESOLVE_NAME_FLAG_FORCE_DNS,
-                                0,
-                                &name,
-                                state->kdc->task->event_ctx);
-       if (c == NULL) {
-               kdc_udp_next_proxy(state);
-               return;
-       }
-       c->async.fn = kdc_udp_proxy_resolve_done;
-       c->async.private_data = state;
+       tevent_req_done(req);
 }
 
-
-/*
-  proxy a UDP kdc request to a writeable DC
- */
-void kdc_udp_proxy(struct kdc_server *kdc, struct kdc_udp_socket *sock,
-                  struct kdc_udp_call *call, uint16_t port)
+NTSTATUS kdc_udp_proxy_recv(struct tevent_req *req,
+                           TALLOC_CTX *mem_ctx,
+                           DATA_BLOB *out)
 {
-       struct kdc_udp_proxy_state *state;
-       WERROR werr;
+       struct kdc_udp_proxy_state *state =
+               tevent_req_data(req,
+               struct kdc_udp_proxy_state);
+       NTSTATUS status;
 
-       state = talloc_zero(kdc, struct kdc_udp_proxy_state);
-       if (state == NULL) {
-               talloc_free(call);
-               return;
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
        }
 
-       state->call = talloc_steal(state, call);
-       state->sock = sock;
-       state->kdc  = kdc;
-       state->port = port;
-
-       werr = kdc_proxy_get_writeable_dcs(kdc, state, &state->proxy_list);
-       if (!W_ERROR_IS_OK(werr)) {
-               kdc_udp_proxy_unavailable(state);
-               return;
-       }
+       out->data = talloc_move(mem_ctx, &state->out.data);
+       out->length = state->out.length;
 
-       kdc_udp_next_proxy(state);
+       tevent_req_received(req);
+       return NT_STATUS_OK;
 }
 
-
 struct kdc_tcp_proxy_state {
        struct kdc_tcp_call *call;
        struct kdc_tcp_connection *kdc_conn;