s3:smbd: consistently use talloc_tos() memory for rpc_pipe_open_interface()
[Samba.git] / source4 / dsdb / samdb / ldb_modules / dirsync.c
blobb5510eccd24673b8000d651213978c0c6323a136
1 /*
2 SAMDB control module
4 Copyright (C) Matthieu Patou <mat@matws.net> 2011
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.
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.
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/>.
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"
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
38 struct dirsync_context {
39 struct ldb_module *module;
40 struct ldb_request *req;
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
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;
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)
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;
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).
100 if ( dsc->noextended == true ) {
101 myaccept = emptyaccept;
102 } else {
103 myaccept = extendedaccept;
105 ldb = ldb_module_get_ctx(dsc->module);
107 if (msg->num_elements == 0) {
109 * Entry that we don't really have access to
111 return LDB_SUCCESS;
113 ldb_dn_extended_filter(msg->dn, myaccept);
116 * If the RDN starts with CN then the CN attribute is never returned
118 rdn = ldb_dn_get_rdn_name(msg->dn);
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.
125 if (referral == true &&
126 ldb_attr_in_list(req->op.search.attrs, "objectGUID") &&
127 dsc->fromreqUSN == 0) {
128 delta++;
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.
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.
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
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
153 newmsg = talloc_zero(dsc->req, struct ldb_message);
154 if (newmsg == NULL) {
155 return ldb_oom(ldb);
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
164 val = strtoull((const char*)msg->elements[i].values[0].data, NULL, 0);
165 continue;
168 if (ldb_attr_cmp(msg->elements[i].name,
169 "replPropertyMetaData") == 0) {
170 replMetaData = (talloc_steal(dsc, &msg->elements[i].values[0]));
171 continue;
175 if (replMetaData == NULL) {
176 bool guidfound = false;
179 * We are in the case of deleted object where we don't have the
180 * right to read it.
182 if (!ldb_msg_find_attr_as_uint(msg, "isDeleted", 0)) {
184 * This is not a deleted item and we don't
185 * have the replPropertyMetaData.
186 * Do not return it
188 return LDB_SUCCESS;
190 newmsg->dn = ldb_dn_new(newmsg, ldb, "");
191 if (newmsg->dn == NULL) {
192 return ldb_oom(ldb);
195 el = ldb_msg_find_element(msg, "objectGUID");
196 if ( el != NULL) {
197 guidfound = true;
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
204 SMB_ASSERT(guidfound == true);
206 if (guidfound == false) {
207 struct GUID guid;
208 struct ldb_val *new_val;
209 DATA_BLOB guid_blob;
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);
216 txt++;
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);
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);
228 new_val = talloc(msg, struct ldb_val);
229 if (new_val == NULL) {
230 return ldb_oom(ldb);
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);
239 ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD);
240 talloc_steal(newmsg->elements, el->name);
241 talloc_steal(newmsg->elements, el->values);
243 talloc_steal(newmsg->elements, msg);
244 return ldb_module_send_entry(dsc->req, msg, controls);
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);
253 if (ldb_attr_in_list(req->op.search.attrs, "name") ||
254 ldb_attr_in_list(req->op.search.attrs, "*")) {
255 nameasked = true;
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.
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
270 delta++;
273 if (dsc->fromreqUSN > 0 || dsc->cursors != NULL) {
274 j = 0;
276 * Allocate an array of size(replMetaData) of char*
277 * we know that it will be oversized but it's a short lived element
279 listAttr = talloc_array(msg, const char*, rmd.ctr.ctr1.count + 1);
280 if (listAttr == NULL) {
281 return ldb_oom(ldb);
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) {
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.
302 goto skip;
305 /* If we are here it's because we have a usn > (max(usn of vectors))*/
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++;
315 listAttr[j] = a->lDAPDisplayName;
316 j++;
317 skip:
318 continue;
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;
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.
338 for (i = msg->num_elements - 1; i >= 0; i--) {
339 const char *ldapattrname;
341 el = &(msg->elements[i]);
342 ldapattrname = el->name;
344 attr = dsdb_attribute_by_lDAPDisplayName(dsc->schema,
345 el->name);
346 keep = false;
348 if (attr->linkID & 1) {
350 * Attribute is a backlink so let's remove it
352 continue;
355 if (ldb_attr_cmp(msg->elements[i].name,
356 "replPropertyMetaData") == 0) {
357 continue;
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) {
364 * Attribute is constructed or not replicated, let's get rid of it
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
372 if (namereturned == false &&
373 (ldb_attr_cmp(attr->lDAPDisplayName, "parentGUID") == 0)) {
374 delta++;
375 continue;
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");
382 talloc_steal(newmsg->elements, el->name);
383 talloc_steal(newmsg->elements, el->values);
384 continue;
388 if (ldb_attr_cmp(msg->elements[i].name, rdn) == 0) {
390 * We have an attribute that is the same as the start of the RDN
391 * (ie. attribute CN with rdn CN=).
393 continue;
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");
402 talloc_steal(newmsg->elements, el->name);
403 talloc_steal(newmsg->elements, el->values);
404 continue;
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
413 if (dsc->functional_level > DS_DOMAIN_FUNCTION_2000 &&
414 attr->linkID != 0 ) {
415 int k;
417 * Elements for incremental changes on linked attributes
419 struct ldb_message_element *el_incr_add = NULL;
420 struct ldb_message_element *el_incr_del = NULL;
422 * Attribute is a forwardlink so let's remove it
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);
438 copydn = ldb_dn_copy(msg, dn->dn);
439 if (copydn == NULL) {
440 ldb_oom(ldb);
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);
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);
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);
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);
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)
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);
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);
481 el->values[k].length = strlen(dn_ln);
484 if (tmp_usn > dsc->fromreqUSN) {
485 if (!dsc->localonly) {
486 struct drsuapi_DsReplicaCursor *tab = dsc->cursors;
487 uint32_t l;
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) {
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.
500 goto skip_link;
503 /* If we are here it's because we have a usn > (max(usn of vectors))*/
504 keep = true;
505 } else {
506 keep = true;
508 /* If we are here it's because the link is more recent than either any
509 * originating usn or local usn
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;
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);
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;
537 tmpel = el_incr_del;
538 } else {
539 tmpel = el_incr_add;
542 if (tmpel == NULL) {
543 tmpel = talloc_zero(newmsg, struct ldb_message_element);
544 if (tmpel == NULL) {
545 return ldb_oom(ldb);
547 tmpel->values = talloc_array(tmpel, struct ldb_val, 1);
548 if (tmpel->values == NULL) {
549 return ldb_oom(ldb);
551 if (flags & DSDB_RMD_FLAG_DELETED) {
552 tmpel->name = talloc_asprintf(tmpel,
553 "%s;range=0-0",
554 el->name);
556 else {
557 tmpel->name = talloc_asprintf(tmpel,
558 "%s;range=1-1",
559 el->name);
561 if (tmpel->name == NULL) {
562 return ldb_oom(ldb);
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);
575 tmpel->values[tmpel->num_values -1].data =talloc_steal(tmpel->values, el->values[k].data);
576 tmpel->values[tmpel->num_values -1].length = el->values[k].length;
578 if (flags & DSDB_RMD_FLAG_DELETED) {
579 el_incr_del = tmpel;
580 } else {
581 el_incr_add = tmpel;
586 if (dsc->linkIncrVal == false) {
587 if (flags & DSDB_RMD_FLAG_DELETED) {
588 if (k < (el->num_values - 1)) {
589 memmove(el->values + k,
590 el->values + (k + 1),
591 ((el->num_values - 1) - k)*sizeof(*el->values));
593 el->num_values--;
596 skip_link:
597 talloc_free(dn);
600 if (keep == true) {
601 if (dsc->linkIncrVal == false) {
602 if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
603 return ldb_error(ldb,
604 LDB_ERR_OPERATIONS_ERROR,
605 "Unable to add attribute");
607 talloc_steal(newmsg->elements, el->name);
608 talloc_steal(newmsg->elements, el->values);
609 } else {
610 if (el_incr_del) {
611 if (ldb_msg_add(newmsg, el_incr_del, LDB_FLAG_MOD_ADD))
612 return ldb_error(ldb,
613 LDB_ERR_OPERATIONS_ERROR,
614 "Unable to add attribute");
616 if (el_incr_add) {
617 if (ldb_msg_add(newmsg, el_incr_add, LDB_FLAG_MOD_ADD))
618 return ldb_error(ldb,
619 LDB_ERR_OPERATIONS_ERROR,
620 "Unable to add attribute");
624 continue;
627 if (listAttr) {
628 for (j=0; j<size; j++) {
630 * We mark attribute that has already been seen well
631 * as seen. So that after attribute that are still in
632 * listAttr are attributes that has been modified after
633 * the requested USN but not present in the attributes
634 * returned by the ldb search.
635 * That is to say attributes that have been removed
637 if (listAttr[j] && ldb_attr_cmp(listAttr[j], ldapattrname) == 0) {
638 listAttr[j] = NULL;
639 keep = true;
640 continue;
643 } else {
644 keep = true;
647 if (keep == true) {
648 if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
649 return ldb_error(ldb,
650 LDB_ERR_OPERATIONS_ERROR,
651 "Unable to add attribute");
653 talloc_steal(newmsg->elements, el->name);
654 talloc_steal(newmsg->elements, el->values);
655 continue;
658 talloc_steal(newmsg->elements, msg);
661 * Here we run through the list of attributes returned
662 * in the propertyMetaData.
663 * Entries of this list have usn > requested_usn,
664 * entries that are also present in the message have been
665 * replaced by NULL, so at this moment the list contains
666 * only elements that have a usn > requested_usn and that
667 * haven't been seen. It's attributes that were removed.
668 * We add them to the message like empty elements.
670 for (j=0; j<size; j++) {
671 if (listAttr[j] && (
672 ldb_attr_in_list(req->op.search.attrs, "*") ||
673 ldb_attr_in_list(req->op.search.attrs, listAttr[j])) &&
674 (ldb_attr_cmp(listAttr[j], rdn) != 0) &&
675 (ldb_attr_cmp(listAttr[j], "instanceType") != 0)) {
676 ldb_msg_add_empty(newmsg, listAttr[j], LDB_FLAG_MOD_DELETE, NULL);
679 talloc_free(listAttr);
681 if ((newmsg->num_elements - ( dsc->nbDefaultAttrs - delta)) > 0) {
683 * After cleaning attributes there is still some attributes that were not added just
684 * for the purpose of the control (objectGUID, instanceType, ...)
687 newmsg->dn = talloc_steal(newmsg, msg->dn);
688 if (val > dsc->highestUSN) {
689 dsc->highestUSN = val;
691 return ldb_module_send_entry(dsc->req, newmsg, controls);
692 } else {
693 talloc_free(newmsg);
694 return LDB_SUCCESS;
699 static int dirsync_create_vector(struct ldb_request *req,
700 struct ldb_reply *ares,
701 struct dirsync_context *dsc,
702 struct ldapControlDirSyncCookie *cookie,
703 struct ldb_context *ldb)
705 struct ldb_result *resVector;
706 const char* attrVector[] = {"replUpToDateVector", NULL };
707 uint64_t highest_usn;
708 uint32_t count = 1;
709 int ret;
710 struct drsuapi_DsReplicaCursor *tab;
712 ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ, &highest_usn);
713 if (ret != LDB_SUCCESS) {
714 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, "Unable to get highest USN from current NC");
717 /* If we have a full answer then the highest USN
718 * is not the highest USN from the result set but the
719 * highest of the naming context, unless the sequence is not updated yet.
721 if (highest_usn > dsc->highestUSN) {
722 dsc->highestUSN = highest_usn;
726 ret = dsdb_module_search_dn(dsc->module, dsc, &resVector,
727 dsc->nc_root,
728 attrVector,
729 DSDB_FLAG_NEXT_MODULE, req);
730 if (ret != LDB_SUCCESS) {
731 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
732 "Unable to get replUpToDateVector for current NC");
735 if (resVector->count != 0) {
736 DATA_BLOB blob;
737 uint32_t i;
738 struct ldb_message_element *el = ldb_msg_find_element(resVector->msgs[0], "replUpToDateVector");
739 if (el) {
740 enum ndr_err_code ndr_err;
741 struct replUpToDateVectorBlob utd;
742 blob.data = el->values[0].data;
743 blob.length = el->values[0].length;
744 ndr_err = ndr_pull_struct_blob(&blob, dsc, &utd,
745 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
747 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
748 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
749 "Unable to pull replUpToDateVectorBlob structure");
753 count += utd.ctr.ctr2.count;
754 tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
755 if (tab == NULL) {
756 return ldb_oom(ldb);
758 for (i=1; i < count; i++) {
759 memset(&tab[i], 0, sizeof(struct drsuapi_DsReplicaCursor));
760 tab[i].highest_usn = utd.ctr.ctr2.cursors[i-1].highest_usn;
761 tab[i].source_dsa_invocation_id = utd.ctr.ctr2.cursors[i-1].source_dsa_invocation_id;
763 } else {
764 tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
765 if (tab == NULL) {
766 return ldb_oom(ldb);
769 } else {
771 * No replUpToDateVector ? it happens quite often (1 DC,
772 * other DCs didn't update ...
774 tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
775 if (tab == NULL) {
776 return ldb_oom(ldb);
779 /* Our vector is always the first */
780 tab[0].highest_usn = dsc->highestUSN;
781 tab[0].source_dsa_invocation_id = *(dsc->our_invocation_id);
784 /* We have to add the updateness vector that we have*/
785 /* Version is always 1 in dirsync cookies */
786 cookie->blob.extra.uptodateness_vector.version = 1;
787 cookie->blob.extra.uptodateness_vector.reserved = 0;
788 cookie->blob.extra.uptodateness_vector.ctr.ctr1.count = count;
789 cookie->blob.extra.uptodateness_vector.ctr.ctr1.reserved = 0;
790 cookie->blob.extra.uptodateness_vector.ctr.ctr1.cursors = tab;
792 return LDB_SUCCESS;
795 static int dirsync_search_callback(struct ldb_request *req, struct ldb_reply *ares)
797 int ret;
798 struct dirsync_context *dsc;
799 struct ldb_result *res, *res2;
800 struct ldb_dirsync_control *control;
801 struct ldapControlDirSyncCookie *cookie;
802 struct ldb_context *ldb;
803 struct ldb_dn *dn;
804 struct ldb_val *val;
805 DATA_BLOB *blob;
806 NTTIME now;
807 const char *attrs[] = { "objectGUID", NULL };
808 enum ndr_err_code ndr_err;
809 char *tmp;
810 uint32_t flags;
812 dsc = talloc_get_type_abort(req->context, struct dirsync_context);
813 ldb = ldb_module_get_ctx(dsc->module);
814 if (!ares) {
815 return ldb_module_done(dsc->req, NULL, NULL,
816 LDB_ERR_OPERATIONS_ERROR);
818 if (ares->error != LDB_SUCCESS) {
819 return ldb_module_done(dsc->req, ares->controls,
820 ares->response, ares->error);
823 switch (ares->type) {
824 case LDB_REPLY_ENTRY:
825 return dirsync_filter_entry(req, ares->message, ares->controls, dsc, false);
827 case LDB_REPLY_REFERRAL:
828 /* Skip the ldap(s):// so up to 8 chars,
829 * we don't care to be precise as the goal is to be in
830 * the name of DC, then we search the next '/'
831 * as it will be the last char before the DN of the referal
833 if (strncmp(ares->referral, "ldap://", 7) == 0) {
834 tmp = ares->referral + 7;
835 } else if (strncmp(ares->referral, "ldaps://", 8) == 0) {
836 tmp = ares->referral + 8;
837 } else {
838 return ldb_operr(ldb);
841 tmp = strchr(tmp, '/');
842 tmp++;
844 dn = ldb_dn_new(dsc, ldb, tmp);
845 if (dn == NULL) {
846 return ldb_oom(ldb);
849 flags = DSDB_FLAG_NEXT_MODULE |
850 DSDB_SEARCH_SHOW_DELETED |
851 DSDB_SEARCH_SHOW_EXTENDED_DN;
853 if (dsc->assystem) {
854 flags = flags | DSDB_FLAG_AS_SYSTEM;
857 ret = dsdb_module_search_tree(dsc->module, dsc, &res,
858 dn, LDB_SCOPE_BASE,
859 req->op.search.tree,
860 req->op.search.attrs,
861 flags, req);
863 if (ret != LDB_SUCCESS) {
864 talloc_free(dn);
865 return ret;
868 if (res->count > 1) {
869 char *ldbmsg = talloc_asprintf(dn, "LDB returned more than result for dn: %s", tmp);
870 if (ldbmsg) {
871 ldb_set_errstring(ldb, ldbmsg);
873 talloc_free(dn);
874 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
875 } else if (res->count == 0) {
876 /* if nothing is returned then it means that we don't
877 * have access to it.
879 return LDB_SUCCESS;
882 talloc_free(dn);
884 * Fetch the objectGUID of the root of current NC
886 ret = dsdb_module_search_dn(dsc->module, dsc, &res2,
887 req->op.search.base,
888 attrs,
889 DSDB_FLAG_NEXT_MODULE, req);
891 if (ret != LDB_SUCCESS) {
892 return ret;
894 if (res2->msgs[0]->num_elements != 1) {
895 ldb_set_errstring(ldb,
896 "More than 1 attribute returned while looking for objectGUID");
897 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
900 val = res2->msgs[0]->elements[0].values;
901 ret = ldb_msg_add_value(res->msgs[0], "parentGUID", val, NULL);
903 * It *very* important to steal otherwise as val is in a subcontext
904 * related to res2, when the value will be one more time stolen
905 * it's elements[x].values that will be stolen, so it's important to
906 * recreate the context hierrachy as if it was done from a ldb_request
908 talloc_steal(res->msgs[0]->elements[0].values, val);
909 if (ret != LDB_SUCCESS) {
910 return ret;
912 return dirsync_filter_entry(req, res->msgs[0], res->controls, dsc, true);
914 case LDB_REPLY_DONE:
916 * Let's add our own control
919 control = talloc_zero(ares->controls, struct ldb_dirsync_control);
920 if (control == NULL) {
921 return ldb_oom(ldb);
925 * When outputing flags is used to say more results.
926 * For the moment we didn't honnor the size info */
928 control->flags = 0;
931 * max_attribute is unused cf. 3.1.1.3.4.1.3 LDAP_SERVER_DIRSYNC_OID in MS-ADTS
934 control->max_attributes = 0;
935 cookie = talloc_zero(control, struct ldapControlDirSyncCookie);
936 if (cookie == NULL) {
937 return ldb_oom(ldb);
940 if (!dsc->partial) {
941 ret = dirsync_create_vector(req, ares, dsc, cookie, ldb);
942 if (ret != LDB_SUCCESS) {
943 return ldb_module_done(dsc->req, NULL, NULL, ret);
947 unix_to_nt_time(&now, time(NULL));
948 cookie->blob.time = now;
949 cookie->blob.highwatermark.highest_usn = dsc->highestUSN;
950 cookie->blob.highwatermark.tmp_highest_usn = dsc->highestUSN;
951 cookie->blob.guid1 = *(dsc->our_invocation_id);
953 blob = talloc_zero(control, DATA_BLOB);
954 if (blob == NULL) {
955 return ldb_oom(ldb);
958 ndr_err = ndr_push_struct_blob(blob, blob, cookie,
959 (ndr_push_flags_fn_t)ndr_push_ldapControlDirSyncCookie);
961 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
962 ldb_set_errstring(ldb, "Can't marshall ldapControlDirSyncCookie struct");
963 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
965 control->cookie = (char *)blob->data;
966 control->cookie_len = blob->length;
967 ldb_reply_add_control(ares, LDB_CONTROL_DIRSYNC_OID, true, control);
969 return ldb_module_done(dsc->req, ares->controls,
970 ares->response, LDB_SUCCESS);
973 return LDB_SUCCESS;
976 static int dirsync_ldb_search(struct ldb_module *module, struct ldb_request *req)
978 struct ldb_control *control;
979 struct ldb_result *acl_res;
980 struct ldb_dirsync_control *dirsync_ctl;
981 struct ldb_request *down_req;
982 struct dirsync_context *dsc;
983 struct ldb_context *ldb;
984 struct ldb_parse_tree *new_tree = req->op.search.tree;
985 uint32_t flags = 0;
986 enum ndr_err_code ndr_err;
987 DATA_BLOB blob;
988 const char **attrs;
989 int ret;
992 if (ldb_dn_is_special(req->op.search.base)) {
993 return ldb_next_request(module, req);
997 * check if there's an extended dn control
999 control = ldb_request_get_control(req, LDB_CONTROL_DIRSYNC_OID);
1000 if (control == NULL) {
1001 /* not found go on */
1002 return ldb_next_request(module, req);
1005 ldb = ldb_module_get_ctx(module);
1007 * This control must always be critical otherwise we return PROTOCOL error
1009 if (!control->critical) {
1010 return ldb_operr(ldb);
1013 dsc = talloc_zero(req, struct dirsync_context);
1014 if (dsc == NULL) {
1015 return ldb_oom(ldb);
1017 dsc->module = module;
1018 dsc->req = req;
1019 dsc->nbDefaultAttrs = 0;
1022 dirsync_ctl = talloc_get_type(control->data, struct ldb_dirsync_control);
1023 if (dirsync_ctl == NULL) {
1024 return ldb_error(ldb, LDB_ERR_PROTOCOL_ERROR, "No data in dirsync control");
1027 ret = dsdb_find_nc_root(ldb, dsc, req->op.search.base, &dsc->nc_root);
1028 if (ret != LDB_SUCCESS) {
1029 return ret;
1032 if (ldb_dn_compare(dsc->nc_root, req->op.search.base) != 0) {
1033 if (dirsync_ctl->flags & LDAP_DIRSYNC_OBJECT_SECURITY) {
1034 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1035 "DN is not one of the naming context");
1037 else {
1038 return ldb_error(ldb, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS,
1039 "dN is not one of the naming context");
1043 if (!(dirsync_ctl->flags & LDAP_DIRSYNC_OBJECT_SECURITY)) {
1044 struct dom_sid *sid;
1045 struct security_descriptor *sd = NULL;
1046 const char *acl_attrs[] = { "nTSecurityDescriptor", "objectSid", NULL };
1048 * If we don't have the flag and if we have the "replicate directory change" granted
1049 * then we upgrade ourself to system to not be blocked by the acl
1051 /* FIXME we won't check the replicate directory change filtered attribute set
1052 * it should be done so that if attr is not empty then we check that the user
1053 * has also this right
1057 * First change to system to get the SD of the root of current NC
1058 * if we don't the acl_read will forbid us the right to read it ...
1060 ret = dsdb_module_search_dn(module, dsc, &acl_res,
1061 req->op.search.base,
1062 acl_attrs,
1063 DSDB_FLAG_NEXT_MODULE|DSDB_FLAG_AS_SYSTEM, req);
1065 if (ret != LDB_SUCCESS) {
1066 return ret;
1069 sid = samdb_result_dom_sid(dsc, acl_res->msgs[0], "objectSid");
1070 /* sid can be null ... */
1071 ret = dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(module), acl_res, acl_res->msgs[0], &sd);
1073 if (ret != LDB_SUCCESS) {
1074 return ret;
1076 ret = acl_check_extended_right(dsc, sd, acl_user_token(module), GUID_DRS_GET_CHANGES, SEC_ADS_CONTROL_ACCESS, sid);
1078 if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
1079 return ret;
1081 dsc->assystem = true;
1082 ret = ldb_request_add_control(req, LDB_CONTROL_AS_SYSTEM_OID, false, NULL);
1084 if (ret != LDB_SUCCESS) {
1085 return ret;
1087 talloc_free(acl_res);
1088 } else {
1089 flags |= DSDB_ACL_CHECKS_DIRSYNC_FLAG;
1091 if (ret != LDB_SUCCESS) {
1092 return ret;
1097 dsc->functional_level = dsdb_functional_level(ldb);
1099 if (req->op.search.attrs) {
1100 attrs = ldb_attr_list_copy(dsc, req->op.search.attrs);
1101 if (attrs == NULL) {
1102 return ldb_oom(ldb);
1105 * Check if we have only "dn" as attribute, if so then
1106 * treat as if "*" was requested
1108 if (attrs && attrs[0]) {
1109 if (ldb_attr_cmp(attrs[0], "dn") == 0 && !attrs[1]) {
1110 attrs = talloc_array(dsc, const char*, 2);
1111 if (attrs == NULL) {
1112 return ldb_oom(ldb);
1114 attrs[0] = "*";
1115 attrs[1] = NULL;
1119 * When returning all the attributes return also the SD as
1120 * Windws do so.
1122 if (ldb_attr_in_list(attrs, "*")) {
1123 struct ldb_sd_flags_control *sdctr = talloc_zero(dsc, struct ldb_sd_flags_control);
1124 sdctr->secinfo_flags = 0xF;
1125 ret = ldb_request_add_control(req, LDB_CONTROL_SD_FLAGS_OID, false, sdctr);
1126 if (ret != LDB_SUCCESS) {
1127 return ret;
1129 attrs = ldb_attr_list_copy_add(dsc, attrs, "parentGUID");
1130 if (attrs == NULL) {
1131 return ldb_oom(ldb);
1133 attrs = ldb_attr_list_copy_add(dsc, attrs, "replPropertyMetaData");
1134 if (attrs == NULL) {
1135 return ldb_oom(ldb);
1138 * When no attributes are asked we in anycase expect at least 3 attributes:
1139 * * instanceType
1140 * * objectGUID
1141 * * parentGUID
1144 dsc->nbDefaultAttrs = 3;
1145 } else {
1147 * We will need this two attributes in the callback
1149 attrs = ldb_attr_list_copy_add(dsc, attrs, "usnChanged");
1150 if (attrs == NULL) {
1151 return ldb_operr(ldb);
1153 attrs = ldb_attr_list_copy_add(dsc, attrs, "replPropertyMetaData");
1154 if (attrs == NULL) {
1155 return ldb_operr(ldb);
1158 if (!ldb_attr_in_list(attrs, "instanceType")) {
1159 attrs = ldb_attr_list_copy_add(dsc, attrs, "instanceType");
1160 if (attrs == NULL) {
1161 return ldb_operr(ldb);
1163 dsc->nbDefaultAttrs++;
1166 if (!ldb_attr_in_list(attrs, "objectGUID")) {
1167 attrs = ldb_attr_list_copy_add(dsc, attrs, "objectGUID");
1168 if (attrs == NULL) {
1169 return ldb_operr(ldb);
1173 * Always increment the number of asked attributes as we don't care if objectGUID was asked
1174 * or not for counting the number of "real" attributes returned.
1176 dsc->nbDefaultAttrs++;
1178 if (!ldb_attr_in_list(attrs, "parentGUID")) {
1179 attrs = ldb_attr_list_copy_add(dsc, attrs, "parentGUID");
1180 if (attrs == NULL) {
1181 return ldb_operr(ldb);
1184 dsc->nbDefaultAttrs++;
1187 } else {
1188 struct ldb_sd_flags_control *sdctr = talloc_zero(dsc, struct ldb_sd_flags_control);
1189 sdctr->secinfo_flags = 0xF;
1190 ret = ldb_request_add_control(req, LDB_CONTROL_SD_FLAGS_OID, false, sdctr);
1191 attrs = talloc_array(dsc, const char*, 4);
1192 if (attrs == NULL) {
1193 return ldb_operr(ldb);
1195 attrs[0] = "*";
1196 attrs[1] = "parentGUID";
1197 attrs[2] = "replPropertyMetaData";
1198 attrs[3] = NULL;
1199 if (ret != LDB_SUCCESS) {
1200 return ret;
1203 * When no attributes are asked we in anycase expect at least 3 attributes:
1204 * * instanceType
1205 * * objectGUID
1206 * * parentGUID
1209 dsc->nbDefaultAttrs = 3;
1212 if (!ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID)) {
1213 ret = ldb_request_add_control(req, LDB_CONTROL_EXTENDED_DN_OID, false, NULL);
1214 if (ret != LDB_SUCCESS) {
1215 return ret;
1217 dsc->noextended = true;
1220 if (ldb_request_get_control(req, LDB_CONTROL_REVEAL_INTERNALS) == NULL) {
1221 ret = ldb_request_add_control(req, LDB_CONTROL_REVEAL_INTERNALS, false, NULL);
1222 if (ret != LDB_SUCCESS) {
1223 return ret;
1227 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID) == NULL) {
1228 ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_RECYCLED_OID, false, NULL);
1229 if (ret != LDB_SUCCESS) {
1230 return ret;
1234 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID) == NULL) {
1235 ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, false, NULL);
1236 if (ret != LDB_SUCCESS) {
1237 return ret;
1241 if (dirsync_ctl->flags & LDAP_DIRSYNC_INCREMENTAL_VALUES) {
1242 dsc->linkIncrVal = true;
1243 } else {
1244 dsc->linkIncrVal = false;
1247 dsc->our_invocation_id = samdb_ntds_invocation_id(ldb);
1248 if (dsc->our_invocation_id == NULL) {
1249 return ldb_operr(ldb);
1252 if (dirsync_ctl->cookie_len > 0) {
1253 struct ldapControlDirSyncCookie cookie;
1255 blob.data = (uint8_t *)dirsync_ctl->cookie;
1256 blob.length = dirsync_ctl->cookie_len;
1257 ndr_err = ndr_pull_struct_blob(&blob, dsc, &cookie,
1258 (ndr_pull_flags_fn_t)ndr_pull_ldapControlDirSyncCookie);
1260 /* If we can't unmarshall the cookie into the correct structure we return
1261 * unsupported critical extension
1263 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1264 return ldb_error(ldb, LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION,
1265 "Unable to unmarshall cookie as a ldapControlDirSyncCookie structure");
1269 * Let's search for the max usn withing the cookie
1271 if (GUID_equal(&(cookie.blob.guid1), dsc->our_invocation_id)) {
1273 * Ok, it's our invocation ID so we can treat the demand
1274 * Let's take the highest usn from (tmp)highest_usn
1276 dsc->fromreqUSN = cookie.blob.highwatermark.tmp_highest_usn;
1277 dsc->localonly = true;
1279 if (cookie.blob.highwatermark.highest_usn > cookie.blob.highwatermark.tmp_highest_usn) {
1280 dsc->fromreqUSN = cookie.blob.highwatermark.highest_usn;
1282 } else {
1283 dsc->localonly = false;
1285 if (cookie.blob.extra_length > 0 &&
1286 cookie.blob.extra.uptodateness_vector.ctr.ctr1.count > 0) {
1287 struct drsuapi_DsReplicaCursor cursor;
1288 uint32_t p;
1289 for (p=0; p < cookie.blob.extra.uptodateness_vector.ctr.ctr1.count; p++) {
1290 cursor = cookie.blob.extra.uptodateness_vector.ctr.ctr1.cursors[p];
1291 if (GUID_equal( &(cursor.source_dsa_invocation_id), dsc->our_invocation_id)) {
1292 if (cursor.highest_usn > dsc->fromreqUSN) {
1293 dsc->fromreqUSN = cursor.highest_usn;
1297 dsc->cursors = talloc_steal(dsc,
1298 cookie.blob.extra.uptodateness_vector.ctr.ctr1.cursors);
1299 if (dsc->cursors == NULL) {
1300 return ldb_oom(ldb);
1302 dsc->cursor_size = p;
1306 DEBUG(4, ("Dirsync: searching with min usn > %llu\n",
1307 (long long unsigned int)dsc->fromreqUSN));
1308 if (dsc->fromreqUSN > 0) {
1309 /* FIXME it would be better to use PRId64 */
1310 char *expression = talloc_asprintf(dsc, "(&%s(uSNChanged>=%llu))",
1311 ldb_filter_from_tree(dsc,
1312 req->op.search.tree),
1313 (long long unsigned int)(dsc->fromreqUSN + 1));
1315 if (expression == NULL) {
1316 return ldb_oom(ldb);
1318 new_tree = ldb_parse_tree(req, expression);
1319 if (new_tree == NULL) {
1320 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
1321 "Problem while parsing tree");
1326 * Remove our control from the list of controls
1328 if (!ldb_save_controls(control, req, NULL)) {
1329 return ldb_operr(ldb);
1331 dsc->schema = dsdb_get_schema(ldb, dsc);
1333 * At the begining we make the hypothesis that we will return a complete
1334 * result set
1337 dsc->partial = false;
1340 * 3.1.1.3.4.1.3 of MS-ADTS.pdf specify that if the scope is not subtree
1341 * we treat the search as if subtree was specified
1344 ret = ldb_build_search_req_ex(&down_req, ldb, dsc,
1345 req->op.search.base,
1346 LDB_SCOPE_SUBTREE,
1347 new_tree,
1348 attrs,
1349 req->controls,
1350 dsc, dirsync_search_callback,
1351 req);
1352 ldb_req_set_custom_flags(down_req, flags);
1353 LDB_REQ_SET_LOCATION(down_req);
1354 if (ret != LDB_SUCCESS) {
1355 return ret;
1357 /* perform the search */
1358 return ldb_next_request(module, down_req);
1361 static int dirsync_ldb_init(struct ldb_module *module)
1363 int ret;
1365 ret = ldb_mod_register_control(module, LDB_CONTROL_DIRSYNC_OID);
1366 if (ret != LDB_SUCCESS) {
1367 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
1368 "dirsync: Unable to register control with rootdse!\n");
1369 return ldb_operr(ldb_module_get_ctx(module));
1372 return ldb_next_init(module);
1375 static const struct ldb_module_ops ldb_dirsync_ldb_module_ops = {
1376 .name = "dirsync",
1377 .search = dirsync_ldb_search,
1378 .init_context = dirsync_ldb_init,
1382 initialise the module
1384 _PUBLIC_ int ldb_dirsync_module_init(const char *version)
1386 int ret;
1387 LDB_MODULE_CHECK_VERSION(version);
1388 ret = ldb_register_module(&ldb_dirsync_ldb_module_ops);
1389 return ret;