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