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
;
79 struct ldb_val
*replMetaData
= NULL
;
80 struct replPropertyMetaDataBlob rmd
;
81 const struct dsdb_attribute
*attr
;
82 const char **listAttr
= NULL
;
83 bool namereturned
= false;
84 bool nameasked
= false;
86 /* Ajustment for the added attributes, it will reduce the number of
87 * expected to be here attributes*/
88 unsigned int delta
= 0;
89 const char **myaccept
= NULL
;
90 const char *emptyaccept
[] = { NULL
};
91 const char *extendedaccept
[] = { "GUID", "SID", "WKGUID", NULL
};
92 const char *rdn
= NULL
;
93 struct ldb_message_element
*el
;
94 struct ldb_message
*newmsg
;
97 * Where we asked to do extended dn ?
98 * if so filter out everything bug GUID, SID, WKGUID,
99 * if not filter out everything (just keep the dn).
101 if ( dsc
->noextended
== true ) {
102 myaccept
= emptyaccept
;
104 myaccept
= extendedaccept
;
106 ldb
= ldb_module_get_ctx(dsc
->module
);
108 if (msg
->num_elements
== 0) {
110 * Entry that we don't really have access to
114 ldb_dn_extended_filter(msg
->dn
, myaccept
);
117 * If the RDN starts with CN then the CN attribute is never returned
119 rdn
= ldb_dn_get_rdn_name(msg
->dn
);
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.
127 if (referral
== true &&
128 ldb_attr_in_list(req
->op
.search
.attrs
, "objectGUID") &&
129 dsc
->fromreqUSN
== 0) {
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.
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.
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
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
155 newmsg
= talloc_zero(dsc
->req
, struct ldb_message
);
156 if (newmsg
== NULL
) {
159 for (i
= msg
->num_elements
- 1; i
>= 0; i
--) {
160 attr
= dsdb_attribute_by_lDAPDisplayName(dsc
->schema
, msg
->elements
[i
].name
);
161 if (ldb_attr_cmp(msg
->elements
[i
].name
, "uSNChanged") == 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
166 val
= strtoull((const char*)msg
->elements
[i
].values
[0].data
, NULL
, 0);
170 if (ldb_attr_cmp(msg
->elements
[i
].name
,
171 "replPropertyMetaData") == 0) {
172 replMetaData
= (talloc_steal(dsc
, &msg
->elements
[i
].values
[0]));
177 if (replMetaData
== NULL
) {
178 bool guidfound
= false;
181 * We are in the case of deleted object where we don't have the
184 if (!ldb_msg_find_attr_as_uint(msg
, "isDeleted", 0)) {
186 * This is not a deleted item and we don't
187 * have the replPropertyMetaData.
192 newmsg
->dn
= ldb_dn_new(newmsg
, ldb
, "");
193 if (newmsg
->dn
== NULL
) {
197 el
= ldb_msg_find_element(msg
, "objectGUID");
202 * We expect to find the GUID in the object,
203 * if it turns out not to be the case sometime
204 * well will uncomment the code bellow
206 SMB_ASSERT(guidfound
== true);
208 if (guidfound == false) {
210 struct ldb_val *new_val;
214 txt = strrchr(txt, ':');
216 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
220 status = GUID_from_string(txt, &guid);
221 if (!NT_STATUS_IS_OK(status)) {
222 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
225 status = GUID_to_ndr_blob(&guid, msg, &guid_blob);
226 if (!NT_STATUS_IS_OK(status)) {
227 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
230 new_val = talloc(msg, struct ldb_val);
231 if (new_val == NULL) {
234 new_val->data = talloc_steal(new_val, guid_blob.data);
235 new_val->length = guid_blob.length;
236 if (ldb_msg_add_value(msg, "objectGUID", new_val, NULL) != 0) {
237 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
241 ldb_msg_add(newmsg
, el
, LDB_FLAG_MOD_ADD
);
242 talloc_steal(newmsg
->elements
, el
->name
);
243 talloc_steal(newmsg
->elements
, el
->values
);
246 return ldb_module_send_entry(dsc
->req
, msg
, controls
);
249 ndr_err
= ndr_pull_struct_blob(replMetaData
, dsc
, &rmd
,
250 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
251 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
252 ldb_set_errstring(ldb
, "Unable to unmarshall replPropertyMetaData");
253 return ldb_module_done(dsc
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
255 if (ldb_attr_in_list(req
->op
.search
.attrs
, "name") ||
256 ldb_attr_in_list(req
->op
.search
.attrs
, "*")) {
261 * If we don't have an USN and no updateness array then we skip the
262 * test phase this is an optimisation for the case when you
263 * first query the DC without a cookie.
264 * As this query is most probably the one
265 * that will return the biggest answer, skipping this part
266 * will really save time.
268 if (ldb_dn_compare(dsc
->nc_root
, msg
->dn
) == 0) {
269 /* If we have name then we expect to have parentGUID,
270 * it will not be the case for the root of the NC
275 if (dsc
->fromreqUSN
> 0 || dsc
->cursors
!= NULL
) {
278 * Allocate an array of size(replMetaData) of char*
279 * we know that it will be oversized but it's a short lived element
281 listAttr
= talloc_array(msg
, const char*, rmd
.ctr
.ctr1
.count
+ 1);
282 if (listAttr
== NULL
) {
285 for (n
=0; n
< rmd
.ctr
.ctr1
.count
; n
++) {
286 struct replPropertyMetaData1
*omd
= &rmd
.ctr
.ctr1
.array
[n
];
287 if (omd
->local_usn
> dsc
->fromreqUSN
) {
288 const struct dsdb_attribute
*a
= dsdb_attribute_by_attributeID_id(dsc
->schema
,
290 if (!dsc
->localonly
) {
291 struct drsuapi_DsReplicaCursor
*tab
= dsc
->cursors
;
293 for (l
=0; l
< dsc
->cursor_size
; l
++) {
294 if (GUID_equal(&tab
[l
].source_dsa_invocation_id
, &omd
->originating_invocation_id
) &&
295 tab
[l
].highest_usn
>= omd
->originating_usn
) {
297 * If we have in the uptodateness vector an entry
298 * with the same invocation id as the originating invocation
299 * and if the usn in the vector is greater or equal to
300 * the one in originating_usn, then it means that this entry
301 * has already been sent (from another DC) to the client
302 * no need to resend it one more time.
307 /* If we are here it's because we have a usn > (max(usn of vectors))*/
309 if (namereturned
== false &&
311 ldb_attr_cmp(a
->lDAPDisplayName
, "name") == 0) {
313 if (ldb_dn_compare(dsc
->nc_root
, msg
->dn
) == 0) {
317 listAttr
[j
] = a
->lDAPDisplayName
;
326 if (ldb_attr_in_list(req
->op
.search
.attrs
, "*") ||
327 ldb_attr_in_list(req
->op
.search
.attrs
, "name")) {
334 * Let's loop around the remaining elements
335 * to see which one are in the listAttr.
336 * If they are in this array it means that
337 * their localusn > usn from the request (in the cookie)
338 * if not we remove the attribute.
340 for (i
= msg
->num_elements
- 1; i
>= 0; i
--) {
341 el
= &(msg
->elements
[i
]);
342 attr
= dsdb_attribute_by_lDAPDisplayName(dsc
->schema
,
344 const char *ldapattrname
= el
->name
;
347 if (attr
->linkID
& 1) {
349 * Attribute is a backlink so let's remove it
354 if (ldb_attr_cmp(msg
->elements
[i
].name
,
355 "replPropertyMetaData") == 0) {
359 if ((attr
->systemFlags
& (DS_FLAG_ATTR_NOT_REPLICATED
| DS_FLAG_ATTR_IS_CONSTRUCTED
))) {
360 if (ldb_attr_cmp(attr
->lDAPDisplayName
, "objectGUID") != 0 &&
361 ldb_attr_cmp(attr
->lDAPDisplayName
, "parentGUID") != 0) {
363 * Attribute is constructed or not replicated, let's get rid of it
367 /* Let's keep the attribute that we forced to be added
368 * even if they are not in the replicationMetaData
369 * or are just generated
371 if (namereturned
== false &&
372 (ldb_attr_cmp(attr
->lDAPDisplayName
, "parentGUID") == 0)) {
376 if (ldb_msg_add(newmsg
, el
, LDB_FLAG_MOD_ADD
) != LDB_SUCCESS
) {
377 return ldb_error(ldb
,
378 LDB_ERR_OPERATIONS_ERROR
,
379 "Unable to add attribute");
381 talloc_steal(newmsg
->elements
, el
->name
);
382 talloc_steal(newmsg
->elements
, el
->values
);
387 if (ldb_attr_cmp(msg
->elements
[i
].name
, rdn
) == 0) {
389 * We have an attribute that is the same as the start of the RDN
390 * (ie. attribute CN with rdn CN=).
395 if (ldb_attr_cmp(attr
->lDAPDisplayName
, "instanceType") == 0) {
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");
401 talloc_steal(newmsg
->elements
, el
->name
);
402 talloc_steal(newmsg
->elements
, el
->values
);
405 /* For links, when our functional level > windows 2000
406 * we use the RMD_LOCAL_USN information to decide wether
407 * we return the attribute or not.
408 * For windows 2000 this information is in the replPropertyMetaData
409 * so it will be handled like any other replicated attribute
412 if (dsc
->functional_level
> DS_DOMAIN_FUNCTION_2000
&&
413 attr
->linkID
!= 0 ) {
416 * Elements for incremental changes on linked attributes
418 struct ldb_message_element
*el_incr_add
= NULL
;
419 struct ldb_message_element
*el_incr_del
= NULL
;
421 * Attribute is a forwardlink so let's remove it
424 for (k
= el
->num_values
-1; k
>= 0; k
--) {
427 uint32_t tmp_usn
= 0;
428 uint32_t tmp_usn2
= 0;
429 struct GUID invocation_id
= GUID_zero();
430 struct dsdb_dn
*dn
= dsdb_dn_parse(msg
, ldb
, &el
->values
[k
], attr
->syntax
->ldap_oid
);
431 struct ldb_dn
*copydn
;
433 ldb_set_errstring(ldb
, "Cannot parse DN");
434 return ldb_module_done(dsc
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
437 copydn
= ldb_dn_copy(msg
, dn
->dn
);
438 if (copydn
== NULL
) {
442 status
= dsdb_get_extended_dn_uint32(dn
->dn
, &tmp_usn
, "RMD_LOCAL_USN");
443 if (!NT_STATUS_IS_OK(status
)) {
445 return ldb_module_done(dsc
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
447 status
= dsdb_get_extended_dn_guid(dn
->dn
, &invocation_id
, "RMD_INVOCID");
448 if (!NT_STATUS_IS_OK(status
)) {
450 return ldb_module_done(dsc
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
453 status
= dsdb_get_extended_dn_uint32(dn
->dn
, &flags
, "RMD_FLAGS");
454 if (!NT_STATUS_IS_OK(status
)) {
456 return ldb_module_done(dsc
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
459 status
= dsdb_get_extended_dn_uint32(dn
->dn
, &tmp_usn2
, "RMD_ORIGINATING_USN");
460 if (!NT_STATUS_IS_OK(status
)) {
462 return ldb_module_done(dsc
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
465 ldb_dn_extended_filter(dn
->dn
, myaccept
);
466 dn_ln
= ldb_dn_get_extended_linearized(dn
, dn
->dn
, 1);
470 ldb_set_errstring(ldb
, "Cannot linearize dn");
471 return ldb_module_done(dsc
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
474 talloc_free(el
->values
[k
].data
);
475 el
->values
[k
].data
= (uint8_t*)talloc_steal(el
->values
, dn_ln
);
476 if (el
->values
[k
].data
== NULL
) {
478 return ldb_module_done(dsc
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
480 el
->values
[k
].length
= strlen(dn_ln
);
483 if (tmp_usn
> dsc
->fromreqUSN
) {
484 if (!dsc
->localonly
) {
485 struct drsuapi_DsReplicaCursor
*tab
= dsc
->cursors
;
488 for (l
=0; l
< dsc
->cursor_size
; l
++) {
489 if (GUID_equal(&tab
[l
].source_dsa_invocation_id
, &invocation_id
) &&
490 tab
[l
].highest_usn
>= tmp_usn2
) {
492 * If we have in the uptodateness vector an entry
493 * with the same invocation id as the originating invocation
494 * and if the usn in the vector is greater or equal to
495 * the one in originating_usn, then it means that this entry
496 * has already been sent (from another DC) to the client
497 * no need to resend it one more time.
502 /* If we are here it's because we have a usn > (max(usn of vectors))*/
507 /* If we are here it's because the link is more recent than either any
508 * originating usn or local usn
511 if (dsc
->linkIncrVal
== true) {
512 struct ldb_message_element
*tmpel
;
513 if (flags
& DSDB_RMD_FLAG_DELETED
) {
514 /* We have to check that the inactive link still point to an existing object */
519 status
= dsdb_get_extended_dn_guid(copydn
, &guid
, "GUID");
520 if (!NT_STATUS_IS_OK(status
)) {
521 DEBUG(0,(__location__
" Unable to extract GUID in linked attribute '%s' in '%s'\n",
522 el
->name
, ldb_dn_get_linearized(copydn
)));
523 return ldb_operr(ldb
);
525 ret
= dsdb_module_dn_by_guid(dsc
->module
, newmsg
, &guid
, &tdn
, req
);
526 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
527 DEBUG(2, (" Search of guid %s returned 0 objects, skipping it !\n",
528 GUID_string(newmsg
, &guid
)));
530 } else if (ret
!= LDB_SUCCESS
) {
531 DEBUG(0, (__location__
" Search of guid %s failed with error code %d\n",
532 GUID_string(newmsg
, &guid
),
542 tmpel
= talloc_zero(newmsg
, struct ldb_message_element
);
546 tmpel
->values
= talloc_array(tmpel
, struct ldb_val
, 1);
547 if (tmpel
->values
== NULL
) {
550 if (flags
& DSDB_RMD_FLAG_DELETED
) {
551 tmpel
->name
= talloc_asprintf(tmpel
,
556 tmpel
->name
= talloc_asprintf(tmpel
,
560 if (tmpel
->name
== NULL
) {
563 tmpel
->num_values
= 1;
565 tmpel
->num_values
+= 1;
566 tmpel
->values
= talloc_realloc(tmpel
,
570 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
);
660 * Here we run through the list of attributes returned
661 * in the propertyMetaData.
662 * Entries of this list have usn > requested_usn,
663 * entries that are also present in the message have been
664 * replaced by NULL, so at this moment the list contains
665 * only elements that have a usn > requested_usn and that
666 * haven't been seen. It's attributes that were removed.
667 * We add them to the message like empty elements.
669 for (j
=0; j
<size
; j
++) {
671 ldb_attr_in_list(req
->op
.search
.attrs
, "*") ||
672 ldb_attr_in_list(req
->op
.search
.attrs
, listAttr
[j
])) &&
673 (ldb_attr_cmp(listAttr
[j
], rdn
) != 0) &&
674 (ldb_attr_cmp(listAttr
[j
], "instanceType") != 0)) {
675 ldb_msg_add_empty(newmsg
, listAttr
[j
], LDB_FLAG_MOD_DELETE
, NULL
);
678 talloc_free(listAttr
);
680 if ((newmsg
->num_elements
- ( dsc
->nbDefaultAttrs
- delta
)) > 0) {
682 * After cleaning attributes there is still some attributes that were not added just
683 * for the purpose of the control (objectGUID, instanceType, ...)
686 newmsg
->dn
= talloc_steal(newmsg
, msg
->dn
);
687 if (val
> dsc
->highestUSN
) {
688 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
;
708 struct ldb_dn
*nc_root
;
711 struct drsuapi_DsReplicaCursor
*tab
;
713 nc_root
= ldb_get_default_basedn(ldb
);
714 ret
= ldb_sequence_number(ldb
, LDB_SEQ_HIGHEST_SEQ
, &highest_usn
);
715 if (ret
!= LDB_SUCCESS
) {
716 return ldb_error(ldb
, LDB_ERR_OPERATIONS_ERROR
, "Unable to get highest USN from current NC");
719 /* If we have a full answer then the highest USN
720 * is not the highest USN from the result set but the
721 * highest of the naming context, unless the sequence is not updated yet.
723 if (highest_usn
> dsc
->highestUSN
) {
724 dsc
->highestUSN
= highest_usn
;
728 ret
= dsdb_module_search_dn(dsc
->module
, dsc
, &resVector
,
731 DSDB_FLAG_NEXT_MODULE
, req
);
733 if (resVector
->count
!= 0) {
736 struct ldb_message_element
*el
= ldb_msg_find_element(resVector
->msgs
[0], "replUpToDateVector");
738 enum ndr_err_code ndr_err
;
739 struct replUpToDateVectorBlob utd
;
740 blob
.data
= el
->values
[0].data
;
741 blob
.length
= el
->values
[0].length
;
742 ndr_err
= ndr_pull_struct_blob(&blob
, dsc
, &utd
,
743 (ndr_pull_flags_fn_t
)ndr_pull_replUpToDateVectorBlob
);
745 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
746 return ldb_error(ldb
, LDB_ERR_OPERATIONS_ERROR
,
747 "Unable to pull replUpToDateVectorBlob structure");
751 count
+= utd
.ctr
.ctr2
.count
;
752 tab
= talloc_array(cookie
, struct drsuapi_DsReplicaCursor
, count
);
756 for (i
=1; i
< count
; i
++) {
757 memset(&tab
[i
], 0, sizeof(struct drsuapi_DsReplicaCursor
));
758 tab
[i
].highest_usn
= utd
.ctr
.ctr2
.cursors
[i
-1].highest_usn
;
759 tab
[i
].source_dsa_invocation_id
= utd
.ctr
.ctr2
.cursors
[i
-1].source_dsa_invocation_id
;
762 tab
= talloc_array(cookie
, struct drsuapi_DsReplicaCursor
, count
);
769 * No replUpToDateVector ? it happens quite often (1 DC,
770 * other DCs didn't update ...
772 tab
= talloc_array(cookie
, struct drsuapi_DsReplicaCursor
, count
);
777 /* Our vector is always the first */
778 tab
[0].highest_usn
= dsc
->highestUSN
;
779 tab
[0].source_dsa_invocation_id
= *(dsc
->our_invocation_id
);
782 /* We have to add the updateness vector that we have*/
783 /* Version is always 1 in dirsync cookies */
784 cookie
->blob
.extra
.uptodateness_vector
.version
= 1;
785 cookie
->blob
.extra
.uptodateness_vector
.reserved
= 0;
786 cookie
->blob
.extra
.uptodateness_vector
.ctr
.ctr1
.count
= count
;
787 cookie
->blob
.extra
.uptodateness_vector
.ctr
.ctr1
.reserved
= 0;
788 cookie
->blob
.extra
.uptodateness_vector
.ctr
.ctr1
.cursors
= tab
;
793 static int dirsync_search_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
796 struct dirsync_context
*dsc
;
797 struct ldb_result
*res
, *res2
;
798 struct ldb_dirsync_control
*control
;
799 struct ldapControlDirSyncCookie
*cookie
;
800 struct ldb_context
*ldb
;
805 const char *attrs
[] = { "objectGUID", NULL
};
806 enum ndr_err_code ndr_err
;
810 dsc
= talloc_get_type_abort(req
->context
, struct dirsync_context
);
811 ldb
= ldb_module_get_ctx(dsc
->module
);
813 return ldb_module_done(dsc
->req
, NULL
, NULL
,
814 LDB_ERR_OPERATIONS_ERROR
);
816 if (ares
->error
!= LDB_SUCCESS
) {
817 return ldb_module_done(dsc
->req
, ares
->controls
,
818 ares
->response
, ares
->error
);
821 switch (ares
->type
) {
822 case LDB_REPLY_ENTRY
:
823 return dirsync_filter_entry(req
, ares
->message
, ares
->controls
, dsc
, false);
825 case LDB_REPLY_REFERRAL
:
826 /* Skip the ldap(s):// so up to 8 chars,
827 * we don't care to be precise as the goal is to be in
828 * the name of DC, then we search the next '/'
829 * as it will be the last char before the DN of the referal
831 if (strncmp(ares
->referral
, "ldap://", 7) == 0) {
832 tmp
= ares
->referral
+ 7;
833 } else if (strncmp(ares
->referral
, "ldaps://", 8) == 0) {
834 tmp
= ares
->referral
+ 8;
836 return ldb_operr(ldb
);
839 tmp
= strchr(tmp
, '/');
842 dn
= ldb_dn_new(dsc
, ldb
, tmp
);
847 flags
= DSDB_FLAG_NEXT_MODULE
|
848 DSDB_SEARCH_SHOW_DELETED
|
849 DSDB_SEARCH_SHOW_EXTENDED_DN
;
852 flags
= flags
| DSDB_FLAG_AS_SYSTEM
;
855 ret
= dsdb_module_search_tree(dsc
->module
, dsc
, &res
,
858 req
->op
.search
.attrs
,
861 if (ret
!= LDB_SUCCESS
) {
866 if (res
->count
> 1) {
867 char *ldbmsg
= talloc_asprintf(dn
, "LDB returned more than result for dn: %s", tmp
);
869 ldb_set_errstring(ldb
, ldbmsg
);
872 return ldb_module_done(dsc
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
873 } else if (res
->count
== 0) {
874 /* if nothing is returned then it means that we don't
882 * Fetch the objectGUID of the root of current NC
884 ret
= dsdb_module_search_dn(dsc
->module
, dsc
, &res2
,
887 DSDB_FLAG_NEXT_MODULE
, req
);
889 if (ret
!= LDB_SUCCESS
) {
892 if (res2
->msgs
[0]->num_elements
!= 1) {
893 ldb_set_errstring(ldb
,
894 "More than 1 attribute returned while looking for objectGUID");
895 return ldb_module_done(dsc
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
898 val
= res2
->msgs
[0]->elements
[0].values
;
899 ret
= ldb_msg_add_value(res
->msgs
[0], "parentGUID", val
, NULL
);
901 * It *very* important to steal otherwise as val is in a subcontext
902 * related to res2, when the value will be one more time stolen
903 * it's elements[x].values that will be stolen, so it's important to
904 * recreate the context hierrachy as if it was done from a ldb_request
906 talloc_steal(res
->msgs
[0]->elements
[0].values
, val
);
907 if (ret
!= LDB_SUCCESS
) {
910 return dirsync_filter_entry(req
, res
->msgs
[0], res
->controls
, dsc
, true);
914 * Let's add our own control
917 control
= talloc_zero(ares
->controls
, struct ldb_dirsync_control
);
918 if (control
== NULL
) {
923 * When outputing flags is used to say more results.
924 * For the moment we didn't honnor the size info */
929 * max_attribute is unused cf. 3.1.1.3.4.1.3 LDAP_SERVER_DIRSYNC_OID in MS-ADTS
932 control
->max_attributes
= 0;
933 cookie
= talloc_zero(control
, struct ldapControlDirSyncCookie
);
934 if (cookie
== NULL
) {
939 ret
= dirsync_create_vector(req
, ares
, dsc
, cookie
, ldb
);
940 if (ret
!= LDB_SUCCESS
) {
941 return ldb_module_done(dsc
->req
, NULL
, NULL
, ret
);
945 unix_to_nt_time(&now
, time(NULL
));
946 cookie
->blob
.time
= now
;
947 cookie
->blob
.highwatermark
.highest_usn
= dsc
->highestUSN
;
948 cookie
->blob
.highwatermark
.tmp_highest_usn
= dsc
->highestUSN
;
949 cookie
->blob
.guid1
= *(dsc
->our_invocation_id
);
951 blob
= talloc_zero(control
, DATA_BLOB
);
956 ndr_err
= ndr_push_struct_blob(blob
, blob
, cookie
,
957 (ndr_push_flags_fn_t
)ndr_push_ldapControlDirSyncCookie
);
959 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
960 ldb_set_errstring(ldb
, "Can't marshall ldapControlDirSyncCookie struct");
961 return ldb_module_done(dsc
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
963 control
->cookie
= (char *)blob
->data
;
964 control
->cookie_len
= blob
->length
;
965 ldb_reply_add_control(ares
, LDB_CONTROL_DIRSYNC_OID
, true, control
);
967 return ldb_module_done(dsc
->req
, ares
->controls
,
968 ares
->response
, LDB_SUCCESS
);
974 static int dirsync_ldb_search(struct ldb_module
*module
, struct ldb_request
*req
)
976 struct ldb_control
*control
;
977 struct ldb_result
*acl_res
;
978 struct ldb_dirsync_control
*dirsync_ctl
;
979 struct ldb_request
*down_req
;
980 struct dirsync_context
*dsc
;
981 struct ldb_context
*ldb
;
982 struct ldb_parse_tree
*new_tree
= req
->op
.search
.tree
;
984 enum ndr_err_code ndr_err
;
990 if (ldb_dn_is_special(req
->op
.search
.base
)) {
991 return ldb_next_request(module
, req
);
995 * check if there's an extended dn control
997 control
= ldb_request_get_control(req
, LDB_CONTROL_DIRSYNC_OID
);
998 if (control
== NULL
) {
999 /* not found go on */
1000 return ldb_next_request(module
, req
);
1003 ldb
= ldb_module_get_ctx(module
);
1005 * This control must always be critical otherwise we return PROTOCOL error
1007 if (!control
->critical
) {
1008 return ldb_operr(ldb
);
1011 dsc
= talloc_zero(req
, struct dirsync_context
);
1013 return ldb_oom(ldb
);
1015 dsc
->module
= module
;
1017 dsc
->nbDefaultAttrs
= 0;
1020 dirsync_ctl
= talloc_get_type(control
->data
, struct ldb_dirsync_control
);
1021 if (dirsync_ctl
== NULL
) {
1022 return ldb_error(ldb
, LDB_ERR_PROTOCOL_ERROR
, "No data in dirsync control");
1025 ret
= dsdb_find_nc_root(ldb
, dsc
, req
->op
.search
.base
, &dsc
->nc_root
);
1026 if (ret
!= LDB_SUCCESS
) {
1030 if (ldb_dn_compare(dsc
->nc_root
, req
->op
.search
.base
) != 0) {
1031 if (dirsync_ctl
->flags
& LDAP_DIRSYNC_OBJECT_SECURITY
) {
1032 return ldb_error(ldb
, LDB_ERR_UNWILLING_TO_PERFORM
,
1033 "DN is not one of the naming context");
1036 return ldb_error(ldb
, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS
,
1037 "dN is not one of the naming context");
1041 if (!(dirsync_ctl
->flags
& LDAP_DIRSYNC_OBJECT_SECURITY
)) {
1042 struct dom_sid
*sid
;
1043 struct security_descriptor
*sd
= NULL
;
1044 const char *acl_attrs
[] = { "nTSecurityDescriptor", "objectSid", NULL
};
1046 * If we don't have the flag and if we have the "replicate directory change" granted
1047 * then we upgrade ourself to system to not be blocked by the acl
1049 /* FIXME we won't check the replicate directory change filtered attribute set
1050 * it should be done so that if attr is not empty then we check that the user
1051 * has also this right
1055 * First change to system to get the SD of the root of current NC
1056 * if we don't the acl_read will forbid us the right to read it ...
1058 ret
= dsdb_module_search_dn(module
, dsc
, &acl_res
,
1059 req
->op
.search
.base
,
1061 DSDB_FLAG_NEXT_MODULE
|DSDB_FLAG_AS_SYSTEM
, req
);
1063 if (ret
!= LDB_SUCCESS
) {
1067 sid
= samdb_result_dom_sid(dsc
, acl_res
->msgs
[0], "objectSid");
1068 /* sid can be null ... */
1069 ret
= dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(module
), acl_res
, acl_res
->msgs
[0], &sd
);
1071 if (ret
!= LDB_SUCCESS
) {
1074 ret
= acl_check_extended_right(dsc
, sd
, acl_user_token(module
), GUID_DRS_GET_CHANGES
, SEC_ADS_CONTROL_ACCESS
, sid
);
1076 if (ret
== LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS
) {
1079 dsc
->assystem
= true;
1080 ret
= ldb_request_add_control(req
, LDB_CONTROL_AS_SYSTEM_OID
, false, NULL
);
1082 if (ret
!= LDB_SUCCESS
) {
1085 talloc_free(acl_res
);
1087 flags
|= DSDB_ACL_CHECKS_DIRSYNC_FLAG
;
1089 if (ret
!= LDB_SUCCESS
) {
1095 dsc
->functional_level
= dsdb_functional_level(ldb
);
1097 if (req
->op
.search
.attrs
) {
1098 attrs
= ldb_attr_list_copy(dsc
, req
->op
.search
.attrs
);
1099 if (attrs
== NULL
) {
1100 return ldb_oom(ldb
);
1103 * Check if we have only "dn" as attribute, if so then
1104 * treat as if "*" was requested
1106 if (attrs
&& attrs
[0]) {
1107 if (ldb_attr_cmp(attrs
[0], "dn") == 0 && !attrs
[1]) {
1108 attrs
= talloc_array(dsc
, const char*, 2);
1109 if (attrs
== NULL
) {
1110 return ldb_oom(ldb
);
1117 * When returning all the attributes return also the SD as
1120 if (ldb_attr_in_list(attrs
, "*")) {
1121 struct ldb_sd_flags_control
*sdctr
= talloc_zero(dsc
, struct ldb_sd_flags_control
);
1122 sdctr
->secinfo_flags
= 0;
1123 ret
= ldb_request_add_control(req
, LDB_CONTROL_SD_FLAGS_OID
, false, sdctr
);
1124 if (ret
!= LDB_SUCCESS
) {
1127 attrs
= ldb_attr_list_copy_add(dsc
, attrs
, "parentGUID");
1128 if (attrs
== NULL
) {
1129 return ldb_oom(ldb
);
1131 attrs
= ldb_attr_list_copy_add(dsc
, attrs
, "replPropertyMetaData");
1132 if (attrs
== NULL
) {
1133 return ldb_oom(ldb
);
1136 * When no attributes are asked we in anycase expect at least 3 attributes:
1142 dsc
->nbDefaultAttrs
= 3;
1145 * We will need this two attributes in the callback
1147 attrs
= ldb_attr_list_copy_add(dsc
, attrs
, "usnChanged");
1148 if (attrs
== NULL
) {
1149 return ldb_operr(ldb
);
1151 attrs
= ldb_attr_list_copy_add(dsc
, attrs
, "replPropertyMetaData");
1152 if (attrs
== NULL
) {
1153 return ldb_operr(ldb
);
1156 if (!ldb_attr_in_list(attrs
, "instanceType")) {
1157 attrs
= ldb_attr_list_copy_add(dsc
, attrs
, "instanceType");
1158 if (attrs
== NULL
) {
1159 return ldb_operr(ldb
);
1161 dsc
->nbDefaultAttrs
++;
1164 if (!ldb_attr_in_list(attrs
, "objectGUID")) {
1165 attrs
= ldb_attr_list_copy_add(dsc
, attrs
, "objectGUID");
1166 if (attrs
== NULL
) {
1167 return ldb_operr(ldb
);
1171 * Always increment the number of asked attributes as we don't care if objectGUID was asked
1172 * or not for counting the number of "real" attributes returned.
1174 dsc
->nbDefaultAttrs
++;
1176 if (!ldb_attr_in_list(attrs
, "parentGUID")) {
1177 attrs
= ldb_attr_list_copy_add(dsc
, attrs
, "parentGUID");
1178 if (attrs
== NULL
) {
1179 return ldb_operr(ldb
);
1182 dsc
->nbDefaultAttrs
++;
1186 struct ldb_sd_flags_control
*sdctr
= talloc_zero(dsc
, struct ldb_sd_flags_control
);
1187 sdctr
->secinfo_flags
= 0;
1188 ret
= ldb_request_add_control(req
, LDB_CONTROL_SD_FLAGS_OID
, false, sdctr
);
1189 attrs
= talloc_array(dsc
, const char*, 4);
1190 if (attrs
== NULL
) {
1191 return ldb_operr(ldb
);
1194 attrs
[1] = "parentGUID";
1195 attrs
[2] = "replPropertyMetaData";
1197 if (ret
!= LDB_SUCCESS
) {
1201 * When no attributes are asked we in anycase expect at least 3 attributes:
1207 dsc
->nbDefaultAttrs
= 3;
1210 if (!ldb_request_get_control(req
, LDB_CONTROL_EXTENDED_DN_OID
)) {
1211 ret
= ldb_request_add_control(req
, LDB_CONTROL_EXTENDED_DN_OID
, false, NULL
);
1212 if (ret
!= LDB_SUCCESS
) {
1215 dsc
->noextended
= true;
1218 if (ldb_request_get_control(req
, LDB_CONTROL_REVEAL_INTERNALS
) == NULL
) {
1219 ret
= ldb_request_add_control(req
, LDB_CONTROL_REVEAL_INTERNALS
, false, NULL
);
1220 if (ret
!= LDB_SUCCESS
) {
1225 if (ldb_request_get_control(req
, LDB_CONTROL_SHOW_RECYCLED_OID
) == NULL
) {
1226 ret
= ldb_request_add_control(req
, LDB_CONTROL_SHOW_RECYCLED_OID
, false, NULL
);
1227 if (ret
!= LDB_SUCCESS
) {
1232 if (ldb_request_get_control(req
, LDB_CONTROL_SHOW_DELETED_OID
) == NULL
) {
1233 ret
= ldb_request_add_control(req
, LDB_CONTROL_SHOW_DELETED_OID
, false, NULL
);
1234 if (ret
!= LDB_SUCCESS
) {
1239 if (dirsync_ctl
->flags
& LDAP_DIRSYNC_INCREMENTAL_VALUES
) {
1240 dsc
->linkIncrVal
= true;
1242 dsc
->linkIncrVal
= false;
1245 dsc
->our_invocation_id
= samdb_ntds_invocation_id(ldb
);
1246 if (dsc
->our_invocation_id
== NULL
) {
1247 return ldb_operr(ldb
);
1250 if (dirsync_ctl
->cookie_len
> 0) {
1251 struct ldapControlDirSyncCookie cookie
;
1253 blob
.data
= (uint8_t *)dirsync_ctl
->cookie
;
1254 blob
.length
= dirsync_ctl
->cookie_len
;
1255 ndr_err
= ndr_pull_struct_blob(&blob
, dsc
, &cookie
,
1256 (ndr_pull_flags_fn_t
)ndr_pull_ldapControlDirSyncCookie
);
1258 /* If we can't unmarshall the cookie into the correct structure we return
1259 * unsupported critical extension
1261 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1262 return ldb_error(ldb
, LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION
,
1263 "Unable to unmarshall cookie as a ldapControlDirSyncCookie structure");
1267 * Let's search for the max usn withing the cookie
1269 if (GUID_equal(&(cookie
.blob
.guid1
), dsc
->our_invocation_id
)) {
1271 * Ok, it's our invocation ID so we can treat the demand
1272 * Let's take the highest usn from (tmp)highest_usn
1274 dsc
->fromreqUSN
= cookie
.blob
.highwatermark
.tmp_highest_usn
;
1275 dsc
->localonly
= true;
1277 if (cookie
.blob
.highwatermark
.highest_usn
> cookie
.blob
.highwatermark
.tmp_highest_usn
) {
1278 dsc
->fromreqUSN
= cookie
.blob
.highwatermark
.highest_usn
;
1281 dsc
->localonly
= false;
1283 if (cookie
.blob
.extra_length
> 0 &&
1284 cookie
.blob
.extra
.uptodateness_vector
.ctr
.ctr1
.count
> 0) {
1285 struct drsuapi_DsReplicaCursor cursor
;
1287 for (p
=0; p
< cookie
.blob
.extra
.uptodateness_vector
.ctr
.ctr1
.count
; p
++) {
1288 cursor
= cookie
.blob
.extra
.uptodateness_vector
.ctr
.ctr1
.cursors
[p
];
1289 if (GUID_equal( &(cursor
.source_dsa_invocation_id
), dsc
->our_invocation_id
)) {
1290 if (cursor
.highest_usn
> dsc
->fromreqUSN
) {
1291 dsc
->fromreqUSN
= cursor
.highest_usn
;
1295 dsc
->cursors
= talloc_steal(dsc
,
1296 cookie
.blob
.extra
.uptodateness_vector
.ctr
.ctr1
.cursors
);
1297 if (dsc
->cursors
== NULL
) {
1298 return ldb_oom(ldb
);
1300 dsc
->cursor_size
= p
;
1304 DEBUG(4, ("Dirsync: searching with min usn > %llu\n",
1305 (long long unsigned int)dsc
->fromreqUSN
));
1306 if (dsc
->fromreqUSN
> 0) {
1307 /* FIXME it would be better to use PRId64 */
1308 char *expression
= talloc_asprintf(dsc
, "(&%s(uSNChanged>=%llu))",
1309 ldb_filter_from_tree(dsc
,
1310 req
->op
.search
.tree
),
1311 (long long unsigned int)(dsc
->fromreqUSN
+ 1));
1313 if (expression
== NULL
) {
1314 return ldb_oom(ldb
);
1316 new_tree
= ldb_parse_tree(req
, expression
);
1317 if (new_tree
== NULL
) {
1318 return ldb_error(ldb
, LDB_ERR_OPERATIONS_ERROR
,
1319 "Problem while parsing tree");
1324 * Remove our control from the list of controls
1326 if (!ldb_save_controls(control
, req
, NULL
)) {
1327 return ldb_operr(ldb
);
1329 dsc
->schema
= dsdb_get_schema(ldb
, dsc
);
1331 * At the begining we make the hypothesis that we will return a complete
1335 dsc
->partial
= false;
1338 * 3.1.1.3.4.1.3 of MS-ADTS.pdf specify that if the scope is not subtree
1339 * we treat the search as if subtree was specified
1342 ret
= ldb_build_search_req_ex(&down_req
, ldb
, dsc
,
1343 req
->op
.search
.base
,
1348 dsc
, dirsync_search_callback
,
1350 ldb_req_set_custom_flags(down_req
, flags
);
1351 LDB_REQ_SET_LOCATION(down_req
);
1352 if (ret
!= LDB_SUCCESS
) {
1355 /* perform the search */
1356 return ldb_next_request(module
, down_req
);
1359 static int dirsync_ldb_init(struct ldb_module
*module
)
1363 ret
= ldb_mod_register_control(module
, LDB_CONTROL_DIRSYNC_OID
);
1364 if (ret
!= LDB_SUCCESS
) {
1365 ldb_debug(ldb_module_get_ctx(module
), LDB_DEBUG_ERROR
,
1366 "dirsync: Unable to register control with rootdse!\n");
1367 return ldb_operr(ldb_module_get_ctx(module
));
1370 return ldb_next_init(module
);
1373 static const struct ldb_module_ops ldb_dirsync_ldb_module_ops
= {
1375 .search
= dirsync_ldb_search
,
1376 .init_context
= dirsync_ldb_init
,
1380 initialise the module
1382 _PUBLIC_
int ldb_dirsync_module_init(const char *version
)
1385 LDB_MODULE_CHECK_VERSION(version
);
1386 ret
= ldb_register_module(&ldb_dirsync_ldb_module_ops
);