WHATSNEW: Add changes since 3.6.13.
[Samba.git] / source4 / libcli / finddcs_cldap.c
blob293641b37fec52879da4c38eefeb67b56949daba
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 "libcli/cldap/cldap.h"
27 #include "libcli/finddc.h"
28 #include "libcli/security/security.h"
29 #include "lib/util/tevent_ntstatus.h"
30 #include "libcli/composite/composite.h"
32 struct finddcs_cldap_state {
33 struct tevent_context *ev;
34 struct tevent_req *req;
35 const char *domain_name;
36 struct dom_sid *domain_sid;
37 const char *srv_name;
38 const char **srv_addresses;
39 uint32_t minimum_dc_flags;
40 uint32_t srv_address_index;
41 struct cldap_socket *cldap;
42 struct cldap_netlogon *netlogon;
45 static void finddcs_cldap_srv_resolved(struct composite_context *ctx);
46 static void finddcs_cldap_netlogon_replied(struct tevent_req *req);
47 static bool finddcs_cldap_srv_lookup(struct finddcs_cldap_state *state,
48 struct finddcs *io,
49 struct resolve_context *resolve_ctx,
50 struct tevent_context *event_ctx);
51 static bool finddcs_cldap_nbt_lookup(struct finddcs_cldap_state *state,
52 struct finddcs *io,
53 struct resolve_context *resolve_ctx,
54 struct tevent_context *event_ctx);
55 static void finddcs_cldap_name_resolved(struct composite_context *ctx);
56 static void finddcs_cldap_next_server(struct finddcs_cldap_state *state);
57 static bool finddcs_cldap_ipaddress(struct finddcs_cldap_state *state, struct finddcs *io);
61 * find a list of DCs via DNS/CLDAP
64 struct tevent_req *finddcs_cldap_send(TALLOC_CTX *mem_ctx,
65 struct finddcs *io,
66 struct resolve_context *resolve_ctx,
67 struct tevent_context *event_ctx)
69 struct finddcs_cldap_state *state;
70 struct tevent_req *req;
72 req = tevent_req_create(mem_ctx, &state, struct finddcs_cldap_state);
73 if (req == NULL) {
74 return NULL;
77 state->req = req;
78 state->ev = event_ctx;
79 state->minimum_dc_flags = io->in.minimum_dc_flags;
80 state->domain_name = talloc_strdup(state, io->in.domain_name);
81 if (tevent_req_nomem(state->domain_name, req)) {
82 return tevent_req_post(req, event_ctx);
85 if (io->in.domain_sid) {
86 state->domain_sid = dom_sid_dup(state, io->in.domain_sid);
87 if (tevent_req_nomem(state->domain_sid, req)) {
88 return tevent_req_post(req, event_ctx);
90 } else {
91 state->domain_sid = NULL;
94 if (io->in.server_address) {
95 DEBUG(4,("finddcs: searching for a DC by IP %s\n", io->in.server_address));
96 if (!finddcs_cldap_ipaddress(state, io)) {
97 return tevent_req_post(req, event_ctx);
99 } else if (strchr(state->domain_name, '.')) {
100 /* looks like a DNS name */
101 DEBUG(4,("finddcs: searching for a DC by DNS domain %s\n", state->domain_name));
102 if (!finddcs_cldap_srv_lookup(state, io, resolve_ctx, event_ctx)) {
103 return tevent_req_post(req, event_ctx);
105 } else {
106 DEBUG(4,("finddcs: searching for a DC by NBT lookup %s\n", state->domain_name));
107 if (!finddcs_cldap_nbt_lookup(state, io, resolve_ctx, event_ctx)) {
108 return tevent_req_post(req, event_ctx);
112 return req;
117 we've been told the IP of the server, bypass name
118 resolution and go straight to CLDAP
120 static bool finddcs_cldap_ipaddress(struct finddcs_cldap_state *state, struct finddcs *io)
122 NTSTATUS status;
124 state->srv_addresses = talloc_array(state, const char *, 2);
125 if (tevent_req_nomem(state->srv_addresses, state->req)) {
126 return false;
128 state->srv_addresses[0] = talloc_strdup(state->srv_addresses, io->in.server_address);
129 if (tevent_req_nomem(state->srv_addresses[0], state->req)) {
130 return false;
132 state->srv_addresses[1] = NULL;
133 state->srv_address_index = 0;
134 status = cldap_socket_init(state, state->ev, NULL, NULL, &state->cldap);
135 if (tevent_req_nterror(state->req, status)) {
136 return false;
139 finddcs_cldap_next_server(state);
140 return tevent_req_is_nterror(state->req, &status);
144 start a SRV DNS lookup
146 static bool finddcs_cldap_srv_lookup(struct finddcs_cldap_state *state,
147 struct finddcs *io,
148 struct resolve_context *resolve_ctx,
149 struct tevent_context *event_ctx)
151 struct composite_context *creq;
152 struct nbt_name name;
154 if (io->in.site_name) {
155 state->srv_name = talloc_asprintf(state, "_ldap._tcp.%s._sites.%s",
156 io->in.site_name, io->in.domain_name);
157 } else {
158 state->srv_name = talloc_asprintf(state, "_ldap._tcp.%s", io->in.domain_name);
161 DEBUG(4,("finddcs: looking for SRV records for %s\n", state->srv_name));
163 make_nbt_name(&name, state->srv_name, 0);
165 creq = resolve_name_ex_send(resolve_ctx, state,
166 RESOLVE_NAME_FLAG_FORCE_DNS | RESOLVE_NAME_FLAG_DNS_SRV,
167 0, &name, event_ctx);
168 if (tevent_req_nomem(creq, state->req)) {
169 return false;
171 creq->async.fn = finddcs_cldap_srv_resolved;
172 creq->async.private_data = state;
174 return true;
178 start a NBT name lookup for domain<1C>
180 static bool finddcs_cldap_nbt_lookup(struct finddcs_cldap_state *state,
181 struct finddcs *io,
182 struct resolve_context *resolve_ctx,
183 struct tevent_context *event_ctx)
185 struct composite_context *creq;
186 struct nbt_name name;
188 make_nbt_name(&name, state->domain_name, NBT_NAME_LOGON);
189 creq = resolve_name_send(resolve_ctx, state, &name, event_ctx);
190 if (tevent_req_nomem(creq, state->req)) {
191 return false;
193 creq->async.fn = finddcs_cldap_name_resolved;
194 creq->async.private_data = state;
195 return true;
199 fire off a CLDAP query to the next server
201 static void finddcs_cldap_next_server(struct finddcs_cldap_state *state)
203 struct tevent_req *subreq;
205 if (state->srv_addresses[state->srv_address_index] == NULL) {
206 tevent_req_nterror(state->req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
207 DEBUG(2,("finddcs: No matching CLDAP server found\n"));
208 return;
211 state->netlogon = talloc_zero(state, struct cldap_netlogon);
212 if (tevent_req_nomem(state->netlogon, state->req)) {
213 return;
216 state->netlogon->in.dest_address = state->srv_addresses[state->srv_address_index];
217 /* we should get the port from the SRV response */
218 state->netlogon->in.dest_port = 389;
219 if (strchr(state->domain_name, '.')) {
220 state->netlogon->in.realm = state->domain_name;
222 if (state->domain_sid) {
223 state->netlogon->in.domain_sid = dom_sid_string(state, state->domain_sid);
224 if (tevent_req_nomem(state->netlogon->in.domain_sid, state->req)) {
225 return;
228 state->netlogon->in.acct_control = -1;
229 state->netlogon->in.version =
230 NETLOGON_NT_VERSION_5 |
231 NETLOGON_NT_VERSION_5EX |
232 NETLOGON_NT_VERSION_IP;
233 state->netlogon->in.map_response = true;
235 DEBUG(4,("finddcs: performing CLDAP query on %s\n", state->netlogon->in.dest_address));
237 subreq = cldap_netlogon_send(state, state->cldap, state->netlogon);
238 if (tevent_req_nomem(subreq, state->req)) {
239 return;
242 tevent_req_set_callback(subreq, finddcs_cldap_netlogon_replied, state);
247 we have a response from a CLDAP server for a netlogon request
249 static void finddcs_cldap_netlogon_replied(struct tevent_req *subreq)
251 struct finddcs_cldap_state *state;
252 NTSTATUS status;
254 state = tevent_req_callback_data(subreq, struct finddcs_cldap_state);
256 status = cldap_netlogon_recv(subreq, state->netlogon, state->netlogon);
257 talloc_free(subreq);
258 if (!NT_STATUS_IS_OK(status)) {
259 state->srv_address_index++;
260 finddcs_cldap_next_server(state);
261 return;
263 if (state->minimum_dc_flags !=
264 (state->minimum_dc_flags & state->netlogon->out.netlogon.data.nt5_ex.server_type)) {
265 /* the server didn't match the minimum requirements */
266 DEBUG(4,("finddcs: Skipping DC %s with server_type=0x%08x - required 0x%08x\n",
267 state->srv_addresses[state->srv_address_index],
268 state->netlogon->out.netlogon.data.nt5_ex.server_type,
269 state->minimum_dc_flags));
270 state->srv_address_index++;
271 finddcs_cldap_next_server(state);
272 return;
275 DEBUG(4,("finddcs: Found matching DC %s with server_type=0x%08x\n",
276 state->srv_addresses[state->srv_address_index],
277 state->netlogon->out.netlogon.data.nt5_ex.server_type));
279 tevent_req_done(state->req);
283 handle NBT name lookup reply
285 static void finddcs_cldap_name_resolved(struct composite_context *ctx)
287 struct finddcs_cldap_state *state =
288 talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
289 const char *address;
290 NTSTATUS status;
292 status = resolve_name_recv(ctx, state, &address);
293 if (tevent_req_nterror(state->req, status)) {
294 DEBUG(2,("finddcs: No matching NBT <1c> server found\n"));
295 return;
298 DEBUG(4,("finddcs: Found NBT <1c> server at %s\n", address));
300 state->srv_addresses = talloc_array(state, const char *, 2);
301 if (tevent_req_nomem(state->srv_addresses, state->req)) {
302 return;
304 state->srv_addresses[0] = address;
305 state->srv_addresses[1] = NULL;
307 state->srv_address_index = 0;
309 status = cldap_socket_init(state, state->ev, NULL, NULL, &state->cldap);
310 if (tevent_req_nterror(state->req, status)) {
311 return;
314 finddcs_cldap_next_server(state);
319 * Having got a DNS SRV answer, fire off the first CLDAP request
321 static void finddcs_cldap_srv_resolved(struct composite_context *ctx)
323 struct finddcs_cldap_state *state =
324 talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
325 NTSTATUS status;
326 unsigned i;
328 status = resolve_name_multiple_recv(ctx, state, &state->srv_addresses);
329 if (tevent_req_nterror(state->req, status)) {
330 DEBUG(2,("finddcs: Failed to find SRV record for %s\n", state->srv_name));
331 return;
334 for (i=0; state->srv_addresses[i]; i++) {
335 DEBUG(4,("finddcs: DNS SRV response %u at '%s'\n", i, state->srv_addresses[i]));
338 state->srv_address_index = 0;
340 status = cldap_socket_init(state, state->ev, NULL, NULL, &state->cldap);
341 if (tevent_req_nterror(state->req, status)) {
342 return;
345 finddcs_cldap_next_server(state);
349 NTSTATUS finddcs_cldap_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct finddcs *io)
351 struct finddcs_cldap_state *state = tevent_req_data(req, struct finddcs_cldap_state);
352 bool ok;
353 NTSTATUS status;
355 ok = tevent_req_poll(req, state->ev);
356 if (!ok) {
357 talloc_free(req);
358 return NT_STATUS_INTERNAL_ERROR;
360 status = tevent_req_simple_recv_ntstatus(req);
361 if (NT_STATUS_IS_OK(status)) {
362 talloc_steal(mem_ctx, state->netlogon);
363 io->out.netlogon = state->netlogon->out.netlogon;
364 io->out.address = talloc_steal(mem_ctx, state->srv_addresses[state->srv_address_index]);
366 tevent_req_received(req);
367 return status;
370 NTSTATUS finddcs_cldap(TALLOC_CTX *mem_ctx,
371 struct finddcs *io,
372 struct resolve_context *resolve_ctx,
373 struct tevent_context *event_ctx)
375 NTSTATUS status;
376 struct tevent_req *req = finddcs_cldap_send(mem_ctx,
378 resolve_ctx,
379 event_ctx);
380 status = finddcs_cldap_recv(req, mem_ctx, io);
381 talloc_free(req);
382 return status;