/src/samba/dfs_server/dfs_server_ad.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | Copyright Matthieu Patou <mat@matws.net> 2010-2011 |
5 | | Copyright Stefan Metzmacher 2011 |
6 | | |
7 | | This program is free software; you can redistribute it and/or modify |
8 | | it under the terms of the GNU General Public License as published by |
9 | | the Free Software Foundation; either version 3 of the License, or |
10 | | (at your option) any later version. |
11 | | |
12 | | This program is distributed in the hope that it will be useful, |
13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | GNU General Public License for more details. |
16 | | |
17 | | You should have received a copy of the GNU General Public License |
18 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include "includes.h" |
22 | | #include "librpc/gen_ndr/dfsblobs.h" |
23 | | #include "librpc/gen_ndr/ndr_dfsblobs.h" |
24 | | #include "dsdb/samdb/samdb.h" |
25 | | #include "auth/session.h" |
26 | | #include "param/param.h" |
27 | | #include "lib/tsocket/tsocket.h" |
28 | | #include "dfs_server/dfs_server_ad.h" |
29 | | #include "lib/util/util_net.h" |
30 | | #include "libds/common/roles.h" |
31 | | |
32 | | #define MAX_DFS_RESPONSE 56*1024 /* 56 Kb */ |
33 | | |
34 | | #undef strcasecmp |
35 | | |
36 | | /* A DC set is a group of DC, they might have been grouped together |
37 | | because they belong to the same site, or to site with same cost ... |
38 | | */ |
39 | | struct dc_set { |
40 | | const char **names; |
41 | | uint32_t count; |
42 | | }; |
43 | | |
44 | | static void shuffle_dc_set(struct dc_set *list) |
45 | 0 | { |
46 | 0 | uint32_t i; |
47 | |
|
48 | 0 | for (i = list->count; i > 1; i--) { |
49 | 0 | uint32_t r; |
50 | 0 | const char *tmp; |
51 | |
|
52 | 0 | r = generate_random() % i; |
53 | |
|
54 | 0 | tmp = list->names[i - 1]; |
55 | 0 | list->names[i - 1] = list->names[r]; |
56 | 0 | list->names[r] = tmp; |
57 | 0 | } |
58 | 0 | } |
59 | | |
60 | | /* |
61 | | fill a referral type structure |
62 | | */ |
63 | | static NTSTATUS fill_normal_dfs_referraltype(TALLOC_CTX *mem_ctx, |
64 | | struct dfs_referral_type *ref, |
65 | | uint16_t version, |
66 | | const char *dfs_path, |
67 | | const char *server_path, int isfirstoffset) |
68 | 0 | { |
69 | 0 | ZERO_STRUCTP(ref); |
70 | 0 | switch (version) { |
71 | 0 | case 4: |
72 | 0 | ref->version = version; |
73 | | /* For the moment there is a bug with XP that doesn't |
74 | | * seem to appreciate much level4 so we return just |
75 | | * level 3 for everyone |
76 | | */ |
77 | 0 | ref->referral.v4.server_type = DFS_SERVER_NON_ROOT; |
78 | | /* "normal" referral seems to always include the GUID */ |
79 | 0 | ref->referral.v4.size = 34; |
80 | |
|
81 | 0 | if (isfirstoffset) { |
82 | 0 | ref->referral.v4.entry_flags = DFS_HEADER_FLAG_TARGET_BCK; |
83 | 0 | } |
84 | 0 | ref->referral.v4.ttl = 900; /* As w2k8r2 */ |
85 | 0 | ref->referral.v4.referrals.r1.DFS_path = talloc_strdup(mem_ctx, dfs_path); |
86 | 0 | if (ref->referral.v4.referrals.r1.DFS_path == NULL) { |
87 | 0 | return NT_STATUS_NO_MEMORY; |
88 | 0 | } |
89 | 0 | ref->referral.v4.referrals.r1.DFS_alt_path = talloc_strdup(mem_ctx, dfs_path); |
90 | 0 | if (ref->referral.v4.referrals.r1.DFS_alt_path == NULL) { |
91 | 0 | return NT_STATUS_NO_MEMORY; |
92 | 0 | } |
93 | 0 | ref->referral.v4.referrals.r1.netw_address = talloc_strdup(mem_ctx, server_path); |
94 | 0 | if (ref->referral.v4.referrals.r1.netw_address == NULL) { |
95 | 0 | return NT_STATUS_NO_MEMORY; |
96 | 0 | } |
97 | 0 | return NT_STATUS_OK; |
98 | 0 | case 3: |
99 | 0 | ref->version = version; |
100 | 0 | ref->referral.v3.server_type = DFS_SERVER_NON_ROOT; |
101 | | /* "normal" referral seems to always include the GUID */ |
102 | 0 | ref->referral.v3.size = 34; |
103 | |
|
104 | 0 | ref->referral.v3.entry_flags = 0; |
105 | 0 | ref->referral.v3.ttl = 600; /* As w2k3 */ |
106 | 0 | ref->referral.v3.referrals.r1.DFS_path = talloc_strdup(mem_ctx, dfs_path); |
107 | 0 | if (ref->referral.v3.referrals.r1.DFS_path == NULL) { |
108 | 0 | return NT_STATUS_NO_MEMORY; |
109 | 0 | } |
110 | 0 | ref->referral.v3.referrals.r1.DFS_alt_path = talloc_strdup(mem_ctx, dfs_path); |
111 | 0 | if (ref->referral.v3.referrals.r1.DFS_alt_path == NULL) { |
112 | 0 | return NT_STATUS_NO_MEMORY; |
113 | 0 | } |
114 | 0 | ref->referral.v3.referrals.r1.netw_address = talloc_strdup(mem_ctx, server_path); |
115 | 0 | if (ref->referral.v3.referrals.r1.netw_address == NULL) { |
116 | 0 | return NT_STATUS_NO_MEMORY; |
117 | 0 | } |
118 | 0 | return NT_STATUS_OK; |
119 | 0 | } |
120 | 0 | return NT_STATUS_INVALID_LEVEL; |
121 | 0 | } |
122 | | |
123 | | /* |
124 | | fill a domain refererral |
125 | | */ |
126 | | static NTSTATUS fill_domain_dfs_referraltype(TALLOC_CTX *mem_ctx, |
127 | | struct dfs_referral_type *ref, |
128 | | uint16_t version, |
129 | | const char *domain, |
130 | | const char **names, |
131 | | uint16_t numnames) |
132 | 0 | { |
133 | 0 | switch (version) { |
134 | 0 | case 3: |
135 | 0 | ZERO_STRUCTP(ref); |
136 | 0 | DEBUG(8, ("Called fill_domain_dfs_referraltype\n")); |
137 | 0 | ref->version = version; |
138 | 0 | ref->referral.v3.server_type = DFS_SERVER_NON_ROOT; |
139 | | #if 0 |
140 | | /* We use to have variable size, on Windows 2008R2 it's the same |
141 | | * and it seems that it gives better results so ... let's use the same |
142 | | * size. |
143 | | * |
144 | | * Additional note: XP SP2 will ask for version 3 and SP3 for version 4. |
145 | | */ |
146 | | /* |
147 | | * It's hard coded ... don't think it's a good way but the |
148 | | * sizeof return not the correct values |
149 | | * |
150 | | * We have 18 if the GUID is not included 34 otherwise |
151 | | */ |
152 | | if (numnames == 0) { |
153 | | /* Windows return without the guid when returning domain list |
154 | | */ |
155 | | ref->referral.v3.size = 18; |
156 | | } else { |
157 | | ref->referral.v3.size = 34; |
158 | | } |
159 | | #endif |
160 | | /* As seen in w2k8r2 it always return the null GUID */ |
161 | 0 | ref->referral.v3.size = 34; |
162 | 0 | ref->referral.v3.entry_flags = DFS_FLAG_REFERRAL_DOMAIN_RESP; |
163 | 0 | ref->referral.v3.ttl = 600; /* As w2k3 and w2k8r2*/ |
164 | 0 | ref->referral.v3.referrals.r2.special_name = talloc_strdup(mem_ctx, |
165 | 0 | domain); |
166 | 0 | if (ref->referral.v3.referrals.r2.special_name == NULL) { |
167 | 0 | return NT_STATUS_NO_MEMORY; |
168 | 0 | } |
169 | 0 | ref->referral.v3.referrals.r2.nb_expanded_names = numnames; |
170 | | /* Put the final terminator */ |
171 | 0 | if (names) { |
172 | 0 | int i; |
173 | 0 | const char **names2 = talloc_array(mem_ctx, const char *, |
174 | 0 | numnames+1); |
175 | 0 | NT_STATUS_HAVE_NO_MEMORY(names2); |
176 | 0 | for (i = 0; i<numnames; i++) { |
177 | 0 | names2[i] = talloc_asprintf(names2, "\\%s", names[i]); |
178 | 0 | NT_STATUS_HAVE_NO_MEMORY(names2[i]); |
179 | 0 | } |
180 | 0 | names2[numnames] = NULL; |
181 | 0 | ref->referral.v3.referrals.r2.expanded_names = names2; |
182 | 0 | } |
183 | 0 | return NT_STATUS_OK; |
184 | 0 | } |
185 | 0 | return NT_STATUS_INVALID_LEVEL; |
186 | 0 | } |
187 | | |
188 | | /* |
189 | | get the DCs list within a site |
190 | | */ |
191 | | static NTSTATUS get_dcs_insite(TALLOC_CTX *ctx, struct ldb_context *ldb, |
192 | | struct ldb_dn *sitedn, struct dc_set *list, |
193 | | bool dofqdn) |
194 | 0 | { |
195 | 0 | static const char *attrs[] = { "serverReference", NULL }; |
196 | 0 | static const char *attrs2[] = { "dNSHostName", "sAMAccountName", NULL }; |
197 | 0 | struct ldb_result *r; |
198 | 0 | unsigned int i; |
199 | 0 | int ret; |
200 | 0 | const char **dc_list; |
201 | |
|
202 | 0 | ret = ldb_search(ldb, ctx, &r, sitedn, LDB_SCOPE_SUBTREE, attrs, |
203 | 0 | "(&(objectClass=server)(serverReference=*))"); |
204 | 0 | if (ret != LDB_SUCCESS) { |
205 | 0 | DEBUG(2,(__location__ ": Failed to get list of servers - %s\n", |
206 | 0 | ldb_errstring(ldb))); |
207 | 0 | return NT_STATUS_INTERNAL_ERROR; |
208 | 0 | } |
209 | | |
210 | 0 | if (r->count == 0) { |
211 | | /* none in this site */ |
212 | 0 | talloc_free(r); |
213 | 0 | return NT_STATUS_OK; |
214 | 0 | } |
215 | | |
216 | | /* |
217 | | * need to search for all server object to know the size of the array. |
218 | | * Search all the object of class server in this site |
219 | | */ |
220 | 0 | dc_list = talloc_array(r, const char *, r->count); |
221 | 0 | if (dc_list == NULL) { |
222 | 0 | TALLOC_FREE(r); |
223 | 0 | return NT_STATUS_NO_MEMORY; |
224 | 0 | } |
225 | | |
226 | | /* TODO put some random here in the order */ |
227 | 0 | list->names = talloc_realloc(list, list->names, const char *, list->count + r->count); |
228 | 0 | if (list->names == NULL) { |
229 | 0 | TALLOC_FREE(r); |
230 | 0 | return NT_STATUS_NO_MEMORY; |
231 | 0 | } |
232 | | |
233 | 0 | for (i = 0; i<r->count; i++) { |
234 | 0 | struct ldb_dn *dn; |
235 | 0 | struct ldb_message *msg; |
236 | |
|
237 | 0 | dn = ldb_msg_find_attr_as_dn(ldb, ctx, r->msgs[i], "serverReference"); |
238 | 0 | if (!dn) { |
239 | 0 | return NT_STATUS_INTERNAL_ERROR; |
240 | 0 | } |
241 | | |
242 | 0 | ret = dsdb_search_one(ldb, r, &msg, dn, LDB_SCOPE_BASE, attrs2, 0, "(objectClass=computer)"); |
243 | 0 | if (ret != LDB_SUCCESS) { |
244 | 0 | DEBUG(2,(__location__ ": Search for computer on %s failed - %s\n", |
245 | 0 | ldb_dn_get_linearized(dn), ldb_errstring(ldb))); |
246 | 0 | return NT_STATUS_INTERNAL_ERROR; |
247 | 0 | } |
248 | | |
249 | 0 | if (dofqdn) { |
250 | 0 | const char *dns = ldb_msg_find_attr_as_string(msg, "dNSHostName", NULL); |
251 | 0 | if (dns == NULL) { |
252 | 0 | DEBUG(2,(__location__ ": dNSHostName missing on %s\n", |
253 | 0 | ldb_dn_get_linearized(dn))); |
254 | 0 | talloc_free(r); |
255 | 0 | return NT_STATUS_INTERNAL_ERROR; |
256 | 0 | } |
257 | | |
258 | 0 | list->names[list->count] = talloc_strdup(list->names, dns); |
259 | 0 | if (list->names[list->count] == NULL) { |
260 | 0 | TALLOC_FREE(r); |
261 | 0 | return NT_STATUS_NO_MEMORY; |
262 | 0 | } |
263 | 0 | } else { |
264 | 0 | char *tmp; |
265 | 0 | size_t len; |
266 | |
|
267 | 0 | const char *aname = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL); |
268 | 0 | if (aname == NULL) { |
269 | 0 | DEBUG(2,(__location__ ": sAMAccountName missing on %s\n", |
270 | 0 | ldb_dn_get_linearized(dn))); |
271 | 0 | talloc_free(r); |
272 | 0 | return NT_STATUS_INTERNAL_ERROR; |
273 | 0 | } |
274 | | |
275 | 0 | tmp = talloc_strdup(list->names, aname); |
276 | 0 | if (tmp == NULL) { |
277 | 0 | TALLOC_FREE(r); |
278 | 0 | return NT_STATUS_NO_MEMORY; |
279 | 0 | } |
280 | | |
281 | 0 | len = strlen(tmp); |
282 | 0 | if (len == 0) { |
283 | 0 | DBG_NOTICE("sAMAccountName is empty on %s\n", |
284 | 0 | ldb_dn_get_linearized(dn)); |
285 | 0 | TALLOC_FREE(r); |
286 | 0 | return NT_STATUS_INTERNAL_ERROR; |
287 | 0 | } |
288 | | |
289 | | /* Netbios name is also the sAMAccountName for |
290 | | computer but without the final $ */ |
291 | 0 | tmp[len - 1] = '\0'; |
292 | |
|
293 | 0 | list->names[list->count] = tmp; |
294 | 0 | } |
295 | 0 | list->count++; |
296 | 0 | talloc_free(msg); |
297 | 0 | } |
298 | | |
299 | 0 | shuffle_dc_set(list); |
300 | |
|
301 | 0 | talloc_free(r); |
302 | 0 | return NT_STATUS_OK; |
303 | 0 | } |
304 | | |
305 | | |
306 | | /* |
307 | | get all DCs |
308 | | */ |
309 | | static NTSTATUS get_dcs(TALLOC_CTX *ctx, struct ldb_context *ldb, |
310 | | const char *searched_site, bool need_fqdn, |
311 | | struct dc_set ***pset_list, uint32_t flags) |
312 | 0 | { |
313 | | /* |
314 | | * Flags will be used later to indicate things like least-expensive |
315 | | * or same-site options |
316 | | */ |
317 | 0 | const char *attrs_none[] = { NULL }; |
318 | 0 | const char *attrs3[] = { "name", NULL }; |
319 | 0 | struct ldb_dn *configdn, *sitedn, *dn, *sitescontainerdn; |
320 | 0 | struct ldb_result *r; |
321 | 0 | struct dc_set **set_list = NULL; |
322 | 0 | uint32_t i; |
323 | 0 | int ret; |
324 | 0 | uint32_t current_pos = 0; |
325 | 0 | NTSTATUS status; |
326 | 0 | TALLOC_CTX *subctx; |
327 | |
|
328 | 0 | *pset_list = set_list = NULL; |
329 | |
|
330 | 0 | subctx = talloc_new(ctx); |
331 | 0 | NT_STATUS_HAVE_NO_MEMORY(subctx); |
332 | | |
333 | 0 | configdn = ldb_get_config_basedn(ldb); |
334 | | |
335 | | /* Let's search for the Site container */ |
336 | 0 | ret = ldb_search(ldb, subctx, &r, configdn, LDB_SCOPE_SUBTREE, attrs_none, |
337 | 0 | "(objectClass=sitesContainer)"); |
338 | 0 | if (ret != LDB_SUCCESS) { |
339 | 0 | DEBUG(2,(__location__ ": Failed to find sitesContainer within %s - %s\n", |
340 | 0 | ldb_dn_get_linearized(configdn), ldb_errstring(ldb))); |
341 | 0 | talloc_free(subctx); |
342 | 0 | return NT_STATUS_INTERNAL_ERROR; |
343 | 0 | } |
344 | 0 | if (r->count > 1) { |
345 | 0 | DEBUG(2,(__location__ ": Expected 1 sitesContainer - found %u within %s\n", |
346 | 0 | r->count, ldb_dn_get_linearized(configdn))); |
347 | 0 | talloc_free(subctx); |
348 | 0 | return NT_STATUS_INTERNAL_ERROR; |
349 | 0 | } |
350 | | |
351 | 0 | sitescontainerdn = talloc_steal(subctx, r->msgs[0]->dn); |
352 | 0 | talloc_free(r); |
353 | | |
354 | | /* |
355 | | * TODO: Here we should have a more subtle handling |
356 | | * for the case "same-site" |
357 | | */ |
358 | 0 | ret = ldb_search(ldb, subctx, &r, sitescontainerdn, LDB_SCOPE_SUBTREE, |
359 | 0 | attrs_none, "(objectClass=server)"); |
360 | 0 | if (ret != LDB_SUCCESS) { |
361 | 0 | DEBUG(2,(__location__ ": Failed to find servers within %s - %s\n", |
362 | 0 | ldb_dn_get_linearized(sitescontainerdn), ldb_errstring(ldb))); |
363 | 0 | talloc_free(subctx); |
364 | 0 | return NT_STATUS_INTERNAL_ERROR; |
365 | 0 | } |
366 | 0 | talloc_free(r); |
367 | |
|
368 | 0 | if (searched_site != NULL && searched_site[0] != '\0') { |
369 | 0 | ret = ldb_search(ldb, subctx, &r, configdn, LDB_SCOPE_SUBTREE, |
370 | 0 | attrs_none, "(&(name=%s)(objectClass=site))", searched_site); |
371 | 0 | if (ret != LDB_SUCCESS) { |
372 | 0 | talloc_free(subctx); |
373 | 0 | return NT_STATUS_FOOBAR; |
374 | 0 | } else if (r->count != 1) { |
375 | 0 | talloc_free(subctx); |
376 | 0 | return NT_STATUS_FOOBAR; |
377 | 0 | } |
378 | | |
379 | | /* All of this was to get the DN of the searched_site */ |
380 | 0 | sitedn = r->msgs[0]->dn; |
381 | | |
382 | | /* |
383 | | * We will realloc + 2 because we will need one additional place |
384 | | * for element at current_pos + 1 for the NULL element |
385 | | */ |
386 | 0 | set_list = talloc_realloc(subctx, set_list, struct dc_set *, current_pos+2); |
387 | 0 | if (set_list == NULL) { |
388 | 0 | TALLOC_FREE(subctx); |
389 | 0 | return NT_STATUS_NO_MEMORY; |
390 | 0 | } |
391 | | |
392 | 0 | set_list[current_pos] = talloc(set_list, struct dc_set); |
393 | 0 | if (set_list[current_pos] == NULL) { |
394 | 0 | TALLOC_FREE(subctx); |
395 | 0 | return NT_STATUS_NO_MEMORY; |
396 | 0 | } |
397 | | |
398 | 0 | set_list[current_pos]->names = NULL; |
399 | 0 | set_list[current_pos]->count = 0; |
400 | |
|
401 | 0 | set_list[current_pos+1] = NULL; |
402 | |
|
403 | 0 | status = get_dcs_insite(subctx, ldb, sitedn, |
404 | 0 | set_list[current_pos], need_fqdn); |
405 | 0 | if (!NT_STATUS_IS_OK(status)) { |
406 | 0 | DEBUG(2,(__location__ ": Failed to get DC from site %s - %s\n", |
407 | 0 | ldb_dn_get_linearized(sitedn), nt_errstr(status))); |
408 | 0 | talloc_free(subctx); |
409 | 0 | return status; |
410 | 0 | } |
411 | 0 | talloc_free(r); |
412 | 0 | current_pos++; |
413 | 0 | } |
414 | | |
415 | | /* Let's find all the sites */ |
416 | 0 | ret = ldb_search(ldb, subctx, &r, configdn, LDB_SCOPE_SUBTREE, attrs3, "(objectClass=site)"); |
417 | 0 | if (ret != LDB_SUCCESS) { |
418 | 0 | DEBUG(2,(__location__ ": Failed to find any site containers in %s\n", |
419 | 0 | ldb_dn_get_linearized(configdn))); |
420 | 0 | talloc_free(subctx); |
421 | 0 | return NT_STATUS_INTERNAL_DB_CORRUPTION; |
422 | 0 | } |
423 | | |
424 | | /* |
425 | | * TODO: |
426 | | * We should randomize the order in the main site, |
427 | | * it's mostly needed for sysvol/netlogon referral. |
428 | | * Depending of flag we either randomize order of the |
429 | | * not "in the same site DCs" |
430 | | * or we randomize by group of site that have the same cost |
431 | | * In the long run we want to manipulate an array of site_set |
432 | | * All the site in one set have the same cost (if least-expansive options is selected) |
433 | | * and we will put all the dc related to 1 site set into 1 DCs set. |
434 | | * Within a site set, site order has to be randomized |
435 | | * |
436 | | * But for the moment we just return the list of sites |
437 | | */ |
438 | 0 | if (r->count) { |
439 | | /* |
440 | | * We will realloc + 2 because we will need one additional place |
441 | | * for element at current_pos + 1 for the NULL element |
442 | | */ |
443 | 0 | set_list = talloc_realloc(subctx, set_list, struct dc_set *, |
444 | 0 | current_pos+2); |
445 | 0 | if (set_list == NULL) { |
446 | 0 | TALLOC_FREE(subctx); |
447 | 0 | return NT_STATUS_NO_MEMORY; |
448 | 0 | } |
449 | | |
450 | 0 | set_list[current_pos] = talloc(ctx, struct dc_set); |
451 | 0 | if (set_list[current_pos] == NULL) { |
452 | 0 | TALLOC_FREE(subctx); |
453 | 0 | return NT_STATUS_NO_MEMORY; |
454 | 0 | } |
455 | | |
456 | 0 | set_list[current_pos]->names = NULL; |
457 | 0 | set_list[current_pos]->count = 0; |
458 | |
|
459 | 0 | set_list[current_pos+1] = NULL; |
460 | 0 | } |
461 | | |
462 | 0 | for (i=0; i<r->count; i++) { |
463 | 0 | const char *site_name = ldb_msg_find_attr_as_string(r->msgs[i], "name", NULL); |
464 | 0 | if (site_name == NULL) { |
465 | 0 | DEBUG(2,(__location__ ": Failed to find name attribute in %s\n", |
466 | 0 | ldb_dn_get_linearized(r->msgs[i]->dn))); |
467 | 0 | talloc_free(subctx); |
468 | 0 | return NT_STATUS_INTERNAL_DB_CORRUPTION; |
469 | 0 | } |
470 | | |
471 | 0 | if (searched_site == NULL || |
472 | 0 | strcmp(searched_site, site_name) != 0) { |
473 | 0 | DEBUG(2, |
474 | 0 | (__location__ ": Site: %s %s\n", |
475 | 0 | searched_site != NULL ? searched_site |
476 | 0 | : "UNKNOWN", |
477 | 0 | site_name)); |
478 | | |
479 | | /* |
480 | | * Do all the site but the one of the client |
481 | | * (because it has already been done ...) |
482 | | */ |
483 | 0 | dn = r->msgs[i]->dn; |
484 | |
|
485 | 0 | status = get_dcs_insite(subctx, ldb, dn, |
486 | 0 | set_list[current_pos], |
487 | 0 | need_fqdn); |
488 | 0 | if (!NT_STATUS_IS_OK(status)) { |
489 | 0 | talloc_free(subctx); |
490 | 0 | return status; |
491 | 0 | } |
492 | 0 | } |
493 | 0 | } |
494 | | |
495 | 0 | *pset_list = talloc_move(ctx, &set_list); |
496 | 0 | talloc_free(subctx); |
497 | 0 | return NT_STATUS_OK; |
498 | 0 | } |
499 | | |
500 | | static NTSTATUS dodomain_referral(struct loadparm_context *lp_ctx, |
501 | | struct ldb_context *sam_ctx, |
502 | | const struct tsocket_address *client, |
503 | | struct dfs_GetDFSReferral *r) |
504 | 0 | { |
505 | | /* |
506 | | * TODO for the moment we just return the local domain |
507 | | */ |
508 | 0 | NTSTATUS status; |
509 | 0 | const char *dns_domain = lpcfg_dnsdomain(lp_ctx); |
510 | 0 | const char *netbios_domain = lpcfg_workgroup(lp_ctx); |
511 | 0 | struct dfs_referral_type *referrals; |
512 | 0 | const char *referral_str; |
513 | | /* In the future this needs to be fetched from the ldb */ |
514 | 0 | uint32_t found_domain = 2; |
515 | |
|
516 | 0 | if (lpcfg_server_role(lp_ctx) != ROLE_ACTIVE_DIRECTORY_DC) { |
517 | 0 | DEBUG(10 ,("Received a domain referral request on a non DC\n")); |
518 | 0 | return NT_STATUS_INVALID_PARAMETER; |
519 | 0 | } |
520 | | |
521 | 0 | if (r->in.req.max_referral_level < 3) { |
522 | 0 | DEBUG(2,("invalid max_referral_level %u\n", |
523 | 0 | r->in.req.max_referral_level)); |
524 | 0 | return NT_STATUS_UNSUCCESSFUL; |
525 | 0 | } |
526 | | |
527 | 0 | r->out.resp = talloc_zero(r, struct dfs_referral_resp); |
528 | 0 | if (r->out.resp == NULL) { |
529 | 0 | return NT_STATUS_NO_MEMORY; |
530 | 0 | } |
531 | | |
532 | 0 | r->out.resp->path_consumed = 0; |
533 | 0 | r->out.resp->header_flags = 0; /* Do like w2k3 */ |
534 | 0 | r->out.resp->nb_referrals = found_domain; /* the fqdn one + the NT domain */ |
535 | |
|
536 | 0 | referrals = talloc_zero_array(r->out.resp, |
537 | 0 | struct dfs_referral_type, |
538 | 0 | r->out.resp->nb_referrals); |
539 | 0 | if (referrals == NULL) { |
540 | 0 | return NT_STATUS_NO_MEMORY; |
541 | 0 | } |
542 | 0 | r->out.resp->referral_entries = referrals; |
543 | |
|
544 | 0 | referral_str = talloc_asprintf(r, "\\%s", netbios_domain); |
545 | 0 | if (referral_str == NULL) { |
546 | 0 | return NT_STATUS_NO_MEMORY; |
547 | 0 | } |
548 | | |
549 | 0 | status = fill_domain_dfs_referraltype(referrals, |
550 | 0 | &referrals[0], 3, |
551 | 0 | referral_str, |
552 | 0 | NULL, 0); |
553 | 0 | if (!NT_STATUS_IS_OK(status)) { |
554 | 0 | DEBUG(2,("%s: Unable to fill domain referral structure - %s\n", |
555 | 0 | __location__, nt_errstr(status))); |
556 | 0 | return status; |
557 | 0 | } |
558 | | |
559 | 0 | referral_str = talloc_asprintf(r, "\\%s", dns_domain); |
560 | 0 | if (referral_str == NULL) { |
561 | 0 | return NT_STATUS_NO_MEMORY; |
562 | 0 | } |
563 | | |
564 | 0 | status = fill_domain_dfs_referraltype(referrals, |
565 | 0 | &referrals[1], 3, |
566 | 0 | referral_str, |
567 | 0 | NULL, 0); |
568 | 0 | if (!NT_STATUS_IS_OK(status)) { |
569 | 0 | DEBUG(2,("%s: Unable to fill domain referral structure - %s\n", |
570 | 0 | __location__, nt_errstr(status))); |
571 | 0 | return status; |
572 | 0 | } |
573 | | |
574 | 0 | return NT_STATUS_OK; |
575 | 0 | } |
576 | | |
577 | | /* |
578 | | * Handle the logic for dfs referral request like |
579 | | * \\dns_domain or \\netbios_domain. |
580 | | */ |
581 | | static NTSTATUS dodc_referral(struct loadparm_context *lp_ctx, |
582 | | struct ldb_context *sam_ctx, |
583 | | const struct tsocket_address *client, |
584 | | struct dfs_GetDFSReferral *r, |
585 | | const char *domain_name) |
586 | 0 | { |
587 | 0 | NTSTATUS status; |
588 | 0 | const char *site_name = NULL; /* Name of the site where the client is */ |
589 | 0 | bool need_fqdn = false; |
590 | 0 | unsigned int i; |
591 | 0 | const char **dc_list = NULL; |
592 | 0 | uint32_t num_dcs = 0; |
593 | 0 | struct dc_set **set; |
594 | 0 | char *client_str = NULL; |
595 | 0 | struct dfs_referral_type *referrals; |
596 | 0 | const char *referral_str; |
597 | |
|
598 | 0 | if (lpcfg_server_role(lp_ctx) != ROLE_ACTIVE_DIRECTORY_DC) { |
599 | 0 | return NT_STATUS_INVALID_PARAMETER; |
600 | 0 | } |
601 | | |
602 | 0 | if (r->in.req.max_referral_level < 3) { |
603 | 0 | DEBUG(2,("invalid max_referral_level %u\n", |
604 | 0 | r->in.req.max_referral_level)); |
605 | 0 | return NT_STATUS_UNSUCCESSFUL; |
606 | 0 | } |
607 | | |
608 | 0 | DEBUG(10, ("in this we have request for %s requested is %s\n", |
609 | 0 | domain_name, r->in.req.servername)); |
610 | |
|
611 | 0 | if (strchr(domain_name,'.')) { |
612 | 0 | need_fqdn = 1; |
613 | 0 | } |
614 | |
|
615 | 0 | if (tsocket_address_is_inet(client, "ip")) { |
616 | 0 | client_str = tsocket_address_inet_addr_string(client, r); |
617 | 0 | if (client_str == NULL) { |
618 | 0 | return NT_STATUS_NO_MEMORY; |
619 | 0 | } |
620 | 0 | } |
621 | | |
622 | 0 | site_name = samdb_client_site_name(sam_ctx, r, client_str, NULL, true); |
623 | |
|
624 | 0 | status = get_dcs(r, sam_ctx, site_name, need_fqdn, &set, 0); |
625 | 0 | if (!NT_STATUS_IS_OK(status)) { |
626 | 0 | DEBUG(3,("Unable to get list of DCs - %s\n", |
627 | 0 | nt_errstr(status))); |
628 | 0 | return status; |
629 | 0 | } |
630 | | |
631 | 0 | for(i=0; set[i]; i++) { |
632 | 0 | uint32_t j; |
633 | |
|
634 | 0 | dc_list = talloc_realloc(r, dc_list, const char*, |
635 | 0 | num_dcs + set[i]->count + 1); |
636 | 0 | if (dc_list == NULL) { |
637 | 0 | return NT_STATUS_NO_MEMORY; |
638 | 0 | } |
639 | | |
640 | 0 | for(j=0; j<set[i]->count; j++) { |
641 | 0 | dc_list[num_dcs + j] = talloc_move(dc_list, |
642 | 0 | &set[i]->names[j]); |
643 | 0 | } |
644 | 0 | num_dcs = num_dcs + set[i]->count; |
645 | 0 | TALLOC_FREE(set[i]); |
646 | 0 | dc_list[num_dcs] = NULL; |
647 | 0 | } |
648 | | |
649 | 0 | r->out.resp = talloc_zero(r, struct dfs_referral_resp); |
650 | 0 | if (r->out.resp == NULL) { |
651 | 0 | return NT_STATUS_NO_MEMORY; |
652 | 0 | } |
653 | | |
654 | 0 | r->out.resp->path_consumed = 0; |
655 | 0 | r->out.resp->header_flags = 0; /* Do like w2k3 */ |
656 | 0 | r->out.resp->nb_referrals = 1; |
657 | |
|
658 | 0 | referrals = talloc_zero_array(r->out.resp, |
659 | 0 | struct dfs_referral_type, |
660 | 0 | r->out.resp->nb_referrals); |
661 | 0 | if (referrals == NULL) { |
662 | 0 | return NT_STATUS_NO_MEMORY; |
663 | 0 | } |
664 | 0 | r->out.resp->referral_entries = referrals; |
665 | |
|
666 | 0 | if (r->in.req.servername[0] == '\\') { |
667 | 0 | referral_str = talloc_asprintf(referrals, "%s", |
668 | 0 | domain_name); |
669 | 0 | } else { |
670 | 0 | referral_str = talloc_asprintf(referrals, "\\%s", |
671 | 0 | domain_name); |
672 | 0 | } |
673 | 0 | if (referral_str == NULL) { |
674 | 0 | return NT_STATUS_NO_MEMORY; |
675 | 0 | } |
676 | | |
677 | 0 | status = fill_domain_dfs_referraltype(referrals, |
678 | 0 | &referrals[0], 3, |
679 | 0 | referral_str, |
680 | 0 | dc_list, num_dcs); |
681 | 0 | if (!NT_STATUS_IS_OK(status)) { |
682 | 0 | DEBUG(2,("%s: Unable to fill domain referral structure - %s\n", |
683 | 0 | __location__, nt_errstr(status))); |
684 | 0 | return status; |
685 | 0 | } |
686 | | |
687 | 0 | return NT_STATUS_OK; |
688 | 0 | } |
689 | | |
690 | | /* |
691 | | * Handle the logic for dfs referral request like |
692 | | * \\domain\sysvol or \\domain\netlogon |
693 | | */ |
694 | | static NTSTATUS dosysvol_referral(struct loadparm_context *lp_ctx, |
695 | | struct ldb_context *sam_ctx, |
696 | | const struct tsocket_address *client, |
697 | | struct dfs_GetDFSReferral *r, |
698 | | const char *domain_name, |
699 | | const char *dfs_name) |
700 | 0 | { |
701 | 0 | const char *site_name = NULL; /* Name of the site where the client is */ |
702 | 0 | bool need_fqdn = false; |
703 | 0 | unsigned int i, c = 0, nb_entries = 0; |
704 | 0 | struct dc_set **set; |
705 | 0 | char *client_str = NULL; |
706 | 0 | NTSTATUS status; |
707 | 0 | struct dfs_referral_type *referrals; |
708 | |
|
709 | 0 | if (lpcfg_server_role(lp_ctx) != ROLE_ACTIVE_DIRECTORY_DC) { |
710 | 0 | return NT_STATUS_INVALID_PARAMETER; |
711 | 0 | } |
712 | | |
713 | 0 | if (r->in.req.max_referral_level < 3) { |
714 | 0 | DEBUG(2,("invalid max_referral_level %u\n", |
715 | 0 | r->in.req.max_referral_level)); |
716 | 0 | return NT_STATUS_UNSUCCESSFUL; |
717 | 0 | } |
718 | | |
719 | 0 | DEBUG(10, ("in this we have request for %s and share %s requested is %s\n", |
720 | 0 | domain_name, dfs_name, r->in.req.servername)); |
721 | |
|
722 | 0 | if (strchr(domain_name,'.')) { |
723 | 0 | need_fqdn = 1; |
724 | 0 | } |
725 | |
|
726 | 0 | if (tsocket_address_is_inet(client, "ip")) { |
727 | 0 | client_str = tsocket_address_inet_addr_string(client, r); |
728 | 0 | if (client_str == NULL) { |
729 | 0 | return NT_STATUS_NO_MEMORY; |
730 | 0 | } |
731 | 0 | } |
732 | | |
733 | 0 | site_name = samdb_client_site_name(sam_ctx, r, client_str, NULL, true); |
734 | |
|
735 | 0 | status = get_dcs(r, sam_ctx, site_name, need_fqdn, &set, 0); |
736 | 0 | if (!NT_STATUS_IS_OK(status)) { |
737 | 0 | DEBUG(3,("Unable to get list of DCs - %s\n", |
738 | 0 | nt_errstr(status))); |
739 | 0 | return status; |
740 | 0 | } |
741 | | |
742 | 0 | for(i=0; set[i]; i++) { |
743 | 0 | nb_entries = nb_entries + set[i]->count; |
744 | 0 | } |
745 | |
|
746 | 0 | r->out.resp = talloc_zero(r, struct dfs_referral_resp); |
747 | 0 | if (r->out.resp == NULL) { |
748 | 0 | return NT_STATUS_NO_MEMORY; |
749 | 0 | } |
750 | | |
751 | | /* The length is expected in bytes */ |
752 | 0 | r->out.resp->path_consumed = strlen_m(r->in.req.servername) * 2; |
753 | | /* Do like w2k3 and like in 3.3.5.3 of MS-DFSC*/ |
754 | 0 | r->out.resp->header_flags = DFS_HEADER_FLAG_STORAGE_SVR; |
755 | 0 | r->out.resp->nb_referrals = nb_entries; |
756 | |
|
757 | 0 | referrals = talloc_zero_array(r->out.resp, |
758 | 0 | struct dfs_referral_type, |
759 | 0 | r->out.resp->nb_referrals); |
760 | 0 | if (referrals == NULL) { |
761 | 0 | return NT_STATUS_NO_MEMORY; |
762 | 0 | } |
763 | 0 | r->out.resp->referral_entries = referrals; |
764 | |
|
765 | 0 | c = 0; |
766 | 0 | for(i=0; set[i]; i++) { |
767 | 0 | uint32_t j; |
768 | |
|
769 | 0 | for(j=0; j< set[i]->count; j++) { |
770 | 0 | struct dfs_referral_type *ref = &referrals[c]; |
771 | 0 | const char *referral_str; |
772 | |
|
773 | 0 | referral_str = talloc_asprintf(referrals, "\\%s\\%s", |
774 | 0 | set[i]->names[j], dfs_name); |
775 | 0 | if (referral_str == NULL) { |
776 | 0 | return NT_STATUS_NO_MEMORY; |
777 | 0 | } |
778 | | |
779 | 0 | DEBUG(8,("Doing a dfs referral for %s with this value " |
780 | 0 | "%s requested %s\n", |
781 | 0 | set[i]->names[j], referral_str, |
782 | 0 | r->in.req.servername)); |
783 | |
|
784 | 0 | status = fill_normal_dfs_referraltype(referrals, ref, |
785 | 0 | r->in.req.max_referral_level, |
786 | 0 | r->in.req.servername, |
787 | 0 | referral_str, c==0); |
788 | | |
789 | |
|
790 | 0 | if (!NT_STATUS_IS_OK(status)) { |
791 | 0 | DEBUG(2,("%s: Unable to fill domain referral " |
792 | 0 | "structure - %s\n", |
793 | 0 | __location__, nt_errstr(status))); |
794 | 0 | return status; |
795 | 0 | } |
796 | | |
797 | 0 | c++; |
798 | 0 | } |
799 | 0 | } |
800 | | |
801 | 0 | return NT_STATUS_OK; |
802 | 0 | } |
803 | | |
804 | | /* |
805 | | trans2 getdfsreferral implementation |
806 | | */ |
807 | | NTSTATUS dfs_server_ad_get_referrals(struct loadparm_context *lp_ctx, |
808 | | struct ldb_context *sam_ctx, |
809 | | const struct tsocket_address *client, |
810 | | struct dfs_GetDFSReferral *r) |
811 | 0 | { |
812 | 0 | char *server_name = NULL; |
813 | 0 | char *dfs_name = NULL; |
814 | 0 | char *link_path = NULL; |
815 | 0 | const char *netbios_domain; |
816 | 0 | const char *dns_domain; |
817 | 0 | const char *netbios_name; |
818 | 0 | const char *dns_hostname = NULL; |
819 | 0 | const char **netbios_aliases; |
820 | 0 | char path_separator; |
821 | |
|
822 | 0 | if (!lpcfg_host_msdfs(lp_ctx)) { |
823 | 0 | return NT_STATUS_FS_DRIVER_REQUIRED; |
824 | 0 | } |
825 | | |
826 | 0 | if (r->in.req.servername == NULL) { |
827 | 0 | return NT_STATUS_INVALID_PARAMETER; |
828 | 0 | } |
829 | | |
830 | 0 | DEBUG(8, ("Requested DFS name: %s length: %u\n", |
831 | 0 | r->in.req.servername, |
832 | 0 | (unsigned int)strlen_m(r->in.req.servername)*2)); |
833 | | |
834 | | /* |
835 | | * If the servername is "" then we are in a case of domain dfs |
836 | | * and the client just searches for the list of local domain |
837 | | * it is attached and also trusted ones. |
838 | | */ |
839 | 0 | if (strlen(r->in.req.servername) == 0) { |
840 | 0 | return dodomain_referral(lp_ctx, sam_ctx, client, r); |
841 | 0 | } |
842 | | |
843 | 0 | server_name = talloc_strdup(r, r->in.req.servername); |
844 | 0 | if (server_name == NULL) { |
845 | 0 | return NT_STATUS_NO_MEMORY; |
846 | 0 | } |
847 | | |
848 | 0 | path_separator = (*server_name == '/') ? '/' : '\\'; |
849 | |
|
850 | 0 | while(*server_name && *server_name == path_separator) { |
851 | 0 | server_name++; |
852 | 0 | } |
853 | |
|
854 | 0 | dfs_name = strchr_m(server_name, path_separator); |
855 | 0 | if (dfs_name != NULL) { |
856 | 0 | dfs_name[0] = '\0'; |
857 | 0 | dfs_name++; |
858 | |
|
859 | 0 | link_path = strchr_m(dfs_name, path_separator); |
860 | 0 | if (link_path != NULL) { |
861 | 0 | link_path[0] = '\0'; |
862 | 0 | link_path++; |
863 | 0 | } |
864 | 0 | } |
865 | |
|
866 | 0 | if (link_path != NULL) { |
867 | | /* |
868 | | * If it is a DFS Link we do not |
869 | | * handle it here. |
870 | | */ |
871 | 0 | return NT_STATUS_NOT_FOUND; |
872 | 0 | } |
873 | | |
874 | 0 | netbios_domain = lpcfg_workgroup(lp_ctx); |
875 | 0 | dns_domain = lpcfg_dnsdomain(lp_ctx); |
876 | 0 | netbios_name = lpcfg_netbios_name(lp_ctx); |
877 | 0 | dns_hostname = lpcfg_dns_hostname(lp_ctx); |
878 | 0 | if (dns_hostname == NULL) { |
879 | 0 | return NT_STATUS_NO_MEMORY; |
880 | 0 | } |
881 | | |
882 | 0 | if ((strcasecmp_m(server_name, netbios_name) == 0) || |
883 | 0 | (strcasecmp_m(server_name, dns_hostname) == 0)) { |
884 | | /* |
885 | | * If it is not domain related do not |
886 | | * handle it here. |
887 | | */ |
888 | 0 | return NT_STATUS_NOT_FOUND; |
889 | 0 | } |
890 | | |
891 | 0 | if (is_ipaddress(server_name)) { |
892 | | /* |
893 | | * If it is not domain related do not |
894 | | * handle it here. |
895 | | */ |
896 | 0 | return NT_STATUS_NOT_FOUND; |
897 | 0 | } |
898 | | |
899 | 0 | netbios_aliases = lpcfg_netbios_aliases(lp_ctx); |
900 | 0 | while (netbios_aliases && *netbios_aliases) { |
901 | 0 | const char *netbios_alias = *netbios_aliases; |
902 | 0 | char *dns_alias; |
903 | 0 | int cmp; |
904 | |
|
905 | 0 | cmp = strcasecmp_m(server_name, netbios_alias); |
906 | 0 | if (cmp == 0) { |
907 | | /* |
908 | | * If it is not domain related do not |
909 | | * handle it here. |
910 | | */ |
911 | 0 | return NT_STATUS_NOT_FOUND; |
912 | 0 | } |
913 | | |
914 | 0 | dns_alias = talloc_asprintf(r, "%s.%s", |
915 | 0 | netbios_alias, |
916 | 0 | dns_domain); |
917 | 0 | if (dns_alias == NULL) { |
918 | 0 | return NT_STATUS_NO_MEMORY; |
919 | 0 | } |
920 | | |
921 | 0 | cmp = strcasecmp_m(server_name, dns_alias); |
922 | 0 | talloc_free(dns_alias); |
923 | 0 | if (cmp == 0) { |
924 | | /* |
925 | | * If it is not domain related do not |
926 | | * handle it here. |
927 | | */ |
928 | 0 | return NT_STATUS_NOT_FOUND; |
929 | 0 | } |
930 | 0 | netbios_aliases++; |
931 | 0 | } |
932 | | |
933 | 0 | if ((strcasecmp_m(server_name, netbios_domain) != 0) && |
934 | 0 | (strcasecmp_m(server_name, dns_domain) != 0)) { |
935 | | /* |
936 | | * Not a domain we handle. |
937 | | */ |
938 | 0 | return NT_STATUS_INVALID_PARAMETER; |
939 | 0 | } |
940 | | |
941 | | /* |
942 | | * Here we have filtered the thing the requested name don't contain our DNS name. |
943 | | * So if the share == NULL or if share in ("sysvol", "netlogon") |
944 | | * then we proceed. In the first case it will be a dc refereal in the second it will |
945 | | * be just a sysvol/netlogon referral. |
946 | | */ |
947 | 0 | if (dfs_name == NULL) { |
948 | 0 | return dodc_referral(lp_ctx, sam_ctx, |
949 | 0 | client, r, server_name); |
950 | 0 | } |
951 | | |
952 | | /* |
953 | | * Here we have filtered the thing the requested name don't contain our DNS name. |
954 | | * So if the share == NULL or if share in ("sysvol", "netlogon") |
955 | | * then we proceed. In the first case it will be a dc refereal in the second it will |
956 | | * be just a sysvol/netlogon referral. |
957 | | */ |
958 | 0 | if (strcasecmp(dfs_name, "sysvol") == 0 || |
959 | 0 | strcasecmp(dfs_name, "netlogon") == 0) { |
960 | 0 | return dosysvol_referral(lp_ctx, sam_ctx, client, r, |
961 | 0 | server_name, dfs_name); |
962 | 0 | } |
963 | | |
964 | | /* By default until all the case are handled */ |
965 | 0 | return NT_STATUS_NOT_FOUND; |
966 | 0 | } |