smbd: Reduce the complexity of open_file_ntcreate
[mat/samba.git] / source4 / kdc / proxy.c
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  */
39 static 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
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;
92 };
93
94
95 static void kdc_udp_next_proxy(struct tevent_req *req);
96
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)
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
132 static void kdc_udp_proxy_resolve_done(struct composite_context *csubreq);
133
134 /*
135   try the next proxy in the list
136  */
137 static 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
171 static void kdc_udp_proxy_sendto_done(struct tevent_req *subreq);
172 static void kdc_udp_proxy_recvfrom_done(struct tevent_req *subreq);
173
174 static 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  */
247 static 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  */
271 static 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
305 NTSTATUS 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
326 struct 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
343 static void kdc_tcp_next_proxy(struct tevent_req *req);
344
345 struct 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
386 static void kdc_tcp_proxy_resolve_done(struct composite_context *csubreq);
387
388 /*
389   try the next proxy in the list
390  */
391 static 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
425 static void kdc_tcp_proxy_connect_done(struct tevent_req *subreq);
426
427 static 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
474 static void kdc_tcp_proxy_writev_done(struct tevent_req *subreq);
475 static void kdc_tcp_proxy_read_pdu_done(struct tevent_req *subreq);
476
477 static 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
521 static 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
535 static 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_oom(req);
560                 return;
561         }
562
563         tevent_req_done(req);
564 }
565
566 NTSTATUS 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 }