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/>.
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
;
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
,
72 struct ldb_context
*ldb
;
74 enum ndr_err_code ndr_err
;
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;
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
;
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
;
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
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) {
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
) {
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);
168 if (ldb_attr_cmp(msg
->elements
[i
].name
,
169 "replPropertyMetaData") == 0) {
170 replMetaData
= (talloc_steal(dsc
, &msg
->elements
[i
].values
[0]));
175 if (replMetaData
== NULL
) {
176 bool guidfound
= false;
179 * We are in the case of deleted object where we don't have the
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.
190 newmsg
->dn
= ldb_dn_new(newmsg
, ldb
, "");
191 if (newmsg
->dn
== NULL
) {
195 el
= ldb_msg_find_element(msg
, "objectGUID");
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) {
208 struct ldb_val *new_val;
212 txt = strrchr(txt, ':');
214 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
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) {
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
, "*")) {
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
273 if (dsc
->fromreqUSN
> 0 || dsc
->cursors
!= NULL
) {
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
) {
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
,
288 if (!dsc
->localonly
) {
289 struct drsuapi_DsReplicaCursor
*tab
= dsc
->cursors
;
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.
305 /* If we are here it's because we have a usn > (max(usn of vectors))*/
307 if (namereturned
== false &&
309 ldb_attr_cmp(a
->lDAPDisplayName
, "name") == 0) {
311 if (ldb_dn_compare(dsc
->nc_root
, msg
->dn
) == 0) {
315 listAttr
[j
] = a
->lDAPDisplayName
;
324 if (ldb_attr_in_list(req
->op
.search
.attrs
, "*") ||
325 ldb_attr_in_list(req
->op
.search
.attrs
, "name")) {
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
,
348 if (attr
->linkID
& 1) {
350 * Attribute is a backlink so let's remove it
355 if (ldb_attr_cmp(msg
->elements
[i
].name
,
356 "replPropertyMetaData") == 0) {
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
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)) {
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
);
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=).
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
);
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 ) {
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
--) {
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
;
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
) {
443 status
= dsdb_get_extended_dn_uint32(dn
->dn
, &tmp_usn
, "RMD_LOCAL_USN");
444 if (!NT_STATUS_IS_OK(status
)) {
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
)) {
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
)) {
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
)) {
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);
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
) {
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
;
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.
503 /* If we are here it's because we have a usn > (max(usn of vectors))*/
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 */
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
)));
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
),
543 tmpel
= talloc_zero(newmsg
, struct ldb_message_element
);
547 tmpel
->values
= talloc_array(tmpel
, struct ldb_val
, 1);
548 if (tmpel
->values
== NULL
) {
551 if (flags
& DSDB_RMD_FLAG_DELETED
) {
552 tmpel
->name
= talloc_asprintf(tmpel
,
557 tmpel
->name
= talloc_asprintf(tmpel
,
561 if (tmpel
->name
== NULL
) {
564 tmpel
->num_values
= 1;
566 tmpel
->num_values
+= 1;
567 tmpel
->values
= talloc_realloc(tmpel
,
571 if (tmpel
->values
== NULL
) {
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
) {
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
));
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
);
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");
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");
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) {
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
);
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
++) {
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
);
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
;
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
,
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) {
738 struct ldb_message_element
*el
= ldb_msg_find_element(resVector
->msgs
[0], "replUpToDateVector");
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
);
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
;
764 tab
= talloc_array(cookie
, struct drsuapi_DsReplicaCursor
, count
);
771 * No replUpToDateVector ? it happens quite often (1 DC,
772 * other DCs didn't update ...
774 tab
= talloc_array(cookie
, struct drsuapi_DsReplicaCursor
, count
);
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
;
795 static int dirsync_search_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
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
;
807 const char *attrs
[] = { "objectGUID", NULL
};
808 enum ndr_err_code ndr_err
;
812 dsc
= talloc_get_type_abort(req
->context
, struct dirsync_context
);
813 ldb
= ldb_module_get_ctx(dsc
->module
);
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;
838 return ldb_operr(ldb
);
841 tmp
= strchr(tmp
, '/');
844 dn
= ldb_dn_new(dsc
, ldb
, tmp
);
849 flags
= DSDB_FLAG_NEXT_MODULE
|
850 DSDB_SEARCH_SHOW_DELETED
|
851 DSDB_SEARCH_SHOW_EXTENDED_DN
;
854 flags
= flags
| DSDB_FLAG_AS_SYSTEM
;
857 ret
= dsdb_module_search_tree(dsc
->module
, dsc
, &res
,
860 req
->op
.search
.attrs
,
863 if (ret
!= LDB_SUCCESS
) {
868 if (res
->count
> 1) {
869 char *ldbmsg
= talloc_asprintf(dn
, "LDB returned more than result for dn: %s", tmp
);
871 ldb_set_errstring(ldb
, ldbmsg
);
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
884 * Fetch the objectGUID of the root of current NC
886 ret
= dsdb_module_search_dn(dsc
->module
, dsc
, &res2
,
889 DSDB_FLAG_NEXT_MODULE
, req
);
891 if (ret
!= LDB_SUCCESS
) {
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
) {
912 return dirsync_filter_entry(req
, res
->msgs
[0], res
->controls
, dsc
, true);
916 * Let's add our own control
919 control
= talloc_zero(ares
->controls
, struct ldb_dirsync_control
);
920 if (control
== NULL
) {
925 * When outputing flags is used to say more results.
926 * For the moment we didn't honnor the size info */
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
) {
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
);
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
);
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
;
986 enum ndr_err_code ndr_err
;
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
);
1015 return ldb_oom(ldb
);
1017 dsc
->module
= module
;
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
) {
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");
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
,
1063 DSDB_FLAG_NEXT_MODULE
|DSDB_FLAG_AS_SYSTEM
, req
);
1065 if (ret
!= LDB_SUCCESS
) {
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
) {
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
) {
1081 dsc
->assystem
= true;
1082 ret
= ldb_request_add_control(req
, LDB_CONTROL_AS_SYSTEM_OID
, false, NULL
);
1084 if (ret
!= LDB_SUCCESS
) {
1087 talloc_free(acl_res
);
1089 flags
|= DSDB_ACL_CHECKS_DIRSYNC_FLAG
;
1091 if (ret
!= LDB_SUCCESS
) {
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
);
1119 * When returning all the attributes return also the SD as
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
) {
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:
1144 dsc
->nbDefaultAttrs
= 3;
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
++;
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
);
1196 attrs
[1] = "parentGUID";
1197 attrs
[2] = "replPropertyMetaData";
1199 if (ret
!= LDB_SUCCESS
) {
1203 * When no attributes are asked we in anycase expect at least 3 attributes:
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
) {
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
) {
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
) {
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
) {
1241 if (dirsync_ctl
->flags
& LDAP_DIRSYNC_INCREMENTAL_VALUES
) {
1242 dsc
->linkIncrVal
= true;
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
;
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
;
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
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
,
1350 dsc
, dirsync_search_callback
,
1352 ldb_req_set_custom_flags(down_req
, flags
);
1353 LDB_REQ_SET_LOCATION(down_req
);
1354 if (ret
!= LDB_SUCCESS
) {
1357 /* perform the search */
1358 return ldb_next_request(module
, down_req
);
1361 static int dirsync_ldb_init(struct ldb_module
*module
)
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
= {
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
)
1387 LDB_MODULE_CHECK_VERSION(version
);
1388 ret
= ldb_register_module(&ldb_dirsync_ldb_module_ops
);