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