s4-kcc: if we are a GC, auto-add partial replicas
authorAndrew Tridgell <tridge@samba.org>
Wed, 28 Sep 2011 06:45:17 +0000 (16:45 +1000)
committerAndrew Tridgell <tridge@samba.org>
Tue, 4 Oct 2011 04:08:57 +0000 (15:08 +1100)
when we are a global catalog server, the KCC needs to add partial
replicas for all domain partitions that we don't have copies of

source4/dsdb/kcc/kcc_periodic.c

index 2654f9f..d9a716f 100644 (file)
@@ -35,6 +35,7 @@
 #include "librpc/gen_ndr/ndr_drsblobs.h"
 #include "librpc/gen_ndr/ndr_irpc_c.h"
 #include "param/param.h"
+#include "dsdb/common/util.h"
 
 /*
  * see if two repsFromToBlob blobs are for the same source DSA
@@ -309,6 +310,145 @@ NTSTATUS kccsrv_add_repsFrom(struct kccsrv_service *s, TALLOC_CTX *mem_ctx,
 
 }
 
+
+/*
+  form a unique list of DNs from a search result and a given set of attributes
+ */
+static int kccsrv_dn_list(struct ldb_context *ldb, struct ldb_result *res,
+                         TALLOC_CTX *mem_ctx,
+                         const char **attrs,
+                         struct ldb_dn ***dn_list, int *dn_count)
+{
+       int i;
+       struct ldb_dn **nc_list = NULL;
+       int nc_count = 0;
+
+       nc_list = talloc_array(mem_ctx, struct ldb_dn *, 0);
+       if (nc_list == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* gather up a list of all NCs in this forest */
+       for (i=0; i<res->count; i++) {
+               struct ldb_message *msg = res->msgs[i];
+               int j;
+               for (j=0; attrs[j]; j++) {
+                       struct ldb_message_element *el;
+                       int k;
+
+                       el = ldb_msg_find_element(msg, attrs[j]);
+                       if (el == NULL) continue;
+                       for (k=0; k<el->num_values; k++) {
+                               struct ldb_dn *dn;
+                               dn = ldb_dn_from_ldb_val(nc_list, ldb, &el->values[k]);
+                               if (dn != NULL) {
+                                       int l;
+                                       for (l=0; l<nc_count; l++) {
+                                               if (ldb_dn_compare(nc_list[l], dn) == 0) break;
+                                       }
+                                       if (l < nc_count) continue;
+                                       nc_list = talloc_realloc(mem_ctx, nc_list, struct ldb_dn *, nc_count+1);
+                                       if (nc_list == NULL) {
+                                               return LDB_ERR_OPERATIONS_ERROR;
+                                       }
+                                       nc_list[nc_count] = dn;
+                                       nc_count++;
+                               }
+                       }
+               }
+       }
+
+       (*dn_list) = nc_list;
+       (*dn_count) = nc_count;
+       return LDB_SUCCESS;
+}
+
+
+/*
+  look for any additional global catalog partitions that we should be
+  replicating (by looking for msDS-HasDomainNCs), and add them to our
+  hasPartialReplicaNCs NTDS attribute
+ */
+static int kccsrv_gc_update(struct kccsrv_service *s, struct ldb_result *res)
+{
+       int i;
+       struct ldb_dn **nc_list = NULL;
+       int nc_count = 0;
+       struct ldb_dn **our_nc_list = NULL;
+       int our_nc_count = 0;
+       const char *attrs1[] = { "msDS-hasMasterNCs", "hasMasterNCs", "msDS-HasDomainNCs", NULL };
+       const char *attrs2[] = { "msDS-hasMasterNCs", "hasMasterNCs", "msDS-HasDomainNCs", "hasPartialReplicaNCs", NULL };
+       int ret;
+       TALLOC_CTX *tmp_ctx = talloc_new(res);
+       struct ldb_result *res2;
+       struct ldb_message *msg;
+
+       /* get a complete list of NCs for the forest */
+       ret = kccsrv_dn_list(s->samdb, res, tmp_ctx, attrs1, &nc_list, &nc_count);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(1,("Failed to get NC list for GC update - %s\n", ldb_errstring(s->samdb)));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       /* get a list of what NCs we are already replicating */
+       ret = dsdb_search_dn(s->samdb, tmp_ctx, &res2, samdb_ntds_settings_dn(s->samdb), attrs2, 0);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(1,("Failed to get our NC list attributes for GC update - %s\n", ldb_errstring(s->samdb)));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       ret = kccsrv_dn_list(s->samdb, res2, tmp_ctx, attrs2, &our_nc_list, &our_nc_count);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(1,("Failed to get our NC list for GC update - %s\n", ldb_errstring(s->samdb)));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       msg = ldb_msg_new(tmp_ctx);
+       if (msg == NULL) {
+               talloc_free(tmp_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       msg->dn = res2->msgs[0]->dn;
+
+       /* see if we are missing any */
+       for (i=0; i<nc_count; i++) {
+               int j;
+               for (j=0; j<our_nc_count; j++) {
+                       if (ldb_dn_compare(nc_list[i], our_nc_list[j]) == 0) break;
+               }
+               if (j == our_nc_count) {
+                       /* its a new one */
+                       ret = ldb_msg_add_string(msg, "hasPartialReplicaNCs",
+                                                ldb_dn_get_extended_linearized(msg, nc_list[i], 1));
+                       if (ret != LDB_SUCCESS) {
+                               talloc_free(tmp_ctx);
+                               return ret;
+                       }
+
+               }
+       }
+
+       if (msg->num_elements == 0) {
+               /* none to add */
+               talloc_free(tmp_ctx);
+               return LDB_SUCCESS;
+       }
+       msg->elements[0].flags = LDB_FLAG_MOD_ADD;
+
+       ret = dsdb_modify(s->samdb, msg, 0);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,("Failed to add hasPartialReplicaNCs - %s\n",
+                        ldb_errstring(s->samdb)));
+       }
+
+       talloc_free(tmp_ctx);
+       return ret;
+}
+
+
 /*
   this is the core of our initial simple KCC
   We just add a repsFrom entry for all DCs we find that have nTDSDSA
@@ -319,18 +459,22 @@ NTSTATUS kccsrv_simple_update(struct kccsrv_service *s, TALLOC_CTX *mem_ctx)
        struct ldb_result *res;
        unsigned int i;
        int ret;
-       const char *attrs[] = { "objectGUID", "invocationID", "msDS-hasMasterNCs", "hasMasterNCs", NULL };
+       const char *attrs[] = { "objectGUID", "invocationID", "msDS-hasMasterNCs", "hasMasterNCs", "msDS-HasDomainNCs", NULL };
        struct repsFromToBlob *reps = NULL;
        uint32_t count = 0;
        struct kcc_connection_list *ntds_conn, *dsa_conn;
 
-       ret = ldb_search(s->samdb, mem_ctx, &res, s->config_dn, LDB_SCOPE_SUBTREE, 
-                        attrs, "objectClass=nTDSDSA");
+       ret = dsdb_search(s->samdb, mem_ctx, &res, s->config_dn, LDB_SCOPE_SUBTREE,
+                         attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, "objectClass=nTDSDSA");
        if (ret != LDB_SUCCESS) {
                DEBUG(0,(__location__ ": Failed nTDSDSA search - %s\n", ldb_errstring(s->samdb)));
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
 
+       if (samdb_is_gc(s->samdb)) {
+               kccsrv_gc_update(s, res);
+       }
+
        /* get the current list of connections */
        ntds_conn = kccsrv_find_connections(s, mem_ctx);