Coverage Report

Created: 2025-12-31 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source4/libcli/finddcs_cldap.c
Line
Count
Source
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
#include "source3/libads/netlogon_ping.h"
34
35
struct finddcs_cldap_state {
36
  struct tevent_context *ev;
37
  struct tevent_req *req;
38
  const char *domain_name;
39
  struct dom_sid *domain_sid;
40
  const char *srv_name;
41
  const char **srv_addresses;
42
  uint32_t minimum_dc_flags;
43
  enum client_netlogon_ping_protocol proto;
44
  uint32_t srv_address_index;
45
  struct netlogon_samlogon_response *response;
46
  NTSTATUS status;
47
};
48
49
static void finddcs_cldap_srv_resolved(struct composite_context *ctx);
50
static void finddcs_cldap_netlogon_replied(struct tevent_req *req);
51
static bool finddcs_cldap_srv_lookup(struct finddcs_cldap_state *state,
52
             struct finddcs *io,
53
             struct resolve_context *resolve_ctx,
54
             struct tevent_context *event_ctx);
55
static bool finddcs_cldap_nbt_lookup(struct finddcs_cldap_state *state,
56
             struct finddcs *io,
57
             struct resolve_context *resolve_ctx,
58
             struct tevent_context *event_ctx);
59
static void finddcs_cldap_nbt_resolved(struct composite_context *ctx);
60
static bool finddcs_cldap_name_lookup(struct finddcs_cldap_state *state,
61
              struct finddcs *io,
62
              struct resolve_context *resolve_ctx,
63
              struct tevent_context *event_ctx);
64
static void finddcs_cldap_name_resolved(struct composite_context *ctx);
65
static void finddcs_cldap_next_server(struct finddcs_cldap_state *state);
66
static bool finddcs_cldap_ipaddress(struct finddcs_cldap_state *state, struct finddcs *io);
67
68
69
/*
70
 * find a list of DCs via DNS/CLDAP
71
 */
72
struct tevent_req *finddcs_cldap_send(TALLOC_CTX *mem_ctx,
73
              struct finddcs *io,
74
              struct resolve_context *resolve_ctx,
75
              struct tevent_context *event_ctx)
76
0
{
77
0
  struct finddcs_cldap_state *state;
78
0
  struct tevent_req *req;
79
80
0
  req = tevent_req_create(mem_ctx, &state, struct finddcs_cldap_state);
81
0
  if (req == NULL) {
82
0
    return NULL;
83
0
  }
84
85
0
  state->req = req;
86
0
  state->ev = event_ctx;
87
0
  state->minimum_dc_flags = io->in.minimum_dc_flags;
88
0
  state->proto = io->in.proto;
89
90
0
  if (io->in.domain_name) {
91
0
    state->domain_name = talloc_strdup(state, io->in.domain_name);
92
0
    if (tevent_req_nomem(state->domain_name, req)) {
93
0
      return tevent_req_post(req, event_ctx);
94
0
    }
95
0
  } else {
96
0
    state->domain_name = NULL;
97
0
  }
98
99
0
  if (io->in.domain_sid) {
100
0
    state->domain_sid = dom_sid_dup(state, io->in.domain_sid);
101
0
    if (tevent_req_nomem(state->domain_sid, req)) {
102
0
      return tevent_req_post(req, event_ctx);
103
0
    }
104
0
  } else {
105
0
    state->domain_sid = NULL;
106
0
  }
107
108
0
  if (io->in.server_address) {
109
0
    if (is_ipaddress(io->in.server_address)) {
110
0
      DEBUG(4,("finddcs: searching for a DC by IP %s\n",
111
0
         io->in.server_address));
112
0
      if (!finddcs_cldap_ipaddress(state, io)) {
113
0
        return tevent_req_post(req, event_ctx);
114
0
      }
115
0
    } else {
116
0
      if (!finddcs_cldap_name_lookup(state, io, resolve_ctx,
117
0
                   event_ctx)) {
118
0
        return tevent_req_post(req, event_ctx);
119
0
      }
120
0
    }
121
0
  } else if (io->in.domain_name) {
122
0
    if (strchr(state->domain_name, '.')) {
123
      /* looks like a DNS name */
124
0
      DEBUG(4,("finddcs: searching for a DC by DNS domain %s\n", state->domain_name));
125
0
      if (!finddcs_cldap_srv_lookup(state, io, resolve_ctx,
126
0
                  event_ctx)) {
127
0
        return tevent_req_post(req, event_ctx);
128
0
      }
129
0
    } else {
130
0
      DEBUG(4,("finddcs: searching for a DC by NBT lookup %s\n", state->domain_name));
131
0
      if (!finddcs_cldap_nbt_lookup(state, io, resolve_ctx,
132
0
                  event_ctx)) {
133
0
        return tevent_req_post(req, event_ctx);
134
0
      }
135
0
    }
136
0
  } else {
137
    /* either we have the domain name or the IP address */
138
0
    tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
139
0
    DEBUG(2,("finddcs: Please specify at least the domain name or the IP address! \n"));
140
0
    return tevent_req_post(req, event_ctx);
141
0
  }
142
143
0
  return req;
144
0
}
145
146
147
/*
148
  we've been told the IP of the server, bypass name
149
  resolution and go straight to CLDAP
150
*/
151
static bool finddcs_cldap_ipaddress(struct finddcs_cldap_state *state, struct finddcs *io)
152
0
{
153
0
  NTSTATUS status;
154
155
0
  state->srv_addresses = talloc_array(state, const char *, 2);
156
0
  if (tevent_req_nomem(state->srv_addresses, state->req)) {
157
0
    return false;
158
0
  }
159
0
  state->srv_addresses[0] = talloc_strdup(state->srv_addresses, io->in.server_address);
160
0
  if (tevent_req_nomem(state->srv_addresses[0], state->req)) {
161
0
    return false;
162
0
  }
163
0
  state->srv_addresses[1] = NULL;
164
0
  state->srv_address_index = 0;
165
166
0
  finddcs_cldap_next_server(state);
167
0
  return tevent_req_is_nterror(state->req, &status);
168
0
}
169
170
/*
171
  start a SRV DNS lookup
172
 */
