idtree: fix overflow for v. large ids on allocation and removal
[Samba.git] / source4 / libcli / finddcs_cldap.c
blob1928243283b5df4506ab957bd56d38e381fc4f59
1 /*
2 Unix SMB/CIFS implementation.
4 a composite API for finding a DC and its name via CLDAP
6 Copyright (C) Andrew Tridgell 2010
7 Copyright (C) Andrew Bartlett 2010
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.
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.
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/>.
23 #include "include/includes.h"
24 #include <tevent.h>
25 #include "libcli/resolve/resolve.h"
26 #include "lib/messaging/messaging.h"
27 #include "libcli/libcli.h"
28 #include "libcli/cldap/cldap.h"
29 #include "libcli/finddc.h"
30 #include "libcli/security/security.h"
31 #include "lib/util/tevent_ntstatus.h"
32 #include "libcli/composite/composite.h"
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;
47 static void finddcs_cldap_srv_resolved(struct composite_context *ctx);
48 static void finddcs_cldap_netlogon_replied(struct tevent_req *req);
49 static bool finddcs_cldap_srv_lookup(struct finddcs_cldap_state *state,
50 struct finddcs *io,
51 struct resolve_context *resolve_ctx,
52 struct tevent_context *event_ctx);
53 static bool finddcs_cldap_nbt_lookup(struct finddcs_cldap_state *state,
54 struct finddcs *io,
55 struct resolve_context *resolve_ctx,
56 struct tevent_context *event_ctx);
57 static void finddcs_cldap_name_resolved(struct composite_context *ctx);
58 static void finddcs_cldap_next_server(struct finddcs_cldap_state *state);
59 static bool finddcs_cldap_ipaddress(struct finddcs_cldap_state *state, struct finddcs *io);
63 * find a list of DCs via DNS/CLDAP
66 struct tevent_req *finddcs_cldap_send(TALLOC_CTX *mem_ctx,
67 struct finddcs *io,
68 struct resolve_context *resolve_ctx,
69 struct tevent_context *event_ctx)
71 struct finddcs_cldap_state *state;
72 struct tevent_req *req;
74 req = tevent_req_create(mem_ctx, &state, struct finddcs_cldap_state);
75 if (req == NULL) {
76 return NULL;
79 state->req = req;
80 state->ev = event_ctx;
81 state->minimum_dc_flags = io->in.minimum_dc_flags;
82 state->domain_name = talloc_strdup(state, io->in.domain_name);
83 if (tevent_req_nomem(state->domain_name, req)) {
84 return tevent_req_post(req, event_ctx);
87 if (io->in.domain_sid) {
88 state->domain_sid = dom_sid_dup(state, io->in.domain_sid);
89 if (tevent_req_nomem(state->domain_sid, req)) {
90 return tevent_req_post(req, event_ctx);
92 } else {
93 state->domain_sid = NULL;
96 if (io->in.server_address) {
97 DEBUG(4,("finddcs: searching for a DC by IP %s\n", io->in.server_address));
98 if (!finddcs_cldap_ipaddress(state, io)) {
99 return tevent_req_post(req, event_ctx);
101 } else if (strchr(state->domain_name, '.')) {
102 /* looks like a DNS name */
103 DEBUG(4,("finddcs: searching for a DC by DNS domain %s\n", state->domain_name));
104 if (!finddcs_cldap_srv_lookup(state, io, resolve_ctx, event_ctx)) {
105 return tevent_req_post(req, event_ctx);
107 } else {
108 DEBUG(4,("finddcs: searching for a DC by NBT lookup %s\n", state->domain_name));
109 if (!finddcs_cldap_nbt_lookup(state, io, resolve_ctx, event_ctx)) {
110 return tevent_req_post(req, event_ctx);
114 return req;
119 we've been told the IP of the server, bypass name
120 resolution and go straight to CLDAP
122 static bool finddcs_cldap_ipaddress(struct finddcs_cldap_state *state, struct finddcs *io)
124 NTSTATUS status;
126 state->srv_addresses = talloc_array(state, const char *, 2);
127 if (tevent_req_nomem(state->srv_addresses, state->req)) {
128 return false;
130 state->srv_addresses[0] = talloc_strdup(state->srv_addresses, io->in.server_address);
131 if (tevent_req_nomem(state->srv_addresses[0], state->req)) {
132 return false;
134 state->srv_addresses[1] = NULL;
135 state->srv_address_index = 0;
136 status = cldap_socket_init(state, state->ev, NULL, NULL, &state->cldap);
137 if (tevent_req_nterror(state->req, status)) {
138 return false;
141 finddcs_cldap_next_server(state);
142 return tevent_req_is_nterror(state->req, &status);
146 start a SRV DNS lookup
148 static bool finddcs_cldap_srv_lookup(struct finddcs_cldap_state *state,
149 struct finddcs *io,
150 struct resolve_context *resolve_ctx,
151 struct tevent_context *event_ctx)
153 struct composite_context *creq;
154 struct nbt_name name;
156 if (io->in.site_name) {
157 state->srv_name = talloc_asprintf(state, "_ldap._tcp.%s._sites.%s",
158 io->in.site_name, io->in.domain_name);
159 } else {
160 state->srv_name = talloc_asprintf(state, "_ldap._tcp.%s", io->in.domain_name);
163 DEBUG(4,("finddcs: looking for SRV records for %s\n", state->srv_name));
165 make_nbt_name(&name, state->srv_name, 0);
167 creq = resolve_name_ex_send(resolve_ctx, state,
168 RESOLVE_NAME_FLAG_FORCE_DNS | RESOLVE_NAME_FLAG_DNS_SRV,
169 0, &name, event_ctx);
170 if (tevent_req_nomem(creq, state->req)) {
171 return false;
173 creq->async.fn = finddcs_cldap_srv_resolved;
174 creq->async.private_data = state;
176 return true;
180 start a NBT name lookup for domain<1C>
182 static bool finddcs_cldap_nbt_lookup(struct finddcs_cldap_state *state,
183 struct finddcs *io,
184 struct resolve_context *resolve_ctx,
185 struct tevent_context *event_ctx)
187 struct composite_context *creq;
188 struct nbt_name name;
190 make_nbt_name(&name, state->domain_name, NBT_NAME_LOGON);
191 creq = resolve_name_send(resolve_ctx, state, &name, event_ctx);
192 if (tevent_req_nomem(creq, state->req)) {
193 return false;
195 creq->async.fn = finddcs_cldap_name_resolved;
196 creq->async.private_data = state;
197 return true;
201 fire off a CLDAP query to the next server
203 static void finddcs_cldap_next_server(struct finddcs_cldap_state *state)
205 struct tevent_req *subreq;
207 if (state->srv_addresses[state->srv_address_index] == NULL) {
208 tevent_req_nterror(state->req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
209 DEBUG(2,("finddcs: No matching CLDAP server found\n"));
210 return;
213 state->netlogon = talloc_zero(state, struct cldap_netlogon);
214 if (tevent_req_nomem(state->netlogon, state->req)) {
215 return;
218 state->netlogon->in.dest_address = state->srv_addresses[state->srv_address_index];
219 /* we should get the port from the SRV response */
220 state->netlogon->in.dest_port = 389;
221 if (strchr(state->domain_name, '.')) {
222 state->netlogon->in.realm = state->domain_name;
224 if (state->domain_sid) {
225 state->netlogon->in.domain_sid = dom_sid_string(state, state->domain_sid);
226 if (tevent_req_nomem(state->netlogon->in.domain_sid, state->req)) {
227 return;
230 state->netlogon->in.acct_control = -1;
231 state->netlogon->in.version =
232 NETLOGON_NT_VERSION_5 |
233 NETLOGON_NT_VERSION_5EX |
234 NETLOGON_NT_VERSION_IP;
235 state->netlogon->in.map_response = true;
237 DEBUG(4,("finddcs: performing CLDAP query on %s\n", state->netlogon->in.dest_address));
239 subreq = cldap_netlogon_send(state, state->cldap, state->netlogon);
240 if (tevent_req_nomem(subreq, state->req)) {
241 return;
244 tevent_req_set_callback(subreq, finddcs_cldap_netlogon_replied, state);
249 we have a response from a CLDAP server for a netlogon request
251 static void finddcs_cldap_netlogon_replied(struct tevent_req *subreq)
253 struct finddcs_cldap_state *state;
254 NTSTATUS status;
256 state = tevent_req_callback_data(subreq, struct finddcs_cldap_state);
258 status = cldap_netlogon_recv(subreq, state->netlogon, state->netlogon);
259 talloc_free(subreq);
260 if (!NT_STATUS_IS_OK(status)) {
261 state->srv_address_index++;
262 finddcs_cldap_next_server(state);
263 return;
265 if (state->minimum_dc_flags !=
266 (state->minimum_dc_flags & state->netlogon->out.netlogon.data.nt5_ex.server_type)) {
267 /* the server didn't match the minimum requirements */
268 DEBUG(4,("finddcs: Skipping DC %s with server_type=0x%08x - required 0x%08x\n",
269 state->srv_addresses[state->srv_address_index],
270 state->netlogon->out.netlogon.data.nt5_ex.server_type,
271 state->minimum_dc_flags));
272 state->srv_address_index++;
273 finddcs_cldap_next_server(state);
274 return;
277 DEBUG(4,("finddcs: Found matching DC %s with server_type=0x%08x\n",
278 state->srv_addresses[state->srv_address_index],
279 state->netlogon->out.netlogon.data.nt5_ex.server_type));
281 tevent_req_done(state->req);
285 handle NBT name lookup reply
287 static void finddcs_cldap_name_resolved(struct composite_context *ctx)
289 struct finddcs_cldap_state *state =
290 talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
291 const char *address;
292 NTSTATUS status;
294 status = resolve_name_recv(ctx, state, &address);
295 if (tevent_req_nterror(state->req, status)) {
296 DEBUG(2,("finddcs: No matching NBT <1c> server found\n"));
297 return;
300 DEBUG(4,("finddcs: Found NBT <1c> server at %s\n", address));
302 state->srv_addresses = talloc_array(state, const char *, 2);
303 if (tevent_req_nomem(state->srv_addresses, state->req)) {
304 return;
306 state->srv_addresses[0] = address;
307 state->srv_addresses[1] = NULL;
309 state->srv_address_index = 0;
311 status = cldap_socket_init(state, state->ev, NULL, NULL, &state->cldap);
312 if (tevent_req_nterror(state->req, status)) {
313 return;
316 finddcs_cldap_next_server(state);
321 * Having got a DNS SRV answer, fire off the first CLDAP request
323 static void finddcs_cldap_srv_resolved(struct composite_context *ctx)
325 struct finddcs_cldap_state *state =
326 talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
327 NTSTATUS status;
328 unsigned i;
330 status = resolve_name_multiple_recv(ctx, state, &state->srv_addresses);
331 if (tevent_req_nterror(state->req, status)) {
332 DEBUG(2,("finddcs: Failed to find SRV record for %s\n", state->srv_name));
333 return;
336 for (i=0; state->srv_addresses[i]; i++) {
337 DEBUG(4,("finddcs: DNS SRV response %u at '%s'\n", i, state->srv_addresses[i]));
340 state->srv_address_index = 0;
342 status = cldap_socket_init(state, state->ev, NULL, NULL, &state->cldap);
343 if (tevent_req_nterror(state->req, status)) {
344 return;
347 finddcs_cldap_next_server(state);
351 NTSTATUS finddcs_cldap_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct finddcs *io)
353 struct finddcs_cldap_state *state = tevent_req_data(req, struct finddcs_cldap_state);
354 bool ok;
355 NTSTATUS status;
357 ok = tevent_req_poll(req, state->ev);
358 if (!ok) {
359 talloc_free(req);
360 return NT_STATUS_INTERNAL_ERROR;
362 status = tevent_req_simple_recv_ntstatus(req);
363 if (NT_STATUS_IS_OK(status)) {
364 talloc_steal(mem_ctx, state->netlogon);
365 io->out.netlogon = state->netlogon->out.netlogon;
366 io->out.address = talloc_steal(mem_ctx, state->srv_addresses[state->srv_address_index]);
368 tevent_req_received(req);
369 return status;
372 NTSTATUS finddcs_cldap(TALLOC_CTX *mem_ctx,
373 struct finddcs *io,
374 struct resolve_context *resolve_ctx,
375 struct tevent_context *event_ctx)
377 NTSTATUS status;
378 struct tevent_req *req = finddcs_cldap_send(mem_ctx,
380 resolve_ctx,
381 event_ctx);
382 status = finddcs_cldap_recv(req, mem_ctx, io);
383 talloc_free(req);
384 return status;