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