4 Copyright (C) Matthieu Patou <mat@matws.net> 2011
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "ldb/include/ldb.h"
23 #include "ldb/include/ldb_errors.h"
24 #include "ldb/include/ldb_module.h"
25 #include "libcli/security/security.h"
26 #include "librpc/gen_ndr/drsblobs.h"
27 #include "librpc/gen_ndr/ndr_drsblobs.h"
28 #include "librpc/ndr/libndr.h"
29 #include "dsdb/samdb/samdb.h"
30 #include "dsdb/samdb/ldb_modules/util.h"
31 #include "lib/util/smb_strtox.h"
33 #define LDAP_DIRSYNC_OBJECT_SECURITY 0x01
34 #define LDAP_DIRSYNC_ANCESTORS_FIRST_ORDER 0x800
35 #define LDAP_DIRSYNC_PUBLIC_DATA_ONLY 0x2000
36 #define LDAP_DIRSYNC_INCREMENTAL_VALUES 0x80000000
39 struct dirsync_context {
40 struct ldb_module *module;
41 struct ldb_request *req;
44 * We keep a track of the number of attributes that we
45 * add just for the need of the implementation
46 * it will be usefull to track then entries that needs not to
47 * be returned because there is no real change
50 unsigned int nbDefaultAttrs;
61 const struct GUID *our_invocation_id;
62 const struct dsdb_schema *schema;
63 struct ldb_dn *nc_root;
64 struct drsuapi_DsReplicaCursor *cursors;
68 static int dirsync_filter_entry(struct ldb_request *req,
69 struct ldb_message *msg,
70 struct ldb_control **controls,
71 struct dirsync_context *dsc,
74 struct ldb_context *ldb;
76 enum ndr_err_code ndr_err;
80 struct ldb_val *replMetaData = NULL;
81 struct replPropertyMetaDataBlob rmd;
82 const struct dsdb_attribute *attr;
83 const char **listAttr = NULL;
84 bool namereturned = false;
85 bool nameasked = false;
87 /* Ajustment for the added attributes, it will reduce the number of
88 * expected to be here attributes*/
89 unsigned int delta = 0;
90 const char **myaccept = NULL;
91 const char *emptyaccept[] = { NULL };
92 const char *extendedaccept[] = { "GUID", "SID", "WKGUID", NULL };
93 const char *rdn = NULL;
94 struct ldb_message_element *el;
95 struct ldb_message *newmsg;
98 * Where we asked to do extended dn ?
99 * if so filter out everything bug GUID, SID, WKGUID,
100 * if not filter out everything (just keep the dn).
102 if ( dsc->noextended == true ) {
103 myaccept = emptyaccept;
105 myaccept = extendedaccept;
107 ldb = ldb_module_get_ctx(dsc->module);
109 if (msg->num_elements == 0) {
111 * Entry that we don't really have access to
115 ldb_dn_extended_filter(msg->dn, myaccept);
118 * If the RDN starts with CN then the CN attribute is never returned
120 rdn = ldb_dn_get_rdn_name(msg->dn);
123 * if objectGUID is asked and we are dealing for the referrals entries and
124 * the usn searched is 0 then we didn't count the objectGUID as an automatically
125 * returned attribute, do to so we increament delta.
127 if (referral == true &&
128 ldb_attr_in_list(req->op.search.attrs, "objectGUID") &&
129 dsc->fromreqUSN == 0) {
135 * In terms of big O notation this is not the best algorithm,
136 * but we try our best not to make the worse one.
137 * We are obliged to run through the n message's elements
138 * and through the p elements of the replPropertyMetaData.
140 * It turns out that we are crawling twice the message's elements
141 * the first crawl is to remove the non replicated and generated
142 * attributes. The second one is to remove attributes that haven't
143 * a USN > as the requested one.
145 * In the second crawl we are reading the list of elements in the
146 * replPropertyMetaData for each remaining replicated attribute.
147 * In order to keep the list small
149 * We have a O(n'*p') complexity, in worse case n' = n and p' = p
150 * but in most case n' = n/2 (at least half of returned attributes
151 * are not replicated or generated) and p' is small as we
152 * list only the attribute that have been modified since last interogation
155 newmsg = ldb_msg_new(dsc->req);
156 if (newmsg == NULL) {
159 for (i = msg->num_elements - 1; i >= 0; i--) {
160 if (ldb_attr_cmp(msg->elements[i].name, "uSNChanged") == 0) {
162 /* Read the USN it will used at the end of the filtering
163 * to update the max USN in the cookie if we
164 * decide to keep this entry
167 (const char*)msg->elements[i].values[0].data,
173 ldb_set_errstring(ldb,
174 "Failed to convert USN");
175 return ldb_module_done(dsc->req,
178 LDB_ERR_OPERATIONS_ERROR);
183 if (ldb_attr_cmp(msg->elements[i].name,
184 "replPropertyMetaData") == 0) {
185 replMetaData = (talloc_steal(dsc, &msg->elements[i].values[0]));
190 if (replMetaData == NULL) {
191 bool guidfound = false;
194 * We are in the case of deleted object where we don't have the
197 if (!ldb_msg_find_attr_as_uint(msg, "isDeleted", 0)) {
199 * This is not a deleted item and we don't
200 * have the replPropertyMetaData.
205 newmsg->dn = ldb_dn_new(newmsg, ldb, "");
206 if (newmsg->dn == NULL) {
210 el = ldb_msg_find_element(msg, "objectGUID");
215 * We expect to find the GUID in the object,
216 * if it turns out not to be the case sometime
217 * well will uncomment the code bellow
219 SMB_ASSERT(guidfound == true);
221 if (guidfound == false) {
223 struct ldb_val *new_val;
227 txt = strrchr(txt, ':');
229 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
233 status = GUID_from_string(txt, &guid);
234 if (!NT_STATUS_IS_OK(status)) {
235 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
238 status = GUID_to_ndr_blob(&guid, msg, &guid_blob);
239 if (!NT_STATUS_IS_OK(status)) {
240 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
243 new_val = talloc(msg, struct ldb_val);
244 if (new_val == NULL) {
247 new_val->data = talloc_steal(new_val, guid_blob.data);
248 new_val->length = guid_blob.length;
249 if (ldb_msg_add_value(msg, "objectGUID", new_val, NULL) != 0) {
250 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
254 ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD);
255 talloc_steal(newmsg->elements, el->name);
256 talloc_steal(newmsg->elements, el->values);
258 talloc_steal(newmsg->elements, msg);
259 return ldb_module_send_entry(dsc->req, msg, controls);
262 ndr_err = ndr_pull_struct_blob(replMetaData, dsc, &rmd,
263 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
264 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
265 ldb_set_errstring(ldb, "Unable to unmarshall replPropertyMetaData");
266 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
268 if (ldb_attr_in_list(req->op.search.attrs, "name") ||
269 ldb_attr_in_list(req->op.search.attrs, "*")) {
274 * If we don't have an USN and no updateness array then we skip the
275 * test phase this is an optimisation for the case when you
276 * first query the DC without a cookie.
277 * As this query is most probably the one
278 * that will return the biggest answer, skipping this part
279 * will really save time.
281 if (ldb_dn_compare(dsc->nc_root, msg->dn) == 0) {
282 /* If we have name then we expect to have parentGUID,
283 * it will not be the case for the root of the NC
288 if (dsc->fromreqUSN > 0 || dsc->cursors != NULL) {
291 * Allocate an array of size(replMetaData) of char*
292 * we know that it will be oversized but it's a short lived element
294 listAttr = talloc_array(msg, const char*, rmd.ctr.ctr1.count + 1);
295 if (listAttr == NULL) {
298 for (n=0; n < rmd.ctr.ctr1.count; n++) {
299 struct replPropertyMetaData1 *omd = &rmd.ctr.ctr1.array[n];
300 if (omd->local_usn > dsc->fromreqUSN) {
301 const struct dsdb_attribute *a = dsdb_attribute_by_attributeID_id(dsc->schema,
303 if (!dsc->localonly) {
304 struct drsuapi_DsReplicaCursor *tab = dsc->cursors;
306 for (l=0; l < dsc->cursor_size; l++) {
307 if (GUID_equal(&tab[l].source_dsa_invocation_id, &omd->originating_invocation_id) &&
308 tab[l].highest_usn >= omd->originating_usn) {
310 * If we have in the uptodateness vector an entry
311 * with the same invocation id as the originating invocation
312 * and if the usn in the vector is greater or equal to
313 * the one in originating_usn, then it means that this entry
314 * has already been sent (from another DC) to the client
315 * no need to resend it one more time.
320 /* If we are here it's because we have a usn > (max(usn of vectors))*/
322 if (namereturned == false &&
324 ldb_attr_cmp(a->lDAPDisplayName, "name") == 0) {
326 if (ldb_dn_compare(dsc->nc_root, msg->dn) == 0) {
330 listAttr[j] = a->lDAPDisplayName;
339 if (ldb_attr_in_list(req->op.search.attrs, "*") ||
340 ldb_attr_in_list(req->op.search.attrs, "name")) {
347 * Let's loop around the remaining elements
348 * to see which one are in the listAttr.
349 * If they are in this array it means that
350 * their localusn > usn from the request (in the cookie)
351 * if not we remove the attribute.
353 for (i = msg->num_elements - 1; i >= 0; i--) {
354 const char *ldapattrname;
356 el = &(msg->elements[i]);
357 ldapattrname = el->name;
359 attr = dsdb_attribute_by_lDAPDisplayName(dsc->schema,
367 if (attr->linkID & 1) {
369 * Attribute is a backlink so let's remove it
374 if (ldb_attr_cmp(msg->elements[i].name,
375 "replPropertyMetaData") == 0) {
379 if ((attr->systemFlags & (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED))) {
380 if (ldb_attr_cmp(attr->lDAPDisplayName, "objectGUID") != 0 &&
381 ldb_attr_cmp(attr->lDAPDisplayName, "parentGUID") != 0) {
383 * Attribute is constructed or not replicated, let's get rid of it
387 /* Let's keep the attribute that we forced to be added
388 * even if they are not in the replicationMetaData
389 * or are just generated
391 if (namereturned == false &&
392 (ldb_attr_cmp(attr->lDAPDisplayName, "parentGUID") == 0)) {
396 if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
397 return ldb_error(ldb,
398 LDB_ERR_OPERATIONS_ERROR,
399 "Unable to add attribute");
401 talloc_steal(newmsg->elements, el->name);
402 talloc_steal(newmsg->elements, el->values);
407 if (ldb_attr_cmp(msg->elements[i].name, rdn) == 0) {
409 * We have an attribute that is the same as the start of the RDN
410 * (ie. attribute CN with rdn CN=).
415 if (ldb_attr_cmp(attr->lDAPDisplayName, "instanceType") == 0) {
416 if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
417 return ldb_error(ldb,
418 LDB_ERR_OPERATIONS_ERROR,
419 "Unable to add attribute");
421 talloc_steal(newmsg->elements, el->name);
422 talloc_steal(newmsg->elements, el->values);
425 /* For links, when our functional level > windows 2000
426 * we use the RMD_LOCAL_USN information to decide whether
427 * we return the attribute or not.
428 * For windows 2000 this information is in the replPropertyMetaData
429 * so it will be handled like any other replicated attribute
432 if (dsc->functional_level > DS_DOMAIN_FUNCTION_2000 &&
433 attr->linkID != 0 ) {
436 * Elements for incremental changes on linked attributes
438 struct ldb_message_element *el_incr_add = NULL;
439 struct ldb_message_element *el_incr_del = NULL;
441 * Attribute is a forwardlink so let's remove it
444 for (k = el->num_values -1; k >= 0; k--) {
447 uint32_t tmp_usn = 0;
448 uint32_t tmp_usn2 = 0;
449 struct GUID invocation_id = GUID_zero();
450 struct dsdb_dn *dn = dsdb_dn_parse(msg, ldb, &el->values[k], attr->syntax->ldap_oid);
451 struct ldb_dn *copydn;
453 ldb_set_errstring(ldb, "Cannot parse DN");
454 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
457 copydn = ldb_dn_copy(msg, dn->dn);
458 if (copydn == NULL) {
462 status = dsdb_get_extended_dn_uint32(dn->dn, &tmp_usn, "RMD_LOCAL_USN");
463 if (!NT_STATUS_IS_OK(status)) {
465 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
467 status = dsdb_get_extended_dn_guid(dn->dn, &invocation_id, "RMD_INVOCID");
468 if (!NT_STATUS_IS_OK(status)) {
470 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
473 status = dsdb_get_extended_dn_uint32(dn->dn, &flags, "RMD_FLAGS");
474 if (!NT_STATUS_IS_OK(status)) {
476 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
479 status = dsdb_get_extended_dn_uint32(dn->dn, &tmp_usn2, "RMD_ORIGINATING_USN");
480 if (!NT_STATUS_IS_OK(status)) {
482 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
485 ldb_dn_extended_filter(dn->dn, myaccept);
486 dn_ln = dsdb_dn_get_extended_linearized(dn, dn,
491 ldb_set_errstring(ldb, "Cannot linearize dn");
492 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
495 talloc_free(el->values[k].data);
496 el->values[k].data = (uint8_t*)talloc_steal(el->values, dn_ln);
497 if (el->values[k].data == NULL) {
499 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
501 el->values[k].length = strlen(dn_ln);
504 if (tmp_usn > dsc->fromreqUSN) {
505 if (!dsc->localonly) {
506 struct drsuapi_DsReplicaCursor *tab = dsc->cursors;
509 for (l=0; l < dsc->cursor_size; l++) {
510 if (GUID_equal(&tab[l].source_dsa_invocation_id, &invocation_id) &&
511 tab[l].highest_usn >= tmp_usn2) {
513 * If we have in the uptodateness vector an entry
514 * with the same invocation id as the originating invocation
515 * and if the usn in the vector is greater or equal to
516 * the one in originating_usn, then it means that this entry
517 * has already been sent (from another DC) to the client
518 * no need to resend it one more time.
523 /* If we are here it's because we have a usn > (max(usn of vectors))*/
528 /* If we are here it's because the link is more recent than either any
529 * originating usn or local usn
532 if (dsc->linkIncrVal == true) {
533 struct ldb_message_element *tmpel;
534 if (flags & DSDB_RMD_FLAG_DELETED) {
535 /* We have to check that the inactive link still point to an existing object */
540 status = dsdb_get_extended_dn_guid(copydn, &guid, "GUID");
541 if (!NT_STATUS_IS_OK(status)) {
542 DEBUG(0,(__location__ " Unable to extract GUID in linked attribute '%s' in '%s'\n",
543 el->name, ldb_dn_get_linearized(copydn)));
544 return ldb_operr(ldb);
546 ret = dsdb_module_dn_by_guid(dsc->module, newmsg, &guid, &tdn, req);
547 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
548 DEBUG(2, (" Search of guid %s returned 0 objects, skipping it !\n",
549 GUID_string(newmsg, &guid)));
551 } else if (ret != LDB_SUCCESS) {
552 DEBUG(0, (__location__ " Search of guid %s failed with error code %d\n",
553 GUID_string(newmsg, &guid),
563 tmpel = talloc_zero(newmsg, struct ldb_message_element);
567 tmpel->values = talloc_array(tmpel, struct ldb_val, 1);
568 if (tmpel->values == NULL) {
571 if (flags & DSDB_RMD_FLAG_DELETED) {
572 tmpel->name = talloc_asprintf(tmpel,
577 tmpel->name = talloc_asprintf(tmpel,
581 if (tmpel->name == NULL) {
584 tmpel->num_values = 1;
586 tmpel->num_values += 1;
587 tmpel->values = talloc_realloc(tmpel,
591 if (tmpel->values == NULL) {
595 tmpel->values[tmpel->num_values -1].data =talloc_steal(tmpel->values, el->values[k].data);
596 tmpel->values[tmpel->num_values -1].length = el->values[k].length;
598 if (flags & DSDB_RMD_FLAG_DELETED) {
606 if (dsc->linkIncrVal == false) {
607 if (flags & DSDB_RMD_FLAG_DELETED) {
620 if (dsc->linkIncrVal == false) {
621 if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
622 return ldb_error(ldb,
623 LDB_ERR_OPERATIONS_ERROR,
624 "Unable to add attribute");
626 talloc_steal(newmsg->elements, el->name);
627 talloc_steal(newmsg->elements, el->values);
630 if (ldb_msg_add(newmsg, el_incr_del, LDB_FLAG_MOD_ADD))
631 return ldb_error(ldb,
632 LDB_ERR_OPERATIONS_ERROR,
633 "Unable to add attribute");
636 if (ldb_msg_add(newmsg, el_incr_add, LDB_FLAG_MOD_ADD))
637 return ldb_error(ldb,
638 LDB_ERR_OPERATIONS_ERROR,
639 "Unable to add attribute");
647 for (j=0; j<size; j++) {
649 * We mark attribute that has already been seen well
650 * as seen. So that after attribute that are still in
651 * listAttr are attributes that has been modified after
652 * the requested USN but not present in the attributes
653 * returned by the ldb search.
654 * That is to say attributes that have been removed
656 if (listAttr[j] && ldb_attr_cmp(listAttr[j], ldapattrname) == 0) {
667 if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
668 return ldb_error(ldb,
669 LDB_ERR_OPERATIONS_ERROR,
670 "Unable to add attribute");
672 talloc_steal(newmsg->elements, el->name);
673 talloc_steal(newmsg->elements, el->values);
677 talloc_steal(newmsg->elements, msg);
680 * Here we run through the list of attributes returned
681 * in the propertyMetaData.
682 * Entries of this list have usn > requested_usn,
683 * entries that are also present in the message have been
684 * replaced by NULL, so at this moment the list contains
685 * only elements that have a usn > requested_usn and that
686 * haven't been seen. It's attributes that were removed.
687 * We add them to the message like empty elements.
689 for (j=0; j<size; j++) {
691 ldb_attr_in_list(req->op.search.attrs, "*") ||
692 ldb_attr_in_list(req->op.search.attrs, listAttr[j])) &&
693 (ldb_attr_cmp(listAttr[j], rdn) != 0) &&
694 (ldb_attr_cmp(listAttr[j], "instanceType") != 0)) {
695 ldb_msg_add_empty(newmsg, listAttr[j], LDB_FLAG_MOD_DELETE, NULL);
698 talloc_free(listAttr);
700 if ((newmsg->num_elements - ( dsc->nbDefaultAttrs - delta)) > 0) {
702 * After cleaning attributes there is still some attributes that were not added just
703 * for the purpose of the control (objectGUID, instanceType, ...)
706 newmsg->dn = talloc_steal(newmsg, msg->dn);
707 if (val > dsc->highestUSN) {
708 dsc->highestUSN = val;
710 return ldb_module_send_entry(dsc->req, newmsg, controls);
718 static int dirsync_create_vector(struct ldb_request *req,
719 struct ldb_reply *ares,
720 struct dirsync_context *dsc,
721 struct ldapControlDirSyncCookie *cookie,
722 struct ldb_context *ldb)
724 struct ldb_result *resVector;
725 const char* attrVector[] = {"replUpToDateVector", NULL };
726 uint64_t highest_usn;
729 struct drsuapi_DsReplicaCursor *tab;
731 ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ, &highest_usn);
732 if (ret != LDB_SUCCESS) {
733 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, "Unable to get highest USN from current NC");
736 /* If we have a full answer then the highest USN
737 * is not the highest USN from the result set but the
738 * highest of the naming context, unless the sequence is not updated yet.
740 if (highest_usn > dsc->highestUSN) {
741 dsc->highestUSN = highest_usn;
745 ret = dsdb_module_search_dn(dsc->module, dsc, &resVector,
748 DSDB_FLAG_NEXT_MODULE, req);
749 if (ret != LDB_SUCCESS) {
750 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
751 "Unable to get replUpToDateVector for current NC");
754 if (resVector->count != 0) {
757 struct ldb_message_element *el = ldb_msg_find_element(resVector->msgs[0], "replUpToDateVector");
759 enum ndr_err_code ndr_err;
760 struct replUpToDateVectorBlob utd;
761 blob.data = el->values[0].data;
762 blob.length = el->values[0].length;
763 ndr_err = ndr_pull_struct_blob(&blob, dsc, &utd,
764 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
766 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
767 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
768 "Unable to pull replUpToDateVectorBlob structure");
772 count += utd.ctr.ctr2.count;
773 tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
777 for (i=1; i < count; i++) {
778 memset(&tab[i], 0, sizeof(struct drsuapi_DsReplicaCursor));
779 tab[i].highest_usn = utd.ctr.ctr2.cursors[i-1].highest_usn;
780 tab[i].source_dsa_invocation_id = utd.ctr.ctr2.cursors[i-1].source_dsa_invocation_id;
783 tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
790 * No replUpToDateVector ? it happens quite often (1 DC,
791 * other DCs didn't update ...
793 tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
798 /* Our vector is always the first */
799 tab[0].highest_usn = dsc->highestUSN;
800 tab[0].source_dsa_invocation_id = *(dsc->our_invocation_id);
803 /* We have to add the updateness vector that we have*/
804 /* Version is always 1 in dirsync cookies */
805 cookie->blob.extra.uptodateness_vector.version = 1;
806 cookie->blob.extra.uptodateness_vector.reserved = 0;
807 cookie->blob.extra.uptodateness_vector.ctr.ctr1.count = count;
808 cookie->blob.extra.uptodateness_vector.ctr.ctr1.reserved = 0;
809 cookie->blob.extra.uptodateness_vector.ctr.ctr1.cursors = tab;
814 static int dirsync_search_callback(struct ldb_request *req, struct ldb_reply *ares)
817 struct dirsync_context *dsc;
818 struct ldb_result *res, *res2;
819 struct ldb_dirsync_control *control;
820 struct ldapControlDirSyncCookie *cookie;
821 struct ldb_context *ldb;
826 const char *attrs[] = { "objectGUID", NULL };
827 enum ndr_err_code ndr_err;
831 dsc = talloc_get_type_abort(req->context, struct dirsync_context);
832 ldb = ldb_module_get_ctx(dsc->module);
834 return ldb_module_done(dsc->req, NULL, NULL,
835 LDB_ERR_OPERATIONS_ERROR);
837 if (ares->error != LDB_SUCCESS) {
838 return ldb_module_done(dsc->req, ares->controls,
839 ares->response, ares->error);
842 switch (ares->type) {
843 case LDB_REPLY_ENTRY:
844 return dirsync_filter_entry(req, ares->message, ares->controls, dsc, false);
846 case LDB_REPLY_REFERRAL:
847 /* Skip the ldap(s):// so up to 8 chars,
848 * we don't care to be precise as the goal is to be in
849 * the name of DC, then we search the next '/'
850 * as it will be the last char before the DN of the referal
852 if (strncmp(ares->referral, "ldap://", 7) == 0) {
853 tmp = ares->referral + 7;
854 } else if (strncmp(ares->referral, "ldaps://", 8) == 0) {
855 tmp = ares->referral + 8;
857 return ldb_operr(ldb);
860 tmp = strchr(tmp, '/');
862 return ldb_operr(ldb);
866 dn = ldb_dn_new(dsc, ldb, tmp);
871 flags = DSDB_FLAG_NEXT_MODULE |
872 DSDB_SEARCH_SHOW_DELETED |
873 DSDB_SEARCH_SHOW_EXTENDED_DN;
876 flags = flags | DSDB_FLAG_AS_SYSTEM;
879 ret = dsdb_module_search_tree(dsc->module, dsc, &res,
882 req->op.search.attrs,
885 if (ret != LDB_SUCCESS) {
890 if (res->count > 1) {
891 char *ldbmsg = talloc_asprintf(dn, "LDB returned more than result for dn: %s", tmp);
893 ldb_set_errstring(ldb, ldbmsg);
896 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
897 } else if (res->count == 0) {
898 /* if nothing is returned then it means that we don't
906 * Fetch the objectGUID of the root of current NC
908 ret = dsdb_module_search_dn(dsc->module, dsc, &res2,
911 DSDB_FLAG_NEXT_MODULE, req);
913 if (ret != LDB_SUCCESS) {
916 if (res2->msgs[0]->num_elements != 1) {
917 ldb_set_errstring(ldb,
918 "More than 1 attribute returned while looking for objectGUID");
919 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
922 val = res2->msgs[0]->elements[0].values;
923 ret = ldb_msg_add_value(res->msgs[0], "parentGUID", val, NULL);
925 * It *very* important to steal otherwise as val is in a subcontext
926 * related to res2, when the value will be one more time stolen
927 * it's elements[x].values that will be stolen, so it's important to
928 * recreate the context hierrachy as if it was done from a ldb_request
930 talloc_steal(res->msgs[0]->elements[0].values, val);
931 if (ret != LDB_SUCCESS) {
934 return dirsync_filter_entry(req, res->msgs[0], res->controls, dsc, true);
938 * Let's add our own control
941 control = talloc_zero(ares->controls, struct ldb_dirsync_control);
942 if (control == NULL) {
947 * When outputing flags is used to say more results.
948 * For the moment we didn't honnor the size info */
953 * max_attribute is unused cf. 3.1.1.3.4.1.3 LDAP_SERVER_DIRSYNC_OID in MS-ADTS
956 control->max_attributes = 0;
957 cookie = talloc_zero(control, struct ldapControlDirSyncCookie);
958 if (cookie == NULL) {
963 ret = dirsync_create_vector(req, ares, dsc, cookie, ldb);
964 if (ret != LDB_SUCCESS) {
965 return ldb_module_done(dsc->req, NULL, NULL, ret);
969 unix_to_nt_time(&now, time(NULL));
970 cookie->blob.time = now;
971 cookie->blob.highwatermark.highest_usn = dsc->highestUSN;
972 cookie->blob.highwatermark.tmp_highest_usn = dsc->highestUSN;
973 cookie->blob.guid1 = *(dsc->our_invocation_id);
975 blob = talloc_zero(control, DATA_BLOB);
980 ndr_err = ndr_push_struct_blob(blob, blob, cookie,
981 (ndr_push_flags_fn_t)ndr_push_ldapControlDirSyncCookie);
983 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
984 ldb_set_errstring(ldb, "Can't marshall ldapControlDirSyncCookie struct");
985 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
987 control->cookie = (char *)blob->data;
988 control->cookie_len = blob->length;
989 ldb_reply_add_control(ares, LDB_CONTROL_DIRSYNC_OID, true, control);
991 return ldb_module_done(dsc->req, ares->controls,
992 ares->response, LDB_SUCCESS);
998 static int dirsync_ldb_search(struct ldb_module *module, struct ldb_request *req)
1000 struct ldb_control *control;
1001 struct ldb_result *acl_res;
1002 struct ldb_dirsync_control *dirsync_ctl;
1003 struct ldb_control *extended = NULL;
1004 struct ldb_request *down_req;
1005 struct dirsync_context *dsc;
1006 struct ldb_context *ldb;
1007 struct ldb_parse_tree *new_tree = req->op.search.tree;
1009 enum ndr_err_code ndr_err;
1015 if (ldb_dn_is_special(req->op.search.base)) {
1016 return ldb_next_request(module, req);
1020 * check if there's a dirsync control
1022 control = ldb_request_get_control(req, LDB_CONTROL_DIRSYNC_OID);
1023 if (control == NULL) {
1024 /* not found go on */
1025 return ldb_next_request(module, req);
1028 ldb = ldb_module_get_ctx(module);
1030 * This control must always be critical otherwise we return PROTOCOL error
1032 if (!control->critical) {
1033 return ldb_operr(ldb);
1036 dsc = talloc_zero(req, struct dirsync_context);
1038 return ldb_oom(ldb);
1040 dsc->module = module;
1042 dsc->nbDefaultAttrs = 0;
1045 dirsync_ctl = talloc_get_type(control->data, struct ldb_dirsync_control);
1046 if (dirsync_ctl == NULL) {
1047 return ldb_error(ldb, LDB_ERR_PROTOCOL_ERROR, "No data in dirsync control");
1050 ret = dsdb_find_nc_root(ldb, dsc, req->op.search.base, &dsc->nc_root);
1051 if (ret != LDB_SUCCESS) {
1055 if (ldb_dn_compare(dsc->nc_root, req->op.search.base) != 0) {
1056 if (dirsync_ctl->flags & LDAP_DIRSYNC_OBJECT_SECURITY) {
1057 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1058 "DN is not one of the naming context");
1061 return ldb_error(ldb, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS,
1062 "dN is not one of the naming context");
1066 if (!(dirsync_ctl->flags & LDAP_DIRSYNC_OBJECT_SECURITY)) {
1067 struct dom_sid *sid;
1068 struct security_descriptor *sd = NULL;
1069 const char *acl_attrs[] = { "nTSecurityDescriptor", "objectSid", "objectClass", NULL };
1070 const struct dsdb_schema *schema = NULL;
1071 const struct dsdb_class *objectclass = NULL;
1073 * If we don't have the flag and if we have the "replicate directory change" granted
1074 * then we upgrade ourself to system to not be blocked by the acl
1076 /* FIXME we won't check the replicate directory change filtered attribute set
1077 * it should be done so that if attr is not empty then we check that the user
1078 * has also this right
1082 * First change to system to get the SD of the root of current NC
1083 * if we don't the acl_read will forbid us the right to read it ...
1085 ret = dsdb_module_search_dn(module, dsc, &acl_res,
1086 req->op.search.base,
1088 DSDB_FLAG_NEXT_MODULE|DSDB_FLAG_AS_SYSTEM, req);
1090 if (ret != LDB_SUCCESS) {
1094 sid = samdb_result_dom_sid(dsc, acl_res->msgs[0], "objectSid");
1095 /* sid can be null ... */
1096 ret = dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(module), acl_res, acl_res->msgs[0], &sd);
1098 if (ret != LDB_SUCCESS) {
1101 schema = dsdb_get_schema(ldb, req);
1103 return LDB_ERR_OPERATIONS_ERROR;
1105 objectclass = dsdb_get_structural_oc_from_msg(schema, acl_res->msgs[0]);
1106 ret = acl_check_extended_right(dsc, module, req, objectclass,
1107 sd, acl_user_token(module),
1108 GUID_DRS_GET_CHANGES, SEC_ADS_CONTROL_ACCESS, sid);
1110 if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
1113 dsc->assystem = true;
1114 ret = ldb_request_add_control(req, LDB_CONTROL_AS_SYSTEM_OID, false, NULL);
1116 if (ret != LDB_SUCCESS) {
1119 talloc_free(acl_res);
1121 flags |= DSDB_ACL_CHECKS_DIRSYNC_FLAG;
1123 if (ret != LDB_SUCCESS) {
1129 dsc->functional_level = dsdb_functional_level(ldb);
1131 if (req->op.search.attrs) {
1132 attrs = ldb_attr_list_copy(dsc, req->op.search.attrs);
1133 if (attrs == NULL) {
1134 return ldb_oom(ldb);
1137 * Check if we have only "dn" as attribute, if so then
1138 * treat as if "*" was requested
1140 if (attrs && attrs[0]) {
1141 if (ldb_attr_cmp(attrs[0], "dn") == 0 && !attrs[1]) {
1142 attrs = talloc_array(dsc, const char*, 2);
1143 if (attrs == NULL) {
1144 return ldb_oom(ldb);
1151 * When returning all the attributes return also the SD as
1154 if (ldb_attr_in_list(attrs, "*")) {
1155 struct ldb_sd_flags_control *sdctr = talloc_zero(dsc, struct ldb_sd_flags_control);
1156 sdctr->secinfo_flags = 0xF;
1157 ret = ldb_request_add_control(req, LDB_CONTROL_SD_FLAGS_OID, false, sdctr);
1158 if (ret != LDB_SUCCESS) {
1161 attrs = ldb_attr_list_copy_add(dsc, attrs, "parentGUID");
1162 if (attrs == NULL) {
1163 return ldb_oom(ldb);
1165 attrs = ldb_attr_list_copy_add(dsc, attrs, "replPropertyMetaData");
1166 if (attrs == NULL) {
1167 return ldb_oom(ldb);
1170 * When no attributes are asked we in anycase expect at least 3 attributes:
1176 dsc->nbDefaultAttrs = 3;
1179 * We will need this two attributes in the callback
1181 attrs = ldb_attr_list_copy_add(dsc, attrs, "usnChanged");
1182 if (attrs == NULL) {
1183 return ldb_operr(ldb);
1185 attrs = ldb_attr_list_copy_add(dsc, attrs, "replPropertyMetaData");
1186 if (attrs == NULL) {
1187 return ldb_operr(ldb);
1190 if (!ldb_attr_in_list(attrs, "instanceType")) {
1191 attrs = ldb_attr_list_copy_add(dsc, attrs, "instanceType");
1192 if (attrs == NULL) {
1193 return ldb_operr(ldb);
1195 dsc->nbDefaultAttrs++;
1198 if (!ldb_attr_in_list(attrs, "objectGUID")) {
1199 attrs = ldb_attr_list_copy_add(dsc, attrs, "objectGUID");
1200 if (attrs == NULL) {
1201 return ldb_operr(ldb);
1205 * Always increment the number of asked attributes as we don't care if objectGUID was asked
1206 * or not for counting the number of "real" attributes returned.
1208 dsc->nbDefaultAttrs++;
1210 if (!ldb_attr_in_list(attrs, "parentGUID")) {
1211 attrs = ldb_attr_list_copy_add(dsc, attrs, "parentGUID");
1212 if (attrs == NULL) {
1213 return ldb_operr(ldb);
1216 dsc->nbDefaultAttrs++;
1220 struct ldb_sd_flags_control *sdctr = talloc_zero(dsc, struct ldb_sd_flags_control);
1221 sdctr->secinfo_flags = 0xF;
1222 ret = ldb_request_add_control(req, LDB_CONTROL_SD_FLAGS_OID, false, sdctr);
1223 attrs = talloc_array(dsc, const char*, 4);
1224 if (attrs == NULL) {
1225 return ldb_operr(ldb);
1228 attrs[1] = "parentGUID";
1229 attrs[2] = "replPropertyMetaData";
1231 if (ret != LDB_SUCCESS) {
1235 * When no attributes are asked we in anycase expect at least 3 attributes:
1241 dsc->nbDefaultAttrs = 3;
1244 /* check if there's an extended dn control */
1245 extended = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
1246 if (extended != NULL) {
1247 struct ldb_extended_dn_control *extended_ctrl = NULL;
1249 if (extended->data != NULL) {
1250 extended_ctrl = talloc_get_type(extended->data,
1251 struct ldb_extended_dn_control);
1253 if (extended_ctrl != NULL) {
1254 dsc->extended_type = extended_ctrl->type;
1257 ret = ldb_request_add_control(req, LDB_CONTROL_EXTENDED_DN_OID, false, NULL);
1258 if (ret != LDB_SUCCESS) {
1261 dsc->noextended = true;
1264 if (ldb_request_get_control(req, LDB_CONTROL_REVEAL_INTERNALS) == NULL) {
1265 ret = ldb_request_add_control(req, LDB_CONTROL_REVEAL_INTERNALS, false, NULL);
1266 if (ret != LDB_SUCCESS) {
1271 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID) == NULL) {
1272 ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_RECYCLED_OID, false, NULL);
1273 if (ret != LDB_SUCCESS) {
1278 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID) == NULL) {
1279 ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, false, NULL);
1280 if (ret != LDB_SUCCESS) {
1285 if (dirsync_ctl->flags & LDAP_DIRSYNC_INCREMENTAL_VALUES) {
1286 dsc->linkIncrVal = true;
1288 dsc->linkIncrVal = false;
1291 dsc->our_invocation_id = samdb_ntds_invocation_id(ldb);
1292 if (dsc->our_invocation_id == NULL) {
1293 return ldb_operr(ldb);
1296 if (dirsync_ctl->cookie_len > 0) {
1297 struct ldapControlDirSyncCookie cookie;
1299 blob.data = (uint8_t *)dirsync_ctl->cookie;
1300 blob.length = dirsync_ctl->cookie_len;
1301 ndr_err = ndr_pull_struct_blob(&blob, dsc, &cookie,
1302 (ndr_pull_flags_fn_t)ndr_pull_ldapControlDirSyncCookie);
1304 /* If we can't unmarshall the cookie into the correct structure we return
1305 * unsupported critical extension
1307 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1308 return ldb_error(ldb, LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION,
1309 "Unable to unmarshall cookie as a ldapControlDirSyncCookie structure");
1313 * Let's search for the max usn within the cookie
1315 if (GUID_equal(&(cookie.blob.guid1), dsc->our_invocation_id)) {
1317 * Ok, it's our invocation ID so we can treat the demand
1318 * Let's take the highest usn from (tmp)highest_usn
1320 dsc->fromreqUSN = cookie.blob.highwatermark.tmp_highest_usn;
1321 dsc->localonly = true;
1323 if (cookie.blob.highwatermark.highest_usn > cookie.blob.highwatermark.tmp_highest_usn) {
1324 dsc->fromreqUSN = cookie.blob.highwatermark.highest_usn;
1327 dsc->localonly = false;
1329 if (cookie.blob.extra_length > 0 &&
1330 cookie.blob.extra.uptodateness_vector.ctr.ctr1.count > 0) {
1331 struct drsuapi_DsReplicaCursor cursor;
1333 for (p=0; p < cookie.blob.extra.uptodateness_vector.ctr.ctr1.count; p++) {
1334 cursor = cookie.blob.extra.uptodateness_vector.ctr.ctr1.cursors[p];
1335 if (GUID_equal( &(cursor.source_dsa_invocation_id), dsc->our_invocation_id)) {
1336 if (cursor.highest_usn > dsc->fromreqUSN) {
1337 dsc->fromreqUSN = cursor.highest_usn;
1341 dsc->cursors = talloc_steal(dsc,
1342 cookie.blob.extra.uptodateness_vector.ctr.ctr1.cursors);
1343 if (dsc->cursors == NULL) {
1344 return ldb_oom(ldb);
1346 dsc->cursor_size = p;
1350 DEBUG(4, ("Dirsync: searching with min usn > %llu\n",
1351 (long long unsigned int)dsc->fromreqUSN));
1352 if (dsc->fromreqUSN > 0) {
1353 /* FIXME it would be better to use PRId64 */
1354 char *expression = talloc_asprintf(dsc, "(&%s(uSNChanged>=%llu))",
1355 ldb_filter_from_tree(dsc,
1356 req->op.search.tree),
1357 (long long unsigned int)(dsc->fromreqUSN + 1));
1359 if (expression == NULL) {
1360 return ldb_oom(ldb);
1362 new_tree = ldb_parse_tree(req, expression);
1363 if (new_tree == NULL) {
1364 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
1365 "Problem while parsing tree");
1370 * Mark dirsync control as uncritical (done)
1372 * We need this so ranged_results knows how to behave with
1375 control->critical = false;
1376 dsc->schema = dsdb_get_schema(ldb, dsc);
1378 * At the beginning we make the hypothesis that we will return a
1379 * complete result set.
1382 dsc->partial = false;
1385 * 3.1.1.3.4.1.3 of MS-ADTS.pdf specify that if the scope is not subtree
1386 * we treat the search as if subtree was specified
1389 ret = ldb_build_search_req_ex(&down_req, ldb, dsc,
1390 req->op.search.base,
1395 dsc, dirsync_search_callback,
1397 ldb_req_set_custom_flags(down_req, flags);
1398 LDB_REQ_SET_LOCATION(down_req);
1399 if (ret != LDB_SUCCESS) {
1402 /* perform the search */
1403 return ldb_next_request(module, down_req);
1406 static int dirsync_ldb_init(struct ldb_module *module)
1410 ret = ldb_mod_register_control(module, LDB_CONTROL_DIRSYNC_OID);
1411 if (ret != LDB_SUCCESS) {
1412 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
1413 "dirsync: Unable to register control with rootdse!\n");
1414 return ldb_operr(ldb_module_get_ctx(module));
1417 return ldb_next_init(module);
1420 static const struct ldb_module_ops ldb_dirsync_ldb_module_ops = {
1422 .search = dirsync_ldb_search,
1423 .init_context = dirsync_ldb_init,
1427 initialise the module
1429 _PUBLIC_ int ldb_dirsync_module_init(const char *version)
1432 LDB_MODULE_CHECK_VERSION(version);
1433 ret = ldb_register_module(&ldb_dirsync_ldb_module_ops);