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