s4:dsdb/repl: make use of dcerpc_binding_set_string_option("target_principal")
[amitay/samba.git] / source4 / dsdb / repl / drepl_partitions.c
index f2d4b1321bfa95caa8807e7a50fc141700886577..847a7e1f2f39cbbdf2b54ee36b9c2ae48ecce9f7 100644 (file)
@@ -41,7 +41,7 @@
 WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
 {
        WERROR status;
-       static const char *attrs[] = { "hasMasterNCs", "hasPartialReplicaNCs", "msDS-HasFullReplicaNCs", NULL };
+       static const char *attrs[] = { "hasMasterNCs", "msDS-hasMasterNCs", "hasPartialReplicaNCs", "msDS-HasFullReplicaNCs", NULL };
        unsigned int a;
        int ret;
        TALLOC_CTX *tmp_ctx;
@@ -52,7 +52,7 @@ WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
        tmp_ctx = talloc_new(s);
        W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
 
-       ntds_dn = samdb_ntds_settings_dn(s->samdb);
+       ntds_dn = samdb_ntds_settings_dn(s->samdb, tmp_ctx);
        if (!ntds_dn) {
                DEBUG(1,(__location__ ": Unable to find ntds_dn: %s\n", ldb_errstring(s->samdb)));
                talloc_free(tmp_ctx);
@@ -75,7 +75,8 @@ WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
                }
                for (i=0; i<el->num_values; i++) {
                        struct ldb_dn *pdn;
-                       struct dreplsrv_partition *p;
+                       struct dreplsrv_partition *p, *tp;
+                       bool found;
 
                        pdn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]);
                        if (pdn == NULL) {
@@ -98,8 +99,20 @@ WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
                                p->rodc_replica = true;
                        }
 
-                       DLIST_ADD(s->partitions, p);
+                       /* Do not add partitions more than once */
+                       found = false;
+                       for (tp = s->partitions; tp; tp = tp->next) {
+                               if (ldb_dn_compare(tp->dn, p->dn) == 0) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+                       if (found) {
+                               talloc_free(p);
+                               continue;
+                       }
 
+                       DLIST_ADD(s->partitions, p);
                        DEBUG(2, ("dreplsrv_partition[%s] loaded\n", ldb_dn_get_linearized(p->dn)));
                }
        }
@@ -112,6 +125,31 @@ WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
        return WERR_OK;
 }
 
+/*
+  Check if particular SPN exists for an account
+ */
+static bool dreplsrv_spn_exists(struct ldb_context *samdb, struct ldb_dn *account_dn,
+                               const char *principal_name)
+{
+       TALLOC_CTX *tmp_ctx;
+       const char *attrs_empty[] = { NULL };
+       int ret;
+       struct ldb_result *res;
+
+       tmp_ctx = talloc_new(samdb);
+
+       ret = dsdb_search(samdb, tmp_ctx, &res, account_dn, LDB_SCOPE_BASE, attrs_empty,
+                       0, "servicePrincipalName=%s",
+                       ldb_binary_encode_string(tmp_ctx, principal_name));
+       if (ret != LDB_SUCCESS || res->count != 1) {
+               talloc_free(tmp_ctx);
+               return false;
+       }
+
+       talloc_free(tmp_ctx);
+       return true;
+}
+
 /*
   work out the principal to use for DRS replication connections
  */
