s4:kdc: split the kdc_udp_proxy() logic from the main kdc logic
[Samba.git] / source4 / kdc / proxy.c
bloba11f253b26bf0c9c115d1dccaec3ac9b5ee534a5
1 /*
2 Unix SMB/CIFS implementation.
4 KDC Server request proxying
6 Copyright (C) Andrew Tridgell 2010
7 Copyright (C) Andrew Bartlett 2010
8 Copyright (C) Stefan Metzmacher 2011
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "smbd/process_model.h"
26 #include "lib/tsocket/tsocket.h"
27 #include "libcli/util/tstream.h"
28 #include "lib/util/tevent_ntstatus.h"
29 #include "lib/stream/packet.h"
30 #include "kdc/kdc-glue.h"
31 #include "dsdb/samdb/samdb.h"
32 #include "libcli/composite/composite.h"
33 #include "libcli/resolve/resolve.h"
37 get a list of our replication partners from repsFrom, returning it in *proxy_list
39 static WERROR kdc_proxy_get_writeable_dcs(struct kdc_server *kdc, TALLOC_CTX *mem_ctx, char ***proxy_list)
41 WERROR werr;
42 uint32_t count, i;
43 struct repsFromToBlob *reps;
45 werr = dsdb_loadreps(kdc->samdb, mem_ctx, ldb_get_default_basedn(kdc->samdb), "repsFrom", &reps, &count);
46 W_ERROR_NOT_OK_RETURN(werr);
48 if (count == 0) {
49 /* we don't have any DCs to replicate with. Very
50 strange for a RODC */
51 DEBUG(1,(__location__ ": No replication sources for RODC in KDC proxy\n"));
52 talloc_free(reps);
53 return WERR_DS_DRA_NO_REPLICA;
56 (*proxy_list) = talloc_array(mem_ctx, char *, count+1);
57 W_ERROR_HAVE_NO_MEMORY_AND_FREE(*proxy_list, reps);
59 talloc_steal(*proxy_list, reps);
61 for (i=0; i<count; i++) {
62 const char *dns_name = NULL;
63 if (reps->version == 1) {
64 dns_name = reps->ctr.ctr1.other_info->dns_name;
65 } else if (reps->version == 2) {
66 dns_name = reps->ctr.ctr2.other_info->dns_name1;
68 (*proxy_list)[i] = talloc_strdup(*proxy_list, dns_name);
69 W_ERROR_HAVE_NO_MEMORY_AND_FREE((*proxy_list)[i], *proxy_list);
71 (*proxy_list)[i] = NULL;
73 talloc_free(reps);
75 return WERR_OK;
79 struct kdc_udp_proxy_state {
80 struct tevent_context *ev;
81 struct kdc_server *kdc;
82 uint16_t port;
83 DATA_BLOB in;
84 DATA_BLOB out;
85 char **proxy_list;
86 uint32_t next_proxy;
87 struct {
88 struct nbt_name name;
89 const char *ip;
90 struct tdgram_context *dgram;
91 } proxy;
95 static void kdc_udp_next_proxy(struct tevent_req *req);
97 struct tevent_req *kdc_udp_proxy_send(TALLOC_CTX *mem_ctx,
98 struct tevent_context *ev,
99 struct kdc_server *kdc,
100 uint16_t port,
101 DATA_BLOB in)
103 struct tevent_req *req;
104 struct kdc_udp_proxy_state *state;
105 WERROR werr;
107 req = tevent_req_create(mem_ctx, &state,
108 struct kdc_udp_proxy_state);
109 if (req == NULL) {
110 return NULL;
112 state->ev = ev;
113 state->kdc = kdc;
114 state->port = port;
115 state->in = in;
117 werr = kdc_proxy_get_writeable_dcs(kdc, state, &state->proxy_list);
118 if (!W_ERROR_IS_OK(werr)) {
119 NTSTATUS status = werror_to_ntstatus(werr);
120 tevent_req_nterror(req, status);
121 return tevent_req_post(req, ev);
124 kdc_udp_next_proxy(req);
125 if (!tevent_req_is_in_progress(req)) {
126 return tevent_req_post(req, ev);
129 return req;
132 static void kdc_udp_proxy_resolve_done(struct composite_context *csubreq);
135 try the next proxy in the list
137 static void kdc_udp_next_proxy(struct tevent_req *req)
139 struct kdc_udp_proxy_state *state =
140 tevent_req_data(req,
141 struct kdc_udp_proxy_state);
142 const char *proxy_dnsname = state->proxy_list[state->next_proxy];
143 struct composite_context *csubreq;
145 if (proxy_dnsname == NULL) {
146 tevent_req_nterror(req, NT_STATUS_NO_LOGON_SERVERS);
147 return;
150 state->next_proxy++;
152 /* make sure we close the socket of the last try */
153 TALLOC_FREE(state->proxy.dgram);
154 ZERO_STRUCT(state->proxy);
156 make_nbt_name(&state->proxy.name, proxy_dnsname, 0);
158 csubreq = resolve_name_ex_send(lpcfg_resolve_context(state->kdc->task->lp_ctx),
159 state,
160 RESOLVE_NAME_FLAG_FORCE_DNS,
162 &state->proxy.name,
163 state->ev);
164 if (tevent_req_nomem(csubreq, req)) {
165 return;
167 csubreq->async.fn = kdc_udp_proxy_resolve_done;
168 csubreq->async.private_data = req;
171 static void kdc_udp_proxy_sendto_done(struct tevent_req *subreq);
172 static void kdc_udp_proxy_recvfrom_done(struct tevent_req *subreq);
174 static void kdc_udp_proxy_resolve_done(struct composite_context *csubreq)
176 struct tevent_req *req =
177 talloc_get_type_abort(csubreq->async.private_data,
178 struct tevent_req);
179 struct kdc_udp_proxy_state *state =
180 tevent_req_data(req,
181 struct kdc_udp_proxy_state);
182 NTSTATUS status;
183 struct tevent_req *subreq;
184 struct tsocket_address *local_addr, *proxy_addr;
185 int ret;
187 status = resolve_name_recv(csubreq, state, &state->proxy.ip);
188 if (!NT_STATUS_IS_OK(status)) {
189 DEBUG(0,("Unable to resolve proxy[%s] - %s\n",
190 state->proxy.name.name, nt_errstr(status)));
191 kdc_udp_next_proxy(req);
192 return;
195 /* get an address for us to use locally */
196 ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0, &local_addr);
197 if (ret != 0) {
198 kdc_udp_next_proxy(req);
199 return;
202 ret = tsocket_address_inet_from_strings(state, "ip",
203 state->proxy.ip,
204 state->port,
205 &proxy_addr);
206 if (ret != 0) {
207 kdc_udp_next_proxy(req);
208 return;
211 /* create a socket for us to work on */
212 ret = tdgram_inet_udp_socket(local_addr, proxy_addr,
213 state, &state->proxy.dgram);
214 if (ret != 0) {
215 kdc_udp_next_proxy(req);
216 return;
219 subreq = tdgram_sendto_send(state,
220 state->ev,
221 state->proxy.dgram,
222 state->in.data,
223 state->in.length,
224 NULL);
225 if (tevent_req_nomem(subreq, req)) {
226 return;
228 tevent_req_set_callback(subreq, kdc_udp_proxy_sendto_done, req);
230 /* setup to receive the reply from the proxy */
231 subreq = tdgram_recvfrom_send(state, state->ev, state->proxy.dgram);
232 if (tevent_req_nomem(subreq, req)) {
233 return;
235 tevent_req_set_callback(subreq, kdc_udp_proxy_recvfrom_done, req);
236 tevent_req_set_endtime(subreq, state->ev,
237 timeval_current_ofs(state->kdc->proxy_timeout, 0));
239 DEBUG(4,("kdc_udp_proxy: proxying request to %s[%s]\n",
240 state->proxy.name.name, state->proxy.ip));
244 called when the send of the call to the proxy is complete
245 this is used to get an errors from the sendto()
247 static void kdc_udp_proxy_sendto_done(struct tevent_req *subreq)
249 struct tevent_req *req =
250 tevent_req_callback_data(subreq,
251 struct tevent_req);
252 struct kdc_udp_proxy_state *state =
253 tevent_req_data(req,
254 struct kdc_udp_proxy_state);
255 ssize_t ret;
256 int sys_errno;
258 ret = tdgram_sendto_recv(subreq, &sys_errno);
259 TALLOC_FREE(subreq);
260 if (ret == -1) {
261 DEBUG(4,("kdc_udp_proxy: sendto for %s[%s] gave %d : %s\n",
262 state->proxy.name.name, state->proxy.ip,
263 sys_errno, strerror(sys_errno)));
264 kdc_udp_next_proxy(req);
269 called when the proxy replies
271 static void kdc_udp_proxy_recvfrom_done(struct tevent_req *subreq)
273 struct tevent_req *req =
274 tevent_req_callback_data(subreq,
275 struct tevent_req);
276 struct kdc_udp_proxy_state *state =
277 tevent_req_data(req,
278 struct kdc_udp_proxy_state);
279 int sys_errno;
280 uint8_t *buf;
281 ssize_t len;
283 len = tdgram_recvfrom_recv(subreq, &sys_errno,
284 state, &buf, NULL);
285 TALLOC_FREE(subreq);
286 if (len == -1) {
287 DEBUG(4,("kdc_udp_proxy: reply from %s[%s] gave %d : %s\n",
288 state->proxy.name.name, state->proxy.ip,
289 sys_errno, strerror(sys_errno)));
290 kdc_udp_next_proxy(req);
291 return;
295 * Check the reply came from the right IP?
296 * As we use connected udp sockets, that should not be needed...
299 state->out.length = len;
300 state->out.data = buf;
302 tevent_req_done(req);
305 NTSTATUS kdc_udp_proxy_recv(struct tevent_req *req,
306 TALLOC_CTX *mem_ctx,
307 DATA_BLOB *out)
309 struct kdc_udp_proxy_state *state =
310 tevent_req_data(req,
311 struct kdc_udp_proxy_state);
312 NTSTATUS status;
314 if (tevent_req_is_nterror(req, &status)) {
315 tevent_req_received(req);
316 return status;
319 out->data = talloc_move(mem_ctx, &state->out.data);
320 out->length = state->out.length;
322 tevent_req_received(req);
323 return NT_STATUS_OK;
326 struct kdc_tcp_proxy_state {
327 struct kdc_tcp_call *call;
328 struct kdc_tcp_connection *kdc_conn;
329 struct kdc_server *kdc;
330 uint16_t port;
331 uint32_t next_proxy;
332 char **proxy_list;
333 const char *proxy_ip;
336 static void kdc_tcp_next_proxy(struct kdc_tcp_proxy_state *state);
339 called when the send of the proxied reply to the client is done
341 static void kdc_tcp_proxy_reply_done(struct tevent_req *req)
343 struct kdc_tcp_proxy_state *state = tevent_req_callback_data(req,
344 struct kdc_tcp_proxy_state);
345 int ret, sys_errno;
347 ret = tstream_writev_queue_recv(req, &sys_errno);
348 if (ret == -1) {
349 DEBUG(4,("kdc_tcp_proxy: writev of reply gave %d : %s\n",
350 sys_errno, strerror(sys_errno)));
352 talloc_free(req);
353 talloc_free(state);
357 called when the recv of the proxied reply is done
359 static void kdc_tcp_proxy_recv_done(struct tevent_req *req)
361 struct kdc_tcp_proxy_state *state = tevent_req_callback_data(req,
362 struct kdc_tcp_proxy_state);
363 NTSTATUS status;
365 status = tstream_read_pdu_blob_recv(req,
366 state,
367 &state->call->out);
368 talloc_free(req);
370 if (!NT_STATUS_IS_OK(status)) {
371 kdc_tcp_next_proxy(state);
372 return;
376 /* send the reply to the original caller */
378 state->call->out_iov[0].iov_base = (char *)state->call->out.data;
379 state->call->out_iov[0].iov_len = state->call->out.length;
381 req = tstream_writev_queue_send(state,
382 state->kdc_conn->conn->event.ctx,
383 state->kdc_conn->tstream,
384 state->kdc_conn->send_queue,
385 state->call->out_iov, 1);
386 if (req == NULL) {
387 kdc_tcp_next_proxy(state);
388 return;
391 tevent_req_set_callback(req, kdc_tcp_proxy_reply_done, state);
395 called when the send of the proxied packet is done
397 static void kdc_tcp_proxy_send_done(struct tevent_req *req)
399 struct kdc_tcp_proxy_state *state = tevent_req_callback_data(req,
400 struct kdc_tcp_proxy_state);
401 int ret, sys_errno;
403 ret = tstream_writev_queue_recv(req, &sys_errno);
404 talloc_free(req);
405 if (ret == -1) {
406 kdc_tcp_next_proxy(state);
411 called when we've connected to the proxy
413 static void kdc_tcp_proxy_connect_done(struct tevent_req *req)
415 struct kdc_tcp_proxy_state *state = tevent_req_callback_data(req,
416 struct kdc_tcp_proxy_state);
417 int ret, sys_errno;
418 struct tstream_context *stream;
419 struct tevent_queue *send_queue;
422 ret = tstream_inet_tcp_connect_recv(req, &sys_errno, state, &stream, NULL);
423 talloc_free(req);
425 if (ret != 0) {
426 kdc_tcp_next_proxy(state);
427 return;
430 RSIVAL(state->call->out_hdr, 0, state->call->in.length);
431 state->call->out_iov[0].iov_base = (char *)state->call->out_hdr;
432 state->call->out_iov[0].iov_len = 4;
433 state->call->out_iov[1].iov_base = (char *) state->call->in.data;
434 state->call->out_iov[1].iov_len = state->call->in.length;
436 send_queue = tevent_queue_create(state, "kdc_tcp_proxy");
437 if (send_queue == NULL) {
438 kdc_tcp_next_proxy(state);
439 return;
442 req = tstream_writev_queue_send(state,
443 state->kdc_conn->conn->event.ctx,
444 stream,
445 send_queue,
446 state->call->out_iov, 2);
447 if (req == NULL) {
448 kdc_tcp_next_proxy(state);
449 return;
452 tevent_req_set_callback(req, kdc_tcp_proxy_send_done, state);
454 req = tstream_read_pdu_blob_send(state,
455 state->kdc_conn->conn->event.ctx,
456 stream,
457 4, /* initial_read_size */
458 packet_full_request_u32,
459 state);
460 if (req == NULL) {
461 kdc_tcp_next_proxy(state);
462 return;
465 tevent_req_set_callback(req, kdc_tcp_proxy_recv_done, state);
466 tevent_req_set_endtime(req, state->kdc->task->event_ctx,
467 timeval_current_ofs(state->kdc->proxy_timeout, 0));
473 called when name resolution for a proxy is done
475 static void kdc_tcp_proxy_resolve_done(struct composite_context *c)
477 struct kdc_tcp_proxy_state *state;
478 NTSTATUS status;
479 struct tevent_req *req;
480 struct tsocket_address *local_addr, *proxy_addr;
481 int ret;
483 state = talloc_get_type(c->async.private_data, struct kdc_tcp_proxy_state);
485 status = resolve_name_recv(c, state, &state->proxy_ip);
486 if (!NT_STATUS_IS_OK(status)) {
487 kdc_tcp_next_proxy(state);
488 return;
491 /* get an address for us to use locally */
492 ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0, &local_addr);
493 if (ret != 0) {
494 kdc_tcp_next_proxy(state);
495 return;
498 ret = tsocket_address_inet_from_strings(state, "ip",
499 state->proxy_ip, state->port, &proxy_addr);
500 if (ret != 0) {
501 kdc_tcp_next_proxy(state);
502 return;
505 /* connect to the proxy */
506 req = tstream_inet_tcp_connect_send(state, state->kdc->task->event_ctx, local_addr, proxy_addr);
507 if (req == NULL) {
508 kdc_tcp_next_proxy(state);
509 return;
512 tevent_req_set_callback(req, kdc_tcp_proxy_connect_done, state);
514 tevent_req_set_endtime(req, state->kdc->task->event_ctx,
515 timeval_current_ofs(state->kdc->proxy_timeout, 0));
517 DEBUG(4,("kdc_tcp_proxy: proxying request to %s\n", state->proxy_ip));
522 called when our proxies are not available
524 static void kdc_tcp_proxy_unavailable(struct kdc_tcp_proxy_state *state)
526 int kret;
527 krb5_data k5_error_blob;
528 struct tevent_req *req;
530 kret = krb5_mk_error(state->kdc->smb_krb5_context->krb5_context,
531 KRB5KDC_ERR_SVC_UNAVAILABLE, NULL, NULL,
532 NULL, NULL, NULL, NULL, &k5_error_blob);
533 if (kret != 0) {
534 DEBUG(2,(__location__ ": Unable to form krb5 error reply\n"));
535 talloc_free(state);
536 return;
540 state->call->out = data_blob_talloc(state, k5_error_blob.data, k5_error_blob.length);
541 krb5_data_free(&k5_error_blob);
542 if (!state->call->out.data) {
543 talloc_free(state);
544 return;
547 state->call->out_iov[0].iov_base = (char *)state->call->out.data;
548 state->call->out_iov[0].iov_len = state->call->out.length;
550 req = tstream_writev_queue_send(state,
551 state->kdc_conn->conn->event.ctx,
552 state->kdc_conn->tstream,
553 state->kdc_conn->send_queue,
554 state->call->out_iov, 1);
555 if (!req) {
556 talloc_free(state);
557 return;
560 tevent_req_set_callback(req, kdc_tcp_proxy_reply_done, state);
564 try the next proxy in the list
566 static void kdc_tcp_next_proxy(struct kdc_tcp_proxy_state *state)
568 const char *proxy_dnsname = state->proxy_list[state->next_proxy];
569 struct nbt_name name;
570 struct composite_context *c;
572 if (proxy_dnsname == NULL) {
573 kdc_tcp_proxy_unavailable(state);
574 return;
577 state->next_proxy++;
579 make_nbt_name(&name, proxy_dnsname, 0);
581 c = resolve_name_ex_send(lpcfg_resolve_context(state->kdc->task->lp_ctx),
582 state,
583 RESOLVE_NAME_FLAG_FORCE_DNS,
585 &name,
586 state->kdc->task->event_ctx);
587 if (c == NULL) {
588 kdc_tcp_next_proxy(state);
589 return;
591 c->async.fn = kdc_tcp_proxy_resolve_done;
592 c->async.private_data = state;
597 proxy a TCP kdc request to a writeable DC
599 void kdc_tcp_proxy(struct kdc_server *kdc, struct kdc_tcp_connection *kdc_conn,
600 struct kdc_tcp_call *call, uint16_t port)
602 struct kdc_tcp_proxy_state *state;
603 WERROR werr;
605 state = talloc_zero(kdc_conn, struct kdc_tcp_proxy_state);
607 state->call = talloc_steal(state, call);
608 state->kdc_conn = kdc_conn;
609 state->kdc = kdc;
610 state->port = port;
612 werr = kdc_proxy_get_writeable_dcs(kdc, state, &state->proxy_list);
613 if (!W_ERROR_IS_OK(werr)) {
614 kdc_tcp_proxy_unavailable(state);
615 return;
618 kdc_tcp_next_proxy(state);