s4/scripting/bin: py2/py3 compatability always decode result of b64encode
[samba.git] / source4 / libcli / finddcs_cldap.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    a composite API for finding a DC and its name via CLDAP
5
6    Copyright (C) Andrew Tridgell 2010
7    Copyright (C) Andrew Bartlett 2010
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "include/includes.h"
24 #include <tevent.h>
25 #include "libcli/resolve/resolve.h"
26 #include "libcli/cldap/cldap.h"
27 #include "libcli/finddc.h"
28 #include "libcli/security/security.h"
29 #include "lib/util/tevent_ntstatus.h"
30 #include "lib/tsocket/tsocket.h"
31 #include "libcli/composite/composite.h"
32 #include "lib/util/util_net.h"
33
34 struct finddcs_cldap_state {
35         struct tevent_context *ev;
36         struct tevent_req *req;
37         const char *domain_name;
38         struct dom_sid *domain_sid;
39         const char *srv_name;
40         const char **srv_addresses;
41         uint32_t minimum_dc_flags;
42         uint32_t srv_address_index;
43         struct cldap_socket *cldap;
44         struct cldap_netlogon *netlogon;
45         NTSTATUS status;
46 };
47
48 static void finddcs_cldap_srv_resolved(struct composite_context *ctx);
49 static void finddcs_cldap_netlogon_replied(struct tevent_req *req);
50 static bool finddcs_cldap_srv_lookup(struct finddcs_cldap_state *state,
51                                      struct finddcs *io,
52                                      struct resolve_context *resolve_ctx,
53                                      struct tevent_context *event_ctx);
54 static bool finddcs_cldap_nbt_lookup(struct finddcs_cldap_state *state,
55                                      struct finddcs *io,
56                                      struct resolve_context *resolve_ctx,
57                                      struct tevent_context *event_ctx);
58 static void finddcs_cldap_nbt_resolved(struct composite_context *ctx);
59 static bool finddcs_cldap_name_lookup(struct finddcs_cldap_state *state,
60                                       struct finddcs *io,
61                                       struct resolve_context *resolve_ctx,
62                                       struct tevent_context *event_ctx);
63 static void finddcs_cldap_name_resolved(struct composite_context *ctx);
64 static void finddcs_cldap_next_server(struct finddcs_cldap_state *state);
65 static bool finddcs_cldap_ipaddress(struct finddcs_cldap_state *state, struct finddcs *io);
66
67
68 /*
69  * find a list of DCs via DNS/CLDAP
70  */
71 struct tevent_req *finddcs_cldap_send(TALLOC_CTX *mem_ctx,
72                                       struct finddcs *io,
73                                       struct resolve_context *resolve_ctx,
74                                       struct tevent_context *event_ctx)
75 {
76         struct finddcs_cldap_state *state;
77         struct tevent_req *req;
78
79         req = tevent_req_create(mem_ctx, &state, struct finddcs_cldap_state);
80         if (req == NULL) {
81                 return NULL;
82         }
83
84         state->req = req;
85         state->ev = event_ctx;
86         state->minimum_dc_flags = io->in.minimum_dc_flags;
87
88         if (io->in.domain_name) {
89                 state->domain_name = talloc_strdup(state, io->in.domain_name);
90                 if (tevent_req_nomem(state->domain_name, req)) {
91                         return tevent_req_post(req, event_ctx);
92                 }
93         } else {
94                 state->domain_name = NULL;
95         }
96
97         if (io->in.domain_sid) {
98                 state->domain_sid = dom_sid_dup(state, io->in.domain_sid);
99                 if (tevent_req_nomem(state->domain_sid, req)) {
100                         return tevent_req_post(req, event_ctx);
101                 }
102         } else {
103                 state->domain_sid = NULL;
104         }
105
106         if (io->in.server_address) {
107                 if (is_ipaddress(io->in.server_address)) {
108                         DEBUG(4,("finddcs: searching for a DC by IP %s\n",
109                                  io->in.server_address));
110                         if (!finddcs_cldap_ipaddress(state, io)) {
111                                 return tevent_req_post(req, event_ctx);
112                         }
113                 } else {
114                         if (!finddcs_cldap_name_lookup(state, io, resolve_ctx,
115                                                        event_ctx)) {
116                                 return tevent_req_post(req, event_ctx);
117                         }
118                 }
119         } else if (io->in.domain_name) {
120                 if (strchr(state->domain_name, '.')) {
121                         /* looks like a DNS name */
122                         DEBUG(4,("finddcs: searching for a DC by DNS domain %s\n", state->domain_name));
123                         if (!finddcs_cldap_srv_lookup(state, io, resolve_ctx,
124                                                       event_ctx)) {
125                                 return tevent_req_post(req, event_ctx);
126                         }
127                 } else {
128                         DEBUG(4,("finddcs: searching for a DC by NBT lookup %s\n", state->domain_name));
129                         if (!finddcs_cldap_nbt_lookup(state, io, resolve_ctx,
130                                                       event_ctx)) {
131                                 return tevent_req_post(req, event_ctx);
132                         }
133                 }
134         } else {
135                 /* either we have the domain name or the IP address */
136                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
137                 DEBUG(2,("finddcs: Please specify at least the domain name or the IP address! \n"));
138                 return tevent_req_post(req, event_ctx);
139         }
140
141         return req;
142 }
143
144
145 /*
146   we've been told the IP of the server, bypass name
147   resolution and go straight to CLDAP
148 */
149 static bool finddcs_cldap_ipaddress(struct finddcs_cldap_state *state, struct finddcs *io)
150 {
151         NTSTATUS status;
152
153         state->srv_addresses = talloc_array(state, const char *, 2);
154         if (tevent_req_nomem(state->srv_addresses, state->req)) {
155                 return false;
156         }
157         state->srv_addresses[0] = talloc_strdup(state->srv_addresses, io->in.server_address);
158         if (tevent_req_nomem(state->srv_addresses[0], state->req)) {
159                 return false;
160         }
161         state->srv_addresses[1] = NULL;
162         state->srv_address_index = 0;
163
164         finddcs_cldap_next_server(state);
165         return tevent_req_is_nterror(state->req, &status);
166 }
167
168 /*
169   start a SRV DNS lookup
170  */
171 static bool finddcs_cldap_srv_lookup(struct finddcs_cldap_state *state,
172                                      struct finddcs *io,
173                                      struct resolve_context *resolve_ctx,
174                                      struct tevent_context *event_ctx)
175 {
176         struct composite_context *creq;
177         struct nbt_name name;
178
179         if (io->in.site_name) {
180                 state->srv_name = talloc_asprintf(state, "_ldap._tcp.%s._sites.%s",
181                                            io->in.site_name, io->in.domain_name);
182         } else {
183                 state->srv_name = talloc_asprintf(state, "_ldap._tcp.%s", io->in.domain_name);
184         }
185
186         DEBUG(4,("finddcs: looking for SRV records for %s\n", state->srv_name));
187
188         make_nbt_name(&name, state->srv_name, 0);
189
190         creq = resolve_name_ex_send(resolve_ctx, state,
191                                     RESOLVE_NAME_FLAG_FORCE_DNS | RESOLVE_NAME_FLAG_DNS_SRV,
192                                     0, &name, event_ctx);
193         if (tevent_req_nomem(creq, state->req)) {
194                 return false;
195         }
196         creq->async.fn = finddcs_cldap_srv_resolved;
197         creq->async.private_data = state;
198
199         return true;
200 }
201
202 /*
203   start a NBT name lookup for domain<1C>
204  */
205 static bool finddcs_cldap_nbt_lookup(struct finddcs_cldap_state *state,
206                                      struct finddcs *io,
207                                      struct resolve_context *resolve_ctx,
208                                      struct tevent_context *event_ctx)
209 {
210         struct composite_context *creq;
211         struct nbt_name name;
212
213         make_nbt_name(&name, state->domain_name, NBT_NAME_LOGON);
214         creq = resolve_name_send(resolve_ctx, state, &name, event_ctx);
215         if (tevent_req_nomem(creq, state->req)) {
216                 return false;
217         }
218         creq->async.fn = finddcs_cldap_nbt_resolved;
219         creq->async.private_data = state;
220         return true;
221 }
222
223 static bool finddcs_cldap_name_lookup(struct finddcs_cldap_state *state,
224                                       struct finddcs *io,
225                                       struct resolve_context *resolve_ctx,
226                                       struct tevent_context *event_ctx)
227 {
228         struct composite_context *creq;
229         struct nbt_name name;
230
231         make_nbt_name(&name, io->in.server_address, NBT_NAME_SERVER);
232         creq = resolve_name_send(resolve_ctx, state, &name, event_ctx);
233         if (tevent_req_nomem(creq, state->req)) {
234                 return false;
235         }
236         creq->async.fn = finddcs_cldap_name_resolved;
237         creq->async.private_data = state;
238         return true;
239 }
240
241 /*
242   fire off a CLDAP query to the next server
243  */
244 static void finddcs_cldap_next_server(struct finddcs_cldap_state *state)
245 {
246         struct tevent_req *subreq;
247         struct tsocket_address *dest;
248         int ret;
249
250         TALLOC_FREE(state->cldap);
251
252         if (state->srv_addresses[state->srv_address_index] == NULL) {
253                 if (NT_STATUS_IS_OK(state->status)) {
254                         tevent_req_nterror(state->req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
255                 } else {
256                         tevent_req_nterror(state->req, state->status);
257                 }
258                 DEBUG(2,("finddcs: No matching CLDAP server found\n"));
259                 return;
260         }
261
262         /* we should get the port from the SRV response */
263         ret = tsocket_address_inet_from_strings(state, "ip",
264                                                 state->srv_addresses[state->srv_address_index],
265                                                 389,
266                                                 &dest);
267         if (ret == 0) {
268                 state->status = NT_STATUS_OK;
269         } else {
270                 state->status = map_nt_error_from_unix_common(errno);
271         }
272         if (!NT_STATUS_IS_OK(state->status)) {
273                 state->srv_address_index++;
274                 finddcs_cldap_next_server(state);
275                 return;
276         }
277
278         state->status = cldap_socket_init(state, NULL, dest, &state->cldap);
279         if (!NT_STATUS_IS_OK(state->status)) {
280                 state->srv_address_index++;
281                 finddcs_cldap_next_server(state);
282                 return;
283         }
284
285         TALLOC_FREE(state->netlogon);
286         state->netlogon = talloc_zero(state, struct cldap_netlogon);
287         if (state->netlogon == NULL) {
288                 state->status = NT_STATUS_NO_MEMORY;
289                 state->srv_address_index++;
290                 finddcs_cldap_next_server(state);
291                 return;
292         }
293
294         if ((state->domain_name != NULL) && (strchr(state->domain_name, '.'))) {
295                 state->netlogon->in.realm = state->domain_name;
296         }
297         if (state->domain_sid) {
298                 state->netlogon->in.domain_sid = dom_sid_string(state, state->domain_sid);
299                 if (state->netlogon->in.domain_sid == NULL) {
300                         state->status = NT_STATUS_NO_MEMORY;
301                         state->srv_address_index++;
302                         finddcs_cldap_next_server(state);
303                         return;
304                 }
305         }
306         state->netlogon->in.acct_control = -1;
307         state->netlogon->in.version =
308                 NETLOGON_NT_VERSION_5 |
309                 NETLOGON_NT_VERSION_5EX |
310                 NETLOGON_NT_VERSION_IP;
311         state->netlogon->in.map_response = true;
312
313         DEBUG(4,("finddcs: performing CLDAP query on %s\n",
314                  state->srv_addresses[state->srv_address_index]));
315
316         subreq = cldap_netlogon_send(state, state->ev,
317                                      state->cldap, state->netlogon);
318         if (subreq == NULL) {
319                 state->status = NT_STATUS_NO_MEMORY;
320                 state->srv_address_index++;
321                 finddcs_cldap_next_server(state);
322                 return;
323         }
324
325         tevent_req_set_callback(subreq, finddcs_cldap_netlogon_replied, state);
326 }
327
328
329 /*
330   we have a response from a CLDAP server for a netlogon request
331  */
332 static void finddcs_cldap_netlogon_replied(struct tevent_req *subreq)
333 {
334         struct finddcs_cldap_state *state;
335         NTSTATUS status;
336
337         state = tevent_req_callback_data(subreq, struct finddcs_cldap_state);
338
339         status = cldap_netlogon_recv(subreq, state->netlogon, state->netlogon);
340         TALLOC_FREE(subreq);
341         TALLOC_FREE(state->cldap);
342         if (!NT_STATUS_IS_OK(status)) {
343                 state->status = status;
344                 state->srv_address_index++;
345                 finddcs_cldap_next_server(state);
346                 return;
347         }
348         if (state->minimum_dc_flags !=
349             (state->minimum_dc_flags & state->netlogon->out.netlogon.data.nt5_ex.server_type)) {
350                 /* the server didn't match the minimum requirements */
351                 DEBUG(4,("finddcs: Skipping DC %s with server_type=0x%08x - required 0x%08x\n",
352                          state->srv_addresses[state->srv_address_index],
353                          state->netlogon->out.netlogon.data.nt5_ex.server_type,
354                          state->minimum_dc_flags));
355                 state->srv_address_index++;
356                 finddcs_cldap_next_server(state);
357                 return;
358         }
359
360         DEBUG(4,("finddcs: Found matching DC %s with server_type=0x%08x\n",
361                  state->srv_addresses[state->srv_address_index],
362                  state->netlogon->out.netlogon.data.nt5_ex.server_type));
363
364         tevent_req_done(state->req);
365 }
366
367 static void finddcs_cldap_name_resolved(struct composite_context *ctx)
368 {
369         struct finddcs_cldap_state *state =
370                 talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
371         NTSTATUS status;
372         unsigned i;
373
374         status = resolve_name_multiple_recv(ctx, state, &state->srv_addresses);
375         if (tevent_req_nterror(state->req, status)) {
376                 DEBUG(2,("finddcs: No matching server found\n"));
377                 return;
378         }
379
380         for (i=0; state->srv_addresses[i]; i++) {
381                 DEBUG(4,("finddcs: response %u at '%s'\n",
382                          i, state->srv_addresses[i]));
383         }
384
385         state->srv_address_index = 0;
386
387         state->status = NT_STATUS_OK;
388         finddcs_cldap_next_server(state);
389 }
390
391 /*
392    handle NBT name lookup reply
393  */
394 static void finddcs_cldap_nbt_resolved(struct composite_context *ctx)
395 {
396         struct finddcs_cldap_state *state =
397                 talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
398         NTSTATUS status;
399         unsigned i;
400
401         status = resolve_name_multiple_recv(ctx, state, &state->srv_addresses);
402         if (tevent_req_nterror(state->req, status)) {
403                 DEBUG(2,("finddcs: No matching NBT <1c> server found\n"));
404                 return;
405         }
406
407         for (i=0; state->srv_addresses[i]; i++) {
408                 DEBUG(4,("finddcs: NBT <1c> response %u at '%s'\n",
409                          i, state->srv_addresses[i]));
410         }
411
412         state->srv_address_index = 0;
413
414         finddcs_cldap_next_server(state);
415 }
416
417
418 /*
419  * Having got a DNS SRV answer, fire off the first CLDAP request
420  */
421 static void finddcs_cldap_srv_resolved(struct composite_context *ctx)
422 {
423         struct finddcs_cldap_state *state =
424                 talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
425         NTSTATUS status;
426         unsigned i;
427
428         status = resolve_name_multiple_recv(ctx, state, &state->srv_addresses);
429         if (tevent_req_nterror(state->req, status)) {
430                 DEBUG(2,("finddcs: Failed to find SRV record for %s\n", state->srv_name));
431                 return;
432         }
433
434         for (i=0; state->srv_addresses[i]; i++) {
435                 DEBUG(4,("finddcs: DNS SRV response %u at '%s'\n", i, state->srv_addresses[i]));
436         }
437
438         state->srv_address_index = 0;
439
440         state->status = NT_STATUS_OK;
441         finddcs_cldap_next_server(state);
442 }
443
444
445 NTSTATUS finddcs_cldap_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct finddcs *io)
446 {
447         struct finddcs_cldap_state *state = tevent_req_data(req, struct finddcs_cldap_state);
448         bool ok;
449         NTSTATUS status;
450
451         ok = tevent_req_poll(req, state->ev);
452         if (!ok) {
453                 talloc_free(req);
454                 return NT_STATUS_INTERNAL_ERROR;
455         }
456         if (tevent_req_is_nterror(req, &status)) {
457                 tevent_req_received(req);
458                 return status;
459         }
460
461         talloc_steal(mem_ctx, state->netlogon);
462         io->out.netlogon = state->netlogon->out.netlogon;
463         io->out.address = talloc_steal(
464                 mem_ctx, state->srv_addresses[state->srv_address_index]);
465
466         tevent_req_received(req);
467         return NT_STATUS_OK;
468 }
469
470 NTSTATUS finddcs_cldap(TALLOC_CTX *mem_ctx,
471                        struct finddcs *io,
472                        struct resolve_context *resolve_ctx,
473                        struct tevent_context *event_ctx)
474 {
475         NTSTATUS status;
476         struct tevent_req *req = finddcs_cldap_send(mem_ctx,
477                                                     io,
478                                                     resolve_ctx,
479                                                     event_ctx);
480         status = finddcs_cldap_recv(req, mem_ctx, io);
481         talloc_free(req);
482         return status;
483 }