source: trunk/server/source4/kdc/proxy.c

Last change on this file was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

File size: 15.2 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3
4 KDC Server request proxying
5
6 Copyright (C) Andrew Tridgell 2010
7 Copyright (C) Andrew Bartlett 2010
8 Copyright (C) Stefan Metzmacher 2011
9
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.
14
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.
19
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/>.
22*/
23
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"
34
35
36/*
37 get a list of our replication partners from repsFrom, returning it in *proxy_list
38 */
39static WERROR kdc_proxy_get_writeable_dcs(struct kdc_server *kdc, TALLOC_CTX *mem_ctx, char ***proxy_list)
40{
41 WERROR werr;
42 uint32_t count, i;
43 struct repsFromToBlob *reps;
44
45 werr = dsdb_loadreps(kdc->samdb, mem_ctx, ldb_get_default_basedn(kdc->samdb), "repsFrom", &reps, &count);
46 W_ERROR_NOT_OK_RETURN(werr);
47
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;
54 }
55
56 (*proxy_list) = talloc_array(mem_ctx, char *, count+1);
57 W_ERROR_HAVE_NO_MEMORY_AND_FREE(*proxy_list, reps);
58
59 talloc_steal(*proxy_list, reps);
60
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;
67 }
68 (*proxy_list)[i] = talloc_strdup(*proxy_list, dns_name);
69 W_ERROR_HAVE_NO_MEMORY_AND_FREE((*proxy_list)[i], *proxy_list);
70 }
71 (*proxy_list)[i] = NULL;
72
73 talloc_free(reps);
74
75 return WERR_OK;
76}
77
78
79struct 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;
92};
93
94
95static void kdc_udp_next_proxy(struct tevent_req *req);
96
97struct 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)
102{
103 struct tevent_req *req;
104 struct kdc_udp_proxy_state *state;
105 WERROR werr;
106
107 req = tevent_req_create(mem_ctx, &state,
108 struct kdc_udp_proxy_state);
109 if (req == NULL) {
110 return NULL;
111 }
112 state->ev = ev;
113 state->kdc = kdc;
114 state->port = port;
115 state->in = in;
116
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);
122 }
123
124 kdc_udp_next_proxy(req);
125 if (!tevent_req_is_in_progress(req)) {
126 return tevent_req_post(req, ev);
127 }
128
129 return req;
130}
131
132static void kdc_udp_proxy_resolve_done(struct composite_context *csubreq);
133
134/*
135 try the next proxy in the list
136 */
137static void kdc_udp_next_proxy(struct tevent_req *req)
138{
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;
144
145 if (proxy_dnsname == NULL) {
146 tevent_req_nterror(req, NT_STATUS_NO_LOGON_SERVERS);
147 return;
148 }
149
150 state->next_proxy++;
151
152 /* make sure we close the socket of the last try */
153 TALLOC_FREE(state->proxy.dgram);
154 ZERO_STRUCT(state->proxy);
155
156 make_nbt_name(&state->proxy.name, proxy_dnsname, 0);
157
158 csubreq = resolve_name_ex_send(lpcfg_resolve_context(state->kdc->task->lp_ctx),
159 state,
160 RESOLVE_NAME_FLAG_FORCE_DNS,
161 0,
162 &state->proxy.name,
163 state->ev);
164 if (tevent_req_nomem(csubreq, req)) {
165 return;
166 }
167 csubreq->async.fn = kdc_udp_proxy_resolve_done;
168 csubreq->async.private_data = req;
169}
170
171static void kdc_udp_proxy_sendto_done(struct tevent_req *subreq);
172static void kdc_udp_proxy_recvfrom_done(struct tevent_req *subreq);
173
174static void kdc_udp_proxy_resolve_done(struct composite_context *csubreq)
175{
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;
186
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;
193 }
194
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;
200 }
201
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;
209 }
210
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;
217 }
218
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;
227 }
228 tevent_req_set_callback(subreq, kdc_udp_proxy_sendto_done, req);
229
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;
234 }
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));
238
239 DEBUG(4,("kdc_udp_proxy: proxying request to %s[%s]\n",
240 state->proxy.name.name, state->proxy.ip));
241}
242
243/*
244 called when the send of the call to the proxy is complete
245 this is used to get an errors from the sendto()
246 */
247static void kdc_udp_proxy_sendto_done(struct tevent_req *subreq)
248{
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;
257
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);
265 }
266}
267
268/*
269 called when the proxy replies
270 */
271static void kdc_udp_proxy_recvfrom_done(struct tevent_req *subreq)
272{
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;
282
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;
292 }
293
294 /*
295 * Check the reply came from the right IP?
296 * As we use connected udp sockets, that should not be needed...
297 */
298
299 state->out.length = len;
300 state->out.data = buf;
301
302 tevent_req_done(req);
303}
304
305NTSTATUS kdc_udp_proxy_recv(struct tevent_req *req,
306 TALLOC_CTX *mem_ctx,
307 DATA_BLOB *out)
308{
309 struct kdc_udp_proxy_state *state =
310 tevent_req_data(req,
311 struct kdc_udp_proxy_state);
312 NTSTATUS status;
313
314 if (tevent_req_is_nterror(req, &status)) {
315 tevent_req_received(req);
316 return status;
317 }
318
319 out->data = talloc_move(mem_ctx, &state->out.data);
320 out->length = state->out.length;
321
322 tevent_req_received(req);
323 return NT_STATUS_OK;
324}
325
326struct kdc_tcp_proxy_state {
327 struct tevent_context *ev;
328 struct kdc_server *kdc;
329 uint16_t port;
330 DATA_BLOB in;
331 uint8_t in_hdr[4];
332 struct iovec in_iov[2];
333 DATA_BLOB out;
334 char **proxy_list;
335 uint32_t next_proxy;
336 struct {
337 struct nbt_name name;
338 const char *ip;
339 struct tstream_context *stream;
340 } proxy;
341};
342
343static void kdc_tcp_next_proxy(struct tevent_req *req);
344
345struct tevent_req *kdc_tcp_proxy_send(TALLOC_CTX *mem_ctx,
346 struct tevent_context *ev,
347 struct kdc_server *kdc,
348 uint16_t port,
349 DATA_BLOB in)
350{
351 struct tevent_req *req;
352 struct kdc_tcp_proxy_state *state;
353 WERROR werr;
354
355 req = tevent_req_create(mem_ctx, &state,
356 struct kdc_tcp_proxy_state);
357 if (req == NULL) {
358 return NULL;
359 }
360 state->ev = ev;
361 state->kdc = kdc;
362 state->port = port;
363 state->in = in;
364
365 werr = kdc_proxy_get_writeable_dcs(kdc, state, &state->proxy_list);
366 if (!W_ERROR_IS_OK(werr)) {
367 NTSTATUS status = werror_to_ntstatus(werr);
368 tevent_req_nterror(req, status);
369 return tevent_req_post(req, ev);
370 }
371
372 RSIVAL(state->in_hdr, 0, state->in.length);
373 state->in_iov[0].iov_base = (char *)state->in_hdr;
374 state->in_iov[0].iov_len = 4;
375 state->in_iov[1].iov_base = (char *)state->in.data;
376 state->in_iov[1].iov_len = state->in.length;
377
378 kdc_tcp_next_proxy(req);
379 if (!tevent_req_is_in_progress(req)) {
380 return tevent_req_post(req, ev);
381 }
382
383 return req;
384}
385
386static void kdc_tcp_proxy_resolve_done(struct composite_context *csubreq);
387
388/*
389 try the next proxy in the list
390 */
391static void kdc_tcp_next_proxy(struct tevent_req *req)
392{
393 struct kdc_tcp_proxy_state *state =
394 tevent_req_data(req,
395 struct kdc_tcp_proxy_state);
396 const char *proxy_dnsname = state->proxy_list[state->next_proxy];
397 struct composite_context *csubreq;
398
399 if (proxy_dnsname == NULL) {
400 tevent_req_nterror(req, NT_STATUS_NO_LOGON_SERVERS);
401 return;
402 }
403
404 state->next_proxy++;
405
406 /* make sure we close the socket of the last try */
407 TALLOC_FREE(state->proxy.stream);
408 ZERO_STRUCT(state->proxy);
409
410 make_nbt_name(&state->proxy.name, proxy_dnsname, 0);
411
412 csubreq = resolve_name_ex_send(lpcfg_resolve_context(state->kdc->task->lp_ctx),
413 state,
414 RESOLVE_NAME_FLAG_FORCE_DNS,
415 0,
416 &state->proxy.name,
417 state->ev);
418 if (tevent_req_nomem(csubreq, req)) {
419 return;
420 }
421 csubreq->async.fn = kdc_tcp_proxy_resolve_done;
422 csubreq->async.private_data = req;
423}
424
425static void kdc_tcp_proxy_connect_done(struct tevent_req *subreq);
426
427static void kdc_tcp_proxy_resolve_done(struct composite_context *csubreq)
428{
429 struct tevent_req *req =
430 talloc_get_type_abort(csubreq->async.private_data,
431 struct tevent_req);
432 struct kdc_tcp_proxy_state *state =
433 tevent_req_data(req,
434 struct kdc_tcp_proxy_state);
435 NTSTATUS status;
436 struct tevent_req *subreq;
437 struct tsocket_address *local_addr, *proxy_addr;
438 int ret;
439
440 status = resolve_name_recv(csubreq, state, &state->proxy.ip);
441 if (!NT_STATUS_IS_OK(status)) {
442 DEBUG(0,("Unable to resolve proxy[%s] - %s\n",
443 state->proxy.name.name, nt_errstr(status)));
444 kdc_tcp_next_proxy(req);
445 return;
446 }
447
448 /* get an address for us to use locally */
449 ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0, &local_addr);
450 if (ret != 0) {
451 kdc_tcp_next_proxy(req);
452 return;
453 }
454
455 ret = tsocket_address_inet_from_strings(state, "ip",
456 state->proxy.ip,
457 state->port,
458 &proxy_addr);
459 if (ret != 0) {
460 kdc_tcp_next_proxy(req);
461 return;
462 }
463
464 subreq = tstream_inet_tcp_connect_send(state, state->ev,
465 local_addr, proxy_addr);
466 if (tevent_req_nomem(subreq, req)) {
467 return;
468 }
469 tevent_req_set_callback(subreq, kdc_tcp_proxy_connect_done, req);
470 tevent_req_set_endtime(subreq, state->ev,
471 timeval_current_ofs(state->kdc->proxy_timeout, 0));
472}
473
474static void kdc_tcp_proxy_writev_done(struct tevent_req *subreq);
475static void kdc_tcp_proxy_read_pdu_done(struct tevent_req *subreq);
476
477static void kdc_tcp_proxy_connect_done(struct tevent_req *subreq)
478{
479 struct tevent_req *req =
480 tevent_req_callback_data(subreq,
481 struct tevent_req);
482 struct kdc_tcp_proxy_state *state =
483 tevent_req_data(req,
484 struct kdc_tcp_proxy_state);
485 int ret, sys_errno;
486
487 ret = tstream_inet_tcp_connect_recv(subreq, &sys_errno,
488 state, &state->proxy.stream, NULL);
489 TALLOC_FREE(subreq);
490 if (ret != 0) {
491 kdc_tcp_next_proxy(req);
492 return;
493 }
494
495 subreq = tstream_writev_send(state,
496 state->ev,
497 state->proxy.stream,
498 state->in_iov, 2);
499 if (tevent_req_nomem(subreq, req)) {
500 return;
501 }
502 tevent_req_set_callback(subreq, kdc_tcp_proxy_writev_done, req);
503
504 subreq = tstream_read_pdu_blob_send(state,
505 state->ev,
506 state->proxy.stream,
507 4, /* initial_read_size */
508 packet_full_request_u32,
509 req);
510 if (tevent_req_nomem(subreq, req)) {
511 return;
512 }
513 tevent_req_set_callback(subreq, kdc_tcp_proxy_read_pdu_done, req);
514 tevent_req_set_endtime(subreq, state->kdc->task->event_ctx,
515 timeval_current_ofs(state->kdc->proxy_timeout, 0));
516
517 DEBUG(4,("kdc_tcp_proxy: proxying request to %s[%s]\n",
518 state->proxy.name.name, state->proxy.ip));
519}
520
521static void kdc_tcp_proxy_writev_done(struct tevent_req *subreq)
522{
523 struct tevent_req *req =
524 tevent_req_callback_data(subreq,
525 struct tevent_req);
526 int ret, sys_errno;
527
528 ret = tstream_writev_recv(subreq, &sys_errno);
529 TALLOC_FREE(subreq);
530 if (ret == -1) {
531 kdc_tcp_next_proxy(req);
532 }
533}
534
535static void kdc_tcp_proxy_read_pdu_done(struct tevent_req *subreq)
536{
537 struct tevent_req *req =
538 tevent_req_callback_data(subreq,
539 struct tevent_req);
540 struct kdc_tcp_proxy_state *state =
541 tevent_req_data(req,
542 struct kdc_tcp_proxy_state);
543 NTSTATUS status;
544 DATA_BLOB raw;
545
546 status = tstream_read_pdu_blob_recv(subreq, state, &raw);
547 TALLOC_FREE(subreq);
548 if (!NT_STATUS_IS_OK(status)) {
549 kdc_tcp_next_proxy(req);
550 return;
551 }
552
553 /*
554 * raw blob has the length in the first 4 bytes,
555 * which we do not need here.
556 */
557 state->out = data_blob_talloc(state, raw.data + 4, raw.length - 4);
558 if (state->out.length != raw.length - 4) {
559 tevent_req_nomem(NULL, req);
560 return;
561 }
562
563 tevent_req_done(req);
564}
565
566NTSTATUS kdc_tcp_proxy_recv(struct tevent_req *req,
567 TALLOC_CTX *mem_ctx,
568 DATA_BLOB *out)
569{
570 struct kdc_tcp_proxy_state *state =
571 tevent_req_data(req,
572 struct kdc_tcp_proxy_state);
573 NTSTATUS status;
574
575 if (tevent_req_is_nterror(req, &status)) {
576 tevent_req_received(req);
577 return status;
578 }
579
580 out->data = talloc_move(mem_ctx, &state->out.data);
581 out->length = state->out.length;
582
583 tevent_req_received(req);
584 return NT_STATUS_OK;
585}
Note: See TracBrowser for help on using the repository browser.