53de556d4341ec3ead98e92945b342dc8ac6a038
[abartlet/samba.git/.git] / source4 / dsdb / kcc / kcc_topology.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    KCC service
5
6    Copyright (C) Crístian Deives 2010
7
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.
12
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.
17
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/>.
20
21 */
22
23 #include "includes.h"
24 #include "dsdb/samdb/samdb.h"
25 #include "lib/messaging/irpc.h"
26 #include "librpc/gen_ndr/ndr_misc.h"
27
28 #define NTDSTRANSPORT_OPT_IGNORE_SCHEDULES 0x00000001
29 #define NTDSTRANSPORT_OPT_BRIDGES_REQUIRED 0x00000002
30
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];
37 };
38
39 /** color of a vertex */
40 enum kcctpl_color { RED, BLACK, WHITE };
41
42 /** a GUID array list */
43 struct GUID_list {
44         struct GUID *data;
45         unsigned int count;
46 };
47
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;
57
58         /* Dijkstra data */
59         struct GUID root_id;
60         bool demoted;
61
62         /* Kruskal data */
63         struct GUID component_id;
64         int component_index;
65 };
66
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;
74 };
75
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;
81 };
82
83 /** a vertices array list */
84 struct kcctpl_vertex_list {
85         struct kcctpl_vertex *data;
86         unsigned int count;
87 };
88
89 /** an edges linked list */
90 struct kcctpl_multi_edge_list {
91         struct kcctpl_multi_edge *data;
92         unsigned int count;
93 };
94
95 /** an edge sets linked list */
96 struct kcctpl_multi_edge_set_list {
97         struct kcctpl_multi_edge_set *data;
98         unsigned int count;
99 };
100
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;
106 };
107
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;
114 };
115
116 /**
117  * find a graph edge based on its GUID.
118  */
119 static struct kcctpl_multi_edge *kcctpl_find_edge_by_guid(struct kcctpl_graph *graph,
120                                                           struct GUID guid)
121 {
122         unsigned int i;
123
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];
127                 }
128         }
129         return NULL;
130 }
131 /**
132  * create a kcctpl_graph instance.
133  */
134 static NTSTATUS kcctpl_create_graph(TALLOC_CTX *mem_ctx,
135                                     struct GUID_list *guids,
136                                     struct kcctpl_graph **_graph)
137 {
138         struct kcctpl_graph *graph;
139         unsigned int i;
140
141         graph = talloc_zero(mem_ctx, struct kcctpl_graph);
142         NT_STATUS_HAVE_NO_MEMORY(graph);
143
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);
148
149         qsort(guids->data, guids->count, sizeof(struct GUID),
150               QSORT_CAST GUID_compare);
151
152         for (i = 0; i < guids->count; i++) {
153                 graph->vertices.data[i].id = guids->data[i];
154         }
155
156         *_graph = graph;
157         return NT_STATUS_OK;
158 }
159
160 /**
161  * create a kcctpl_multi_edge instance.
162  */
163 static NTSTATUS kcctpl_create_edge(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
164                                    struct GUID type,
165                                    struct ldb_message *site_link,
166                                    struct kcctpl_multi_edge **_edge)
167 {
168         struct kcctpl_multi_edge *edge;
169         TALLOC_CTX *tmp_ctx;
170         struct ldb_result *res;
171         const char * const attrs[] = { "siteList", NULL };
172         int ret;
173         struct ldb_message_element *el;
174         unsigned int i;
175         struct ldb_val val;
176
177         edge = talloc(mem_ctx, struct kcctpl_multi_edge);
178         NT_STATUS_HAVE_NO_MEMORY(edge);
179
180         edge->id = samdb_result_guid(site_link, "objectGUID");
181
182         tmp_ctx = talloc_new(mem_ctx);
183         ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_BASE, attrs,
184                          "objectGUID=%s", GUID_string(tmp_ctx, &edge->id));
185         if (ret != LDB_SUCCESS) {
186                 DEBUG(0, ("failed to find siteLink object %s: %s\n",
187                           GUID_string(tmp_ctx, &edge->id), ldb_strerror(ret)));
188                 talloc_free(edge);
189                 talloc_free(tmp_ctx);
190                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
191         }
192         if (res->count == 0) {
193                 DEBUG(0, ("failed to find siteLink object %s\n",
194                           GUID_string(tmp_ctx, &edge->id)));
195                 talloc_free(edge);
196                 talloc_free(tmp_ctx);
197                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
198         }
199
200         el = ldb_msg_find_element(res->msgs[0], "siteList");
201         edge->vertex_ids.count = el->num_values;
202         edge->vertex_ids.data = talloc_array(edge, struct GUID, el->num_values);
203         if (!edge->vertex_ids.data) {
204                 talloc_free(edge);
205                 talloc_free(tmp_ctx);
206                 return NT_STATUS_NO_MEMORY;
207         }
208
209         for (i = 0; i < el->num_values; i++) {
210                 struct ldb_dn *dn;
211                 struct GUID guid;
212
213                 val = el->values[i];
214                 dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &val);
215                 ret = dsdb_find_guid_by_dn(ldb, dn, &guid);
216                 if (ret != LDB_SUCCESS) {
217                         DEBUG(0, ("failed to find objectGUID for object %s: "
218                                   "%s\n", ldb_dn_get_linearized(dn),
219                                   ldb_strerror(ret)));
220                         talloc_free(edge);
221                         talloc_free(tmp_ctx);
222                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
223                 }
224
225                 edge->vertex_ids.data = talloc_realloc(edge,
226                                                        edge->vertex_ids.data,
227                                                        struct GUID,
228                                                        edge->vertex_ids.count + 1);
229                 if (!edge->vertex_ids.data) {
230                         talloc_free(edge);
231                         talloc_free(tmp_ctx);
232                         return NT_STATUS_NO_MEMORY;
233                 }
234                 edge->vertex_ids.data[edge->vertex_ids.count] = guid;
235                 edge->vertex_ids.count++;
236         }
237
238         edge->repl_info.cost = samdb_result_int64(site_link, "cost", 0);
239         edge->repl_info.options = samdb_result_int64(site_link, "options", 0);
240         edge->repl_info.interval = samdb_result_int64(site_link, "replInterval",
241                                                       0);
242         /* val = ldb_msg_find_ldb_val(site_link, "schedule");
243         edge->repl_info.schedule = val->data; */
244         edge->type = type;
245         edge->directed = false;
246
247         *_edge = edge;
248         talloc_free(tmp_ctx);
249         return NT_STATUS_OK;
250 }
251
252 /**
253  * create a kcctpl_multi_edge_set instance containing edges for all siteLink
254  * objects.
255  */
256 static NTSTATUS kcctpl_create_auto_edge_set(struct kcctpl_graph *graph,
257                                             struct GUID type,
258                                             struct ldb_result *res_site_link,
259                                             struct kcctpl_multi_edge_set **_set)
260 {
261         struct kcctpl_multi_edge_set *set;
262         unsigned int i;
263
264         set = talloc_zero(graph, struct kcctpl_multi_edge_set);
265         NT_STATUS_HAVE_NO_MEMORY(set);
266
267         for (i = 0; i < res_site_link->count; i++) {
268                 struct GUID site_link_guid;
269                 struct kcctpl_multi_edge *edge;
270
271                 site_link_guid = samdb_result_guid(res_site_link->msgs[i],
272                                                    "objectGUID");
273                 edge = kcctpl_find_edge_by_guid(graph, site_link_guid);
274                 if (!edge) {
275                         TALLOC_CTX *tmp_ctx = talloc_new(graph);
276                         DEBUG(0, ("failed to find a graph edge with ID=%s\n",
277                                   GUID_string(tmp_ctx, &site_link_guid)));
278                         talloc_free(tmp_ctx);
279                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
280                 }
281
282                 if (GUID_compare(&edge->type, &type) == 0) {
283                         set->edge_ids.data = talloc_realloc(set,
284                                                             set->edge_ids.data,
285                                                             struct GUID,
286                                                             set->edge_ids.count + 1);
287                         NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set->edge_ids.data,
288                                                           set);
289                         set->edge_ids.data[set->edge_ids.count] = site_link_guid;
290                         set->edge_ids.count++;
291                 }
292         }
293
294         *_set = set;
295         return NT_STATUS_OK;
296 }
297
298 /**
299  * create a kcctpl_multi_edge_set instance.
300  */
301 static NTSTATUS kcctpl_create_edge_set(struct ldb_context *ldb,
302                                        struct kcctpl_graph *graph,
303                                        struct GUID type,
304                                        struct ldb_message *bridge,
305                                        struct kcctpl_multi_edge_set **_set)
306 {
307         struct kcctpl_multi_edge_set *set;
308         struct ldb_message_element *el;
309         unsigned int i;
310
311         set = talloc_zero(graph, struct kcctpl_multi_edge_set);
312         NT_STATUS_HAVE_NO_MEMORY(set);
313
314         set->id = samdb_result_guid(bridge, "objectGUID");
315
316         el = ldb_msg_find_element(bridge, "siteLinkList");
317         for (i = 0; i < el->num_values; i++) {
318                 struct ldb_val val;
319                 TALLOC_CTX *tmp_ctx;
320                 struct ldb_dn *dn;
321                 struct GUID site_link_guid;
322                 int ret;
323                 struct kcctpl_multi_edge *edge;
324
325                 val = el->values[i];
326                 tmp_ctx = talloc_new(graph);
327                 dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &val);
328                 if (!ldb_dn_validate(dn)) {
329                         DEBUG(0, ("invalid DN in siteLinkList attr of %s\n",
330                                   GUID_string(tmp_ctx, &set->id)));
331                         talloc_free(set);
332                         talloc_free(tmp_ctx);
333                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
334                 }
335
336                 ret = dsdb_find_guid_by_dn(ldb, dn, &site_link_guid);
337                 if (ret != LDB_SUCCESS) {
338                         DEBUG(0, ("failed to find objectGUID for object %s: "
339                                   "%s\n", ldb_dn_get_linearized(dn),
340                                   ldb_strerror(ret)));
341                         talloc_free(set);
342                         talloc_free(tmp_ctx);
343                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
344                 }
345
346                 edge = kcctpl_find_edge_by_guid(graph, site_link_guid);
347                 if (!edge) {
348                         DEBUG(0, ("failed to find a graph edge with ID=%s\n",
349                                   GUID_string(tmp_ctx, &site_link_guid)));
350                         talloc_free(set);
351                         talloc_free(tmp_ctx);
352                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
353                 }
354
355                 talloc_free(tmp_ctx);
356
357                 if (GUID_compare(&edge->type, &type) == 0) {
358                         set->edge_ids.data = talloc_realloc(set,
359                                                             set->edge_ids.data,
360                                                             struct GUID,
361                                                             set->edge_ids.count + 1);
362                         NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set->edge_ids.data,
363                                                           set);
364                         set->edge_ids.data[set->edge_ids.count] = site_link_guid;
365                         set->edge_ids.count++;
366                 }
367         }
368
369         *_set = set;
370         return NT_STATUS_OK;
371 }
372
373 /**
374  * set up a kcctpl_graph, populated with a kcctpl_vertex for each site object, a
375  * kcctpl_multi_edge for each siteLink object, and a kcctpl_multi_edge_set for
376  * each siteLinkBridge object (or implied siteLinkBridge).
377  */
378 static NTSTATUS kcctpl_setup_graph(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
379                                    struct kcctpl_graph **_graph)
380 {
381         struct kcctpl_graph *graph;
382         struct ldb_dn *config_dn, *base_dn;
383         TALLOC_CTX *tmp_ctx;
384         bool ok;
385         struct ldb_result *res;
386         const char * const site_link_attrs[] = { "objectGUID", NULL };
387         const char * const inter_site_transport_attrs[] = { "objectGUID",
388                                                             "distinguishedName",
389                                                             NULL };
390         const char * const attrs[] = { "objectGUID", "cost", "options",
391                                        "replInterval", "schedule", NULL };
392         const char * const site_link_bridge_attrs[] = { "objectGUID",
393                                                         "siteLinkList",
394                                                         NULL };
395         int ret;
396         struct GUID_list vertex_ids;
397         unsigned int i;
398         NTSTATUS status;
399
400         config_dn = samdb_config_dn(ldb);
401         if (!config_dn) {
402                 DEBUG(0, ("failed to find our own Config DN\n"));
403                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
404         }
405
406         tmp_ctx = talloc_new(mem_ctx);
407         base_dn = ldb_dn_copy(tmp_ctx, config_dn);
408         if (!base_dn) {
409                 DEBUG(0, ("failed to copy Config DN %s\n",
410                           ldb_dn_get_linearized(config_dn)));
411                 talloc_free(tmp_ctx);
412                 return NT_STATUS_NO_MEMORY;
413         }
414
415         ok = ldb_dn_add_child_fmt(base_dn, "CN=Sites");
416         if (!ok) {
417                 if (ldb_dn_validate(base_dn)) {
418                         DEBUG(0, ("failed to format DN %s\n",
419                                   ldb_dn_get_linearized(base_dn)));
420                 }
421                 talloc_free(tmp_ctx);
422                 return NT_STATUS_NO_MEMORY;
423         }
424
425         ret = ldb_search(ldb, tmp_ctx, &res, base_dn, LDB_SCOPE_SUBTREE,
426                          site_link_attrs, "objectClass=siteLink");
427         if (ret != LDB_SUCCESS) {
428                 DEBUG(0, ("failed to find siteLink objects under %s: %s\n",
429                           ldb_dn_get_linearized(base_dn), ldb_strerror(ret)));
430                 talloc_free(tmp_ctx);
431                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
432         }
433
434         ZERO_STRUCT(vertex_ids);
435         for (i = 0; i < res->count; i++) {
436                 struct GUID guid;
437
438                 guid = samdb_result_guid(res->msgs[i], "objectGUID");
439                 vertex_ids.data = talloc_realloc(tmp_ctx, vertex_ids.data,
440                                                  struct GUID,
441                                                  vertex_ids.count + 1);
442                 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(vertex_ids.data, tmp_ctx);
443                 vertex_ids.data[vertex_ids.count] = guid;
444                 vertex_ids.count++;
445         }
446
447         status = kcctpl_create_graph(mem_ctx, &vertex_ids, &graph);
448         if (NT_STATUS_IS_ERR(status)) {
449                 DEBUG(0, ("failed to create graph: %s\n", nt_errstr(status)));
450                 talloc_free(tmp_ctx);
451                 return status;
452         }
453
454         /* get site of local DC */
455
456         ok = ldb_dn_add_child_fmt(base_dn, "CN=Inter-Site Transports");
457         if (!ok) {
458                 if (ldb_dn_validate(base_dn)) {
459                         DEBUG(0, ("failed to format DN %s\n",
460                                   ldb_dn_get_linearized(base_dn)));
461                 }
462                 talloc_free(tmp_ctx);
463                 return NT_STATUS_NO_MEMORY;
464         }
465
466         ret = ldb_search(ldb, tmp_ctx, &res, base_dn, LDB_SCOPE_SUBTREE,
467                         inter_site_transport_attrs,
468                         "objectClass=interSiteTransport");
469         if (ret != LDB_SUCCESS) {
470                 DEBUG(0, ("failed to find interSiteTransport objects under %s: "
471                           "%s\n", ldb_dn_get_linearized(base_dn),
472                           ldb_strerror(ret)));
473                 talloc_free(tmp_ctx);
474                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
475         }
476
477         for (i = 0; i < res->count; i++) {
478                 struct ldb_message *transport;
479                 struct ldb_result *res_site_link;
480                 struct GUID transport_guid;
481                 unsigned int j;
482                 int options;
483
484                 transport = res->msgs[i];
485
486                 base_dn = samdb_result_dn(ldb, tmp_ctx, transport,
487                                           "distinguishedName", NULL);
488                 if (!base_dn) {
489                         DEBUG(0, ("failed to find DN for interSiteTransport "
490                                   "object\n"));
491                         talloc_free(graph);
492                         talloc_free(tmp_ctx);
493                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
494                 }
495
496                 /* TODO: don't need to ldb_search again; search in res. */
497                 ret = ldb_search(ldb, tmp_ctx, &res_site_link, base_dn,
498                                  LDB_SCOPE_SUBTREE, attrs,
499                                  "objectClass=siteLink");
500                 if (ret != LDB_SUCCESS) {
501                         DEBUG(0, ("failed to find siteLink objects under %s: "
502                                   "%s\n", ldb_dn_get_linearized(base_dn),
503                                   ldb_strerror(ret)));
504                         talloc_free(graph);
505                         talloc_free(tmp_ctx);
506                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
507                 }
508
509                 transport_guid = samdb_result_guid(transport, "objectGUID");
510                 for (j = 0; j < res_site_link->count; j++) {
511                         struct kcctpl_multi_edge *edge;
512
513                         status = kcctpl_create_edge(ldb, graph, transport_guid,
514                                                     res_site_link->msgs[j],
515                                                     &edge);
516                         if (NT_STATUS_IS_ERR(status)) {
517                                 DEBUG(0, ("failed to create edge: %s\n",
518                                           nt_errstr(status)));
519                                 talloc_free(graph);
520                                 talloc_free(tmp_ctx);
521                                 return status;
522                         }
523
524                         graph->edges.data = talloc_realloc(graph,
525                                                            graph->edges.data,
526                                                            struct kcctpl_multi_edge,
527                                                            graph->edges.count + 1);
528                         if (!graph->edges.data) {
529                                 talloc_free(graph);
530                                 talloc_free(tmp_ctx);
531                                 return NT_STATUS_NO_MEMORY;
532                         }
533                         graph->edges.data[graph->edges.count] = *edge;
534                         graph->edges.count++;
535                 }
536
537                 options = samdb_result_int64(transport, "options", 0);
538                 if ((options & NTDSTRANSPORT_OPT_BRIDGES_REQUIRED) == 0) {
539                         struct kcctpl_multi_edge_set *edge_set;
540
541                         status = kcctpl_create_auto_edge_set(graph,
542                                                              transport_guid,
543                                                              res_site_link,
544                                                              &edge_set);
545                         if (NT_STATUS_IS_ERR(status)) {
546                                 DEBUG(0, ("failed to create edge set: %s\n",
547                                           nt_errstr(status)));
548                                 talloc_free(graph);
549                                 talloc_free(tmp_ctx);
550                                 return status;
551                         }
552
553                         graph->edge_sets.data = talloc_realloc(graph,
554                                                                graph->edge_sets.data,
555                                                                struct kcctpl_multi_edge_set,
556                                                                graph->edge_sets.count + 1);
557                         if (!graph->edge_sets.data) {
558                                 talloc_free(graph);
559                                 talloc_free(tmp_ctx);
560                                 return NT_STATUS_NO_MEMORY;
561                         }
562                         graph->edge_sets.data[graph->edge_sets.count] = *edge_set;
563                         graph->edge_sets.count++;
564                 } else {
565                         ret = ldb_search(ldb, tmp_ctx, &res_site_link, base_dn,
566                                          LDB_SCOPE_SUBTREE,
567                                          site_link_bridge_attrs,
568                                          "objectClass=siteLinkBridge");
569                         if (ret != LDB_SUCCESS) {
570                                 DEBUG(0, ("failed to find siteLinkBridge "
571                                           "objects under %s: %s\n",
572                                           ldb_dn_get_linearized(base_dn),
573                                           ldb_strerror(ret)));
574                                 talloc_free(graph);
575                                 talloc_free(tmp_ctx);
576                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
577                         }
578
579                         for (j = 0; j < res_site_link->count; j++) {
580                                 struct ldb_message *bridge;
581                                 struct kcctpl_multi_edge_set *edge_set;
582
583                                 bridge = res_site_link->msgs[j];
584                                 status = kcctpl_create_edge_set(ldb, graph,
585                                                                 transport_guid,
586                                                                 bridge,
587                                                                 &edge_set);
588                                 if (NT_STATUS_IS_ERR(status)) {
589                                         DEBUG(0, ("failed to create edge set: "
590                                                   "%s\n", nt_errstr(status)));
591                                         talloc_free(graph);
592                                         talloc_free(tmp_ctx);
593                                         return status;
594                                 }
595
596                                 graph->edge_sets.data = talloc_realloc(graph,
597                                                                        graph->edge_sets.data,
598                                                                        struct kcctpl_multi_edge_set,
599                                                                        graph->edge_sets.count + 1);
600                                 if (!graph->edge_sets.data) {
601                                         talloc_free(graph);
602                                         talloc_free(tmp_ctx);
603                                         return NT_STATUS_NO_MEMORY;
604                                 }
605                                 graph->edge_sets.data[graph->edge_sets.count] = *edge_set;
606                                 graph->edge_sets.count++;
607                         }
608                 }
609         }
610
611         *_graph = graph;
612         talloc_free(tmp_ctx);
613         return NT_STATUS_OK;
614 }