@@ -122,11 +160,11 @@ NTSTATUS dreplsrv_get_target_principal(struct dreplsrv_service *s,
 {
        TALLOC_CTX *tmp_ctx;
        struct ldb_result *res;
-       const char *attrs_server[] = { "dNSHostName", NULL };
+       const char *attrs_server[] = { "dNSHostName", "serverReference", NULL };
        const char *attrs_ntds[] = { "msDS-HasDomainNCs", "hasMasterNCs", NULL };
        int ret;
        const char *hostname, *dnsdomain=NULL;
-       struct ldb_dn *ntds_dn, *server_dn;
+       struct ldb_dn *ntds_dn, *server_dn, *computer_dn;
        struct ldb_dn *forest_dn, *nc_dn;
 
        *target_principal = NULL;
@@ -134,7 +172,7 @@ NTSTATUS dreplsrv_get_target_principal(struct dreplsrv_service *s,
        tmp_ctx = talloc_new(mem_ctx);
 
        /* we need to find their hostname */
-       ret = dsdb_find_dn_by_guid(s->samdb, tmp_ctx, &rft->source_dsa_obj_guid, &ntds_dn);
+       ret = dsdb_find_dn_by_guid(s->samdb, tmp_ctx, &rft->source_dsa_obj_guid, 0, &ntds_dn);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
                /* its OK for their NTDSDSA DN not to be in our database */
@@ -167,16 +205,27 @@ NTSTATUS dreplsrv_get_target_principal(struct dreplsrv_service *s,
        }
 
        hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
-       if (hostname != NULL) {
+       computer_dn = ldb_msg_find_attr_as_dn(s->samdb, tmp_ctx, res->msgs[0], "serverReference");
+       if (hostname != NULL && computer_dn != NULL) {
+               char *local_principal;
+
                /*
                  if we have the dNSHostName attribute then we can use
                  the GC/hostname/realm SPN. All DCs should have this SPN
+
+                 Windows DC may set up it's dNSHostName before setting up
+                 GC/xx/xx SPN. So make sure it exists, before using it.
                 */
-               *target_principal = talloc_asprintf(mem_ctx, "GC/%s/%s",
+               local_principal = talloc_asprintf(mem_ctx, "GC/%s/%s",
                                                    hostname,
                                                    samdb_dn_to_dns_domain(tmp_ctx, forest_dn));
-               talloc_free(tmp_ctx);
-               return NT_STATUS_OK;
+               if (dreplsrv_spn_exists(s->samdb, computer_dn, local_principal)) {
+                       *target_principal = local_principal;
+                       talloc_free(tmp_ctx);
+                       return NT_STATUS_OK;
+               }
+
+               talloc_free(local_principal);
        }
 
        /*
@@ -258,6 +307,7 @@ WERROR dreplsrv_out_connection_attach(struct dreplsrv_service *s,
        if (!conn) {
                NTSTATUS nt_status;
                char *binding_str;
+               const char *target_principal = NULL;
 
                conn = talloc_zero(s, struct dreplsrv_out_connection);
                W_ERROR_HAVE_NO_MEMORY(conn);
@@ -275,7 +325,14 @@ WERROR dreplsrv_out_connection_attach(struct dreplsrv_service *s,
 
                /* use the GC principal for DRS replication */
                nt_status = dreplsrv_get_target_principal(s, conn->binding,
-                                                         rft, &conn->binding->target_principal);
+                                                         rft, &target_principal);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       return ntstatus_to_werror(nt_status);
+               }
+
+               nt_status = dcerpc_binding_set_string_option(conn->binding,
+                                                            "target_principal",
+                                                            target_principal);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        return ntstatus_to_werror(nt_status);
                }
@@ -364,6 +421,11 @@ static WERROR dreplsrv_partition_add_source_dsa(struct dreplsrv_service *s,
        return WERR_OK;
 }
 
+/**
+ * Find a partition when given a NC
+ * If the NC can't be found it will return BAD_NC
+ * Initial checks for invalid parameters have to be done beforehand
+ */
 WERROR dreplsrv_partition_find_for_nc(struct dreplsrv_service *s,
                                      struct GUID *nc_guid,
                                      struct dom_sid *nc_sid,
@@ -380,8 +442,8 @@ WERROR dreplsrv_partition_find_for_nc(struct dreplsrv_service *s,
        valid_sid  = nc_sid && !dom_sid_equal(&null_sid, nc_sid);
        valid_guid = nc_guid && !GUID_all_zero(nc_guid);
 
-       if (!valid_sid && !valid_guid && !nc_dn_str) {
-               return WERR_DS_DRA_INVALID_PARAMETER;
+       if (!valid_sid && !valid_guid && (!nc_dn_str)) {
+               return WERR_DS_DRA_BAD_NC;
        }
 
        for (p = s->partitions; p; p = p->next) {
@@ -545,7 +607,7 @@ static WERROR dreplsrv_refresh_partition(struct dreplsrv_service *s,
 
        if (r != NULL && (orf_el = ldb_msg_find_element(r->msgs[0], "repsFrom"))) {
                for (i=0; i < orf_el->num_values; i++) {
-                       status = dreplsrv_partition_add_source_dsa(s, p, &p->sources, 
+                       status = dreplsrv_partition_add_source_dsa(s, p, &p->sources,
                                                                   NULL, &orf_el->values[i]);
                        W_ERROR_NOT_OK_GOTO_DONE(status);
                }
@@ -553,7 +615,7 @@ static WERROR dreplsrv_refresh_partition(struct dreplsrv_service *s,
 
        if (r != NULL && (orf_el = ldb_msg_find_element(r->msgs[0], "repsTo"))) {
                for (i=0; i < orf_el->num_values; i++) {
-                       status = dreplsrv_partition_add_source_dsa(s, p, &p->notifies, 
+                       status = dreplsrv_partition_add_source_dsa(s, p, &p->notifies,
                                                                   p->sources, &orf_el->values[i]);
                        W_ERROR_NOT_OK_GOTO_DONE(status);
                }