From e70b09e8f8f99b2d63047525f1d5e360582f7eaa Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 9 Feb 2011 09:07:47 +0100 Subject: s4:kdc: split the kdc_udp_proxy() logic from the main kdc logic 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 | 23 ++-- source4/kdc/kdc.c | 68 +++++++++- source4/kdc/proxy.c | 347 ++++++++++++++++++++++--------------------------- 3 files changed, 231 insertions(+), 207 deletions(-) diff --git a/source4/kdc/kdc-glue.h b/source4/kdc/kdc-glue.h index 75b6b988fe..f9489b1fd1 100644 --- a/source4/kdc/kdc-glue.h +++ b/source4/kdc/kdc-glue.h @@ -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); diff --git a/source4/kdc/kdc.c b/source4/kdc/kdc.c index 249004323c..05c1d9c40d 100644 --- a/source4/kdc/kdc.c +++ b/source4/kdc/kdc.c @@ -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, diff --git a/source4/kdc/proxy.c b/source4/kdc/proxy.c index 6179bf18ab..a11f253b26 100644 --- a/source4/kdc/proxy.c +++ b/source4/kdc/proxy.c @@ -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; -- cgit