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