#include "param/param.h"
#include "kdc/kdc-glue.h"
#include "librpc/gen_ndr/ndr_misc.h"
-
+#include "dsdb/samdb/samdb.h"
+#include "auth/session.h"
extern struct krb5plugin_windc_ftable windc_plugin_table;
extern struct hdb_method hdb_samba4;
-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;
-};
-
static void kdc_tcp_terminate_connection(struct kdc_tcp_connection *kdcconn, const char *reason)
{
stream_terminate_connection(kdcconn->conn, reason);
*reply = data_blob(NULL, 0);
return KDC_PROCESS_FAILED;
}
+
+ if (ret == HDB_ERR_NOT_FOUND_HERE) {
+ *reply = data_blob(NULL, 0);
+ return KDC_PROCESS_PROXY;
+ }
+
if (k5_reply.length) {
*reply = data_blob_talloc(mem_ctx, k5_reply.data, k5_reply.length);
krb5_data_free(&k5_reply);
return KDC_PROCESS_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];
-};
-
static void kdc_tcp_call_writev_done(struct tevent_req *subreq);
static void kdc_tcp_call_loop(struct tevent_req *subreq)
return;
}
+ if (ret == KDC_PROCESS_PROXY) {
+ 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;
+ }
+
/* 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;
}
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.
.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 tsocket_address *src;
- DATA_BLOB in;
- DATA_BLOB out;
-};
-
static void kdc_udp_call_sendto_done(struct tevent_req *subreq);
static void kdc_udp_call_loop(struct tevent_req *subreq)
goto done;
}
+ if (ret == KDC_PROCESS_PROXY) {
+ 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));
+ goto done;
+ }
+
subreq = tdgram_sendto_queue_send(call,
sock->kdc_socket->kdc->task->event_ctx,
sock->dgram,
NTSTATUS status;
krb5_error_code ret;
struct interface *ifaces;
+ int ldb_ret;
switch (lpcfg_server_role(task->lp_ctx)) {
case ROLE_STANDALONE:
task_server_set_title(task, "task[kdc]");
- kdc = talloc(task, struct kdc_server);
+ kdc = talloc_zero(task, struct kdc_server);
if (kdc == NULL) {
task_server_terminate(task, "kdc: out of memory", true);
return;
kdc->task = task;
+
+ /* get a samdb connection */
+ kdc->samdb = samdb_connect(kdc, kdc->task->event_ctx, kdc->task->lp_ctx,
+ system_session(kdc->task->lp_ctx), 0);
+ if (!kdc->samdb) {
+ DEBUG(1,("kdc_task_init: unable to connect to samdb\n"));
+ task_server_terminate(task, "kdc: krb5_init_context samdb connect failed", true);
+ return;
+ }
+
+ ldb_ret = samdb_rodc(kdc->samdb, &kdc->am_rodc);
+ if (ldb_ret != LDB_SUCCESS) {
+ DEBUG(1, ("kdc_task_init: Cannot determine if we are an RODC: %s\n",
+ ldb_errstring(kdc->samdb)));
+ task_server_terminate(task, "kdc: krb5_init_context samdb RODC connect failed", true);
+ return;
+ }
+
+ kdc->proxy_timeout = lpcfg_parm_int(kdc->task->lp_ctx, NULL, "kdc", "proxy timeout", 5);
+
initialize_krb5_error_table();
ret = smb_krb5_init_context(kdc, task->event_ctx, task->lp_ctx, &kdc->smb_krb5_context);
--- /dev/null
+/*
+ Unix SMB/CIFS implementation.
+
+ KDC Server request proxying
+
+ Copyright (C) Andrew Tridgell 2010
+ Copyright (C) Andrew Bartlett 2010
+
+ 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 "smbd/process_model.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+#include "system/network.h"
+#include "param/param.h"
+#include "lib/stream/packet.h"
+#include "kdc/kdc-glue.h"
+#include "ldb.h"
+#include "librpc/gen_ndr/drsblobs.h"
+#include "dsdb/schema/schema.h"
+#include "dsdb/common/proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/resolve/resolve.h"
+
+
+/*
+ get a list of our replication partners from repsFrom, returning it in *proxy_list
+ */
+static WERROR kdc_proxy_get_writeable_dcs(struct kdc_server *kdc, TALLOC_CTX *mem_ctx, char ***proxy_list)
+{
+ WERROR werr;
+ uint32_t count, i;
+ struct repsFromToBlob *reps;
+
+ werr = dsdb_loadreps(kdc->samdb, mem_ctx, ldb_get_default_basedn(kdc->samdb), "repsFrom", &reps, &count);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ if (count == 0) {
+ /* we don't have any DCs to replicate with. Very
+ strange for a RODC */
+ DEBUG(1,(__location__ ": No replication sources for RODC in KDC proxy\n"));
+ talloc_free(reps);
+ return WERR_DS_DRA_NO_REPLICA;
+ }
+
+ (*proxy_list) = talloc_array(mem_ctx, char *, count+1);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(*proxy_list, reps);
+
+ talloc_steal(*proxy_list, reps);
+
+ for (i=0; i<count; i++) {
+ const char *dns_name = NULL;
+ if (reps->version == 1) {
+ dns_name = reps->ctr.ctr1.other_info->dns_name;
+ } else if (reps->version == 2) {
+ dns_name = reps->ctr.ctr2.other_info->dns_name1;
+ }
+ (*proxy_list)[i] = talloc_strdup(*proxy_list, dns_name);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE((*proxy_list)[i], *proxy_list);
+ }
+ (*proxy_list)[i] = NULL;
+
+ talloc_free(reps);
+
+ return WERR_OK;
+}
+
+
+struct kdc_udp_proxy_state {
+ struct kdc_udp_call *call;
+ struct kdc_udp_socket *sock;
+ struct kdc_server *kdc;
+ char **proxy_list;
+ uint32_t next_proxy;
+ const char *proxy_ip;
+ uint16_t port;
+};
+
+
+static void kdc_udp_next_proxy(struct kdc_udp_proxy_state *state);
+
+/*
+ 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 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);
+
+ 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);
+ }
+}
+
+/*
+ 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;
+
+ 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)));
+ }
+
+ /* all done - we can destroy the proxy state */
+ talloc_free(req);
+ talloc_free(state);
+}
+
+
+/*
+ called when the proxy replies
+ */
+static void kdc_udp_proxy_reply(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;
+
+ 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);
+ return;
+ }
+
+ state->call->out.length = len;
+ state->call->out.data = buf;
+
+ /* TODO: check the reply came from the right IP? */
+
+ 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);
+ return;
+ }
+
+ tevent_req_set_callback(req, kdc_udp_proxy_reply_done, state);
+}
+
+
+/*
+ called when we've resolved the name of a proxy
+ */
+static void kdc_udp_proxy_resolve_done(struct composite_context *c)
+{
+ struct kdc_udp_proxy_state *state;
+ NTSTATUS status;
+ struct tevent_req *req;
+ 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);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to resolve proxy\n"));
+ kdc_udp_next_proxy(state);
+ 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);
+ return;
+ }
+
+ ret = tsocket_address_inet_from_strings(state, "ip",
+ state->proxy_ip, state->port, &proxy_addr);
+ if (ret != 0) {
+ kdc_udp_next_proxy(state);
+ return;
+ }
+
+ /* create a socket for us to work on */
+ ret = tdgram_inet_udp_socket(local_addr, proxy_addr, state, &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);
+ 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);
+ return;
+ }
+
+ tevent_req_set_callback(req, kdc_udp_proxy_sendto_done, state);
+
+ /* 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);
+ return;
+ }
+
+ tevent_req_set_callback(req, kdc_udp_proxy_reply, state);
+
+ tevent_req_set_endtime(req, state->kdc->task->event_ctx,
+ timeval_current_ofs(state->kdc->proxy_timeout, 0));
+
+ DEBUG(4,("kdc_udp_proxy: proxying request to %s\n", state->proxy_ip));
+}
+
+
+/*
+ called when our proxies are not available
+ */
+static void kdc_udp_proxy_unavailable(struct kdc_udp_proxy_state *state)
+{
+ 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;
+ }
+
+ 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;
+ }
+
+ tevent_req_set_callback(req, kdc_udp_proxy_reply_done, state);
+}
+
+/*
+ try the next proxy in the list
+ */
+static void kdc_udp_next_proxy(struct kdc_udp_proxy_state *state)
+{
+ const char *proxy_dnsname = state->proxy_list[state->next_proxy];
+ struct nbt_name name;
+ struct composite_context *c;
+
+ if (proxy_dnsname == NULL) {
+ kdc_udp_proxy_unavailable(state);
+ return;
+ }
+
+ state->next_proxy++;
+
+ make_nbt_name(&name, proxy_dnsname, 0);
+
+ 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;
+}
+
+
+/*
+ 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)
+{
+ struct kdc_udp_proxy_state *state;
+ WERROR werr;
+
+ state = talloc_zero(kdc, struct kdc_udp_proxy_state);
+ if (state == NULL) {
+ talloc_free(call);
+ return;
+ }
+
+ 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;
+ }
+
+ kdc_udp_next_proxy(state);
+}
+
+
+struct kdc_tcp_proxy_state {
+ struct kdc_tcp_call *call;
+ struct kdc_tcp_connection *kdc_conn;
+ struct kdc_server *kdc;
+ uint16_t port;
+ uint32_t next_proxy;
+ char **proxy_list;
+ const char *proxy_ip;
+};
+
+static void kdc_tcp_next_proxy(struct kdc_tcp_proxy_state *state);
+
+/*
+ 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 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);
+ if (ret == -1) {
+ DEBUG(4,("kdc_tcp_proxy: writev of reply gave %d : %s\n",
+ sys_errno, strerror(sys_errno)));
+ }
+ 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);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ kdc_tcp_next_proxy(state);
+ return;
+ }
+
+
+ /* 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;
+ }
+
+ tevent_req_set_callback(req, kdc_tcp_proxy_reply_done, state);
+}
+
+/*
+ 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);
+ }
+}
+
+/*
+ called when we've connected to the proxy
+ */
+static void kdc_tcp_proxy_connect_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;
+ struct tstream_context *stream;
+ struct tevent_queue *send_queue;
+
+
+ ret = tstream_inet_tcp_connect_recv(req, &sys_errno, state, &stream, NULL);
+ talloc_free(req);
+
+ if (ret != 0) {
+ kdc_tcp_next_proxy(state);
+ 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;
+ }
+
+ 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;
+ }
+
+ tevent_req_set_callback(req, kdc_tcp_proxy_send_done, state);
+
+ 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);
+ 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));
+
+}
+
+
+/*
+ called when name resolution for a proxy is done
+ */
+static void kdc_tcp_proxy_resolve_done(struct composite_context *c)
+{
+ struct kdc_tcp_proxy_state *state;
+ NTSTATUS status;
+ struct tevent_req *req;
+ 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);
+ if (!NT_STATUS_IS_OK(status)) {
+ kdc_tcp_next_proxy(state);
+ 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);
+ return;
+ }
+
+ ret = tsocket_address_inet_from_strings(state, "ip",
+ state->proxy_ip, state->port, &proxy_addr);
+ if (ret != 0) {
+ kdc_tcp_next_proxy(state);
+ 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);
+ return;
+ }
+
+ tevent_req_set_callback(req, kdc_tcp_proxy_connect_done, state);
+
+ tevent_req_set_endtime(req, state->kdc->task->event_ctx,
+ timeval_current_ofs(state->kdc->proxy_timeout, 0));
+
+ DEBUG(4,("kdc_tcp_proxy: proxying request to %s\n", state->proxy_ip));
+}
+
+
+/*
+ called when our proxies are not available
+ */
+static void kdc_tcp_proxy_unavailable(struct kdc_tcp_proxy_state *state)
+{
+ 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;
+ }
+
+ 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);
+ return;
+ }
+
+ tevent_req_set_callback(req, kdc_tcp_proxy_reply_done, state);
+}
+
+/*
+ try the next proxy in the list
+ */
+static void kdc_tcp_next_proxy(struct kdc_tcp_proxy_state *state)
+{
+ const char *proxy_dnsname = state->proxy_list[state->next_proxy];
+ struct nbt_name name;
+ struct composite_context *c;
+
+ if (proxy_dnsname == NULL) {
+ kdc_tcp_proxy_unavailable(state);
+ return;
+ }
+
+ state->next_proxy++;
+
+ make_nbt_name(&name, proxy_dnsname, 0);
+
+ 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);
+ return;
+ }
+ c->async.fn = kdc_tcp_proxy_resolve_done;
+ c->async.private_data = state;
+}
+
+
+/*
+ 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)
+{
+ 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;
+
+ werr = kdc_proxy_get_writeable_dcs(kdc, state, &state->proxy_list);
+ if (!W_ERROR_IS_OK(werr)) {
+ kdc_tcp_proxy_unavailable(state);
+ return;
+ }
+
+ kdc_tcp_next_proxy(state);
+}