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 */
finddcs_cldap_send(TALLOC_CTX * mem_ctx,struct finddcs * io,struct resolve_context * resolve_ctx,struct tevent_context * event_ctx)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 */
finddcs_cldap_ipaddress(struct finddcs_cldap_state * state,struct finddcs * io)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 */
finddcs_cldap_srv_lookup(struct finddcs_cldap_state * state,struct finddcs * io,struct resolve_context * resolve_ctx,struct tevent_context * event_ctx)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 */
finddcs_cldap_nbt_lookup(struct finddcs_cldap_state * state,struct finddcs * io,struct resolve_context * resolve_ctx,struct tevent_context * event_ctx)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
finddcs_cldap_name_lookup(struct finddcs_cldap_state * state,struct finddcs * io,struct resolve_context * resolve_ctx,struct tevent_context * event_ctx)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 */
finddcs_cldap_next_server(struct finddcs_cldap_state * state)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 */
finddcs_cldap_netlogon_replied(struct tevent_req * subreq)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
finddcs_cldap_name_resolved(struct composite_context * ctx)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 */
finddcs_cldap_nbt_resolved(struct composite_context * ctx)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 */
finddcs_cldap_srv_resolved(struct composite_context * ctx)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
finddcs_cldap_recv(struct tevent_req * req,TALLOC_CTX * mem_ctx,struct finddcs * io)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
finddcs_cldap(TALLOC_CTX * mem_ctx,struct finddcs * io,struct resolve_context * resolve_ctx,struct tevent_context * event_ctx)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 }
484