173
static bool finddcs_cldap_srv_lookup(struct finddcs_cldap_state *state,
174
             struct finddcs *io,
175
             struct resolve_context *resolve_ctx,
176
             struct tevent_context *event_ctx)
177
0
{
178
0
  struct composite_context *creq;
179
0
  struct nbt_name name;
180
181
0
  if (io->in.site_name) {
182
0
    state->srv_name = talloc_asprintf(state, "_ldap._tcp.%s._sites.%s",
183
0
             io->in.site_name, io->in.domain_name);
184
0
  } else {
185
0
    state->srv_name = talloc_asprintf(state, "_ldap._tcp.%s", io->in.domain_name);
186
0
  }
187
188
0
  DEBUG(4,("finddcs: looking for SRV records for %s\n", state->srv_name));
189
190
0
  make_nbt_name(&name, state->srv_name, 0);
191
192
0
  creq = resolve_name_ex_send(resolve_ctx, state,
193
0
            RESOLVE_NAME_FLAG_FORCE_DNS | RESOLVE_NAME_FLAG_DNS_SRV,
194
0
            0, &name, event_ctx);
195
0
  if (tevent_req_nomem(creq, state->req)) {
196
0
    return false;
197
0
  }
198
0
  creq->async.fn = finddcs_cldap_srv_resolved;
199
0
  creq->async.private_data = state;
200
201
0
  return true;
202
0
}
203
204
/*
205
  start a NBT name lookup for domain<1C>
206
 */
207
static bool finddcs_cldap_nbt_lookup(struct finddcs_cldap_state *state,
208
             struct finddcs *io,
209
             struct resolve_context *resolve_ctx,
210
             struct tevent_context *event_ctx)
211
0
{
212
0
  struct composite_context *creq;
213
0
  struct nbt_name name;
214
215
0
  make_nbt_name(&name, state->domain_name, NBT_NAME_LOGON);
216
0
  creq = resolve_name_send(resolve_ctx, state, &name, event_ctx);
217
0
  if (tevent_req_nomem(creq, state->req)) {
218
0
    return false;
219
0
  }
220
0
  creq->async.fn = finddcs_cldap_nbt_resolved;
221
0
  creq->async.private_data = state;
222
0
  return true;
223
0
}
224
225
static bool finddcs_cldap_name_lookup(struct finddcs_cldap_state *state,
226
              struct finddcs *io,
227
              struct resolve_context *resolve_ctx,
228
              struct tevent_context *event_ctx)
