/src/samba/source3/libsmb/dsgetdcname.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | dsgetdcname |
5 | | |
6 | | Copyright (C) Gerald Carter 2006 |
7 | | Copyright (C) Guenther Deschner 2007-2008 |
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 "includes.h" |
24 | | #include "libsmb/dsgetdcname.h" |
25 | | #include "libsmb/namequery.h" |
26 | | #include "libads/sitename_cache.h" |
27 | | #include "../librpc/gen_ndr/ndr_netlogon.h" |
28 | | #include "libads/cldap.h" |
29 | | #include "libads/netlogon_ping.h" |
30 | | #include "lib/addns/dnsquery_srv.h" |
31 | | #include "libsmb/clidgram.h" |
32 | | #include "lib/gencache.h" |
33 | | #include "lib/util/util_net.h" |
34 | | #include "lib/tsocket/tsocket.h" |
35 | | |
36 | | /* 15 minutes */ |
37 | 0 | #define DSGETDCNAME_CACHE_TTL 60*15 |
38 | | |
39 | | struct ip_service_name { |
40 | | struct samba_sockaddr sa; |
41 | | const char *hostname; |
42 | | }; |
43 | | |
44 | | static NTSTATUS make_dc_info_from_cldap_reply( |
45 | | TALLOC_CTX *mem_ctx, |
46 | | uint32_t flags, |
47 | | const struct samba_sockaddr *sa, |
48 | | struct NETLOGON_SAM_LOGON_RESPONSE_EX *r, |
49 | | struct netr_DsRGetDCNameInfo **info); |
50 | | |
51 | | /**************************************************************** |
52 | | ****************************************************************/ |
53 | | |
54 | | static char *dsdcinfo_flags_str(TALLOC_CTX *mem_ctx, uint32_t flags) |
55 | 0 | { |
56 | 0 | char *s = NULL; |
57 | |
|
58 | 0 | s = talloc_asprintf(mem_ctx, "0x%08x\n\t", flags); |
59 | |
|
60 | 0 | if (flags & DS_FORCE_REDISCOVERY) |
61 | 0 | talloc_asprintf_addbuf(&s, "DS_FORCE_REDISCOVERY "); |
62 | 0 | if (flags & 0x00000002) |
63 | 0 | talloc_asprintf_addbuf(&s, "0x00000002 "); |
64 | 0 | if (flags & 0x00000004) |
65 | 0 | talloc_asprintf_addbuf(&s, "0x00000004 "); |
66 | 0 | if (flags & 0x00000008) |
67 | 0 | talloc_asprintf_addbuf(&s, "0x00000008 "); |
68 | 0 | if (flags & DS_DIRECTORY_SERVICE_REQUIRED) |
69 | 0 | talloc_asprintf_addbuf(&s, "DS_DIRECTORY_SERVICE_REQUIRED "); |
70 | 0 | if (flags & DS_DIRECTORY_SERVICE_PREFERRED) |
71 | 0 | talloc_asprintf_addbuf(&s, "DS_DIRECTORY_SERVICE_PREFERRED "); |
72 | 0 | if (flags & DS_GC_SERVER_REQUIRED) |
73 | 0 | talloc_asprintf_addbuf(&s, "DS_GC_SERVER_REQUIRED "); |
74 | 0 | if (flags & DS_PDC_REQUIRED) |
75 | 0 | talloc_asprintf_addbuf(&s, "DS_PDC_REQUIRED "); |
76 | 0 | if (flags & DS_BACKGROUND_ONLY) |
77 | 0 | talloc_asprintf_addbuf(&s, "DS_BACKGROUND_ONLY "); |
78 | 0 | if (flags & DS_IP_REQUIRED) |
79 | 0 | talloc_asprintf_addbuf(&s, "DS_IP_REQUIRED "); |
80 | 0 | if (flags & DS_KDC_REQUIRED) |
81 | 0 | talloc_asprintf_addbuf(&s, "DS_KDC_REQUIRED "); |
82 | 0 | if (flags & DS_TIMESERV_REQUIRED) |
83 | 0 | talloc_asprintf_addbuf(&s, "DS_TIMESERV_REQUIRED "); |
84 | 0 | if (flags & DS_WRITABLE_REQUIRED) |
85 | 0 | talloc_asprintf_addbuf(&s, "DS_WRITABLE_REQUIRED "); |
86 | 0 | if (flags & DS_GOOD_TIMESERV_PREFERRED) |
87 | 0 | talloc_asprintf_addbuf(&s, "DS_GOOD_TIMESERV_PREFERRED "); |
88 | 0 | if (flags & DS_AVOID_SELF) |
89 | 0 | talloc_asprintf_addbuf(&s, "DS_AVOID_SELF "); |
90 | 0 | if (flags & DS_ONLY_LDAP_NEEDED) |
91 | 0 | talloc_asprintf_addbuf(&s, "DS_ONLY_LDAP_NEEDED "); |
92 | 0 | if (flags & DS_IS_FLAT_NAME) |
93 | 0 | talloc_asprintf_addbuf(&s, "DS_IS_FLAT_NAME "); |
94 | 0 | if (flags & DS_IS_DNS_NAME) |
95 | 0 | talloc_asprintf_addbuf(&s, "DS_IS_DNS_NAME "); |
96 | 0 | if (flags & DS_TRY_NEXTCLOSEST_SITE) |
97 | 0 | talloc_asprintf_addbuf(&s, "DS_TRY_NEXTCLOSEST_SITE "); |
98 | 0 | if (flags & DS_DIRECTORY_SERVICE_6_REQUIRED) |
99 | 0 | talloc_asprintf_addbuf(&s, "DS_DIRECTORY_SERVICE_6_REQUIRED "); |
100 | 0 | if (flags & DS_WEB_SERVICE_REQUIRED) |
101 | 0 | talloc_asprintf_addbuf(&s, "DS_WEB_SERVICE_REQUIRED "); |
102 | 0 | if (flags & DS_DIRECTORY_SERVICE_8_REQUIRED) |
103 | 0 | talloc_asprintf_addbuf(&s, "DS_DIRECTORY_SERVICE_8_REQUIRED "); |
104 | 0 | if (flags & DS_DIRECTORY_SERVICE_9_REQUIRED) |
105 | 0 | talloc_asprintf_addbuf(&s, "DS_DIRECTORY_SERVICE_9_REQUIRED "); |
106 | 0 | if (flags & DS_DIRECTORY_SERVICE_10_REQUIRED) |
107 | 0 | talloc_asprintf_addbuf(&s, |
108 | 0 | "DS_DIRECTORY_SERVICE_10_REQUIRED "); |
109 | 0 | if (flags & 0x01000000) |
110 | 0 | talloc_asprintf_addbuf(&s, "0x01000000 "); |
111 | 0 | if (flags & 0x02000000) |
112 | 0 | talloc_asprintf_addbuf(&s, "0x02000000 "); |
113 | 0 | if (flags & 0x04000000) |
114 | 0 | talloc_asprintf_addbuf(&s, "0x04000000 "); |
115 | 0 | if (flags & 0x08000000) |
116 | 0 | talloc_asprintf_addbuf(&s, "0x08000000 "); |
117 | 0 | if (flags & 0x10000000) |
118 | 0 | talloc_asprintf_addbuf(&s, "0x10000000 "); |
119 | 0 | if (flags & 0x20000000) |
120 | 0 | talloc_asprintf_addbuf(&s, "0x20000000 "); |
121 | 0 | if (flags & DS_RETURN_DNS_NAME) |
122 | 0 | talloc_asprintf_addbuf(&s, "DS_RETURN_DNS_NAME "); |
123 | 0 | if (flags & DS_RETURN_FLAT_NAME) |
124 | 0 | talloc_asprintf_addbuf(&s, "DS_RETURN_FLAT_NAME "); |
125 | 0 | if (flags) |
126 | 0 | talloc_asprintf_addbuf(&s, "\n"); |
127 | |
|
128 | 0 | return s; |
129 | 0 | } |
130 | | |
131 | | /**************************************************************** |
132 | | ****************************************************************/ |
133 | | |
134 | | static char *dsgetdcname_cache_key(TALLOC_CTX *mem_ctx, const char *domain) |
135 | 0 | { |
136 | 0 | if (!domain) { |
137 | 0 | return NULL; |
138 | 0 | } |
139 | | |
140 | 0 | return talloc_asprintf_strupper_m(mem_ctx, "DSGETDCNAME/DOMAIN/%s", |
141 | 0 | domain); |
142 | 0 | } |
143 | | |
144 | | /**************************************************************** |
145 | | ****************************************************************/ |
146 | | |
147 | | static NTSTATUS dsgetdcname_cache_delete(TALLOC_CTX *mem_ctx, |
148 | | const char *domain_name) |
149 | 0 | { |
150 | 0 | char *key; |
151 | |
|
152 | 0 | key = dsgetdcname_cache_key(mem_ctx, domain_name); |
153 | 0 | if (!key) { |
154 | 0 | return NT_STATUS_NO_MEMORY; |
155 | 0 | } |
156 | | |
157 | 0 | if (!gencache_del(key)) { |
158 | 0 | return NT_STATUS_UNSUCCESSFUL; |
159 | 0 | } |
160 | | |
161 | 0 | return NT_STATUS_OK; |
162 | 0 | } |
163 | | |
164 | | /**************************************************************** |
165 | | ****************************************************************/ |
166 | | |
167 | | static NTSTATUS dsgetdcname_cache_store(TALLOC_CTX *mem_ctx, |
168 | | const char *domain_name, |
169 | | DATA_BLOB blob) |
170 | 0 | { |
171 | 0 | time_t expire_time; |
172 | 0 | char *key; |
173 | 0 | bool ret = false; |
174 | |
|
175 | 0 | key = dsgetdcname_cache_key(mem_ctx, domain_name); |
176 | 0 | if (!key) { |
177 | 0 | return NT_STATUS_NO_MEMORY; |
178 | 0 | } |
179 | | |
180 | 0 | expire_time = time(NULL) + DSGETDCNAME_CACHE_TTL; |
181 | |
|
182 | 0 | ret = gencache_set_data_blob(key, blob, expire_time); |
183 | |
|
184 | 0 | return ret ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; |
185 | 0 | } |
186 | | |
187 | | /**************************************************************** |
188 | | ****************************************************************/ |
189 | | |
190 | | static NTSTATUS store_cldap_reply(TALLOC_CTX *mem_ctx, |
191 | | uint32_t flags, |
192 | | struct samba_sockaddr *sa, |
193 | | uint32_t nt_version, |
194 | | struct NETLOGON_SAM_LOGON_RESPONSE_EX *r) |
195 | 0 | { |
196 | 0 | DATA_BLOB blob; |
197 | 0 | enum ndr_err_code ndr_err; |
198 | 0 | NTSTATUS status; |
199 | 0 | char addr[INET6_ADDRSTRLEN]; |
200 | |
|
201 | 0 | print_sockaddr(addr, sizeof(addr), &sa->u.ss); |
202 | | |
203 | | /* FIXME */ |
204 | 0 | r->sockaddr_size = 0x10; /* the w32 winsock addr size */ |
205 | 0 | r->sockaddr.sockaddr_family = 2; /* AF_INET */ |
206 | 0 | if (is_ipaddress_v4(addr)) { |
207 | 0 | r->sockaddr.pdc_ip = talloc_strdup(mem_ctx, addr); |
208 | 0 | if (r->sockaddr.pdc_ip == NULL) { |
209 | 0 | return NT_STATUS_NO_MEMORY; |
210 | 0 | } |
211 | 0 | } else { |
212 | | /* |
213 | | * ndr_push_NETLOGON_SAM_LOGON_RESPONSE_EX will |
214 | | * fail with an ipv6 address. |
215 | | * |
216 | | * This matches windows behaviour in the CLDAP |
217 | | * response when NETLOGON_NT_VERSION_5EX_WITH_IP |
218 | | * is used. |
219 | | * |
220 | | * Windows returns the ipv4 address of the ipv6 |
221 | | * server interface and falls back to 127.0.0.1 |
222 | | * if there's no ipv4 address. |
223 | | */ |
224 | 0 | r->sockaddr.pdc_ip = talloc_strdup(mem_ctx, "127.0.0.1"); |
225 | 0 | if (r->sockaddr.pdc_ip == NULL) { |
226 | 0 | return NT_STATUS_NO_MEMORY; |
227 | 0 | } |
228 | 0 | } |
229 | | |
230 | 0 | ndr_err = ndr_push_struct_blob(&blob, mem_ctx, r, |
231 | 0 | (ndr_push_flags_fn_t)ndr_push_NETLOGON_SAM_LOGON_RESPONSE_EX); |
232 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
233 | 0 | return ndr_map_error2ntstatus(ndr_err); |
234 | 0 | } |
235 | | |
236 | 0 | if (r->domain_name) { |
237 | 0 | status = dsgetdcname_cache_store(mem_ctx, r->domain_name, |
238 | 0 | blob); |
239 | 0 | if (!NT_STATUS_IS_OK(status)) { |
240 | 0 | goto done; |
241 | 0 | } |
242 | 0 | if (r->client_site) { |
243 | 0 | sitename_store(r->domain_name, r->client_site); |
244 | 0 | } |
245 | 0 | } |
246 | 0 | if (r->dns_domain) { |
247 | 0 | status = dsgetdcname_cache_store(mem_ctx, r->dns_domain, blob); |
248 | 0 | if (!NT_STATUS_IS_OK(status)) { |
249 | 0 | goto done; |
250 | 0 | } |
251 | 0 | if (r->client_site) { |
252 | 0 | sitename_store(r->dns_domain, r->client_site); |
253 | 0 | } |
254 | 0 | } |
255 | | |
256 | 0 | status = NT_STATUS_OK; |
257 | |
|
258 | 0 | done: |
259 | 0 | data_blob_free(&blob); |
260 | |
|
261 | 0 | return status; |
262 | 0 | } |
263 | | |
264 | | /**************************************************************** |
265 | | ****************************************************************/ |
266 | | |
267 | | static NTSTATUS dsgetdcname_cache_fetch(TALLOC_CTX *mem_ctx, |
268 | | const char *domain_name, |
269 | | const struct GUID *domain_guid, |
270 | | uint32_t flags, |
271 | | struct netr_DsRGetDCNameInfo **info_p) |
272 | 0 | { |
273 | 0 | char *key; |
274 | 0 | DATA_BLOB blob; |
275 | 0 | enum ndr_err_code ndr_err; |
276 | 0 | struct netr_DsRGetDCNameInfo *info; |
277 | 0 | struct NETLOGON_SAM_LOGON_RESPONSE_EX r; |
278 | 0 | NTSTATUS status; |
279 | |
|
280 | 0 | key = dsgetdcname_cache_key(mem_ctx, domain_name); |
281 | 0 | if (!key) { |
282 | 0 | return NT_STATUS_NO_MEMORY; |
283 | 0 | } |
284 | | |
285 | 0 | if (!gencache_get_data_blob(key, NULL, &blob, NULL, NULL)) { |
286 | 0 | return NT_STATUS_NOT_FOUND; |
287 | 0 | } |
288 | | |
289 | 0 | info = talloc_zero(mem_ctx, struct netr_DsRGetDCNameInfo); |
290 | 0 | if (!info) { |
291 | 0 | data_blob_free(&blob); |
292 | 0 | return NT_STATUS_NO_MEMORY; |
293 | 0 | } |
294 | | |
295 | 0 | ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r, |
296 | 0 | (ndr_pull_flags_fn_t)ndr_pull_NETLOGON_SAM_LOGON_RESPONSE_EX); |
297 | |
|
298 | 0 | data_blob_free(&blob); |
299 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
300 | 0 | dsgetdcname_cache_delete(mem_ctx, domain_name); |
301 | 0 | return ndr_map_error2ntstatus(ndr_err); |
302 | 0 | } |
303 | | |
304 | 0 | status = make_dc_info_from_cldap_reply(mem_ctx, flags, NULL, |
305 | 0 | &r, &info); |
306 | 0 | if (!NT_STATUS_IS_OK(status)) { |
307 | 0 | return status; |
308 | 0 | } |
309 | | |
310 | 0 | if (DEBUGLEVEL >= 10) { |
311 | 0 | NDR_PRINT_DEBUG(netr_DsRGetDCNameInfo, info); |
312 | 0 | } |
313 | | |
314 | | /* check flags */ |
315 | 0 | if (!check_cldap_reply_required_flags(info->dc_flags, flags)) { |
316 | 0 | DEBUG(10,("invalid flags\n")); |
317 | 0 | return NT_STATUS_INVALID_PARAMETER; |
318 | 0 | } |
319 | | |
320 | 0 | if ((flags & DS_IP_REQUIRED) && |
321 | 0 | (info->dc_address_type != DS_ADDRESS_TYPE_INET)) { |
322 | 0 | return NT_STATUS_INVALID_PARAMETER_MIX; |
323 | 0 | } |
324 | | |
325 | 0 | *info_p = info; |
326 | |
|
327 | 0 | return NT_STATUS_OK; |
328 | 0 | } |
329 | | |
330 | | /**************************************************************** |
331 | | ****************************************************************/ |
332 | | |
333 | | static NTSTATUS dsgetdcname_cached(TALLOC_CTX *mem_ctx, |
334 | | struct messaging_context *msg_ctx, |
335 | | const char *domain_name, |
336 | | const struct GUID *domain_guid, |
337 | | uint32_t flags, |
338 | | const char *site_name, |
339 | | struct netr_DsRGetDCNameInfo **info) |
340 | 0 | { |
341 | 0 | NTSTATUS status; |
342 | |
|
343 | 0 | status = dsgetdcname_cache_fetch(mem_ctx, domain_name, domain_guid, |
344 | 0 | flags, info); |
345 | 0 | if (!NT_STATUS_IS_OK(status) |
346 | 0 | && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { |
347 | 0 | DEBUG(10,("dsgetdcname_cached: cache fetch failed with: %s\n", |
348 | 0 | nt_errstr(status))); |
349 | 0 | return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; |
350 | 0 | } |
351 | | |
352 | 0 | if (flags & DS_BACKGROUND_ONLY) { |
353 | 0 | return status; |
354 | 0 | } |
355 | | |
356 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { |
357 | 0 | struct netr_DsRGetDCNameInfo *dc_info; |
358 | |
|
359 | 0 | status = dsgetdcname(mem_ctx, msg_ctx, domain_name, |
360 | 0 | domain_guid, site_name, |
361 | 0 | flags | DS_FORCE_REDISCOVERY, |
362 | 0 | &dc_info); |
363 | |
|
364 | 0 | if (!NT_STATUS_IS_OK(status)) { |
365 | 0 | return status; |
366 | 0 | } |
367 | | |
368 | 0 | *info = dc_info; |
369 | 0 | } |
370 | | |
371 | 0 | return status; |
372 | 0 | } |
373 | | |
374 | | /**************************************************************** |
375 | | ****************************************************************/ |
376 | | |
377 | | static bool check_allowed_required_flags(uint32_t flags, |
378 | | const char *site_name) |
379 | 0 | { |
380 | 0 | uint32_t return_type = flags & (DS_RETURN_FLAT_NAME|DS_RETURN_DNS_NAME); |
381 | 0 | uint32_t offered_type = flags & (DS_IS_FLAT_NAME|DS_IS_DNS_NAME); |
382 | 0 | uint32_t query_type = flags & (DS_BACKGROUND_ONLY|DS_FORCE_REDISCOVERY); |
383 | | |
384 | | /* FIXME: check for DSGETDC_VALID_FLAGS and check for exclusive bits |
385 | | * (DS_PDC_REQUIRED, DS_KDC_REQUIRED, DS_GC_SERVER_REQUIRED) */ |
386 | |
|
387 | 0 | if ((flags & DS_TRY_NEXTCLOSEST_SITE) && site_name) { |
388 | 0 | return false; |
389 | 0 | } |
390 | | |
391 | 0 | if (return_type == (DS_RETURN_FLAT_NAME|DS_RETURN_DNS_NAME)) { |
392 | 0 | return false; |
393 | 0 | } |
394 | | |
395 | 0 | if (offered_type == (DS_IS_DNS_NAME|DS_IS_FLAT_NAME)) { |
396 | 0 | return false; |
397 | 0 | } |
398 | | |
399 | 0 | if (query_type == (DS_BACKGROUND_ONLY|DS_FORCE_REDISCOVERY)) { |
400 | 0 | return false; |
401 | 0 | } |
402 | | |
403 | | #if 0 |
404 | | if ((flags & DS_RETURN_DNS_NAME) && (!(flags & DS_IP_REQUIRED))) { |
405 | | printf("gd: here5 \n"); |
406 | | return false; |
407 | | } |
408 | | #endif |
409 | 0 | return true; |
410 | 0 | } |
411 | | |
412 | | /**************************************************************** |
413 | | ****************************************************************/ |
414 | | |
415 | | static NTSTATUS discover_dc_netbios(TALLOC_CTX *mem_ctx, |
416 | | const char *domain_name, |
417 | | uint32_t flags, |
418 | | struct ip_service_name **returned_dclist, |
419 | | size_t *returned_count) |
420 | 0 | { |
421 | 0 | NTSTATUS status; |
422 | 0 | enum nbt_name_type name_type = NBT_NAME_LOGON; |
423 | 0 | struct samba_sockaddr *salist = NULL; |
424 | 0 | size_t i; |
425 | 0 | struct ip_service_name *dclist = NULL; |
426 | 0 | size_t count = 0; |
427 | 0 | static const char *resolve_order[] = { "lmhosts", "wins", "bcast", NULL }; |
428 | |
|
429 | 0 | if (flags & DS_PDC_REQUIRED) { |
430 | 0 | name_type = NBT_NAME_PDC; |
431 | 0 | } |
432 | |
|
433 | 0 | status = internal_resolve_name(mem_ctx, |
434 | 0 | domain_name, |
435 | 0 | name_type, |
436 | 0 | NULL, |
437 | 0 | &salist, |
438 | 0 | &count, |
439 | 0 | resolve_order); |
440 | 0 | if (!NT_STATUS_IS_OK(status)) { |
441 | 0 | NTSTATUS raw_status = status; |
442 | |
|
443 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { |
444 | 0 | status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; |
445 | 0 | } |
446 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_ADDRESS)) { |
447 | 0 | status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; |
448 | 0 | } |
449 | |
|
450 | 0 | DBG_DEBUG("failed to find DC for %s: %s => %s\n", |
451 | 0 | domain_name, |
452 | 0 | nt_errstr(raw_status), |
453 | 0 | nt_errstr(status)); |
454 | 0 | return status; |
455 | 0 | } |
456 | | |
457 | 0 | dclist = talloc_zero_array(mem_ctx, struct ip_service_name, count); |
458 | 0 | if (!dclist) { |
459 | 0 | TALLOC_FREE(salist); |
460 | 0 | return NT_STATUS_NO_MEMORY; |
461 | 0 | } |
462 | | |
463 | 0 | for (i=0; i<count; i++) { |
464 | 0 | char addr[INET6_ADDRSTRLEN]; |
465 | 0 | struct ip_service_name *r = &dclist[i]; |
466 | |
|
467 | 0 | print_sockaddr(addr, sizeof(addr), |
468 | 0 | &salist[i].u.ss); |
469 | |
|
470 | 0 | r->sa = salist[i]; |
471 | 0 | r->hostname = talloc_strdup(mem_ctx, addr); |
472 | 0 | if (!r->hostname) { |
473 | 0 | TALLOC_FREE(salist); |
474 | 0 | TALLOC_FREE(dclist); |
475 | 0 | return NT_STATUS_NO_MEMORY; |
476 | 0 | } |
477 | |
|
478 | 0 | } |
479 | | |
480 | 0 | TALLOC_FREE(salist); |
481 | |
|
482 | 0 | *returned_dclist = dclist; |
483 | 0 | *returned_count = count; |
484 | |
|
485 | 0 | return NT_STATUS_OK; |
486 | 0 | } |
487 | | |
488 | | /**************************************************************** |
489 | | ****************************************************************/ |
490 | | |
491 | | static NTSTATUS discover_dc_dns(TALLOC_CTX *mem_ctx, |
492 | | const char *domain_name, |
493 | | const struct GUID *domain_guid, |
494 | | uint32_t flags, |
495 | | const char *site_name, |
496 | | struct ip_service_name **returned_dclist, |
497 | | size_t *return_count) |
498 | 0 | { |
499 | 0 | size_t i; |
500 | 0 | NTSTATUS status; |
501 | 0 | struct dns_rr_srv *dcs = NULL; |
502 | 0 | size_t numdcs = 0; |
503 | 0 | struct ip_service_name *dclist = NULL; |
504 | 0 | size_t ret_count = 0; |
505 | 0 | char *query = NULL; |
506 | |
|
507 | 0 | if (flags & DS_PDC_REQUIRED) { |
508 | 0 | query = ads_dns_query_string_pdc(mem_ctx, domain_name); |
509 | 0 | } else if (flags & DS_GC_SERVER_REQUIRED) { |
510 | 0 | query = ads_dns_query_string_gcs(mem_ctx, domain_name); |
511 | 0 | } else if (flags & DS_KDC_REQUIRED) { |
512 | 0 | query = ads_dns_query_string_kdcs(mem_ctx, domain_name); |
513 | 0 | } else if (flags & DS_DIRECTORY_SERVICE_REQUIRED) { |
514 | 0 | query = ads_dns_query_string_dcs(mem_ctx, domain_name); |
515 | 0 | } else if (domain_guid) { |
516 | 0 | query = ads_dns_query_string_dcs_guid( |
517 | 0 | mem_ctx, domain_guid, domain_name); |
518 | 0 | } else { |
519 | 0 | query = ads_dns_query_string_dcs(mem_ctx, domain_name); |
520 | 0 | } |
521 | |
|
522 | 0 | if (query == NULL) { |
523 | 0 | return NT_STATUS_NO_MEMORY; |
524 | 0 | } |
525 | | |
526 | 0 | status = ads_dns_query_srv( |
527 | 0 | mem_ctx, |
528 | 0 | lp_get_async_dns_timeout(), |
529 | 0 | site_name, |
530 | 0 | query, |
531 | 0 | &dcs, |
532 | 0 | &numdcs); |
533 | 0 | TALLOC_FREE(query); |
534 | 0 | if (!NT_STATUS_IS_OK(status)) { |
535 | 0 | return status; |
536 | 0 | } |
537 | | |
538 | 0 | if (numdcs == 0) { |
539 | 0 | TALLOC_FREE(dcs); |
540 | 0 | return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; |
541 | 0 | } |
542 | | |
543 | | /* Check for integer wrap. */ |
544 | 0 | if (numdcs + numdcs < numdcs) { |
545 | 0 | TALLOC_FREE(dcs); |
546 | 0 | return NT_STATUS_INVALID_PARAMETER; |
547 | 0 | } |
548 | | |
549 | | /* |
550 | | * We're only returning up to 2 addresses per |
551 | | * DC name, so just allocate size numdcs x 2. |
552 | | */ |
553 | | |
554 | 0 | dclist = talloc_zero_array(mem_ctx, |
555 | 0 | struct ip_service_name, |
556 | 0 | numdcs * 2); |
557 | 0 | if (!dclist) { |
558 | 0 | TALLOC_FREE(dcs); |
559 | 0 | return NT_STATUS_NO_MEMORY; |
560 | 0 | } |
561 | | |
562 | | /* |
563 | | * First, copy the SRV record replies that |
564 | | * have IP addresses returned with them. |
565 | | */ |
566 | 0 | ret_count = 0; |
567 | 0 | for (i = 0; i < numdcs; i++) { |
568 | 0 | size_t j; |
569 | 0 | bool have_v4_addr = false; |
570 | 0 | bool have_v6_addr = false; |
571 | |
|
572 | 0 | if (dcs[i].num_ips == 0) { |
573 | 0 | continue; |
574 | 0 | } |
575 | | |
576 | | /* |
577 | | * Pick up to 1 address from each address |
578 | | * family (IPv4, IPv6). |
579 | | * |
580 | | * This is different from the previous |
581 | | * code which picked a 'next ip' address |
582 | | * each time, incrementing an index. |
583 | | * Too complex to maintain :-(. |
584 | | */ |
585 | 0 | for (j = 0; j < dcs[i].num_ips; j++) { |
586 | 0 | if ((dcs[i].ss_s[j].ss_family == AF_INET && !have_v4_addr) || |
587 | 0 | (dcs[i].ss_s[j].ss_family == AF_INET6 && !have_v6_addr)) { |
588 | 0 | bool ok; |
589 | 0 | dclist[ret_count].hostname = |
590 | 0 | talloc_strdup(dclist, dcs[i].hostname); |
591 | 0 | ok = sockaddr_storage_to_samba_sockaddr( |
592 | 0 | &dclist[ret_count].sa, |
593 | 0 | &dcs[i].ss_s[j]); |
594 | 0 | if (!ok) { |
595 | 0 | TALLOC_FREE(dcs); |
596 | 0 | TALLOC_FREE(dclist); |
597 | 0 | return NT_STATUS_INVALID_PARAMETER; |
598 | 0 | } |
599 | 0 | ret_count++; |
600 | 0 | if (dcs[i].ss_s[j].ss_family == AF_INET) { |
601 | 0 | have_v4_addr = true; |
602 | 0 | } else { |
603 | 0 | have_v6_addr = true; |
604 | 0 | } |
605 | 0 | if (have_v4_addr && have_v6_addr) { |
606 | 0 | break; |
607 | 0 | } |
608 | 0 | } |
609 | 0 | } |
610 | 0 | } |
611 | | |
612 | 0 | TALLOC_FREE(dcs); |
613 | |
|
614 | 0 | if (ret_count == 0) { |
615 | 0 | TALLOC_FREE(dclist); |
616 | 0 | return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; |
617 | 0 | } |
618 | | |
619 | 0 | *returned_dclist = dclist; |
620 | 0 | *return_count = ret_count; |
621 | 0 | return NT_STATUS_OK; |
622 | 0 | } |
623 | | |
624 | | /**************************************************************** |
625 | | ****************************************************************/ |
626 | | |
627 | | static NTSTATUS make_domain_controller_info(TALLOC_CTX *mem_ctx, |
628 | | const char *dc_unc, |
629 | | const char *dc_address, |
630 | | uint32_t dc_address_type, |
631 | | const struct GUID *domain_guid, |
632 | | const char *domain_name, |
633 | | const char *forest_name, |
634 | | uint32_t flags, |
635 | | const char *dc_site_name, |
636 | | const char *client_site_name, |
637 | | struct netr_DsRGetDCNameInfo **info_out) |
638 | 0 | { |
639 | 0 | struct netr_DsRGetDCNameInfo *info; |
640 | |
|
641 | 0 | info = talloc_zero(mem_ctx, struct netr_DsRGetDCNameInfo); |
642 | 0 | NT_STATUS_HAVE_NO_MEMORY(info); |
643 | | |
644 | 0 | if (dc_unc) { |
645 | 0 | if (!(dc_unc[0] == '\\' && dc_unc[1] == '\\')) { |
646 | 0 | info->dc_unc = talloc_asprintf(mem_ctx, "\\\\%s", |
647 | 0 | dc_unc); |
648 | 0 | } else { |
649 | 0 | info->dc_unc = talloc_strdup(mem_ctx, dc_unc); |
650 | 0 | } |
651 | 0 | NT_STATUS_HAVE_NO_MEMORY(info->dc_unc); |
652 | 0 | } |
653 | | |
654 | 0 | if (dc_address) { |
655 | 0 | if (!(dc_address[0] == '\\' && dc_address[1] == '\\')) { |
656 | 0 | info->dc_address = talloc_asprintf(mem_ctx, "\\\\%s", |
657 | 0 | dc_address); |
658 | 0 | } else { |
659 | 0 | info->dc_address = talloc_strdup(mem_ctx, dc_address); |
660 | 0 | } |
661 | 0 | NT_STATUS_HAVE_NO_MEMORY(info->dc_address); |
662 | 0 | } |
663 | | |
664 | 0 | info->dc_address_type = dc_address_type; |
665 | |
|
666 | 0 | if (domain_guid) { |
667 | 0 | info->domain_guid = *domain_guid; |
668 | 0 | } |
669 | |
|
670 | 0 | if (domain_name) { |
671 | 0 | info->domain_name = talloc_strdup(mem_ctx, domain_name); |
672 | 0 | NT_STATUS_HAVE_NO_MEMORY(info->domain_name); |
673 | 0 | } |
674 | | |
675 | 0 | if (forest_name && *forest_name) { |
676 | 0 | info->forest_name = talloc_strdup(mem_ctx, forest_name); |
677 | 0 | NT_STATUS_HAVE_NO_MEMORY(info->forest_name); |
678 | 0 | flags |= DS_DNS_FOREST_ROOT; |
679 | 0 | } |
680 | | |
681 | 0 | info->dc_flags = flags; |
682 | |
|
683 | 0 | if (dc_site_name) { |
684 | 0 | info->dc_site_name = talloc_strdup(mem_ctx, dc_site_name); |
685 | 0 | NT_STATUS_HAVE_NO_MEMORY(info->dc_site_name); |
686 | 0 | } |
687 | | |
688 | 0 | if (client_site_name) { |
689 | 0 | info->client_site_name = talloc_strdup(mem_ctx, |
690 | 0 | client_site_name); |
691 | 0 | NT_STATUS_HAVE_NO_MEMORY(info->client_site_name); |
692 | 0 | } |
693 | | |
694 | 0 | *info_out = info; |
695 | |
|
696 | 0 | return NT_STATUS_OK; |
697 | 0 | } |
698 | | |
699 | | /**************************************************************** |
700 | | ****************************************************************/ |
701 | | |
702 | | static void map_dc_and_domain_names(uint32_t flags, |
703 | | const char *dc_name, |
704 | | const char *domain_name, |
705 | | const char *dns_dc_name, |
706 | | const char *dns_domain_name, |
707 | | uint32_t *dc_flags, |
708 | | const char **hostname_p, |
709 | | const char **domain_p) |
710 | 0 | { |
711 | 0 | switch (flags & 0xf0000000) { |
712 | 0 | case DS_RETURN_FLAT_NAME: |
713 | 0 | if (dc_name && domain_name && |
714 | 0 | *dc_name && *domain_name) { |
715 | 0 | *hostname_p = dc_name; |
716 | 0 | *domain_p = domain_name; |
717 | 0 | break; |
718 | 0 | } |
719 | | |
720 | 0 | FALL_THROUGH; |
721 | 0 | case DS_RETURN_DNS_NAME: |
722 | 0 | default: |
723 | 0 | if (dns_dc_name && dns_domain_name && |
724 | 0 | *dns_dc_name && *dns_domain_name) { |
725 | 0 | *hostname_p = dns_dc_name; |
726 | 0 | *domain_p = dns_domain_name; |
727 | 0 | *dc_flags |= DS_DNS_DOMAIN | DS_DNS_CONTROLLER; |
728 | 0 | break; |
729 | 0 | } |
730 | 0 | if (dc_name && domain_name && |
731 | 0 | *dc_name && *domain_name) { |
732 | 0 | *hostname_p = dc_name; |
733 | 0 | *domain_p = domain_name; |
734 | 0 | break; |
735 | 0 | } |
736 | 0 | } |
737 | 0 | } |
738 | | |
739 | | /**************************************************************** |
740 | | ****************************************************************/ |
741 | | |
742 | | static NTSTATUS make_dc_info_from_cldap_reply( |
743 | | TALLOC_CTX *mem_ctx, |
744 | | uint32_t flags, |
745 | | const struct samba_sockaddr *sa, |
746 | | struct NETLOGON_SAM_LOGON_RESPONSE_EX *r, |
747 | | struct netr_DsRGetDCNameInfo **info) |
748 | 0 | { |
749 | 0 | const char *dc_hostname = NULL; |
750 | 0 | const char *dc_domain_name = NULL; |
751 | 0 | const char *dc_address = NULL; |
752 | 0 | const char *dc_forest = NULL; |
753 | 0 | uint32_t dc_address_type = 0; |
754 | 0 | uint32_t dc_flags = 0; |
755 | 0 | struct GUID *dc_domain_guid = NULL; |
756 | 0 | const char *dc_server_site = NULL; |
757 | 0 | const char *dc_client_site = NULL; |
758 | |
|
759 | 0 | char addr[INET6_ADDRSTRLEN]; |
760 | |
|
761 | 0 | if (r->command == LOGON_SAM_LOGON_PAUSE_RESPONSE || |
762 | 0 | r->command == LOGON_SAM_LOGON_PAUSE_RESPONSE_EX) |
763 | 0 | { |
764 | 0 | return NT_STATUS_NETLOGON_NOT_STARTED; |
765 | 0 | } |
766 | | |
767 | 0 | if (sa != NULL) { |
768 | 0 | print_sockaddr(addr, sizeof(addr), &sa->u.ss); |
769 | 0 | dc_address = addr; |
770 | 0 | dc_address_type = DS_ADDRESS_TYPE_INET; |
771 | 0 | } else { |
772 | 0 | if (r->sockaddr.pdc_ip) { |
773 | 0 | dc_address = r->sockaddr.pdc_ip; |
774 | 0 | dc_address_type = DS_ADDRESS_TYPE_INET; |
775 | 0 | } else { |
776 | 0 | dc_address = r->pdc_name; |
777 | 0 | dc_address_type = DS_ADDRESS_TYPE_NETBIOS; |
778 | 0 | } |
779 | 0 | } |
780 | |
|
781 | 0 | map_dc_and_domain_names(flags, |
782 | 0 | r->pdc_name, |
783 | 0 | r->domain_name, |
784 | 0 | r->pdc_dns_name, |
785 | 0 | r->dns_domain, |
786 | 0 | &dc_flags, |
787 | 0 | &dc_hostname, |
788 | 0 | &dc_domain_name); |
789 | |
|
790 | 0 | dc_flags |= r->server_type; |
791 | 0 | dc_forest = r->forest; |
792 | 0 | dc_domain_guid = &r->domain_uuid; |
793 | 0 | dc_server_site = r->server_site; |
794 | 0 | dc_client_site = r->client_site; |
795 | |
|
796 | 0 | return make_domain_controller_info(mem_ctx, |
797 | 0 | dc_hostname, |
798 | 0 | dc_address, |
799 | 0 | dc_address_type, |
800 | 0 | dc_domain_guid, |
801 | 0 | dc_domain_name, |
802 | 0 | dc_forest, |
803 | 0 | dc_flags, |
804 | 0 | dc_server_site, |
805 | 0 | dc_client_site, |
806 | 0 | info); |
807 | 0 | } |
808 | | |
809 | | /**************************************************************** |
810 | | ****************************************************************/ |
811 | | |
812 | | static uint32_t map_ds_flags_to_nt_version(uint32_t flags) |
813 | 0 | { |
814 | 0 | uint32_t nt_version = 0; |
815 | |
|
816 | 0 | if (flags & DS_PDC_REQUIRED) { |
817 | 0 | nt_version |= NETLOGON_NT_VERSION_PDC; |
818 | 0 | } |
819 | |
|
820 | 0 | if (flags & DS_GC_SERVER_REQUIRED) { |
821 | 0 | nt_version |= NETLOGON_NT_VERSION_GC; |
822 | 0 | } |
823 | |
|
824 | 0 | if (flags & DS_TRY_NEXTCLOSEST_SITE) { |
825 | 0 | nt_version |= NETLOGON_NT_VERSION_WITH_CLOSEST_SITE; |
826 | 0 | } |
827 | |
|
828 | 0 | if (flags & DS_IP_REQUIRED) { |
829 | 0 | nt_version |= NETLOGON_NT_VERSION_IP; |
830 | 0 | } |
831 | |
|
832 | 0 | return nt_version; |
833 | 0 | } |
834 | | |
835 | | /**************************************************************** |
836 | | ****************************************************************/ |
837 | | |
838 | | static NTSTATUS process_dc_dns(TALLOC_CTX *mem_ctx, |
839 | | const char *domain_name, |
840 | | uint32_t flags, |
841 | | struct ip_service_name *dclist, |
842 | | size_t num_dcs, |
843 | | struct netr_DsRGetDCNameInfo **info) |
844 | 0 | { |
845 | 0 | size_t i = 0; |
846 | 0 | struct tsocket_address **addrs = NULL; |
847 | 0 | struct netlogon_samlogon_response **responses = NULL; |
848 | 0 | const uint32_t nt_version = NETLOGON_NT_VERSION_5 | |
849 | 0 | NETLOGON_NT_VERSION_5EX; |
850 | 0 | NTSTATUS status; |
851 | |
|
852 | 0 | addrs = talloc_array(mem_ctx, struct tsocket_address *, num_dcs); |
853 | 0 | if (addrs == NULL) { |
854 | 0 | return NT_STATUS_NO_MEMORY; |
855 | 0 | } |
856 | | |
857 | 0 | for (i=0; i<num_dcs; i++) { |
858 | 0 | int ret = tsocket_address_bsd_from_samba_sockaddr( |
859 | 0 | addrs, &dclist[i].sa, &addrs[i]); |
860 | 0 | if (ret != 0) { |
861 | 0 | status = map_nt_error_from_unix(errno); |
862 | 0 | goto done; |
863 | 0 | } |
864 | 0 | } |
865 | | |
866 | 0 | status = netlogon_pings( |
867 | 0 | addrs, /* mem_ctx */ |
868 | 0 | lp_client_netlogon_ping_protocol(), /* proto */ |
869 | 0 | addrs, /* servers */ |
870 | 0 | num_dcs, /* num_servers */ |
871 | 0 | (struct netlogon_ping_filter){ |
872 | 0 | .ntversion = nt_version, |
873 | 0 | .acct_ctrl = -1, |
874 | 0 | .domain = domain_name, |
875 | 0 | .required_flags = flags, |
876 | 0 | }, |
877 | 0 | 1, /* wanted_servers */ |
878 | 0 | timeval_current_ofs(MAX(3, lp_ldap_timeout() / 2), 0), |
879 | 0 | &responses); |
880 | |
|
881 | 0 | if (!NT_STATUS_IS_OK(status)) { |
882 | 0 | status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; |
883 | 0 | goto done; |
884 | 0 | } |
885 | | |
886 | 0 | for (i = 0; i < num_dcs; i++) { |
887 | 0 | struct netlogon_samlogon_response *r = responses[i]; |
888 | |
|
889 | 0 | if (r == NULL) { |
890 | 0 | continue; |
891 | 0 | } |
892 | 0 | if (r->ntver != NETLOGON_NT_VERSION_5EX) { |
893 | 0 | continue; |
894 | 0 | } |
895 | 0 | status = make_dc_info_from_cldap_reply( |
896 | 0 | mem_ctx, flags, &dclist[i].sa, &r->data.nt5_ex, info); |
897 | 0 | if (!NT_STATUS_IS_OK(status)) { |
898 | 0 | continue; |
899 | 0 | } |
900 | 0 | status = store_cldap_reply(mem_ctx, |
901 | 0 | flags, |
902 | 0 | &dclist[i].sa, |
903 | 0 | nt_version, |
904 | 0 | &r->data.nt5_ex); |
905 | 0 | if (!NT_STATUS_IS_OK(status)) { |
906 | 0 | continue; |
907 | 0 | } |
908 | 0 | goto done; |
909 | 0 | } |
910 | | |
911 | 0 | status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; |
912 | 0 | done: |
913 | 0 | TALLOC_FREE(addrs); |
914 | 0 | return status; |
915 | 0 | } |
916 | | |
917 | | /**************************************************************** |
918 | | ****************************************************************/ |
919 | | |
920 | | /**************************************************************** |
921 | | ****************************************************************/ |
922 | | |
923 | | static NTSTATUS process_dc_netbios(TALLOC_CTX *mem_ctx, |
924 | | struct messaging_context *msg_ctx, |
925 | | const char *domain_name, |
926 | | uint32_t flags, |
927 | | struct ip_service_name *dclist, |
928 | | size_t num_dcs, |
929 | | struct netr_DsRGetDCNameInfo **info) |
930 | 0 | { |
931 | 0 | enum nbt_name_type name_type = NBT_NAME_LOGON; |
932 | 0 | NTSTATUS status; |
933 | 0 | size_t i; |
934 | 0 | const char *dc_name = NULL; |
935 | 0 | fstring tmp_dc_name; |
936 | 0 | struct netlogon_samlogon_response *r = NULL; |
937 | 0 | bool store_cache = false; |
938 | 0 | uint32_t nt_version = NETLOGON_NT_VERSION_1 | |
939 | 0 | NETLOGON_NT_VERSION_5 | |
940 | 0 | NETLOGON_NT_VERSION_5EX_WITH_IP; |
941 | 0 | size_t len = strlen(lp_netbios_name()); |
942 | 0 | char my_acct_name[len+2]; |
943 | |
|
944 | 0 | if (msg_ctx == NULL) { |
945 | 0 | return NT_STATUS_INVALID_PARAMETER; |
946 | 0 | } |
947 | | |
948 | 0 | if (flags & DS_PDC_REQUIRED) { |
949 | 0 | name_type = NBT_NAME_PDC; |
950 | 0 | } |
951 | | |
952 | | /* |
953 | | * It's 2024 we always want an AD style response! |
954 | | */ |
955 | 0 | nt_version |= NETLOGON_NT_VERSION_AVOID_NT4EMUL; |
956 | |
|
957 | 0 | nt_version |= map_ds_flags_to_nt_version(flags); |
958 | |
|
959 | 0 | snprintf(my_acct_name, |
960 | 0 | sizeof(my_acct_name), |
961 | 0 | "%s$", |
962 | 0 | lp_netbios_name()); |
963 | |
|
964 | 0 | DEBUG(10,("process_dc_netbios\n")); |
965 | |
|
966 | 0 | for (i=0; i<num_dcs; i++) { |
967 | 0 | uint16_t val; |
968 | |
|
969 | 0 | generate_random_buffer((uint8_t *)&val, 2); |
970 | |
|
971 | 0 | status = nbt_getdc(msg_ctx, 10, &dclist[i].sa.u.ss, domain_name, |
972 | 0 | NULL, my_acct_name, ACB_WSTRUST, nt_version, |
973 | 0 | mem_ctx, &nt_version, &dc_name, &r); |
974 | 0 | if (NT_STATUS_IS_OK(status)) { |
975 | 0 | store_cache = true; |
976 | 0 | namecache_store(dc_name, |
977 | 0 | NBT_NAME_SERVER, |
978 | 0 | 1, |
979 | 0 | &dclist[i].sa); |
980 | 0 | goto make_reply; |
981 | 0 | } |
982 | | |
983 | 0 | if (name_status_find(domain_name, |
984 | 0 | name_type, |
985 | 0 | NBT_NAME_SERVER, |
986 | 0 | &dclist[i].sa.u.ss, |
987 | 0 | tmp_dc_name)) |
988 | 0 | { |
989 | 0 | struct NETLOGON_SAM_LOGON_RESPONSE_NT40 logon1; |
990 | |
|
991 | 0 | r = talloc_zero(mem_ctx, struct netlogon_samlogon_response); |
992 | 0 | NT_STATUS_HAVE_NO_MEMORY(r); |
993 | | |
994 | 0 | ZERO_STRUCT(logon1); |
995 | |
|
996 | 0 | nt_version = NETLOGON_NT_VERSION_1; |
997 | |
|
998 | 0 | logon1.nt_version = nt_version; |
999 | 0 | logon1.pdc_name = tmp_dc_name; |
1000 | 0 | logon1.domain_name = talloc_strdup_upper(mem_ctx, domain_name); |
1001 | 0 | NT_STATUS_HAVE_NO_MEMORY(logon1.domain_name); |
1002 | | |
1003 | 0 | r->data.nt4 = logon1; |
1004 | 0 | r->ntver = nt_version; |
1005 | |
|
1006 | 0 | map_netlogon_samlogon_response(r); |
1007 | |
|
1008 | 0 | namecache_store(tmp_dc_name, |
1009 | 0 | NBT_NAME_SERVER, |
1010 | 0 | 1, |
1011 | 0 | &dclist[i].sa); |
1012 | |
|
1013 | 0 | goto make_reply; |
1014 | 0 | } |
1015 | 0 | } |
1016 | | |
1017 | 0 | return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; |
1018 | | |
1019 | 0 | make_reply: |
1020 | |
|
1021 | 0 | status = make_dc_info_from_cldap_reply(mem_ctx, flags, &dclist[i].sa, |
1022 | 0 | &r->data.nt5_ex, info); |
1023 | 0 | if (NT_STATUS_IS_OK(status) && store_cache) { |
1024 | 0 | return store_cldap_reply(mem_ctx, flags, &dclist[i].sa, |
1025 | 0 | nt_version, &r->data.nt5_ex); |
1026 | 0 | } |
1027 | | |
1028 | 0 | return status; |
1029 | 0 | } |
1030 | | |
1031 | | /**************************************************************** |
1032 | | ****************************************************************/ |
1033 | | |
1034 | | static NTSTATUS dsgetdcname_rediscover(TALLOC_CTX *mem_ctx, |
1035 | | struct messaging_context *msg_ctx, |
1036 | | const char *domain_name, |
1037 | | const struct GUID *domain_guid, |
1038 | | uint32_t flags, |
1039 | | const char *site_name, |
1040 | | struct netr_DsRGetDCNameInfo **info) |
1041 | 0 | { |
1042 | 0 | NTSTATUS status; |
1043 | 0 | struct ip_service_name *dclist = NULL; |
1044 | 0 | size_t num_dcs = 0; |
1045 | |
|
1046 | 0 | DEBUG(10,("dsgetdcname_rediscover\n")); |
1047 | |
|
1048 | 0 | if (flags & DS_IS_FLAT_NAME) { |
1049 | |
|
1050 | 0 | if (lp_disable_netbios()) { |
1051 | 0 | return NT_STATUS_NOT_SUPPORTED; |
1052 | 0 | } |
1053 | | |
1054 | 0 | status = discover_dc_netbios(mem_ctx, domain_name, flags, |
1055 | 0 | &dclist, &num_dcs); |
1056 | 0 | NT_STATUS_NOT_OK_RETURN(status); |
1057 | | |
1058 | 0 | return process_dc_netbios(mem_ctx, msg_ctx, domain_name, flags, |
1059 | 0 | dclist, num_dcs, info); |
1060 | 0 | } |
1061 | | |
1062 | 0 | if (flags & DS_IS_DNS_NAME) { |
1063 | |
|
1064 | 0 | status = discover_dc_dns(mem_ctx, domain_name, domain_guid, |
1065 | 0 | flags, site_name, &dclist, &num_dcs); |
1066 | 0 | NT_STATUS_NOT_OK_RETURN(status); |
1067 | | |
1068 | 0 | return process_dc_dns(mem_ctx, domain_name, flags, |
1069 | 0 | dclist, num_dcs, info); |
1070 | 0 | } |
1071 | | |
1072 | 0 | status = discover_dc_dns(mem_ctx, domain_name, domain_guid, flags, |
1073 | 0 | site_name, &dclist, &num_dcs); |
1074 | |
|
1075 | 0 | if (NT_STATUS_IS_OK(status) && num_dcs != 0) { |
1076 | |
|
1077 | 0 | status = process_dc_dns(mem_ctx, domain_name, flags, dclist, |
1078 | 0 | num_dcs, info); |
1079 | 0 | if (NT_STATUS_IS_OK(status)) { |
1080 | 0 | return status; |
1081 | 0 | } |
1082 | 0 | } |
1083 | | |
1084 | 0 | if (lp_disable_netbios()) { |
1085 | 0 | return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; |
1086 | 0 | } |
1087 | | |
1088 | 0 | status = discover_dc_netbios(mem_ctx, domain_name, flags, &dclist, |
1089 | 0 | &num_dcs); |
1090 | 0 | NT_STATUS_NOT_OK_RETURN(status); |
1091 | | |
1092 | 0 | return process_dc_netbios(mem_ctx, msg_ctx, domain_name, flags, dclist, |
1093 | 0 | num_dcs, info); |
1094 | 0 | } |
1095 | | |
1096 | | static bool is_closest_site(struct netr_DsRGetDCNameInfo *info) |
1097 | 0 | { |
1098 | 0 | if (info->dc_flags & DS_SERVER_CLOSEST) { |
1099 | 0 | return true; |
1100 | 0 | } |
1101 | | |
1102 | 0 | if (!info->client_site_name) { |
1103 | 0 | return true; |
1104 | 0 | } |
1105 | | |
1106 | 0 | if (!info->dc_site_name) { |
1107 | 0 | return false; |
1108 | 0 | } |
1109 | | |
1110 | 0 | if (strcmp(info->client_site_name, info->dc_site_name) == 0) { |
1111 | 0 | return true; |
1112 | 0 | } |
1113 | | |
1114 | 0 | return false; |
1115 | 0 | } |
1116 | | |
1117 | | /******************************************************************** |
1118 | | Internal dsgetdcname. |
1119 | | ********************************************************************/ |
1120 | | |
1121 | | static NTSTATUS dsgetdcname_internal(TALLOC_CTX *mem_ctx, |
1122 | | struct messaging_context *msg_ctx, |
1123 | | const char *domain_name, |
1124 | | const struct GUID *domain_guid, |
1125 | | const char *site_name, |
1126 | | uint32_t flags, |
1127 | | struct netr_DsRGetDCNameInfo **info) |
1128 | 0 | { |
1129 | 0 | NTSTATUS status; |
1130 | 0 | struct netr_DsRGetDCNameInfo *myinfo = NULL; |
1131 | 0 | bool first = true; |
1132 | 0 | struct netr_DsRGetDCNameInfo *first_info = NULL; |
1133 | |
|
1134 | 0 | DEBUG(10,("dsgetdcname_internal: domain_name: %s, " |
1135 | 0 | "domain_guid: %s, site_name: %s, flags: 0x%08x\n", |
1136 | 0 | domain_name, |
1137 | 0 | domain_guid ? GUID_string(mem_ctx, domain_guid) : "(null)", |
1138 | 0 | site_name ? site_name : "(null)", flags)); |
1139 | |
|
1140 | 0 | *info = NULL; |
1141 | |
|
1142 | 0 | if (DEBUGLEVEL >= DBGLVL_DEBUG) { |
1143 | 0 | char *dbg = dsdcinfo_flags_str(mem_ctx, flags); |
1144 | 0 | if (dbg != NULL) { |
1145 | 0 | DBG_DEBUG("%s", dbg); |
1146 | 0 | TALLOC_FREE(dbg); |
1147 | 0 | } |
1148 | 0 | } |
1149 | |
|
1150 | 0 | if (!check_allowed_required_flags(flags, site_name)) { |
1151 | 0 | DEBUG(0,("invalid flags specified\n")); |
1152 | 0 | return NT_STATUS_INVALID_PARAMETER; |
1153 | 0 | } |
1154 | | |
1155 | 0 | if (flags & DS_FORCE_REDISCOVERY) { |
1156 | 0 | goto rediscover; |
1157 | 0 | } |
1158 | | |
1159 | 0 | status = dsgetdcname_cached(mem_ctx, msg_ctx, domain_name, domain_guid, |
1160 | 0 | flags, site_name, &myinfo); |
1161 | 0 | if (NT_STATUS_IS_OK(status)) { |
1162 | 0 | *info = myinfo; |
1163 | 0 | goto done; |
1164 | 0 | } |
1165 | | |
1166 | 0 | if (flags & DS_BACKGROUND_ONLY) { |
1167 | 0 | goto done; |
1168 | 0 | } |
1169 | | |
1170 | 0 | rediscover: |
1171 | 0 | status = dsgetdcname_rediscover(mem_ctx, msg_ctx, domain_name, |
1172 | 0 | domain_guid, flags, site_name, |
1173 | 0 | &myinfo); |
1174 | |
|
1175 | 0 | done: |
1176 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1177 | 0 | if (!first) { |
1178 | 0 | *info = first_info; |
1179 | 0 | return NT_STATUS_OK; |
1180 | 0 | } |
1181 | 0 | return status; |
1182 | 0 | } |
1183 | | |
1184 | 0 | if (!first) { |
1185 | 0 | TALLOC_FREE(first_info); |
1186 | 0 | } else if (!is_closest_site(myinfo)) { |
1187 | 0 | first = false; |
1188 | 0 | first_info = myinfo; |
1189 | | /* TODO: may use the next_closest_site here */ |
1190 | 0 | site_name = myinfo->client_site_name; |
1191 | 0 | goto rediscover; |
1192 | 0 | } |
1193 | | |
1194 | 0 | *info = myinfo; |
1195 | 0 | return NT_STATUS_OK; |
1196 | 0 | } |
1197 | | |
1198 | | /******************************************************************** |
1199 | | dsgetdcname. |
1200 | | |
1201 | | This will be the only public function here. |
1202 | | ********************************************************************/ |
1203 | | |
1204 | | NTSTATUS dsgetdcname(TALLOC_CTX *mem_ctx, |
1205 | | struct messaging_context *msg_ctx, |
1206 | | const char *domain_name, |
1207 | | const struct GUID *domain_guid, |
1208 | | const char *site_name, |
1209 | | uint32_t flags, |
1210 | | struct netr_DsRGetDCNameInfo **info) |
1211 | 0 | { |
1212 | 0 | NTSTATUS status; |
1213 | 0 | const char *query_site = NULL; |
1214 | 0 | char *ptr_to_free = NULL; |
1215 | 0 | bool retry_query_with_null = false; |
1216 | |
|
1217 | 0 | if ((site_name == NULL) || (site_name[0] == '\0')) { |
1218 | 0 | ptr_to_free = sitename_fetch(mem_ctx, domain_name); |
1219 | 0 | if (ptr_to_free != NULL) { |
1220 | 0 | retry_query_with_null = true; |
1221 | 0 | } |
1222 | 0 | query_site = ptr_to_free; |
1223 | 0 | } else { |
1224 | 0 | query_site = site_name; |
1225 | 0 | } |
1226 | |
|
1227 | 0 | status = dsgetdcname_internal(mem_ctx, |
1228 | 0 | msg_ctx, |
1229 | 0 | domain_name, |
1230 | 0 | domain_guid, |
1231 | 0 | query_site, |
1232 | 0 | flags, |
1233 | 0 | info); |
1234 | |
|
1235 | 0 | TALLOC_FREE(ptr_to_free); |
1236 | |
|
1237 | 0 | if (!NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) { |
1238 | 0 | return status; |
1239 | 0 | } |
1240 | | |
1241 | | /* Should we try again with site_name == NULL ? */ |
1242 | 0 | if (retry_query_with_null) { |
1243 | 0 | status = dsgetdcname_internal(mem_ctx, |
1244 | 0 | msg_ctx, |
1245 | 0 | domain_name, |
1246 | 0 | domain_guid, |
1247 | 0 | NULL, |
1248 | 0 | flags, |
1249 | 0 | info); |
1250 | 0 | } |
1251 | |
|
1252 | 0 | return status; |
1253 | 0 | } |
1254 | | |
1255 | | NTSTATUS dsgetonedcname(TALLOC_CTX *mem_ctx, |
1256 | | struct messaging_context *msg_ctx, |
1257 | | const char *domain_name, |
1258 | | const char *dcname, |
1259 | | uint32_t flags, |
1260 | | struct netr_DsRGetDCNameInfo **info) |
1261 | 0 | { |
1262 | 0 | NTSTATUS status; |
1263 | 0 | struct sockaddr_storage *addrs; |
1264 | 0 | unsigned int num_addrs, i; |
1265 | 0 | const char *hostname = strip_hostname(dcname); |
1266 | |
|
1267 | 0 | status = resolve_name_list(mem_ctx, hostname, 0x20, |
1268 | 0 | &addrs, &num_addrs); |
1269 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1270 | 0 | return status; |
1271 | 0 | } |
1272 | | |
1273 | 0 | for (i = 0; i < num_addrs; i++) { |
1274 | |
|
1275 | 0 | bool ok; |
1276 | 0 | struct ip_service_name dclist; |
1277 | |
|
1278 | 0 | dclist.hostname = hostname; |
1279 | 0 | ok = sockaddr_storage_to_samba_sockaddr(&dclist.sa, &addrs[i]); |
1280 | 0 | if (!ok) { |
1281 | 0 | TALLOC_FREE(addrs); |
1282 | 0 | return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; |
1283 | 0 | } |
1284 | | |
1285 | 0 | status = process_dc_dns(mem_ctx, domain_name, flags, |
1286 | 0 | &dclist, 1, info); |
1287 | 0 | if (NT_STATUS_IS_OK(status)) { |
1288 | 0 | TALLOC_FREE(addrs); |
1289 | 0 | return NT_STATUS_OK; |
1290 | 0 | } |
1291 | | |
1292 | 0 | if (lp_disable_netbios()) { |
1293 | 0 | continue; |
1294 | 0 | } |
1295 | | |
1296 | 0 | status = process_dc_netbios(mem_ctx, msg_ctx, domain_name, flags, |
1297 | 0 | &dclist, 1, info); |
1298 | 0 | if (NT_STATUS_IS_OK(status)) { |
1299 | 0 | TALLOC_FREE(addrs); |
1300 | 0 | return NT_STATUS_OK; |
1301 | 0 | } |
1302 | 0 | } |
1303 | | |
1304 | 0 | TALLOC_FREE(addrs); |
1305 | 0 | return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; |
1306 | 0 | } |