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 /* for non-administrator replications, check that they have
755 given the correct source_dsa_invocation_id */
756 security_level = security_session_user_level(dce_call->conn->auth_state.session_info);
758 if (security_level < SECURITY_ADMINISTRATOR) {
759 /* validate their guid */
760 ret = dsdb_validate_invocation_id(b_state->sam_ctx,
761 &req8->source_dsa_invocation_id,
762 dce_call->conn->auth_state.session_info->security_token->user_sid);
763 if (ret != LDB_SUCCESS) {
764 DEBUG(0,(__location__ ": Attempted replication with invalid invocationId %s\n",
765 GUID_string(mem_ctx, &req8->source_dsa_invocation_id)));
766 return WERR_DS_DRA_INVALID_PARAMETER;
770 if (security_level < SECURITY_ADMINISTRATOR &&
771 (req8->replica_flags & DRSUAPI_DRS_WRIT_REP)) {
773 ret = samdb_is_rodc(b_state->sam_ctx, &req8->source_dsa_invocation_id, &is_rodc);
774 if (ret != LDB_SUCCESS || is_rodc) {
775 DEBUG(0,(__location__ ": Attempt to do writeable replication by RODC %s\n",
776 GUID_string(mem_ctx, &req8->source_dsa_invocation_id)));
777 return WERR_DS_DRA_INVALID_PARAMETER;
782 if (req8->replica_flags & DRSUAPI_DRS_FULL_SYNC_PACKET) {
783 /* Ignore the _in_ uptpdateness vector*/
784 req8->uptodateness_vector = NULL;
787 werr = drs_security_level_check(dce_call, "DsGetNCChanges");
788 if (!W_ERROR_IS_OK(werr)) {
792 /* we don't yet support extended operations */
793 switch (req8->extended_op) {
794 case DRSUAPI_EXOP_NONE:
797 case DRSUAPI_EXOP_FSMO_RID_ALLOC:
798 werr = getncchanges_rid_alloc(b_state, mem_ctx, req8, &r->out.ctr->ctr6);
799 W_ERROR_NOT_OK_RETURN(werr);
800 search_dn = ldb_get_default_basedn(b_state->sam_ctx);
803 case DRSUAPI_EXOP_FSMO_REQ_ROLE:
804 case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
805 case DRSUAPI_EXOP_FSMO_REQ_PDC:
806 case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
807 case DRSUAPI_EXOP_REPL_OBJ:
808 case DRSUAPI_EXOP_REPL_SECRET:
809 DEBUG(0,(__location__ ": Request for DsGetNCChanges unsupported extended op 0x%x\n",
810 (unsigned)req8->extended_op));
811 return WERR_DS_DRA_NOT_SUPPORTED;
814 getnc_state = b_state->getncchanges_state;
816 /* see if a previous replication has been abandoned */
818 struct ldb_dn *new_dn = ldb_dn_new(getnc_state, b_state->sam_ctx, ncRoot->dn);
819 if (ldb_dn_compare(new_dn, getnc_state->ncRoot_dn) != 0) {
820 DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication on different DN %s %s (last_dn %s)\n",
821 ldb_dn_get_linearized(new_dn),
822 ldb_dn_get_linearized(getnc_state->ncRoot_dn),
823 ldb_dn_get_linearized(getnc_state->last_dn)));
824 talloc_free(getnc_state);
829 if (getnc_state == NULL) {
830 getnc_state = talloc_zero(b_state, struct drsuapi_getncchanges_state);
831 if (getnc_state == NULL) {
834 b_state->getncchanges_state = getnc_state;
835 getnc_state->ncRoot_dn = ldb_dn_new(getnc_state, b_state->sam_ctx, ncRoot->dn);
838 if (!ldb_dn_validate(getnc_state->ncRoot_dn) ||
839 ldb_dn_is_null(getnc_state->ncRoot_dn)) {
840 DEBUG(0,(__location__ ": Bad DN '%s'\n", ncRoot->dn));
841 return WERR_DS_DRA_INVALID_PARAMETER;
844 /* we need the session key for encrypting password attributes */
845 status = dcesrv_inherited_session_key(dce_call->conn, &session_key);
846 if (!NT_STATUS_IS_OK(status)) {
847 DEBUG(0,(__location__ ": Failed to get session key\n"));
848 return WERR_DS_DRA_INTERNAL_ERROR;
852 TODO: MS-DRSR section 4.1.10.1.1
853 Work out if this is the start of a new cycle */
855 if (getnc_state->site_res == NULL) {
857 enum ldb_scope scope = LDB_SCOPE_SUBTREE;
858 const char *extra_filter;
860 extra_filter = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
862 getnc_state->min_usn = req8->highwatermark.highest_usn;
864 /* Construct response. */
865 search_filter = talloc_asprintf(mem_ctx,
866 "(uSNChanged>=%llu)",
867 (unsigned long long)(getnc_state->min_usn+1));
870 search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter);
873 if (req8->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
874 search_filter = talloc_asprintf(mem_ctx,
875 "(&%s(isCriticalSystemObject=TRUE))",
879 if (req8->replica_flags & DRSUAPI_DRS_ASYNC_REP) {
880 scope = LDB_SCOPE_BASE;
884 search_dn = getnc_state->ncRoot_dn;
887 DEBUG(1,(__location__ ": getncchanges on %s using filter %s\n",
888 ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter));
889 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, getnc_state, &getnc_state->site_res,
890 search_dn, scope, attrs,
892 if (ret != LDB_SUCCESS) {
893 return WERR_DS_DRA_INTERNAL_ERROR;
896 if (req8->replica_flags & DRSUAPI_DRS_GET_ANC) {
897 TYPESAFE_QSORT(getnc_state->site_res->msgs,
898 getnc_state->site_res->count,
899 site_res_cmp_parent_order);
901 TYPESAFE_QSORT(getnc_state->site_res->msgs,
902 getnc_state->site_res->count,
903 site_res_cmp_usn_order);
906 getnc_state->uptodateness_vector = talloc_steal(getnc_state, req8->uptodateness_vector);
907 if (getnc_state->uptodateness_vector) {
908 /* make sure its sorted */
909 TYPESAFE_QSORT(getnc_state->uptodateness_vector->cursors,
910 getnc_state->uptodateness_vector->count,
911 drsuapi_DsReplicaCursor_compare);
916 schema = dsdb_get_schema(b_state->sam_ctx, mem_ctx);
918 DEBUG(0,("No schema in sam_ctx\n"));
919 return WERR_DS_DRA_INTERNAL_ERROR;
922 r->out.ctr->ctr6.naming_context = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
923 *r->out.ctr->ctr6.naming_context = *ncRoot;
925 if (dsdb_find_guid_by_dn(b_state->sam_ctx, getnc_state->ncRoot_dn,
926 &r->out.ctr->ctr6.naming_context->guid) != LDB_SUCCESS) {
927 DEBUG(0,(__location__ ": Failed to find GUID of ncRoot_dn %s\n",
928 ldb_dn_get_linearized(getnc_state->ncRoot_dn)));
929 return WERR_DS_DRA_INTERNAL_ERROR;
932 /* find the SID if there is one */
933 dsdb_find_sid_by_dn(b_state->sam_ctx, getnc_state->ncRoot_dn, &r->out.ctr->ctr6.naming_context->sid);
935 dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, &ctr);
936 r->out.ctr->ctr6.mapping_ctr = *ctr;
938 r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(b_state->sam_ctx));
939 r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(b_state->sam_ctx));
941 r->out.ctr->ctr6.old_highwatermark = req8->highwatermark;
942 r->out.ctr->ctr6.new_highwatermark = req8->highwatermark;
944 r->out.ctr->ctr6.first_object = NULL;
945 currentObject = &r->out.ctr->ctr6.first_object;
947 /* use this to force single objects at a time, which is useful
948 * for working out what object is giving problems
950 max_objects = lp_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max object sync", 1000);
951 if (req8->max_object_count < max_objects) {
952 max_objects = req8->max_object_count;
955 for(i=getnc_state->num_sent;
956 i<getnc_state->site_res->count &&
957 (r->out.ctr->ctr6.object_count < max_objects);
960 struct drsuapi_DsReplicaObjectListItemEx *obj;
961 struct ldb_message *msg = getnc_state->site_res->msgs[i];
963 obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
965 werr = get_nc_changes_build_object(obj, msg,
966 b_state->sam_ctx, getnc_state->ncRoot_dn,
967 schema, &session_key, getnc_state->min_usn,
968 req8->replica_flags, getnc_state->uptodateness_vector);
969 if (!W_ERROR_IS_OK(werr)) {
973 werr = get_nc_changes_add_links(b_state->sam_ctx, getnc_state,
974 getnc_state->ncRoot_dn,
975 schema, getnc_state->min_usn,
978 &getnc_state->la_list,
979 &getnc_state->la_count,
980 getnc_state->uptodateness_vector);
981 if (!W_ERROR_IS_OK(werr)) {
985 uSN = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1);
986 if (uSN > r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn) {
987 r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn = uSN;
989 if (uSN > getnc_state->highest_usn) {
990 getnc_state->highest_usn = uSN;
993 if (obj->meta_data_ctr == NULL) {
994 DEBUG(8,(__location__ ": getncchanges skipping send of object %s\n",
995 ldb_dn_get_linearized(msg->dn)));
996 /* no attributes to send */
1001 r->out.ctr->ctr6.object_count++;
1003 *currentObject = obj;
1004 currentObject = &obj->next_object;
1006 talloc_free(getnc_state->last_dn);
1007 getnc_state->last_dn = ldb_dn_copy(getnc_state, msg->dn);
1009 DEBUG(8,(__location__ ": replicating object %s\n", ldb_dn_get_linearized(msg->dn)));
1012 getnc_state->num_sent += r->out.ctr->ctr6.object_count;
1014 r->out.ctr->ctr6.nc_object_count = getnc_state->site_res->count;
1016 /* the client can us to call UpdateRefs on its behalf to
1017 re-establish monitoring of the NC */
1018 if ((req8->replica_flags & DRSUAPI_DRS_ADD_REF) &&
1019 !GUID_all_zero(&req8->destination_dsa_guid)) {
1020 struct drsuapi_DsReplicaUpdateRefsRequest1 ureq;
1021 ureq.naming_context = ncRoot;
1022 ureq.dest_dsa_dns_name = talloc_asprintf(mem_ctx, "%s._msdcs.%s",
1023 GUID_string(mem_ctx, &req8->destination_dsa_guid),
1024 lp_realm(dce_call->conn->dce_ctx->lp_ctx));
1025 if (!ureq.dest_dsa_dns_name) {
1028 ureq.dest_dsa_guid = req8->destination_dsa_guid;
1029 ureq.options = DRSUAPI_DRS_ADD_REF |
1030 DRSUAPI_DRS_ASYNC_OP |
1031 DRSUAPI_DRS_GETCHG_CHECK;
1032 werr = drsuapi_UpdateRefs(b_state, mem_ctx, &ureq);
1033 if (!W_ERROR_IS_OK(werr)) {
1034 DEBUG(0,(__location__ ": Failed UpdateRefs in DsGetNCChanges - %s\n",
1039 if (i < getnc_state->site_res->count) {
1040 r->out.ctr->ctr6.more_data = true;
1042 r->out.ctr->ctr6.linked_attributes_count = getnc_state->la_count;
1043 r->out.ctr->ctr6.linked_attributes = talloc_steal(mem_ctx, getnc_state->la_list);
1045 LDB_TYPESAFE_QSORT(r->out.ctr->ctr6.linked_attributes, r->out.ctr->ctr6.linked_attributes_count,
1046 b_state->sam_ctx, linked_attribute_compare);
1048 r->out.ctr->ctr6.uptodateness_vector = talloc(mem_ctx, struct drsuapi_DsReplicaCursor2CtrEx);
1049 r->out.ctr->ctr6.new_highwatermark.highest_usn = r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn;
1051 werr = get_nc_changes_udv(b_state->sam_ctx, getnc_state->ncRoot_dn,
1052 r->out.ctr->ctr6.uptodateness_vector);
1053 if (!W_ERROR_IS_OK(werr)) {
1057 talloc_free(getnc_state);
1058 b_state->getncchanges_state = NULL;
1061 if (req8->extended_op != DRSUAPI_EXOP_NONE) {
1062 r->out.ctr->ctr6.uptodateness_vector = NULL;
1063 r->out.ctr->ctr6.nc_object_count = 0;
1064 ZERO_STRUCT(r->out.ctr->ctr6.new_highwatermark);
1067 DEBUG(r->out.ctr->ctr6.more_data?2:1,
1068 ("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %d/%d la=%d)\n",
1069 (unsigned long long)(req8->highwatermark.highest_usn+1),
1070 req8->replica_flags,
1071 ncRoot->dn, r->out.ctr->ctr6.object_count,
1072 i, r->out.ctr->ctr6.more_data?getnc_state->site_res->count:i,
1073 r->out.ctr->ctr6.linked_attributes_count));
1076 if (!r->out.ctr->ctr6.more_data) {
1077 NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsGetNCChanges, NDR_BOTH, r);