229
0
{
230
0
  struct composite_context *creq;
231
0
  struct nbt_name name;
232
233
0
  make_nbt_name(&name, io->in.server_address, NBT_NAME_SERVER);
234
0
  creq = resolve_name_send(resolve_ctx, state, &name, event_ctx);
235
0
  if (tevent_req_nomem(creq, state->req)) {
236
0
    return false;
237
0
  }
238
0
  creq->async.fn = finddcs_cldap_name_resolved;
239
0
  creq->async.private_data = state;
240
0
  return true;
241
0
}
242
243
/*
244
  fire off a CLDAP query to the next server
245
 */
246
static void finddcs_cldap_next_server(struct finddcs_cldap_state *state)
247
0
{
248
0
  struct tevent_req *subreq;
249
0
  struct tsocket_address **servers = NULL;
250
0
  const char *realm = NULL;
251
0
  size_t i, num_servers;
252
0
  int ret;
253
254
0
  num_servers = str_list_length(state->srv_addresses);
255
256
0
  servers = talloc_array(state, struct tsocket_address *, num_servers);
257
0
  if (tevent_req_nomem(servers, state->req)) {
258
0
    return;
259
0
  }
260
261
0
  for (i = 0; i < num_servers; i++) {
262
0
    ret = tsocket_address_inet_from_strings(
263
0
      servers,
264
0
      "ip",
265
0
      state->srv_addresses[i],
266
0
      389,
267
0
      &servers[i]);
268
0
    if (ret == -1) {
269
0
      NTSTATUS status = map_nt_error_from_unix_common(errno);
270
0
      tevent_req_nterror(state->req, status);
271
0
      return;
272
0
    }
273
0
  }
274
275
0
  if ((state->domain_name != NULL) && (strchr(state->domain_name, '.'))) {
276
0
    realm = state->domain_name;
277
0
  }
278
279
0
  subreq = netlogon_pings_send(
280
0
    state,
281
0
    state->ev,
282
0
    state->proto,
283
0
    servers,
284
0
    num_servers,
285
0
    (struct netlogon_ping_filter){
286
0
      .ntversion = NETLOGON_NT_VERSION_5 |
287
0
             NETLOGON_NT_VERSION_5EX |
288
0
             NETLOGON_NT_VERSION_IP,
289
290
0
      .acct_ctrl = -1,
291
0
      .domain = realm,
292
0
      .domain_sid = state->domain_sid,
293
0
      .required_flags = state->minimum_dc_flags,
294
0
    },
295
0
    1, /* min_servers */
296
0
    tevent_timeval_current_ofs(2, 0));
297
0
  if (tevent_req_nomem(subreq, state->req)) {
298
0
    return;
299
0
  }
300
0
  tevent_req_set_callback(subreq, finddcs_cldap_netlogon_replied, state);
301
0
}
302
303
304
/*
305
  we have a response from a CLDAP server for a netlogon request
306
 */
307
static void finddcs_cldap_netlogon_replied(struct tevent_req *subreq)
308
0
{
309
0
  struct finddcs_cldap_state *state;
310
0
  struct netlogon_samlogon_response **responses = NULL;
311
0
  size_t i, num_responses;
312
0
  NTSTATUS status;
313
314
0
  state = tevent_req_callback_data(subreq, struct finddcs_cldap_state);
315
316
0
  status = netlogon_pings_recv(subreq, state, &responses);
317
0
  TALLOC_FREE(subreq);
318
0
  if (tevent_req_nterror(state->req, status)) {
319
0
    return;
320
0
  }
321
322
0
  num_responses = talloc_array_length(responses);
323
324
0
  for (i = 0; i < num_responses; i++) {
325
0
    if (responses[i] != NULL) {
326
0
      state->srv_address_index = i;
327
0
      state->response = responses[i];
328
0
    }
329
0
  }
330
331
0
  if (state->response == NULL) {
332
0
    tevent_req_nterror(state->req,
333
0
           NT_STATUS_OBJECT_NAME_NOT_FOUND);
334
0
    return;
335
0
  }
336
337
0
  map_netlogon_samlogon_response(state->response);
338
339
0
  DEBUG(4,("finddcs: Found matching DC %s with server_type=0x%08x\n",
340
0
     state->srv_addresses[state->srv_address_index],
341
0
     state->response->data.nt5_ex.server_type));
342
343
0
  tevent_req_done(state->req);
344
0
}
345
346
static void finddcs_cldap_name_resolved(struct composite_context *ctx)
347
0
{
348
0
  struct finddcs_cldap_state *state =
349
0
    talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
350
0
  NTSTATUS status;
351
0
  unsigned i;
352
353
0
  status = resolve_name_multiple_recv(ctx, state, &state->srv_addresses);
354
0
  if (tevent_req_nterror(state->req, status)) {
355
0
    DEBUG(2,("finddcs: No matching server found\n"));
356
0
    return;
357
0
  }
358
359
0
  for (i=0; state->srv_addresses[i]; i++) {
360
0
    DEBUG(4,("finddcs: response %u at '%s'\n",
361
0
             i, state->srv_addresses[i]));
362
0
  }
363
364
0
  state->srv_address_index = 0;
365
366
0
  state->status = NT_STATUS_OK;
367
0
  finddcs_cldap_next_server(state);
368
0
}
369
370
/*
371
   handle NBT name lookup reply
372
 */
