s4:operational LDB module - enable support for passing referrals through it
[Samba/ekacnet.git] / source4 / dsdb / kcc / kcc_topology.c
blob571bd8482eedb225fe5ee5ab297aca528285a57c
1 /*
2 Unix SMB/CIFS implementation.
4 KCC service
6 Copyright (C) Crístian Deives 2010
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "dsdb/samdb/samdb.h"
25 #include "lib/messaging/irpc.h"
26 #include "librpc/gen_ndr/ndr_misc.h"
28 #define NTDSTRANSPORT_OPT_IGNORE_SCHEDULES 0x00000001
29 #define NTDSTRANSPORT_OPT_BRIDGES_REQUIRED 0x00000002
31 /** replication parameters of a graph edge */
32 struct kcctpl_repl_info {
33 int cost;
34 int interval;
35 int options;
36 uint8_t schedule[84];
39 /** color of a vertex */
40 enum kcctpl_color { RED, BLACK, WHITE };
42 /** a GUID array list */
43 struct GUID_list {
44 struct GUID *data;
45 unsigned int count;
48 /** a vertex in the site graph */
49 struct kcctpl_vertex {
50 struct GUID id;
51 struct GUID_list edge_ids;
52 enum kcctpl_color color;
53 struct GUID_list accept_red_red;
54 struct GUID_list accept_black;
55 struct kcctpl_repl_info repl_info;
56 int dist_to_red;
58 /* Dijkstra data */
59 struct GUID root_id;
60 bool demoted;
62 /* Kruskal data */
63 struct GUID component_id;
64 int component_index;
67 /** fully connected subgraph of vertices */
68 struct kcctpl_multi_edge {
69 struct GUID id;
70 struct GUID_list vertex_ids;
71 struct GUID type;
72 struct kcctpl_repl_info repl_info;
73 bool directed;
76 /** set of transitively connected kcc_multi_edge's. all edges within the set
77 * have the same type. */
78 struct kcctpl_multi_edge_set {
79 struct GUID id;
80 struct GUID_list edge_ids;
83 /** a vertices array list */
84 struct kcctpl_vertex_list {
85 struct kcctpl_vertex *data;
86 unsigned int count;
89 /** an edges linked list */
90 struct kcctpl_multi_edge_list {
91 struct kcctpl_multi_edge *data;
92 unsigned int count;
95 /** an edge sets linked list */
96 struct kcctpl_multi_edge_set_list {
97 struct kcctpl_multi_edge_set *data;
98 unsigned int count;
101 /** a site graph */
102 struct kcctpl_graph {
103 struct kcctpl_vertex_list vertices;
104 struct kcctpl_multi_edge_list edges;
105 struct kcctpl_multi_edge_set_list edge_sets;
108 /** path found in the graph between two non-white vertices */
109 struct kcctpl_internal_edge {
110 struct GUID v1id, v2id;
111 bool red_red;
112 struct kcctpl_repl_info repl_info;
113 struct GUID type;
117 * find a graph edge based on its GUID.
119 static struct kcctpl_multi_edge *kcctpl_find_edge_by_guid(struct kcctpl_graph *graph,
120 struct GUID guid)
122 unsigned int i;
124 for (i = 0; i < graph->edges.count; i++) {
125 if (GUID_compare(&graph->edges.data[i].id, &guid) == 0) {
126 return &graph->edges.data[i];
129 return NULL;
132 * create a kcctpl_graph instance.
134 static NTSTATUS kcctpl_create_graph(TALLOC_CTX *mem_ctx,
135 struct GUID_list *guids,
136 struct kcctpl_graph **_graph)
138 struct kcctpl_graph *graph;
139 unsigned int i;
141 graph = talloc_zero(mem_ctx, struct kcctpl_graph);
142 NT_STATUS_HAVE_NO_MEMORY(graph);
144 graph->vertices.count = guids->count;
145 graph->vertices.data = talloc_zero_array(graph, struct kcctpl_vertex,
146 guids->count);
147 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(graph->vertices.data, graph);
149 TYPESAFE_QSORT(guids->data, guids->count, GUID_compare);
151 for (i = 0; i < guids->count; i++) {
152 graph->vertices.data[i].id = guids->data[i];
155 *_graph = graph;
156 return NT_STATUS_OK;
160 * create a kcctpl_multi_edge instance.
162 static NTSTATUS kcctpl_create_edge(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
163 struct GUID type,
164 struct ldb_message *site_link,
165 struct kcctpl_multi_edge **_edge)
167 struct kcctpl_multi_edge *edge;
168 TALLOC_CTX *tmp_ctx;
169 struct ldb_result *res;
170 const char * const attrs[] = { "siteList", NULL };
171 int ret;
172 struct ldb_message_element *el;
173 unsigned int i;
174 struct ldb_val val;
176 edge = talloc(mem_ctx, struct kcctpl_multi_edge);
177 NT_STATUS_HAVE_NO_MEMORY(edge);
179 edge->id = samdb_result_guid(site_link, "objectGUID");
181 tmp_ctx = talloc_new(mem_ctx);
182 ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_BASE, attrs,
183 "objectGUID=%s", GUID_string(tmp_ctx, &edge->id));
184 if (ret != LDB_SUCCESS) {
185 DEBUG(0, ("failed to find siteLink object %s: %s\n",
186 GUID_string(tmp_ctx, &edge->id), ldb_strerror(ret)));
187 talloc_free(edge);
188 talloc_free(tmp_ctx);
189 return NT_STATUS_INTERNAL_DB_CORRUPTION;
191 if (res->count == 0) {
192 DEBUG(0, ("failed to find siteLink object %s\n",
193 GUID_string(tmp_ctx, &edge->id)));
194 talloc_free(edge);
195 talloc_free(tmp_ctx);
196 return NT_STATUS_INTERNAL_DB_CORRUPTION;
199 el = ldb_msg_find_element(res->msgs[0], "siteList");
200 edge->vertex_ids.count = el->num_values;
201 edge->vertex_ids.data = talloc_array(edge, struct GUID, el->num_values);
202 if (!edge->vertex_ids.data) {
203 talloc_free(edge);
204 talloc_free(tmp_ctx);
205 return NT_STATUS_NO_MEMORY;
208 for (i = 0; i < el->num_values; i++) {
209 struct ldb_dn *dn;
210 struct GUID guid;
212 val = el->values[i];
213 dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &val);
214 ret = dsdb_find_guid_by_dn(ldb, dn, &guid);
215 if (ret != LDB_SUCCESS) {
216 DEBUG(0, ("failed to find objectGUID for object %s: "
217 "%s\n", ldb_dn_get_linearized(dn),
218 ldb_strerror(ret)));
219 talloc_free(edge);
220 talloc_free(tmp_ctx);
221 return NT_STATUS_INTERNAL_DB_CORRUPTION;
224 edge->vertex_ids.data = talloc_realloc(edge,
225 edge->vertex_ids.data,
226 struct GUID,
227 edge->vertex_ids.count + 1);
228 if (!edge->vertex_ids.data) {
229 talloc_free(edge);
230 talloc_free(tmp_ctx);
231 return NT_STATUS_NO_MEMORY;
233 edge->vertex_ids.data[edge->vertex_ids.count] = guid;
234 edge->vertex_ids.count++;
237 edge->repl_info.cost = samdb_result_int64(site_link, "cost", 0);
238 edge->repl_info.options = samdb_result_int64(site_link, "options", 0);
239 edge->repl_info.interval = samdb_result_int64(site_link, "replInterval",
241 /* val = ldb_msg_find_ldb_val(site_link, "schedule");
242 edge->repl_info.schedule = val->data; */
243 edge->type = type;
244 edge->directed = false;
246 *_edge = edge;
247 talloc_free(tmp_ctx);
248 return NT_STATUS_OK;
252 * create a kcctpl_multi_edge_set instance containing edges for all siteLink
253 * objects.
255 static NTSTATUS kcctpl_create_auto_edge_set(struct kcctpl_graph *graph,
256 struct GUID type,
257 struct ldb_result *res_site_link,
258 struct kcctpl_multi_edge_set **_set)
260 struct kcctpl_multi_edge_set *set;
261 unsigned int i;
263 set = talloc_zero(graph, struct kcctpl_multi_edge_set);
264 NT_STATUS_HAVE_NO_MEMORY(set);
266 for (i = 0; i < res_site_link->count; i++) {
267 struct GUID site_link_guid;
268 struct kcctpl_multi_edge *edge;
270 site_link_guid = samdb_result_guid(res_site_link->msgs[i],
271 "objectGUID");
272 edge = kcctpl_find_edge_by_guid(graph, site_link_guid);
273 if (!edge) {
274 TALLOC_CTX *tmp_ctx = talloc_new(graph);
275 DEBUG(0, ("failed to find a graph edge with ID=%s\n",
276 GUID_string(tmp_ctx, &site_link_guid)));
277 talloc_free(tmp_ctx);
278 return NT_STATUS_INTERNAL_DB_CORRUPTION;
281 if (GUID_compare(&edge->type, &type) == 0) {
282 set->edge_ids.data = talloc_realloc(set,
283 set->edge_ids.data,
284 struct GUID,
285 set->edge_ids.count + 1);
286 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set->edge_ids.data,
287 set);
288 set->edge_ids.data[set->edge_ids.count] = site_link_guid;
289 set->edge_ids.count++;
293 *_set = set;
294 return NT_STATUS_OK;
298 * create a kcctpl_multi_edge_set instance.
300 static NTSTATUS kcctpl_create_edge_set(struct ldb_context *ldb,
301 struct kcctpl_graph *graph,
302 struct GUID type,
303 struct ldb_message *bridge,
304 struct kcctpl_multi_edge_set **_set)
306 struct kcctpl_multi_edge_set *set;
307 struct ldb_message_element *el;
308 unsigned int i;
310 set = talloc_zero(graph, struct kcctpl_multi_edge_set);
311 NT_STATUS_HAVE_NO_MEMORY(set);
313 set->id = samdb_result_guid(bridge, "objectGUID");
315 el = ldb_msg_find_element(bridge, "siteLinkList");
316 for (i = 0; i < el->num_values; i++) {
317 struct ldb_val val;
318 TALLOC_CTX *tmp_ctx;
319 struct ldb_dn *dn;
320 struct GUID site_link_guid;
321 int ret;
322 struct kcctpl_multi_edge *edge;
324 val = el->values[i];
325 tmp_ctx = talloc_new(graph);
326 dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &val);
327 if (!ldb_dn_validate(dn)) {
328 DEBUG(0, ("invalid DN in siteLinkList attr of %s\n",
329 GUID_string(tmp_ctx, &set->id)));
330 talloc_free(set);
331 talloc_free(tmp_ctx);
332 return NT_STATUS_INTERNAL_DB_CORRUPTION;
335 ret = dsdb_find_guid_by_dn(ldb, dn, &site_link_guid);
336 if (ret != LDB_SUCCESS) {
337 DEBUG(0, ("failed to find objectGUID for object %s: "
338 "%s\n", ldb_dn_get_linearized(dn),
339 ldb_strerror(ret)));
340 talloc_free(set);
341 talloc_free(tmp_ctx);
342 return NT_STATUS_INTERNAL_DB_CORRUPTION;
345 edge = kcctpl_find_edge_by_guid(graph, site_link_guid);
346 if (!edge) {
347 DEBUG(0, ("failed to find a graph edge with ID=%s\n",
348 GUID_string(tmp_ctx, &site_link_guid)));
349 talloc_free(set);
350 talloc_free(tmp_ctx);
351 return NT_STATUS_INTERNAL_DB_CORRUPTION;
354 talloc_free(tmp_ctx);
356 if (GUID_compare(&edge->type, &type) == 0) {
357 set->edge_ids.data = talloc_realloc(set,
358 set->edge_ids.data,
359 struct GUID,
360 set->edge_ids.count + 1);
361 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set->edge_ids.data,
362 set);
363 set->edge_ids.data[set->edge_ids.count] = site_link_guid;
364 set->edge_ids.count++;
368 *_set = set;
369 return NT_STATUS_OK;
373 * set up a kcctpl_graph, populated with a kcctpl_vertex for each site object, a
374 * kcctpl_multi_edge for each siteLink object, and a kcctpl_multi_edge_set for
375 * each siteLinkBridge object (or implied siteLinkBridge).
377 static NTSTATUS kcctpl_setup_graph(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
378 struct kcctpl_graph **_graph)
380 struct kcctpl_graph *graph;
381 struct ldb_dn *config_dn, *base_dn;
382 TALLOC_CTX *tmp_ctx;
383 bool ok;
384 struct ldb_result *res;
385 const char * const site_link_attrs[] = { "objectGUID", NULL };
386 const char * const inter_site_transport_attrs[] = { "objectGUID",
387 "distinguishedName",
388 NULL };
389 const char * const attrs[] = { "objectGUID", "cost", "options",
390 "replInterval", "schedule", NULL };
391 const char * const site_link_bridge_attrs[] = { "objectGUID",
392 "siteLinkList",
393 NULL };
394 int ret;
395 struct GUID_list vertex_ids;
396 unsigned int i;
397 NTSTATUS status;
399 config_dn = samdb_config_dn(ldb);
400 if (!config_dn) {
401 DEBUG(0, ("failed to find our own Config DN\n"));
402 return NT_STATUS_INTERNAL_DB_CORRUPTION;
405 tmp_ctx = talloc_new(mem_ctx);
406 base_dn = ldb_dn_copy(tmp_ctx, config_dn);
407 if (!base_dn) {
408 DEBUG(0, ("failed to copy Config DN %s\n",
409 ldb_dn_get_linearized(config_dn)));
410 talloc_free(tmp_ctx);
411 return NT_STATUS_NO_MEMORY;
414 ok = ldb_dn_add_child_fmt(base_dn, "CN=Sites");
415 if (!ok) {
416 if (ldb_dn_validate(base_dn)) {
417 DEBUG(0, ("failed to format DN %s\n",
418 ldb_dn_get_linearized(base_dn)));
420 talloc_free(tmp_ctx);
421 return NT_STATUS_NO_MEMORY;
424 ret = ldb_search(ldb, tmp_ctx, &res, base_dn, LDB_SCOPE_SUBTREE,
425 site_link_attrs, "objectClass=siteLink");
426 if (ret != LDB_SUCCESS) {
427 DEBUG(0, ("failed to find siteLink objects under %s: %s\n",
428 ldb_dn_get_linearized(base_dn), ldb_strerror(ret)));
429 talloc_free(tmp_ctx);
430 return NT_STATUS_INTERNAL_DB_CORRUPTION;
433 ZERO_STRUCT(vertex_ids);
434 for (i = 0; i < res->count; i++) {
435 struct GUID guid;
437 guid = samdb_result_guid(res->msgs[i], "objectGUID");
438 vertex_ids.data = talloc_realloc(tmp_ctx, vertex_ids.data,
439 struct GUID,
440 vertex_ids.count + 1);
441 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(vertex_ids.data, tmp_ctx);
442 vertex_ids.data[vertex_ids.count] = guid;
443 vertex_ids.count++;
446 status = kcctpl_create_graph(mem_ctx, &vertex_ids, &graph);
447 if (NT_STATUS_IS_ERR(status)) {
448 DEBUG(0, ("failed to create graph: %s\n", nt_errstr(status)));
449 talloc_free(tmp_ctx);
450 return status;
453 /* get site of local DC */
455 ok = ldb_dn_add_child_fmt(base_dn, "CN=Inter-Site Transports");
456 if (!ok) {
457 if (ldb_dn_validate(base_dn)) {
458 DEBUG(0, ("failed to format DN %s\n",
459 ldb_dn_get_linearized(base_dn)));
461 talloc_free(tmp_ctx);
462 return NT_STATUS_NO_MEMORY;
465 ret = ldb_search(ldb, tmp_ctx, &res, base_dn, LDB_SCOPE_SUBTREE,
466 inter_site_transport_attrs,
467 "objectClass=interSiteTransport");
468 if (ret != LDB_SUCCESS) {
469 DEBUG(0, ("failed to find interSiteTransport objects under %s: "
470 "%s\n", ldb_dn_get_linearized(base_dn),
471 ldb_strerror(ret)));
472 talloc_free(tmp_ctx);
473 return NT_STATUS_INTERNAL_DB_CORRUPTION;
476 for (i = 0; i < res->count; i++) {
477 struct ldb_message *transport;
478 struct ldb_result *res_site_link;
479 struct GUID transport_guid;
480 unsigned int j;
481 int options;
483 transport = res->msgs[i];
485 base_dn = samdb_result_dn(ldb, tmp_ctx, transport,
486 "distinguishedName", NULL);
487 if (!base_dn) {
488 DEBUG(0, ("failed to find DN for interSiteTransport "
489 "object\n"));
490 talloc_free(graph);
491 talloc_free(tmp_ctx);
492 return NT_STATUS_INTERNAL_DB_CORRUPTION;
495 /* TODO: don't need to ldb_search again; search in res. */
496 ret = ldb_search(ldb, tmp_ctx, &res_site_link, base_dn,
497 LDB_SCOPE_SUBTREE, attrs,
498 "objectClass=siteLink");
499 if (ret != LDB_SUCCESS) {
500 DEBUG(0, ("failed to find siteLink objects under %s: "
501 "%s\n", ldb_dn_get_linearized(base_dn),
502 ldb_strerror(ret)));
503 talloc_free(graph);
504 talloc_free(tmp_ctx);
505 return NT_STATUS_INTERNAL_DB_CORRUPTION;
508 transport_guid = samdb_result_guid(transport, "objectGUID");
509 for (j = 0; j < res_site_link->count; j++) {
510 struct kcctpl_multi_edge *edge;
512 status = kcctpl_create_edge(ldb, graph, transport_guid,
513 res_site_link->msgs[j],
514 &edge);
515 if (NT_STATUS_IS_ERR(status)) {
516 DEBUG(0, ("failed to create edge: %s\n",
517 nt_errstr(status)));
518 talloc_free(graph);
519 talloc_free(tmp_ctx);
520 return status;
523 graph->edges.data = talloc_realloc(graph,
524 graph->edges.data,
525 struct kcctpl_multi_edge,
526 graph->edges.count + 1);
527 if (!graph->edges.data) {
528 talloc_free(graph);
529 talloc_free(tmp_ctx);
530 return NT_STATUS_NO_MEMORY;
532 graph->edges.data[graph->edges.count] = *edge;
533 graph->edges.count++;
536 options = samdb_result_int64(transport, "options", 0);
537 if ((options & NTDSTRANSPORT_OPT_BRIDGES_REQUIRED) == 0) {
538 struct kcctpl_multi_edge_set *edge_set;
540 status = kcctpl_create_auto_edge_set(graph,
541 transport_guid,
542 res_site_link,
543 &edge_set);
544 if (NT_STATUS_IS_ERR(status)) {
545 DEBUG(0, ("failed to create edge set: %s\n",
546 nt_errstr(status)));
547 talloc_free(graph);
548 talloc_free(tmp_ctx);
549 return status;
552 graph->edge_sets.data = talloc_realloc(graph,
553 graph->edge_sets.data,
554 struct kcctpl_multi_edge_set,
555 graph->edge_sets.count + 1);
556 if (!graph->edge_sets.data) {
557 talloc_free(graph);
558 talloc_free(tmp_ctx);
559 return NT_STATUS_NO_MEMORY;
561 graph->edge_sets.data[graph->edge_sets.count] = *edge_set;
562 graph->edge_sets.count++;
563 } else {
564 ret = ldb_search(ldb, tmp_ctx, &res_site_link, base_dn,
565 LDB_SCOPE_SUBTREE,
566 site_link_bridge_attrs,
567 "objectClass=siteLinkBridge");
568 if (ret != LDB_SUCCESS) {
569 DEBUG(0, ("failed to find siteLinkBridge "
570 "objects under %s: %s\n",
571 ldb_dn_get_linearized(base_dn),
572 ldb_strerror(ret)));
573 talloc_free(graph);
574 talloc_free(tmp_ctx);
575 return NT_STATUS_INTERNAL_DB_CORRUPTION;
578 for (j = 0; j < res_site_link->count; j++) {
579 struct ldb_message *bridge;
580 struct kcctpl_multi_edge_set *edge_set;
582 bridge = res_site_link->msgs[j];
583 status = kcctpl_create_edge_set(ldb, graph,
584 transport_guid,
585 bridge,
586 &edge_set);
587 if (NT_STATUS_IS_ERR(status)) {
588 DEBUG(0, ("failed to create edge set: "
589 "%s\n", nt_errstr(status)));
590 talloc_free(graph);
591 talloc_free(tmp_ctx);
592 return status;
595 graph->edge_sets.data = talloc_realloc(graph,
596 graph->edge_sets.data,
597 struct kcctpl_multi_edge_set,
598 graph->edge_sets.count + 1);
599 if (!graph->edge_sets.data) {
600 talloc_free(graph);
601 talloc_free(tmp_ctx);
602 return NT_STATUS_NO_MEMORY;
604 graph->edge_sets.data[graph->edge_sets.count] = *edge_set;
605 graph->edge_sets.count++;
610 *_graph = graph;
611 talloc_free(tmp_ctx);
612 return NT_STATUS_OK;