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