X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source4%2Fdsdb%2Frepl%2Fdrepl_partitions.c;h=2aa2d7c8d68eb649576022f091c493ce8aaeb27d;hb=2b929b0b515ae6c3f000d1f522dbac542fff9546;hp=f36b735d325da37fe4a589a53827c6be2fb1c2c3;hpb=694a579cb8d418a4feb441a77c0dc000023f2c6e;p=ira%2Fwip.git diff --git a/source4/dsdb/repl/drepl_partitions.c b/source4/dsdb/repl/drepl_partitions.c index f36b735d325..2aa2d7c8d68 100644 --- a/source4/dsdb/repl/drepl_partitions.c +++ b/source4/dsdb/repl/drepl_partitions.c @@ -24,66 +24,104 @@ #include "auth/auth.h" #include "smbd/service.h" #include "lib/events/events.h" -#include "lib/messaging/irpc.h" #include "dsdb/repl/drepl_service.h" -#include "lib/ldb/include/ldb_errors.h" +#include #include "../lib/util/dlinklist.h" #include "librpc/gen_ndr/ndr_misc.h" #include "librpc/gen_ndr/ndr_drsuapi.h" #include "librpc/gen_ndr/ndr_drsblobs.h" +#include "libcli/security/security.h" #include "param/param.h" - -static WERROR dreplsrv_refresh_partitions(struct dreplsrv_service *s); +#include "dsdb/common/util.h" WERROR dreplsrv_load_partitions(struct dreplsrv_service *s) { WERROR status; - struct ldb_dn *basedn; - struct ldb_result *r; - struct ldb_message_element *el; - static const char *attrs[] = { "namingContexts", NULL }; - uint32_t i; + static const char *attrs[] = { "hasMasterNCs", "hasPartialReplicaNCs", NULL }; + unsigned int i; int ret; + TALLOC_CTX *tmp_ctx; + struct ldb_result *res; + struct ldb_message_element *el; + struct ldb_dn *ntds_dn; - basedn = ldb_dn_new(s, s->samdb, NULL); - W_ERROR_HAVE_NO_MEMORY(basedn); + tmp_ctx = talloc_new(s); + W_ERROR_HAVE_NO_MEMORY(tmp_ctx); - ret = ldb_search(s->samdb, s, &r, basedn, LDB_SCOPE_BASE, attrs, - "(objectClass=*)"); - talloc_free(basedn); + ntds_dn = samdb_ntds_settings_dn(s->samdb); + if (!ntds_dn) { + DEBUG(1,(__location__ ": Unable to find ntds_dn: %s\n", ldb_errstring(s->samdb))); + talloc_free(tmp_ctx); + return WERR_DS_DRA_INTERNAL_ERROR; + } + + ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, ntds_dn, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN); if (ret != LDB_SUCCESS) { - return WERR_FOOBAR; - } else if (r->count != 1) { - talloc_free(r); - return WERR_FOOBAR; + DEBUG(1,("Searching for hasMasterNCs in NTDS DN failed: %s\n", ldb_errstring(s->samdb))); + talloc_free(tmp_ctx); + return WERR_DS_DRA_INTERNAL_ERROR; } - el = ldb_msg_find_element(r->msgs[0], "namingContexts"); + el = ldb_msg_find_element(res->msgs[0], "hasMasterNCs"); if (!el) { - return WERR_FOOBAR; + DEBUG(1,("Finding hasMasterNCs element in root_res failed: %s\n", + ldb_errstring(s->samdb))); + talloc_free(tmp_ctx); + return WERR_DS_DRA_INTERNAL_ERROR; } - for (i=0; el && i < el->num_values; i++) { - const char *v = (const char *)el->values[i].data; + for (i=0; inum_values; i++) { struct ldb_dn *pdn; struct dreplsrv_partition *p; - pdn = ldb_dn_new(s, s->samdb, v); + pdn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]); + if (pdn == NULL) { + talloc_free(tmp_ctx); + return WERR_DS_DRA_INTERNAL_ERROR; + } if (!ldb_dn_validate(pdn)) { - return WERR_FOOBAR; + return WERR_DS_DRA_INTERNAL_ERROR; } p = talloc_zero(s, struct dreplsrv_partition); W_ERROR_HAVE_NO_MEMORY(p); p->dn = talloc_steal(p, pdn); + p->service = s; DLIST_ADD(s->partitions, p); - DEBUG(2, ("dreplsrv_partition[%s] loaded\n", v)); + DEBUG(2, ("dreplsrv_partition[%s] loaded\n", ldb_dn_get_linearized(p->dn))); } - talloc_free(r); + el = ldb_msg_find_element(res->msgs[0], "hasPartialReplicaNCs"); + + for (i=0; el && inum_values; i++) { + struct ldb_dn *pdn; + struct dreplsrv_partition *p; + + pdn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]); + if (pdn == NULL) { + talloc_free(tmp_ctx); + return WERR_DS_DRA_INTERNAL_ERROR; + } + if (!ldb_dn_validate(pdn)) { + return WERR_DS_DRA_INTERNAL_ERROR; + } + + p = talloc_zero(s, struct dreplsrv_partition); + W_ERROR_HAVE_NO_MEMORY(p); + + p->dn = talloc_steal(p, pdn); + p->partial_replica = true; + p->service = s; + + DLIST_ADD(s->partitions, p); + + DEBUG(2, ("dreplsrv_partition[%s] loaded (partial replica)\n", ldb_dn_get_linearized(p->dn))); + } + + talloc_free(tmp_ctx); status = dreplsrv_refresh_partitions(s); W_ERROR_NOT_OK_RETURN(status); @@ -91,9 +129,79 @@ WERROR dreplsrv_load_partitions(struct dreplsrv_service *s) return WERR_OK; } -static WERROR dreplsrv_out_connection_attach(struct dreplsrv_service *s, - const struct repsFromTo1 *rft, - struct dreplsrv_out_connection **_conn) +/* + work out the principal to use for DRS replication connections + */ +NTSTATUS dreplsrv_get_target_principal(struct dreplsrv_service *s, + TALLOC_CTX *mem_ctx, + const struct repsFromTo1 *rft, + const char **target_principal) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_result *res; + const char *attrs[] = { "dNSHostName", NULL }; + int ret; + const char *hostname; + struct ldb_dn *dn; + struct ldb_dn *forest_dn; + + *target_principal = NULL; + + 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, &dn); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + /* its OK for their NTDSDSA DN not to be in our database */ + return NT_STATUS_OK; + } + + /* strip off the NTDS Settings */ + if (!ldb_dn_remove_child_components(dn, 1)) { + talloc_free(tmp_ctx); + return NT_STATUS_OK; + } + + ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, dn, attrs, 0); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + /* its OK for their account DN not to be in our database */ + return NT_STATUS_OK; + } + + hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL); + if (hostname == NULL) { + talloc_free(tmp_ctx); + /* its OK to not have a dnshostname */ + return NT_STATUS_OK; + } + + /* All DCs have the GC/hostname/realm name, but if some of the + * preconditions are not satisfied, then we will fall back to + * the + * E3514235-4B06-11D1-AB04-00C04FC2DCD2/${NTDSGUID}/${DNSDOMAIN} + * name. This means that if a AD server has a dnsHostName set + * on it's record, it must also have GC/hostname/realm + * servicePrincipalName */ + + forest_dn = ldb_get_root_basedn(s->samdb); + if (forest_dn == NULL) { + talloc_free(tmp_ctx); + return NT_STATUS_OK; + } + + *target_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; +} + + +WERROR dreplsrv_out_connection_attach(struct dreplsrv_service *s, + const struct repsFromTo1 *rft, + struct dreplsrv_out_connection **_conn) { struct dreplsrv_out_connection *cur, *conn = NULL; const char *hostname; @@ -133,37 +241,65 @@ static WERROR dreplsrv_out_connection_attach(struct dreplsrv_service *s, return ntstatus_to_werror(nt_status); } + /* use the GC principal for DRS replication */ + nt_status = dreplsrv_get_target_principal(s, conn->binding, + rft, &conn->binding->target_principal); + if (!NT_STATUS_IS_OK(nt_status)) { + return ntstatus_to_werror(nt_status); + } + DLIST_ADD_END(s->connections, conn, struct dreplsrv_out_connection *); - DEBUG(2,("dreplsrv_out_connection_attach(%s): create\n", conn->binding->host)); + DEBUG(4,("dreplsrv_out_connection_attach(%s): create\n", conn->binding->host)); } else { - DEBUG(2,("dreplsrv_out_connection_attach(%s): attach\n", conn->binding->host)); + DEBUG(4,("dreplsrv_out_connection_attach(%s): attach\n", conn->binding->host)); } *_conn = conn; return WERR_OK; } +/* + find an existing source dsa in a list + */ +static struct dreplsrv_partition_source_dsa *dreplsrv_find_source_dsa(struct dreplsrv_partition_source_dsa *list, + struct GUID *guid) +{ + struct dreplsrv_partition_source_dsa *s; + for (s=list; s; s=s->next) { + if (GUID_compare(&s->repsFrom1->source_dsa_obj_guid, guid) == 0) { + return s; + } + } + return NULL; +} + + + static WERROR dreplsrv_partition_add_source_dsa(struct dreplsrv_service *s, struct dreplsrv_partition *p, + struct dreplsrv_partition_source_dsa **listp, + struct dreplsrv_partition_source_dsa *check_list, const struct ldb_val *val) { WERROR status; enum ndr_err_code ndr_err; - struct dreplsrv_partition_source_dsa *source; + struct dreplsrv_partition_source_dsa *source, *s2; source = talloc_zero(p, struct dreplsrv_partition_source_dsa); W_ERROR_HAVE_NO_MEMORY(source); ndr_err = ndr_pull_struct_blob(val, source, - lp_iconv_convenience(s->task->lp_ctx), &source->_repsFromBlob, + &source->_repsFromBlob, (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err); + talloc_free(source); return ntstatus_to_werror(nt_status); } /* NDR_PRINT_DEBUG(repsFromToBlob, &source->_repsFromBlob); */ if (source->_repsFromBlob.version != 1) { + talloc_free(source); return WERR_DS_DRA_INTERNAL_ERROR; } @@ -173,42 +309,173 @@ static WERROR dreplsrv_partition_add_source_dsa(struct dreplsrv_service *s, status = dreplsrv_out_connection_attach(s, source->repsFrom1, &source->conn); W_ERROR_NOT_OK_RETURN(status); - DLIST_ADD_END(p->sources, source, struct dreplsrv_partition_source_dsa *); + if (check_list && + dreplsrv_find_source_dsa(check_list, &source->repsFrom1->source_dsa_obj_guid)) { + /* its in the check list, don't add it again */ + talloc_free(source); + return WERR_OK; + } + + /* re-use an existing source if found */ + for (s2=*listp; s2; s2=s2->next) { + if (GUID_compare(&s2->repsFrom1->source_dsa_obj_guid, + &source->repsFrom1->source_dsa_obj_guid) == 0) { + talloc_free(s2->repsFrom1->other_info); + *s2->repsFrom1 = *source->repsFrom1; + talloc_steal(s2, s2->repsFrom1->other_info); + talloc_free(source); + return WERR_OK; + } + } + + DLIST_ADD_END(*listp, source, struct dreplsrv_partition_source_dsa *); + return WERR_OK; +} + +WERROR dreplsrv_partition_find_for_nc(struct dreplsrv_service *s, + const struct GUID *nc_guid, + const struct dom_sid *nc_sid, + const char *nc_dn_str, + struct dreplsrv_partition **_p) +{ + struct dreplsrv_partition *p; + bool valid_sid, valid_guid; + struct dom_sid null_sid; + ZERO_STRUCT(null_sid); + + SMB_ASSERT(_p); + + 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; + } + + for (p = s->partitions; p; p = p->next) { + if ((valid_guid && GUID_equal(&p->nc.guid, nc_guid)) + || strequal(p->nc.dn, nc_dn_str) + || (valid_sid && dom_sid_equal(&p->nc.sid, nc_sid))) + { + *_p = p; + return WERR_OK; + } + } + + return WERR_DS_DRA_BAD_NC; +} + +WERROR dreplsrv_partition_source_dsa_by_guid(struct dreplsrv_partition *p, + const struct GUID *dsa_guid, + struct dreplsrv_partition_source_dsa **_dsa) +{ + struct dreplsrv_partition_source_dsa *dsa; + + SMB_ASSERT(dsa_guid != NULL); + SMB_ASSERT(!GUID_all_zero(dsa_guid)); + SMB_ASSERT(_dsa); + + for (dsa = p->sources; dsa; dsa = dsa->next) { + if (GUID_equal(dsa_guid, &dsa->repsFrom1->source_dsa_obj_guid)) { + *_dsa = dsa; + return WERR_OK; + } + } + + return WERR_DS_DRA_NO_REPLICA; +} + +WERROR dreplsrv_partition_source_dsa_by_dns(const struct dreplsrv_partition *p, + const char *dsa_dns, + struct dreplsrv_partition_source_dsa **_dsa) +{ + struct dreplsrv_partition_source_dsa *dsa; + + SMB_ASSERT(dsa_dns != NULL); + SMB_ASSERT(_dsa); + + for (dsa = p->sources; dsa; dsa = dsa->next) { + if (strequal(dsa_dns, dsa->repsFrom1->other_info->dns_name)) { + *_dsa = dsa; + return WERR_OK; + } + } + + return WERR_DS_DRA_NO_REPLICA; +} + + +/* + create a temporary dsa structure for a replication. This is needed + for the initial replication of a new partition, such as when a new + domain NC is created and we are a global catalog server + */ +WERROR dreplsrv_partition_source_dsa_temporary(struct dreplsrv_partition *p, + TALLOC_CTX *mem_ctx, + const struct GUID *dsa_guid, + struct dreplsrv_partition_source_dsa **_dsa) +{ + struct dreplsrv_partition_source_dsa *dsa; + WERROR werr; + + dsa = talloc_zero(mem_ctx, struct dreplsrv_partition_source_dsa); + W_ERROR_HAVE_NO_MEMORY(dsa); + + dsa->partition = p; + dsa->repsFrom1 = &dsa->_repsFromBlob.ctr.ctr1; + dsa->repsFrom1->replica_flags = 0; + dsa->repsFrom1->source_dsa_obj_guid = *dsa_guid; + + dsa->repsFrom1->other_info = talloc_zero(dsa, struct repsFromTo1OtherInfo); + W_ERROR_HAVE_NO_MEMORY(dsa->repsFrom1->other_info); + + dsa->repsFrom1->other_info->dns_name = samdb_ntds_msdcs_dns_name(p->service->samdb, + dsa->repsFrom1->other_info, dsa_guid); + W_ERROR_HAVE_NO_MEMORY(dsa->repsFrom1->other_info->dns_name); + + werr = dreplsrv_out_connection_attach(p->service, dsa->repsFrom1, &dsa->conn); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(0,(__location__ ": Failed to attach connection to %s\n", + ldb_dn_get_linearized(p->dn))); + talloc_free(dsa); + return werr; + } + + *_dsa = dsa; + return WERR_OK; } + static WERROR dreplsrv_refresh_partition(struct dreplsrv_service *s, - struct dreplsrv_partition *p, - TALLOC_CTX *mem_ctx) + struct dreplsrv_partition *p) { WERROR status; - const struct ldb_val *ouv_value; - struct replUpToDateVectorBlob ouv; struct dom_sid *nc_sid; struct ldb_message_element *orf_el = NULL; struct ldb_result *r; - uint32_t i; + unsigned int i; int ret; + TALLOC_CTX *mem_ctx = talloc_new(p); static const char *attrs[] = { "objectSid", "objectGUID", - "replUpToDateVector", "repsFrom", + "repsTo", NULL }; - DEBUG(2, ("dreplsrv_refresh_partition(%s)\n", + DEBUG(4, ("dreplsrv_refresh_partition(%s)\n", ldb_dn_get_linearized(p->dn))); ret = ldb_search(s->samdb, mem_ctx, &r, p->dn, LDB_SCOPE_BASE, attrs, "(objectClass=*)"); if (ret != LDB_SUCCESS) { - return WERR_FOOBAR; - } else if (r->count != 1) { - talloc_free(r); + talloc_free(mem_ctx); return WERR_FOOBAR; } - + + talloc_free(discard_const(p->nc.dn)); ZERO_STRUCT(p->nc); p->nc.dn = ldb_dn_alloc_linearized(p, p->dn); W_ERROR_HAVE_NO_MEMORY(p->nc.dn); @@ -216,53 +483,49 @@ static WERROR dreplsrv_refresh_partition(struct dreplsrv_service *s, nc_sid = samdb_result_dom_sid(p, r->msgs[0], "objectSid"); if (nc_sid) { p->nc.sid = *nc_sid; + talloc_free(nc_sid); } - ouv_value = ldb_msg_find_ldb_val(r->msgs[0], "replUpToDateVector"); - if (ouv_value) { - enum ndr_err_code ndr_err; - ndr_err = ndr_pull_struct_blob(ouv_value, mem_ctx, - lp_iconv_convenience(s->task->lp_ctx), &ouv, - (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err); - return ntstatus_to_werror(nt_status); - } - /* NDR_PRINT_DEBUG(replUpToDateVectorBlob, &ouv); */ - if (ouv.version != 2) { - return WERR_DS_DRA_INTERNAL_ERROR; - } + talloc_free(p->uptodatevector.cursors); + talloc_free(p->uptodatevector_ex.cursors); + ZERO_STRUCT(p->uptodatevector); + ZERO_STRUCT(p->uptodatevector_ex); - p->uptodatevector.count = ouv.ctr.ctr2.count; - p->uptodatevector.reserved = ouv.ctr.ctr2.reserved; - p->uptodatevector.cursors = talloc_steal(p, ouv.ctr.ctr2.cursors); + ret = dsdb_load_udv_v2(s->samdb, p->dn, p, &p->uptodatevector.cursors, &p->uptodatevector.count); + if (ret != LDB_SUCCESS) { + DEBUG(4,(__location__ ": no UDV available for %s\n", ldb_dn_get_linearized(p->dn))); } - /* - * TODO: add our own uptodatevector cursor - */ - - orf_el = ldb_msg_find_element(r->msgs[0], "repsFrom"); if (orf_el) { for (i=0; i < orf_el->num_values; i++) { - status = dreplsrv_partition_add_source_dsa(s, p, &orf_el->values[i]); + status = dreplsrv_partition_add_source_dsa(s, p, &p->sources, + NULL, &orf_el->values[i]); + W_ERROR_NOT_OK_RETURN(status); + } + } + + orf_el = ldb_msg_find_element(r->msgs[0], "repsTo"); + if (orf_el) { + for (i=0; i < orf_el->num_values; i++) { + status = dreplsrv_partition_add_source_dsa(s, p, &p->notifies, + p->sources, &orf_el->values[i]); W_ERROR_NOT_OK_RETURN(status); } } - talloc_free(r); + talloc_free(mem_ctx); return WERR_OK; } -static WERROR dreplsrv_refresh_partitions(struct dreplsrv_service *s) +WERROR dreplsrv_refresh_partitions(struct dreplsrv_service *s) { WERROR status; struct dreplsrv_partition *p; for (p = s->partitions; p; p = p->next) { - status = dreplsrv_refresh_partition(s, p, p); + status = dreplsrv_refresh_partition(s, p); W_ERROR_NOT_OK_RETURN(status); }