373
static void finddcs_cldap_nbt_resolved(struct composite_context *ctx)
374
0
{
375
0
  struct finddcs_cldap_state *state =
376
0
    talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
377
0
  NTSTATUS status;
378
0
  unsigned i;
379
380
0
  status = resolve_name_multiple_recv(ctx, state, &state->srv_addresses);
381
0
  if (tevent_req_nterror(state->req, status)) {
382
0
    DEBUG(2,("finddcs: No matching NBT <1c> server found\n"));
383
0
    return;
384
0
  }
385
386
0
  for (i=0; state->srv_addresses[i]; i++) {
387
0
    DEBUG(4,("finddcs: NBT <1c> response %u at '%s'\n",
388
0
             i, state->srv_addresses[i]));
389
0
  }
390
391
0
  state->srv_address_index = 0;
392
393
0
  finddcs_cldap_next_server(state);
394
0
}
395
396
397
/*
398
 * Having got a DNS SRV answer, fire off the first CLDAP request
399
 */
400
static void finddcs_cldap_srv_resolved(struct composite_context *ctx)
401
0
{
402
0
  struct finddcs_cldap_state *state =
403
0
    talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
404
0
  NTSTATUS status;
405
0
  unsigned i;
406
407
0
  status = resolve_name_multiple_recv(ctx, state, &state->srv_addresses);
408
0
  if (tevent_req_nterror(state->req, status)) {
409
0
    DEBUG(2,("finddcs: Failed to find SRV record for %s\n", state->srv_name));
410
0
    return;
411
0
  }
412
413
0
  for (i=0; state->srv_addresses[i]; i++) {
414
0
    DEBUG(4,("finddcs: DNS SRV response %u at '%s'\n", i, state->srv_addresses[i]));
415
0
  }
416
417
0
  state->srv_address_index = 0;
418
419
0
  state->status = NT_STATUS_OK;
420
0
  finddcs_cldap_next_server(state);
421
0
}
422
423
424
NTSTATUS finddcs_cldap_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct finddcs *io)
425
0
{
426
0
  struct finddcs_cldap_state *state = tevent_req_data(req, struct finddcs_cldap_state);
427
0
  bool ok;
428
0
  NTSTATUS status;
429
430
0
  ok = tevent_req_poll(req, state->ev);
431
0
  if (!ok) {
432
0
    talloc_free(req);
433
0
    return NT_STATUS_INTERNAL_ERROR;
434
0
  }
435
0
  if (tevent_req_is_nterror(req, &status)) {
436
0
    tevent_req_received(req);
437
0
    return status;
438
0
  }
439
440
0
  io->out.netlogon = talloc_move(mem_ctx, &state->response);
441
0
  io->out.address = talloc_steal(
442
0
    mem_ctx, state->srv_addresses[state->srv_address_index]);
443
444
0
  tevent_req_received(req);
445
0
  return NT_STATUS_OK;
446
0
}
447
448
NTSTATUS finddcs_cldap(TALLOC_CTX *mem_ctx,
449
           struct finddcs *io,
450
           struct resolve_context *resolve_ctx,
451
           struct tevent_context *event_ctx)
452
0
{
453
0
  NTSTATUS status;
454
0
  struct tevent_req *req = finddcs_cldap_send(mem_ctx,
455
0
                io,
456
0
                resolve_ctx,
457
0
                event_ctx);
458
0
  status = finddcs_cldap_recv(req, mem_ctx, io);
459
  talloc_free(req);
460
0
  return status;
461
0
}