2 Unix SMB/CIFS implementation.
4 implement the DRSUpdateRefs call
6 Copyright (C) Anatoliy Atanasov 2009
7 Copyright (C) Andrew Tridgell 2009
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "rpc_server/dcerpc_server.h"
25 #include "dsdb/samdb/samdb.h"
26 #include "param/param.h"
27 #include "librpc/gen_ndr/ndr_drsblobs.h"
28 #include "librpc/gen_ndr/ndr_drsuapi.h"
29 #include "rpc_server/drsuapi/dcesrv_drsuapi.h"
30 #include "rpc_server/dcerpc_server_proto.h"
31 #include "../libcli/drsuapi/drsuapi.h"
32 #include "libcli/security/security.h"
33 #include "lib/util/binsearch.h"
34 #include "lib/util/tsort.h"
35 #include "auth/session.h"
38 build a DsReplicaObjectIdentifier from a ldb msg
40 static struct drsuapi_DsReplicaObjectIdentifier *get_object_identifier(TALLOC_CTX *mem_ctx,
41 struct ldb_message *msg)
43 struct drsuapi_DsReplicaObjectIdentifier *identifier;
46 identifier = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
47 if (identifier == NULL) {
51 identifier->dn = ldb_dn_alloc_linearized(identifier, msg->dn);
52 identifier->guid = samdb_result_guid(msg, "objectGUID");
54 sid = samdb_result_dom_sid(identifier, msg, "objectSid");
56 identifier->sid = *sid;
58 ZERO_STRUCT(identifier->sid);
63 static int udv_compare(const struct GUID *guid1, struct GUID guid2)
65 return GUID_compare(guid1, &guid2);
69 see if we can filter an attribute using the uptodateness_vector
71 static bool udv_filter(const struct drsuapi_DsReplicaCursorCtrEx *udv,
72 const struct GUID *originating_invocation_id,
73 uint64_t originating_usn)
75 const struct drsuapi_DsReplicaCursor *c;
76 if (udv == NULL) return false;
77 BINARY_ARRAY_SEARCH(udv->cursors, udv->count, source_dsa_invocation_id,
78 originating_invocation_id, udv_compare, c);
79 if (c && originating_usn <= c->highest_usn) {
87 drsuapi_DsGetNCChanges for one object
89 static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItemEx *obj,
90 struct ldb_message *msg,
91 struct ldb_context *sam_ctx,
92 struct ldb_dn *ncRoot_dn,
93 struct dsdb_schema *schema,
94 DATA_BLOB *session_key,
96 uint32_t replica_flags,
97 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
99 const struct ldb_val *md_value;
101 struct replPropertyMetaDataBlob md;
103 enum ndr_err_code ndr_err;
106 const struct dsdb_attribute *rdn_sa;
107 unsigned int instanceType;
108 int rodc_filtered_flags;
110 instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType", 0);
111 if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
112 obj->is_nc_prefix = true;
113 obj->parent_object_guid = NULL;
115 obj->is_nc_prefix = false;
116 obj->parent_object_guid = talloc(obj, struct GUID);
117 if (obj->parent_object_guid == NULL) {
118 return WERR_DS_DRA_INTERNAL_ERROR;
120 *obj->parent_object_guid = samdb_result_guid(msg, "parentGUID");
121 if (GUID_all_zero(obj->parent_object_guid)) {
122 DEBUG(0,(__location__ ": missing parentGUID for %s\n",
123 ldb_dn_get_linearized(msg->dn)));
124 return WERR_DS_DRA_INTERNAL_ERROR;
127 obj->next_object = NULL;
129 md_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
131 /* nothing to send */
135 ndr_err = ndr_pull_struct_blob(md_value, obj,
136 lp_iconv_convenience(ldb_get_opaque(sam_ctx, "loadparm")), &md,
137 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
138 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
139 return WERR_DS_DRA_INTERNAL_ERROR;
142 if (md.version != 1) {
143 return WERR_DS_DRA_INTERNAL_ERROR;
146 rdn = ldb_dn_get_rdn_name(msg->dn);
148 DEBUG(0,(__location__ ": No rDN for %s\n", ldb_dn_get_linearized(msg->dn)));
149 return WERR_DS_DRA_INTERNAL_ERROR;
152 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn);
153 if (rdn_sa == NULL) {
154 DEBUG(0,(__location__ ": Can't find dsds_attribute for rDN %s in %s\n",
155 rdn, ldb_dn_get_linearized(msg->dn)));
156 return WERR_DS_DRA_INTERNAL_ERROR;
159 obj->meta_data_ctr = talloc(obj, struct drsuapi_DsReplicaMetaDataCtr);
160 attids = talloc_array(obj, uint32_t, md.ctr.ctr1.count);
162 obj->object.identifier = get_object_identifier(obj, msg);
163 if (obj->object.identifier == NULL) {
166 dom_sid_split_rid(NULL, &obj->object.identifier->sid, NULL, &rid);
168 obj->meta_data_ctr->meta_data = talloc_array(obj, struct drsuapi_DsReplicaMetaData, md.ctr.ctr1.count);
169 for (n=i=0; i<md.ctr.ctr1.count; i++) {
170 const struct dsdb_attribute *sa;
171 /* if the attribute has not changed, and it is not the
172 instanceType then don't include it */
173 if (md.ctr.ctr1.array[i].local_usn < highest_usn &&
174 md.ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType) continue;
176 /* don't include the rDN */
177 if (md.ctr.ctr1.array[i].attid == rdn_sa->attributeID_id) continue;
179 sa = dsdb_attribute_by_attributeID_id(schema, md.ctr.ctr1.array[i].attid);
181 DEBUG(0,(__location__ ": Failed to find attribute in schema for attrid %u mentioned in replPropertyMetaData of %s\n",
182 (unsigned int)md.ctr.ctr1.array[i].attid,
183 ldb_dn_get_linearized(msg->dn)));
184 return WERR_DS_DRA_INTERNAL_ERROR;
188 struct ldb_message_element *el;
189 el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
190 if (el && el->num_values && dsdb_dn_is_upgraded_link_val(&el->values[0])) {
191 /* don't send upgraded links inline */
196 /* filter by uptodateness_vector */
197 if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType &&
198 udv_filter(uptodateness_vector,
199 &md.ctr.ctr1.array[i].originating_invocation_id,
200 md.ctr.ctr1.array[i].originating_usn)) {
204 /* if the recipient is a RODC, then we should not add any
205 * RODC filtered attribute */
206 /* TODO: This is not strictly correct, as it doesn't allow for administrators
207 to setup some users to transfer passwords to specific RODCs. To support that
208 we would instead remove this check and rely on extended ACL checking in the dsdb
210 rodc_filtered_flags = SEARCH_FLAG_RODC_ATTRIBUTE | SEARCH_FLAG_CONFIDENTIAL;
211 if ((replica_flags & DRSUAPI_DRS_WRIT_REP) == 0 &&
212 (sa->searchFlags & rodc_filtered_flags)) {
217 obj->meta_data_ctr->meta_data[n].originating_change_time = md.ctr.ctr1.array[i].originating_change_time;
218 obj->meta_data_ctr->meta_data[n].version = md.ctr.ctr1.array[i].version;
219 obj->meta_data_ctr->meta_data[n].originating_invocation_id = md.ctr.ctr1.array[i].originating_invocation_id;
220 obj->meta_data_ctr->meta_data[n].originating_usn = md.ctr.ctr1.array[i].originating_usn;
221 attids[n] = md.ctr.ctr1.array[i].attid;
225 /* ignore it if its an empty change. Note that renames always
226 * change the 'name' attribute, so they won't be ignored by
229 (n == 1 && attids[0] == DRSUAPI_ATTRIBUTE_instanceType)) {
230 talloc_free(obj->meta_data_ctr);
231 obj->meta_data_ctr = NULL;
235 obj->meta_data_ctr->count = n;
237 obj->object.flags = DRSUAPI_DS_REPLICA_OBJECT_FROM_MASTER;
238 obj->object.attribute_ctr.num_attributes = obj->meta_data_ctr->count;
239 obj->object.attribute_ctr.attributes = talloc_array(obj, struct drsuapi_DsReplicaAttribute,
240 obj->object.attribute_ctr.num_attributes);
243 * Note that the meta_data array and the attributes array must
244 * be the same size and in the same order
246 for (i=0; i<obj->object.attribute_ctr.num_attributes; i++) {
247 struct ldb_message_element *el;
249 const struct dsdb_attribute *sa;
251 sa = dsdb_attribute_by_attributeID_id(schema, attids[i]);
253 DEBUG(0,("Unable to find attributeID %u in schema\n", attids[i]));
254 return WERR_DS_DRA_INTERNAL_ERROR;
257 el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
259 /* this happens for attributes that have been removed */
260 DEBUG(5,("No element '%s' for attributeID %u in message\n",
261 sa->lDAPDisplayName, attids[i]));
262 ZERO_STRUCT(obj->object.attribute_ctr.attributes[i]);
263 obj->object.attribute_ctr.attributes[i].attid = attids[i];
265 werr = dsdb_attribute_ldb_to_drsuapi(sam_ctx, schema, el, obj,
266 &obj->object.attribute_ctr.attributes[i]);
267 if (!W_ERROR_IS_OK(werr)) {
268 DEBUG(0,("Unable to convert %s to DRS object - %s\n",
269 sa->lDAPDisplayName, win_errstr(werr)));
272 /* if DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING is set
273 * check if attribute is secret and send a null value
275 if (replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
276 drsuapi_process_secret_attribute(&obj->object.attribute_ctr.attributes[i],
277 &obj->meta_data_ctr->meta_data[i]);
279 /* some attributes needs to be encrypted
281 werr = drsuapi_encrypt_attribute(obj, session_key, rid,
282 &obj->object.attribute_ctr.attributes[i]);
283 if (!W_ERROR_IS_OK(werr)) {
284 DEBUG(0,("Unable to encrypt %s in DRS object - %s\n",
285 sa->lDAPDisplayName, win_errstr(werr)));
296 add one linked attribute from an object to the list of linked
297 attributes in a getncchanges request
299 static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx,
300 struct ldb_context *sam_ctx,
301 const struct dsdb_schema *schema,
302 const struct dsdb_attribute *sa,
303 struct ldb_message *msg,
304 struct dsdb_dn *dsdb_dn,
305 struct drsuapi_DsReplicaLinkedAttribute **la_list,
308 struct drsuapi_DsReplicaLinkedAttribute *la;
313 (*la_list) = talloc_realloc(mem_ctx, *la_list, struct drsuapi_DsReplicaLinkedAttribute, (*la_count)+1);
314 W_ERROR_HAVE_NO_MEMORY(*la_list);
316 la = &(*la_list)[*la_count];
318 la->identifier = get_object_identifier(*la_list, msg);
319 W_ERROR_HAVE_NO_MEMORY(la->identifier);
321 active = (dsdb_dn_rmd_flags(dsdb_dn->dn) & DSDB_RMD_FLAG_DELETED) == 0;
323 la->attid = sa->attributeID_id;
324 la->flags = active?DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE:0;
326 status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->originating_add_time, "RMD_ADDTIME");
327 if (!NT_STATUS_IS_OK(status)) {
328 return ntstatus_to_werror(status);
330 status = dsdb_get_extended_dn_uint32(dsdb_dn->dn, &la->meta_data.version, "RMD_VERSION");
331 if (!NT_STATUS_IS_OK(status)) {
332 return ntstatus_to_werror(status);
334 status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->meta_data.originating_change_time, "RMD_CHANGETIME");
335 if (!NT_STATUS_IS_OK(status)) {
336 return ntstatus_to_werror(status);
338 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &la->meta_data.originating_invocation_id, "RMD_INVOCID");
339 if (!NT_STATUS_IS_OK(status)) {
340 return ntstatus_to_werror(status);
342 status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &la->meta_data.originating_usn, "RMD_ORIGINATING_USN");
343 if (!NT_STATUS_IS_OK(status)) {
344 return ntstatus_to_werror(status);
347 werr = dsdb_dn_la_to_blob(sam_ctx, sa, schema, *la_list, dsdb_dn, &la->value.blob);
348 W_ERROR_NOT_OK_RETURN(werr);
356 add linked attributes from an object to the list of linked
357 attributes in a getncchanges request
359 static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
361 struct ldb_dn *ncRoot_dn,
362 struct dsdb_schema *schema,
363 uint64_t highest_usn,
364 uint32_t replica_flags,
365 struct ldb_message *msg,
366 struct drsuapi_DsReplicaLinkedAttribute **la_list,
368 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
371 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
372 uint64_t uSNChanged = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1);
374 for (i=0; i<msg->num_elements; i++) {
375 struct ldb_message_element *el = &msg->elements[i];
376 const struct dsdb_attribute *sa;
379 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
381 if (!sa || sa->linkID == 0 || (sa->linkID & 1)) {
382 /* we only want forward links */
386 if (el->num_values && !dsdb_dn_is_upgraded_link_val(&el->values[0])) {
387 /* its an old style link, it will have been
388 * sent in the main replication data */
392 for (j=0; j<el->num_values; j++) {
393 struct dsdb_dn *dsdb_dn;
398 dsdb_dn = dsdb_dn_parse(tmp_ctx, sam_ctx, &el->values[j], sa->syntax->ldap_oid);
399 if (dsdb_dn == NULL) {
400 DEBUG(1,(__location__ ": Failed to parse DN for %s in %s\n",
401 el->name, ldb_dn_get_linearized(msg->dn)));
402 talloc_free(tmp_ctx);
403 return WERR_DS_DRA_INTERNAL_ERROR;
406 status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &local_usn, "RMD_LOCAL_USN");
407 if (!NT_STATUS_IS_OK(status)) {
408 /* this can happen for attributes
409 given to us with old style meta
414 if (local_usn > uSNChanged) {
415 DEBUG(1,(__location__ ": uSNChanged less than RMD_LOCAL_USN for %s on %s\n",
416 el->name, ldb_dn_get_linearized(msg->dn)));
417 talloc_free(tmp_ctx);
418 return WERR_DS_DRA_INTERNAL_ERROR;
421 if (local_usn < highest_usn) {
425 werr = get_nc_changes_add_la(mem_ctx, sam_ctx, schema, sa, msg,
426 dsdb_dn, la_list, la_count);
427 if (!W_ERROR_IS_OK(werr)) {
428 talloc_free(tmp_ctx);
434 talloc_free(tmp_ctx);
439 fill in the cursors return based on the replUpToDateVector for the ncRoot_dn
441 static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
442 struct ldb_dn *ncRoot_dn,
443 struct drsuapi_DsReplicaCursor2CtrEx *udv)
451 ret = dsdb_load_udv_v2(sam_ctx, ncRoot_dn, udv, &udv->cursors, &udv->count);
452 if (ret != LDB_SUCCESS) {
453 DEBUG(0,(__location__ ": Failed to load UDV for %s - %s\n",
454 ldb_dn_get_linearized(ncRoot_dn), ldb_errstring(sam_ctx)));
455 return WERR_DS_DRA_INTERNAL_ERROR;
462 /* comparison function for linked attributes - see CompareLinks() in
463 * MS-DRSR section 4.1.10.5.17 */
464 static int linked_attribute_compare(const struct drsuapi_DsReplicaLinkedAttribute *la1,
465 const struct drsuapi_DsReplicaLinkedAttribute *la2,
466 struct ldb_context *sam_ctx)
471 const struct dsdb_schema *schema;
472 const struct dsdb_attribute *schema_attrib;
473 struct dsdb_dn *dn1, *dn2;
474 struct GUID guid1, guid2;
477 c = GUID_compare(&la1->identifier->guid,
478 &la2->identifier->guid);
479 if (c != 0) return c;
481 if (la1->attid != la2->attid) {
482 return la1->attid < la2->attid? -1:1;
485 if ((la1->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) !=
486 (la2->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)) {
487 return (la1->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)? 1:-1;
490 /* we need to get the target GUIDs to compare */
491 tmp_ctx = talloc_new(sam_ctx);
493 schema = dsdb_get_schema(sam_ctx, tmp_ctx);
494 schema_attrib = dsdb_attribute_by_attributeID_id(schema, la1->attid);
496 werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, tmp_ctx, la1->value.blob, &dn1);
497 if (!W_ERROR_IS_OK(werr)) {
498 DEBUG(0,(__location__ ": Bad la1 blob in sort\n"));
499 talloc_free(tmp_ctx);
503 werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, tmp_ctx, la2->value.blob, &dn2);
504 if (!W_ERROR_IS_OK(werr)) {
505 DEBUG(0,(__location__ ": Bad la2 blob in sort\n"));
506 talloc_free(tmp_ctx);
510 status = dsdb_get_extended_dn_guid(dn1->dn, &guid1, "GUID");
511 if (!NT_STATUS_IS_OK(status)) {
512 DEBUG(0,(__location__ ": Bad la1 guid in sort\n"));
513 talloc_free(tmp_ctx);
516 status = dsdb_get_extended_dn_guid(dn2->dn, &guid2, "GUID");
517 if (!NT_STATUS_IS_OK(status)) {
518 DEBUG(0,(__location__ ": Bad la2 guid in sort\n"));
519 talloc_free(tmp_ctx);
523 talloc_free(tmp_ctx);
525 return GUID_compare(&guid1, &guid2);
530 sort the objects we send by tree order
532 static int site_res_cmp_parent_order(struct ldb_message **m1, struct ldb_message **m2)
534 return ldb_dn_compare((*m2)->dn, (*m1)->dn);
538 sort the objects we send first by uSNChanged
540 static int site_res_cmp_usn_order(struct ldb_message **m1, struct ldb_message **m2)
542 unsigned usnchanged1, usnchanged2;
544 cn1 = ldb_dn_get_comp_num((*m1)->dn);
545 cn2 = ldb_dn_get_comp_num((*m2)->dn);
547 return cn1 > cn2 ? 1 : -1;
549 usnchanged1 = ldb_msg_find_attr_as_uint(*m1, "uSNChanged", 0);
550 usnchanged2 = ldb_msg_find_attr_as_uint(*m2, "uSNChanged", 0);
551 if (usnchanged1 == usnchanged2) {
554 return usnchanged1 > usnchanged2 ? 1 : -1;
559 handle a DRSUAPI_EXOP_FSMO_RID_ALLOC call
561 static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state,
563 struct drsuapi_DsGetNCChangesRequest8 *req8,
564 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
566 struct ldb_dn *rid_manager_dn, *fsmo_role_dn, *req_dn;
568 struct ldb_context *ldb = b_state->sam_ctx;
569 struct ldb_result *ext_res;
570 struct ldb_dn *base_dn;
571 struct dsdb_fsmo_extended_op *exop;
575 - verify that the DN being asked for is the RID Manager DN
576 - verify that we are the RID Manager
579 /* work out who is the RID Manager */
580 ret = samdb_rid_manager_dn(ldb, mem_ctx, &rid_manager_dn);
581 if (ret != LDB_SUCCESS) {
582 DEBUG(0, (__location__ ": Failed to find RID Manager object - %s\n", ldb_errstring(ldb)));
583 return WERR_DS_DRA_INTERNAL_ERROR;
586 req_dn = ldb_dn_new(mem_ctx, ldb, req8->naming_context->dn);
588 !ldb_dn_validate(req_dn) ||
589 ldb_dn_compare(req_dn, rid_manager_dn) != 0) {
590 /* that isn't the RID Manager DN */
591 DEBUG(0,(__location__ ": RID Alloc request for wrong DN %s\n",
592 req8->naming_context->dn));
593 ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
597 /* find the DN of the RID Manager */
598 ret = samdb_reference_dn(ldb, mem_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
599 if (ret != LDB_SUCCESS) {
600 DEBUG(0,(__location__ ": Failed to find fSMORoleOwner in RID Manager object - %s\n",
601 ldb_errstring(ldb)));
602 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
603 return WERR_DS_DRA_INTERNAL_ERROR;
606 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
607 /* we're not the RID Manager - go away */
608 DEBUG(0,(__location__ ": RID Alloc request when not RID Manager\n"));
609 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
613 exop = talloc(mem_ctx, struct dsdb_fsmo_extended_op);
614 W_ERROR_HAVE_NO_MEMORY(exop);
616 exop->fsmo_info = req8->fsmo_info;
617 exop->destination_dsa_guid = req8->destination_dsa_guid;
619 ret = ldb_transaction_start(ldb);
620 if (ret != LDB_SUCCESS) {
621 DEBUG(0,(__location__ ": Failed transaction start - %s\n",
622 ldb_errstring(ldb)));
623 return WERR_DS_DRA_INTERNAL_ERROR;
626 ret = ldb_extended(ldb, DSDB_EXTENDED_ALLOCATE_RID_POOL, exop, &ext_res);
627 if (ret != LDB_SUCCESS) {
628 DEBUG(0,(__location__ ": Failed extended allocation RID pool operation - %s\n",
629 ldb_errstring(ldb)));
630 ldb_transaction_cancel(ldb);
631 return WERR_DS_DRA_INTERNAL_ERROR;
634 ret = ldb_transaction_commit(ldb);
635 if (ret != LDB_SUCCESS) {
636 DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
637 ldb_errstring(ldb)));
638 return WERR_DS_DRA_INTERNAL_ERROR;
641 talloc_free(ext_res);
643 base_dn = ldb_get_default_basedn(ldb);
645 DEBUG(2,("Allocated RID pool for server %s\n",
646 GUID_string(mem_ctx, &req8->destination_dsa_guid)));
648 ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
655 /* state of a partially completed getncchanges call */
656 struct drsuapi_getncchanges_state {
657 struct ldb_result *site_res;
659 struct ldb_dn *ncRoot_dn;
661 uint64_t highest_usn;
662 struct ldb_dn *last_dn;
663 struct drsuapi_DsReplicaLinkedAttribute *la_list;
665 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
669 drsuapi_DsGetNCChanges
671 see MS-DRSR 4.1.10.5.2 for basic logic of this function
673 WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
674 struct drsuapi_DsGetNCChanges *r)
676 struct drsuapi_DsReplicaObjectIdentifier *ncRoot;
679 struct dsdb_schema *schema;
680 struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
681 struct drsuapi_DsReplicaObjectListItemEx **currentObject;
683 DATA_BLOB session_key;
684 const char *attrs[] = { "*", "distinguishedName",
685 "nTSecurityDescriptor",
687 "replPropertyMetaData",
692 "supplementalCredentials",
695 struct dcesrv_handle *h;
696 struct drsuapi_bind_state *b_state;
697 struct drsuapi_getncchanges_state *getnc_state;
698 struct drsuapi_DsGetNCChangesRequest8 *req8;
700 uint32_t max_objects;
701 struct ldb_dn *search_dn = NULL;
703 enum security_user_level security_level;
705 DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
708 *r->out.level_out = 6;
709 /* TODO: linked attributes*/
710 r->out.ctr->ctr6.linked_attributes_count = 0;
711 r->out.ctr->ctr6.linked_attributes = NULL;
713 r->out.ctr->ctr6.object_count = 0;
714 r->out.ctr->ctr6.nc_object_count = 0;
715 r->out.ctr->ctr6.more_data = false;
716 r->out.ctr->ctr6.uptodateness_vector = NULL;
718 /* a RODC doesn't allow for any replication */
719 ret = samdb_rodc(b_state->sam_ctx, &am_rodc);
720 if (ret == LDB_SUCCESS && am_rodc) {
721 DEBUG(0,(__location__ ": DsGetNCChanges attempt on RODC\n"));
722 return WERR_DS_DRA_SOURCE_DISABLED;
725 /* Check request revision.
726 TODO: Adding mappings to req8 from the other levels
728 if (r->in.level != 8) {
729 DEBUG(0,(__location__ ": Request for DsGetNCChanges with unsupported level %u\n",
731 return WERR_REVISION_MISMATCH;
734 req8 = &r->in.req->req8;
736 /* Perform access checks. */
737 /* TODO: we need to support a sync on a specific non-root
738 * DN. We'll need to find the real partition root here */
739 ncRoot = req8->naming_context;
740 if (ncRoot == NULL) {
741 DEBUG(0,(__location__ ": Request for DsGetNCChanges with no NC\n"));
742 return WERR_DS_DRA_INVALID_PARAMETER;
745 if (samdb_ntds_options(b_state->sam_ctx, &options) != LDB_SUCCESS) {
746 return WERR_DS_DRA_INTERNAL_ERROR;
749 if ((options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) &&
750 !(req8->replica_flags & DRSUAPI_DRS_SYNC_FORCED)) {
751 return WERR_DS_DRA_SOURCE_DISABLED;
754 werr = drs_security_level_check(dce_call, "DsGetNCChanges", SECURITY_RO_DOMAIN_CONTROLLER);
755 if (!W_ERROR_IS_OK(werr)) {
759 /* for non-administrator replications, check that they have
760 given the correct source_dsa_invocation_id */
761 security_level = security_session_user_level(dce_call->conn->auth_state.session_info,
762 samdb_domain_sid(b_state->sam_ctx));
763 if (security_level == SECURITY_RO_DOMAIN_CONTROLLER &&
764 (req8->replica_flags & DRSUAPI_DRS_WRIT_REP)) {
765 DEBUG(0,(__location__ ": Attempt to do writeable replication by RODC %s\n",
766 dom_sid_string(mem_ctx,
767 dce_call->conn->auth_state.session_info->security_token->user_sid)));
768 return WERR_DS_DRA_INVALID_PARAMETER;
772 if (req8->replica_flags & DRSUAPI_DRS_FULL_SYNC_PACKET) {
773 /* Ignore the _in_ uptpdateness vector*/
774 req8->uptodateness_vector = NULL;
777 /* we don't yet support extended operations */
778 switch (req8->extended_op) {
779 case DRSUAPI_EXOP_NONE:
782 case DRSUAPI_EXOP_FSMO_RID_ALLOC:
783 werr = getncchanges_rid_alloc(b_state, mem_ctx, req8, &r->out.ctr->ctr6);
784 W_ERROR_NOT_OK_RETURN(werr);
785 search_dn = ldb_get_default_basedn(b_state->sam_ctx);
788 case DRSUAPI_EXOP_FSMO_REQ_ROLE:
789 case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
790 case DRSUAPI_EXOP_FSMO_REQ_PDC:
791 case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
792 case DRSUAPI_EXOP_REPL_OBJ:
793 case DRSUAPI_EXOP_REPL_SECRET:
794 DEBUG(0,(__location__ ": Request for DsGetNCChanges unsupported extended op 0x%x\n",
795 (unsigned)req8->extended_op));
796 return WERR_DS_DRA_NOT_SUPPORTED;
799 getnc_state = b_state->getncchanges_state;
801 /* see if a previous replication has been abandoned */
803 struct ldb_dn *new_dn = ldb_dn_new(getnc_state, b_state->sam_ctx, ncRoot->dn);
804 if (ldb_dn_compare(new_dn, getnc_state->ncRoot_dn) != 0) {
805 DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication on different DN %s %s (last_dn %s)\n",
806 ldb_dn_get_linearized(new_dn),
807 ldb_dn_get_linearized(getnc_state->ncRoot_dn),
808 ldb_dn_get_linearized(getnc_state->last_dn)));
809 talloc_free(getnc_state);
814 if (getnc_state == NULL) {
815 getnc_state = talloc_zero(b_state, struct drsuapi_getncchanges_state);
816 if (getnc_state == NULL) {
819 b_state->getncchanges_state = getnc_state;
820 getnc_state->ncRoot_dn = ldb_dn_new(getnc_state, b_state->sam_ctx, ncRoot->dn);
823 if (!ldb_dn_validate(getnc_state->ncRoot_dn) ||
824 ldb_dn_is_null(getnc_state->ncRoot_dn)) {
825 DEBUG(0,(__location__ ": Bad DN '%s'\n", ncRoot->dn));
826 return WERR_DS_DRA_INVALID_PARAMETER;
829 /* we need the session key for encrypting password attributes */
830 status = dcesrv_inherited_session_key(dce_call->conn, &session_key);
831 if (!NT_STATUS_IS_OK(status)) {
832 DEBUG(0,(__location__ ": Failed to get session key\n"));
833 return WERR_DS_DRA_INTERNAL_ERROR;
837 TODO: MS-DRSR section 4.1.10.1.1
838 Work out if this is the start of a new cycle */
840 if (getnc_state->site_res == NULL) {
842 enum ldb_scope scope = LDB_SCOPE_SUBTREE;
843 const char *extra_filter;
845 extra_filter = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
847 getnc_state->min_usn = req8->highwatermark.highest_usn;
849 /* Construct response. */
850 search_filter = talloc_asprintf(mem_ctx,
851 "(uSNChanged>=%llu)",
852 (unsigned long long)(getnc_state->min_usn+1));
855 search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter);
858 if (req8->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
859 search_filter = talloc_asprintf(mem_ctx,
860 "(&%s(isCriticalSystemObject=TRUE))",
864 if (req8->replica_flags & DRSUAPI_DRS_ASYNC_REP) {
865 scope = LDB_SCOPE_BASE;
869 search_dn = getnc_state->ncRoot_dn;
872 DEBUG(1,(__location__ ": getncchanges on %s using filter %s\n",
873 ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter));
874 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, getnc_state, &getnc_state->site_res,
875 search_dn, scope, attrs,
877 if (ret != LDB_SUCCESS) {
878 return WERR_DS_DRA_INTERNAL_ERROR;
881 if (req8->replica_flags & DRSUAPI_DRS_GET_ANC) {
882 TYPESAFE_QSORT(getnc_state->site_res->msgs,
883 getnc_state->site_res->count,
884 site_res_cmp_parent_order);
886 TYPESAFE_QSORT(getnc_state->site_res->msgs,
887 getnc_state->site_res->count,
888 site_res_cmp_usn_order);
891 getnc_state->uptodateness_vector = talloc_steal(getnc_state, req8->uptodateness_vector);
892 if (getnc_state->uptodateness_vector) {
893 /* make sure its sorted */
894 TYPESAFE_QSORT(getnc_state->uptodateness_vector->cursors,
895 getnc_state->uptodateness_vector->count,
896 drsuapi_DsReplicaCursor_compare);
901 schema = dsdb_get_schema(b_state->sam_ctx, mem_ctx);
903 DEBUG(0,("No schema in sam_ctx\n"));
904 return WERR_DS_DRA_INTERNAL_ERROR;
907 r->out.ctr->ctr6.naming_context = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
908 *r->out.ctr->ctr6.naming_context = *ncRoot;
910 if (dsdb_find_guid_by_dn(b_state->sam_ctx, getnc_state->ncRoot_dn,
911 &r->out.ctr->ctr6.naming_context->guid) != LDB_SUCCESS) {
912 DEBUG(0,(__location__ ": Failed to find GUID of ncRoot_dn %s\n",
913 ldb_dn_get_linearized(getnc_state->ncRoot_dn)));
914 return WERR_DS_DRA_INTERNAL_ERROR;
917 /* find the SID if there is one */
918 dsdb_find_sid_by_dn(b_state->sam_ctx, getnc_state->ncRoot_dn, &r->out.ctr->ctr6.naming_context->sid);
920 dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, &ctr);
921 r->out.ctr->ctr6.mapping_ctr = *ctr;
923 r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(b_state->sam_ctx));
924 r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(b_state->sam_ctx));
926 r->out.ctr->ctr6.old_highwatermark = req8->highwatermark;
927 r->out.ctr->ctr6.new_highwatermark = req8->highwatermark;
929 r->out.ctr->ctr6.first_object = NULL;
930 currentObject = &r->out.ctr->ctr6.first_object;
932 /* use this to force single objects at a time, which is useful
933 * for working out what object is giving problems
935 max_objects = lp_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max object sync", 1000);
936 if (req8->max_object_count < max_objects) {
937 max_objects = req8->max_object_count;
940 for(i=getnc_state->num_sent;
941 i<getnc_state->site_res->count &&
942 (r->out.ctr->ctr6.object_count < max_objects);
945 struct drsuapi_DsReplicaObjectListItemEx *obj;
946 struct ldb_message *msg = getnc_state->site_res->msgs[i];
948 obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
950 werr = get_nc_changes_build_object(obj, msg,
951 b_state->sam_ctx, getnc_state->ncRoot_dn,
952 schema, &session_key, getnc_state->min_usn,
953 req8->replica_flags, getnc_state->uptodateness_vector);
954 if (!W_ERROR_IS_OK(werr)) {
958 werr = get_nc_changes_add_links(b_state->sam_ctx, getnc_state,
959 getnc_state->ncRoot_dn,
960 schema, getnc_state->min_usn,
963 &getnc_state->la_list,
964 &getnc_state->la_count,
965 getnc_state->uptodateness_vector);
966 if (!W_ERROR_IS_OK(werr)) {
970 uSN = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1);
971 if (uSN > r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn) {
972 r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn = uSN;
974 if (uSN > getnc_state->highest_usn) {
975 getnc_state->highest_usn = uSN;
978 if (obj->meta_data_ctr == NULL) {
979 DEBUG(8,(__location__ ": getncchanges skipping send of object %s\n",
980 ldb_dn_get_linearized(msg->dn)));
981 /* no attributes to send */
986 r->out.ctr->ctr6.object_count++;
988 *currentObject = obj;
989 currentObject = &obj->next_object;
991 talloc_free(getnc_state->last_dn);
992 getnc_state->last_dn = ldb_dn_copy(getnc_state, msg->dn);
994 DEBUG(8,(__location__ ": replicating object %s\n", ldb_dn_get_linearized(msg->dn)));
997 getnc_state->num_sent += r->out.ctr->ctr6.object_count;
999 r->out.ctr->ctr6.nc_object_count = getnc_state->site_res->count;
1001 /* the client can us to call UpdateRefs on its behalf to
1002 re-establish monitoring of the NC */
1003 if ((req8->replica_flags & DRSUAPI_DRS_ADD_REF) &&
1004 !GUID_all_zero(&req8->destination_dsa_guid)) {
1005 struct drsuapi_DsReplicaUpdateRefsRequest1 ureq;
1006 ureq.naming_context = ncRoot;
1007 ureq.dest_dsa_dns_name = talloc_asprintf(mem_ctx, "%s._msdcs.%s",
1008 GUID_string(mem_ctx, &req8->destination_dsa_guid),
1009 lp_realm(dce_call->conn->dce_ctx->lp_ctx));
1010 if (!ureq.dest_dsa_dns_name) {
1013 ureq.dest_dsa_guid = req8->destination_dsa_guid;
1014 ureq.options = DRSUAPI_DRS_ADD_REF |
1015 DRSUAPI_DRS_ASYNC_OP |
1016 DRSUAPI_DRS_GETCHG_CHECK;
1017 werr = drsuapi_UpdateRefs(b_state, mem_ctx, &ureq);
1018 if (!W_ERROR_IS_OK(werr)) {
1019 DEBUG(0,(__location__ ": Failed UpdateRefs in DsGetNCChanges - %s\n",
1024 if (i < getnc_state->site_res->count) {
1025 r->out.ctr->ctr6.more_data = true;
1027 r->out.ctr->ctr6.linked_attributes_count = getnc_state->la_count;
1028 r->out.ctr->ctr6.linked_attributes = talloc_steal(mem_ctx, getnc_state->la_list);
1030 LDB_TYPESAFE_QSORT(r->out.ctr->ctr6.linked_attributes, r->out.ctr->ctr6.linked_attributes_count,
1031 b_state->sam_ctx, linked_attribute_compare);
1033 r->out.ctr->ctr6.uptodateness_vector = talloc(mem_ctx, struct drsuapi_DsReplicaCursor2CtrEx);
1034 r->out.ctr->ctr6.new_highwatermark.highest_usn = r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn;
1036 werr = get_nc_changes_udv(b_state->sam_ctx, getnc_state->ncRoot_dn,
1037 r->out.ctr->ctr6.uptodateness_vector);
1038 if (!W_ERROR_IS_OK(werr)) {
1042 talloc_free(getnc_state);
1043 b_state->getncchanges_state = NULL;
1046 if (req8->extended_op != DRSUAPI_EXOP_NONE) {
1047 r->out.ctr->ctr6.uptodateness_vector = NULL;
1048 r->out.ctr->ctr6.nc_object_count = 0;
1049 ZERO_STRUCT(r->out.ctr->ctr6.new_highwatermark);
1052 DEBUG(r->out.ctr->ctr6.more_data?2:1,
1053 ("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %d/%d la=%d)\n",
1054 (unsigned long long)(req8->highwatermark.highest_usn+1),
1055 req8->replica_flags,
1056 ncRoot->dn, r->out.ctr->ctr6.object_count,
1057 i, r->out.ctr->ctr6.more_data?getnc_state->site_res->count:i,
1058 r->out.ctr->ctr6.linked_attributes_count));
1061 if (!r->out.ctr->ctr6.more_data) {
1062 NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsGetNCChanges, NDR_BOTH, r);