CVE-2018-14628: s4:dsdb: remove unused code in dirsync_filter_entry()
[samba.git] / source4 / dsdb / samdb / ldb_modules / dirsync.c
1 /*
2    SAMDB control module
3
4    Copyright (C) Matthieu Patou <mat@matws.net> 2011
5
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.
10
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.
15
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/>.
18 */
19
20
21 #include "includes.h"
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"
32
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
37
38
39 struct dirsync_context {
40         struct ldb_module *module;
41         struct ldb_request *req;
42
43         /*
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 useful to track the entries that need not to
47          * be returned because there is no real change
48          */
49
50         unsigned int nbDefaultAttrs;
51         uint64_t highestUSN;
52         uint64_t fromreqUSN;
53         uint32_t cursor_size;
54         bool noextended;
55         int extended_type;
56         bool linkIncrVal;
57         bool localonly;
58         bool partial;
59         int functional_level;
60         const struct GUID *our_invocation_id;
61         const struct dsdb_schema *schema;
62         struct ldb_dn *nc_root;
63         struct drsuapi_DsReplicaCursor *cursors;
64 };
65
66
67 static int dirsync_filter_entry(struct ldb_request *req,
68                                         struct ldb_message *msg,
69                                         struct ldb_control **controls,
70                                         struct dirsync_context *dsc,
71                                         bool referral)
72 {
73         struct ldb_context *ldb;
74         uint64_t val = 0;
75         enum ndr_err_code ndr_err;
76         uint32_t n;
77         int i;
78         unsigned int size, j;
79         struct ldb_val *replMetaData = NULL;
80         struct replPropertyMetaDataBlob rmd;
81         const struct dsdb_attribute *attr;
82         const char **listAttr = NULL;
83         bool namereturned = false;
84         bool nameasked = false;
85         NTSTATUS status;
86         /* Adjustment for the added attributes, it will reduce the number of
87          * expected to be here attributes*/
88         unsigned int delta = 0;
89         const char **myaccept = NULL;
90         const char *emptyaccept[] = { NULL };
91         const char *extendedaccept[] = { "GUID", "SID", "WKGUID", NULL };
92         const char *rdn = NULL;
93         struct ldb_message_element *el;
94         struct ldb_message *newmsg;
95         bool keep = false;
96         /*
97          * Where we asked to do extended dn ?
98          * if so filter out everything bug GUID, SID, WKGUID,
99          * if not filter out everything (just keep the dn).
100          */
101         if ( dsc->noextended == true ) {
102                 myaccept = emptyaccept;
103         } else {
104                 myaccept = extendedaccept;
105         }
106         ldb = ldb_module_get_ctx(dsc->module);
107
108         if (msg->num_elements == 0) {
109                 /*
110                         * Entry that we don't really have access to
111                         */
112                 return LDB_SUCCESS;
113         }
114         ldb_dn_extended_filter(msg->dn, myaccept);
115
116         /*
117         * If the RDN starts with CN then the CN attribute is never returned
118         */
119         rdn = ldb_dn_get_rdn_name(msg->dn);
120
121         /*
122          * if objectGUID is asked and we are dealing for the referrals entries and
123          * the usn searched is 0 then we didn't count the objectGUID as an automatically
124          * returned attribute, do to so we increment delta.
125          */
126         if (referral == true &&
127                         ldb_attr_in_list(req->op.search.attrs, "objectGUID") &&
128                         dsc->fromreqUSN == 0) {
129                 delta++;
130         }
131
132
133         /*
134          * In terms of big O notation this is not the best algorithm,
135          * but we try our best not to make the worse one.
136          * We are obliged to run through the n message's elements
137          * and through the p elements of the replPropertyMetaData.
138          *
139          * It turns out that we are crawling twice the message's elements
140          * the first crawl is to remove the non replicated and generated
141          * attributes. The second one is to remove attributes that haven't
142          * a USN > as the requested one.
143          *
144          * In the second crawl we are reading the list of elements in the
145          * replPropertyMetaData for each remaining replicated attribute.
146          * In order to keep the list small
147          *
148          * We have a O(n'*p') complexity, in worse case n' = n and p' = p
149          * but in most case n' = n/2 (at least half of returned attributes
150          * are not replicated or generated) and p' is small as we
151          * list only the attribute that have been modified since last interrogation
152          *
153          */
154         for (i = msg->num_elements - 1; i >= 0; i--) {
155                 if (ldb_attr_cmp(msg->elements[i].name, "uSNChanged") == 0) {
156                         int error = 0;
157                         /* Read the USN it will be used at the end of the filtering
158                          * to update the max USN in the cookie if we
159                          * decide to keep this entry
160                          */
161                         val = smb_strtoull(
162                                 (const char*)msg->elements[i].values[0].data,
163                                 NULL,
164                                 0,
165                                 &error,
166                                 SMB_STR_STANDARD);
167                         if (error != 0) {
168                                 ldb_set_errstring(ldb,
169                                                   "Failed to convert USN");
170                                 return ldb_module_done(dsc->req,
171                                                        NULL,
172                                                        NULL,
173                                                        LDB_ERR_OPERATIONS_ERROR);
174                         }
175                         continue;
176                 }
177
178                 if (ldb_attr_cmp(msg->elements[i].name,
179                                                 "replPropertyMetaData") == 0) {
180                         replMetaData = (talloc_steal(dsc, &msg->elements[i].values[0]));
181                         continue;
182                 }
183         }
184
185         if (replMetaData == NULL) {
186                 bool guidfound = false;
187
188                 /*
189                  * We are in the case of deleted object where we don't have the
190                  * right to read it.
191                  */
192                 if (!ldb_msg_find_attr_as_uint(msg, "isDeleted", 0)) {
193                         /*
194                          * This is not a deleted item and we don't
195                          * have the replPropertyMetaData.
196                          * Do not return it
197                          */
198                         return LDB_SUCCESS;
199                 }
200                 el = ldb_msg_find_element(msg, "objectGUID");
201                 if ( el != NULL) {
202                         guidfound = true;
203                 }
204                 /*
205                  * We expect to find the GUID in the object,
206                  * if it turns out not to be the case sometimes
207                  * we will uncomment the code below
208                  */
209                 SMB_ASSERT(guidfound == true);
210                 return ldb_module_send_entry(dsc->req, msg, controls);
211         }
212
213         newmsg = ldb_msg_new(dsc->req);
214         if (newmsg == NULL) {
215                 return ldb_oom(ldb);
216         }
217
218         ndr_err = ndr_pull_struct_blob(replMetaData, dsc, &rmd,
219                 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
220         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
221                 ldb_set_errstring(ldb, "Unable to unmarshall replPropertyMetaData");
222                 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
223         }
224         if (ldb_attr_in_list(req->op.search.attrs, "name") ||
225                         ldb_attr_in_list(req->op.search.attrs, "*")) {
226                 nameasked = true;
227         }
228
229         /*
230                 * If we don't have an USN and no uptodateness array then we skip the
231                 * test phase this is an optimisation for the case when you
232                 * first query the DC without a cookie.
233                 * As this query is most probably the one
234                 * that will return the biggest answer, skipping this part
235                 * will really save time.
236                 */
237         if (ldb_dn_compare(dsc->nc_root, msg->dn) == 0) {
238                 /* If we have name then we expect to have parentGUID,
239                  * it will not be the case for the root of the NC
240                  */
241                 delta++;
242         }
243
244         if (dsc->fromreqUSN > 0 || dsc->cursors != NULL) {
245                 j = 0;
246                 /*
247                 * Allocate an array of size(replMetaData) of char*
248                 * we know that it will be oversized but it's a short lived element
249                 */
250                 listAttr = talloc_array(msg, const char*, rmd.ctr.ctr1.count + 1);
251                 if (listAttr == NULL) {
252                         return ldb_oom(ldb);
253                 }
254                 for (n=0; n < rmd.ctr.ctr1.count; n++) {
255                         struct replPropertyMetaData1 *omd = &rmd.ctr.ctr1.array[n];
256                         if (omd->local_usn > dsc->fromreqUSN) {
257                                 const struct dsdb_attribute *a = dsdb_attribute_by_attributeID_id(dsc->schema,
258                                                                                 omd->attid);
259                                 if (!dsc->localonly) {
260                                         struct drsuapi_DsReplicaCursor *tab = dsc->cursors;
261                                         uint32_t l;
262                                         for (l=0; l < dsc->cursor_size; l++) {
263                                                 if (GUID_equal(&tab[l].source_dsa_invocation_id, &omd->originating_invocation_id) &&
264                                                                 tab[l].highest_usn >= omd->originating_usn) {
265                                                         /*
266                                                          * If we have in the uptodateness vector an entry
267                                                          * with the same invocation id as the originating invocation
268                                                          * and if the usn in the vector is greater or equal to
269                                                          * the one in originating_usn, then it means that this entry
270                                                          * has already been sent (from another DC) to the client
271                                                          * no need to resend it one more time.
272                                                          */
273                                                         goto skip;
274                                                 }
275                                         }
276                                         /* If we are here it's because we have a usn > (max(usn of vectors))*/
277                                 }
278                                 if (namereturned == false &&
279                                                 nameasked == true &&
280                                                 ldb_attr_cmp(a->lDAPDisplayName, "name") == 0) {
281                                         namereturned = true;
282                                         if (ldb_dn_compare(dsc->nc_root, msg->dn) == 0) {
283                                                 delta++;
284                                         }
285                                 }
286                                 listAttr[j] = a->lDAPDisplayName;
287                                 j++;
288 skip:
289                                 continue;
290                         }
291                 }
292                 size = j;
293         } else {
294                 size = 0;
295                 if (ldb_attr_in_list(req->op.search.attrs, "*") ||
296                                 ldb_attr_in_list(req->op.search.attrs, "name")) {
297                         namereturned = true;
298                 }
299         }
300
301
302         /*
303          * Let's loop around the remaining elements
304          * to see which one are in the listAttr.
305          * If they are in this array it means that
306          * their localusn > usn from the request (in the cookie)
307          * if not we remove the attribute.
308          */
309         for (i = msg->num_elements - 1; i >= 0; i--) {
310                 const char *ldapattrname;
311
312                 el = &(msg->elements[i]);
313                 ldapattrname = el->name;
314
315                 attr = dsdb_attribute_by_lDAPDisplayName(dsc->schema,
316                                 el->name);
317                 if (attr == NULL) {
318                         continue;
319                 }
320
321                 keep = false;
322
323                 if (attr->linkID & 1) {
324                         /*
325                          * Attribute is a backlink so let's remove it
326                          */
327                         continue;
328                 }
329
330                 if (ldb_attr_cmp(msg->elements[i].name,
331                                                 "replPropertyMetaData") == 0) {
332                         continue;
333                 }
334
335                 if ((attr->systemFlags & (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED))) {
336                         if (ldb_attr_cmp(attr->lDAPDisplayName, "objectGUID") != 0 &&
337                                         ldb_attr_cmp(attr->lDAPDisplayName, "parentGUID") != 0) {
338                                 /*
339                                  * Attribute is constructed or not replicated, let's get rid of it
340                                  */
341                                 continue;
342                         } else {
343                                 /* Let's keep the attribute that we forced to be added
344                                  * even if they are not in the replicationMetaData
345                                  * or are just generated
346                                  */
347                                 if (namereturned == false &&
348                                         (ldb_attr_cmp(attr->lDAPDisplayName, "parentGUID") == 0)) {
349                                         delta++;
350                                         continue;
351                                 }
352                                 if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
353                                         return ldb_error(ldb,
354                                                 LDB_ERR_OPERATIONS_ERROR,
355                                                 "Unable to add attribute");
356                                 }
357                                 talloc_steal(newmsg->elements, el->name);
358                                 talloc_steal(newmsg->elements, el->values);
359                                 continue;
360                         }
361                 }
362
363                 if (ldb_attr_cmp(msg->elements[i].name, rdn) == 0) {
364                         /*
365                          * We have an attribute that is the same as the start of the RDN
366                          * (ie. attribute CN with rdn CN=).
367                          */
368                         continue;
369                 }
370
371                 if (ldb_attr_cmp(attr->lDAPDisplayName, "instanceType") == 0) {
372                         if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
373                                 return ldb_error(ldb,
374                                                 LDB_ERR_OPERATIONS_ERROR,
375                                                 "Unable to add attribute");
376                         }
377                         talloc_steal(newmsg->elements, el->name);
378                         talloc_steal(newmsg->elements, el->values);
379                         continue;
380                 }
381                 /* For links, when our functional level > windows 2000
382                  * we use the RMD_LOCAL_USN information to decide whether
383                  * we return the attribute or not.
384                  * For windows 2000 this information is in the replPropertyMetaData
385                  * so it will be handled like any other replicated attribute
386                  */
387
388                 if (dsc->functional_level > DS_DOMAIN_FUNCTION_2000 &&
389                                 attr->linkID != 0 ) {
390                         int k;
391                         /*
392                          * Elements for incremental changes on linked attributes
393                          */
394                         struct ldb_message_element *el_incr_add = NULL;
395                         struct ldb_message_element *el_incr_del = NULL;
396                         /*
397                          * Attribute is a forwardlink so let's remove it
398                          */
399
400                         for (k = el->num_values -1; k >= 0; k--) {
401                                 char *dn_ln;
402                                 uint32_t flags = 0;
403                                 uint32_t tmp_usn = 0;
404                                 uint32_t tmp_usn2 = 0;
405                                 struct GUID invocation_id = GUID_zero();
406                                 struct dsdb_dn *dn = dsdb_dn_parse(msg, ldb, &el->values[k], attr->syntax->ldap_oid);
407                                 struct ldb_dn *copydn;
408                                 if (dn == NULL) {
409                                         ldb_set_errstring(ldb, "Cannot parse DN");
410                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
411                                 }
412
413                                 copydn = ldb_dn_copy(msg, dn->dn);
414                                 if (copydn == NULL) {
415                                         ldb_oom(ldb);
416                                 }
417
418                                 status = dsdb_get_extended_dn_uint32(dn->dn, &tmp_usn, "RMD_LOCAL_USN");
419                                 if (!NT_STATUS_IS_OK(status)) {
420                                         talloc_free(dn);
421                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
422                                 }
423                                 status = dsdb_get_extended_dn_guid(dn->dn,  &invocation_id, "RMD_INVOCID");
424                                 if (!NT_STATUS_IS_OK(status)) {
425                                         talloc_free(dn);
426                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
427                                 }
428
429                                 status = dsdb_get_extended_dn_uint32(dn->dn, &flags, "RMD_FLAGS");
430                                 if (!NT_STATUS_IS_OK(status)) {
431                                         talloc_free(dn);
432                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
433                                 }
434
435                                 status = dsdb_get_extended_dn_uint32(dn->dn, &tmp_usn2, "RMD_ORIGINATING_USN");
436                                 if (!NT_STATUS_IS_OK(status)) {
437                                         talloc_free(dn);
438                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
439                                 }
440
441                                 ldb_dn_extended_filter(dn->dn, myaccept);
442                                 dn_ln = dsdb_dn_get_extended_linearized(dn, dn,
443                                                         dsc->extended_type);
444                                 if (dn_ln == NULL)
445                                 {
446                                         talloc_free(dn);
447                                         ldb_set_errstring(ldb, "Cannot linearize dn");
448                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
449                                 }
450
451                                 talloc_free(el->values[k].data);
452                                 el->values[k].data = (uint8_t*)talloc_steal(el->values, dn_ln);
453                                 if (el->values[k].data == NULL) {
454                                         talloc_free(dn);
455                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
456                                 }
457                                 el->values[k].length = strlen(dn_ln);
458
459
460                                 if (tmp_usn > dsc->fromreqUSN) {
461                                         if (!dsc->localonly) {
462                                                 struct drsuapi_DsReplicaCursor *tab = dsc->cursors;
463                                                 uint32_t l;
464
465                                                 for (l=0; l < dsc->cursor_size; l++) {
466                                                         if (GUID_equal(&tab[l].source_dsa_invocation_id, &invocation_id) &&
467                                                                         tab[l].highest_usn >= tmp_usn2) {
468                                                                 /*
469                                                                 * If we have in the uptodateness vector an entry
470                                                                 * with the same invocation id as the originating invocation
471                                                                 * and if the usn in the vector is greater or equal to
472                                                                 * the one in originating_usn, then it means that this entry
473                                                                 * has already been sent (from another DC) to the client
474                                                                 * no need to resend it one more time.
475                                                                 */
476                                                                 goto skip_link;
477                                                         }
478                                                 }
479                                                 /* If we are here it's because we have a usn > (max(usn of vectors))*/
480                                                 keep = true;
481                                         } else {
482                                                 keep = true;
483                                         }
484                                 /* If we are here it's because the link is more recent than either any
485                                  * originating usn or local usn
486                                  */
487
488                                         if (dsc->linkIncrVal == true) {
489                                                 struct ldb_message_element *tmpel;
490                                                 if (flags & DSDB_RMD_FLAG_DELETED) {
491                                                         /* We have to check that the inactive link still point to an existing object */
492                                                         struct GUID guid;
493                                                         struct ldb_dn *tdn;
494                                                         int ret;
495
496                                                         status = dsdb_get_extended_dn_guid(copydn, &guid, "GUID");
497                                                         if (!NT_STATUS_IS_OK(status)) {
498                                                                 DEBUG(0,(__location__ " Unable to extract GUID in linked attribute '%s' in '%s'\n",
499                                                                         el->name, ldb_dn_get_linearized(copydn)));
500                                                                 return ldb_operr(ldb);
501                                                         }
502                                                         ret = dsdb_module_dn_by_guid(dsc->module, newmsg, &guid, &tdn, req);
503                                                         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
504                                                                 DEBUG(2, (" Search of guid %s returned 0 objects, skipping it !\n",
505                                                                                         GUID_string(newmsg, &guid)));
506                                                                 continue;
507                                                         } else if (ret != LDB_SUCCESS) {
508                                                                 DEBUG(0, (__location__ " Search of guid %s failed with error code %d\n",
509                                                                                         GUID_string(newmsg, &guid),
510                                                                                         ret));
511                                                                 continue;
512                                                         }
513                                                         tmpel = el_incr_del;
514                                                 } else {
515                                                         tmpel = el_incr_add;
516                                                 }
517
518                                                 if (tmpel == NULL) {
519                                                         tmpel = talloc_zero(newmsg, struct ldb_message_element);
520                                                         if (tmpel == NULL) {
521                                                                 return ldb_oom(ldb);
522                                                         }
523                                                         tmpel->values = talloc_array(tmpel, struct ldb_val, 1);
524                                                         if (tmpel->values == NULL) {
525                                                                 return ldb_oom(ldb);
526                                                         }
527                                                         if (flags & DSDB_RMD_FLAG_DELETED) {
528                                                                 tmpel->name = talloc_asprintf(tmpel,
529                                                                                 "%s;range=0-0",
530                                                                                 el->name);
531                                                         }
532                                                         else {
533                                                                 tmpel->name = talloc_asprintf(tmpel,
534                                                                                 "%s;range=1-1",
535                                                                                 el->name);
536                                                         }
537                                                         if (tmpel->name == NULL) {
538                                                                 return ldb_oom(ldb);
539                                                         }
540                                                         tmpel->num_values = 1;
541                                                 } else {
542                                                         tmpel->num_values += 1;
543                                                         tmpel->values = talloc_realloc(tmpel,
544                                                                                                 tmpel->values,
545                                                                                                 struct ldb_val,
546                                                                                                 tmpel->num_values);
547                                                         if (tmpel->values == NULL) {
548                                                                 return ldb_oom(ldb);
549                                                         }
550                                                 }
551                                                 tmpel->values[tmpel->num_values -1].data =talloc_steal(tmpel->values, el->values[k].data);
552                                                 tmpel->values[tmpel->num_values -1].length = el->values[k].length;
553
554                                                 if (flags & DSDB_RMD_FLAG_DELETED) {
555                                                         el_incr_del = tmpel;
556                                                 } else {
557                                                         el_incr_add = tmpel;
558                                                 }
559                                         }
560                                 }
561
562                                 if (dsc->linkIncrVal == false) {
563                                         if (flags & DSDB_RMD_FLAG_DELETED) {
564                                                 ARRAY_DEL_ELEMENT(
565                                                         el->values,
566                                                         k,
567                                                         el->num_values);
568                                                 el->num_values--;
569                                         }
570                                 }
571 skip_link:
572                                 talloc_free(dn);
573
574                         }
575                         if (keep == true) {
576                                 if (dsc->linkIncrVal == false) {
577                                         if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
578                                                 return ldb_error(ldb,
579                                                         LDB_ERR_OPERATIONS_ERROR,
580                                                         "Unable to add attribute");
581                                         }
582                                         talloc_steal(newmsg->elements, el->name);
583                                         talloc_steal(newmsg->elements, el->values);
584                                 } else {
585                                         if (el_incr_del) {
586                                                 if (ldb_msg_add(newmsg, el_incr_del, LDB_FLAG_MOD_ADD))
587                                                         return ldb_error(ldb,
588                                                                 LDB_ERR_OPERATIONS_ERROR,
589                                                                 "Unable to add attribute");
590                                         }
591                                         if (el_incr_add) {
592                                                 if (ldb_msg_add(newmsg, el_incr_add, LDB_FLAG_MOD_ADD))
593                                                         return ldb_error(ldb,
594                                                                 LDB_ERR_OPERATIONS_ERROR,
595                                                                 "Unable to add attribute");
596                                         }
597                                 }
598                         }
599                         continue;
600                 }
601
602                 if (listAttr) {
603                         for (j=0; j<size; j++) {
604                         /*
605                                 * We mark attribute that has already been seen well
606                                 * as seen. So that after attribute that are still in
607                                 * listAttr are attributes that has been modified after
608                                 * the requested USN but not present in the attributes
609                                 * returned by the ldb search.
610                                 * That is to say attributes that have been removed
611                                 */
612                                 if (listAttr[j] && ldb_attr_cmp(listAttr[j], ldapattrname) == 0) {
613                                         listAttr[j] = NULL;
614                                         keep = true;
615                                         continue;
616                                 }
617                         }
618                 } else {
619                         keep = true;
620                 }
621
622                 if (keep == true) {
623                         if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
624                                 return ldb_error(ldb,
625                                         LDB_ERR_OPERATIONS_ERROR,
626                                         "Unable to add attribute");
627                         }
628                         talloc_steal(newmsg->elements, el->name);
629                         talloc_steal(newmsg->elements, el->values);
630                         continue;
631                 }
632         }
633         talloc_steal(newmsg->elements, msg);
634
635         /*
636          * Here we run through the list of attributes returned
637          * in the propertyMetaData.
638          * Entries of this list have usn > requested_usn,
639          * entries that are also present in the message have been
640          * replaced by NULL, so at this moment the list contains
641          * only elements that have a usn > requested_usn and that
642          * haven't been seen. It's attributes that were removed.
643          * We add them to the message like empty elements.
644          */
645         for (j=0; j<size; j++) {
646                 if (listAttr[j] && (
647                                 ldb_attr_in_list(req->op.search.attrs, "*") ||
648                                 ldb_attr_in_list(req->op.search.attrs, listAttr[j])) &&
649                                 (ldb_attr_cmp(listAttr[j], rdn) != 0) &&
650                                 (ldb_attr_cmp(listAttr[j], "instanceType") != 0)) {
651                         ldb_msg_add_empty(newmsg, listAttr[j], LDB_FLAG_MOD_DELETE, NULL);
652                 }
653         }
654         talloc_free(listAttr);
655
656         if ((newmsg->num_elements - ( dsc->nbDefaultAttrs - delta)) > 0) {
657                 /*
658                  * After cleaning attributes there is still some attributes that were not added just
659                  * for the purpose of the control (objectGUID, instanceType, ...)
660                  */
661
662                 newmsg->dn = talloc_steal(newmsg, msg->dn);
663                 if (val > dsc->highestUSN) {
664                         dsc->highestUSN = val;
665                 }
666                 return ldb_module_send_entry(dsc->req, newmsg, controls);
667         } else {
668                 talloc_free(newmsg);
669                 return LDB_SUCCESS;
670         }
671 }
672
673
674 static int dirsync_create_vector(struct ldb_request *req,
675                                         struct ldb_reply *ares,
676                                         struct dirsync_context *dsc,
677                                         struct ldapControlDirSyncCookie *cookie,
678                                         struct ldb_context *ldb)
679 {
680         struct ldb_result *resVector;
681         const char* attrVector[] = {"replUpToDateVector", NULL };
682         uint64_t highest_usn;
683         uint32_t count = 1;
684         int ret;
685         struct drsuapi_DsReplicaCursor *tab;
686
687         ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ, &highest_usn);
688         if (ret != LDB_SUCCESS) {
689                 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, "Unable to get highest USN from current NC");
690         }
691
692         /* If we have a full answer then the highest USN
693          * is not the highest USN from the result set but the
694          * highest of the naming context, unless the sequence is not updated yet.
695          */
696         if (highest_usn > dsc->highestUSN) {
697                 dsc->highestUSN = highest_usn;
698         }
699
700
701         ret = dsdb_module_search_dn(dsc->module, dsc, &resVector,
702                         dsc->nc_root,
703                         attrVector,
704                         DSDB_FLAG_NEXT_MODULE, req);
705         if (ret != LDB_SUCCESS) {
706                 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
707                                  "Unable to get replUpToDateVector for current NC");
708         }
709
710         if (resVector->count != 0) {
711                 DATA_BLOB blob;
712                 uint32_t i;
713                 struct ldb_message_element *el = ldb_msg_find_element(resVector->msgs[0], "replUpToDateVector");
714                 if (el) {
715                         enum ndr_err_code ndr_err;
716                         struct replUpToDateVectorBlob utd;
717                         blob.data = el->values[0].data;
718                         blob.length = el->values[0].length;
719                         ndr_err = ndr_pull_struct_blob(&blob, dsc, &utd,
720                                                 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
721
722                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
723                                 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
724                                                 "Unable to pull replUpToDateVectorBlob structure");
725                         }
726
727
728                         count += utd.ctr.ctr2.count;
729                         tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
730                         if (tab == NULL) {
731                                 return ldb_oom(ldb);
732                         }
733                         for (i=1; i < count; i++) {
734                                 memset(&tab[i], 0, sizeof(struct drsuapi_DsReplicaCursor));
735                                 tab[i].highest_usn = utd.ctr.ctr2.cursors[i-1].highest_usn;
736                                 tab[i].source_dsa_invocation_id = utd.ctr.ctr2.cursors[i-1].source_dsa_invocation_id;
737                         }
738                 } else {
739                         tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
740                         if (tab == NULL) {
741                                 return ldb_oom(ldb);
742                         }
743                 }
744         } else {
745                 /*
746                  * No replUpToDateVector ? it happens quite often (1 DC,
747                  * other DCs didn't update ...
748                  */
749                 tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
750                 if (tab == NULL) {
751                         return ldb_oom(ldb);
752                 }
753         }
754         /* Our vector is always the first */
755         tab[0].highest_usn = dsc->highestUSN;
756         tab[0].source_dsa_invocation_id = *(dsc->our_invocation_id);
757
758
759         /* We have to add the uptodateness vector that we have*/
760         /* Version is always 1 in dirsync cookies */
761         cookie->blob.extra.uptodateness_vector.version = 1;
762         cookie->blob.extra.uptodateness_vector.reserved = 0;
763         cookie->blob.extra.uptodateness_vector.ctr.ctr1.count = count;
764         cookie->blob.extra.uptodateness_vector.ctr.ctr1.reserved = 0;
765         cookie->blob.extra.uptodateness_vector.ctr.ctr1.cursors = tab;
766
767         return LDB_SUCCESS;
768 }
769
770 static int dirsync_search_callback(struct ldb_request *req, struct ldb_reply *ares)
771 {
772         int ret;
773         struct dirsync_context *dsc;
774         struct ldb_result *res, *res2;
775         struct ldb_dirsync_control *control;
776         struct ldapControlDirSyncCookie *cookie;
777         struct ldb_context *ldb;
778         struct ldb_dn *dn;
779         struct ldb_val *val;
780         DATA_BLOB *blob;
781         NTTIME now;
782         const char *attrs[] = { "objectGUID", NULL };
783         enum ndr_err_code ndr_err;
784         char *tmp;
785         uint32_t flags;
786
787         dsc = talloc_get_type_abort(req->context, struct dirsync_context);
788         ldb = ldb_module_get_ctx(dsc->module);
789         if (!ares) {
790                 return ldb_module_done(dsc->req, NULL, NULL,
791                                        LDB_ERR_OPERATIONS_ERROR);
792         }
793         if (ares->error != LDB_SUCCESS) {
794                 return ldb_module_done(dsc->req, ares->controls,
795                                        ares->response, ares->error);
796         }
797
798         switch (ares->type) {
799         case LDB_REPLY_ENTRY:
800                 return dirsync_filter_entry(req, ares->message, ares->controls, dsc, false);
801
802         case LDB_REPLY_REFERRAL:
803                 /* Skip the ldap(s):// so up to 8 chars,
804                  * we don't care to be precise as the goal is to be in
805                  * the name of DC, then we search the next '/'
806                  * as it will be the last char before the DN of the referral
807                  */
808                 if (strncmp(ares->referral, "ldap://", 7) == 0) {
809                         tmp = ares->referral + 7;
810                 } else if (strncmp(ares->referral, "ldaps://", 8) == 0) {
811                         tmp = ares->referral + 8;
812                 } else {
813                         return ldb_operr(ldb);
814                 }
815
816                 tmp = strchr(tmp, '/');
817                 if (tmp == NULL) {
818                         return ldb_operr(ldb);
819                 }
820                 tmp++;
821
822                 dn = ldb_dn_new(dsc, ldb, tmp);
823                 if (dn == NULL) {
824                         return ldb_oom(ldb);
825                 }
826
827                 flags = DSDB_FLAG_NEXT_MODULE |
828                         DSDB_SEARCH_SHOW_DELETED |
829                         DSDB_SEARCH_SHOW_EXTENDED_DN;
830
831                 ret = dsdb_module_search_tree(dsc->module, dsc, &res,
832                                         dn, LDB_SCOPE_BASE,
833                                         req->op.search.tree,
834                                         req->op.search.attrs,
835                                         flags, req);
836
837                 if (ret != LDB_SUCCESS) {
838                         talloc_free(dn);
839                         return ret;
840                 }
841
842                 if (res->count > 1) {
843                         char *ldbmsg = talloc_asprintf(dn, "LDB returned more than result for dn: %s", tmp);
844                         if (ldbmsg) {
845                                 ldb_set_errstring(ldb, ldbmsg);
846                         }
847                         talloc_free(dn);
848                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
849                 } else if (res->count == 0) {
850                         /* if nothing is returned then it means that we don't
851                         * have access to it.
852                         */
853                         return LDB_SUCCESS;
854                 }
855
856                 talloc_free(dn);
857                 /*
858                  * Fetch the objectGUID of the root of current NC
859                  */
860                 ret = dsdb_module_search_dn(dsc->module, dsc, &res2,
861                                         req->op.search.base,
862                                         attrs,
863                                         DSDB_FLAG_NEXT_MODULE, req);
864
865                 if (ret != LDB_SUCCESS) {
866                         return ret;
867                 }
868                 if (res2->msgs[0]->num_elements != 1) {
869                         ldb_set_errstring(ldb,
870                                           "More than 1 attribute returned while looking for objectGUID");
871                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
872                 }
873
874                 val = res2->msgs[0]->elements[0].values;
875                 ret = ldb_msg_add_value(res->msgs[0], "parentGUID", val, NULL);
876                 /*
877                  * It *very* important to steal otherwise as val is in a subcontext
878                  * related to res2, when the value will be one more time stolen
879                  * it's elements[x].values that will be stolen, so it's important to
880                  * recreate the context hierarchy as if it was done from a ldb_request
881                  */
882                 talloc_steal(res->msgs[0]->elements[0].values, val);
883                 if (ret != LDB_SUCCESS) {
884                         return ret;
885                 }
886                 return dirsync_filter_entry(req, res->msgs[0], res->controls, dsc, true);
887
888         case LDB_REPLY_DONE:
889                 /*
890                  * Let's add our own control
891                  */
892
893                 control = talloc_zero(ares->controls, struct ldb_dirsync_control);
894                 if (control == NULL) {
895                         return ldb_oom(ldb);
896                 }
897
898                 /*
899                  * When outputting flags is used to say more results.
900                  * For the moment we didn't honour the size info */
901
902                 control->flags = 0;
903
904                 /*
905                  * max_attribute is unused cf. 3.1.1.3.4.1.3 LDAP_SERVER_DIRSYNC_OID in MS-ADTS
906                  */
907
908                 control->max_attributes = 0;
909                 cookie = talloc_zero(control, struct ldapControlDirSyncCookie);
910                 if (cookie == NULL) {
911                         return ldb_oom(ldb);
912                 }
913
914                 if (!dsc->partial) {
915                         ret = dirsync_create_vector(req, ares, dsc, cookie, ldb);
916                         if (ret != LDB_SUCCESS) {
917                                 return ldb_module_done(dsc->req, NULL, NULL, ret);
918                         }
919                 }
920
921                 unix_to_nt_time(&now, time(NULL));
922                 cookie->blob.time = now;
923                 cookie->blob.highwatermark.highest_usn = dsc->highestUSN;
924                 cookie->blob.highwatermark.tmp_highest_usn = dsc->highestUSN;
925                 cookie->blob.guid1 = *(dsc->our_invocation_id);
926
927                 blob = talloc_zero(control, DATA_BLOB);
928                 if (blob == NULL) {
929                         return ldb_oom(ldb);
930                 }
931
932                 ndr_err = ndr_push_struct_blob(blob, blob, cookie,
933                                                 (ndr_push_flags_fn_t)ndr_push_ldapControlDirSyncCookie);
934
935                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
936                         ldb_set_errstring(ldb, "Can't marshall ldapControlDirSyncCookie struct");
937                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
938                 }
939                 control->cookie = (char *)blob->data;
940                 control->cookie_len = blob->length;
941                 ldb_reply_add_control(ares, LDB_CONTROL_DIRSYNC_OID, true, control);
942
943                 return ldb_module_done(dsc->req, ares->controls,
944                                        ares->response, LDB_SUCCESS);
945
946         }
947         return LDB_SUCCESS;
948 }
949
950 static int dirsync_ldb_search(struct ldb_module *module, struct ldb_request *req)
951 {
952         struct ldb_control *control;
953         struct ldb_result *acl_res;
954         struct ldb_dirsync_control *dirsync_ctl;
955         struct ldb_control *extended = NULL;
956         struct ldb_request *down_req;
957         struct dirsync_context *dsc;
958         struct ldb_context *ldb;
959         struct ldb_parse_tree *new_tree = req->op.search.tree;
960         enum ndr_err_code ndr_err;
961         DATA_BLOB blob;
962         const char **attrs;
963         int ret;
964
965
966         if (ldb_dn_is_special(req->op.search.base)) {
967                 return ldb_next_request(module, req);
968         }
969
970         /*
971          * check if there's a dirsync control
972          */
973         control = ldb_request_get_control(req, LDB_CONTROL_DIRSYNC_OID);
974         if (control == NULL) {
975                 /* not found go on */
976                 return ldb_next_request(module, req);
977         }
978
979         ldb = ldb_module_get_ctx(module);
980         /*
981          * This control must always be critical otherwise we return PROTOCOL error
982          */
983         if (!control->critical) {
984                 return ldb_operr(ldb);
985         }
986
987         dsc = talloc_zero(req, struct dirsync_context);
988         if (dsc == NULL) {
989                 return ldb_oom(ldb);
990         }
991         dsc->module = module;
992         dsc->req = req;
993         dsc->nbDefaultAttrs = 0;
994
995
996         dirsync_ctl = talloc_get_type(control->data, struct ldb_dirsync_control);
997         if (dirsync_ctl == NULL) {
998                 return ldb_error(ldb, LDB_ERR_PROTOCOL_ERROR, "No data in dirsync control");
999         }
1000
1001         ret = dsdb_find_nc_root(ldb, dsc, req->op.search.base, &dsc->nc_root);
1002         if (ret != LDB_SUCCESS) {
1003                 return ret;
1004         }
1005
1006         if (ldb_dn_compare(dsc->nc_root, req->op.search.base) != 0) {
1007                 if (dirsync_ctl->flags & LDAP_DIRSYNC_OBJECT_SECURITY) {
1008                         return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1009                                  "DN is not one of the naming context");
1010                 }
1011                 else {
1012                         return ldb_error(ldb, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS,
1013                                  "dN is not one of the naming context");
1014                 }
1015         }
1016
1017         if (!(dirsync_ctl->flags & LDAP_DIRSYNC_OBJECT_SECURITY)) {
1018                 struct dom_sid *sid;
1019                 struct security_descriptor *sd = NULL;
1020                 const char *acl_attrs[] = { "nTSecurityDescriptor", "objectSid", "objectClass", NULL };
1021                 const struct dsdb_schema *schema = NULL;
1022                 const struct dsdb_class *objectclass = NULL;
1023                 /*
1024                  * If we don't have the flag and if we have the "replicate directory change" granted
1025                  * then we upgrade ourself to system to not be blocked by the acl
1026                  */
1027                 /* FIXME we won't check the replicate directory change filtered attribute set
1028                  * it should be done so that if attr is not empty then we check that the user
1029                  * has also this right
1030                  */
1031
1032                 /*
1033                  * First change to system to get the SD of the root of current NC
1034                  * if we don't the acl_read will forbid us the right to read it ...
1035                  */
1036                 ret = dsdb_module_search_dn(module, dsc, &acl_res,
1037                                         req->op.search.base,
1038                                         acl_attrs,
1039                                         DSDB_FLAG_NEXT_MODULE|DSDB_FLAG_AS_SYSTEM, req);
1040
1041                 if (ret != LDB_SUCCESS) {
1042                         return ret;
1043                 }
1044
1045                 sid = samdb_result_dom_sid(dsc, acl_res->msgs[0], "objectSid");
1046                 /* sid can be null ... */
1047                 ret = dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(module), acl_res, acl_res->msgs[0], &sd);
1048
1049                 if (ret != LDB_SUCCESS) {
1050                         return ret;
1051                 }
1052                 schema = dsdb_get_schema(ldb, req);
1053                 if (!schema) {
1054                         return LDB_ERR_OPERATIONS_ERROR;
1055                 }
1056                 objectclass = dsdb_get_structural_oc_from_msg(schema, acl_res->msgs[0]);
1057
1058                 /*
1059                  * While we never use the answer to this for access
1060                  * control (after CVE-2023-4154), we return a
1061                  * different error message depending on if the user
1062                  * was granted GUID_DRS_GET_CHANGES to provide a closer
1063                  * emulation and keep some tests passing.
1064                  *
1065                  * (Samba's ACL logic is not well suited to redacting
1066                  * only the secret and RODC filtered attributes).
1067                  */
1068                 ret = acl_check_extended_right(dsc, module, req, objectclass,
1069                                                sd, acl_user_token(module),
1070                                                GUID_DRS_GET_CHANGES, SEC_ADS_CONTROL_ACCESS, sid);
1071
1072                 if (ret != LDB_SUCCESS) {
1073                         return ret;
1074                 }
1075                 talloc_free(acl_res);
1076         }
1077
1078         dsc->functional_level = dsdb_functional_level(ldb);
1079
1080         if (req->op.search.attrs) {
1081                 attrs = ldb_attr_list_copy(dsc, req->op.search.attrs);
1082                 if (attrs == NULL) {
1083                         return ldb_oom(ldb);
1084                 }
1085                 /*
1086                 * Check if we have only "dn" as attribute, if so then
1087                 * treat as if "*" was requested
1088                 */
1089                 if (attrs && attrs[0]) {
1090                         if (ldb_attr_cmp(attrs[0], "dn") == 0 && !attrs[1]) {
1091                                 attrs = talloc_array(dsc, const char*, 2);
1092                                 if (attrs == NULL) {
1093                                         return ldb_oom(ldb);
1094                                 }
1095                                 attrs[0] = "*";
1096                                 attrs[1] = NULL;
1097                         }
1098                 }
1099                 /*
1100                  * When returning all the attributes return also the SD as
1101                  * Windows does so.
1102                  */
1103                 if (ldb_attr_in_list(attrs, "*")) {
1104                         struct ldb_sd_flags_control *sdctr = talloc_zero(dsc, struct ldb_sd_flags_control);
1105                         sdctr->secinfo_flags = 0xF;
1106                         ret = ldb_request_add_control(req, LDB_CONTROL_SD_FLAGS_OID, false, sdctr);
1107                         if (ret != LDB_SUCCESS) {
1108                                 return ret;
1109                         }
1110                         attrs = ldb_attr_list_copy_add(dsc, attrs, "parentGUID");
1111                         if (attrs == NULL) {
1112                                 return ldb_oom(ldb);
1113                         }
1114                         attrs = ldb_attr_list_copy_add(dsc, attrs, "replPropertyMetaData");
1115                         if (attrs == NULL) {
1116                                 return ldb_oom(ldb);
1117                         }
1118                         /*
1119                         * When no attributes are asked we in any case expect at least 3 attributes:
1120                         * * instanceType
1121                         * * objectGUID
1122                         * * parentGUID
1123                         */
1124
1125                         dsc->nbDefaultAttrs = 3;
1126                 } else {
1127                         /*
1128                          * We will need this two attributes in the callback
1129                          */
1130                         attrs = ldb_attr_list_copy_add(dsc, attrs, "usnChanged");
1131                         if (attrs == NULL) {
1132                                 return ldb_operr(ldb);
1133                         }
1134                         attrs = ldb_attr_list_copy_add(dsc, attrs, "replPropertyMetaData");
1135                         if (attrs == NULL) {
1136                                 return ldb_operr(ldb);
1137                         }
1138
1139                         if (!ldb_attr_in_list(attrs, "instanceType")) {
1140                                 attrs = ldb_attr_list_copy_add(dsc, attrs, "instanceType");
1141                                 if (attrs == NULL) {
1142                                         return ldb_operr(ldb);
1143                                 }
1144                                 dsc->nbDefaultAttrs++;
1145                         }
1146
1147                         if (!ldb_attr_in_list(attrs, "objectGUID")) {
1148                                 attrs = ldb_attr_list_copy_add(dsc, attrs, "objectGUID");
1149                                 if (attrs == NULL) {
1150                                         return ldb_operr(ldb);
1151                                 }
1152                         }
1153                         /*
1154                          * Always increment the number of asked attributes as we don't care if objectGUID was asked
1155                          * or not for counting the number of "real" attributes returned.
1156                          */
1157                         dsc->nbDefaultAttrs++;
1158
1159                         if (!ldb_attr_in_list(attrs, "parentGUID")) {
1160                                 attrs = ldb_attr_list_copy_add(dsc, attrs, "parentGUID");
1161                                 if (attrs == NULL) {
1162                                         return ldb_operr(ldb);
1163                                 }
1164                         }
1165                         dsc->nbDefaultAttrs++;
1166
1167                 }
1168         } else {
1169                 struct ldb_sd_flags_control *sdctr = talloc_zero(dsc, struct ldb_sd_flags_control);
1170                 sdctr->secinfo_flags = 0xF;
1171                 ret = ldb_request_add_control(req, LDB_CONTROL_SD_FLAGS_OID, false, sdctr);
1172                 attrs = talloc_array(dsc, const char*, 4);
1173                 if (attrs == NULL) {
1174                         return ldb_operr(ldb);
1175                 }
1176                 attrs[0] = "*";
1177                 attrs[1] = "parentGUID";
1178                 attrs[2] = "replPropertyMetaData";
1179                 attrs[3] = NULL;
1180                 if (ret != LDB_SUCCESS) {
1181                         return ret;
1182                 }
1183                 /*
1184                  * When no attributes are asked we in anycase expect at least 3 attributes:
1185                  * * instanceType
1186                  * * objectGUID
1187                  * * parentGUID
1188                  */
1189
1190                 dsc->nbDefaultAttrs = 3;
1191         }
1192
1193         /* check if there's an extended dn control */
1194         extended = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
1195         if (extended != NULL) {
1196                 struct ldb_extended_dn_control *extended_ctrl = NULL;
1197
1198                 if (extended->data != NULL) {
1199                         extended_ctrl = talloc_get_type(extended->data,
1200                                                 struct ldb_extended_dn_control);
1201                 }
1202                 if (extended_ctrl != NULL) {
1203                         dsc->extended_type = extended_ctrl->type;
1204                 }
1205         } else {
1206                 ret = ldb_request_add_control(req, LDB_CONTROL_EXTENDED_DN_OID, false, NULL);
1207                 if (ret != LDB_SUCCESS) {
1208                         return ret;
1209                 }
1210                 dsc->noextended = true;
1211         }
1212
1213         if (ldb_request_get_control(req, LDB_CONTROL_REVEAL_INTERNALS) == NULL) {
1214                 ret = ldb_request_add_control(req, LDB_CONTROL_REVEAL_INTERNALS, false, NULL);
1215                 if (ret != LDB_SUCCESS) {
1216                         return ret;
1217                 }
1218         }
1219
1220         if (ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID) == NULL) {
1221                 ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_RECYCLED_OID, false, NULL);
1222                 if (ret != LDB_SUCCESS) {
1223                         return ret;
1224                 }
1225         }
1226
1227         if (ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID) == NULL) {
1228                 ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, false, NULL);
1229                 if (ret != LDB_SUCCESS) {
1230                         return ret;
1231                 }
1232         }
1233
1234         if (dirsync_ctl->flags & LDAP_DIRSYNC_INCREMENTAL_VALUES) {
1235                 dsc->linkIncrVal = true;
1236         } else {
1237                 dsc->linkIncrVal = false;
1238         }
1239
1240         dsc->our_invocation_id = samdb_ntds_invocation_id(ldb);
1241         if (dsc->our_invocation_id == NULL) {
1242                 return ldb_operr(ldb);
1243         }
1244
1245         if (dirsync_ctl->cookie_len > 0) {
1246                 struct ldapControlDirSyncCookie cookie;
1247
1248                 blob.data = (uint8_t *)dirsync_ctl->cookie;
1249                 blob.length = dirsync_ctl->cookie_len;
1250                 ndr_err = ndr_pull_struct_blob(&blob, dsc, &cookie,
1251                                                 (ndr_pull_flags_fn_t)ndr_pull_ldapControlDirSyncCookie);
1252
1253                 /* If we can't unmarshall the cookie into the correct structure we return
1254                 * unsupported critical extension
1255                 */
1256                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1257                         return ldb_error(ldb, LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION,
1258                                          "Unable to unmarshall cookie as a ldapControlDirSyncCookie structure");
1259                 }
1260
1261                 /*
1262                 * Let's search for the max usn within the cookie
1263                 */
1264                 if (GUID_equal(&(cookie.blob.guid1), dsc->our_invocation_id)) {
1265                         /*
1266                          * Ok, it's our invocation ID so we can treat the demand
1267                          * Let's take the highest usn from (tmp)highest_usn
1268                          */
1269                         dsc->fromreqUSN = cookie.blob.highwatermark.tmp_highest_usn;
1270                         dsc->localonly = true;
1271
1272                         if (cookie.blob.highwatermark.highest_usn > cookie.blob.highwatermark.tmp_highest_usn) {
1273                                 dsc->fromreqUSN = cookie.blob.highwatermark.highest_usn;
1274                         }
1275                 } else {
1276                         dsc->localonly = false;
1277                 }
1278                 if (cookie.blob.extra_length > 0 &&
1279                                 cookie.blob.extra.uptodateness_vector.ctr.ctr1.count > 0) {
1280                         struct drsuapi_DsReplicaCursor cursor;
1281                         uint32_t p;
1282                         for (p=0; p < cookie.blob.extra.uptodateness_vector.ctr.ctr1.count; p++) {
1283                                 cursor = cookie.blob.extra.uptodateness_vector.ctr.ctr1.cursors[p];
1284                                 if (GUID_equal( &(cursor.source_dsa_invocation_id), dsc->our_invocation_id)) {
1285                                         if (cursor.highest_usn > dsc->fromreqUSN) {
1286                                                 dsc->fromreqUSN = cursor.highest_usn;
1287                                         }
1288                                 }
1289                         }
1290                         dsc->cursors = talloc_steal(dsc,
1291                                         cookie.blob.extra.uptodateness_vector.ctr.ctr1.cursors);
1292                         if (dsc->cursors == NULL) {
1293                                 return ldb_oom(ldb);
1294                         }
1295                         dsc->cursor_size = p;
1296                 }
1297         }
1298
1299         DEBUG(4, ("Dirsync: searching with min usn > %llu\n",
1300                                 (long long unsigned int)dsc->fromreqUSN));
1301         if (dsc->fromreqUSN > 0) {
1302                 /* FIXME it would be better to use PRId64 */
1303                 char *expression = talloc_asprintf(dsc, "(&%s(uSNChanged>=%llu))",
1304                                                         ldb_filter_from_tree(dsc,
1305                                                              req->op.search.tree),
1306                                                         (long long unsigned int)(dsc->fromreqUSN + 1));
1307
1308                 if (expression == NULL) {
1309                         return ldb_oom(ldb);
1310                 }
1311                 new_tree = ldb_parse_tree(req, expression);
1312                 if (new_tree == NULL) {
1313                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
1314                                         "Problem while parsing tree");
1315                 }
1316
1317         }
1318         /*
1319          * Mark dirsync control as uncritical (done)
1320          *
1321          * We need this so ranged_results knows how to behave with
1322          * dirsync
1323          */
1324         control->critical = false;
1325         dsc->schema = dsdb_get_schema(ldb, dsc);
1326         /*
1327          * At the beginning we make the hypothesis that we will return a
1328          * complete result set.
1329          */
1330
1331         dsc->partial = false;
1332
1333         /*
1334          * 3.1.1.3.4.1.3 of MS-ADTS.pdf specify that if the scope is not subtree
1335          * we treat the search as if subtree was specified
1336          */
1337
1338         ret = ldb_build_search_req_ex(&down_req, ldb, dsc,
1339                                       req->op.search.base,
1340                                       LDB_SCOPE_SUBTREE,
1341                                       new_tree,
1342                                       attrs,
1343                                       req->controls,
1344                                       dsc, dirsync_search_callback,
1345                                       req);
1346         LDB_REQ_SET_LOCATION(down_req);
1347         if (ret != LDB_SUCCESS) {
1348                 return ret;
1349         }
1350         /* perform the search */
1351         return ldb_next_request(module, down_req);
1352 }
1353
1354 static int dirsync_ldb_init(struct ldb_module *module)
1355 {
1356         int ret;
1357
1358         ret = ldb_mod_register_control(module, LDB_CONTROL_DIRSYNC_OID);
1359         if (ret != LDB_SUCCESS) {
1360                 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
1361                         "dirsync: Unable to register control with rootdse!\n");
1362                 return ldb_operr(ldb_module_get_ctx(module));
1363         }
1364
1365         return ldb_next_init(module);
1366 }
1367
1368 static const struct ldb_module_ops ldb_dirsync_ldb_module_ops = {
1369         .name              = "dirsync",
1370         .search            = dirsync_ldb_search,
1371         .init_context      = dirsync_ldb_init,
1372 };
1373
1374 /*
1375   initialise the module
1376  */
1377 _PUBLIC_ int ldb_dirsync_module_init(const char *version)
1378 {
1379         int ret;
1380         LDB_MODULE_CHECK_VERSION(version);
1381         ret = ldb_register_module(&ldb_dirsync_ldb_module_ops);
1382         return ret;
1383 }