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