From 8002406c51cf1f9f6daebef7e03bfef77f56cf38 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 9 Feb 2011 12:17:23 +0100 Subject: [PATCH] s4:kdc: split the kdc_tcp_proxy() logic from the main kdc logic By having kdc_tcp_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_socket, kdc_process_fn_t, kdc_tcp_call and kdc_tcp_socket private to kdc.c again. metze --- source4/kdc/kdc-glue.h | 50 +----- source4/kdc/kdc.c | 127 +++++++++++++- source4/kdc/proxy.c | 378 +++++++++++++++++++---------------------- 3 files changed, 303 insertions(+), 252 deletions(-) diff --git a/source4/kdc/kdc-glue.h b/source4/kdc/kdc-glue.h index f9489b1fd16..b365cd75c6a 100644 --- a/source4/kdc/kdc-glue.h +++ b/source4/kdc/kdc-glue.h @@ -50,46 +50,6 @@ enum kdc_process_ret { KDC_PROCESS_FAILED, KDC_PROCESS_PROXY}; -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]; -}; - -typedef enum kdc_process_ret (*kdc_process_fn_t)(struct kdc_server *kdc, - TALLOC_CTX *mem_ctx, - DATA_BLOB *input, - DATA_BLOB *reply, - struct tsocket_address *peer_addr, - struct tsocket_address *my_addr, - int datagram); - - -/* hold information about one kdc socket */ -struct kdc_socket { - struct kdc_server *kdc; - struct tsocket_address *local_address; - kdc_process_fn_t process; -}; - -/* - 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; -}; - - enum kdc_process_ret kpasswdd_process(struct kdc_server *kdc, TALLOC_CTX *mem_ctx, DATA_BLOB *input, @@ -112,7 +72,13 @@ 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); +struct tevent_req *kdc_tcp_proxy_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kdc_server *kdc, + uint16_t port, + DATA_BLOB in); +NTSTATUS kdc_tcp_proxy_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *out); #endif diff --git a/source4/kdc/kdc.c b/source4/kdc/kdc.c index 05c1d9c40d5..26365acc7c0 100644 --- a/source4/kdc/kdc.c +++ b/source4/kdc/kdc.c @@ -62,6 +62,45 @@ static NTSTATUS kdc_proxy_unavailable_error(struct kdc_server *kdc, return NT_STATUS_OK; } +typedef enum kdc_process_ret (*kdc_process_fn_t)(struct kdc_server *kdc, + TALLOC_CTX *mem_ctx, + DATA_BLOB *input, + DATA_BLOB *reply, + struct tsocket_address *peer_addr, + struct tsocket_address *my_addr, + int datagram); + +/* hold information about one kdc socket */ +struct kdc_socket { + struct kdc_server *kdc; + struct tsocket_address *local_address; + kdc_process_fn_t process; +}; + +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); @@ -143,6 +182,7 @@ static enum kdc_process_ret kdc_process(struct kdc_server *kdc, return KDC_PROCESS_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) @@ -202,14 +242,94 @@ static void kdc_tcp_call_loop(struct tevent_req *subreq) } if (ret == KDC_PROCESS_PROXY) { + 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; } - kdc_tcp_proxy(kdc_conn->kdc_socket->kdc, kdc_conn, call, - tsocket_address_inet_port(kdc_conn->conn->local_address)); - goto done; + 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 */ @@ -232,7 +352,6 @@ static void kdc_tcp_call_loop(struct tevent_req *subreq) } tevent_req_set_callback(subreq, kdc_tcp_call_writev_done, call); -done: /* * The krb5 tcp pdu's has the length as 4 byte (initial_read_size), * packet_full_request_u32 provides the pdu length then. diff --git a/source4/kdc/proxy.c b/source4/kdc/proxy.c index a11f253b26b..98db956f65e 100644 --- a/source4/kdc/proxy.c +++ b/source4/kdc/proxy.c @@ -324,296 +324,262 @@ NTSTATUS kdc_udp_proxy_recv(struct tevent_req *req, } struct kdc_tcp_proxy_state { - struct kdc_tcp_call *call; - struct kdc_tcp_connection *kdc_conn; + struct tevent_context *ev; struct kdc_server *kdc; uint16_t port; - uint32_t next_proxy; + DATA_BLOB in; + uint8_t in_hdr[4]; + struct iovec in_iov[2]; + DATA_BLOB out; char **proxy_list; - const char *proxy_ip; + uint32_t next_proxy; + struct { + struct nbt_name name; + const char *ip; + struct tstream_context *stream; + } proxy; }; -static void kdc_tcp_next_proxy(struct kdc_tcp_proxy_state *state); +static void kdc_tcp_next_proxy(struct tevent_req *req); -/* - called when the send of the proxied reply to the client is done - */ -static void kdc_tcp_proxy_reply_done(struct tevent_req *req) +struct tevent_req *kdc_tcp_proxy_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kdc_server *kdc, + uint16_t port, + DATA_BLOB in) { - struct kdc_tcp_proxy_state *state = tevent_req_callback_data(req, - struct kdc_tcp_proxy_state); - int ret, sys_errno; + struct tevent_req *req; + struct kdc_tcp_proxy_state *state; + WERROR werr; - ret = tstream_writev_queue_recv(req, &sys_errno); - if (ret == -1) { - DEBUG(4,("kdc_tcp_proxy: writev of reply gave %d : %s\n", - sys_errno, strerror(sys_errno))); + req = tevent_req_create(mem_ctx, &state, + struct kdc_tcp_proxy_state); + if (req == NULL) { + return NULL; } - talloc_free(req); - talloc_free(state); -} - -/* - called when the recv of the proxied reply is done - */ -static void kdc_tcp_proxy_recv_done(struct tevent_req *req) -{ - struct kdc_tcp_proxy_state *state = tevent_req_callback_data(req, - struct kdc_tcp_proxy_state); - NTSTATUS status; - - status = tstream_read_pdu_blob_recv(req, - state, - &state->call->out); - talloc_free(req); + state->ev = ev; + state->kdc = kdc; + state->port = port; + state->in = in; - if (!NT_STATUS_IS_OK(status)) { - kdc_tcp_next_proxy(state); - return; + 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); } + RSIVAL(state->in_hdr, 0, state->in.length); + state->in_iov[0].iov_base = (char *)state->in_hdr; + state->in_iov[0].iov_len = 4; + state->in_iov[1].iov_base = (char *)state->in.data; + state->in_iov[1].iov_len = state->in.length; - /* send the reply to the original caller */ - - state->call->out_iov[0].iov_base = (char *)state->call->out.data; - state->call->out_iov[0].iov_len = state->call->out.length; - - req = tstream_writev_queue_send(state, - state->kdc_conn->conn->event.ctx, - state->kdc_conn->tstream, - state->kdc_conn->send_queue, - state->call->out_iov, 1); - if (req == NULL) { - kdc_tcp_next_proxy(state); - return; + kdc_tcp_next_proxy(req); + if (!tevent_req_is_in_progress(req)) { + return tevent_req_post(req, ev); } - tevent_req_set_callback(req, kdc_tcp_proxy_reply_done, state); + return req; } -/* - called when the send of the proxied packet is done - */ -static void kdc_tcp_proxy_send_done(struct tevent_req *req) -{ - struct kdc_tcp_proxy_state *state = tevent_req_callback_data(req, - struct kdc_tcp_proxy_state); - int ret, sys_errno; - - ret = tstream_writev_queue_recv(req, &sys_errno); - talloc_free(req); - if (ret == -1) { - kdc_tcp_next_proxy(state); - } -} +static void kdc_tcp_proxy_resolve_done(struct composite_context *csubreq); /* - called when we've connected to the proxy + try the next proxy in the list */ -static void kdc_tcp_proxy_connect_done(struct tevent_req *req) +static void kdc_tcp_next_proxy(struct tevent_req *req) { - struct kdc_tcp_proxy_state *state = tevent_req_callback_data(req, - struct kdc_tcp_proxy_state); - int ret, sys_errno; - struct tstream_context *stream; - struct tevent_queue *send_queue; - - - ret = tstream_inet_tcp_connect_recv(req, &sys_errno, state, &stream, NULL); - talloc_free(req); + struct kdc_tcp_proxy_state *state = + tevent_req_data(req, + struct kdc_tcp_proxy_state); + const char *proxy_dnsname = state->proxy_list[state->next_proxy]; + struct composite_context *csubreq; - if (ret != 0) { - kdc_tcp_next_proxy(state); + if (proxy_dnsname == NULL) { + tevent_req_nterror(req, NT_STATUS_NO_LOGON_SERVERS); return; } - RSIVAL(state->call->out_hdr, 0, state->call->in.length); - state->call->out_iov[0].iov_base = (char *)state->call->out_hdr; - state->call->out_iov[0].iov_len = 4; - state->call->out_iov[1].iov_base = (char *) state->call->in.data; - state->call->out_iov[1].iov_len = state->call->in.length; - - send_queue = tevent_queue_create(state, "kdc_tcp_proxy"); - if (send_queue == NULL) { - kdc_tcp_next_proxy(state); - return; - } + state->next_proxy++; - req = tstream_writev_queue_send(state, - state->kdc_conn->conn->event.ctx, - stream, - send_queue, - state->call->out_iov, 2); - if (req == NULL) { - kdc_tcp_next_proxy(state); - return; - } + /* make sure we close the socket of the last try */ + TALLOC_FREE(state->proxy.stream); + ZERO_STRUCT(state->proxy); - tevent_req_set_callback(req, kdc_tcp_proxy_send_done, state); + make_nbt_name(&state->proxy.name, proxy_dnsname, 0); - req = tstream_read_pdu_blob_send(state, - state->kdc_conn->conn->event.ctx, - stream, - 4, /* initial_read_size */ - packet_full_request_u32, - state); - if (req == NULL) { - kdc_tcp_next_proxy(state); + 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_tcp_proxy_recv_done, state); - tevent_req_set_endtime(req, state->kdc->task->event_ctx, - timeval_current_ofs(state->kdc->proxy_timeout, 0)); - + csubreq->async.fn = kdc_tcp_proxy_resolve_done; + csubreq->async.private_data = req; } +static void kdc_tcp_proxy_connect_done(struct tevent_req *subreq); -/* - called when name resolution for a proxy is done - */ -static void kdc_tcp_proxy_resolve_done(struct composite_context *c) +static void kdc_tcp_proxy_resolve_done(struct composite_context *csubreq) { - struct kdc_tcp_proxy_state *state; + struct tevent_req *req = + talloc_get_type_abort(csubreq->async.private_data, + struct tevent_req); + struct kdc_tcp_proxy_state *state = + tevent_req_data(req, + struct kdc_tcp_proxy_state); NTSTATUS status; - struct tevent_req *req; + struct tevent_req *subreq; struct tsocket_address *local_addr, *proxy_addr; int ret; - state = talloc_get_type(c->async.private_data, struct kdc_tcp_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)) { - kdc_tcp_next_proxy(state); + DEBUG(0,("Unable to resolve proxy[%s] - %s\n", + state->proxy.name.name, nt_errstr(status))); + kdc_tcp_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_tcp_next_proxy(state); + kdc_tcp_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_tcp_next_proxy(state); + kdc_tcp_next_proxy(req); return; } - /* connect to the proxy */ - req = tstream_inet_tcp_connect_send(state, state->kdc->task->event_ctx, local_addr, proxy_addr); - if (req == NULL) { - kdc_tcp_next_proxy(state); + subreq = tstream_inet_tcp_connect_send(state, state->ev, + local_addr, proxy_addr); + if (tevent_req_nomem(subreq, req)) { return; } - - tevent_req_set_callback(req, kdc_tcp_proxy_connect_done, state); - - tevent_req_set_endtime(req, state->kdc->task->event_ctx, + tevent_req_set_callback(subreq, kdc_tcp_proxy_connect_done, req); + tevent_req_set_endtime(subreq, state->ev, timeval_current_ofs(state->kdc->proxy_timeout, 0)); - - DEBUG(4,("kdc_tcp_proxy: proxying request to %s\n", state->proxy_ip)); } +static void kdc_tcp_proxy_writev_done(struct tevent_req *subreq); +static void kdc_tcp_proxy_read_pdu_done(struct tevent_req *subreq); -/* - called when our proxies are not available - */ -static void kdc_tcp_proxy_unavailable(struct kdc_tcp_proxy_state *state) +static void kdc_tcp_proxy_connect_done(struct tevent_req *subreq) { - int kret; - krb5_data k5_error_blob; - struct tevent_req *req; + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct kdc_tcp_proxy_state *state = + tevent_req_data(req, + struct kdc_tcp_proxy_state); + int ret, sys_errno; - 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); + ret = tstream_inet_tcp_connect_recv(subreq, &sys_errno, + state, &state->proxy.stream, NULL); + TALLOC_FREE(subreq); + if (ret != 0) { + kdc_tcp_next_proxy(req); 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); + subreq = tstream_writev_send(state, + state->ev, + state->proxy.stream, + state->in_iov, 2); + if (tevent_req_nomem(subreq, req)) { return; } + tevent_req_set_callback(subreq, kdc_tcp_proxy_writev_done, req); - state->call->out_iov[0].iov_base = (char *)state->call->out.data; - state->call->out_iov[0].iov_len = state->call->out.length; - - req = tstream_writev_queue_send(state, - state->kdc_conn->conn->event.ctx, - state->kdc_conn->tstream, - state->kdc_conn->send_queue, - state->call->out_iov, 1); - if (!req) { - talloc_free(state); + subreq = tstream_read_pdu_blob_send(state, + state->ev, + state->proxy.stream, + 4, /* initial_read_size */ + packet_full_request_u32, + req); + if (tevent_req_nomem(subreq, req)) { return; } + tevent_req_set_callback(subreq, kdc_tcp_proxy_read_pdu_done, req); + tevent_req_set_endtime(subreq, state->kdc->task->event_ctx, + timeval_current_ofs(state->kdc->proxy_timeout, 0)); - tevent_req_set_callback(req, kdc_tcp_proxy_reply_done, state); + DEBUG(4,("kdc_tcp_proxy: proxying request to %s[%s]\n", + state->proxy.name.name, state->proxy.ip)); } -/* - try the next proxy in the list - */ -static void kdc_tcp_next_proxy(struct kdc_tcp_proxy_state *state) +static void kdc_tcp_proxy_writev_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); + int ret, sys_errno; - if (proxy_dnsname == NULL) { - kdc_tcp_proxy_unavailable(state); - return; + ret = tstream_writev_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + if (ret == -1) { + kdc_tcp_next_proxy(req); } +} - state->next_proxy++; +static void kdc_tcp_proxy_read_pdu_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct kdc_tcp_proxy_state *state = + tevent_req_data(req, + struct kdc_tcp_proxy_state); + NTSTATUS status; + DATA_BLOB raw; - make_nbt_name(&name, proxy_dnsname, 0); + status = tstream_read_pdu_blob_recv(subreq, state, &raw); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + kdc_tcp_next_proxy(req); + return; + } - 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_tcp_next_proxy(state); + /* + * raw blob has the length in the first 4 bytes, + * which we do not need here. + */ + state->out = data_blob_talloc(state, raw.data + 4, raw.length - 4); + if (state->out.length != raw.length - 4) { + tevent_req_nomem(NULL, req); return; } - c->async.fn = kdc_tcp_proxy_resolve_done; - c->async.private_data = state; -} + tevent_req_done(req); +} -/* - proxy a TCP kdc request to a writeable DC - */ -void kdc_tcp_proxy(struct kdc_server *kdc, struct kdc_tcp_connection *kdc_conn, - struct kdc_tcp_call *call, uint16_t port) +NTSTATUS kdc_tcp_proxy_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *out) { - struct kdc_tcp_proxy_state *state; - WERROR werr; - - state = talloc_zero(kdc_conn, struct kdc_tcp_proxy_state); - - state->call = talloc_steal(state, call); - state->kdc_conn = kdc_conn; - state->kdc = kdc; - state->port = port; + struct kdc_tcp_proxy_state *state = + tevent_req_data(req, + struct kdc_tcp_proxy_state); + NTSTATUS status; - werr = kdc_proxy_get_writeable_dcs(kdc, state, &state->proxy_list); - if (!W_ERROR_IS_OK(werr)) { - kdc_tcp_proxy_unavailable(state); - return; + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; } - kdc_tcp_next_proxy(state); + out->data = talloc_move(mem_ctx, &state->out.data); + out->length = state->out.length; + + tevent_req_received(req); + return NT_STATUS_OK; } -- 2.34.1