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