X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Frpc_server%2Fdrsuapi%2Fgetncchanges.c;h=07e64d321de4a1539eb6b6cdb2ca79585d9488b6;hb=refs%2Fheads%2Fdrs_recycled;hp=1ad95babf9bcb709195e7548a52da3d019b1e11a;hpb=a0e9814c0d50ee822188e2bd6be8c4879aaebacc;p=mat%2Fsamba.git diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c index 1ad95babf9..07e64d321d 100644 --- a/source4/rpc_server/drsuapi/getncchanges.c +++ b/source4/rpc_server/drsuapi/getncchanges.c @@ -27,11 +27,11 @@ #include "librpc/gen_ndr/ndr_drsblobs.h" #include "librpc/gen_ndr/ndr_drsuapi.h" #include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" +#include "libcli/security/session.h" #include "rpc_server/drsuapi/dcesrv_drsuapi.h" #include "rpc_server/dcerpc_server_proto.h" #include "../libcli/drsuapi/drsuapi.h" -#include "libcli/security/security.h" -#include "libcli/security/session.h" #include "lib/util/binsearch.h" #include "lib/util/tsort.h" #include "auth/session.h" @@ -119,7 +119,8 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem uint32_t replica_flags, struct drsuapi_DsPartialAttributeSet *partial_attribute_set, struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector, - enum drsuapi_DsExtendedOperation extended_op) + enum drsuapi_DsExtendedOperation extended_op, + bool force_object_return) { const struct ldb_val *md_value; uint32_t i, n; @@ -207,7 +208,7 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem instanceType then don't include it */ if (md.ctr.ctr1.array[i].local_usn < highest_usn && extended_op != DRSUAPI_EXOP_REPL_SECRET && - md.ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType) continue; + md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) continue; /* don't include the rDN */ if (md.ctr.ctr1.array[i].attid == rdn_sa->attributeID_id) continue; @@ -237,7 +238,7 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem } /* filter by uptodateness_vector */ - if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType && + if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType && !force_attribute && udv_filter(uptodateness_vector, &md.ctr.ctr1.array[i].originating_invocation_id, @@ -260,9 +261,17 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem /* ignore it if its an empty change. Note that renames always * change the 'name' attribute, so they won't be ignored by - * this */ + * this + + * the force_object_return check is used to force an empty + * object return when we timeout in the getncchanges loop. + * This allows us to return an empty object, which keeps the + * client happy while preventing timeouts + */ if (n == 0 || - (n == 1 && attids[0] == DRSUAPI_ATTRIBUTE_instanceType)) { + (n == 1 && + attids[0] == DRSUAPI_ATTID_instanceType && + !force_object_return)) { talloc_free(obj->meta_data_ctr); obj->meta_data_ctr = NULL; return WERR_OK; @@ -274,6 +283,9 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem obj->object.attribute_ctr.num_attributes = obj->meta_data_ctr->count; obj->object.attribute_ctr.attributes = talloc_array(obj, struct drsuapi_DsReplicaAttribute, obj->object.attribute_ctr.num_attributes); + if (obj->object.attribute_ctr.attributes == NULL) { + return WERR_NOMEM; + } /* * Note that the meta_data array and the attributes array must @@ -328,7 +340,6 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem return WERR_OK; } - /* add one linked attribute from an object to the list of linked attributes in a getncchanges request @@ -357,30 +368,95 @@ static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx, active = (dsdb_dn_rmd_flags(dsdb_dn->dn) & DSDB_RMD_FLAG_DELETED) == 0; + if (!active) { + /* We have to check that the inactive link still point to an existing object */ + struct GUID guid; + struct ldb_dn *tdn; + int ret; + const char *v; + + v = ldb_msg_find_attr_as_string(msg, "isDeleted", "FALSE"); + if (strncmp(v, "TRUE", 4) == 0) { + /* + * Note: we skip the transmition of the deleted link even if the other part used to + * know about it because when we transmit the deletion of the object, the link will + * be deleted too due to deletion of object where link points and Windows do so. + */ + if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008_R2) { + v = ldb_msg_find_attr_as_string(msg, "isRecycled", "FALSE"); + /* + * On Windows 2008R2 isRecycled is always present even if FL or DL are < FL 2K8R2 + * if it join an existing domain with deleted objets, it firsts impose to have a + * schema with the is-Recycled object and for all deleted objects it adds the isRecycled + * either during initial replication or after the getNCChanges. + * Behavior of samba has been changed to always have this attribute if it's present in the schema. + * + * So if FL <2K8R2 isRecycled might be here or not but we don't care, it's meaning less. + * If FL >=2K8R2 we are sure that this attribute will be here. + * For this kind of forest level we do not return the link if the object is recycled + * (isRecycled = true). + */ + if (strncmp(v, "TRUE", 4) == 0) { + DEBUG(2, (" object %s is recycled, not returning linked attribute !\n", + ldb_dn_get_linearized(msg->dn))); + return WERR_OK; + } + } else { + return WERR_OK; + } + } + status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID"); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,(__location__ " Unable to extract GUID in linked attribute '%s' in '%s'\n", + sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn))); + return ntstatus_to_werror(status); + } + ret = dsdb_find_dn_by_guid(sam_ctx, mem_ctx, &guid, &tdn); + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + DEBUG(2, (" Search of guid %s returned 0 objects, skipping it !\n", + GUID_string(mem_ctx, &guid))); + return WERR_OK; + } else if (ret != LDB_SUCCESS) { + DEBUG(0, (__location__ " Search of guid %s failed with error code %d\n", + GUID_string(mem_ctx, &guid), + ret)); + return WERR_OK; + } + } la->attid = sa->attributeID_id; la->flags = active?DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE:0; - status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->originating_add_time, "RMD_ADDTIME"); - if (!NT_STATUS_IS_OK(status)) { - return ntstatus_to_werror(status); - } status = dsdb_get_extended_dn_uint32(dsdb_dn->dn, &la->meta_data.version, "RMD_VERSION"); if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,(__location__ " No RMD_VERSION in linked attribute '%s' in '%s'\n", + sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn))); return ntstatus_to_werror(status); } status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->meta_data.originating_change_time, "RMD_CHANGETIME"); if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,(__location__ " No RMD_CHANGETIME in linked attribute '%s' in '%s'\n", + sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn))); return ntstatus_to_werror(status); } status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &la->meta_data.originating_invocation_id, "RMD_INVOCID"); if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,(__location__ " No RMD_INVOCID in linked attribute '%s' in '%s'\n", + sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn))); return ntstatus_to_werror(status); } status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &la->meta_data.originating_usn, "RMD_ORIGINATING_USN"); if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,(__location__ " No RMD_ORIGINATING_USN in linked attribute '%s' in '%s'\n", + sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn))); return ntstatus_to_werror(status); } + status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->originating_add_time, "RMD_ADDTIME"); + if (!NT_STATUS_IS_OK(status)) { + /* this is possible for upgraded links */ + la->originating_add_time = la->meta_data.originating_change_time; + } + werr = dsdb_dn_la_to_blob(sam_ctx, sa, schema, *la_list, dsdb_dn, &la->value.blob); W_ERROR_NOT_OK_RETURN(werr); @@ -562,29 +638,37 @@ static int linked_attribute_compare(const struct drsuapi_DsReplicaLinkedAttribut return GUID_compare(&guid1, &guid2); } +struct drsuapi_changed_objects { + struct ldb_dn *dn; + struct GUID guid; + uint64_t usn; +}; /* sort the objects we send by tree order */ -static int site_res_cmp_parent_order(struct ldb_message **m1, struct ldb_message **m2) +static int site_res_cmp_parent_order(struct drsuapi_changed_objects *m1, + struct drsuapi_changed_objects *m2) { - return ldb_dn_compare((*m2)->dn, (*m1)->dn); + return ldb_dn_compare(m2->dn, m1->dn); } /* sort the objects we send first by uSNChanged */ -static int site_res_cmp_usn_order(struct ldb_message **m1, struct ldb_message **m2) +static int site_res_cmp_dn_usn_order(struct drsuapi_changed_objects *m1, + struct drsuapi_changed_objects *m2) { unsigned usnchanged1, usnchanged2; unsigned cn1, cn2; - cn1 = ldb_dn_get_comp_num((*m1)->dn); - cn2 = ldb_dn_get_comp_num((*m2)->dn); + + cn1 = ldb_dn_get_comp_num(m1->dn); + cn2 = ldb_dn_get_comp_num(m2->dn); if (cn1 != cn2) { return cn1 > cn2 ? 1 : -1; } - usnchanged1 = ldb_msg_find_attr_as_uint(*m1, "uSNChanged", 0); - usnchanged2 = ldb_msg_find_attr_as_uint(*m2, "uSNChanged", 0); + usnchanged1 = m1->usn; + usnchanged2 = m2->usn; if (usnchanged1 == usnchanged2) { return 0; } @@ -621,8 +705,7 @@ static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state, } req_dn = drs_ObjectIdentifier_to_dn(mem_ctx, ldb, req10->naming_context); - if (!req_dn || - !ldb_dn_validate(req_dn) || + if (!ldb_dn_validate(req_dn) || ldb_dn_compare(req_dn, rid_manager_dn) != 0) { /* that isn't the RID Manager DN */ DEBUG(0,(__location__ ": RID Alloc request for wrong DN %s\n", @@ -964,8 +1047,7 @@ static WERROR getncchanges_change_master(struct drsuapi_bind_state *b_state, */ req_dn = drs_ObjectIdentifier_to_dn(mem_ctx, ldb, req10->naming_context); - if (!req_dn || - !ldb_dn_validate(req_dn)) { + if (!ldb_dn_validate(req_dn)) { /* that is not a valid dn */ DEBUG(0,(__location__ ": FSMO role transfer request for invalid DN %s\n", drs_ObjectIdentifier_to_string(mem_ctx, req10->naming_context))); @@ -995,12 +1077,14 @@ static WERROR getncchanges_change_master(struct drsuapi_bind_state *b_state, msg->dn = drs_ObjectIdentifier_to_dn(msg, ldb, req10->naming_context); W_ERROR_HAVE_NO_MEMORY(msg->dn); + /* TODO: make sure ntds_dn is a valid nTDSDSA object */ ret = dsdb_find_dn_by_guid(ldb, msg, &req10->destination_dsa_guid, &ntds_dn); if (ret != LDB_SUCCESS) { DEBUG(0, (__location__ ": Unable to find NTDS object for guid %s - %s\n", GUID_string(mem_ctx, &req10->destination_dsa_guid), ldb_errstring(ldb))); talloc_free(msg); - return WERR_DS_DRA_INTERNAL_ERROR; + ctr6->extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER; + return WERR_OK; } ret = ldb_msg_add_string(msg, "fSMORoleOwner", ldb_dn_get_linearized(ntds_dn)); @@ -1042,8 +1126,9 @@ static WERROR getncchanges_change_master(struct drsuapi_bind_state *b_state, /* state of a partially completed getncchanges call */ struct drsuapi_getncchanges_state { - struct ldb_result *site_res; - uint32_t num_sent; + struct GUID *guids; + uint32_t num_records; + uint32_t num_processed; struct ldb_dn *ncRoot_dn; bool is_schema_nc; uint64_t min_usn; @@ -1113,20 +1198,90 @@ static WERROR dcesrv_drsuapi_is_reveal_secrets_request(struct drsuapi_bind_state } } + if (req10->partial_attribute_set_ex) { + /* check the extended attributes they asked for */ + for (i=0; ipartial_attribute_set_ex->num_attids; i++) { + const struct dsdb_attribute *sa; + sa = dsdb_attribute_by_attributeID_id(schema, req10->partial_attribute_set_ex->attids[i]); + if (sa == NULL) { + return WERR_DS_DRA_SCHEMA_MISMATCH; + } + if (!dsdb_attr_in_rodc_fas(sa)) { + *is_secret_request = true; + return WERR_OK; + } + } + } + + *is_secret_request = false; + return WERR_OK; +} + +/* + see if this getncchanges request is only for attributes in the GC + partial attribute set + */ +static WERROR dcesrv_drsuapi_is_gc_pas_request(struct drsuapi_bind_state *b_state, + struct drsuapi_DsGetNCChangesRequest10 *req10, + bool *is_gc_pas_request) +{ + enum drsuapi_DsExtendedOperation exop; + uint32_t i; + struct dsdb_schema *schema; + + exop = req10->extended_op; + + switch (exop) { + case DRSUAPI_EXOP_FSMO_REQ_ROLE: + case DRSUAPI_EXOP_FSMO_RID_ALLOC: + case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE: + case DRSUAPI_EXOP_FSMO_REQ_PDC: + case DRSUAPI_EXOP_FSMO_ABANDON_ROLE: + case DRSUAPI_EXOP_REPL_SECRET: + *is_gc_pas_request = false; + return WERR_OK; + case DRSUAPI_EXOP_REPL_OBJ: + case DRSUAPI_EXOP_NONE: + break; + } + + if (req10->partial_attribute_set == NULL) { + /* they want it all */ + *is_gc_pas_request = false; + return WERR_OK; + } + + schema = dsdb_get_schema(b_state->sam_ctx, NULL); + /* check the attributes they asked for */ - for (i=0; ipartial_attribute_set_ex->num_attids; i++) { + for (i=0; ipartial_attribute_set->num_attids; i++) { const struct dsdb_attribute *sa; - sa = dsdb_attribute_by_attributeID_id(schema, req10->partial_attribute_set_ex->attids[i]); + sa = dsdb_attribute_by_attributeID_id(schema, req10->partial_attribute_set->attids[i]); if (sa == NULL) { return WERR_DS_DRA_SCHEMA_MISMATCH; } - if (!dsdb_attr_in_rodc_fas(sa)) { - *is_secret_request = true; + if (!sa->isMemberOfPartialAttributeSet) { + *is_gc_pas_request = false; return WERR_OK; } } - *is_secret_request = false; + if (req10->partial_attribute_set_ex) { + /* check the extended attributes they asked for */ + for (i=0; ipartial_attribute_set_ex->num_attids; i++) { + const struct dsdb_attribute *sa; + sa = dsdb_attribute_by_attributeID_id(schema, req10->partial_attribute_set_ex->attids[i]); + if (sa == NULL) { + return WERR_DS_DRA_SCHEMA_MISMATCH; + } + if (!sa->isMemberOfPartialAttributeSet) { + *is_gc_pas_request = false; + return WERR_OK; + } + } + } + + *is_gc_pas_request = true; return WERR_OK; } @@ -1162,6 +1317,91 @@ getncchanges_map_req8(TALLOC_CTX *mem_ctx, } +/** + * Collects object for normal replication cycle. + */ +static WERROR getncchanges_collect_objects(struct drsuapi_bind_state *b_state, + TALLOC_CTX *mem_ctx, + struct drsuapi_DsGetNCChangesRequest10 *req10, + struct ldb_dn *search_dn, + const char *extra_filter, + struct ldb_result **search_res) +{ + int ret; + char* search_filter; + enum ldb_scope scope = LDB_SCOPE_SUBTREE; + //const char *extra_filter; + struct drsuapi_getncchanges_state *getnc_state = b_state->getncchanges_state; + const char *attrs[] = { "uSNChanged", + "objectGUID" , + NULL }; + + if (req10->extended_op == DRSUAPI_EXOP_REPL_OBJ || + req10->extended_op == DRSUAPI_EXOP_REPL_SECRET) { + scope = LDB_SCOPE_BASE; + } + + //extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter"); + + //getnc_state->min_usn = req10->highwatermark.highest_usn; + + /* Construct response. */ + search_filter = talloc_asprintf(mem_ctx, + "(uSNChanged>=%llu)", + (unsigned long long)(getnc_state->min_usn+1)); + + if (extra_filter) { + search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter); + } + + if (req10->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) { + search_filter = talloc_asprintf(mem_ctx, + "(&%s(isCriticalSystemObject=TRUE))", + search_filter); + } + + if (req10->replica_flags & DRSUAPI_DRS_ASYNC_REP) { + scope = LDB_SCOPE_BASE; + } + + if (!search_dn) { + search_dn = getnc_state->ncRoot_dn; + } + + DEBUG(2,(__location__ ": getncchanges on %s using filter %s\n", + ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter)); + ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, getnc_state, search_res, + search_dn, scope, attrs, + search_filter); + if (ret != LDB_SUCCESS) { + return WERR_DS_DRA_INTERNAL_ERROR; + } + + return WERR_OK; +} + +/** + * Collects object for normal replication cycle. + */ +static WERROR getncchanges_collect_objects_exop(struct drsuapi_bind_state *b_state, + TALLOC_CTX *mem_ctx, + struct drsuapi_DsGetNCChangesRequest10 *req10, + struct drsuapi_DsGetNCChangesCtr6 *ctr6, + struct ldb_dn *search_dn, + const char *extra_filter, + struct ldb_result **search_res) +{ + /* we have nothing to do in case of ex-op failure */ + if (ctr6->extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) { + return WERR_OK; + } + + /* TODO: implement extended op specific collection + * of objects. Right now we just normal procedure + * for collecting objects */ + return getncchanges_collect_objects(b_state, mem_ctx, req10, search_dn, extra_filter, search_res); +} + /* drsuapi_DsGetNCChanges @@ -1178,16 +1418,6 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ struct drsuapi_DsReplicaObjectListItemEx **currentObject; NTSTATUS status; DATA_BLOB session_key; - const char *attrs[] = { "*", "distinguishedName", - "nTSecurityDescriptor", - "parentGUID", - "replPropertyMetaData", - "unicodePwd", - "dBCSPwd", - "ntPwdHistory", - "lmPwdHistory", - "supplementalCredentials", - NULL }; WERROR werr; struct dcesrv_handle *h; struct drsuapi_bind_state *b_state; @@ -1205,6 +1435,11 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ struct ldb_context *sam_ctx; struct dom_sid *user_sid; bool is_secret_request; + bool is_gc_pas_request; + struct drsuapi_changed_objects *changes; + time_t max_wait; + time_t start = time(NULL); + bool max_wait_reached = false; DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE); b_state = h->data; @@ -1267,6 +1502,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ user_sid = &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX]; + /* all clients must have GUID_DRS_GET_CHANGES */ werr = drs_security_access_check_nc_root(b_state->sam_ctx, mem_ctx, dce_call->conn->auth_state.session_info->security_token, @@ -1276,6 +1512,23 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ return werr; } + /* allowed if the GC PAS and client has + GUID_DRS_GET_FILTERED_ATTRIBUTES */ + werr = dcesrv_drsuapi_is_gc_pas_request(b_state, req10, &is_gc_pas_request); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + if (is_gc_pas_request) { + werr = drs_security_access_check_nc_root(b_state->sam_ctx, + mem_ctx, + dce_call->conn->auth_state.session_info->security_token, + req10->naming_context, + GUID_DRS_GET_FILTERED_ATTRIBUTES); + if (W_ERROR_IS_OK(werr)) { + goto allowed; + } + } + werr = dcesrv_drsuapi_is_reveal_secrets_request(b_state, req10, &is_secret_request); if (!W_ERROR_IS_OK(werr)) { return werr; @@ -1291,6 +1544,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ } } +allowed: /* for non-administrator replications, check that they have given the correct source_dsa_invocation_id */ security_level = security_session_user_level(dce_call->conn->auth_state.session_info, @@ -1335,6 +1589,10 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ ldb_get_schema_basedn(b_state->sam_ctx)); getnc_state->is_schema_nc = (0 == ret); + if (req10->extended_op != DRSUAPI_EXOP_NONE) { + r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_SUCCESS; + } + /* * This is the first replication cycle and it is * a good place to handle extended operations @@ -1398,60 +1656,50 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ TODO: MS-DRSR section 4.1.10.1.1 Work out if this is the start of a new cycle */ - if (getnc_state->site_res == NULL) { - char* search_filter; - enum ldb_scope scope = LDB_SCOPE_SUBTREE; + if (getnc_state->guids == NULL) { const char *extra_filter; - - if (req10->extended_op == DRSUAPI_EXOP_REPL_OBJ || - req10->extended_op == DRSUAPI_EXOP_REPL_SECRET) { - scope = LDB_SCOPE_BASE; - } + struct ldb_result *search_res = NULL; extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter"); getnc_state->min_usn = req10->highwatermark.highest_usn; - /* Construct response. */ - search_filter = talloc_asprintf(mem_ctx, - "(uSNChanged>=%llu)", - (unsigned long long)(getnc_state->min_usn+1)); - - if (extra_filter) { - search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter); - } - - if (req10->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) { - search_filter = talloc_asprintf(mem_ctx, - "(&%s(isCriticalSystemObject=TRUE))", - search_filter); - } - - if (req10->replica_flags & DRSUAPI_DRS_ASYNC_REP) { - scope = LDB_SCOPE_BASE; - } - - if (!search_dn) { - search_dn = getnc_state->ncRoot_dn; + if (req10->extended_op == DRSUAPI_EXOP_NONE) { + werr = getncchanges_collect_objects(b_state, mem_ctx, req10, + search_dn, extra_filter, + &search_res); + } else { + werr = getncchanges_collect_objects_exop(b_state, mem_ctx, req10, + &r->out.ctr->ctr6, + search_dn, extra_filter, + &search_res); } - - DEBUG(2,(__location__ ": getncchanges on %s using filter %s\n", - ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter)); - ret = drsuapi_search_with_extended_dn(sam_ctx, getnc_state, &getnc_state->site_res, - search_dn, scope, attrs, - search_filter); - if (ret != LDB_SUCCESS) { - return WERR_DS_DRA_INTERNAL_ERROR; + W_ERROR_NOT_OK_RETURN(werr); + + /* extract out the GUIDs list */ + getnc_state->num_records = search_res ? search_res->count : 0; + getnc_state->guids = talloc_array(getnc_state, struct GUID, getnc_state->num_records); + W_ERROR_HAVE_NO_MEMORY(getnc_state->guids); + + changes = talloc_array(getnc_state, + struct drsuapi_changed_objects, + getnc_state->num_records); + W_ERROR_HAVE_NO_MEMORY(changes); + + for (i=0; inum_records; i++) { + changes[i].dn = search_res->msgs[i]->dn; + changes[i].guid = samdb_result_guid(search_res->msgs[i], "objectGUID"); + changes[i].usn = ldb_msg_find_attr_as_uint64(search_res->msgs[i], "uSNChanged", 0); } if (req10->replica_flags & DRSUAPI_DRS_GET_ANC) { - TYPESAFE_QSORT(getnc_state->site_res->msgs, - getnc_state->site_res->count, + TYPESAFE_QSORT(changes, + getnc_state->num_records, site_res_cmp_parent_order); } else { - TYPESAFE_QSORT(getnc_state->site_res->msgs, - getnc_state->site_res->count, - site_res_cmp_usn_order); + TYPESAFE_QSORT(changes, + getnc_state->num_records, + site_res_cmp_dn_usn_order); } getnc_state->uptodateness_vector = talloc_steal(getnc_state, req10->uptodateness_vector); @@ -1461,6 +1709,18 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ getnc_state->uptodateness_vector->count, drsuapi_DsReplicaCursor_compare); } + + for (i=0; i < getnc_state->num_records; i++) { + getnc_state->guids[i] = changes[i].guid; + if (GUID_all_zero(&getnc_state->guids[i])) { + DEBUG(2,("getncchanges: bad objectGUID from %s\n", + ldb_dn_get_linearized(search_res->msgs[i]->dn))); + return WERR_DS_DRA_INTERNAL_ERROR; + } + } + + talloc_free(search_res); + talloc_free(changes); } /* Prefix mapping */ @@ -1507,16 +1767,56 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ */ max_links = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max link sync", 1500); - for (i=getnc_state->num_sent; - isite_res->count && + /* + * Maximum time that we can spend in a getncchanges + * in order to avoid timeout of the other part. + * 10 seconds by default. + */ + max_wait = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max work time", 10); + for (i=getnc_state->num_processed; + inum_records && !null_scope && - (r->out.ctr->ctr6.object_count < max_objects); + (r->out.ctr->ctr6.object_count < max_objects) + && !max_wait_reached; i++) { int uSN; struct drsuapi_DsReplicaObjectListItemEx *obj; - struct ldb_message *msg = getnc_state->site_res->msgs[i]; + struct ldb_message *msg; + static const char * const msg_attrs[] = { + "*", + "nTSecurityDescriptor", + "parentGUID", + "replPropertyMetaData", + DSDB_SECRET_ATTRIBUTES, + NULL }; + struct ldb_result *msg_res; + struct ldb_dn *msg_dn; obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx); + W_ERROR_HAVE_NO_MEMORY(obj); + + msg_dn = ldb_dn_new_fmt(obj, sam_ctx, "", GUID_string(obj, &getnc_state->guids[i])); + W_ERROR_HAVE_NO_MEMORY(msg_dn); + + + /* by re-searching here we avoid having a lot of full + * records in memory between calls to getncchanges + */ + ret = drsuapi_search_with_extended_dn(sam_ctx, obj, &msg_res, + msg_dn, + LDB_SCOPE_BASE, msg_attrs, NULL); + if (ret != LDB_SUCCESS) { + if (ret != LDB_ERR_NO_SUCH_OBJECT) { + DEBUG(1,("getncchanges: failed to fetch DN %s - %s\n", + ldb_dn_get_extended_linearized(obj, msg_dn, 1), ldb_errstring(sam_ctx))); + } + talloc_free(obj); + continue; + } + + msg = msg_res->msgs[0]; + + max_wait_reached = (time(NULL) - start > max_wait); werr = get_nc_changes_build_object(obj, msg, sam_ctx, getnc_state->ncRoot_dn, @@ -1525,7 +1825,8 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ req10->replica_flags, req10->partial_attribute_set, getnc_state->uptodateness_vector, - req10->extended_op); + req10->extended_op, + max_wait_reached); if (!W_ERROR_IS_OK(werr)) { return werr; } @@ -1567,11 +1868,14 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ getnc_state->last_dn = ldb_dn_copy(getnc_state, msg->dn); DEBUG(8,(__location__ ": replicating object %s\n", ldb_dn_get_linearized(msg->dn))); + + talloc_free(msg_res); + talloc_free(msg_dn); } - getnc_state->num_sent += r->out.ctr->ctr6.object_count; + getnc_state->num_processed = i; - r->out.ctr->ctr6.nc_object_count = getnc_state->site_res->count; + r->out.ctr->ctr6.nc_object_count = getnc_state->num_records; /* the client can us to call UpdateRefs on its behalf to re-establish monitoring of the NC */ @@ -1581,9 +1885,8 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ DEBUG(3,("UpdateRefs on getncchanges for %s\n", GUID_string(mem_ctx, &req10->destination_dsa_guid))); ureq.naming_context = ncRoot; - ureq.dest_dsa_dns_name = talloc_asprintf(mem_ctx, "%s._msdcs.%s", - GUID_string(mem_ctx, &req10->destination_dsa_guid), - lpcfg_dnsdomain(dce_call->conn->dce_ctx->lp_ctx)); + ureq.dest_dsa_dns_name = samdb_ntds_msdcs_dns_name(b_state->sam_ctx, mem_ctx, + &req10->destination_dsa_guid); if (!ureq.dest_dsa_dns_name) { return WERR_NOMEM; } @@ -1618,7 +1921,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ link_total = getnc_state->la_count; - if (i < getnc_state->site_res->count) { + if (i < getnc_state->num_records) { r->out.ctr->ctr6.more_data = true; } else { /* sort the whole array the first time */ @@ -1662,7 +1965,6 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ r->out.ctr->ctr6.uptodateness_vector = NULL; r->out.ctr->ctr6.nc_object_count = 0; ZERO_STRUCT(r->out.ctr->ctr6.new_highwatermark); - r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_SUCCESS; } DEBUG(r->out.ctr->ctr6.more_data?4:2, @@ -1670,7 +1972,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ (unsigned long long)(req10->highwatermark.highest_usn+1), req10->replica_flags, drs_ObjectIdentifier_to_string(mem_ctx, ncRoot), r->out.ctr->ctr6.object_count, - i, r->out.ctr->ctr6.more_data?getnc_state->site_res->count:i, + i, r->out.ctr->ctr6.more_data?getnc_state->num_records:i, r->out.ctr->ctr6.linked_attributes_count, link_given, link_total, dom_sid_string(mem_ctx, user_sid)));