Line data Source code
1 : /*
2 : ldb database library
3 :
4 : Copyright (C) Simo Sorce 2004-2008
5 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2013
6 : Copyright (C) Andrew Tridgell 2005-2009
7 : Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
8 : Copyright (C) Matthieu Patou <mat@samba.org> 2010-2011
9 :
10 : This program is free software; you can redistribute it and/or modify
11 : it under the terms of the GNU General Public License as published by
12 : the Free Software Foundation; either version 3 of the License, or
13 : (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program. If not, see <http://www.gnu.org/licenses/>.
22 : */
23 :
24 : /*
25 : * Name: ldb
26 : *
27 : * Component: ldb repl_meta_data module
28 : *
29 : * Description: - add a unique objectGUID onto every new record,
30 : * - handle whenCreated, whenChanged timestamps
31 : * - handle uSNCreated, uSNChanged numbers
32 : * - handle replPropertyMetaData attribute
33 : *
34 : * Author: Simo Sorce
35 : * Author: Stefan Metzmacher
36 : */
37 :
38 : #include "includes.h"
39 : #include "ldb_module.h"
40 : #include "dsdb/samdb/samdb.h"
41 : #include "dsdb/common/proto.h"
42 : #include "dsdb/common/util.h"
43 : #include "../libds/common/flags.h"
44 : #include "librpc/gen_ndr/irpc.h"
45 : #include "librpc/gen_ndr/ndr_misc.h"
46 : #include "librpc/gen_ndr/ndr_drsuapi.h"
47 : #include "librpc/gen_ndr/ndr_drsblobs.h"
48 : #include "param/param.h"
49 : #include "libcli/security/security.h"
50 : #include "lib/util/dlinklist.h"
51 : #include "dsdb/samdb/ldb_modules/util.h"
52 : #include "lib/util/tsort.h"
53 : #include "lib/util/binsearch.h"
54 :
55 : #undef strcasecmp
56 :
57 : #undef DBGC_CLASS
58 : #define DBGC_CLASS DBGC_DRS_REPL
59 :
60 : /* the RMD_VERSION for linked attributes starts from 1 */
61 : #define RMD_VERSION_INITIAL 1
62 :
63 : /*
64 : * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
65 : * Deleted Objects Container
66 : */
67 : static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
68 :
69 : struct replmd_private {
70 : TALLOC_CTX *la_ctx;
71 : struct la_group *la_list;
72 : struct nc_entry {
73 : struct nc_entry *prev, *next;
74 : struct ldb_dn *dn;
75 : uint64_t mod_usn;
76 : uint64_t mod_usn_urgent;
77 : } *ncs;
78 : struct ldb_dn *schema_dn;
79 : bool originating_updates;
80 : bool sorted_links;
81 : uint32_t total_links;
82 : uint32_t num_processed;
83 : bool recyclebin_enabled;
84 : bool recyclebin_state_known;
85 : };
86 :
87 : /*
88 : * groups link attributes together by source-object and attribute-ID,
89 : * to improve processing efficiency (i.e. for 'member' attribute, which
90 : * could have 100s or 1000s of links).
91 : * Note this grouping is best effort - the same source object could still
92 : * correspond to several la_groups (a lot depends on the order DRS sends
93 : * the links in). The groups currently don't span replication chunks (which
94 : * caps the size to ~1500 links by default).
95 : */
96 : struct la_group {
97 : struct la_group *next, *prev;
98 : struct la_entry *la_entries;
99 : };
100 :
101 : struct la_entry {
102 : struct la_entry *next, *prev;
103 : struct drsuapi_DsReplicaLinkedAttribute *la;
104 : uint32_t dsdb_repl_flags;
105 : };
106 :
107 : struct replmd_replicated_request {
108 : struct ldb_module *module;
109 : struct ldb_request *req;
110 :
111 : const struct dsdb_schema *schema;
112 : struct GUID our_invocation_id;
113 :
114 : /* the controls we pass down */
115 : struct ldb_control **controls;
116 :
117 : /*
118 : * Backlinks for the replmd_add() case (we want to create
119 : * backlinks after creating the user, but before the end of
120 : * the ADD request)
121 : */
122 : struct la_backlink *la_backlinks;
123 :
124 : /* details for the mode where we apply a bunch of inbound replication meessages */
125 : bool apply_mode;
126 : uint32_t index_current;
127 : struct dsdb_extended_replicated_objects *objs;
128 :
129 : struct ldb_message *search_msg;
130 : struct GUID local_parent_guid;
131 :
132 : uint64_t seq_num;
133 : bool is_urgent;
134 :
135 : bool isDeleted;
136 :
137 : bool fix_link_sid;
138 : };
139 :
140 : /*
141 : * the result of replmd_process_linked_attribute(): either there was no change
142 : * (update was ignored), a new link was added (either inactive or active), or
143 : * an existing link was modified (active/inactive status may have changed).
144 : */
145 : typedef enum {
146 : LINK_CHANGE_NONE,
147 : LINK_CHANGE_ADDED,
148 : LINK_CHANGE_MODIFIED,
149 : } replmd_link_changed;
150 :
151 : static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
152 : static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
153 : static int replmd_check_upgrade_links(struct ldb_context *ldb,
154 : struct parsed_dn *dns, uint32_t count,
155 : struct ldb_message_element *el,
156 : const char *ldap_oid);
157 : static int replmd_verify_link_target(struct replmd_replicated_request *ar,
158 : TALLOC_CTX *mem_ctx,
159 : struct la_entry *la_entry,
160 : struct ldb_dn *src_dn,
161 : const struct dsdb_attribute *attr);
162 : static int replmd_get_la_entry_source(struct ldb_module *module,
163 : struct la_entry *la_entry,
164 : TALLOC_CTX *mem_ctx,
165 : const struct dsdb_attribute **ret_attr,
166 : struct ldb_message **source_msg);
167 : static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
168 : struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
169 : uint64_t usn, uint64_t local_usn, NTTIME nttime,
170 : uint32_t version, bool deleted);
171 :
172 : static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
173 : struct ldb_context *ldb,
174 : struct ldb_dn *dn,
175 : const char *rdn_name,
176 : const struct ldb_val *rdn_value,
177 : struct GUID guid);
178 :
179 : enum urgent_situation {
180 : REPL_URGENT_ON_CREATE = 1,
181 : REPL_URGENT_ON_UPDATE = 2,
182 : REPL_URGENT_ON_DELETE = 4
183 : };
184 :
185 : enum deletion_state {
186 : OBJECT_NOT_DELETED=1,
187 : OBJECT_DELETED=2,
188 : OBJECT_RECYCLED=3,
189 : OBJECT_TOMBSTONE=4,
190 : OBJECT_REMOVED=5
191 : };
192 :
193 152720 : static bool replmd_recyclebin_enabled(struct ldb_module *module)
194 : {
195 152720 : bool enabled = false;
196 143670 : struct replmd_private *replmd_private =
197 152720 : talloc_get_type_abort(ldb_module_get_private(module),
198 : struct replmd_private);
199 :
200 : /*
201 : * only lookup the recycle-bin state once per replication, then cache
202 : * the result. This can save us 1000s of DB searches
203 : */
204 152720 : if (!replmd_private->recyclebin_state_known) {
205 31346 : int ret = dsdb_recyclebin_enabled(module, &enabled);
206 31346 : if (ret != LDB_SUCCESS) {
207 1127 : return false;
208 : }
209 :
210 30219 : replmd_private->recyclebin_enabled = enabled;
211 30219 : replmd_private->recyclebin_state_known = true;
212 : }
213 :
214 151593 : return replmd_private->recyclebin_enabled;
215 : }
216 :
217 152720 : static void replmd_deletion_state(struct ldb_module *module,
218 : const struct ldb_message *msg,
219 : enum deletion_state *current_state,
220 : enum deletion_state *next_state)
221 : {
222 152720 : bool enabled = false;
223 :
224 152720 : if (msg == NULL) {
225 0 : *current_state = OBJECT_REMOVED;
226 0 : if (next_state != NULL) {
227 0 : *next_state = OBJECT_REMOVED;
228 : }
229 0 : return;
230 : }
231 :
232 152720 : enabled = replmd_recyclebin_enabled(module);
233 :
234 152720 : if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
235 85762 : if (!enabled) {
236 85762 : *current_state = OBJECT_TOMBSTONE;
237 85762 : if (next_state != NULL) {
238 85583 : *next_state = OBJECT_REMOVED;
239 : }
240 85762 : return;
241 : }
242 :
243 0 : if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) {
244 0 : *current_state = OBJECT_RECYCLED;
245 0 : if (next_state != NULL) {
246 0 : *next_state = OBJECT_REMOVED;
247 : }
248 0 : return;
249 : }
250 :
251 0 : *current_state = OBJECT_DELETED;
252 0 : if (next_state != NULL) {
253 0 : *next_state = OBJECT_RECYCLED;
254 : }
255 0 : return;
256 : }
257 :
258 66958 : *current_state = OBJECT_NOT_DELETED;
259 66958 : if (next_state == NULL) {
260 19250 : return;
261 : }
262 :
263 47708 : if (enabled) {
264 0 : *next_state = OBJECT_DELETED;
265 : } else {
266 47708 : *next_state = OBJECT_TOMBSTONE;
267 : }
268 : }
269 :
270 : static const struct {
271 : const char *update_name;
272 : enum urgent_situation repl_situation;
273 : } urgent_objects[] = {
274 : {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
275 : {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
276 : {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
277 : {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
278 : {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
279 : {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
280 : {NULL, 0}
281 : };
282 :
283 : /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
284 : static const char *urgent_attrs[] = {
285 : "lockoutTime",
286 : "pwdLastSet",
287 : "userAccountControl",
288 : NULL
289 : };
290 :
291 :
292 645835 : static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
293 : enum urgent_situation situation)
294 : {
295 : unsigned int i, j;
296 3906399 : for (i=0; urgent_objects[i].update_name; i++) {
297 :
298 3424782 : if ((situation & urgent_objects[i].repl_situation) == 0) {
299 745848 : continue;
300 : }
301 :
302 8884950 : for (j=0; j<objectclass_el->num_values; j++) {
303 6370234 : const struct ldb_val *v = &objectclass_el->values[j];
304 6370234 : if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
305 164218 : return true;
306 : }
307 : }
308 : }
309 481617 : return false;
310 : }
311 :
312 407154 : static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
313 : {
314 407154 : if (ldb_attr_in_list(urgent_attrs, el->name)) {
315 23810 : return true;
316 : }
317 383344 : return false;
318 : }
319 :
320 : static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar);
321 :
322 : /*
323 : initialise the module
324 : allocate the private structure and build the list
325 : of partition DNs for use by replmd_notify()
326 : */
327 108013 : static int replmd_init(struct ldb_module *module)
328 : {
329 : struct replmd_private *replmd_private;
330 108013 : struct ldb_context *ldb = ldb_module_get_ctx(module);
331 : int ret;
332 :
333 108013 : replmd_private = talloc_zero(module, struct replmd_private);
334 108013 : if (replmd_private == NULL) {
335 0 : ldb_oom(ldb);
336 0 : return LDB_ERR_OPERATIONS_ERROR;
337 : }
338 :
339 108013 : ret = dsdb_check_samba_compatible_feature(module,
340 : SAMBA_SORTED_LINKS_FEATURE,
341 : &replmd_private->sorted_links);
342 108013 : if (ret != LDB_SUCCESS) {
343 0 : talloc_free(replmd_private);
344 0 : return ret;
345 : }
346 :
347 108013 : replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
348 108013 : ldb_module_set_private(module, replmd_private);
349 108013 : return ldb_next_init(module);
350 : }
351 :
352 : /*
353 : cleanup our per-transaction contexts
354 : */
355 488965 : static void replmd_txn_cleanup(struct replmd_private *replmd_private)
356 : {
357 488965 : talloc_free(replmd_private->la_ctx);
358 488965 : replmd_private->la_list = NULL;
359 488965 : replmd_private->la_ctx = NULL;
360 488965 : replmd_private->recyclebin_state_known = false;
361 488965 : }
362 :
363 :
364 : struct la_backlink {
365 : struct la_backlink *next, *prev;
366 : const char *attr_name;
367 : struct ldb_dn *forward_dn;
368 : struct GUID target_guid;
369 : bool active;
370 : };
371 :
372 : /*
373 : a ldb_modify request operating on modules below the
374 : current module
375 : */
376 2465 : static int linked_attr_modify(struct ldb_module *module,
377 : const struct ldb_message *message,
378 : struct ldb_request *parent)
379 : {
380 : struct ldb_request *mod_req;
381 : int ret;
382 2465 : struct ldb_context *ldb = ldb_module_get_ctx(module);
383 2465 : TALLOC_CTX *tmp_ctx = talloc_new(module);
384 : struct ldb_result *res;
385 :
386 2465 : res = talloc_zero(tmp_ctx, struct ldb_result);
387 2465 : if (!res) {
388 0 : talloc_free(tmp_ctx);
389 0 : return ldb_oom(ldb_module_get_ctx(module));
390 : }
391 :
392 2465 : ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
393 : message,
394 : NULL,
395 : res,
396 : ldb_modify_default_callback,
397 : parent);
398 2465 : LDB_REQ_SET_LOCATION(mod_req);
399 2465 : if (ret != LDB_SUCCESS) {
400 0 : talloc_free(tmp_ctx);
401 0 : return ret;
402 : }
403 :
404 2465 : ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
405 : false, NULL);
406 2465 : if (ret != LDB_SUCCESS) {
407 0 : return ret;
408 : }
409 :
410 : /* Run the new request */
411 2465 : ret = ldb_next_request(module, mod_req);
412 :
413 2465 : if (ret == LDB_SUCCESS) {
414 2465 : ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
415 : }
416 :
417 2465 : talloc_free(tmp_ctx);
418 2465 : return ret;
419 : }
420 :
421 : /*
422 : process a backlinks we accumulated during a transaction, adding and
423 : deleting the backlinks from the target objects
424 : */
425 34996 : static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
426 : {
427 : struct ldb_dn *target_dn, *source_dn;
428 : int ret;
429 34996 : struct ldb_context *ldb = ldb_module_get_ctx(module);
430 : struct ldb_message *msg;
431 34996 : TALLOC_CTX *frame = talloc_stackframe();
432 : char *dn_string;
433 :
434 : /*
435 : - find DN of target
436 : - find DN of source
437 : - construct ldb_message
438 : - either an add or a delete
439 : */
440 34996 : ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
441 34996 : if (ret != LDB_SUCCESS) {
442 : struct GUID_txt_buf guid_str;
443 19 : DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
444 : GUID_buf_string(&bl->target_guid, &guid_str));
445 19 : DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
446 19 : talloc_free(frame);
447 19 : return LDB_SUCCESS;
448 : }
449 :
450 34977 : msg = ldb_msg_new(frame);
451 34977 : if (msg == NULL) {
452 0 : ldb_module_oom(module);
453 0 : talloc_free(frame);
454 0 : return LDB_ERR_OPERATIONS_ERROR;
455 : }
456 :
457 34977 : source_dn = ldb_dn_copy(frame, bl->forward_dn);
458 34977 : if (!source_dn) {
459 0 : ldb_module_oom(module);
460 0 : talloc_free(frame);
461 0 : return LDB_ERR_OPERATIONS_ERROR;
462 : } else {
463 : /* Filter down to the attributes we want in the backlink */
464 34977 : const char *accept[] = { "GUID", "SID", NULL };
465 34977 : ldb_dn_extended_filter(source_dn, accept);
466 : }
467 :
468 : /* construct a ldb_message for adding/deleting the backlink */
469 34977 : msg->dn = target_dn;
470 34977 : dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
471 34977 : if (!dn_string) {
472 0 : ldb_module_oom(module);
473 0 : talloc_free(frame);
474 0 : return LDB_ERR_OPERATIONS_ERROR;
475 : }
476 34977 : ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
477 34977 : if (ret != LDB_SUCCESS) {
478 0 : talloc_free(frame);
479 0 : return ret;
480 : }
481 34977 : msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
482 :
483 : /* a backlink should never be single valued. Unfortunately the
484 : exchange schema has a attribute
485 : msExchBridgeheadedLocalConnectorsDNBL which is single
486 : valued and a backlink. We need to cope with that by
487 : ignoring the single value flag */
488 34977 : msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
489 :
490 34977 : ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
491 34977 : if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
492 : /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
493 : cope with possible corruption where the backlink has
494 : already been removed */
495 330 : DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
496 : ldb_dn_get_linearized(target_dn),
497 : ldb_dn_get_linearized(source_dn),
498 : ldb_errstring(ldb)));
499 330 : ret = LDB_SUCCESS;
500 34647 : } else if (ret != LDB_SUCCESS) {
501 0 : ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
502 0 : bl->active?"add":"remove",
503 : ldb_dn_get_linearized(source_dn),
504 : ldb_dn_get_linearized(target_dn),
505 : ldb_errstring(ldb));
506 0 : talloc_free(frame);
507 0 : return ret;
508 : }
509 34977 : talloc_free(frame);
510 34977 : return ret;
511 : }
512 :
513 : /*
514 : add a backlink to the list of backlinks to add/delete in the prepare
515 : commit
516 :
517 : forward_dn is stolen onto the defereed context
518 : */
519 6450 : static int replmd_defer_add_backlink(struct ldb_module *module,
520 : struct replmd_private *replmd_private,
521 : const struct dsdb_schema *schema,
522 : struct replmd_replicated_request *ac,
523 : struct ldb_dn *forward_dn,
524 : struct GUID *target_guid, bool active,
525 : const struct dsdb_attribute *schema_attr,
526 : struct ldb_request *parent)
527 : {
528 : const struct dsdb_attribute *target_attr;
529 : struct la_backlink *bl;
530 :
531 6450 : bl = talloc(ac, struct la_backlink);
532 6450 : if (bl == NULL) {
533 0 : ldb_module_oom(module);
534 0 : return LDB_ERR_OPERATIONS_ERROR;
535 : }
536 :
537 6450 : target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
538 6450 : if (!target_attr) {
539 : /*
540 : * windows 2003 has a broken schema where the
541 : * definition of msDS-IsDomainFor is missing (which is
542 : * supposed to be the backlink of the
543 : * msDS-HasDomainNCs attribute
544 : */
545 817 : return LDB_SUCCESS;
546 : }
547 :
548 5633 : bl->attr_name = target_attr->lDAPDisplayName;
549 5633 : bl->forward_dn = talloc_steal(bl, forward_dn);
550 5633 : bl->target_guid = *target_guid;
551 5633 : bl->active = active;
552 :
553 5633 : DLIST_ADD(ac->la_backlinks, bl);
554 :
555 5633 : return LDB_SUCCESS;
556 : }
557 :
558 : /*
559 : add a backlink to the list of backlinks to add/delete in the prepare
560 : commit
561 : */
562 35977 : static int replmd_add_backlink(struct ldb_module *module,
563 : struct replmd_private *replmd_private,
564 : const struct dsdb_schema *schema,
565 : struct ldb_dn *forward_dn,
566 : struct GUID *target_guid, bool active,
567 : const struct dsdb_attribute *schema_attr,
568 : struct ldb_request *parent)
569 : {
570 : const struct dsdb_attribute *target_attr;
571 : struct la_backlink bl;
572 : int ret;
573 :
574 35977 : target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
575 35977 : if (!target_attr) {
576 : /*
577 : * windows 2003 has a broken schema where the
578 : * definition of msDS-IsDomainFor is missing (which is
579 : * supposed to be the backlink of the
580 : * msDS-HasDomainNCs attribute
581 : */
582 6612 : return LDB_SUCCESS;
583 : }
584 :
585 29365 : bl.attr_name = target_attr->lDAPDisplayName;
586 29365 : bl.forward_dn = forward_dn;
587 29365 : bl.target_guid = *target_guid;
588 29365 : bl.active = active;
589 :
590 29365 : ret = replmd_process_backlink(module, &bl, parent);
591 29365 : return ret;
592 : }
593 :
594 :
595 : /*
596 : * Callback for most write operations in this module:
597 : *
598 : * notify the repl task that a object has changed. The notifies are
599 : * gathered up in the replmd_private structure then written to the
600 : * @REPLCHANGED object in each partition during the prepare_commit
601 : */
602 1042776 : static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
603 : {
604 : int ret;
605 926881 : struct replmd_replicated_request *ac =
606 1042776 : talloc_get_type_abort(req->context, struct replmd_replicated_request);
607 926881 : struct replmd_private *replmd_private =
608 1042776 : talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
609 : struct nc_entry *modified_partition;
610 : struct ldb_control *partition_ctrl;
611 : const struct dsdb_control_current_partition *partition;
612 :
613 : struct ldb_control **controls;
614 :
615 1042776 : partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
616 :
617 1042776 : controls = ares->controls;
618 1042776 : if (ldb_request_get_control(ac->req,
619 : DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
620 : /*
621 : * Remove the current partition control from what we pass up
622 : * the chain if it hasn't been requested manually.
623 : */
624 1042776 : controls = ldb_controls_except_specified(ares->controls, ares,
625 : partition_ctrl);
626 : }
627 :
628 1042776 : if (ares->error != LDB_SUCCESS) {
629 : struct GUID_txt_buf guid_txt;
630 365 : struct ldb_message *msg = NULL;
631 365 : char *s = NULL;
632 :
633 365 : if (ac->apply_mode == false) {
634 365 : DBG_NOTICE("Originating update failure. Error is: %s\n",
635 : ldb_strerror(ares->error));
636 365 : return ldb_module_done(ac->req, controls,
637 : ares->response, ares->error);
638 : }
639 :
640 0 : msg = ac->objs->objects[ac->index_current].msg;
641 : /*
642 : * Set at DBG_NOTICE as once these start to happe, they
643 : * will happen a lot until resolved, due to repeated
644 : * replication. The caller will probably print the
645 : * ldb error string anyway.
646 : */
647 0 : DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
648 : ldb_dn_get_linearized(msg->dn),
649 : ldb_strerror(ares->error));
650 :
651 0 : s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
652 : ac,
653 : LDB_CHANGETYPE_ADD,
654 : msg);
655 :
656 0 : DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
657 : ac->search_msg == NULL ? "ADD" : "MODIFY",
658 : GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
659 : &guid_txt),
660 : s);
661 0 : talloc_free(s);
662 0 : return ldb_module_done(ac->req, controls,
663 : ares->response, ares->error);
664 : }
665 :
666 1042411 : if (ares->type != LDB_REPLY_DONE) {
667 0 : ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
668 0 : return ldb_module_done(ac->req, NULL,
669 : NULL, LDB_ERR_OPERATIONS_ERROR);
670 : }
671 :
672 1042411 : if (ac->apply_mode == false) {
673 : struct la_backlink *bl;
674 : /*
675 : * process our backlink list after an replmd_add(),
676 : * creating and deleting backlinks as necessary (this
677 : * code is sync). The other cases are handled inline
678 : * with the modify.
679 : */
680 674872 : for (bl=ac->la_backlinks; bl; bl=bl->next) {
681 5631 : ret = replmd_process_backlink(ac->module, bl, ac->req);
682 5631 : if (ret != LDB_SUCCESS) {
683 0 : return ldb_module_done(ac->req, NULL,
684 : NULL, ret);
685 : }
686 : }
687 : }
688 :
689 1042411 : if (!partition_ctrl) {
690 0 : ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
691 0 : return ldb_module_done(ac->req, NULL,
692 : NULL, LDB_ERR_OPERATIONS_ERROR);
693 : }
694 :
695 1042411 : partition = talloc_get_type_abort(partition_ctrl->data,
696 : struct dsdb_control_current_partition);
697 :
698 1042411 : if (ac->seq_num > 0) {
699 1800457 : for (modified_partition = replmd_private->ncs; modified_partition;
700 182493 : modified_partition = modified_partition->next) {
701 889031 : if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
702 706538 : break;
703 : }
704 : }
705 :
706 862064 : if (modified_partition == NULL) {
707 155526 : modified_partition = talloc_zero(replmd_private, struct nc_entry);
708 155526 : if (!modified_partition) {
709 0 : ldb_oom(ldb_module_get_ctx(ac->module));
710 0 : return ldb_module_done(ac->req, NULL,
711 : NULL, LDB_ERR_OPERATIONS_ERROR);
712 : }
713 155526 : modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
714 155526 : if (!modified_partition->dn) {
715 0 : ldb_oom(ldb_module_get_ctx(ac->module));
716 0 : return ldb_module_done(ac->req, NULL,
717 : NULL, LDB_ERR_OPERATIONS_ERROR);
718 : }
719 155526 : DLIST_ADD(replmd_private->ncs, modified_partition);
720 : }
721 :
722 862064 : if (ac->seq_num > modified_partition->mod_usn) {
723 862024 : modified_partition->mod_usn = ac->seq_num;
724 862024 : if (ac->is_urgent) {
725 176817 : modified_partition->mod_usn_urgent = ac->seq_num;
726 : }
727 : }
728 862064 : if (!ac->apply_mode) {
729 488894 : replmd_private->originating_updates = true;
730 : }
731 : }
732 :
733 1042411 : if (ac->apply_mode) {
734 373170 : ret = replmd_replicated_apply_isDeleted(ac);
735 373170 : if (ret != LDB_SUCCESS) {
736 30 : return ldb_module_done(ac->req, NULL, NULL, ret);
737 : }
738 373140 : return ret;
739 : } else {
740 : /* free the partition control container here, for the
741 : * common path. Other cases will have it cleaned up
742 : * eventually with the ares */
743 669241 : talloc_free(partition_ctrl);
744 669241 : return ldb_module_done(ac->req, controls,
745 : ares->response, LDB_SUCCESS);
746 : }
747 : }
748 :
749 :
750 : /*
751 : * update a @REPLCHANGED record in each partition if there have been
752 : * any writes of replicated data in the partition
753 : */
754 212233 : static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
755 : {
756 167038 : struct replmd_private *replmd_private =
757 212233 : talloc_get_type(ldb_module_get_private(module), struct replmd_private);
758 :
759 534599 : while (replmd_private->ncs) {
760 : int ret;
761 155328 : struct nc_entry *modified_partition = replmd_private->ncs;
762 :
763 155328 : ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
764 : modified_partition->mod_usn,
765 : modified_partition->mod_usn_urgent, parent);
766 155328 : if (ret != LDB_SUCCESS) {
767 0 : DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
768 : ldb_dn_get_linearized(modified_partition->dn)));
769 0 : return ret;
770 : }
771 :
772 155328 : if (ldb_dn_compare(modified_partition->dn,
773 : replmd_private->schema_dn) == 0) {
774 : struct ldb_result *ext_res;
775 2596 : ret = dsdb_module_extended(module,
776 1448 : replmd_private->schema_dn,
777 : &ext_res,
778 : DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
779 : ext_res,
780 : DSDB_FLAG_NEXT_MODULE,
781 : parent);
782 1448 : if (ret != LDB_SUCCESS) {
783 0 : return ret;
784 : }
785 1448 : talloc_free(ext_res);
786 : }
787 :
788 155328 : DLIST_REMOVE(replmd_private->ncs, modified_partition);
789 155328 : talloc_free(modified_partition);
790 : }
791 :
792 212233 : return LDB_SUCCESS;
793 : }
794 :
795 :
796 : /*
797 : created a replmd_replicated_request context
798 : */
799 673233 : static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
800 : struct ldb_request *req)
801 : {
802 : struct ldb_context *ldb;
803 : struct replmd_replicated_request *ac;
804 : const struct GUID *our_invocation_id;
805 :
806 673233 : ldb = ldb_module_get_ctx(module);
807 :
808 673233 : ac = talloc_zero(req, struct replmd_replicated_request);
809 673233 : if (ac == NULL) {
810 0 : ldb_oom(ldb);
811 0 : return NULL;
812 : }
813 :
814 673233 : ac->module = module;
815 673233 : ac->req = req;
816 :
817 673233 : ac->schema = dsdb_get_schema(ldb, ac);
818 673233 : if (!ac->schema) {
819 0 : ldb_debug_set(ldb, LDB_DEBUG_FATAL,
820 : "replmd_modify: no dsdb_schema loaded");
821 0 : DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
822 0 : talloc_free(ac);
823 0 : return NULL;
824 : }
825 :
826 : /* get our invocationId */
827 673233 : our_invocation_id = samdb_ntds_invocation_id(ldb);
828 673233 : if (!our_invocation_id) {
829 0 : ldb_debug_set(ldb, LDB_DEBUG_FATAL,
830 : "replmd_add: unable to find invocationId\n");
831 0 : talloc_free(ac);
832 0 : return NULL;
833 : }
834 673233 : ac->our_invocation_id = *our_invocation_id;
835 :
836 673233 : return ac;
837 : }
838 :
839 : /*
840 : add a time element to a record
841 : */
842 172344 : static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
843 : {
844 : struct ldb_message_element *el;
845 : char *s;
846 : int ret;
847 :
848 172344 : if (ldb_msg_find_element(msg, attr) != NULL) {
849 0 : return LDB_SUCCESS;
850 : }
851 :
852 172344 : s = ldb_timestring(msg, t);
853 172344 : if (s == NULL) {
854 0 : return LDB_ERR_OPERATIONS_ERROR;
855 : }
856 :
857 172344 : ret = ldb_msg_add_string(msg, attr, s);
858 172344 : if (ret != LDB_SUCCESS) {
859 0 : return ret;
860 : }
861 :
862 172344 : el = ldb_msg_find_element(msg, attr);
863 : /* always set as replace. This works because on add ops, the flag
864 : is ignored */
865 172344 : el->flags = LDB_FLAG_MOD_REPLACE;
866 :
867 172344 : return LDB_SUCCESS;
868 : }
869 :
870 : /*
871 : add a uint64_t element to a record
872 : */
873 172344 : static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
874 : const char *attr, uint64_t v)
875 : {
876 : struct ldb_message_element *el;
877 : int ret;
878 :
879 172344 : if (ldb_msg_find_element(msg, attr) != NULL) {
880 0 : return LDB_SUCCESS;
881 : }
882 :
883 172344 : ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
884 172344 : if (ret != LDB_SUCCESS) {
885 0 : return ret;
886 : }
887 :
888 172344 : el = ldb_msg_find_element(msg, attr);
889 : /* always set as replace. This works because on add ops, the flag
890 : is ignored */
891 172344 : el->flags = LDB_FLAG_MOD_REPLACE;
892 :
893 172344 : return LDB_SUCCESS;
894 : }
895 :
896 34610397 : static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
897 : const struct replPropertyMetaData1 *m2)
898 : {
899 : /*
900 : * This assignment seems inoccous, but it is critical for the
901 : * system, as we need to do the comparisons as a unsigned
902 : * quantity, not signed (enums are signed integers)
903 : */
904 34610397 : uint32_t attid_1 = m1->attid;
905 34610397 : uint32_t attid_2 = m2->attid;
906 :
907 34610397 : if (attid_1 == attid_2) {
908 0 : return 0;
909 : }
910 :
911 : /*
912 : * See above regarding this being an unsigned comparison.
913 : * Otherwise when the high bit is set on non-standard
914 : * attributes, they would end up first, before objectClass
915 : * (0).
916 : */
917 34610397 : return attid_1 > attid_2 ? 1 : -1;
918 : }
919 :
920 909991 : static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
921 : struct replPropertyMetaDataCtr1 *ctr1,
922 : struct ldb_dn *dn)
923 : {
924 909991 : if (ctr1->count == 0) {
925 0 : ldb_debug_set(ldb, LDB_DEBUG_FATAL,
926 : "No elements found in replPropertyMetaData for %s!\n",
927 : ldb_dn_get_linearized(dn));
928 0 : return LDB_ERR_CONSTRAINT_VIOLATION;
929 : }
930 :
931 : /* the objectClass attribute is value 0x00000000, so must be first */
932 909991 : if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
933 0 : ldb_debug_set(ldb, LDB_DEBUG_FATAL,
934 : "No objectClass found in replPropertyMetaData for %s!\n",
935 : ldb_dn_get_linearized(dn));
936 0 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
937 : }
938 :
939 909991 : return LDB_SUCCESS;
940 : }
941 :
942 909991 : static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
943 : struct replPropertyMetaDataCtr1 *ctr1,
944 : struct ldb_dn *dn)
945 : {
946 : /* Note this is O(n^2) for the almost-sorted case, which this is */
947 909991 : TYPESAFE_QSORT(ctr1->array, ctr1->count,
948 : replmd_replPropertyMetaData1_attid_sort);
949 909991 : return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
950 : }
951 :
952 64270096 : static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
953 : const struct ldb_message_element *e2,
954 : const struct dsdb_schema *schema)
955 : {
956 : const struct dsdb_attribute *a1;
957 : const struct dsdb_attribute *a2;
958 :
959 : /*
960 : * TODO: make this faster by caching the dsdb_attribute pointer
961 : * on the ldb_messag_element
962 : */
963 :
964 64270096 : a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
965 64270096 : a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
966 :
967 : /*
968 : * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
969 : * in the schema
970 : */
971 64270096 : if (!a1 || !a2) {
972 0 : return strcasecmp(e1->name, e2->name);
973 : }
974 64270096 : if (a1->attributeID_id == a2->attributeID_id) {
975 4326204 : return 0;
976 : }
977 59943892 : return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
978 : }
979 :
980 692547 : static void replmd_ldb_message_sort(struct ldb_message *msg,
981 : const struct dsdb_schema *schema)
982 : {
983 692547 : LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
984 692547 : }
985 :
986 : static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
987 : const struct GUID *invocation_id,
988 : uint64_t local_usn, NTTIME nttime);
989 :
990 : static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
991 :
992 : static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
993 : struct ldb_message_element *el, struct parsed_dn **pdn,
994 : const char *ldap_oid, struct ldb_request *parent);
995 :
996 : static int check_parsed_dn_duplicates(struct ldb_module *module,
997 : struct ldb_message_element *el,
998 : struct parsed_dn *pdn);
999 :
1000 : /*
1001 : fix up linked attributes in replmd_add.
1002 : This involves setting up the right meta-data in extended DN
1003 : components, and creating backlinks to the object
1004 : */
1005 2596 : static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1006 : struct replmd_private *replmd_private,
1007 : struct ldb_message_element *el,
1008 : struct replmd_replicated_request *ac,
1009 : NTTIME now,
1010 : struct ldb_dn *forward_dn,
1011 : const struct dsdb_attribute *sa,
1012 : struct ldb_request *parent)
1013 : {
1014 : unsigned int i;
1015 2596 : TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1016 2596 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1017 : struct parsed_dn *pdn;
1018 : /* We will take a reference to the schema in replmd_add_backlink */
1019 2596 : const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
1020 2596 : struct ldb_val *new_values = NULL;
1021 : int ret;
1022 :
1023 2596 : if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
1024 2595 : el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1025 : } else {
1026 1 : ldb_asprintf_errstring(ldb,
1027 : "Attribute %s is single valued but "
1028 : "more than one value has been supplied",
1029 : el->name);
1030 1 : talloc_free(tmp_ctx);
1031 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
1032 : }
1033 :
1034 : /*
1035 : * At the successful end of these functions el->values is
1036 : * overwritten with new_values. However get_parsed_dns()
1037 : * points p->v at the supplied el and it effectively gets used
1038 : * as a working area by replmd_build_la_val(). So we must
1039 : * duplicate it because our caller only called
1040 : * ldb_msg_copy_shallow().
1041 : */
1042 :
1043 2595 : el->values = talloc_memdup(tmp_ctx,
1044 : el->values,
1045 : sizeof(el->values[0]) * el->num_values);
1046 2595 : if (el->values == NULL) {
1047 0 : ldb_module_oom(module);
1048 0 : talloc_free(tmp_ctx);
1049 0 : return LDB_ERR_OPERATIONS_ERROR;
1050 : }
1051 :
1052 2595 : ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
1053 2595 : sa->syntax->ldap_oid, parent);
1054 2595 : if (ret != LDB_SUCCESS) {
1055 0 : talloc_free(tmp_ctx);
1056 0 : return ret;
1057 : }
1058 :
1059 2595 : ret = check_parsed_dn_duplicates(module, el, pdn);
1060 2595 : if (ret != LDB_SUCCESS) {
1061 1 : talloc_free(tmp_ctx);
1062 1 : return ret;
1063 : }
1064 :
1065 2594 : new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
1066 2594 : if (new_values == NULL) {
1067 0 : ldb_module_oom(module);
1068 0 : talloc_free(tmp_ctx);
1069 0 : return LDB_ERR_OPERATIONS_ERROR;
1070 : }
1071 :
1072 9044 : for (i = 0; i < el->num_values; i++) {
1073 6450 : struct parsed_dn *p = &pdn[i];
1074 11990 : ret = replmd_build_la_val(new_values, p->v, p->dsdb_dn,
1075 6450 : &ac->our_invocation_id,
1076 : ac->seq_num, now);
1077 6450 : if (ret != LDB_SUCCESS) {
1078 0 : talloc_free(tmp_ctx);
1079 0 : return ret;
1080 : }
1081 :
1082 6450 : ret = replmd_defer_add_backlink(module, replmd_private,
1083 : schema, ac,
1084 : forward_dn, &p->guid, true, sa,
1085 : parent);
1086 6450 : if (ret != LDB_SUCCESS) {
1087 0 : talloc_free(tmp_ctx);
1088 0 : return ret;
1089 : }
1090 :
1091 6450 : new_values[i] = *p->v;
1092 : }
1093 2594 : el->values = talloc_steal(mem_ctx, new_values);
1094 :
1095 2594 : talloc_free(tmp_ctx);
1096 2594 : return LDB_SUCCESS;
1097 : }
1098 :
1099 2006 : static int replmd_add_make_extended_dn(struct ldb_request *req,
1100 : const DATA_BLOB *guid_blob,
1101 : struct ldb_dn **_extended_dn)
1102 : {
1103 : int ret;
1104 : const DATA_BLOB *sid_blob;
1105 : /* Calculate an extended DN for any linked attributes */
1106 2006 : struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
1107 2006 : if (!extended_dn) {
1108 0 : return LDB_ERR_OPERATIONS_ERROR;
1109 : }
1110 2006 : ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
1111 2006 : if (ret != LDB_SUCCESS) {
1112 0 : return ret;
1113 : }
1114 :
1115 2006 : sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
1116 2006 : if (sid_blob != NULL) {
1117 1009 : ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
1118 1009 : if (ret != LDB_SUCCESS) {
1119 0 : return ret;
1120 : }
1121 : }
1122 2006 : *_extended_dn = extended_dn;
1123 2006 : return LDB_SUCCESS;
1124 : }
1125 :
1126 : /*
1127 : intercept add requests
1128 : */
1129 319750 : static int replmd_add(struct ldb_module *module, struct ldb_request *req)
1130 : {
1131 : struct ldb_context *ldb;
1132 : struct ldb_control *control;
1133 : struct replmd_replicated_request *ac;
1134 : enum ndr_err_code ndr_err;
1135 : struct ldb_request *down_req;
1136 : struct ldb_message *msg;
1137 : const DATA_BLOB *guid_blob;
1138 : DATA_BLOB guid_blob_stack;
1139 : struct GUID guid;
1140 : uint8_t guid_data[16];
1141 : struct replPropertyMetaDataBlob nmd;
1142 : struct ldb_val nmd_value;
1143 319750 : struct ldb_dn *extended_dn = NULL;
1144 :
1145 : /*
1146 : * The use of a time_t here seems odd, but as the NTTIME
1147 : * elements are actually declared as NTTIME_1sec in the IDL,
1148 : * getting a higher resolution timestamp is not required.
1149 : */
1150 319750 : time_t t = time(NULL);
1151 : NTTIME now;
1152 : char *time_str;
1153 : int ret;
1154 : unsigned int i;
1155 : unsigned int functional_level;
1156 319750 : uint32_t ni=0;
1157 319750 : bool allow_add_guid = false;
1158 319750 : bool remove_current_guid = false;
1159 319750 : bool is_urgent = false;
1160 319750 : bool is_schema_nc = false;
1161 : struct ldb_message_element *objectclass_el;
1162 257264 : struct replmd_private *replmd_private =
1163 319750 : talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
1164 :
1165 : /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
1166 319750 : control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
1167 319750 : if (control) {
1168 159671 : allow_add_guid = true;
1169 : }
1170 :
1171 : /* do not manipulate our control entries */
1172 319750 : if (ldb_dn_is_special(req->op.add.message->dn)) {
1173 371 : return ldb_next_request(module, req);
1174 : }
1175 :
1176 319379 : ldb = ldb_module_get_ctx(module);
1177 :
1178 319379 : ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
1179 :
1180 319379 : guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
1181 319379 : if (guid_blob != NULL) {
1182 126367 : if (!allow_add_guid) {
1183 1 : ldb_set_errstring(ldb,
1184 : "replmd_add: it's not allowed to add an object with objectGUID!");
1185 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
1186 : } else {
1187 126366 : NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
1188 126366 : if (!NT_STATUS_IS_OK(status)) {
1189 0 : ldb_set_errstring(ldb,
1190 : "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
1191 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
1192 : }
1193 : /* we remove this attribute as it can be a string and
1194 : * will not be treated correctly and then we will re-add
1195 : * it later on in the good format */
1196 126366 : remove_current_guid = true;
1197 : }
1198 : } else {
1199 : /* a new GUID */
1200 193012 : guid = GUID_random();
1201 :
1202 193012 : guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
1203 :
1204 : /* This can't fail */
1205 193012 : ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
1206 : (ndr_push_flags_fn_t)ndr_push_GUID);
1207 193012 : guid_blob = &guid_blob_stack;
1208 : }
1209 :
1210 319378 : ac = replmd_ctx_init(module, req);
1211 319378 : if (ac == NULL) {
1212 0 : return ldb_module_oom(module);
1213 : }
1214 :
1215 319378 : functional_level = dsdb_functional_level(ldb);
1216 :
1217 : /* Get a sequence number from the backend */
1218 319378 : ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1219 319378 : if (ret != LDB_SUCCESS) {
1220 0 : talloc_free(ac);
1221 0 : return ret;
1222 : }
1223 :
1224 : /* we have to copy the message as the caller might have it as a const */
1225 319378 : msg = ldb_msg_copy_shallow(ac, req->op.add.message);
1226 319378 : if (msg == NULL) {
1227 0 : ldb_oom(ldb);
1228 0 : talloc_free(ac);
1229 0 : return LDB_ERR_OPERATIONS_ERROR;
1230 : }
1231 :
1232 : /* generated times */
1233 319378 : unix_to_nt_time(&now, t);
1234 319378 : time_str = ldb_timestring(msg, t);
1235 319378 : if (!time_str) {
1236 0 : ldb_oom(ldb);
1237 0 : talloc_free(ac);
1238 0 : return LDB_ERR_OPERATIONS_ERROR;
1239 : }
1240 319378 : if (remove_current_guid) {
1241 126366 : ldb_msg_remove_attr(msg,"objectGUID");
1242 : }
1243 :
1244 : /*
1245 : * remove autogenerated attributes
1246 : */
1247 319378 : ldb_msg_remove_attr(msg, "whenCreated");
1248 319378 : ldb_msg_remove_attr(msg, "whenChanged");
1249 319378 : ldb_msg_remove_attr(msg, "uSNCreated");
1250 319378 : ldb_msg_remove_attr(msg, "uSNChanged");
1251 319378 : ldb_msg_remove_attr(msg, "replPropertyMetaData");
1252 :
1253 : /*
1254 : * readd replicated attributes
1255 : */
1256 319378 : ret = ldb_msg_add_string(msg, "whenCreated", time_str);
1257 319378 : if (ret != LDB_SUCCESS) {
1258 0 : ldb_oom(ldb);
1259 0 : talloc_free(ac);
1260 0 : return ret;
1261 : }
1262 :
1263 : /* build the replication meta_data */
1264 319378 : ZERO_STRUCT(nmd);
1265 319378 : nmd.version = 1;
1266 319378 : nmd.ctr.ctr1.count = msg->num_elements;
1267 319378 : nmd.ctr.ctr1.array = talloc_array(msg,
1268 : struct replPropertyMetaData1,
1269 : nmd.ctr.ctr1.count);
1270 319378 : if (!nmd.ctr.ctr1.array) {
1271 0 : ldb_oom(ldb);
1272 0 : talloc_free(ac);
1273 0 : return LDB_ERR_OPERATIONS_ERROR;
1274 : }
1275 :
1276 319378 : is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
1277 :
1278 5645765 : for (i=0; i < msg->num_elements;) {
1279 5069448 : struct ldb_message_element *e = &msg->elements[i];
1280 5069448 : struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
1281 : const struct dsdb_attribute *sa;
1282 :
1283 5069448 : if (e->name[0] == '@') {
1284 0 : i++;
1285 0 : continue;
1286 : }
1287 :
1288 5069448 : sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
1289 5069448 : if (!sa) {
1290 0 : ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1291 : "replmd_add: attribute '%s' not defined in schema\n",
1292 : e->name);
1293 0 : talloc_free(ac);
1294 0 : return LDB_ERR_NO_SUCH_ATTRIBUTE;
1295 : }
1296 :
1297 5069448 : if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1298 : /* if the attribute is not replicated (0x00000001)
1299 : * or constructed (0x00000004) it has no metadata
1300 : */
1301 98004 : i++;
1302 98004 : continue;
1303 : }
1304 :
1305 4971444 : if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
1306 2596 : if (extended_dn == NULL) {
1307 2006 : ret = replmd_add_make_extended_dn(req,
1308 : guid_blob,
1309 : &extended_dn);
1310 2006 : if (ret != LDB_SUCCESS) {
1311 0 : talloc_free(ac);
1312 0 : return ret;
1313 : }
1314 : }
1315 :
1316 : /*
1317 : * Prepare the context for the backlinks and
1318 : * create metadata for the forward links. The
1319 : * backlinks are created in
1320 : * replmd_op_callback() after the successful
1321 : * ADD of the object.
1322 : */
1323 2596 : ret = replmd_add_fix_la(module, msg->elements,
1324 : replmd_private, e,
1325 : ac, now,
1326 : extended_dn,
1327 : sa, req);
1328 2596 : if (ret != LDB_SUCCESS) {
1329 2 : talloc_free(ac);
1330 2 : return ret;
1331 : }
1332 : /* linked attributes are not stored in
1333 : replPropertyMetaData in FL above w2k */
1334 2594 : i++;
1335 2594 : continue;
1336 : }
1337 :
1338 4968848 : m->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
1339 4968848 : m->version = 1;
1340 4968848 : if (m->attid == DRSUAPI_ATTID_isDeleted) {
1341 285 : const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1342 : const char* rdn;
1343 :
1344 285 : if (rdn_val == NULL) {
1345 0 : ldb_oom(ldb);
1346 0 : talloc_free(ac);
1347 0 : return LDB_ERR_OPERATIONS_ERROR;
1348 : }
1349 :
1350 285 : rdn = (const char*)rdn_val->data;
1351 285 : if (strcmp(rdn, "Deleted Objects") == 0) {
1352 : /*
1353 : * Set the originating_change_time to 29/12/9999 at 23:59:59
1354 : * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1355 : */
1356 282 : m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1357 : } else {
1358 3 : m->originating_change_time = now;
1359 : }
1360 : } else {
1361 4968563 : m->originating_change_time = now;
1362 : }
1363 4968848 : m->originating_invocation_id = ac->our_invocation_id;
1364 4968848 : m->originating_usn = ac->seq_num;
1365 4968848 : m->local_usn = ac->seq_num;
1366 4968848 : ni++;
1367 :
1368 4968848 : if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1369 4851046 : i++;
1370 4851046 : continue;
1371 : }
1372 :
1373 117802 : e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1374 :
1375 117802 : if (e->num_values != 0) {
1376 22616 : i++;
1377 22616 : continue;
1378 : }
1379 :
1380 95186 : ldb_msg_remove_element(msg, e);
1381 : }
1382 :
1383 : /* fix meta data count */
1384 319376 : nmd.ctr.ctr1.count = ni;
1385 :
1386 : /*
1387 : * sort meta data array
1388 : */
1389 319376 : ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
1390 319376 : if (ret != LDB_SUCCESS) {
1391 0 : ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
1392 0 : talloc_free(ac);
1393 0 : return ret;
1394 : }
1395 :
1396 : /* generated NDR encoded values */
1397 319376 : ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1398 : &nmd,
1399 : (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1400 319376 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1401 0 : ldb_oom(ldb);
1402 0 : talloc_free(ac);
1403 0 : return LDB_ERR_OPERATIONS_ERROR;
1404 : }
1405 :
1406 : /*
1407 : * add the autogenerated values
1408 : */
1409 319376 : ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
1410 319376 : if (ret != LDB_SUCCESS) {
1411 0 : ldb_oom(ldb);
1412 0 : talloc_free(ac);
1413 0 : return ret;
1414 : }
1415 319376 : ret = ldb_msg_add_string(msg, "whenChanged", time_str);
1416 319376 : if (ret != LDB_SUCCESS) {
1417 0 : ldb_oom(ldb);
1418 0 : talloc_free(ac);
1419 0 : return ret;
1420 : }
1421 319376 : ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
1422 319376 : if (ret != LDB_SUCCESS) {
1423 0 : ldb_oom(ldb);
1424 0 : talloc_free(ac);
1425 0 : return ret;
1426 : }
1427 319376 : ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1428 319376 : if (ret != LDB_SUCCESS) {
1429 0 : ldb_oom(ldb);
1430 0 : talloc_free(ac);
1431 0 : return ret;
1432 : }
1433 319376 : ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1434 319376 : if (ret != LDB_SUCCESS) {
1435 0 : ldb_oom(ldb);
1436 0 : talloc_free(ac);
1437 0 : return ret;
1438 : }
1439 :
1440 : /*
1441 : * sort the attributes by attid before storing the object
1442 : */
1443 319376 : replmd_ldb_message_sort(msg, ac->schema);
1444 :
1445 : /*
1446 : * Assert that we do have an objectClass
1447 : */
1448 319376 : objectclass_el = ldb_msg_find_element(msg, "objectClass");
1449 319376 : if (objectclass_el == NULL) {
1450 0 : ldb_asprintf_errstring(ldb, __location__
1451 : ": objectClass missing on %s\n",
1452 : ldb_dn_get_linearized(msg->dn));
1453 0 : talloc_free(ac);
1454 0 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
1455 : }
1456 319376 : is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1457 : REPL_URGENT_ON_CREATE);
1458 :
1459 319376 : ac->is_urgent = is_urgent;
1460 319376 : ret = ldb_build_add_req(&down_req, ldb, ac,
1461 : msg,
1462 : req->controls,
1463 : ac, replmd_op_callback,
1464 : req);
1465 :
1466 319376 : LDB_REQ_SET_LOCATION(down_req);
1467 319376 : if (ret != LDB_SUCCESS) {
1468 0 : talloc_free(ac);
1469 0 : return ret;
1470 : }
1471 :
1472 : /* current partition control is needed by "replmd_op_callback" */
1473 319376 : if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1474 319376 : ret = ldb_request_add_control(down_req,
1475 : DSDB_CONTROL_CURRENT_PARTITION_OID,
1476 : false, NULL);
1477 319376 : if (ret != LDB_SUCCESS) {
1478 0 : talloc_free(ac);
1479 0 : return ret;
1480 : }
1481 : }
1482 :
1483 319376 : if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1484 14383 : ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1485 14383 : if (ret != LDB_SUCCESS) {
1486 0 : talloc_free(ac);
1487 0 : return ret;
1488 : }
1489 : }
1490 :
1491 : /* mark the relax control done */
1492 319376 : if (control) {
1493 159671 : control->critical = 0;
1494 : }
1495 : /* go on with the call chain */
1496 319376 : return ldb_next_request(module, down_req);
1497 : }
1498 :
1499 :
1500 : /*
1501 : * update the replPropertyMetaData for one element
1502 : */
1503 1340765 : static int replmd_update_rpmd_element(struct ldb_context *ldb,
1504 : struct ldb_message *msg,
1505 : struct ldb_message_element *el,
1506 : struct ldb_message_element *old_el,
1507 : struct replPropertyMetaDataBlob *omd,
1508 : const struct dsdb_schema *schema,
1509 : uint64_t *seq_num,
1510 : const struct GUID *our_invocation_id,
1511 : NTTIME now,
1512 : bool is_schema_nc,
1513 : bool is_forced_rodc,
1514 : struct ldb_request *req)
1515 : {
1516 : uint32_t i;
1517 : const struct dsdb_attribute *a;
1518 : struct replPropertyMetaData1 *md1;
1519 1340765 : bool may_skip = false;
1520 : uint32_t attid;
1521 :
1522 1340765 : a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1523 1340765 : if (a == NULL) {
1524 0 : if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1525 : /* allow this to make it possible for dbcheck
1526 : to remove bad attributes */
1527 0 : return LDB_SUCCESS;
1528 : }
1529 :
1530 0 : DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1531 : el->name));
1532 0 : return LDB_ERR_OPERATIONS_ERROR;
1533 : }
1534 :
1535 1340765 : attid = dsdb_attribute_get_attid(a, is_schema_nc);
1536 :
1537 1340765 : if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1538 192864 : return LDB_SUCCESS;
1539 : }
1540 :
1541 : /*
1542 : * if the attribute's value haven't changed, and this isn't
1543 : * just a delete of everything then return LDB_SUCCESS Unless
1544 : * we have the provision control or if the attribute is
1545 : * interSiteTopologyGenerator as this page explain:
1546 : * http://support.microsoft.com/kb/224815 this attribute is
1547 : * periodicaly written by the DC responsible for the intersite
1548 : * generation in a given site
1549 : *
1550 : * Unchanged could be deleting or replacing an already-gone
1551 : * thing with an unconstrained delete/empty replace or a
1552 : * replace with the same value, but not an add with the same
1553 : * value because that could be about adding a duplicate (which
1554 : * is for someone else to error out on).
1555 : */
1556 1147901 : if (old_el != NULL && ldb_msg_element_equal_ordered(el, old_el)) {
1557 216216 : if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1558 92809 : may_skip = true;
1559 : }
1560 1036906 : } else if (old_el == NULL && el->num_values == 0) {
1561 41927 : if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
1562 25076 : may_skip = true;
1563 2 : } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
1564 1 : may_skip = true;
1565 : }
1566 1025930 : } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1567 14102 : ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
1568 : /*
1569 : * We intentionally skip the version bump when attempting to
1570 : * vanish links.
1571 : *
1572 : * The control is set by dbcheck and expunge-tombstones which
1573 : * both attempt to be non-replicating. Otherwise, making an
1574 : * alteration to the replication state would trigger a
1575 : * broadcast of all expunged objects.
1576 : */
1577 13671 : may_skip = true;
1578 : }
1579 :
1580 1147901 : if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
1581 73601 : may_skip = false;
1582 73601 : el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1583 : }
1584 :
1585 1147901 : if (may_skip) {
1586 213520 : if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1587 106760 : !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1588 : /*
1589 : * allow this to make it possible for dbcheck
1590 : * to rebuild broken metadata
1591 : */
1592 106752 : return LDB_SUCCESS;
1593 : }
1594 : }
1595 :
1596 14538630 : for (i=0; i<omd->ctr.ctr1.count; i++) {
1597 : /*
1598 : * First check if we find it under the msDS-IntID,
1599 : * then check if we find it under the OID and
1600 : * prefixMap ID.
1601 : *
1602 : * This allows the administrator to simply re-write
1603 : * the attributes and so restore replication, which is
1604 : * likely what they will try to do.
1605 : */
1606 14033271 : if (attid == omd->ctr.ctr1.array[i].attid) {
1607 535788 : break;
1608 : }
1609 :
1610 13497483 : if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
1611 2 : break;
1612 : }
1613 : }
1614 :
1615 1041149 : if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1616 : /* linked attributes are not stored in
1617 : replPropertyMetaData in FL above w2k, but we do
1618 : raise the seqnum for the object */
1619 19040 : if (*seq_num == 0 &&
1620 7130 : ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1621 0 : return LDB_ERR_OPERATIONS_ERROR;
1622 : }
1623 11910 : return LDB_SUCCESS;
1624 : }
1625 :
1626 1029239 : if (i == omd->ctr.ctr1.count) {
1627 : /* we need to add a new one */
1628 493449 : omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1629 : struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1630 493449 : if (omd->ctr.ctr1.array == NULL) {
1631 0 : ldb_oom(ldb);
1632 0 : return LDB_ERR_OPERATIONS_ERROR;
1633 : }
1634 493449 : omd->ctr.ctr1.count++;
1635 493449 : ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1636 : }
1637 :
1638 : /* Get a new sequence number from the backend. We only do this
1639 : * if we have a change that requires a new
1640 : * replPropertyMetaData element
1641 : */
1642 1029239 : if (*seq_num == 0) {
1643 162774 : int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1644 162774 : if (ret != LDB_SUCCESS) {
1645 0 : return LDB_ERR_OPERATIONS_ERROR;
1646 : }
1647 : }
1648 :
1649 1029239 : md1 = &omd->ctr.ctr1.array[i];
1650 1029239 : md1->version++;
1651 1029239 : md1->attid = attid;
1652 :
1653 1029239 : if (md1->attid == DRSUAPI_ATTID_isDeleted) {
1654 47979 : const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1655 : const char* rdn;
1656 :
1657 47979 : if (rdn_val == NULL) {
1658 0 : ldb_oom(ldb);
1659 0 : return LDB_ERR_OPERATIONS_ERROR;
1660 : }
1661 :
1662 47979 : rdn = (const char*)rdn_val->data;
1663 47979 : if (strcmp(rdn, "Deleted Objects") == 0) {
1664 : /*
1665 : * Set the originating_change_time to 29/12/9999 at 23:59:59
1666 : * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1667 : */
1668 6 : md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1669 : } else {
1670 47973 : md1->originating_change_time = now;
1671 : }
1672 : } else {
1673 981260 : md1->originating_change_time = now;
1674 : }
1675 1029239 : md1->originating_invocation_id = *our_invocation_id;
1676 1029239 : md1->originating_usn = *seq_num;
1677 1029239 : md1->local_usn = *seq_num;
1678 :
1679 1029239 : if (is_forced_rodc) {
1680 : /* Force version to 0 to be overridden later via replication */
1681 8 : md1->version = 0;
1682 : }
1683 :
1684 1029239 : return LDB_SUCCESS;
1685 : }
1686 :
1687 : /*
1688 : * Bump the replPropertyMetaData version on an attribute, and if it
1689 : * has changed (or forced by leaving rdn_old NULL), update the value
1690 : * in the entry.
1691 : *
1692 : * This is important, as calling a modify operation may not change the
1693 : * version number if the values appear unchanged, but a rename between
1694 : * parents bumps this value.
1695 : *
1696 : */
1697 324029 : static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
1698 : struct ldb_message *msg,
1699 : const struct ldb_val *rdn_new,
1700 : const struct ldb_val *rdn_old,
1701 : struct replPropertyMetaDataBlob *omd,
1702 : struct replmd_replicated_request *ar,
1703 : NTTIME now,
1704 : bool is_schema_nc,
1705 : bool is_forced_rodc)
1706 : {
1707 324029 : const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
1708 317499 : const struct dsdb_attribute *rdn_attr =
1709 324029 : dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
1710 324029 : const char *attr_name = rdn_attr != NULL ?
1711 324029 : rdn_attr->lDAPDisplayName :
1712 : rdn_name;
1713 324029 : struct ldb_message_element new_el = {
1714 : .flags = LDB_FLAG_MOD_REPLACE,
1715 : .name = attr_name,
1716 : .num_values = 1,
1717 : .values = discard_const_p(struct ldb_val, rdn_new)
1718 : };
1719 641528 : struct ldb_message_element old_el = {
1720 : .flags = LDB_FLAG_MOD_REPLACE,
1721 : .name = attr_name,
1722 324029 : .num_values = rdn_old ? 1 : 0,
1723 : .values = discard_const_p(struct ldb_val, rdn_old)
1724 : };
1725 :
1726 324029 : if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
1727 324006 : int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
1728 324006 : if (ret != LDB_SUCCESS) {
1729 0 : return ldb_oom(ldb);
1730 : }
1731 : }
1732 :
1733 641528 : return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
1734 : omd, ar->schema, &ar->seq_num,
1735 324029 : &ar->our_invocation_id,
1736 : now, is_schema_nc, is_forced_rodc,
1737 : ar->req);
1738 :
1739 : }
1740 :
1741 18 : static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1742 : {
1743 18 : uint32_t count = omd.ctr.ctr1.count;
1744 18 : uint64_t max = 0;
1745 : uint32_t i;
1746 310 : for (i=0; i < count; i++) {
1747 292 : struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1748 292 : if (max < m.local_usn) {
1749 68 : max = m.local_usn;
1750 : }
1751 : }
1752 18 : return max;
1753 : }
1754 :
1755 : /*
1756 : * update the replPropertyMetaData object each time we modify an
1757 : * object. This is needed for DRS replication, as the merge on the
1758 : * client is based on this object
1759 : */
1760 350300 : static int replmd_update_rpmd(struct ldb_module *module,
1761 : const struct dsdb_schema *schema,
1762 : struct ldb_request *req,
1763 : const char * const *rename_attrs,
1764 : struct ldb_message *msg, uint64_t *seq_num,
1765 : time_t t, bool is_schema_nc,
1766 : bool *is_urgent, bool *rodc)
1767 350300 : {
1768 : const struct ldb_val *omd_value;
1769 : enum ndr_err_code ndr_err;
1770 : struct replPropertyMetaDataBlob omd;
1771 : unsigned int i;
1772 : NTTIME now;
1773 : const struct GUID *our_invocation_id;
1774 : int ret;
1775 350300 : const char * const *attrs = NULL;
1776 350300 : const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1777 : struct ldb_result *res;
1778 : struct ldb_context *ldb;
1779 : struct ldb_message_element *objectclass_el;
1780 : enum urgent_situation situation;
1781 : bool rmd_is_provided;
1782 350300 : bool rmd_is_just_resorted = false;
1783 350300 : const char *not_rename_attrs[4 + msg->num_elements];
1784 350300 : bool is_forced_rodc = false;
1785 :
1786 350300 : if (rename_attrs) {
1787 883 : attrs = rename_attrs;
1788 : } else {
1789 1387985 : for (i = 0; i < msg->num_elements; i++) {
1790 1038568 : not_rename_attrs[i] = msg->elements[i].name;
1791 : }
1792 349417 : not_rename_attrs[i] = "replPropertyMetaData";
1793 349417 : not_rename_attrs[i+1] = "objectClass";
1794 349417 : not_rename_attrs[i+2] = "instanceType";
1795 349417 : not_rename_attrs[i+3] = NULL;
1796 349417 : attrs = not_rename_attrs;
1797 : }
1798 :
1799 350300 : ldb = ldb_module_get_ctx(module);
1800 :
1801 350300 : ret = samdb_rodc(ldb, rodc);
1802 350300 : if (ret != LDB_SUCCESS) {
1803 0 : DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1804 0 : *rodc = false;
1805 : }
1806 :
1807 359721 : if (*rodc &&
1808 9421 : ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
1809 535 : is_forced_rodc = true;
1810 : }
1811 :
1812 350300 : our_invocation_id = samdb_ntds_invocation_id(ldb);
1813 350300 : if (!our_invocation_id) {
1814 : /* this happens during an initial vampire while
1815 : updating the schema */
1816 0 : DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1817 0 : return LDB_SUCCESS;
1818 : }
1819 :
1820 350300 : unix_to_nt_time(&now, t);
1821 :
1822 350300 : if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1823 23569 : rmd_is_provided = true;
1824 23569 : if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
1825 23550 : rmd_is_just_resorted = true;
1826 : }
1827 : } else {
1828 326731 : rmd_is_provided = false;
1829 : }
1830 :
1831 : /* if isDeleted is present and is TRUE, then we consider we are deleting,
1832 : * otherwise we consider we are updating */
1833 350300 : if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1834 47722 : situation = REPL_URGENT_ON_DELETE;
1835 302578 : } else if (rename_attrs) {
1836 883 : situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1837 : } else {
1838 301695 : situation = REPL_URGENT_ON_UPDATE;
1839 : }
1840 :
1841 350300 : if (rmd_is_provided) {
1842 : /* In this case the change_replmetadata control was supplied */
1843 : /* We check that it's the only attribute that is provided
1844 : * (it's a rare case so it's better to keep the code simplier)
1845 : * We also check that the highest local_usn is bigger or the same as
1846 : * uSNChanged. */
1847 : uint64_t db_seq;
1848 47137 : if( msg->num_elements != 1 ||
1849 23568 : strncmp(msg->elements[0].name,
1850 : "replPropertyMetaData", 20) ) {
1851 1 : DEBUG(0,(__location__ ": changereplmetada control called without "\
1852 : "a specified replPropertyMetaData attribute or with others\n"));
1853 1 : return LDB_ERR_OPERATIONS_ERROR;
1854 : }
1855 23568 : if (situation != REPL_URGENT_ON_UPDATE) {
1856 0 : DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1857 0 : return LDB_ERR_OPERATIONS_ERROR;
1858 : }
1859 23568 : omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1860 23568 : if (!omd_value) {
1861 0 : DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1862 : ldb_dn_get_linearized(msg->dn)));
1863 0 : return LDB_ERR_OPERATIONS_ERROR;
1864 : }
1865 23568 : ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1866 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1867 23568 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1868 0 : DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1869 : ldb_dn_get_linearized(msg->dn)));
1870 0 : return LDB_ERR_OPERATIONS_ERROR;
1871 : }
1872 :
1873 23568 : ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1874 : DSDB_FLAG_NEXT_MODULE |
1875 : DSDB_SEARCH_SHOW_RECYCLED |
1876 : DSDB_SEARCH_SHOW_EXTENDED_DN |
1877 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1878 : DSDB_SEARCH_REVEAL_INTERNALS, req);
1879 :
1880 23568 : if (ret != LDB_SUCCESS) {
1881 0 : return ret;
1882 : }
1883 :
1884 23568 : if (rmd_is_just_resorted == false) {
1885 18 : *seq_num = find_max_local_usn(omd);
1886 :
1887 18 : db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1888 :
1889 : /*
1890 : * The test here now allows for a new
1891 : * replPropertyMetaData with no change, if was
1892 : * just dbcheck re-sorting the values.
1893 : */
1894 18 : if (*seq_num <= db_seq) {
1895 2 : DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
1896 : " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
1897 : (long long)*seq_num, (long long)db_seq));
1898 2 : return LDB_ERR_OPERATIONS_ERROR;
1899 : }
1900 : }
1901 :
1902 : } else {
1903 : /* search for the existing replPropertyMetaDataBlob. We need
1904 : * to use REVEAL and ask for DNs in storage format to support
1905 : * the check for values being the same in
1906 : * replmd_update_rpmd_element()
1907 : */
1908 326731 : ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1909 : DSDB_FLAG_NEXT_MODULE |
1910 : DSDB_SEARCH_SHOW_RECYCLED |
1911 : DSDB_SEARCH_SHOW_EXTENDED_DN |
1912 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1913 : DSDB_SEARCH_REVEAL_INTERNALS, req);
1914 326731 : if (ret != LDB_SUCCESS) {
1915 28 : return ret;
1916 : }
1917 :
1918 326703 : omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1919 326703 : if (!omd_value) {
1920 0 : DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1921 : ldb_dn_get_linearized(msg->dn)));
1922 0 : return LDB_ERR_OPERATIONS_ERROR;
1923 : }
1924 :
1925 326703 : ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1926 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1927 326703 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1928 0 : DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1929 : ldb_dn_get_linearized(msg->dn)));
1930 0 : return LDB_ERR_OPERATIONS_ERROR;
1931 : }
1932 :
1933 326703 : if (omd.version != 1) {
1934 0 : DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1935 : omd.version, ldb_dn_get_linearized(msg->dn)));
1936 0 : return LDB_ERR_OPERATIONS_ERROR;
1937 : }
1938 :
1939 1623208 : for (i=0; i<msg->num_elements;) {
1940 1016736 : struct ldb_message_element *el = &msg->elements[i];
1941 : struct ldb_message_element *old_el;
1942 :
1943 1016736 : old_el = ldb_msg_find_element(res->msgs[0], el->name);
1944 1016736 : ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
1945 : &omd, schema, seq_num,
1946 : our_invocation_id,
1947 : now, is_schema_nc,
1948 : is_forced_rodc,
1949 : req);
1950 1016736 : if (ret != LDB_SUCCESS) {
1951 0 : return ret;
1952 : }
1953 :
1954 1016736 : if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1955 407154 : *is_urgent = replmd_check_urgent_attribute(el);
1956 : }
1957 :
1958 1016736 : if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
1959 1016736 : i++;
1960 1016736 : continue;
1961 : }
1962 :
1963 0 : el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
1964 :
1965 0 : if (el->num_values != 0) {
1966 0 : i++;
1967 0 : continue;
1968 : }
1969 :
1970 0 : ldb_msg_remove_element(msg, el);
1971 : }
1972 : }
1973 :
1974 : /*
1975 : * Assert that we have an objectClass attribute - this is major
1976 : * corruption if we don't have this!
1977 : */
1978 350269 : objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1979 350269 : if (objectclass_el != NULL) {
1980 : /*
1981 : * Now check if this objectClass means we need to do urgent replication
1982 : */
1983 350269 : if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1984 : situation)) {
1985 35734 : *is_urgent = true;
1986 : }
1987 0 : } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
1988 0 : ldb_asprintf_errstring(ldb, __location__
1989 : ": objectClass missing on %s\n",
1990 : ldb_dn_get_linearized(msg->dn));
1991 0 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
1992 : }
1993 :
1994 : /*
1995 : * replmd_update_rpmd_element has done an update if the
1996 : * seq_num is set
1997 : */
1998 350269 : if (*seq_num != 0 || rmd_is_just_resorted == true) {
1999 : struct ldb_val *md_value;
2000 : struct ldb_message_element *el;
2001 :
2002 : /*if we are RODC and this is a DRSR update then its ok*/
2003 193470 : if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
2004 193454 : && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
2005 169897 : && !is_forced_rodc) {
2006 : unsigned instanceType;
2007 :
2008 169889 : if (*rodc) {
2009 11 : ldb_set_errstring(ldb, "RODC modify is forbidden!");
2010 22 : return LDB_ERR_REFERRAL;
2011 : }
2012 :
2013 169878 : instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
2014 169878 : if (!(instanceType & INSTANCE_TYPE_WRITE)) {
2015 0 : return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
2016 : "cannot change replicated attribute on partial replica");
2017 : }
2018 : }
2019 :
2020 193459 : md_value = talloc(msg, struct ldb_val);
2021 193459 : if (md_value == NULL) {
2022 0 : ldb_oom(ldb);
2023 0 : return LDB_ERR_OPERATIONS_ERROR;
2024 : }
2025 :
2026 193459 : ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
2027 193459 : if (ret != LDB_SUCCESS) {
2028 0 : ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
2029 0 : return ret;
2030 : }
2031 :
2032 193459 : ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
2033 : (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2034 193459 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2035 0 : DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
2036 : ldb_dn_get_linearized(msg->dn)));
2037 0 : return LDB_ERR_OPERATIONS_ERROR;
2038 : }
2039 :
2040 193459 : ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
2041 193459 : if (ret != LDB_SUCCESS) {
2042 0 : DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
2043 : ldb_dn_get_linearized(msg->dn)));
2044 0 : return ret;
2045 : }
2046 :
2047 193459 : el->num_values = 1;
2048 193459 : el->values = md_value;
2049 : }
2050 :
2051 350258 : return LDB_SUCCESS;
2052 : }
2053 :
2054 856667 : static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
2055 : {
2056 856667 : int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
2057 856667 : if (ret == 0) {
2058 259284 : return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
2059 259284 : &pdn2->dsdb_dn->extra_part);
2060 : }
2061 597383 : return ret;
2062 : }
2063 :
2064 : /*
2065 : get a series of message element values as an array of DNs and GUIDs
2066 : the result is sorted by GUID
2067 : */
2068 27029 : static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
2069 : struct ldb_message_element *el, struct parsed_dn **pdn,
2070 : const char *ldap_oid, struct ldb_request *parent)
2071 : {
2072 : unsigned int i;
2073 27029 : bool values_are_sorted = true;
2074 27029 : struct ldb_context *ldb = ldb_module_get_ctx(module);
2075 :
2076 27029 : if (el == NULL) {
2077 82 : *pdn = NULL;
2078 82 : return LDB_SUCCESS;
2079 : }
2080 :
2081 26947 : (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
2082 26947 : if (!*pdn) {
2083 0 : ldb_module_oom(module);
2084 0 : return LDB_ERR_OPERATIONS_ERROR;
2085 : }
2086 :
2087 100115 : for (i=0; i<el->num_values; i++) {
2088 28817 : struct ldb_val *v = &el->values[i];
2089 : NTSTATUS status;
2090 : struct ldb_dn *dn;
2091 : struct parsed_dn *p;
2092 :
2093 28817 : p = &(*pdn)[i];
2094 :
2095 28817 : p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
2096 28817 : if (p->dsdb_dn == NULL) {
2097 3 : return LDB_ERR_INVALID_DN_SYNTAX;
2098 : }
2099 :
2100 28817 : dn = p->dsdb_dn->dn;
2101 :
2102 28817 : status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
2103 44278 : if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
2104 28814 : unlikely(GUID_all_zero(&p->guid))) {
2105 : /* we got a DN without a GUID - go find the GUID */
2106 9913 : int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
2107 9913 : if (ret != LDB_SUCCESS) {
2108 3 : char *dn_str = NULL;
2109 3 : dn_str = ldb_dn_get_extended_linearized(mem_ctx,
2110 : (dn), 1);
2111 3 : ldb_asprintf_errstring(ldb,
2112 : "Unable to find GUID for DN %s\n",
2113 : dn_str);
2114 5 : if (ret == LDB_ERR_NO_SUCH_OBJECT &&
2115 5 : LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
2116 3 : ldb_attr_cmp(el->name, "member") == 0) {
2117 3 : return LDB_ERR_UNWILLING_TO_PERFORM;
2118 : }
2119 0 : return ret;
2120 : }
2121 9910 : ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
2122 9910 : if (ret != LDB_SUCCESS) {
2123 0 : return ret;
2124 : }
2125 18904 : } else if (!NT_STATUS_IS_OK(status)) {
2126 0 : return LDB_ERR_OPERATIONS_ERROR;
2127 : }
2128 28814 : if (i > 0 && values_are_sorted) {
2129 1887 : int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
2130 1887 : if (cmp < 0) {
2131 919 : values_are_sorted = false;
2132 : }
2133 : }
2134 : /* keep a pointer to the original ldb_val */
2135 28814 : p->v = v;
2136 : }
2137 26944 : if (! values_are_sorted) {
2138 919 : TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
2139 : }
2140 26944 : return LDB_SUCCESS;
2141 : }
2142 :
2143 : /*
2144 : * Get a series of trusted message element values. The result is sorted by
2145 : * GUID, even though the GUIDs might not be known. That works because we trust
2146 : * the database to give us the elements like that if the
2147 : * replmd_private->sorted_links flag is set.
2148 : *
2149 : * We also ensure that the links are in the Functional Level 2003
2150 : * linked attributes format.
2151 : */
2152 42903 : static int get_parsed_dns_trusted_fallback(struct ldb_module *module,
2153 : struct replmd_private *replmd_private,
2154 : TALLOC_CTX *mem_ctx,
2155 : struct ldb_message_element *el,
2156 : struct parsed_dn **pdn,
2157 : const char *ldap_oid,
2158 : struct ldb_request *parent)
2159 : {
2160 : int ret;
2161 42903 : if (el == NULL) {
2162 3068 : *pdn = NULL;
2163 3068 : return LDB_SUCCESS;
2164 : }
2165 :
2166 39835 : if (!replmd_private->sorted_links) {
2167 : /* We need to sort the list. This is the slow old path we want
2168 : to avoid.
2169 : */
2170 58 : ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
2171 : parent);
2172 58 : if (ret != LDB_SUCCESS) {
2173 0 : return ret;
2174 : }
2175 : } else {
2176 39777 : ret = get_parsed_dns_trusted(mem_ctx, el, pdn);
2177 39777 : if (ret != LDB_SUCCESS) {
2178 0 : ldb_module_oom(module);
2179 0 : return LDB_ERR_OPERATIONS_ERROR;
2180 : }
2181 : }
2182 :
2183 : /*
2184 : * This upgrades links to FL2003 style, and sorts the result
2185 : * if that was needed.
2186 : *
2187 : * TODO: Add a database feature that asserts we have no FL2000
2188 : * style links to avoid this check or add a feature that
2189 : * uses a similar check to find sorted/unsorted links
2190 : * for an on-the-fly upgrade.
2191 : */
2192 :
2193 39835 : ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
2194 : *pdn, el->num_values,
2195 : el,
2196 : ldap_oid);
2197 39835 : if (ret != LDB_SUCCESS) {
2198 0 : return ret;
2199 : }
2200 :
2201 39835 : return LDB_SUCCESS;
2202 : }
2203 :
2204 : /*
2205 : Return LDB_SUCCESS if a parsed_dn list contains no duplicate values,
2206 : otherwise an error code. For compatibility the error code differs depending
2207 : on whether or not the attribute is "member".
2208 :
2209 : As always, the parsed_dn list is assumed to be sorted.
2210 : */
2211 2738 : static int check_parsed_dn_duplicates(struct ldb_module *module,
2212 : struct ldb_message_element *el,
2213 : struct parsed_dn *pdn)
2214 : {
2215 : unsigned int i;
2216 2738 : struct ldb_context *ldb = ldb_module_get_ctx(module);
2217 :
2218 6753 : for (i = 1; i < el->num_values; i++) {
2219 4017 : struct parsed_dn *p = &pdn[i];
2220 4017 : if (parsed_dn_compare(p, &pdn[i - 1]) == 0) {
2221 2 : ldb_asprintf_errstring(ldb,
2222 : "Linked attribute %s has "
2223 : "multiple identical values",
2224 : el->name);
2225 2 : if (ldb_attr_cmp(el->name, "member") == 0) {
2226 2 : return LDB_ERR_ENTRY_ALREADY_EXISTS;
2227 : } else {
2228 0 : return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2229 : }
2230 : }
2231 : }
2232 2736 : return LDB_SUCCESS;
2233 : }
2234 :
2235 : /*
2236 : build a new extended DN, including all meta data fields
2237 :
2238 : RMD_FLAGS = DSDB_RMD_FLAG_* bits
2239 : RMD_ADDTIME = originating_add_time
2240 : RMD_INVOCID = originating_invocation_id
2241 : RMD_CHANGETIME = originating_change_time
2242 : RMD_ORIGINATING_USN = originating_usn
2243 : RMD_LOCAL_USN = local_usn
2244 : RMD_VERSION = version
2245 : */
2246 17837 : static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v,
2247 : struct dsdb_dn *dsdb_dn,
2248 : const struct GUID *invocation_id,
2249 : uint64_t local_usn, NTTIME nttime)
2250 : {
2251 17837 : return replmd_set_la_val(mem_ctx, v, dsdb_dn, NULL, invocation_id,
2252 : local_usn, local_usn, nttime,
2253 : RMD_VERSION_INITIAL, false);
2254 : }
2255 :
2256 : static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2257 : struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2258 : uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
2259 : bool deleted);
2260 :
2261 : /*
2262 : check if any links need upgrading from w2k format
2263 : */
2264 39977 : static int replmd_check_upgrade_links(struct ldb_context *ldb,
2265 : struct parsed_dn *dns, uint32_t count,
2266 : struct ldb_message_element *el,
2267 : const char *ldap_oid)
2268 : {
2269 : uint32_t i;
2270 39977 : const struct GUID *invocation_id = NULL;
2271 297851 : for (i=0; i<count; i++) {
2272 : NTSTATUS status;
2273 : uint32_t version;
2274 : int ret;
2275 284732 : if (dns[i].dsdb_dn == NULL) {
2276 284610 : ret = really_parse_trusted_dn(dns, ldb, &dns[i],
2277 : ldap_oid);
2278 284610 : if (ret != LDB_SUCCESS) {
2279 26858 : return LDB_ERR_INVALID_DN_SYNTAX;
2280 : }
2281 : }
2282 :
2283 284732 : status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
2284 : &version, "RMD_VERSION");
2285 284732 : if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2286 : /*
2287 : * We optimistically assume they are all the same; if
2288 : * the first one is fixed, they are all fixed.
2289 : *
2290 : * If the first one was *not* fixed and we find a
2291 : * later one that is, that is an occasion to shout
2292 : * with DEBUG(0).
2293 : */
2294 26859 : if (i == 0) {
2295 26858 : return LDB_SUCCESS;
2296 : }
2297 1 : DEBUG(0, ("Mixed w2k and fixed format "
2298 : "linked attributes\n"));
2299 1 : continue;
2300 : }
2301 :
2302 257873 : if (invocation_id == NULL) {
2303 10808 : invocation_id = samdb_ntds_invocation_id(ldb);
2304 10808 : if (invocation_id == NULL) {
2305 0 : return LDB_ERR_OPERATIONS_ERROR;
2306 : }
2307 : }
2308 :
2309 :
2310 : /* it's an old one that needs upgrading */
2311 478251 : ret = replmd_update_la_val(el->values, dns[i].v,
2312 478251 : dns[i].dsdb_dn, dns[i].dsdb_dn,
2313 : invocation_id, 1, 1, 0, false);
2314 257873 : if (ret != LDB_SUCCESS) {
2315 0 : return ret;
2316 : }
2317 : }
2318 :
2319 : /*
2320 : * This sort() is critical for the operation of
2321 : * get_parsed_dns_trusted_fallback() because callers of this function
2322 : * expect a sorted list, and FL2000 style links are not
2323 : * sorted. In particular, as well as the upgrade case,
2324 : * get_parsed_dns_trusted_fallback() is called from
2325 : * replmd_delete_remove_link() even in FL2000 mode
2326 : *
2327 : * We do not normally pay the cost of the qsort() due to the
2328 : * early return in the RMD_VERSION found case.
2329 : */
2330 13119 : TYPESAFE_QSORT(dns, count, parsed_dn_compare);
2331 13119 : return LDB_SUCCESS;
2332 : }
2333 :
2334 : /*
2335 : Sets the value for a linked attribute, including all meta data fields
2336 :
2337 : see replmd_build_la_val for value names
2338 : */
2339 283325 : static int replmd_set_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2340 : struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2341 : uint64_t usn, uint64_t local_usn, NTTIME nttime,
2342 : uint32_t version, bool deleted)
2343 : {
2344 283325 : struct ldb_dn *dn = dsdb_dn->dn;
2345 : const char *tstring, *usn_string, *flags_string;
2346 : struct ldb_val tval;
2347 : struct ldb_val iid;
2348 : struct ldb_val usnv, local_usnv;
2349 : struct ldb_val vers, flagsv;
2350 283325 : const struct ldb_val *old_addtime = NULL;
2351 : NTSTATUS status;
2352 : int ret;
2353 : const char *dnstring;
2354 : char *vstring;
2355 283325 : uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
2356 :
2357 283325 : tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
2358 283325 : if (!tstring) {
2359 0 : return LDB_ERR_OPERATIONS_ERROR;
2360 : }
2361 283325 : tval = data_blob_string_const(tstring);
2362 :
2363 283325 : usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
2364 283325 : if (!usn_string) {
2365 0 : return LDB_ERR_OPERATIONS_ERROR;
2366 : }
2367 283325 : usnv = data_blob_string_const(usn_string);
2368 :
2369 283325 : usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
2370 283325 : if (!usn_string) {
2371 0 : return LDB_ERR_OPERATIONS_ERROR;
2372 : }
2373 283325 : local_usnv = data_blob_string_const(usn_string);
2374 :
2375 283325 : status = GUID_to_ndr_blob(invocation_id, dn, &iid);
2376 283325 : if (!NT_STATUS_IS_OK(status)) {
2377 0 : return LDB_ERR_OPERATIONS_ERROR;
2378 : }
2379 :
2380 283325 : flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
2381 283325 : if (!flags_string) {
2382 0 : return LDB_ERR_OPERATIONS_ERROR;
2383 : }
2384 283325 : flagsv = data_blob_string_const(flags_string);
2385 :
2386 283325 : ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
2387 283325 : if (ret != LDB_SUCCESS) return ret;
2388 :
2389 : /* get the ADDTIME from the original */
2390 283325 : if (old_dsdb_dn != NULL) {
2391 258911 : old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn,
2392 : "RMD_ADDTIME");
2393 : }
2394 283325 : if (old_addtime == NULL) {
2395 282287 : old_addtime = &tval;
2396 : }
2397 541812 : if (dsdb_dn != old_dsdb_dn ||
2398 258487 : ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
2399 282711 : ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
2400 282711 : if (ret != LDB_SUCCESS) return ret;
2401 : }
2402 :
2403 : /* use our invocation id */
2404 283325 : ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
2405 283325 : if (ret != LDB_SUCCESS) return ret;
2406 :
2407 : /* changetime is the current time */
2408 283325 : ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
2409 283325 : if (ret != LDB_SUCCESS) return ret;
2410 :
2411 : /* update the USN */
2412 283325 : ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
2413 283325 : if (ret != LDB_SUCCESS) return ret;
2414 :
2415 283325 : ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
2416 283325 : if (ret != LDB_SUCCESS) return ret;
2417 :
2418 283325 : vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
2419 283325 : vers = data_blob_string_const(vstring);
2420 283325 : ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
2421 283325 : if (ret != LDB_SUCCESS) return ret;
2422 :
2423 283325 : dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
2424 283325 : if (dnstring == NULL) {
2425 0 : return LDB_ERR_OPERATIONS_ERROR;
2426 : }
2427 283325 : *v = data_blob_string_const(dnstring);
2428 :
2429 283325 : return LDB_SUCCESS;
2430 : }
2431 :
2432 : /**
2433 : * Updates the value for a linked attribute, including all meta data fields
2434 : */
2435 258860 : static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
2436 : struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
2437 : uint64_t usn, uint64_t local_usn, NTTIME nttime,
2438 : bool deleted)
2439 : {
2440 : uint32_t old_version;
2441 258860 : uint32_t version = RMD_VERSION_INITIAL;
2442 : NTSTATUS status;
2443 :
2444 : /*
2445 : * We're updating the linked attribute locally, so increase the version
2446 : * by 1 so that other DCs will see the change when it gets replicated out
2447 : */
2448 258860 : status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version,
2449 : "RMD_VERSION");
2450 :
2451 258860 : if (NT_STATUS_IS_OK(status)) {
2452 987 : version = old_version + 1;
2453 : }
2454 :
2455 258860 : return replmd_set_la_val(mem_ctx, v, dsdb_dn, old_dsdb_dn, invocation_id,
2456 : usn, local_usn, nttime, version, deleted);
2457 : }
2458 :
2459 : /*
2460 : handle adding a linked attribute
2461 : */
2462 11340 : static int replmd_modify_la_add(struct ldb_module *module,
2463 : struct replmd_private *replmd_private,
2464 : struct replmd_replicated_request *ac,
2465 : struct ldb_message *msg,
2466 : struct ldb_message_element *el,
2467 : struct ldb_message_element *old_el,
2468 : const struct dsdb_attribute *schema_attr,
2469 : time_t t,
2470 : struct ldb_dn *msg_dn,
2471 : struct ldb_request *parent)
2472 : {
2473 : unsigned int i, j;
2474 : struct parsed_dn *dns, *old_dns;
2475 11340 : TALLOC_CTX *tmp_ctx = talloc_new(msg);
2476 : int ret;
2477 11340 : struct ldb_val *new_values = NULL;
2478 11340 : unsigned old_num_values = old_el ? old_el->num_values : 0;
2479 11340 : unsigned num_values = 0;
2480 : unsigned max_num_values;
2481 11340 : struct ldb_context *ldb = ldb_module_get_ctx(module);
2482 : NTTIME now;
2483 11340 : unix_to_nt_time(&now, t);
2484 :
2485 : /* get the DNs to be added, fully parsed.
2486 : *
2487 : * We need full parsing because they came off the wire and we don't
2488 : * trust them, besides which we need their details to know where to put
2489 : * them.
2490 : */
2491 11340 : ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2492 11340 : schema_attr->syntax->ldap_oid, parent);
2493 11340 : if (ret != LDB_SUCCESS) {
2494 0 : talloc_free(tmp_ctx);
2495 0 : return ret;
2496 : }
2497 :
2498 : /* get the existing DNs, lazily parsed */
2499 11340 : ret = get_parsed_dns_trusted_fallback(module, replmd_private,
2500 : tmp_ctx, old_el, &old_dns,
2501 11340 : schema_attr->syntax->ldap_oid,
2502 : parent);
2503 :
2504 11340 : if (ret != LDB_SUCCESS) {
2505 0 : talloc_free(tmp_ctx);
2506 0 : return ret;
2507 : }
2508 :
2509 11340 : max_num_values = old_num_values + el->num_values;
2510 11340 : if (max_num_values < old_num_values) {
2511 0 : DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
2512 : "old values: %u, new values: %u, sum: %u\n",
2513 : old_num_values, el->num_values, max_num_values));
2514 0 : talloc_free(tmp_ctx);
2515 0 : return LDB_ERR_OPERATIONS_ERROR;
2516 : }
2517 :
2518 11340 : new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
2519 :
2520 11340 : if (new_values == NULL) {
2521 0 : ldb_module_oom(module);
2522 0 : talloc_free(tmp_ctx);
2523 0 : return LDB_ERR_OPERATIONS_ERROR;
2524 : }
2525 :
2526 : /*
2527 : * For each new value, find where it would go in the list. If there is
2528 : * a matching GUID there, we update the existing value; otherwise we
2529 : * put it in place.
2530 : */
2531 11340 : j = 0;
2532 22811 : for (i = 0; i < el->num_values; i++) {
2533 : struct parsed_dn *exact;
2534 : struct parsed_dn *next;
2535 : unsigned offset;
2536 29512 : int err = parsed_dn_find(ldb, old_dns, old_num_values,
2537 11482 : &dns[i].guid,
2538 11482 : dns[i].dsdb_dn->dn,
2539 11482 : dns[i].dsdb_dn->extra_part, 0,
2540 : &exact, &next,
2541 11482 : schema_attr->syntax->ldap_oid,
2542 : true);
2543 11482 : if (err != LDB_SUCCESS) {
2544 0 : talloc_free(tmp_ctx);
2545 11 : return err;
2546 : }
2547 :
2548 11482 : if (ac->fix_link_sid) {
2549 2 : char *fixed_dnstring = NULL;
2550 2 : struct dom_sid tmp_sid = { 0, };
2551 2 : DATA_BLOB sid_blob = data_blob_null;
2552 : enum ndr_err_code ndr_err;
2553 : NTSTATUS status;
2554 : int num;
2555 :
2556 2 : if (exact == NULL) {
2557 0 : talloc_free(tmp_ctx);
2558 0 : return ldb_operr(ldb);
2559 : }
2560 :
2561 2 : if (dns[i].dsdb_dn->dn_format != DSDB_NORMAL_DN) {
2562 0 : talloc_free(tmp_ctx);
2563 0 : return ldb_operr(ldb);
2564 : }
2565 :
2566 : /*
2567 : * Only "<GUID=...><SID=...>" is allowed.
2568 : *
2569 : * We get the GUID to just to find the old
2570 : * value and the SID in order to add it
2571 : * to the found value.
2572 : */
2573 :
2574 2 : num = ldb_dn_get_comp_num(dns[i].dsdb_dn->dn);
2575 2 : if (num != 0) {
2576 0 : talloc_free(tmp_ctx);
2577 0 : return ldb_operr(ldb);
2578 : }
2579 :
2580 2 : num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
2581 2 : if (num != 2) {
2582 0 : talloc_free(tmp_ctx);
2583 0 : return ldb_operr(ldb);
2584 : }
2585 :
2586 2 : status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
2587 : &tmp_sid, "SID");
2588 2 : if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2589 : /* this is what we expect */
2590 0 : } else if (NT_STATUS_IS_OK(status)) {
2591 : struct GUID_txt_buf guid_str;
2592 0 : ldb_debug_set(ldb, LDB_DEBUG_FATAL,
2593 : "i[%u] SID NOT MISSING... Attribute %s already "
2594 : "exists for target GUID %s, SID %s, DN: %s",
2595 : i, el->name,
2596 0 : GUID_buf_string(&exact->guid,
2597 : &guid_str),
2598 : dom_sid_string(tmp_ctx, &tmp_sid),
2599 : dsdb_dn_get_extended_linearized(tmp_ctx,
2600 0 : exact->dsdb_dn, 1));
2601 0 : talloc_free(tmp_ctx);
2602 0 : return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2603 : } else {
2604 0 : talloc_free(tmp_ctx);
2605 0 : return ldb_operr(ldb);
2606 : }
2607 :
2608 2 : status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
2609 : &tmp_sid, "SID");
2610 2 : if (!NT_STATUS_IS_OK(status)) {
2611 : struct GUID_txt_buf guid_str;
2612 0 : ldb_asprintf_errstring(ldb,
2613 : "NO SID PROVIDED... Attribute %s already "
2614 : "exists for target GUID %s",
2615 : el->name,
2616 0 : GUID_buf_string(&exact->guid,
2617 : &guid_str));
2618 0 : talloc_free(tmp_ctx);
2619 0 : return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2620 : }
2621 :
2622 2 : ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &tmp_sid,
2623 : (ndr_push_flags_fn_t)ndr_push_dom_sid);
2624 2 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2625 0 : talloc_free(tmp_ctx);
2626 0 : return ldb_operr(ldb);
2627 : }
2628 :
2629 2 : ret = ldb_dn_set_extended_component(exact->dsdb_dn->dn, "SID", &sid_blob);
2630 2 : data_blob_free(&sid_blob);
2631 2 : if (ret != LDB_SUCCESS) {
2632 0 : talloc_free(tmp_ctx);
2633 0 : return ret;
2634 : }
2635 :
2636 2 : fixed_dnstring = dsdb_dn_get_extended_linearized(
2637 2 : new_values, exact->dsdb_dn, 1);
2638 2 : if (fixed_dnstring == NULL) {
2639 0 : talloc_free(tmp_ctx);
2640 0 : return ldb_operr(ldb);
2641 : }
2642 :
2643 : /*
2644 : * We just replace the existing value...
2645 : */
2646 2 : *exact->v = data_blob_string_const(fixed_dnstring);
2647 :
2648 2 : continue;
2649 : }
2650 :
2651 11480 : if (exact != NULL) {
2652 : /*
2653 : * We are trying to add one that exists, which is only
2654 : * allowed if it was previously deleted.
2655 : *
2656 : * When we do undelete a link we change it in place.
2657 : * It will be copied across into the right spot in due
2658 : * course.
2659 : */
2660 : uint32_t rmd_flags;
2661 221 : rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2662 :
2663 221 : if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2664 : struct GUID_txt_buf guid_str;
2665 11 : ldb_asprintf_errstring(ldb,
2666 : "Attribute %s already "
2667 : "exists for target GUID %s",
2668 : el->name,
2669 11 : GUID_buf_string(&exact->guid,
2670 : &guid_str));
2671 11 : talloc_free(tmp_ctx);
2672 : /* error codes for 'member' need to be
2673 : special cased */
2674 11 : if (ldb_attr_cmp(el->name, "member") == 0) {
2675 7 : return LDB_ERR_ENTRY_ALREADY_EXISTS;
2676 : } else {
2677 4 : return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2678 : }
2679 : }
2680 :
2681 598 : ret = replmd_update_la_val(new_values, exact->v,
2682 210 : dns[i].dsdb_dn,
2683 210 : exact->dsdb_dn,
2684 210 : &ac->our_invocation_id,
2685 : ac->seq_num, ac->seq_num,
2686 : now, false);
2687 210 : if (ret != LDB_SUCCESS) {
2688 0 : talloc_free(tmp_ctx);
2689 0 : return ret;
2690 : }
2691 :
2692 210 : ret = replmd_add_backlink(module, replmd_private,
2693 : ac->schema,
2694 : msg_dn,
2695 210 : &dns[i].guid,
2696 : true,
2697 : schema_attr,
2698 : parent);
2699 210 : if (ret != LDB_SUCCESS) {
2700 0 : talloc_free(tmp_ctx);
2701 0 : return ret;
2702 : }
2703 210 : continue;
2704 : }
2705 : /*
2706 : * Here we don't have an exact match.
2707 : *
2708 : * If next is NULL, this one goes beyond the end of the
2709 : * existing list, so we need to add all of those ones first.
2710 : *
2711 : * If next is not NULL, we need to add all the ones before
2712 : * next.
2713 : */
2714 11259 : if (next == NULL) {
2715 5238 : offset = old_num_values;
2716 : } else {
2717 : /* next should have been parsed, but let's make sure */
2718 6021 : if (next->dsdb_dn == NULL) {
2719 0 : ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
2720 0 : schema_attr->syntax->ldap_oid);
2721 0 : if (ret != LDB_SUCCESS) {
2722 0 : return ret;
2723 : }
2724 : }
2725 6021 : offset = MIN(next - old_dns, old_num_values);
2726 : }
2727 :
2728 : /* put all the old ones before next on the list */
2729 220843 : for (; j < offset; j++) {
2730 209584 : new_values[num_values] = *old_dns[j].v;
2731 209584 : num_values++;
2732 : }
2733 :
2734 11259 : ret = replmd_add_backlink(module, replmd_private,
2735 : ac->schema, msg_dn,
2736 11259 : &dns[i].guid,
2737 : true, schema_attr,
2738 : parent);
2739 : /* Make the new linked attribute ldb_val. */
2740 20070 : ret = replmd_build_la_val(new_values, &new_values[num_values],
2741 11259 : dns[i].dsdb_dn, &ac->our_invocation_id,
2742 : ac->seq_num, now);
2743 11259 : if (ret != LDB_SUCCESS) {
2744 0 : talloc_free(tmp_ctx);
2745 0 : return ret;
2746 : }
2747 11259 : num_values++;
2748 11259 : if (ret != LDB_SUCCESS) {
2749 0 : talloc_free(tmp_ctx);
2750 0 : return ret;
2751 : }
2752 : }
2753 : /* copy the rest of the old ones (if any) */
2754 213464 : for (; j < old_num_values; j++) {
2755 202135 : new_values[num_values] = *old_dns[j].v;
2756 202135 : num_values++;
2757 : }
2758 :
2759 11329 : talloc_steal(msg->elements, new_values);
2760 11329 : if (old_el != NULL) {
2761 8261 : talloc_steal(msg->elements, old_el->values);
2762 : }
2763 11329 : el->values = new_values;
2764 11329 : el->num_values = num_values;
2765 :
2766 11329 : talloc_free(tmp_ctx);
2767 :
2768 : /* we now tell the backend to replace all existing values
2769 : with the one we have constructed */
2770 11329 : el->flags = LDB_FLAG_MOD_REPLACE;
2771 :
2772 11329 : return LDB_SUCCESS;
2773 : }
2774 :
2775 :
2776 : /*
2777 : handle deleting all active linked attributes
2778 : */
2779 12754 : static int replmd_modify_la_delete(struct ldb_module *module,
2780 : struct replmd_private *replmd_private,
2781 : struct replmd_replicated_request *ac,
2782 : struct ldb_message *msg,
2783 : struct ldb_message_element *el,
2784 : struct ldb_message_element *old_el,
2785 : const struct dsdb_attribute *schema_attr,
2786 : time_t t,
2787 : struct ldb_dn *msg_dn,
2788 : struct ldb_request *parent)
2789 : {
2790 : unsigned int i;
2791 : struct parsed_dn *dns, *old_dns;
2792 12754 : TALLOC_CTX *tmp_ctx = NULL;
2793 : int ret;
2794 12754 : struct ldb_context *ldb = ldb_module_get_ctx(module);
2795 12754 : struct ldb_control *vanish_links_ctrl = NULL;
2796 12754 : bool vanish_links = false;
2797 12754 : unsigned int num_to_delete = el->num_values;
2798 : uint32_t rmd_flags;
2799 : NTTIME now;
2800 :
2801 12754 : unix_to_nt_time(&now, t);
2802 :
2803 12754 : if (old_el == NULL || old_el->num_values == 0) {
2804 : /* there is nothing to delete... */
2805 3 : if (num_to_delete == 0) {
2806 : /* and we're deleting nothing, so that's OK */
2807 0 : return LDB_SUCCESS;
2808 : }
2809 3 : return LDB_ERR_NO_SUCH_ATTRIBUTE;
2810 : }
2811 :
2812 12751 : tmp_ctx = talloc_new(msg);
2813 12751 : if (tmp_ctx == NULL) {
2814 0 : return LDB_ERR_OPERATIONS_ERROR;
2815 : }
2816 :
2817 12751 : ret = get_parsed_dns(module, tmp_ctx, el, &dns,
2818 12751 : schema_attr->syntax->ldap_oid, parent);
2819 12751 : if (ret != LDB_SUCCESS) {
2820 3 : talloc_free(tmp_ctx);
2821 3 : return ret;
2822 : }
2823 :
2824 12748 : ret = get_parsed_dns_trusted_fallback(module, replmd_private,
2825 : tmp_ctx, old_el, &old_dns,
2826 12748 : schema_attr->syntax->ldap_oid,
2827 : parent);
2828 :
2829 12748 : if (ret != LDB_SUCCESS) {
2830 0 : talloc_free(tmp_ctx);
2831 0 : return ret;
2832 : }
2833 :
2834 12748 : vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
2835 12748 : if (vanish_links_ctrl) {
2836 12325 : vanish_links = true;
2837 12325 : vanish_links_ctrl->critical = false;
2838 : }
2839 :
2840 : /* we empty out el->values here to avoid damage if we return early. */
2841 12748 : el->num_values = 0;
2842 12748 : el->values = NULL;
2843 :
2844 : /*
2845 : * If vanish links is set, we are actually removing members of
2846 : * old_el->values; otherwise we are just marking them deleted.
2847 : *
2848 : * There is a special case when no values are given: we remove them
2849 : * all. When we have the vanish_links control we just have to remove
2850 : * the backlinks and change our element to replace the existing values
2851 : * with the empty list.
2852 : */
2853 :
2854 12748 : if (num_to_delete == 0) {
2855 10249 : for (i = 0; i < old_el->num_values; i++) {
2856 7535 : struct parsed_dn *p = &old_dns[i];
2857 7535 : if (p->dsdb_dn == NULL) {
2858 4821 : ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
2859 4821 : schema_attr->syntax->ldap_oid);
2860 4821 : if (ret != LDB_SUCCESS) {
2861 0 : return ret;
2862 : }
2863 : }
2864 7535 : ret = replmd_add_backlink(module, replmd_private,
2865 : ac->schema, msg_dn, &p->guid,
2866 : false, schema_attr,
2867 : parent);
2868 7535 : if (ret != LDB_SUCCESS) {
2869 0 : talloc_free(tmp_ctx);
2870 0 : return ret;
2871 : }
2872 7535 : if (vanish_links) {
2873 7524 : continue;
2874 : }
2875 :
2876 11 : rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
2877 11 : if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2878 1 : continue;
2879 : }
2880 :
2881 17 : ret = replmd_update_la_val(old_el->values, p->v,
2882 : p->dsdb_dn, p->dsdb_dn,
2883 10 : &ac->our_invocation_id,
2884 : ac->seq_num, ac->seq_num,
2885 : now, true);
2886 10 : if (ret != LDB_SUCCESS) {
2887 0 : talloc_free(tmp_ctx);
2888 0 : return ret;
2889 : }
2890 : }
2891 :
2892 2714 : if (vanish_links) {
2893 2704 : el->flags = LDB_FLAG_MOD_REPLACE;
2894 2704 : talloc_free(tmp_ctx);
2895 2704 : return LDB_SUCCESS;
2896 : }
2897 : }
2898 :
2899 :
2900 20183 : for (i = 0; i < num_to_delete; i++) {
2901 10149 : struct parsed_dn *p = &dns[i];
2902 10149 : struct parsed_dn *exact = NULL;
2903 10149 : struct parsed_dn *next = NULL;
2904 17905 : ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
2905 10149 : &p->guid,
2906 : NULL,
2907 10149 : p->dsdb_dn->extra_part, 0,
2908 : &exact, &next,
2909 10149 : schema_attr->syntax->ldap_oid,
2910 : true);
2911 10149 : if (ret != LDB_SUCCESS) {
2912 0 : talloc_free(tmp_ctx);
2913 10 : return ret;
2914 : }
2915 10149 : if (exact == NULL) {
2916 : struct GUID_txt_buf buf;
2917 6 : ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
2918 : "exist for target GUID %s",
2919 : el->name,
2920 6 : GUID_buf_string(&p->guid, &buf));
2921 6 : if (ldb_attr_cmp(el->name, "member") == 0) {
2922 6 : talloc_free(tmp_ctx);
2923 6 : return LDB_ERR_UNWILLING_TO_PERFORM;
2924 : } else {
2925 0 : talloc_free(tmp_ctx);
2926 0 : return LDB_ERR_NO_SUCH_ATTRIBUTE;
2927 : }
2928 : }
2929 :
2930 10143 : if (vanish_links) {
2931 9621 : if (CHECK_DEBUGLVL(5)) {
2932 0 : rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2933 0 : if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
2934 : struct GUID_txt_buf buf;
2935 0 : const char *guid_str = \
2936 0 : GUID_buf_string(&p->guid, &buf);
2937 0 : DEBUG(5, ("Deleting deleted linked "
2938 : "attribute %s to %s, because "
2939 : "vanish_links control is set\n",
2940 : el->name, guid_str));
2941 : }
2942 : }
2943 :
2944 : /* remove the backlink */
2945 9621 : ret = replmd_add_backlink(module,
2946 : replmd_private,
2947 : ac->schema,
2948 : msg_dn,
2949 : &p->guid,
2950 : false, schema_attr,
2951 : parent);
2952 9621 : if (ret != LDB_SUCCESS) {
2953 0 : talloc_free(tmp_ctx);
2954 0 : return ret;
2955 : }
2956 :
2957 : /* We flag the deletion and tidy it up later. */
2958 9621 : exact->v = NULL;
2959 9621 : continue;
2960 : }
2961 :
2962 522 : rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
2963 :
2964 522 : if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
2965 : struct GUID_txt_buf buf;
2966 4 : const char *guid_str = GUID_buf_string(&p->guid, &buf);
2967 4 : ldb_asprintf_errstring(ldb, "Attribute %s already "
2968 : "deleted for target GUID %s",
2969 : el->name, guid_str);
2970 4 : if (ldb_attr_cmp(el->name, "member") == 0) {
2971 4 : talloc_free(tmp_ctx);
2972 4 : return LDB_ERR_UNWILLING_TO_PERFORM;
2973 : } else {
2974 0 : talloc_free(tmp_ctx);
2975 0 : return LDB_ERR_NO_SUCH_ATTRIBUTE;
2976 : }
2977 : }
2978 :
2979 1416 : ret = replmd_update_la_val(old_el->values, exact->v,
2980 967 : exact->dsdb_dn, exact->dsdb_dn,
2981 518 : &ac->our_invocation_id,
2982 : ac->seq_num, ac->seq_num,
2983 : now, true);
2984 518 : if (ret != LDB_SUCCESS) {
2985 0 : talloc_free(tmp_ctx);
2986 0 : return ret;
2987 : }
2988 518 : ret = replmd_add_backlink(module, replmd_private,
2989 : ac->schema, msg_dn,
2990 : &p->guid,
2991 : false, schema_attr,
2992 : parent);
2993 518 : if (ret != LDB_SUCCESS) {
2994 0 : talloc_free(tmp_ctx);
2995 0 : return ret;
2996 : }
2997 : }
2998 :
2999 10034 : if (vanish_links) {
3000 9621 : unsigned j = 0;
3001 9621 : struct ldb_val *tmp_vals = NULL;
3002 :
3003 9621 : tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
3004 : old_el->num_values);
3005 9621 : if (tmp_vals == NULL) {
3006 0 : talloc_free(tmp_ctx);
3007 0 : return ldb_module_oom(module);
3008 : }
3009 263939 : for (i = 0; i < old_el->num_values; i++) {
3010 254318 : if (old_dns[i].v == NULL) {
3011 9621 : continue;
3012 : }
3013 244697 : tmp_vals[j] = *old_dns[i].v;
3014 244697 : j++;
3015 : }
3016 254318 : for (i = 0; i < j; i++) {
3017 244697 : old_el->values[i] = tmp_vals[i];
3018 : }
3019 9621 : old_el->num_values = j;
3020 : }
3021 :
3022 10034 : el->values = talloc_steal(msg->elements, old_el->values);
3023 10034 : el->num_values = old_el->num_values;
3024 :
3025 10034 : talloc_free(tmp_ctx);
3026 :
3027 : /* we now tell the backend to replace all existing values
3028 : with the one we have constructed */
3029 10034 : el->flags = LDB_FLAG_MOD_REPLACE;
3030 :
3031 10034 : return LDB_SUCCESS;
3032 : }
3033 :
3034 : /*
3035 : handle replacing a linked attribute
3036 : */
3037 152 : static int replmd_modify_la_replace(struct ldb_module *module,
3038 : struct replmd_private *replmd_private,
3039 : struct replmd_replicated_request *ac,
3040 : struct ldb_message *msg,
3041 : struct ldb_message_element *el,
3042 : struct ldb_message_element *old_el,
3043 : const struct dsdb_attribute *schema_attr,
3044 : time_t t,
3045 : struct ldb_dn *msg_dn,
3046 : struct ldb_request *parent)
3047 : {
3048 : unsigned int i, old_i, new_i;
3049 : struct parsed_dn *dns, *old_dns;
3050 152 : TALLOC_CTX *tmp_ctx = talloc_new(msg);
3051 : int ret;
3052 152 : struct ldb_context *ldb = ldb_module_get_ctx(module);
3053 152 : struct ldb_val *new_values = NULL;
3054 152 : const char *ldap_oid = schema_attr->syntax->ldap_oid;
3055 : unsigned int old_num_values;
3056 : unsigned int repl_num_values;
3057 : unsigned int max_num_values;
3058 : NTTIME now;
3059 :
3060 152 : unix_to_nt_time(&now, t);
3061 :
3062 : /*
3063 : * The replace operation is unlike the replace and delete cases in that
3064 : * we need to look at every existing link to see whether it is being
3065 : * retained or deleted. In other words, we can't avoid parsing the GUIDs.
3066 : *
3067 : * As we are trying to combine two sorted lists, the algorithm we use
3068 : * is akin to the merge phase of a merge sort. We interleave the two
3069 : * lists, doing different things depending on which side the current
3070 : * item came from.
3071 : *
3072 : * There are three main cases, with some sub-cases.
3073 : *
3074 : * - a DN is in the old list but not the new one. It needs to be
3075 : * marked as deleted (but left in the list).
3076 : * - maybe it is already deleted, and we have less to do.
3077 : *
3078 : * - a DN is in both lists. The old data gets replaced by the new,
3079 : * and the list doesn't grow. The old link may have been marked as
3080 : * deleted, in which case we undelete it.
3081 : *
3082 : * - a DN is in the new list only. We add it in the right place.
3083 : */
3084 :
3085 152 : old_num_values = old_el ? old_el->num_values : 0;
3086 152 : repl_num_values = el->num_values;
3087 152 : max_num_values = old_num_values + repl_num_values;
3088 :
3089 152 : if (max_num_values == 0) {
3090 : /* There is nothing to do! */
3091 9 : return LDB_SUCCESS;
3092 : }
3093 :
3094 : /*
3095 : * At the successful end of these functions el->values is
3096 : * overwritten with new_values. However get_parsed_dns()
3097 : * points p->v at the supplied el and it effectively gets used
3098 : * as a working area by replmd_build_la_val(). So we must
3099 : * duplicate it because our caller only called
3100 : * ldb_msg_copy_shallow().
3101 : */
3102 :
3103 143 : el->values = talloc_memdup(tmp_ctx,
3104 : el->values,
3105 : sizeof(el->values[0]) * el->num_values);
3106 143 : if (el->values == NULL) {
3107 0 : ldb_module_oom(module);
3108 0 : talloc_free(tmp_ctx);
3109 0 : return LDB_ERR_OPERATIONS_ERROR;
3110 : }
3111 :
3112 143 : ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
3113 143 : if (ret != LDB_SUCCESS) {
3114 0 : talloc_free(tmp_ctx);
3115 0 : return ret;
3116 : }
3117 :
3118 143 : ret = check_parsed_dn_duplicates(module, el, dns);
3119 143 : if (ret != LDB_SUCCESS) {
3120 1 : talloc_free(tmp_ctx);
3121 1 : return ret;
3122 : }
3123 :
3124 142 : ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
3125 : ldap_oid, parent);
3126 142 : if (ret != LDB_SUCCESS) {
3127 0 : talloc_free(tmp_ctx);
3128 0 : return ret;
3129 : }
3130 :
3131 142 : ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
3132 : old_el, ldap_oid);
3133 142 : if (ret != LDB_SUCCESS) {
3134 0 : talloc_free(tmp_ctx);
3135 0 : return ret;
3136 : }
3137 :
3138 142 : new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
3139 142 : if (new_values == NULL) {
3140 0 : ldb_module_oom(module);
3141 0 : talloc_free(tmp_ctx);
3142 0 : return LDB_ERR_OPERATIONS_ERROR;
3143 : }
3144 :
3145 142 : old_i = 0;
3146 142 : new_i = 0;
3147 518 : for (i = 0; i < max_num_values; i++) {
3148 : int cmp;
3149 : struct parsed_dn *old_p, *new_p;
3150 416 : if (old_i < old_num_values && new_i < repl_num_values) {
3151 207 : old_p = &old_dns[old_i];
3152 207 : new_p = &dns[new_i];
3153 207 : cmp = parsed_dn_compare(old_p, new_p);
3154 209 : } else if (old_i < old_num_values) {
3155 : /* the new list is empty, read the old list */
3156 69 : old_p = &old_dns[old_i];
3157 69 : new_p = NULL;
3158 69 : cmp = -1;
3159 140 : } else if (new_i < repl_num_values) {
3160 : /* the old list is empty, read new list */
3161 100 : old_p = NULL;
3162 100 : new_p = &dns[new_i];
3163 100 : cmp = 1;
3164 : } else {
3165 40 : break;
3166 : }
3167 :
3168 376 : if (cmp < 0) {
3169 : /*
3170 : * An old ones that come before the next replacement
3171 : * (if any). We mark it as deleted and add it to the
3172 : * final list.
3173 : */
3174 85 : uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3175 85 : if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
3176 156 : ret = replmd_update_la_val(new_values, old_p->v,
3177 : old_p->dsdb_dn,
3178 : old_p->dsdb_dn,
3179 78 : &ac->our_invocation_id,
3180 : ac->seq_num, ac->seq_num,
3181 : now, true);
3182 78 : if (ret != LDB_SUCCESS) {
3183 0 : talloc_free(tmp_ctx);
3184 0 : return ret;
3185 : }
3186 :
3187 78 : ret = replmd_add_backlink(module, replmd_private,
3188 : ac->schema,
3189 : msg_dn,
3190 : &old_p->guid, false,
3191 : schema_attr,
3192 : parent);
3193 78 : if (ret != LDB_SUCCESS) {
3194 0 : talloc_free(tmp_ctx);
3195 0 : return ret;
3196 : }
3197 : }
3198 85 : new_values[i] = *old_p->v;
3199 85 : old_i++;
3200 291 : } else if (cmp == 0) {
3201 : /*
3202 : * We are overwriting one. If it was previously
3203 : * deleted, we need to add a backlink.
3204 : *
3205 : * Note that if any RMD_FLAGs in an extended new DN
3206 : * will be ignored.
3207 : */
3208 : uint32_t rmd_flags;
3209 :
3210 324 : ret = replmd_update_la_val(new_values, old_p->v,
3211 : new_p->dsdb_dn,
3212 : old_p->dsdb_dn,
3213 163 : &ac->our_invocation_id,
3214 : ac->seq_num, ac->seq_num,
3215 : now, false);
3216 163 : if (ret != LDB_SUCCESS) {
3217 0 : talloc_free(tmp_ctx);
3218 0 : return ret;
3219 : }
3220 :
3221 163 : rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
3222 163 : if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
3223 52 : ret = replmd_add_backlink(module, replmd_private,
3224 : ac->schema,
3225 : msg_dn,
3226 : &new_p->guid, true,
3227 : schema_attr,
3228 : parent);
3229 52 : if (ret != LDB_SUCCESS) {
3230 0 : talloc_free(tmp_ctx);
3231 0 : return ret;
3232 : }
3233 : }
3234 :
3235 163 : new_values[i] = *old_p->v;
3236 163 : old_i++;
3237 163 : new_i++;
3238 : } else {
3239 : /*
3240 : * Replacements that don't match an existing one. We
3241 : * just add them to the final list.
3242 : */
3243 230 : ret = replmd_build_la_val(new_values,
3244 : new_p->v,
3245 : new_p->dsdb_dn,
3246 128 : &ac->our_invocation_id,
3247 : ac->seq_num, now);
3248 128 : if (ret != LDB_SUCCESS) {
3249 0 : talloc_free(tmp_ctx);
3250 0 : return ret;
3251 : }
3252 128 : ret = replmd_add_backlink(module, replmd_private,
3253 : ac->schema,
3254 : msg_dn,
3255 : &new_p->guid, true,
3256 : schema_attr,
3257 : parent);
3258 128 : if (ret != LDB_SUCCESS) {
3259 0 : talloc_free(tmp_ctx);
3260 0 : return ret;
3261 : }
3262 128 : new_values[i] = *new_p->v;
3263 128 : new_i++;
3264 : }
3265 : }
3266 142 : if (old_el != NULL) {
3267 60 : talloc_steal(msg->elements, old_el->values);
3268 : }
3269 142 : el->values = talloc_steal(msg->elements, new_values);
3270 142 : el->num_values = i;
3271 142 : talloc_free(tmp_ctx);
3272 :
3273 142 : el->flags = LDB_FLAG_MOD_REPLACE;
3274 :
3275 142 : return LDB_SUCCESS;
3276 : }
3277 :
3278 :
3279 : /*
3280 : handle linked attributes in modify requests
3281 : */
3282 349377 : static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
3283 : struct replmd_private *replmd_private,
3284 : struct replmd_replicated_request *ac,
3285 : struct ldb_message *msg,
3286 : time_t t,
3287 : struct ldb_request *parent)
3288 : {
3289 : struct ldb_result *res;
3290 : unsigned int i;
3291 : int ret;
3292 349377 : struct ldb_context *ldb = ldb_module_get_ctx(module);
3293 : struct ldb_message *old_msg;
3294 :
3295 349377 : if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
3296 : /*
3297 : * Nothing special is required for modifying or vanishing links
3298 : * in fl2000 since they are just strings in a multi-valued
3299 : * attribute.
3300 : */
3301 58728 : struct ldb_control *ctrl = ldb_request_get_control(parent,
3302 : DSDB_CONTROL_REPLMD_VANISH_LINKS);
3303 58728 : if (ctrl) {
3304 1302 : ctrl->critical = false;
3305 : }
3306 58728 : return LDB_SUCCESS;
3307 : }
3308 :
3309 : /*
3310 : * TODO:
3311 : *
3312 : * We should restrict this to the intersection of the list of
3313 : * linked attributes in the schema and the list of attributes
3314 : * being modified.
3315 : *
3316 : * This will help performance a little, as otherwise we have
3317 : * to allocate the entire object value-by-value.
3318 : */
3319 290649 : ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
3320 : DSDB_FLAG_NEXT_MODULE |
3321 : DSDB_SEARCH_SHOW_RECYCLED |
3322 : DSDB_SEARCH_REVEAL_INTERNALS |
3323 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
3324 : parent);
3325 290649 : if (ret != LDB_SUCCESS) {
3326 0 : return ret;
3327 : }
3328 :
3329 290649 : old_msg = res->msgs[0];
3330 :
3331 1380866 : for (i=0; i<msg->num_elements; i++) {
3332 1090247 : struct ldb_message_element *el = &msg->elements[i];
3333 : struct ldb_message_element *old_el, *new_el;
3334 1090247 : unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
3335 907246 : const struct dsdb_attribute *schema_attr
3336 1090247 : = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
3337 1090247 : if (!schema_attr) {
3338 0 : ldb_asprintf_errstring(ldb,
3339 : "%s: attribute %s is not a valid attribute in schema",
3340 : __FUNCTION__, el->name);
3341 30 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
3342 : }
3343 1090247 : if (schema_attr->linkID == 0) {
3344 1954114 : continue;
3345 : }
3346 24264 : if ((schema_attr->linkID & 1) == 1) {
3347 : struct ldb_control *ctrl;
3348 :
3349 18 : ctrl = ldb_request_get_control(parent,
3350 : DSDB_CONTROL_REPLMD_VANISH_LINKS);
3351 18 : if (ctrl != NULL) {
3352 5 : ctrl->critical = false;
3353 5 : continue;
3354 : }
3355 13 : ctrl = ldb_request_get_control(parent,
3356 : DSDB_CONTROL_DBCHECK);
3357 13 : if (ctrl != NULL) {
3358 13 : continue;
3359 : }
3360 :
3361 : /* Odd is for the target. Illegal to modify */
3362 0 : ldb_asprintf_errstring(ldb,
3363 : "attribute %s must not be modified directly, it is a linked attribute", el->name);
3364 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
3365 : }
3366 24246 : old_el = ldb_msg_find_element(old_msg, el->name);
3367 24246 : switch (mod_type) {
3368 152 : case LDB_FLAG_MOD_REPLACE:
3369 152 : ret = replmd_modify_la_replace(module, replmd_private,
3370 : ac, msg, el, old_el,
3371 : schema_attr, t,
3372 : old_msg->dn,
3373 : parent);
3374 152 : break;
3375 12754 : case LDB_FLAG_MOD_DELETE:
3376 12754 : ret = replmd_modify_la_delete(module, replmd_private,
3377 : ac, msg, el, old_el,
3378 : schema_attr, t,
3379 : old_msg->dn,
3380 : parent);
3381 12754 : break;
3382 11340 : case LDB_FLAG_MOD_ADD:
3383 11340 : ret = replmd_modify_la_add(module, replmd_private,
3384 : ac, msg, el, old_el,
3385 : schema_attr, t,
3386 : old_msg->dn,
3387 : parent);
3388 11340 : break;
3389 0 : default:
3390 0 : ldb_asprintf_errstring(ldb,
3391 : "invalid flags 0x%x for %s linked attribute",
3392 : el->flags, el->name);
3393 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
3394 : }
3395 24246 : if (ret != LDB_SUCCESS) {
3396 28 : return ret;
3397 : }
3398 24218 : ret = dsdb_check_single_valued_link(schema_attr, el);
3399 24218 : if (ret != LDB_SUCCESS) {
3400 2 : ldb_asprintf_errstring(ldb,
3401 : "Attribute %s is single valued but more than one value has been supplied",
3402 : el->name);
3403 : /* Return codes as found on Windows 2012r2 */
3404 2 : if (mod_type == LDB_FLAG_MOD_REPLACE) {
3405 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
3406 : } else {
3407 1 : return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
3408 : }
3409 : } else {
3410 24216 : el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
3411 : }
3412 :
3413 24216 : if (old_el) {
3414 21057 : ldb_msg_remove_attr(old_msg, el->name);
3415 : }
3416 24216 : ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
3417 24216 : new_el->num_values = el->num_values;
3418 24216 : new_el->values = talloc_steal(msg->elements, el->values);
3419 :
3420 : /* TODO: this relises a bit too heavily on the exact
3421 : behaviour of ldb_msg_find_element and
3422 : ldb_msg_remove_element */
3423 24216 : old_el = ldb_msg_find_element(msg, el->name);
3424 24216 : if (old_el != el) {
3425 4646 : ldb_msg_remove_element(msg, old_el);
3426 4646 : i--;
3427 : }
3428 : }
3429 :
3430 290619 : talloc_free(res);
3431 290619 : return ret;
3432 : }
3433 :
3434 :
3435 11 : static int send_rodc_referral(struct ldb_request *req,
3436 : struct ldb_context *ldb,
3437 : struct ldb_dn *dn)
3438 : {
3439 11 : char *referral = NULL;
3440 11 : struct loadparm_context *lp_ctx = NULL;
3441 11 : struct ldb_dn *fsmo_role_dn = NULL;
3442 11 : struct ldb_dn *role_owner_dn = NULL;
3443 11 : const char *domain = NULL;
3444 : WERROR werr;
3445 :
3446 11 : lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
3447 : struct loadparm_context);
3448 :
3449 11 : werr = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
3450 : &fsmo_role_dn, &role_owner_dn);
3451 :
3452 11 : if (W_ERROR_IS_OK(werr)) {
3453 11 : struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
3454 11 : if (server_dn != NULL) {
3455 11 : ldb_dn_remove_child_components(server_dn, 1);
3456 11 : domain = samdb_dn_to_dnshostname(ldb, req,
3457 : server_dn);
3458 : }
3459 : }
3460 :
3461 11 : if (domain == NULL) {
3462 0 : domain = lpcfg_dnsdomain(lp_ctx);
3463 : }
3464 :
3465 11 : referral = talloc_asprintf(req, "ldap://%s/%s",
3466 : domain,
3467 : ldb_dn_get_linearized(dn));
3468 11 : if (referral == NULL) {
3469 0 : ldb_oom(ldb);
3470 0 : return LDB_ERR_OPERATIONS_ERROR;
3471 : }
3472 :
3473 11 : return ldb_module_send_referral(req, referral);
3474 : }
3475 :
3476 :
3477 552777 : static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
3478 : {
3479 : struct ldb_context *ldb;
3480 : struct replmd_replicated_request *ac;
3481 : struct ldb_request *down_req;
3482 : struct ldb_message *msg;
3483 552777 : time_t t = time(NULL);
3484 : int ret;
3485 552777 : bool is_urgent = false, rodc = false;
3486 552777 : bool is_schema_nc = false;
3487 : unsigned int functional_level;
3488 552777 : const struct ldb_message_element *guid_el = NULL;
3489 : struct ldb_control *sd_propagation_control;
3490 552777 : struct ldb_control *fix_links_control = NULL;
3491 552777 : struct ldb_control *fix_dn_name_control = NULL;
3492 552777 : struct ldb_control *fix_dn_sid_control = NULL;
3493 458348 : struct replmd_private *replmd_private =
3494 552777 : talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3495 :
3496 : /* do not manipulate our control entries */
3497 552777 : if (ldb_dn_is_special(req->op.mod.message->dn)) {
3498 558 : return ldb_next_request(module, req);
3499 : }
3500 :
3501 552219 : sd_propagation_control = ldb_request_get_control(req,
3502 : DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
3503 552219 : if (sd_propagation_control != NULL) {
3504 123017 : if (req->op.mod.message->num_elements != 1) {
3505 0 : return ldb_module_operr(module);
3506 : }
3507 123017 : ret = strcmp(req->op.mod.message->elements[0].name,
3508 : "nTSecurityDescriptor");
3509 123017 : if (ret != 0) {
3510 0 : return ldb_module_operr(module);
3511 : }
3512 :
3513 123017 : return ldb_next_request(module, req);
3514 : }
3515 :
3516 429202 : ldb = ldb_module_get_ctx(module);
3517 :
3518 429202 : fix_links_control = ldb_request_get_control(req,
3519 : DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
3520 429202 : if (fix_links_control != NULL) {
3521 2 : struct dsdb_schema *schema = NULL;
3522 2 : const struct dsdb_attribute *sa = NULL;
3523 :
3524 2 : if (req->op.mod.message->num_elements != 1) {
3525 0 : return ldb_module_operr(module);
3526 : }
3527 :
3528 2 : if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[0].flags) != LDB_FLAG_MOD_REPLACE) {
3529 0 : return ldb_module_operr(module);
3530 : }
3531 :
3532 2 : schema = dsdb_get_schema(ldb, req);
3533 2 : if (schema == NULL) {
3534 0 : return ldb_module_operr(module);
3535 : }
3536 :
3537 2 : sa = dsdb_attribute_by_lDAPDisplayName(schema,
3538 2 : req->op.mod.message->elements[0].name);
3539 2 : if (sa == NULL) {
3540 0 : return ldb_module_operr(module);
3541 : }
3542 :
3543 2 : if (sa->linkID == 0) {
3544 0 : return ldb_module_operr(module);
3545 : }
3546 :
3547 2 : fix_links_control->critical = false;
3548 2 : return ldb_next_request(module, req);
3549 : }
3550 :
3551 429200 : fix_dn_name_control = ldb_request_get_control(req,
3552 : DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3553 429200 : if (fix_dn_name_control != NULL) {
3554 79781 : struct dsdb_schema *schema = NULL;
3555 79781 : const struct dsdb_attribute *sa = NULL;
3556 :
3557 79781 : if (req->op.mod.message->num_elements != 2) {
3558 0 : return ldb_module_operr(module);
3559 : }
3560 :
3561 79781 : if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[0].flags) != LDB_FLAG_MOD_DELETE) {
3562 0 : return ldb_module_operr(module);
3563 : }
3564 :
3565 79781 : if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[1].flags) != LDB_FLAG_MOD_ADD) {
3566 0 : return ldb_module_operr(module);
3567 : }
3568 :
3569 79781 : if (req->op.mod.message->elements[0].num_values != 1) {
3570 0 : return ldb_module_operr(module);
3571 : }
3572 :
3573 79781 : if (req->op.mod.message->elements[1].num_values != 1) {
3574 0 : return ldb_module_operr(module);
3575 : }
3576 :
3577 79781 : schema = dsdb_get_schema(ldb, req);
3578 79781 : if (schema == NULL) {
3579 0 : return ldb_module_operr(module);
3580 : }
3581 :
3582 79781 : if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
3583 : req->op.mod.message->elements[1].name) != 0) {
3584 0 : return ldb_module_operr(module);
3585 : }
3586 :
3587 79781 : sa = dsdb_attribute_by_lDAPDisplayName(schema,
3588 79781 : req->op.mod.message->elements[0].name);
3589 79781 : if (sa == NULL) {
3590 0 : return ldb_module_operr(module);
3591 : }
3592 :
3593 79781 : if (sa->dn_format == DSDB_INVALID_DN) {
3594 0 : return ldb_module_operr(module);
3595 : }
3596 :
3597 79781 : if (sa->linkID != 0) {
3598 0 : return ldb_module_operr(module);
3599 : }
3600 :
3601 : /*
3602 : * If we are run from dbcheck and we are not updating
3603 : * a link (as these would need to be sorted and so
3604 : * can't go via such a simple update, then do not
3605 : * trigger replicated updates and a new USN from this
3606 : * change, it wasn't a real change, just a new
3607 : * (correct) string DN
3608 : */
3609 :
3610 79781 : fix_dn_name_control->critical = false;
3611 79781 : return ldb_next_request(module, req);
3612 : }
3613 :
3614 349419 : ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
3615 :
3616 349419 : guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
3617 349419 : if (guid_el != NULL) {
3618 0 : ldb_set_errstring(ldb,
3619 : "replmd_modify: it's not allowed to change the objectGUID!");
3620 0 : return LDB_ERR_CONSTRAINT_VIOLATION;
3621 : }
3622 :
3623 349419 : ac = replmd_ctx_init(module, req);
3624 349419 : if (ac == NULL) {
3625 0 : return ldb_module_oom(module);
3626 : }
3627 :
3628 349419 : functional_level = dsdb_functional_level(ldb);
3629 :
3630 : /* we have to copy the message as the caller might have it as a const */
3631 349419 : msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
3632 349419 : if (msg == NULL) {
3633 0 : ldb_oom(ldb);
3634 0 : talloc_free(ac);
3635 0 : return LDB_ERR_OPERATIONS_ERROR;
3636 : }
3637 :
3638 349419 : fix_dn_sid_control = ldb_request_get_control(req,
3639 : DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
3640 349419 : if (fix_dn_sid_control != NULL) {
3641 2 : const struct dsdb_attribute *sa = NULL;
3642 :
3643 2 : if (msg->num_elements != 1) {
3644 0 : talloc_free(ac);
3645 0 : return ldb_module_operr(module);
3646 : }
3647 :
3648 2 : if (LDB_FLAG_MOD_TYPE(msg->elements[0].flags) != LDB_FLAG_MOD_ADD) {
3649 0 : talloc_free(ac);
3650 0 : return ldb_module_operr(module);
3651 : }
3652 :
3653 2 : if (msg->elements[0].num_values != 1) {
3654 0 : talloc_free(ac);
3655 0 : return ldb_module_operr(module);
3656 : }
3657 :
3658 2 : sa = dsdb_attribute_by_lDAPDisplayName(ac->schema,
3659 2 : msg->elements[0].name);
3660 2 : if (sa == NULL) {
3661 0 : talloc_free(ac);
3662 0 : return ldb_module_operr(module);
3663 : }
3664 :
3665 2 : if (sa->dn_format != DSDB_NORMAL_DN) {
3666 0 : talloc_free(ac);
3667 0 : return ldb_module_operr(module);
3668 : }
3669 :
3670 2 : fix_dn_sid_control->critical = false;
3671 2 : ac->fix_link_sid = true;
3672 :
3673 2 : goto handle_linked_attribs;
3674 : }
3675 :
3676 349417 : ldb_msg_remove_attr(msg, "whenChanged");
3677 349417 : ldb_msg_remove_attr(msg, "uSNChanged");
3678 :
3679 349417 : is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3680 :
3681 349417 : ret = replmd_update_rpmd(module, ac->schema, req, NULL,
3682 : msg, &ac->seq_num, t, is_schema_nc,
3683 : &is_urgent, &rodc);
3684 349417 : if (rodc && (ret == LDB_ERR_REFERRAL)) {
3685 11 : ret = send_rodc_referral(req, ldb, msg->dn);
3686 11 : talloc_free(ac);
3687 11 : return ret;
3688 :
3689 : }
3690 :
3691 349406 : if (ret != LDB_SUCCESS) {
3692 31 : talloc_free(ac);
3693 31 : return ret;
3694 : }
3695 :
3696 349375 : handle_linked_attribs:
3697 349377 : ret = replmd_modify_handle_linked_attribs(module, replmd_private,
3698 : ac, msg, t, req);
3699 349377 : if (ret != LDB_SUCCESS) {
3700 30 : talloc_free(ac);
3701 30 : return ret;
3702 : }
3703 :
3704 : /* TODO:
3705 : * - replace the old object with the newly constructed one
3706 : */
3707 :
3708 349347 : ac->is_urgent = is_urgent;
3709 :
3710 349347 : ret = ldb_build_mod_req(&down_req, ldb, ac,
3711 : msg,
3712 : req->controls,
3713 : ac, replmd_op_callback,
3714 : req);
3715 349347 : LDB_REQ_SET_LOCATION(down_req);
3716 349347 : if (ret != LDB_SUCCESS) {
3717 0 : talloc_free(ac);
3718 0 : return ret;
3719 : }
3720 :
3721 : /* current partition control is needed by "replmd_op_callback" */
3722 349347 : if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3723 349347 : ret = ldb_request_add_control(down_req,
3724 : DSDB_CONTROL_CURRENT_PARTITION_OID,
3725 : false, NULL);
3726 349347 : if (ret != LDB_SUCCESS) {
3727 0 : talloc_free(ac);
3728 0 : return ret;
3729 : }
3730 : }
3731 :
3732 : /* If we are in functional level 2000, then
3733 : * replmd_modify_handle_linked_attribs will have done
3734 : * nothing */
3735 349347 : if (functional_level == DS_DOMAIN_FUNCTION_2000) {
3736 58728 : ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3737 58728 : if (ret != LDB_SUCCESS) {
3738 0 : talloc_free(ac);
3739 0 : return ret;
3740 : }
3741 : }
3742 :
3743 349347 : talloc_steal(down_req, msg);
3744 :
3745 : /* we only change whenChanged and uSNChanged if the seq_num
3746 : has changed */
3747 349347 : if (ac->seq_num != 0) {
3748 168996 : ret = add_time_element(msg, "whenChanged", t);
3749 168996 : if (ret != LDB_SUCCESS) {
3750 0 : talloc_free(ac);
3751 0 : ldb_operr(ldb);
3752 0 : return ret;
3753 : }
3754 :
3755 168996 : ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3756 168996 : if (ret != LDB_SUCCESS) {
3757 0 : talloc_free(ac);
3758 0 : ldb_operr(ldb);
3759 0 : return ret;
3760 : }
3761 : }
3762 :
3763 : /* go on with the call chain */
3764 349347 : return ldb_next_request(module, down_req);
3765 : }
3766 :
3767 : static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
3768 :
3769 : /*
3770 : handle a rename request
3771 :
3772 : On a rename we need to do an extra ldb_modify which sets the
3773 : whenChanged and uSNChanged attributes. We do this in a callback after the success.
3774 : */
3775 888 : static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
3776 : {
3777 : struct ldb_context *ldb;
3778 888 : struct ldb_control *fix_dn_name_control = NULL;
3779 : struct replmd_replicated_request *ac;
3780 : int ret;
3781 : struct ldb_request *down_req;
3782 :
3783 : /* do not manipulate our control entries */
3784 888 : if (ldb_dn_is_special(req->op.rename.olddn)) {
3785 0 : return ldb_next_request(module, req);
3786 : }
3787 :
3788 888 : fix_dn_name_control = ldb_request_get_control(req,
3789 : DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
3790 888 : if (fix_dn_name_control != NULL) {
3791 1 : return ldb_next_request(module, req);
3792 : }
3793 :
3794 887 : ldb = ldb_module_get_ctx(module);
3795 :
3796 887 : ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
3797 :
3798 887 : ac = replmd_ctx_init(module, req);
3799 887 : if (ac == NULL) {
3800 0 : return ldb_module_oom(module);
3801 : }
3802 :
3803 3320 : ret = ldb_build_rename_req(&down_req, ldb, ac,
3804 887 : ac->req->op.rename.olddn,
3805 887 : ac->req->op.rename.newdn,
3806 887 : ac->req->controls,
3807 : ac, replmd_rename_callback,
3808 : ac->req);
3809 887 : LDB_REQ_SET_LOCATION(down_req);
3810 887 : if (ret != LDB_SUCCESS) {
3811 0 : talloc_free(ac);
3812 0 : return ret;
3813 : }
3814 :
3815 : /* go on with the call chain */
3816 887 : return ldb_next_request(module, down_req);
3817 : }
3818 :
3819 : /* After the rename is compleated, update the whenchanged etc */
3820 887 : static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3821 : {
3822 : struct ldb_context *ldb;
3823 : struct ldb_request *down_req;
3824 : struct ldb_message *msg;
3825 : const struct dsdb_attribute *rdn_attr;
3826 : const char *rdn_name;
3827 : const struct ldb_val *rdn_val;
3828 887 : const char *attrs[5] = { NULL, };
3829 887 : time_t t = time(NULL);
3830 : int ret;
3831 887 : bool is_urgent = false, rodc = false;
3832 : bool is_schema_nc;
3833 811 : struct replmd_replicated_request *ac =
3834 887 : talloc_get_type(req->context, struct replmd_replicated_request);
3835 811 : struct replmd_private *replmd_private =
3836 887 : talloc_get_type(ldb_module_get_private(ac->module),
3837 : struct replmd_private);
3838 :
3839 887 : ldb = ldb_module_get_ctx(ac->module);
3840 :
3841 887 : if (ares->error != LDB_SUCCESS) {
3842 4 : return ldb_module_done(ac->req, ares->controls,
3843 : ares->response, ares->error);
3844 : }
3845 :
3846 883 : if (ares->type != LDB_REPLY_DONE) {
3847 0 : ldb_set_errstring(ldb,
3848 : "invalid reply type in repl_meta_data rename callback");
3849 0 : talloc_free(ares);
3850 0 : return ldb_module_done(ac->req, NULL, NULL,
3851 : LDB_ERR_OPERATIONS_ERROR);
3852 : }
3853 :
3854 : /* TODO:
3855 : * - replace the old object with the newly constructed one
3856 : */
3857 :
3858 883 : msg = ldb_msg_new(ac);
3859 883 : if (msg == NULL) {
3860 0 : ldb_oom(ldb);
3861 0 : return LDB_ERR_OPERATIONS_ERROR;
3862 : }
3863 :
3864 883 : msg->dn = ac->req->op.rename.newdn;
3865 :
3866 883 : is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
3867 :
3868 883 : rdn_name = ldb_dn_get_rdn_name(msg->dn);
3869 883 : if (rdn_name == NULL) {
3870 0 : talloc_free(ares);
3871 0 : return ldb_module_done(ac->req, NULL, NULL,
3872 : ldb_operr(ldb));
3873 : }
3874 :
3875 : /* normalize the rdn attribute name */
3876 883 : rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
3877 883 : if (rdn_attr == NULL) {
3878 0 : talloc_free(ares);
3879 0 : return ldb_module_done(ac->req, NULL, NULL,
3880 : ldb_operr(ldb));
3881 : }
3882 883 : rdn_name = rdn_attr->lDAPDisplayName;
3883 :
3884 883 : rdn_val = ldb_dn_get_rdn_val(msg->dn);
3885 883 : if (rdn_val == NULL) {
3886 0 : talloc_free(ares);
3887 0 : return ldb_module_done(ac->req, NULL, NULL,
3888 : ldb_operr(ldb));
3889 : }
3890 :
3891 883 : if (ldb_msg_append_value(msg, rdn_name, rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
3892 0 : talloc_free(ares);
3893 0 : return ldb_module_done(ac->req, NULL, NULL,
3894 : ldb_oom(ldb));
3895 : }
3896 883 : if (ldb_msg_append_value(msg, "name", rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
3897 0 : talloc_free(ares);
3898 0 : return ldb_module_done(ac->req, NULL, NULL,
3899 : ldb_oom(ldb));
3900 : }
3901 :
3902 : /*
3903 : * here we let replmd_update_rpmd() only search for
3904 : * the existing "replPropertyMetaData" and rdn_name attributes.
3905 : *
3906 : * We do not want the existing "name" attribute as
3907 : * the "name" attribute needs to get the version
3908 : * updated on rename even if the rdn value hasn't changed.
3909 : *
3910 : * This is the diff of the meta data, for a moved user
3911 : * on a w2k8r2 server:
3912 : *
3913 : * # record 1
3914 : * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
3915 : * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
3916 : * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
3917 : * version : 0x00000001 (1)
3918 : * reserved : 0x00000000 (0)
3919 : * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
3920 : * local_usn : 0x00000000000037a5 (14245)
3921 : * array: struct replPropertyMetaData1
3922 : * attid : DRSUAPI_ATTID_name (0x90001)
3923 : * - version : 0x00000001 (1)
3924 : * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
3925 : * + version : 0x00000002 (2)
3926 : * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
3927 : * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
3928 : * - originating_usn : 0x00000000000037a5 (14245)
3929 : * - local_usn : 0x00000000000037a5 (14245)
3930 : * + originating_usn : 0x0000000000003834 (14388)
3931 : * + local_usn : 0x0000000000003834 (14388)
3932 : * array: struct replPropertyMetaData1
3933 : * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
3934 : * version : 0x00000004 (4)
3935 : */
3936 883 : attrs[0] = "replPropertyMetaData";
3937 883 : attrs[1] = "objectClass";
3938 883 : attrs[2] = "instanceType";
3939 883 : attrs[3] = rdn_name;
3940 883 : attrs[4] = NULL;
3941 :
3942 883 : ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
3943 : msg, &ac->seq_num, t,
3944 : is_schema_nc, &is_urgent, &rodc);
3945 883 : if (rodc && (ret == LDB_ERR_REFERRAL)) {
3946 0 : ret = send_rodc_referral(req, ldb, ac->req->op.rename.olddn);
3947 0 : talloc_free(ares);
3948 0 : return ldb_module_done(req, NULL, NULL, ret);
3949 : }
3950 :
3951 883 : if (ret != LDB_SUCCESS) {
3952 0 : talloc_free(ares);
3953 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
3954 : }
3955 :
3956 883 : if (ac->seq_num == 0) {
3957 0 : talloc_free(ares);
3958 0 : return ldb_module_done(ac->req, NULL, NULL,
3959 : ldb_error(ldb, ret,
3960 : "internal error seq_num == 0"));
3961 : }
3962 883 : ac->is_urgent = is_urgent;
3963 :
3964 883 : ret = ldb_build_mod_req(&down_req, ldb, ac,
3965 : msg,
3966 : req->controls,
3967 : ac, replmd_op_callback,
3968 : req);
3969 883 : LDB_REQ_SET_LOCATION(down_req);
3970 883 : if (ret != LDB_SUCCESS) {
3971 0 : talloc_free(ac);
3972 0 : return ret;
3973 : }
3974 :
3975 : /* current partition control is needed by "replmd_op_callback" */
3976 883 : if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
3977 883 : ret = ldb_request_add_control(down_req,
3978 : DSDB_CONTROL_CURRENT_PARTITION_OID,
3979 : false, NULL);
3980 883 : if (ret != LDB_SUCCESS) {
3981 0 : talloc_free(ac);
3982 0 : return ret;
3983 : }
3984 : }
3985 :
3986 883 : talloc_steal(down_req, msg);
3987 :
3988 883 : ret = add_time_element(msg, "whenChanged", t);
3989 883 : if (ret != LDB_SUCCESS) {
3990 0 : talloc_free(ac);
3991 0 : ldb_operr(ldb);
3992 0 : return ret;
3993 : }
3994 :
3995 883 : ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
3996 883 : if (ret != LDB_SUCCESS) {
3997 0 : talloc_free(ac);
3998 0 : ldb_operr(ldb);
3999 0 : return ret;
4000 : }
4001 :
4002 : /* go on with the call chain - do the modify after the rename */
4003 883 : return ldb_next_request(ac->module, down_req);
4004 : }
4005 :
4006 : /*
4007 : * remove links from objects that point at this object when an object
4008 : * is deleted. We remove it from the NEXT module per MS-DRSR 5.160
4009 : * RemoveObj which states that link removal due to the object being
4010 : * deleted is NOT an originating update - they just go away!
4011 : *
4012 : */
4013 5903 : static int replmd_delete_remove_link(struct ldb_module *module,
4014 : const struct dsdb_schema *schema,
4015 : struct replmd_private *replmd_private,
4016 : struct ldb_dn *dn,
4017 : struct GUID *guid,
4018 : struct ldb_message_element *el,
4019 : const struct dsdb_attribute *sa,
4020 : struct ldb_request *parent,
4021 : bool *caller_should_vanish)
4022 : {
4023 : unsigned int i;
4024 5903 : TALLOC_CTX *tmp_ctx = talloc_new(module);
4025 5903 : struct ldb_context *ldb = ldb_module_get_ctx(module);
4026 :
4027 16713 : for (i=0; i<el->num_values; i++) {
4028 : struct dsdb_dn *dsdb_dn;
4029 : int ret;
4030 : struct ldb_message *msg;
4031 : const struct dsdb_attribute *target_attr;
4032 : struct ldb_message_element *el2;
4033 : const char *dn_str;
4034 : struct ldb_val dn_val;
4035 10810 : uint32_t dsdb_flags = 0;
4036 10810 : const char *attrs[] = { NULL, NULL };
4037 : struct ldb_result *link_res;
4038 : struct ldb_message *link_msg;
4039 : struct ldb_message_element *link_el;
4040 : struct parsed_dn *link_dns;
4041 10810 : struct parsed_dn *p = NULL, *unused = NULL;
4042 :
4043 10810 : if (dsdb_dn_is_deleted_val(&el->values[i])) {
4044 38 : continue;
4045 : }
4046 :
4047 10810 : dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
4048 10810 : if (!dsdb_dn) {
4049 0 : talloc_free(tmp_ctx);
4050 0 : return LDB_ERR_OPERATIONS_ERROR;
4051 : }
4052 :
4053 : /* remove the link */
4054 10810 : msg = ldb_msg_new(tmp_ctx);
4055 10810 : if (!msg) {
4056 0 : ldb_module_oom(module);
4057 0 : talloc_free(tmp_ctx);
4058 0 : return LDB_ERR_OPERATIONS_ERROR;
4059 : }
4060 :
4061 10810 : msg->dn = dsdb_dn->dn;
4062 :
4063 10810 : target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
4064 10810 : if (target_attr == NULL) {
4065 0 : continue;
4066 : }
4067 10810 : attrs[0] = target_attr->lDAPDisplayName;
4068 :
4069 10810 : ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
4070 : LDB_FLAG_MOD_DELETE, &el2);
4071 10810 : if (ret != LDB_SUCCESS) {
4072 0 : ldb_module_oom(module);
4073 0 : talloc_free(tmp_ctx);
4074 0 : return LDB_ERR_OPERATIONS_ERROR;
4075 : }
4076 :
4077 10810 : ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
4078 : msg->dn, attrs,
4079 : DSDB_FLAG_NEXT_MODULE |
4080 : DSDB_SEARCH_SHOW_EXTENDED_DN |
4081 : DSDB_SEARCH_SHOW_RECYCLED,
4082 : parent);
4083 :
4084 10810 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4085 2 : DBG_WARNING("Failed to find forward link object %s "
4086 : "to remove backlink %s on %s",
4087 : ldb_dn_get_linearized(msg->dn),
4088 : sa->lDAPDisplayName,
4089 : ldb_dn_get_linearized(dn));
4090 2 : *caller_should_vanish = true;
4091 2 : continue;
4092 : }
4093 :
4094 10808 : if (ret != LDB_SUCCESS) {
4095 0 : talloc_free(tmp_ctx);
4096 0 : return ret;
4097 : }
4098 :
4099 10808 : link_msg = link_res->msgs[0];
4100 10808 : link_el = ldb_msg_find_element(link_msg,
4101 2948 : target_attr->lDAPDisplayName);
4102 10808 : if (link_el == NULL) {
4103 1 : DBG_WARNING("Failed to find forward link on %s "
4104 : "as %s to remove backlink %s on %s",
4105 : ldb_dn_get_linearized(msg->dn),
4106 : target_attr->lDAPDisplayName,
4107 : sa->lDAPDisplayName,
4108 : ldb_dn_get_linearized(dn));
4109 1 : *caller_should_vanish = true;
4110 1 : continue;
4111 : }
4112 :
4113 : /*
4114 : * This call 'upgrades' the links in link_dns, but we
4115 : * do not commit the result back into the database, so
4116 : * this is safe to call in FL2000 or on databases that
4117 : * have been run at that level in the past.
4118 : */
4119 10807 : ret = get_parsed_dns_trusted_fallback(module, replmd_private,
4120 : tmp_ctx,
4121 : link_el, &link_dns,
4122 10807 : target_attr->syntax->ldap_oid,
4123 : parent);
4124 10807 : if (ret != LDB_SUCCESS) {
4125 0 : talloc_free(tmp_ctx);
4126 0 : return ret;
4127 : }
4128 :
4129 10807 : ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
4130 : guid, dn,
4131 : data_blob_null, 0,
4132 : &p, &unused,
4133 10807 : target_attr->syntax->ldap_oid, false);
4134 10807 : if (ret != LDB_SUCCESS) {
4135 0 : talloc_free(tmp_ctx);
4136 0 : return ret;
4137 : }
4138 :
4139 10807 : if (p == NULL) {
4140 2 : DBG_WARNING("Failed to find forward link on %s "
4141 : "as %s to remove backlink %s on %s",
4142 : ldb_dn_get_linearized(msg->dn),
4143 : target_attr->lDAPDisplayName,
4144 : sa->lDAPDisplayName,
4145 : ldb_dn_get_linearized(dn));
4146 2 : *caller_should_vanish = true;
4147 2 : continue;
4148 : }
4149 :
4150 : /*
4151 : * If we find a backlink to ourself, we will delete
4152 : * the forward link before we get to process that
4153 : * properly, so just let the caller process this via
4154 : * the forward link.
4155 : *
4156 : * We do this once we are sure we have the forward
4157 : * link (to ourself) in case something is very wrong
4158 : * and they are out of sync.
4159 : */
4160 10805 : if (ldb_dn_compare(dsdb_dn->dn, dn) == 0) {
4161 33 : continue;
4162 : }
4163 :
4164 : /* This needs to get the Binary DN, by first searching */
4165 10772 : dn_str = dsdb_dn_get_linearized(tmp_ctx,
4166 10772 : p->dsdb_dn);
4167 :
4168 10772 : dn_val = data_blob_string_const(dn_str);
4169 10772 : el2->values = &dn_val;
4170 10772 : el2->num_values = 1;
4171 :
4172 : /*
4173 : * Ensure that we tell the modification to vanish any linked
4174 : * attributes (not simply mark them as isDeleted = TRUE)
4175 : */
4176 10772 : dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4177 :
4178 10772 : ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
4179 10772 : if (ret != LDB_SUCCESS) {
4180 0 : talloc_free(tmp_ctx);
4181 0 : return ret;
4182 : }
4183 : }
4184 5903 : talloc_free(tmp_ctx);
4185 5903 : return LDB_SUCCESS;
4186 : }
4187 :
4188 :
4189 : /*
4190 : handle update of replication meta data for deletion of objects
4191 :
4192 : This also handles the mapping of delete to a rename operation
4193 : to allow deletes to be replicated.
4194 :
4195 : It also handles the incoming deleted objects, to ensure they are
4196 : fully deleted here. In that case re_delete is true, and we do not
4197 : use this as a signal to change the deleted state, just reinforce it.
4198 :
4199 : */
4200 133293 : static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete)
4201 : {
4202 133293 : int ret = LDB_ERR_OTHER;
4203 : bool retb, disallow_move_on_delete;
4204 133293 : struct ldb_dn *old_dn = NULL, *new_dn = NULL;
4205 : const char *rdn_name;
4206 : const struct ldb_val *rdn_value, *new_rdn_value;
4207 : struct GUID guid;
4208 133293 : struct ldb_context *ldb = ldb_module_get_ctx(module);
4209 : const struct dsdb_schema *schema;
4210 : struct ldb_message *msg, *old_msg;
4211 : struct ldb_message_element *el;
4212 : TALLOC_CTX *tmp_ctx;
4213 : struct ldb_result *res, *parent_res;
4214 : static const char * const preserved_attrs[] = {
4215 : /*
4216 : * This list MUST be kept in case-insensitive sorted order,
4217 : * as we use it in a binary search with ldb_attr_cmp().
4218 : *
4219 : * We get this hard-coded list from
4220 : * MS-ADTS section 3.1.1.5.5.1.1 "Tombstone Requirements".
4221 : */
4222 : "attributeID",
4223 : "attributeSyntax",
4224 : "distinguishedName",
4225 : "dNReferenceUpdate",
4226 : "dNSHostName",
4227 : "flatName",
4228 : "governsID",
4229 : "groupType",
4230 : "instanceType",
4231 : "isDeleted",
4232 : "isRecycled",
4233 : "lastKnownParent",
4234 : "lDAPDisplayName",
4235 : "legacyExchangeDN",
4236 : "mS-DS-CreatorSID",
4237 : "msDS-LastKnownRDN",
4238 : "msDS-PortLDAP",
4239 : "mSMQOwnerID",
4240 : "name",
4241 : "nCName",
4242 : "nTSecurityDescriptor",
4243 : "objectClass",
4244 : "objectGUID",
4245 : "objectSid",
4246 : "oMSyntax",
4247 : "proxiedObjectName",
4248 : "replPropertyMetaData",
4249 : "sAMAccountName",
4250 : "securityIdentifier",
4251 : "sIDHistory",
4252 : "subClassOf",
4253 : "systemFlags",
4254 : "trustAttributes",
4255 : "trustDirection",
4256 : "trustPartner",
4257 : "trustType",
4258 : "userAccountControl",
4259 : "uSNChanged",
4260 : "uSNCreated",
4261 : "whenChanged",
4262 : "whenCreated",
4263 : /*
4264 : * DO NOT JUST APPEND TO THIS LIST.
4265 : *
4266 : * In case you missed the note at the top, this list is kept
4267 : * in case-insensitive sorted order. In the unlikely event you
4268 : * need to add an attrbute, please add it in the RIGHT PLACE.
4269 : */
4270 : };
4271 : static const char * const all_attrs[] = {
4272 : DSDB_SECRET_ATTRIBUTES,
4273 : "*",
4274 : NULL
4275 : };
4276 : static const struct ldb_val true_val = {
4277 : .data = discard_const_p(uint8_t, "TRUE"),
4278 : .length = 4
4279 : };
4280 :
4281 : unsigned int i;
4282 133293 : uint32_t dsdb_flags = 0;
4283 : struct replmd_private *replmd_private;
4284 : enum deletion_state deletion_state, next_deletion_state;
4285 :
4286 133293 : if (ldb_dn_is_special(req->op.del.dn)) {
4287 1 : return ldb_next_request(module, req);
4288 : }
4289 :
4290 : /*
4291 : * We have to allow dbcheck to remove an object that
4292 : * is beyond repair, and to do so totally. This could
4293 : * mean we we can get a partial object from the other
4294 : * DC, causing havoc, so dbcheck suggests
4295 : * re-replication first. dbcheck sets both DBCHECK
4296 : * and RELAX in this situation.
4297 : */
4298 133292 : if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
4299 14 : && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
4300 : /* really, really remove it */
4301 1 : return ldb_next_request(module, req);
4302 : }
4303 :
4304 133291 : tmp_ctx = talloc_new(ldb);
4305 133291 : if (!tmp_ctx) {
4306 0 : ldb_oom(ldb);
4307 0 : return LDB_ERR_OPERATIONS_ERROR;
4308 : }
4309 :
4310 133291 : schema = dsdb_get_schema(ldb, tmp_ctx);
4311 133291 : if (!schema) {
4312 0 : talloc_free(tmp_ctx);
4313 0 : return LDB_ERR_OPERATIONS_ERROR;
4314 : }
4315 :
4316 133291 : old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
4317 :
4318 : /* we need the complete msg off disk, so we can work out which
4319 : attributes need to be removed */
4320 133291 : ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
4321 : DSDB_FLAG_NEXT_MODULE |
4322 : DSDB_SEARCH_SHOW_RECYCLED |
4323 : DSDB_SEARCH_REVEAL_INTERNALS |
4324 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
4325 133291 : if (ret != LDB_SUCCESS) {
4326 0 : ldb_asprintf_errstring(ldb_module_get_ctx(module),
4327 : "repmd_delete: Failed to %s %s, because we failed to find it: %s",
4328 : re_delete ? "re-delete" : "delete",
4329 : ldb_dn_get_linearized(old_dn),
4330 : ldb_errstring(ldb_module_get_ctx(module)));
4331 0 : talloc_free(tmp_ctx);
4332 0 : return ret;
4333 : }
4334 133291 : old_msg = res->msgs[0];
4335 :
4336 133291 : replmd_deletion_state(module, old_msg,
4337 : &deletion_state,
4338 : &next_deletion_state);
4339 :
4340 : /* This supports us noticing an incoming isDeleted and acting on it */
4341 133291 : if (re_delete) {
4342 85577 : SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED);
4343 85577 : next_deletion_state = deletion_state;
4344 : }
4345 :
4346 133291 : if (next_deletion_state == OBJECT_REMOVED) {
4347 : /*
4348 : * We have to prevent objects being deleted, even if
4349 : * the administrator really wants them gone, as
4350 : * without the tombstone, we can get a partial object
4351 : * from the other DC, causing havoc.
4352 : *
4353 : * The only other valid case is when the 180 day
4354 : * timeout has expired, when relax is specified.
4355 : */
4356 6 : if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
4357 : /* it is already deleted - really remove it this time */
4358 6 : talloc_free(tmp_ctx);
4359 6 : return ldb_next_request(module, req);
4360 : }
4361 :
4362 0 : ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
4363 : "This check is to prevent corruption of the replicated state.",
4364 : ldb_dn_get_linearized(old_msg->dn));
4365 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
4366 : }
4367 :
4368 133285 : rdn_name = ldb_dn_get_rdn_name(old_dn);
4369 133285 : rdn_value = ldb_dn_get_rdn_val(old_dn);
4370 133285 : if ((rdn_name == NULL) || (rdn_value == NULL)) {
4371 0 : talloc_free(tmp_ctx);
4372 0 : return ldb_operr(ldb);
4373 : }
4374 :
4375 133285 : msg = ldb_msg_new(tmp_ctx);
4376 133285 : if (msg == NULL) {
4377 0 : ldb_module_oom(module);
4378 0 : talloc_free(tmp_ctx);
4379 0 : return LDB_ERR_OPERATIONS_ERROR;
4380 : }
4381 :
4382 133285 : msg->dn = old_dn;
4383 :
4384 : /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
4385 133285 : disallow_move_on_delete =
4386 133285 : (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
4387 133285 : & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
4388 :
4389 : /* work out where we will be renaming this object to */
4390 133285 : if (!disallow_move_on_delete) {
4391 : struct ldb_dn *deleted_objects_dn;
4392 130778 : ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
4393 : &deleted_objects_dn);
4394 :
4395 : /*
4396 : * We should not move objects if we can't find the
4397 : * deleted objects DN. Not moving (or otherwise
4398 : * harming) the Deleted Objects DN itself is handled
4399 : * in the caller.
4400 : */
4401 130778 : if (re_delete && (ret != LDB_SUCCESS)) {
4402 0 : new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4403 0 : if (new_dn == NULL) {
4404 0 : ldb_module_oom(module);
4405 0 : talloc_free(tmp_ctx);
4406 0 : return LDB_ERR_OPERATIONS_ERROR;
4407 : }
4408 130778 : } else if (ret != LDB_SUCCESS) {
4409 : /* this is probably an attempted delete on a partition
4410 : * that doesn't allow delete operations, such as the
4411 : * schema partition */
4412 0 : ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
4413 : ldb_dn_get_linearized(old_dn));
4414 0 : talloc_free(tmp_ctx);
4415 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
4416 : } else {
4417 130778 : new_dn = deleted_objects_dn;
4418 : }
4419 : } else {
4420 2507 : new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4421 2507 : if (new_dn == NULL) {
4422 0 : ldb_module_oom(module);
4423 0 : talloc_free(tmp_ctx);
4424 0 : return LDB_ERR_OPERATIONS_ERROR;
4425 : }
4426 : }
4427 :
4428 : /* get the objects GUID from the search we just did */
4429 133285 : guid = samdb_result_guid(old_msg, "objectGUID");
4430 :
4431 133285 : if (deletion_state == OBJECT_NOT_DELETED) {
4432 : struct ldb_message_element *is_deleted_el;
4433 :
4434 47708 : ret = replmd_make_deleted_child_dn(tmp_ctx,
4435 : ldb,
4436 : new_dn,
4437 : rdn_name, rdn_value,
4438 : guid);
4439 :
4440 47708 : if (ret != LDB_SUCCESS) {
4441 0 : talloc_free(tmp_ctx);
4442 0 : return ret;
4443 : }
4444 :
4445 47708 : ret = ldb_msg_add_value(msg, "isDeleted", &true_val,
4446 : &is_deleted_el);
4447 47708 : if (ret != LDB_SUCCESS) {
4448 0 : ldb_asprintf_errstring(ldb, __location__
4449 : ": Failed to add isDeleted string to the msg");
4450 0 : talloc_free(tmp_ctx);
4451 0 : return ret;
4452 : }
4453 47708 : is_deleted_el->flags = LDB_FLAG_MOD_REPLACE;
4454 : } else {
4455 : /*
4456 : * No matter what has happened with other renames etc, try again to
4457 : * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj
4458 : */
4459 :
4460 85577 : struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
4461 85577 : retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
4462 85577 : if (!retb) {
4463 0 : ldb_asprintf_errstring(ldb, __location__
4464 : ": Unable to add a prepare rdn of %s",
4465 : ldb_dn_get_linearized(rdn));
4466 0 : talloc_free(tmp_ctx);
4467 0 : return LDB_ERR_OPERATIONS_ERROR;
4468 : }
4469 85577 : SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1);
4470 :
4471 85577 : retb = ldb_dn_add_child(new_dn, rdn);
4472 85577 : if (!retb) {
4473 0 : ldb_asprintf_errstring(ldb, __location__
4474 : ": Unable to add rdn %s to base dn: %s",
4475 : ldb_dn_get_linearized(rdn),
4476 : ldb_dn_get_linearized(new_dn));
4477 0 : talloc_free(tmp_ctx);
4478 0 : return LDB_ERR_OPERATIONS_ERROR;
4479 : }
4480 : }
4481 :
4482 : /*
4483 : now we need to modify the object in the following ways:
4484 :
4485 : - add isDeleted=TRUE
4486 : - update rDN and name, with new rDN
4487 : - remove linked attributes
4488 : - remove objectCategory and sAMAccountType
4489 : - remove attribs not on the preserved list
4490 : - preserved if in above list, or is rDN
4491 : - remove all linked attribs from this object
4492 : - remove all links from other objects to this object
4493 : (note we use the backlinks to do this, so we won't find one-way
4494 : links that still point to this object, or deactivated two-way
4495 : links, i.e. 'member' after the user has been removed from the
4496 : group)
4497 : - add lastKnownParent
4498 : - update replPropertyMetaData?
4499 :
4500 : see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
4501 : */
4502 :
4503 133285 : if (deletion_state == OBJECT_NOT_DELETED) {
4504 47708 : struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
4505 47708 : char *parent_dn_str = NULL;
4506 : struct ldb_message_element *p_el;
4507 :
4508 : /* we need the storage form of the parent GUID */
4509 47708 : ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
4510 : parent_dn, NULL,
4511 : DSDB_FLAG_NEXT_MODULE |
4512 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4513 : DSDB_SEARCH_REVEAL_INTERNALS|
4514 : DSDB_SEARCH_SHOW_RECYCLED, req);
4515 47708 : if (ret != LDB_SUCCESS) {
4516 0 : ldb_asprintf_errstring(ldb_module_get_ctx(module),
4517 : "repmd_delete: Failed to %s %s, "
4518 : "because we failed to find it's parent (%s): %s",
4519 : re_delete ? "re-delete" : "delete",
4520 : ldb_dn_get_linearized(old_dn),
4521 : ldb_dn_get_linearized(parent_dn),
4522 : ldb_errstring(ldb_module_get_ctx(module)));
4523 0 : talloc_free(tmp_ctx);
4524 0 : return ret;
4525 : }
4526 :
4527 : /*
4528 : * Now we can use the DB version,
4529 : * it will have the extended DN info in it
4530 : */
4531 47708 : parent_dn = parent_res->msgs[0]->dn;
4532 47708 : parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
4533 : parent_dn,
4534 : 1);
4535 47708 : if (parent_dn_str == NULL) {
4536 0 : talloc_free(tmp_ctx);
4537 0 : return ldb_module_oom(module);
4538 : }
4539 :
4540 47708 : ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
4541 : parent_dn_str);
4542 47708 : if (ret != LDB_SUCCESS) {
4543 0 : ldb_asprintf_errstring(ldb, __location__
4544 : ": Failed to add lastKnownParent "
4545 : "string when deleting %s",
4546 : ldb_dn_get_linearized(old_dn));
4547 0 : talloc_free(tmp_ctx);
4548 0 : return ret;
4549 : }
4550 47708 : p_el = ldb_msg_find_element(msg,
4551 : "lastKnownParent");
4552 47708 : if (p_el == NULL) {
4553 0 : talloc_free(tmp_ctx);
4554 0 : return ldb_module_operr(module);
4555 : }
4556 47708 : p_el->flags = LDB_FLAG_MOD_REPLACE;
4557 :
4558 47708 : if (next_deletion_state == OBJECT_DELETED) {
4559 0 : ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
4560 0 : if (ret != LDB_SUCCESS) {
4561 0 : ldb_asprintf_errstring(ldb, __location__
4562 : ": Failed to add msDS-LastKnownRDN "
4563 : "string when deleting %s",
4564 : ldb_dn_get_linearized(old_dn));
4565 0 : talloc_free(tmp_ctx);
4566 0 : return ret;
4567 : }
4568 0 : p_el = ldb_msg_find_element(msg,
4569 : "msDS-LastKnownRDN");
4570 0 : if (p_el == NULL) {
4571 0 : talloc_free(tmp_ctx);
4572 0 : return ldb_module_operr(module);
4573 : }
4574 0 : p_el->flags = LDB_FLAG_MOD_ADD;
4575 : }
4576 : }
4577 :
4578 133285 : switch (next_deletion_state) {
4579 :
4580 133285 : case OBJECT_RECYCLED:
4581 : case OBJECT_TOMBSTONE:
4582 :
4583 : /*
4584 : * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements
4585 : * describes what must be removed from a tombstone
4586 : * object
4587 : *
4588 : * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements
4589 : * describes what must be removed from a recycled
4590 : * object
4591 : *
4592 : */
4593 :
4594 : /*
4595 : * we also mark it as recycled, meaning this object can't be
4596 : * recovered (we are stripping its attributes).
4597 : * This is done only if we have this schema object of course ...
4598 : * This behavior is identical to the one of Windows 2008R2 which
4599 : * always set the isRecycled attribute, even if the recycle-bin is
4600 : * not activated and what ever the forest level is.
4601 : */
4602 133285 : if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
4603 : struct ldb_message_element *is_recycled_el;
4604 :
4605 133285 : ret = ldb_msg_add_value(msg, "isRecycled", &true_val,
4606 : &is_recycled_el);
4607 133285 : if (ret != LDB_SUCCESS) {
4608 0 : DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
4609 0 : ldb_module_oom(module);
4610 0 : talloc_free(tmp_ctx);
4611 0 : return ret;
4612 : }
4613 133285 : is_recycled_el->flags = LDB_FLAG_MOD_REPLACE;
4614 : }
4615 :
4616 133285 : replmd_private = talloc_get_type(ldb_module_get_private(module),
4617 : struct replmd_private);
4618 : /* work out which of the old attributes we will be removing */
4619 586006 : for (i=0; i<old_msg->num_elements; i++) {
4620 : const struct dsdb_attribute *sa;
4621 2173139 : el = &old_msg->elements[i];
4622 2173139 : sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
4623 2173139 : if (!sa) {
4624 0 : const char *old_dn_str
4625 0 : = ldb_dn_get_linearized(old_dn);
4626 :
4627 0 : ldb_asprintf_errstring(ldb,
4628 : __location__
4629 : ": Attribute %s "
4630 : "not found in schema "
4631 : "when deleting %s. "
4632 : "Existing record is invalid",
4633 0 : el->name,
4634 : old_dn_str);
4635 0 : talloc_free(tmp_ctx);
4636 0 : return LDB_ERR_OPERATIONS_ERROR;
4637 : }
4638 2173139 : if (ldb_attr_cmp(el->name, rdn_name) == 0) {
4639 : /* don't remove the rDN */
4640 133285 : continue;
4641 : }
4642 :
4643 2039854 : if (sa->linkID & 1) {
4644 5903 : bool caller_should_vanish = false;
4645 : /*
4646 : * we have a backlink in this object
4647 : * that needs to be removed. We're not
4648 : * allowed to remove it directly
4649 : * however, so we instead setup a
4650 : * modify to delete the corresponding
4651 : * forward link
4652 : */
4653 5903 : ret = replmd_delete_remove_link(module, schema,
4654 : replmd_private,
4655 : old_dn, &guid,
4656 : el, sa, req,
4657 : &caller_should_vanish);
4658 5903 : if (ret != LDB_SUCCESS) {
4659 0 : const char *old_dn_str
4660 0 : = ldb_dn_get_linearized(old_dn);
4661 0 : ldb_asprintf_errstring(ldb,
4662 : __location__
4663 : ": Failed to remove backlink of "
4664 : "%s when deleting %s: %s",
4665 0 : el->name,
4666 : old_dn_str,
4667 : ldb_errstring(ldb));
4668 0 : talloc_free(tmp_ctx);
4669 0 : return LDB_ERR_OPERATIONS_ERROR;
4670 : }
4671 :
4672 5903 : if (caller_should_vanish == false) {
4673 : /*
4674 : * now we continue, which means we
4675 : * won't remove this backlink
4676 : * directly
4677 : */
4678 5900 : continue;
4679 : }
4680 :
4681 : /*
4682 : * Otherwise vanish the link, we are
4683 : * out of sync and the controlling
4684 : * object does not have the source
4685 : * link any more
4686 : */
4687 :
4688 3 : dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4689 :
4690 2033951 : } else if (sa->linkID == 0) {
4691 2031159 : const char * const *attr = NULL;
4692 2031159 : if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
4693 1238815 : continue;
4694 : }
4695 1267409 : BINARY_ARRAY_SEARCH_V(preserved_attrs,
4696 : ARRAY_SIZE(preserved_attrs),
4697 : el->name,
4698 : ldb_attr_cmp,
4699 : attr);
4700 : /*
4701 : * If we are preserving, do not do the
4702 : * ldb_msg_add_empty() below, continue
4703 : * to the next element
4704 : */
4705 792344 : if (attr != NULL) {
4706 448185 : continue;
4707 : }
4708 : } else {
4709 : /*
4710 : * Ensure that we tell the modification to vanish any linked
4711 : * attributes (not simply mark them as isDeleted = TRUE)
4712 : */
4713 2792 : dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
4714 : }
4715 346954 : ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
4716 346954 : if (ret != LDB_SUCCESS) {
4717 0 : talloc_free(tmp_ctx);
4718 0 : ldb_module_oom(module);
4719 0 : return ret;
4720 : }
4721 : }
4722 :
4723 133285 : break;
4724 :
4725 0 : case OBJECT_DELETED:
4726 : /*
4727 : * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements
4728 : * describes what must be removed from a deleted
4729 : * object
4730 : */
4731 :
4732 0 : ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
4733 0 : if (ret != LDB_SUCCESS) {
4734 0 : talloc_free(tmp_ctx);
4735 0 : ldb_module_oom(module);
4736 0 : return ret;
4737 : }
4738 :
4739 0 : ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
4740 0 : if (ret != LDB_SUCCESS) {
4741 0 : talloc_free(tmp_ctx);
4742 0 : ldb_module_oom(module);
4743 0 : return ret;
4744 : }
4745 :
4746 0 : break;
4747 :
4748 0 : default:
4749 0 : break;
4750 : }
4751 :
4752 133285 : if (deletion_state == OBJECT_NOT_DELETED) {
4753 : const struct dsdb_attribute *sa;
4754 :
4755 : /* work out what the new rdn value is, for updating the
4756 : rDN and name fields */
4757 47708 : new_rdn_value = ldb_dn_get_rdn_val(new_dn);
4758 47708 : if (new_rdn_value == NULL) {
4759 0 : talloc_free(tmp_ctx);
4760 0 : return ldb_operr(ldb);
4761 : }
4762 :
4763 47708 : sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
4764 47708 : if (!sa) {
4765 0 : talloc_free(tmp_ctx);
4766 0 : return LDB_ERR_OPERATIONS_ERROR;
4767 : }
4768 :
4769 47708 : ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
4770 : &el);
4771 47708 : if (ret != LDB_SUCCESS) {
4772 0 : talloc_free(tmp_ctx);
4773 0 : return ret;
4774 : }
4775 47708 : el->flags = LDB_FLAG_MOD_REPLACE;
4776 :
4777 47708 : el = ldb_msg_find_element(old_msg, "name");
4778 47708 : if (el) {
4779 47708 : ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
4780 47708 : if (ret != LDB_SUCCESS) {
4781 0 : talloc_free(tmp_ctx);
4782 0 : return ret;
4783 : }
4784 47708 : el->flags = LDB_FLAG_MOD_REPLACE;
4785 : }
4786 : }
4787 :
4788 : /*
4789 : * TODO: Per MS-DRSR 5.160 RemoveObj we should remove links directly, not as an originating update!
4790 : *
4791 : */
4792 :
4793 : /*
4794 : * No matter what has happned with other renames, try again to
4795 : * get this to be under the deleted DN.
4796 : */
4797 133285 : if (strcmp(ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(new_dn)) != 0) {
4798 : /* now rename onto the new DN */
4799 47708 : ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
4800 47708 : if (ret != LDB_SUCCESS){
4801 0 : DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
4802 : ldb_dn_get_linearized(old_dn),
4803 : ldb_dn_get_linearized(new_dn),
4804 : ldb_errstring(ldb)));
4805 0 : talloc_free(tmp_ctx);
4806 0 : return ret;
4807 : }
4808 47708 : msg->dn = new_dn;
4809 : }
4810 :
4811 133285 : ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
4812 133285 : if (ret != LDB_SUCCESS) {
4813 0 : char *s = NULL;
4814 : /*
4815 : * This should not fail, so be quite verbose in the
4816 : * error handling if it fails
4817 : */
4818 0 : if (strcmp(ldb_dn_get_linearized(old_dn),
4819 : ldb_dn_get_linearized(new_dn)) != 0) {
4820 0 : DBG_NOTICE("Failure to handle '%s' of object %s "
4821 : "after successful rename to %s. "
4822 : "Error during tombstone modificaton was: %s\n",
4823 : re_delete ? "re-delete" : "delete",
4824 : ldb_dn_get_linearized(new_dn),
4825 : ldb_dn_get_linearized(old_dn),
4826 : ldb_errstring(ldb));
4827 : } else {
4828 0 : DBG_NOTICE("Failure to handle '%s' of object %s. "
4829 : "Error during tombstone modificaton was: %s\n",
4830 : re_delete ? "re-delete" : "delete",
4831 : ldb_dn_get_linearized(new_dn),
4832 : ldb_errstring(ldb));
4833 : }
4834 0 : s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(module),
4835 : tmp_ctx,
4836 : LDB_CHANGETYPE_MODIFY,
4837 : msg);
4838 :
4839 0 : DBG_INFO("Failed tombstone modify%s was:\n%s\n",
4840 : (dsdb_flags & DSDB_REPLMD_VANISH_LINKS) ?
4841 : " with VANISH_LINKS" : "",
4842 : s);
4843 0 : ldb_asprintf_errstring(ldb,
4844 : "replmd_delete: Failed to modify"
4845 : " object %s in '%s' - %s",
4846 : ldb_dn_get_linearized(old_dn),
4847 : re_delete ? "re-delete" : "delete",
4848 : ldb_errstring(ldb));
4849 0 : talloc_free(tmp_ctx);
4850 0 : return ret;
4851 : }
4852 :
4853 133285 : talloc_free(tmp_ctx);
4854 :
4855 133285 : return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
4856 : }
4857 :
4858 47716 : static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
4859 : {
4860 47716 : return replmd_delete_internals(module, req, false);
4861 : }
4862 :
4863 :
4864 0 : static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
4865 : {
4866 0 : return ret;
4867 : }
4868 :
4869 63 : static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
4870 : {
4871 63 : int ret = LDB_ERR_OTHER;
4872 : /* TODO: do some error mapping */
4873 :
4874 : /* Let the caller know the full WERROR */
4875 63 : ar->objs->error = status;
4876 :
4877 63 : return ret;
4878 : }
4879 :
4880 :
4881 : static struct replPropertyMetaData1 *
4882 160004 : replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
4883 : enum drsuapi_DsAttributeId attid)
4884 : {
4885 : uint32_t i;
4886 160004 : struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
4887 :
4888 1017274 : for (i = 0; i < rpmd_ctr->count; i++) {
4889 1016258 : if (rpmd_ctr->array[i].attid == attid) {
4890 158988 : return &rpmd_ctr->array[i];
4891 : }
4892 : }
4893 1016 : return NULL;
4894 : }
4895 :
4896 :
4897 : /*
4898 : return true if an update is newer than an existing entry
4899 : see section 5.11 of MS-ADTS
4900 : */
4901 1042536 : static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
4902 : const struct GUID *update_invocation_id,
4903 : uint32_t current_version,
4904 : uint32_t update_version,
4905 : NTTIME current_change_time,
4906 : NTTIME update_change_time)
4907 : {
4908 1042536 : if (update_version != current_version) {
4909 31818 : return update_version > current_version;
4910 : }
4911 1010718 : if (update_change_time != current_change_time) {
4912 156 : return update_change_time > current_change_time;
4913 : }
4914 1010562 : return GUID_compare(update_invocation_id, current_invocation_id) > 0;
4915 : }
4916 :
4917 1039407 : static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
4918 : struct replPropertyMetaData1 *new_m)
4919 : {
4920 2078814 : return replmd_update_is_newer(&cur_m->originating_invocation_id,
4921 1039407 : &new_m->originating_invocation_id,
4922 : cur_m->version,
4923 : new_m->version,
4924 : cur_m->originating_change_time,
4925 : new_m->originating_change_time);
4926 : }
4927 :
4928 1040423 : static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
4929 : struct replPropertyMetaData1 *cur_m,
4930 : struct replPropertyMetaData1 *new_m)
4931 : {
4932 : bool cmp;
4933 :
4934 : /*
4935 : * If the new replPropertyMetaData entry for this attribute is
4936 : * not provided (this happens in the case where we look for
4937 : * ATTID_name, but the name was not changed), then the local
4938 : * state is clearly still current, as the remote
4939 : * server didn't send it due to being older the high watermark
4940 : * USN we sent.
4941 : */
4942 1040423 : if (new_m == NULL) {
4943 1016 : return false;
4944 : }
4945 :
4946 1039407 : if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4947 : /*
4948 : * if we compare equal then do an
4949 : * update. This is used when a client
4950 : * asks for a FULL_SYNC, and can be
4951 : * used to recover a corrupt
4952 : * replica.
4953 : *
4954 : * This call is a bit tricky, what we
4955 : * are doing it turning the 'is_newer'
4956 : * call into a 'not is older' by
4957 : * swapping cur_m and new_m, and negating the
4958 : * outcome.
4959 : */
4960 1278710 : cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
4961 639355 : cur_m);
4962 : } else {
4963 400052 : cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
4964 : new_m);
4965 : }
4966 1039407 : return cmp;
4967 : }
4968 :
4969 :
4970 : /*
4971 : form a DN for a deleted (DEL:) or conflict (CNF:) DN
4972 : */
4973 47752 : static int replmd_make_prefix_child_dn(TALLOC_CTX *tmp_ctx,
4974 : struct ldb_context *ldb,
4975 : struct ldb_dn *dn,
4976 : const char *four_char_prefix,
4977 : const char *rdn_name,
4978 : const struct ldb_val *rdn_value,
4979 : struct GUID guid)
4980 : {
4981 : struct ldb_val deleted_child_rdn_val;
4982 : struct GUID_txt_buf guid_str;
4983 : int ret;
4984 : bool retb;
4985 :
4986 47752 : GUID_buf_string(&guid, &guid_str);
4987 :
4988 47752 : retb = ldb_dn_add_child_fmt(dn, "X=Y");
4989 47752 : if (!retb) {
4990 0 : ldb_asprintf_errstring(ldb, __location__
4991 : ": Unable to add a formatted child to dn: %s",
4992 : ldb_dn_get_linearized(dn));
4993 0 : return LDB_ERR_OPERATIONS_ERROR;
4994 : }
4995 :
4996 : /*
4997 : * TODO: Per MS-ADTS 3.1.1.5.5 Delete Operation
4998 : * we should truncate this value to ensure the RDN is not more than 255 chars.
4999 : *
5000 : * However we MS-ADTS 3.1.1.5.1.2 Naming Constraints indicates that:
5001 : *
5002 : * "Naming constraints are not enforced for replicated
5003 : * updates." so this is safe and we don't have to work out not
5004 : * splitting a UTF8 char right now.
5005 : */
5006 47752 : deleted_child_rdn_val = ldb_val_dup(tmp_ctx, rdn_value);
5007 :
5008 : /*
5009 : * sizeof(guid_str.buf) will always be longer than
5010 : * strlen(guid_str.buf) but we allocate using this and
5011 : * waste the trailing bytes to avoid scaring folks
5012 : * with memcpy() using strlen() below
5013 : */
5014 :
5015 : deleted_child_rdn_val.data
5016 47752 : = talloc_realloc(tmp_ctx, deleted_child_rdn_val.data,
5017 : uint8_t,
5018 : rdn_value->length + 5
5019 : + sizeof(guid_str.buf));
5020 47752 : if (!deleted_child_rdn_val.data) {
5021 0 : ldb_asprintf_errstring(ldb, __location__
5022 : ": Unable to add a formatted child to dn: %s",
5023 : ldb_dn_get_linearized(dn));
5024 0 : return LDB_ERR_OPERATIONS_ERROR;
5025 : }
5026 :
5027 47752 : deleted_child_rdn_val.length =
5028 47752 : rdn_value->length + 5
5029 47752 : + strlen(guid_str.buf);
5030 :
5031 47752 : SMB_ASSERT(deleted_child_rdn_val.length <
5032 : talloc_get_size(deleted_child_rdn_val.data));
5033 :
5034 : /*
5035 : * talloc won't allocate more than 256MB so we can't
5036 : * overflow but just to be sure
5037 : */
5038 47752 : if (deleted_child_rdn_val.length < rdn_value->length) {
5039 0 : return LDB_ERR_OPERATIONS_ERROR;
5040 : }
5041 :
5042 47752 : deleted_child_rdn_val.data[rdn_value->length] = 0x0a;
5043 47752 : memcpy(&deleted_child_rdn_val.data[rdn_value->length + 1],
5044 : four_char_prefix, 4);
5045 47752 : memcpy(&deleted_child_rdn_val.data[rdn_value->length + 5],
5046 : guid_str.buf,
5047 : sizeof(guid_str.buf));
5048 :
5049 : /* Now set the value into the RDN, without parsing it */
5050 47752 : ret = ldb_dn_set_component(
5051 : dn,
5052 : 0,
5053 : rdn_name,
5054 : deleted_child_rdn_val);
5055 :
5056 47752 : return ret;
5057 : }
5058 :
5059 :
5060 : /*
5061 : form a conflict DN
5062 : */
5063 44 : static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx,
5064 : struct ldb_context *ldb,
5065 : struct ldb_dn *dn,
5066 : struct GUID *guid)
5067 : {
5068 : const struct ldb_val *rdn_val;
5069 : const char *rdn_name;
5070 : struct ldb_dn *new_dn;
5071 : int ret;
5072 :
5073 44 : rdn_val = ldb_dn_get_rdn_val(dn);
5074 44 : rdn_name = ldb_dn_get_rdn_name(dn);
5075 44 : if (!rdn_val || !rdn_name) {
5076 0 : return NULL;
5077 : }
5078 :
5079 44 : new_dn = ldb_dn_get_parent(mem_ctx, dn);
5080 44 : if (!new_dn) {
5081 0 : return NULL;
5082 : }
5083 :
5084 44 : ret = replmd_make_prefix_child_dn(mem_ctx,
5085 : ldb, new_dn,
5086 : "CNF:",
5087 : rdn_name,
5088 : rdn_val,
5089 : *guid);
5090 44 : if (ret != LDB_SUCCESS) {
5091 0 : return NULL;
5092 : }
5093 44 : return new_dn;
5094 : }
5095 :
5096 : /*
5097 : form a deleted DN
5098 : */
5099 47708 : static int replmd_make_deleted_child_dn(TALLOC_CTX *tmp_ctx,
5100 : struct ldb_context *ldb,
5101 : struct ldb_dn *dn,
5102 : const char *rdn_name,
5103 : const struct ldb_val *rdn_value,
5104 : struct GUID guid)
5105 : {
5106 47708 : return replmd_make_prefix_child_dn(tmp_ctx,
5107 : ldb, dn,
5108 : "DEL:",
5109 : rdn_name,
5110 : rdn_value,
5111 : guid);
5112 : }
5113 :
5114 :
5115 : /*
5116 : perform a modify operation which sets the rDN and name attributes to
5117 : their current values. This has the effect of changing these
5118 : attributes to have been last updated by the current DC. This is
5119 : needed to ensure that renames performed as part of conflict
5120 : resolution are propagated to other DCs
5121 : */
5122 50 : static int replmd_name_modify(struct replmd_replicated_request *ar,
5123 : struct ldb_request *req, struct ldb_dn *dn)
5124 : {
5125 : struct ldb_message *msg;
5126 : const char *rdn_name;
5127 : const struct ldb_val *rdn_val;
5128 : const struct dsdb_attribute *rdn_attr;
5129 : int ret;
5130 :
5131 50 : msg = ldb_msg_new(req);
5132 50 : if (msg == NULL) {
5133 0 : goto failed;
5134 : }
5135 50 : msg->dn = dn;
5136 :
5137 50 : rdn_name = ldb_dn_get_rdn_name(dn);
5138 50 : if (rdn_name == NULL) {
5139 0 : goto failed;
5140 : }
5141 :
5142 : /* normalize the rdn attribute name */
5143 50 : rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
5144 50 : if (rdn_attr == NULL) {
5145 0 : goto failed;
5146 : }
5147 50 : rdn_name = rdn_attr->lDAPDisplayName;
5148 :
5149 50 : rdn_val = ldb_dn_get_rdn_val(dn);
5150 50 : if (rdn_val == NULL) {
5151 0 : goto failed;
5152 : }
5153 :
5154 50 : if (ldb_msg_append_value(msg, rdn_name, rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
5155 0 : goto failed;
5156 : }
5157 50 : if (ldb_msg_append_value(msg, "name", rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
5158 0 : goto failed;
5159 : }
5160 :
5161 : /*
5162 : * We have to mark this as a replicated update otherwise
5163 : * schema_data may reject a rename in the schema partition
5164 : */
5165 :
5166 50 : ret = dsdb_module_modify(ar->module, msg,
5167 : DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
5168 : req);
5169 50 : if (ret != LDB_SUCCESS) {
5170 0 : DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
5171 : ldb_dn_get_linearized(dn),
5172 : ldb_errstring(ldb_module_get_ctx(ar->module))));
5173 0 : return ret;
5174 : }
5175 :
5176 50 : talloc_free(msg);
5177 :
5178 50 : return LDB_SUCCESS;
5179 :
5180 0 : failed:
5181 0 : talloc_free(msg);
5182 0 : DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
5183 : ldb_dn_get_linearized(dn)));
5184 0 : return LDB_ERR_OPERATIONS_ERROR;
5185 : }
5186 :
5187 :
5188 : /*
5189 : callback for conflict DN handling where we have renamed the incoming
5190 : record. After renaming it, we need to ensure the change of name and
5191 : rDN for the incoming record is seen as an originating update by this DC.
5192 :
5193 : This also handles updating lastKnownParent for entries sent to lostAndFound
5194 : */
5195 22 : static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
5196 : {
5197 22 : struct replmd_replicated_request *ar =
5198 22 : talloc_get_type_abort(req->context, struct replmd_replicated_request);
5199 22 : struct ldb_dn *conflict_dn = NULL;
5200 : int ret;
5201 :
5202 22 : if (ares->error != LDB_SUCCESS) {
5203 : /* call the normal callback for everything except success */
5204 0 : return replmd_op_callback(req, ares);
5205 : }
5206 :
5207 22 : switch (req->operation) {
5208 20 : case LDB_ADD:
5209 20 : conflict_dn = req->op.add.message->dn;
5210 20 : break;
5211 2 : case LDB_MODIFY:
5212 2 : conflict_dn = req->op.mod.message->dn;
5213 2 : break;
5214 0 : default:
5215 0 : smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
5216 : }
5217 :
5218 : /* perform a modify of the rDN and name of the record */
5219 22 : ret = replmd_name_modify(ar, req, conflict_dn);
5220 22 : if (ret != LDB_SUCCESS) {
5221 0 : ares->error = ret;
5222 0 : return replmd_op_callback(req, ares);
5223 : }
5224 :
5225 22 : if (ar->objs->objects[ar->index_current].last_known_parent) {
5226 8 : struct ldb_message *msg = ldb_msg_new(req);
5227 8 : if (msg == NULL) {
5228 0 : ldb_module_oom(ar->module);
5229 0 : return LDB_ERR_OPERATIONS_ERROR;
5230 : }
5231 :
5232 8 : msg->dn = req->op.add.message->dn;
5233 :
5234 8 : ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
5235 8 : ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
5236 8 : if (ret != LDB_SUCCESS) {
5237 0 : DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
5238 0 : ldb_module_oom(ar->module);
5239 0 : return ret;
5240 : }
5241 8 : msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
5242 :
5243 8 : ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
5244 8 : if (ret != LDB_SUCCESS) {
5245 0 : DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
5246 : ldb_dn_get_linearized(msg->dn),
5247 : ldb_errstring(ldb_module_get_ctx(ar->module))));
5248 0 : return ret;
5249 : }
5250 8 : TALLOC_FREE(msg);
5251 : }
5252 :
5253 22 : return replmd_op_callback(req, ares);
5254 : }
5255 :
5256 :
5257 :
5258 : /*
5259 : * A helper for replmd_op_possible_conflict_callback() and
5260 : * replmd_replicated_handle_rename()
5261 : */
5262 46 : static int incoming_dn_should_be_renamed(TALLOC_CTX *mem_ctx,
5263 : struct replmd_replicated_request *ar,
5264 : struct ldb_dn *conflict_dn,
5265 : struct ldb_result **res,
5266 : bool *rename_incoming_record)
5267 : {
5268 : int ret;
5269 : bool rodc;
5270 : enum ndr_err_code ndr_err;
5271 46 : const struct ldb_val *omd_value = NULL;
5272 46 : struct replPropertyMetaDataBlob omd, *rmd = NULL;
5273 46 : struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
5274 46 : const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
5275 46 : struct replPropertyMetaData1 *omd_name = NULL;
5276 46 : struct replPropertyMetaData1 *rmd_name = NULL;
5277 46 : struct ldb_message *msg = NULL;
5278 :
5279 46 : ret = samdb_rodc(ldb, &rodc);
5280 46 : if (ret != LDB_SUCCESS) {
5281 0 : ldb_asprintf_errstring(
5282 : ldb,
5283 : "Failed to determine if we are an RODC when attempting "
5284 : "to form conflict DN: %s",
5285 : ldb_errstring(ldb));
5286 0 : return LDB_ERR_OPERATIONS_ERROR;
5287 : }
5288 :
5289 46 : if (rodc) {
5290 : /*
5291 : * We are on an RODC, or were a GC for this
5292 : * partition, so we have to fail this until
5293 : * someone who owns the partition sorts it
5294 : * out
5295 : */
5296 2 : ldb_asprintf_errstring(
5297 : ldb,
5298 : "Conflict adding object '%s' from incoming replication "
5299 : "but we are read only for the partition. \n"
5300 : " - We must fail the operation until a master for this "
5301 : "partition resolves the conflict",
5302 : ldb_dn_get_linearized(conflict_dn));
5303 2 : return LDB_ERR_OPERATIONS_ERROR;
5304 : }
5305 :
5306 : /*
5307 : * first we need the replPropertyMetaData attribute from the
5308 : * old record
5309 : */
5310 44 : ret = dsdb_module_search_dn(ar->module, mem_ctx, res, conflict_dn,
5311 : attrs,
5312 : DSDB_FLAG_NEXT_MODULE |
5313 : DSDB_SEARCH_SHOW_DELETED |
5314 : DSDB_SEARCH_SHOW_RECYCLED, ar->req);
5315 44 : if (ret != LDB_SUCCESS) {
5316 0 : DBG_ERR(__location__
5317 : ": Unable to find object for conflicting record '%s'\n",
5318 : ldb_dn_get_linearized(conflict_dn));
5319 0 : return LDB_ERR_OPERATIONS_ERROR;
5320 : }
5321 :
5322 44 : msg = (*res)->msgs[0];
5323 44 : omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
5324 44 : if (omd_value == NULL) {
5325 0 : DBG_ERR(__location__
5326 : ": Unable to find replPropertyMetaData for conflicting "
5327 : "record '%s'\n",
5328 : ldb_dn_get_linearized(conflict_dn));
5329 0 : return LDB_ERR_OPERATIONS_ERROR;
5330 : }
5331 :
5332 44 : ndr_err = ndr_pull_struct_blob(
5333 : omd_value, msg, &omd,
5334 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
5335 44 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5336 0 : DBG_ERR(__location__
5337 : ": Failed to parse old replPropertyMetaData for %s\n",
5338 : ldb_dn_get_linearized(conflict_dn));
5339 0 : return LDB_ERR_OPERATIONS_ERROR;
5340 : }
5341 :
5342 44 : rmd = ar->objs->objects[ar->index_current].meta_data;
5343 :
5344 : /*
5345 : * we decide which is newer based on the RPMD on the name
5346 : * attribute. See [MS-DRSR] ResolveNameConflict.
5347 : *
5348 : * We expect omd_name to be present, as this is from a local
5349 : * search, but while rmd_name should have been given to us by
5350 : * the remote server, if it is missing we just prefer the
5351 : * local name in
5352 : * replmd_replPropertyMetaData1_new_should_be_taken()
5353 : */
5354 44 : rmd_name = replmd_replPropertyMetaData1_find_attid(rmd,
5355 : DRSUAPI_ATTID_name);
5356 44 : omd_name = replmd_replPropertyMetaData1_find_attid(&omd,
5357 : DRSUAPI_ATTID_name);
5358 44 : if (!omd_name) {
5359 0 : DBG_ERR(__location__
5360 : ": Failed to find name attribute in "
5361 : "local LDB replPropertyMetaData for %s\n",
5362 : ldb_dn_get_linearized(conflict_dn));
5363 0 : return LDB_ERR_OPERATIONS_ERROR;
5364 : }
5365 :
5366 : /*
5367 : * Should we preserve the current record, and so rename the
5368 : * incoming record to be a conflict?
5369 : */
5370 44 : *rename_incoming_record =
5371 88 : !replmd_replPropertyMetaData1_new_should_be_taken(
5372 44 : (ar->objs->dsdb_repl_flags &
5373 : DSDB_REPL_FLAG_PRIORITISE_INCOMING),
5374 44 : omd_name, rmd_name);
5375 :
5376 44 : return LDB_SUCCESS;
5377 : }
5378 :
5379 :
5380 : /*
5381 : callback for replmd_replicated_apply_add()
5382 : This copes with the creation of conflict records in the case where
5383 : the DN exists, but with a different objectGUID
5384 : */
5385 317203 : static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct ldb_reply *ares, int (*callback)(struct ldb_request *req, struct ldb_reply *ares))
5386 : {
5387 : struct ldb_dn *conflict_dn;
5388 310673 : struct replmd_replicated_request *ar =
5389 317203 : talloc_get_type_abort(req->context, struct replmd_replicated_request);
5390 : struct ldb_result *res;
5391 : int ret;
5392 : bool rename_incoming_record;
5393 : struct ldb_message *msg;
5394 317203 : struct ldb_request *down_req = NULL;
5395 :
5396 : /* call the normal callback for success */
5397 317203 : if (ares->error == LDB_SUCCESS) {
5398 317170 : return callback(req, ares);
5399 : }
5400 :
5401 : /*
5402 : * we have a conflict, and need to decide if we will keep the
5403 : * new record or the old record
5404 : */
5405 :
5406 33 : msg = ar->objs->objects[ar->index_current].msg;
5407 33 : conflict_dn = msg->dn;
5408 :
5409 : /* For failures other than conflicts, fail the whole operation here */
5410 33 : if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5411 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
5412 : ldb_dn_get_linearized(conflict_dn),
5413 : ldb_errstring(ldb_module_get_ctx(ar->module)));
5414 :
5415 0 : return ldb_module_done(ar->req, NULL, NULL,
5416 : LDB_ERR_OPERATIONS_ERROR);
5417 : }
5418 :
5419 :
5420 33 : ret = incoming_dn_should_be_renamed(req, ar, conflict_dn, &res,
5421 : &rename_incoming_record);
5422 33 : if (ret != LDB_SUCCESS) {
5423 1 : goto failed;
5424 : }
5425 :
5426 32 : if (rename_incoming_record) {
5427 : struct GUID guid;
5428 : struct ldb_dn *new_dn;
5429 :
5430 14 : guid = samdb_result_guid(msg, "objectGUID");
5431 14 : if (GUID_all_zero(&guid)) {
5432 0 : DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
5433 : ldb_dn_get_linearized(conflict_dn)));
5434 0 : goto failed;
5435 : }
5436 14 : new_dn = replmd_conflict_dn(req,
5437 : ldb_module_get_ctx(ar->module),
5438 : conflict_dn, &guid);
5439 14 : if (new_dn == NULL) {
5440 0 : DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5441 : ldb_dn_get_linearized(conflict_dn)));
5442 0 : goto failed;
5443 : }
5444 :
5445 14 : DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
5446 : ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5447 :
5448 : /* re-submit the request, but with the new DN */
5449 14 : callback = replmd_op_name_modify_callback;
5450 14 : msg->dn = new_dn;
5451 : } else {
5452 : /* we are renaming the existing record */
5453 : struct GUID guid;
5454 : struct ldb_dn *new_dn;
5455 :
5456 18 : guid = samdb_result_guid(res->msgs[0], "objectGUID");
5457 18 : if (GUID_all_zero(&guid)) {
5458 0 : DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
5459 : ldb_dn_get_linearized(conflict_dn)));
5460 0 : goto failed;
5461 : }
5462 :
5463 18 : new_dn = replmd_conflict_dn(req,
5464 : ldb_module_get_ctx(ar->module),
5465 : conflict_dn, &guid);
5466 18 : if (new_dn == NULL) {
5467 0 : DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
5468 : ldb_dn_get_linearized(conflict_dn)));
5469 0 : goto failed;
5470 : }
5471 :
5472 18 : DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
5473 : ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
5474 :
5475 18 : ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
5476 : DSDB_FLAG_OWN_MODULE, req);
5477 18 : if (ret != LDB_SUCCESS) {
5478 0 : DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
5479 : ldb_dn_get_linearized(conflict_dn),
5480 : ldb_dn_get_linearized(new_dn),
5481 : ldb_errstring(ldb_module_get_ctx(ar->module))));
5482 0 : goto failed;
5483 : }
5484 :
5485 : /*
5486 : * now we need to ensure that the rename is seen as an
5487 : * originating update. We do that with a modify.
5488 : */
5489 18 : ret = replmd_name_modify(ar, req, new_dn);
5490 18 : if (ret != LDB_SUCCESS) {
5491 0 : goto failed;
5492 : }
5493 :
5494 18 : DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
5495 : ldb_dn_get_linearized(req->op.add.message->dn)));
5496 : }
5497 :
5498 32 : ret = ldb_build_add_req(&down_req,
5499 : ldb_module_get_ctx(ar->module),
5500 : req,
5501 : msg,
5502 : ar->controls,
5503 : ar,
5504 : callback,
5505 : req);
5506 32 : if (ret != LDB_SUCCESS) {
5507 0 : goto failed;
5508 : }
5509 32 : LDB_REQ_SET_LOCATION(down_req);
5510 :
5511 : /* current partition control needed by "repmd_op_callback" */
5512 32 : ret = ldb_request_add_control(down_req,
5513 : DSDB_CONTROL_CURRENT_PARTITION_OID,
5514 : false, NULL);
5515 32 : if (ret != LDB_SUCCESS) {
5516 0 : return replmd_replicated_request_error(ar, ret);
5517 : }
5518 :
5519 32 : if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5520 : /* this tells the partition module to make it a
5521 : partial replica if creating an NC */
5522 0 : ret = ldb_request_add_control(down_req,
5523 : DSDB_CONTROL_PARTIAL_REPLICA,
5524 : false, NULL);
5525 0 : if (ret != LDB_SUCCESS) {
5526 0 : return replmd_replicated_request_error(ar, ret);
5527 : }
5528 : }
5529 :
5530 : /*
5531 : * Finally we re-run the add, otherwise the new record won't
5532 : * exist, as we are here because of that exact failure!
5533 : */
5534 32 : return ldb_next_request(ar->module, down_req);
5535 1 : failed:
5536 :
5537 : /* on failure make the caller get the error. This means
5538 : * replication will stop with an error, but there is not much
5539 : * else we can do.
5540 : */
5541 1 : if (ret == LDB_SUCCESS) {
5542 0 : ret = LDB_ERR_OPERATIONS_ERROR;
5543 : }
5544 1 : return ldb_module_done(ar->req, NULL, NULL,
5545 : ret);
5546 : }
5547 :
5548 : /*
5549 : callback for replmd_replicated_apply_add()
5550 : This copes with the creation of conflict records in the case where
5551 : the DN exists, but with a different objectGUID
5552 : */
5553 317203 : static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
5554 : {
5555 310673 : struct replmd_replicated_request *ar =
5556 317203 : talloc_get_type_abort(req->context, struct replmd_replicated_request);
5557 :
5558 317203 : if (ar->objs->objects[ar->index_current].last_known_parent) {
5559 : /* This is like a conflict DN, where we put the object in LostAndFound
5560 : see MS-DRSR 4.1.10.6.10 FindBestParentObject */
5561 8 : return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
5562 : }
5563 :
5564 317195 : return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
5565 : }
5566 :
5567 : /*
5568 : this is called when a new object comes in over DRS
5569 : */
5570 317203 : static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
5571 : {
5572 : struct ldb_context *ldb;
5573 : struct ldb_request *change_req;
5574 : enum ndr_err_code ndr_err;
5575 : struct ldb_message *msg;
5576 : struct replPropertyMetaDataBlob *md;
5577 : struct ldb_val md_value;
5578 : unsigned int i;
5579 : int ret;
5580 317203 : bool remote_isDeleted = false;
5581 : bool is_schema_nc;
5582 : NTTIME now;
5583 317203 : time_t t = time(NULL);
5584 : const struct ldb_val *rdn_val;
5585 310673 : struct replmd_private *replmd_private =
5586 317203 : talloc_get_type(ldb_module_get_private(ar->module),
5587 : struct replmd_private);
5588 317203 : unix_to_nt_time(&now, t);
5589 :
5590 317203 : ldb = ldb_module_get_ctx(ar->module);
5591 317203 : msg = ar->objs->objects[ar->index_current].msg;
5592 317203 : md = ar->objs->objects[ar->index_current].meta_data;
5593 317203 : is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
5594 :
5595 317203 : ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
5596 317203 : if (ret != LDB_SUCCESS) {
5597 0 : return replmd_replicated_request_error(ar, ret);
5598 : }
5599 :
5600 317203 : ret = dsdb_msg_add_guid(msg,
5601 317203 : &ar->objs->objects[ar->index_current].object_guid,
5602 : "objectGUID");
5603 317203 : if (ret != LDB_SUCCESS) {
5604 0 : return replmd_replicated_request_error(ar, ret);
5605 : }
5606 :
5607 317203 : ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
5608 317203 : if (ret != LDB_SUCCESS) {
5609 0 : return replmd_replicated_request_error(ar, ret);
5610 : }
5611 :
5612 317203 : ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
5613 317203 : if (ret != LDB_SUCCESS) {
5614 0 : return replmd_replicated_request_error(ar, ret);
5615 : }
5616 :
5617 317203 : ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
5618 317203 : if (ret != LDB_SUCCESS) {
5619 0 : return replmd_replicated_request_error(ar, ret);
5620 : }
5621 :
5622 : /* remove any message elements that have zero values */
5623 6389970 : for (i=0; i<msg->num_elements; i++) {
5624 6072767 : struct ldb_message_element *el = &msg->elements[i];
5625 :
5626 6072767 : if (el->num_values == 0) {
5627 384853 : if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
5628 0 : ldb_asprintf_errstring(ldb, __location__
5629 : ": empty objectClass sent on %s, aborting replication\n",
5630 : ldb_dn_get_linearized(msg->dn));
5631 0 : return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
5632 : }
5633 :
5634 384853 : DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
5635 : el->name));
5636 384853 : ldb_msg_remove_element(msg, &msg->elements[i]);
5637 384853 : i--;
5638 384853 : continue;
5639 : }
5640 : }
5641 :
5642 317203 : if (DEBUGLVL(8)) {
5643 : struct GUID_txt_buf guid_txt;
5644 :
5645 0 : char *s = ldb_ldif_message_redacted_string(ldb, ar,
5646 : LDB_CHANGETYPE_ADD,
5647 : msg);
5648 0 : DEBUG(8, ("DRS replication add message of %s:\n%s\n",
5649 : GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5650 : s));
5651 0 : talloc_free(s);
5652 317203 : } else if (DEBUGLVL(4)) {
5653 : struct GUID_txt_buf guid_txt;
5654 0 : DEBUG(4, ("DRS replication add DN of %s is %s\n",
5655 : GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
5656 : ldb_dn_get_linearized(msg->dn)));
5657 : }
5658 317203 : remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
5659 : "isDeleted", false);
5660 :
5661 : /*
5662 : * the meta data array is already sorted by the caller, except
5663 : * for the RDN, which needs to be added.
5664 : */
5665 :
5666 :
5667 317203 : rdn_val = ldb_dn_get_rdn_val(msg->dn);
5668 317203 : ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
5669 : md, ar, now, is_schema_nc,
5670 : false);
5671 317203 : if (ret != LDB_SUCCESS) {
5672 0 : ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5673 0 : return replmd_replicated_request_error(ar, ret);
5674 : }
5675 :
5676 317203 : ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
5677 317203 : if (ret != LDB_SUCCESS) {
5678 0 : ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
5679 0 : return replmd_replicated_request_error(ar, ret);
5680 : }
5681 :
5682 5438361 : for (i=0; i < md->ctr.ctr1.count; i++) {
5683 5121158 : md->ctr.ctr1.array[i].local_usn = ar->seq_num;
5684 : }
5685 317203 : ndr_err = ndr_push_struct_blob(&md_value, msg, md,
5686 : (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
5687 317203 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
5688 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
5689 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
5690 : }
5691 317203 : ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
5692 317203 : if (ret != LDB_SUCCESS) {
5693 0 : return replmd_replicated_request_error(ar, ret);
5694 : }
5695 :
5696 317203 : replmd_ldb_message_sort(msg, ar->schema);
5697 :
5698 317203 : if (!remote_isDeleted) {
5699 : /*
5700 : * Ensure any local ACL inheritence is applied from
5701 : * the parent object.
5702 : *
5703 : * This is needed because descriptor is above
5704 : * repl_meta_data in the module stack, so this will
5705 : * not be trigered 'naturally' by the flow of
5706 : * operations.
5707 : */
5708 782561 : ret = dsdb_module_schedule_sd_propagation(ar->module,
5709 260967 : ar->objs->partition_dn,
5710 260967 : ar->objs->objects[ar->index_current].object_guid,
5711 260967 : ar->objs->objects[ar->index_current].parent_guid ?
5712 260627 : *ar->objs->objects[ar->index_current].parent_guid :
5713 6 : GUID_zero(),
5714 : true);
5715 260967 : if (ret != LDB_SUCCESS) {
5716 0 : return replmd_replicated_request_error(ar, ret);
5717 : }
5718 : }
5719 :
5720 317203 : ar->isDeleted = remote_isDeleted;
5721 :
5722 317203 : ret = ldb_build_add_req(&change_req,
5723 : ldb,
5724 : ar,
5725 : msg,
5726 : ar->controls,
5727 : ar,
5728 : replmd_op_add_callback,
5729 : ar->req);
5730 317203 : LDB_REQ_SET_LOCATION(change_req);
5731 317203 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5732 :
5733 : /* current partition control needed by "repmd_op_callback" */
5734 317203 : ret = ldb_request_add_control(change_req,
5735 : DSDB_CONTROL_CURRENT_PARTITION_OID,
5736 : false, NULL);
5737 317203 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5738 :
5739 317203 : if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
5740 : /* this tells the partition module to make it a
5741 : partial replica if creating an NC */
5742 7858 : ret = ldb_request_add_control(change_req,
5743 : DSDB_CONTROL_PARTIAL_REPLICA,
5744 : false, NULL);
5745 7858 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
5746 : }
5747 :
5748 317203 : return ldb_next_request(ar->module, change_req);
5749 : }
5750 :
5751 1073410 : static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
5752 : struct ldb_reply *ares)
5753 : {
5754 1073410 : struct replmd_replicated_request *ar = talloc_get_type(req->context,
5755 : struct replmd_replicated_request);
5756 : int ret;
5757 :
5758 1073410 : if (!ares) {
5759 0 : return ldb_module_done(ar->req, NULL, NULL,
5760 : LDB_ERR_OPERATIONS_ERROR);
5761 : }
5762 :
5763 : /*
5764 : * The error NO_SUCH_OBJECT is not expected, unless the search
5765 : * base is the partition DN, and that case doesn't happen here
5766 : * because then we wouldn't get a parent_guid_value in any
5767 : * case.
5768 : */
5769 1073410 : if (ares->error != LDB_SUCCESS) {
5770 0 : return ldb_module_done(ar->req, ares->controls,
5771 : ares->response, ares->error);
5772 : }
5773 :
5774 1073410 : switch (ares->type) {
5775 371660 : case LDB_REPLY_ENTRY:
5776 : {
5777 371660 : struct ldb_message *parent_msg = ares->message;
5778 371660 : struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
5779 371660 : struct ldb_dn *parent_dn = NULL;
5780 : int comp_num;
5781 :
5782 371660 : if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
5783 299905 : && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
5784 : /* Per MS-DRSR 4.1.10.6.10
5785 : * FindBestParentObject we need to move this
5786 : * new object under a deleted object to
5787 : * lost-and-found */
5788 : struct ldb_dn *nc_root;
5789 :
5790 8 : ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
5791 8 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
5792 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5793 : "No suitable NC root found for %s. "
5794 : "We need to move this object because parent object %s "
5795 : "is deleted, but this object is not.",
5796 : ldb_dn_get_linearized(msg->dn),
5797 : ldb_dn_get_linearized(parent_msg->dn));
5798 0 : return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5799 8 : } else if (ret != LDB_SUCCESS) {
5800 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5801 : "Unable to find NC root for %s: %s. "
5802 : "We need to move this object because parent object %s "
5803 : "is deleted, but this object is not.",
5804 : ldb_dn_get_linearized(msg->dn),
5805 : ldb_errstring(ldb_module_get_ctx(ar->module)),
5806 : ldb_dn_get_linearized(parent_msg->dn));
5807 0 : return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5808 : }
5809 :
5810 8 : ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
5811 : nc_root,
5812 : DS_GUID_LOSTANDFOUND_CONTAINER,
5813 : &parent_dn);
5814 8 : if (ret != LDB_SUCCESS) {
5815 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5816 : "Unable to find LostAndFound Container for %s "
5817 : "in partition %s: %s. "
5818 : "We need to move this object because parent object %s "
5819 : "is deleted, but this object is not.",
5820 : ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
5821 : ldb_errstring(ldb_module_get_ctx(ar->module)),
5822 : ldb_dn_get_linearized(parent_msg->dn));
5823 0 : return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
5824 : }
5825 8 : ar->objs->objects[ar->index_current].last_known_parent
5826 16 : = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5827 :
5828 : } else {
5829 : parent_dn
5830 371652 : = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
5831 :
5832 : }
5833 371660 : ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
5834 :
5835 371660 : comp_num = ldb_dn_get_comp_num(msg->dn);
5836 371660 : if (comp_num > 1) {
5837 371660 : if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
5838 0 : talloc_free(ares);
5839 0 : return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5840 : }
5841 : }
5842 371660 : if (!ldb_dn_add_base(msg->dn, parent_dn)) {
5843 0 : talloc_free(ares);
5844 0 : return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
5845 : }
5846 371660 : break;
5847 : }
5848 330062 : case LDB_REPLY_REFERRAL:
5849 : /* we ignore referrals */
5850 330062 : break;
5851 :
5852 371688 : case LDB_REPLY_DONE:
5853 :
5854 371688 : if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
5855 : struct GUID_txt_buf str_buf;
5856 28 : if (ar->search_msg != NULL) {
5857 8 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5858 : "No parent with GUID %s found for object locally known as %s",
5859 4 : GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5860 4 : ldb_dn_get_linearized(ar->search_msg->dn));
5861 : } else {
5862 48 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
5863 : "No parent with GUID %s found for object remotely known as %s",
5864 24 : GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
5865 24 : ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
5866 : }
5867 :
5868 : /*
5869 : * This error code is really important, as it
5870 : * is the flag back to the callers to retry
5871 : * this with DRSUAPI_DRS_GET_ANC, and so get
5872 : * the parent objects before the child
5873 : * objects
5874 : */
5875 28 : return ldb_module_done(ar->req, NULL, NULL,
5876 28 : replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
5877 : }
5878 :
5879 371660 : if (ar->search_msg != NULL) {
5880 54797 : ret = replmd_replicated_apply_merge(ar);
5881 : } else {
5882 316863 : ret = replmd_replicated_apply_add(ar);
5883 : }
5884 371660 : if (ret != LDB_SUCCESS) {
5885 1 : return ldb_module_done(ar->req, NULL, NULL, ret);
5886 : }
5887 : }
5888 :
5889 1073381 : talloc_free(ares);
5890 1073381 : return LDB_SUCCESS;
5891 : }
5892 :
5893 : /*
5894 : * Look for the parent object, so we put the new object in the right
5895 : * place This is akin to NameObject in MS-DRSR - this routine and the
5896 : * callbacks find the right parent name, and correct name for this
5897 : * object
5898 : */
5899 :
5900 372182 : static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
5901 : {
5902 : struct ldb_context *ldb;
5903 : int ret;
5904 : char *tmp_str;
5905 : char *filter;
5906 : struct ldb_request *search_req;
5907 : static const char *attrs[] = {"isDeleted", NULL};
5908 : struct GUID_txt_buf guid_str_buf;
5909 :
5910 372182 : ldb = ldb_module_get_ctx(ar->module);
5911 :
5912 372182 : if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
5913 494 : if (ar->search_msg != NULL) {
5914 154 : return replmd_replicated_apply_merge(ar);
5915 : } else {
5916 340 : return replmd_replicated_apply_add(ar);
5917 : }
5918 : }
5919 :
5920 371688 : tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
5921 : &guid_str_buf);
5922 :
5923 371688 : filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
5924 371688 : if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
5925 :
5926 736852 : ret = ldb_build_search_req(&search_req,
5927 : ldb,
5928 : ar,
5929 371688 : ar->objs->partition_dn,
5930 : LDB_SCOPE_SUBTREE,
5931 : filter,
5932 : attrs,
5933 : NULL,
5934 : ar,
5935 : replmd_replicated_apply_search_for_parent_callback,
5936 : ar->req);
5937 371688 : LDB_REQ_SET_LOCATION(search_req);
5938 :
5939 371688 : ret = dsdb_request_add_controls(search_req,
5940 : DSDB_SEARCH_SHOW_RECYCLED|
5941 : DSDB_SEARCH_SHOW_DELETED|
5942 : DSDB_SEARCH_SHOW_EXTENDED_DN);
5943 371688 : if (ret != LDB_SUCCESS) {
5944 0 : return ret;
5945 : }
5946 :
5947 371688 : return ldb_next_request(ar->module, search_req);
5948 : }
5949 :
5950 : /*
5951 : handle renames that come in over DRS replication
5952 : */
5953 6827 : static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
5954 : struct ldb_message *msg,
5955 : struct ldb_request *parent,
5956 : bool *renamed_to_conflict)
5957 : {
5958 : int ret;
5959 6827 : TALLOC_CTX *tmp_ctx = talloc_new(msg);
5960 : struct ldb_result *res;
5961 : struct ldb_dn *conflict_dn;
5962 : bool rename_incoming_record;
5963 : struct ldb_dn *new_dn;
5964 : struct GUID guid;
5965 :
5966 6827 : DEBUG(4,("replmd_replicated_request rename %s => %s\n",
5967 : ldb_dn_get_linearized(ar->search_msg->dn),
5968 : ldb_dn_get_linearized(msg->dn)));
5969 :
5970 :
5971 6827 : ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
5972 : DSDB_FLAG_NEXT_MODULE, ar->req);
5973 6827 : if (ret == LDB_SUCCESS) {
5974 6814 : talloc_free(tmp_ctx);
5975 6814 : return ret;
5976 : }
5977 :
5978 13 : if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
5979 0 : talloc_free(tmp_ctx);
5980 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
5981 0 : ldb_dn_get_linearized(ar->search_msg->dn),
5982 : ldb_dn_get_linearized(msg->dn),
5983 : ldb_errstring(ldb_module_get_ctx(ar->module)));
5984 0 : return ret;
5985 : }
5986 :
5987 13 : conflict_dn = msg->dn;
5988 :
5989 :
5990 13 : ret = incoming_dn_should_be_renamed(tmp_ctx, ar, conflict_dn, &res,
5991 : &rename_incoming_record);
5992 13 : if (ret != LDB_SUCCESS) {
5993 1 : goto failed;
5994 : }
5995 :
5996 12 : if (rename_incoming_record) {
5997 :
5998 2 : new_dn = replmd_conflict_dn(msg,
5999 : ldb_module_get_ctx(ar->module),
6000 : msg->dn,
6001 2 : &ar->objs->objects[ar->index_current].object_guid);
6002 2 : if (new_dn == NULL) {
6003 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
6004 : "Failed to form conflict DN for %s\n",
6005 : ldb_dn_get_linearized(msg->dn));
6006 :
6007 0 : talloc_free(tmp_ctx);
6008 0 : return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6009 : }
6010 :
6011 2 : ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
6012 : DSDB_FLAG_NEXT_MODULE, ar->req);
6013 2 : if (ret != LDB_SUCCESS) {
6014 0 : ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
6015 : "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
6016 : ldb_dn_get_linearized(conflict_dn),
6017 0 : ldb_dn_get_linearized(ar->search_msg->dn),
6018 : ldb_dn_get_linearized(new_dn),
6019 : ldb_errstring(ldb_module_get_ctx(ar->module)));
6020 0 : talloc_free(tmp_ctx);
6021 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6022 : }
6023 :
6024 2 : msg->dn = new_dn;
6025 2 : *renamed_to_conflict = true;
6026 2 : talloc_free(tmp_ctx);
6027 2 : return LDB_SUCCESS;
6028 : }
6029 :
6030 : /* we are renaming the existing record */
6031 :
6032 10 : guid = samdb_result_guid(res->msgs[0], "objectGUID");
6033 10 : if (GUID_all_zero(&guid)) {
6034 0 : DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
6035 : ldb_dn_get_linearized(conflict_dn)));
6036 0 : goto failed;
6037 : }
6038 :
6039 10 : new_dn = replmd_conflict_dn(tmp_ctx,
6040 : ldb_module_get_ctx(ar->module),
6041 : conflict_dn, &guid);
6042 10 : if (new_dn == NULL) {
6043 0 : DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
6044 : ldb_dn_get_linearized(conflict_dn)));
6045 0 : goto failed;
6046 : }
6047 :
6048 10 : DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
6049 : ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
6050 :
6051 10 : ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
6052 : DSDB_FLAG_OWN_MODULE, ar->req);
6053 10 : if (ret != LDB_SUCCESS) {
6054 0 : DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
6055 : ldb_dn_get_linearized(conflict_dn),
6056 : ldb_dn_get_linearized(new_dn),
6057 : ldb_errstring(ldb_module_get_ctx(ar->module))));
6058 0 : goto failed;
6059 : }
6060 :
6061 : /*
6062 : * now we need to ensure that the rename is seen as an
6063 : * originating update. We do that with a modify.
6064 : */
6065 10 : ret = replmd_name_modify(ar, ar->req, new_dn);
6066 10 : if (ret != LDB_SUCCESS) {
6067 0 : goto failed;
6068 : }
6069 :
6070 10 : DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
6071 : ldb_dn_get_linearized(ar->search_msg->dn),
6072 : ldb_dn_get_linearized(msg->dn)));
6073 :
6074 : /*
6075 : * With the other record out of the way, do the rename we had
6076 : * at the top again
6077 : */
6078 10 : ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
6079 : DSDB_FLAG_NEXT_MODULE, ar->req);
6080 10 : if (ret != LDB_SUCCESS) {
6081 0 : DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
6082 : ldb_dn_get_linearized(ar->search_msg->dn),
6083 : ldb_dn_get_linearized(msg->dn),
6084 : ldb_errstring(ldb_module_get_ctx(ar->module))));
6085 0 : goto failed;
6086 : }
6087 :
6088 10 : talloc_free(tmp_ctx);
6089 10 : return ret;
6090 1 : failed:
6091 : /*
6092 : * On failure make the caller get the error
6093 : * This means replication will stop with an error,
6094 : * but there is not much else we can do. In the
6095 : * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
6096 : * needed.
6097 : */
6098 1 : if (ret == LDB_SUCCESS) {
6099 0 : ret = LDB_ERR_OPERATIONS_ERROR;
6100 : }
6101 :
6102 1 : talloc_free(tmp_ctx);
6103 1 : return ret;
6104 : }
6105 :
6106 :
6107 79954 : static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
6108 : {
6109 : struct ldb_context *ldb;
6110 : struct ldb_request *change_req;
6111 : enum ndr_err_code ndr_err;
6112 : struct ldb_message *msg;
6113 : struct replPropertyMetaDataBlob *rmd;
6114 : struct replPropertyMetaDataBlob omd;
6115 : const struct ldb_val *omd_value;
6116 : struct replPropertyMetaDataBlob nmd;
6117 : struct ldb_val nmd_value;
6118 : struct GUID remote_parent_guid;
6119 : unsigned int i;
6120 79954 : uint32_t j,ni=0;
6121 79954 : unsigned int removed_attrs = 0;
6122 : int ret;
6123 79954 : int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
6124 79954 : bool isDeleted = false;
6125 79954 : bool local_isDeleted = false;
6126 79954 : bool remote_isDeleted = false;
6127 79954 : bool take_remote_isDeleted = false;
6128 79954 : bool sd_updated = false;
6129 79954 : bool renamed = false;
6130 79954 : bool renamed_to_conflict = false;
6131 79954 : bool is_schema_nc = false;
6132 : NTSTATUS nt_status;
6133 : const struct ldb_val *old_rdn, *new_rdn;
6134 79954 : struct replmd_private *replmd_private =
6135 79954 : talloc_get_type(ldb_module_get_private(ar->module),
6136 : struct replmd_private);
6137 : NTTIME now;
6138 79954 : time_t t = time(NULL);
6139 79954 : unix_to_nt_time(&now, t);
6140 :
6141 79954 : ldb = ldb_module_get_ctx(ar->module);
6142 79954 : msg = ar->objs->objects[ar->index_current].msg;
6143 :
6144 79954 : is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
6145 :
6146 79954 : rmd = ar->objs->objects[ar->index_current].meta_data;
6147 79954 : ZERO_STRUCT(omd);
6148 79954 : omd.version = 1;
6149 :
6150 : /* find existing meta data */
6151 79954 : omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6152 79954 : if (omd_value) {
6153 79954 : ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6154 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6155 79954 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6156 0 : nt_status = ndr_map_error2ntstatus(ndr_err);
6157 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6158 : }
6159 :
6160 79954 : if (omd.version != 1) {
6161 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6162 : }
6163 : }
6164 :
6165 79954 : if (DEBUGLVL(8)) {
6166 : struct GUID_txt_buf guid_txt;
6167 :
6168 0 : char *s = ldb_ldif_message_redacted_string(ldb, ar,
6169 : LDB_CHANGETYPE_MODIFY, msg);
6170 0 : DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
6171 : "%s\n"
6172 : "%s\n",
6173 : GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
6174 : s,
6175 : ndr_print_struct_string(s,
6176 : (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6177 : "existing replPropertyMetaData",
6178 : &omd),
6179 : ndr_print_struct_string(s,
6180 : (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
6181 : "incoming replPropertyMetaData",
6182 : rmd)));
6183 0 : talloc_free(s);
6184 79954 : } else if (DEBUGLVL(4)) {
6185 : struct GUID_txt_buf guid_txt;
6186 :
6187 0 : DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
6188 : GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6189 : &guid_txt),
6190 : ldb_dn_get_linearized(msg->dn)));
6191 : }
6192 :
6193 79954 : local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
6194 : "isDeleted", false);
6195 79954 : remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
6196 : "isDeleted", false);
6197 :
6198 : /*
6199 : * Fill in the remote_parent_guid with the GUID or an all-zero
6200 : * GUID.
6201 : */
6202 79954 : if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
6203 79800 : remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
6204 : } else {
6205 154 : remote_parent_guid = GUID_zero();
6206 : }
6207 :
6208 : /*
6209 : * To ensure we follow a complex rename chain around, we have
6210 : * to confirm that the DN is the same (mostly to confirm the
6211 : * RDN) and the parentGUID is the same.
6212 : *
6213 : * This ensures we keep things under the correct parent, which
6214 : * replmd_replicated_handle_rename() will do.
6215 : */
6216 :
6217 79954 : if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
6218 73127 : && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
6219 73127 : ret = LDB_SUCCESS;
6220 : } else {
6221 : /*
6222 : * handle renames, even just by case that come in over
6223 : * DRS. Changes in the parent DN don't hit us here,
6224 : * because the search for a parent will clean up those
6225 : * components.
6226 : *
6227 : * We also have already filtered out the case where
6228 : * the peer has an older name to what we have (see
6229 : * replmd_replicated_apply_search_callback())
6230 : */
6231 6827 : ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed_to_conflict);
6232 :
6233 : /*
6234 : * This looks strange, but we must set this after any
6235 : * rename, otherwise the SD propegation will not
6236 : * happen (which might matter if we have a new parent)
6237 : *
6238 : * The additional case of calling
6239 : * replmd_op_name_modify_callback (below) is
6240 : * controlled by renamed_to_conflict.
6241 : */
6242 6827 : renamed = true;
6243 : }
6244 :
6245 79954 : if (ret != LDB_SUCCESS) {
6246 2 : ldb_debug(ldb, LDB_DEBUG_FATAL,
6247 : "replmd_replicated_request rename %s => %s failed - %s\n",
6248 1 : ldb_dn_get_linearized(ar->search_msg->dn),
6249 : ldb_dn_get_linearized(msg->dn),
6250 : ldb_errstring(ldb));
6251 1 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6252 : }
6253 :
6254 79953 : if (renamed_to_conflict == true) {
6255 : /*
6256 : * Set the callback to one that will fix up the name
6257 : * metadata on the new conflict DN
6258 : */
6259 2 : callback = replmd_op_name_modify_callback;
6260 : }
6261 :
6262 79953 : ZERO_STRUCT(nmd);
6263 79953 : nmd.version = 1;
6264 79953 : nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
6265 79953 : nmd.ctr.ctr1.array = talloc_array(ar,
6266 : struct replPropertyMetaData1,
6267 : nmd.ctr.ctr1.count);
6268 79953 : if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6269 :
6270 : /* first copy the old meta data */
6271 1169301 : for (i=0; i < omd.ctr.ctr1.count; i++) {
6272 1089348 : nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
6273 1089348 : ni++;
6274 : }
6275 :
6276 79953 : ar->seq_num = 0;
6277 : /* now merge in the new meta data */
6278 1060474 : for (i=0; i < rmd->ctr.ctr1.count; i++) {
6279 980521 : bool found = false;
6280 :
6281 17621768 : for (j=0; j < ni; j++) {
6282 : bool cmp;
6283 :
6284 8790784 : if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
6285 7830363 : continue;
6286 : }
6287 :
6288 2881263 : cmp = replmd_replPropertyMetaData1_new_should_be_taken(
6289 960421 : ar->objs->dsdb_repl_flags,
6290 960421 : &nmd.ctr.ctr1.array[j],
6291 960421 : &rmd->ctr.ctr1.array[i]);
6292 960421 : if (cmp) {
6293 : /* replace the entry */
6294 616413 : nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
6295 616413 : if (ar->seq_num == 0) {
6296 49527 : ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6297 49527 : if (ret != LDB_SUCCESS) {
6298 0 : return replmd_replicated_request_error(ar, ret);
6299 : }
6300 : }
6301 616413 : nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
6302 616413 : switch (nmd.ctr.ctr1.array[j].attid) {
6303 48127 : case DRSUAPI_ATTID_ntSecurityDescriptor:
6304 48127 : sd_updated = true;
6305 48127 : break;
6306 9732 : case DRSUAPI_ATTID_isDeleted:
6307 9732 : take_remote_isDeleted = true;
6308 9732 : break;
6309 558554 : default:
6310 558554 : break;
6311 : }
6312 616413 : found = true;
6313 616413 : break;
6314 : }
6315 :
6316 344008 : if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
6317 312184 : DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
6318 : msg->elements[i-removed_attrs].name,
6319 : ldb_dn_get_linearized(msg->dn),
6320 : GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
6321 : }
6322 :
6323 : /* we don't want to apply this change so remove the attribute */
6324 344008 : ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
6325 344008 : removed_attrs++;
6326 :
6327 344008 : found = true;
6328 344008 : break;
6329 : }
6330 :
6331 980521 : if (found) continue;
6332 :
6333 20100 : nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
6334 20100 : if (ar->seq_num == 0) {
6335 6441 : ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
6336 6441 : if (ret != LDB_SUCCESS) {
6337 0 : return replmd_replicated_request_error(ar, ret);
6338 : }
6339 : }
6340 20100 : nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
6341 20100 : switch (nmd.ctr.ctr1.array[ni].attid) {
6342 0 : case DRSUAPI_ATTID_ntSecurityDescriptor:
6343 0 : sd_updated = true;
6344 0 : break;
6345 6130 : case DRSUAPI_ATTID_isDeleted:
6346 6130 : take_remote_isDeleted = true;
6347 6130 : break;
6348 13970 : default:
6349 13970 : break;
6350 : }
6351 20100 : ni++;
6352 : }
6353 :
6354 : /*
6355 : * finally correct the size of the meta_data array
6356 : */
6357 79953 : nmd.ctr.ctr1.count = ni;
6358 :
6359 79953 : new_rdn = ldb_dn_get_rdn_val(msg->dn);
6360 79953 : old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
6361 :
6362 79953 : if (renamed) {
6363 6826 : ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
6364 : &nmd, ar, now, is_schema_nc,
6365 : false);
6366 6826 : if (ret != LDB_SUCCESS) {
6367 0 : ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6368 0 : return replmd_replicated_request_error(ar, ret);
6369 : }
6370 : }
6371 : /*
6372 : * sort the new meta data array
6373 : */
6374 79953 : ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
6375 79953 : if (ret != LDB_SUCCESS) {
6376 0 : ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
6377 0 : return ret;
6378 : }
6379 :
6380 : /*
6381 : * Work out if this object is deleted, so we can prune any extra attributes. See MS-DRSR 4.1.10.6.9
6382 : * UpdateObject.
6383 : *
6384 : * This also controls SD propagation below
6385 : */
6386 79953 : if (take_remote_isDeleted) {
6387 15862 : isDeleted = remote_isDeleted;
6388 : } else {
6389 64091 : isDeleted = local_isDeleted;
6390 : }
6391 :
6392 79953 : ar->isDeleted = isDeleted;
6393 :
6394 : /*
6395 : * check if some replicated attributes left, otherwise skip the ldb_modify() call
6396 : */
6397 79953 : if (msg->num_elements == 0) {
6398 23985 : ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
6399 : ar->index_current);
6400 :
6401 23985 : return replmd_replicated_apply_isDeleted(ar);
6402 : }
6403 :
6404 55968 : ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
6405 : ar->index_current, msg->num_elements);
6406 :
6407 55968 : if (renamed) {
6408 : /*
6409 : * This is an new name for this object, so we must
6410 : * inherit from the parent
6411 : *
6412 : * This is needed because descriptor is above
6413 : * repl_meta_data in the module stack, so this will
6414 : * not be trigered 'naturally' by the flow of
6415 : * operations.
6416 : */
6417 20478 : ret = dsdb_module_schedule_sd_propagation(ar->module,
6418 6826 : ar->objs->partition_dn,
6419 6826 : ar->objs->objects[ar->index_current].object_guid,
6420 6826 : ar->objs->objects[ar->index_current].parent_guid ?
6421 6826 : *ar->objs->objects[ar->index_current].parent_guid :
6422 0 : GUID_zero(),
6423 : true);
6424 6826 : if (ret != LDB_SUCCESS) {
6425 0 : return ldb_operr(ldb);
6426 : }
6427 : }
6428 :
6429 55968 : if (sd_updated && !isDeleted) {
6430 : /*
6431 : * This is an existing object, so there is no need to
6432 : * inherit from the parent, but we must inherit any
6433 : * incoming changes to our child objects.
6434 : *
6435 : * This is needed because descriptor is above
6436 : * repl_meta_data in the module stack, so this will
6437 : * not be trigered 'naturally' by the flow of
6438 : * operations.
6439 : */
6440 117035 : ret = dsdb_module_schedule_sd_propagation(ar->module,
6441 39063 : ar->objs->partition_dn,
6442 39063 : ar->objs->objects[ar->index_current].object_guid,
6443 39063 : ar->objs->objects[ar->index_current].parent_guid ?
6444 38909 : *ar->objs->objects[ar->index_current].parent_guid :
6445 0 : GUID_zero(),
6446 : false);
6447 39063 : if (ret != LDB_SUCCESS) {
6448 0 : return ldb_operr(ldb);
6449 : }
6450 : }
6451 :
6452 : /* create the meta data value */
6453 55968 : ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
6454 : (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
6455 55968 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6456 0 : nt_status = ndr_map_error2ntstatus(ndr_err);
6457 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6458 : }
6459 :
6460 : /*
6461 : * when we know that we'll modify the record, add the whenChanged, uSNChanged
6462 : * and replPopertyMetaData attributes
6463 : */
6464 55968 : ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
6465 55968 : if (ret != LDB_SUCCESS) {
6466 0 : return replmd_replicated_request_error(ar, ret);
6467 : }
6468 55968 : ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
6469 55968 : if (ret != LDB_SUCCESS) {
6470 0 : return replmd_replicated_request_error(ar, ret);
6471 : }
6472 55968 : ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
6473 55968 : if (ret != LDB_SUCCESS) {
6474 0 : return replmd_replicated_request_error(ar, ret);
6475 : }
6476 :
6477 55968 : replmd_ldb_message_sort(msg, ar->schema);
6478 :
6479 : /* we want to replace the old values */
6480 867188 : for (i=0; i < msg->num_elements; i++) {
6481 811220 : msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
6482 811220 : if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
6483 48079 : if (msg->elements[i].num_values == 0) {
6484 0 : ldb_asprintf_errstring(ldb, __location__
6485 : ": objectClass removed on %s, aborting replication\n",
6486 : ldb_dn_get_linearized(msg->dn));
6487 0 : return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
6488 : }
6489 : }
6490 : }
6491 :
6492 55968 : if (DEBUGLVL(8)) {
6493 : struct GUID_txt_buf guid_txt;
6494 :
6495 0 : char *s = ldb_ldif_message_redacted_string(ldb, ar,
6496 : LDB_CHANGETYPE_MODIFY,
6497 : msg);
6498 0 : DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
6499 : GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6500 : &guid_txt),
6501 : s));
6502 0 : talloc_free(s);
6503 55968 : } else if (DEBUGLVL(4)) {
6504 : struct GUID_txt_buf guid_txt;
6505 :
6506 0 : DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
6507 : GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6508 : &guid_txt),
6509 : ldb_dn_get_linearized(msg->dn)));
6510 : }
6511 :
6512 55968 : ret = ldb_build_mod_req(&change_req,
6513 : ldb,
6514 : ar,
6515 : msg,
6516 : ar->controls,
6517 : ar,
6518 : callback,
6519 : ar->req);
6520 55968 : LDB_REQ_SET_LOCATION(change_req);
6521 55968 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6522 :
6523 : /* current partition control needed by "repmd_op_callback" */
6524 55968 : ret = ldb_request_add_control(change_req,
6525 : DSDB_CONTROL_CURRENT_PARTITION_OID,
6526 : false, NULL);
6527 55968 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
6528 :
6529 55968 : return ldb_next_request(ar->module, change_req);
6530 : }
6531 :
6532 866168 : static int replmd_replicated_apply_search_callback(struct ldb_request *req,
6533 : struct ldb_reply *ares)
6534 : {
6535 866168 : struct replmd_replicated_request *ar = talloc_get_type(req->context,
6536 : struct replmd_replicated_request);
6537 : int ret;
6538 :
6539 866168 : if (!ares) {
6540 0 : return ldb_module_done(ar->req, NULL, NULL,
6541 : LDB_ERR_OPERATIONS_ERROR);
6542 : }
6543 866502 : if (ares->error != LDB_SUCCESS &&
6544 340 : ares->error != LDB_ERR_NO_SUCH_OBJECT) {
6545 0 : return ldb_module_done(ar->req, ares->controls,
6546 : ares->response, ares->error);
6547 : }
6548 :
6549 866168 : switch (ares->type) {
6550 79958 : case LDB_REPLY_ENTRY:
6551 79958 : ar->search_msg = talloc_steal(ar, ares->message);
6552 79958 : break;
6553 :
6554 389025 : case LDB_REPLY_REFERRAL:
6555 : /* we ignore referrals */
6556 389025 : break;
6557 :
6558 397185 : case LDB_REPLY_DONE:
6559 79957 : {
6560 : struct replPropertyMetaData1 *md_remote;
6561 : struct replPropertyMetaData1 *md_local;
6562 :
6563 : struct replPropertyMetaDataBlob omd;
6564 : const struct ldb_val *omd_value;
6565 : struct replPropertyMetaDataBlob *rmd;
6566 : struct ldb_message *msg;
6567 : int instanceType;
6568 397185 : ar->objs->objects[ar->index_current].local_parent_dn = NULL;
6569 397185 : ar->objs->objects[ar->index_current].last_known_parent = NULL;
6570 :
6571 : /*
6572 : * This is the ADD case, find the appropriate parent,
6573 : * as this object doesn't exist locally:
6574 : */
6575 397185 : if (ar->search_msg == NULL) {
6576 317227 : ret = replmd_replicated_apply_search_for_parent(ar);
6577 317227 : if (ret != LDB_SUCCESS) {
6578 6530 : return ldb_module_done(ar->req, NULL, NULL, ret);
6579 : }
6580 317227 : talloc_free(ares);
6581 317227 : return LDB_SUCCESS;
6582 : }
6583 :
6584 : /*
6585 : * Otherwise, in the MERGE case, work out if we are
6586 : * attempting a rename, and if so find the parent the
6587 : * newly renamed object wants to belong under (which
6588 : * may not be the parent in it's attached string DN
6589 : */
6590 79958 : rmd = ar->objs->objects[ar->index_current].meta_data;
6591 79958 : ZERO_STRUCT(omd);
6592 79958 : omd.version = 1;
6593 :
6594 : /* find existing meta data */
6595 79958 : omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
6596 79958 : if (omd_value) {
6597 : enum ndr_err_code ndr_err;
6598 79958 : ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
6599 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
6600 79958 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
6601 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
6602 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
6603 : }
6604 :
6605 79958 : if (omd.version != 1) {
6606 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6607 : }
6608 : }
6609 :
6610 79958 : ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
6611 :
6612 79958 : instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
6613 79958 : if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
6614 79601 : && GUID_all_zero(&ar->local_parent_guid)) {
6615 0 : DEBUG(0, ("Refusing to replicate new version of %s "
6616 : "as local object has an all-zero parentGUID attribute, "
6617 : "despite not being an NC root\n",
6618 : ldb_dn_get_linearized(ar->search_msg->dn)));
6619 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
6620 : }
6621 :
6622 : /*
6623 : * now we need to check for double renames. We could have a
6624 : * local rename pending which our replication partner hasn't
6625 : * received yet. We choose which one wins by looking at the
6626 : * attribute stamps on the two objects, the newer one wins.
6627 : *
6628 : * This also simply applies the correct algorithms for
6629 : * determining if a change was made to name at all, or
6630 : * if the object has just been renamed under the same
6631 : * parent.
6632 : */
6633 79958 : md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
6634 79958 : md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
6635 79958 : if (!md_local) {
6636 0 : DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
6637 : ldb_dn_get_linearized(ar->search_msg->dn)));
6638 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
6639 : }
6640 :
6641 : /*
6642 : * if there is no name attribute given then we have to assume the
6643 : * object we've received has the older name
6644 : */
6645 79958 : if (replmd_replPropertyMetaData1_new_should_be_taken(
6646 79958 : ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
6647 : md_local, md_remote)) {
6648 : struct GUID_txt_buf p_guid_local;
6649 : struct GUID_txt_buf p_guid_remote;
6650 54955 : msg = ar->objs->objects[ar->index_current].msg;
6651 :
6652 : /* Merge on the existing object, with rename */
6653 :
6654 54955 : DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
6655 : "as incoming object changing to %s under %s\n",
6656 : ldb_dn_get_linearized(ar->search_msg->dn),
6657 : GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6658 : ldb_dn_get_linearized(msg->dn),
6659 : GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6660 : &p_guid_remote)));
6661 54955 : ret = replmd_replicated_apply_search_for_parent(ar);
6662 : } else {
6663 : struct GUID_txt_buf p_guid_local;
6664 : struct GUID_txt_buf p_guid_remote;
6665 25003 : msg = ar->objs->objects[ar->index_current].msg;
6666 :
6667 : /*
6668 : * Merge on the existing object, force no
6669 : * rename (code below just to explain why in
6670 : * the DEBUG() logs)
6671 : */
6672 :
6673 25003 : if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
6674 : ldb_dn_get_linearized(msg->dn)) == 0) {
6675 49741 : if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
6676 24769 : GUID_equal(&ar->local_parent_guid,
6677 24769 : ar->objs->objects[ar->index_current].parent_guid)
6678 24769 : == false) {
6679 0 : DEBUG(4,(__location__ ": Keeping object %s at under %s "
6680 : "despite incoming object changing parent to %s\n",
6681 : ldb_dn_get_linearized(ar->search_msg->dn),
6682 : GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6683 : GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6684 : &p_guid_remote)));
6685 : }
6686 : } else {
6687 31 : DEBUG(4,(__location__ ": Keeping object %s at under %s "
6688 : " and rejecting older rename to %s under %s\n",
6689 : ldb_dn_get_linearized(ar->search_msg->dn),
6690 : GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
6691 : ldb_dn_get_linearized(msg->dn),
6692 : GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
6693 : &p_guid_remote)));
6694 : }
6695 : /*
6696 : * This assignment ensures that the strcmp()
6697 : * and GUID_equal() calls in
6698 : * replmd_replicated_apply_merge() avoids the
6699 : * rename call
6700 : */
6701 50006 : ar->objs->objects[ar->index_current].parent_guid =
6702 25003 : &ar->local_parent_guid;
6703 :
6704 25003 : msg->dn = ar->search_msg->dn;
6705 25003 : ret = replmd_replicated_apply_merge(ar);
6706 : }
6707 79958 : if (ret != LDB_SUCCESS) {
6708 1 : return ldb_module_done(ar->req, NULL, NULL, ret);
6709 : }
6710 : }
6711 : }
6712 :
6713 548940 : talloc_free(ares);
6714 548940 : return LDB_SUCCESS;
6715 : }
6716 :
6717 : /**
6718 : * Returns true if we can group together processing this link attribute,
6719 : * i.e. it has the same source-object and attribute ID as other links
6720 : * already in the group
6721 : */
6722 9295 : static bool la_entry_matches_group(struct la_entry *la_entry,
6723 : struct la_group *la_group)
6724 : {
6725 9295 : struct la_entry *prev = la_group->la_entries;
6726 :
6727 17526 : return (la_entry->la->attid == prev->la->attid &&
6728 8231 : GUID_equal(&la_entry->la->identifier->guid,
6729 8231 : &prev->la->identifier->guid));
6730 : }
6731 :
6732 : /**
6733 : * Creates a new la_entry to store replication info for a single
6734 : * linked attribute.
6735 : */
6736 : static struct la_entry *
6737 10030 : create_la_entry(struct replmd_private *replmd_private,
6738 : struct drsuapi_DsReplicaLinkedAttribute *la,
6739 : uint32_t dsdb_repl_flags)
6740 : {
6741 : struct la_entry *la_entry;
6742 :
6743 10030 : if (replmd_private->la_ctx == NULL) {
6744 339 : replmd_private->la_ctx = talloc_new(replmd_private);
6745 : }
6746 10030 : la_entry = talloc(replmd_private->la_ctx, struct la_entry);
6747 10030 : if (la_entry == NULL) {
6748 0 : return NULL;
6749 : }
6750 10030 : la_entry->la = talloc(la_entry,
6751 : struct drsuapi_DsReplicaLinkedAttribute);
6752 10030 : if (la_entry->la == NULL) {
6753 0 : talloc_free(la_entry);
6754 0 : return NULL;
6755 : }
6756 10030 : *la_entry->la = *la;
6757 10030 : la_entry->dsdb_repl_flags = dsdb_repl_flags;
6758 :
6759 : /*
6760 : * we need to steal the non-scalars so they stay
6761 : * around until the end of the transaction
6762 : */
6763 10030 : talloc_steal(la_entry->la, la_entry->la->identifier);
6764 10030 : talloc_steal(la_entry->la, la_entry->la->value.blob);
6765 :
6766 10030 : return la_entry;
6767 : }
6768 :
6769 : /**
6770 : * Stores the linked attributes received in the replication chunk - these get
6771 : * applied at the end of the transaction. We also check that each linked
6772 : * attribute is valid, i.e. source and target objects are known.
6773 : */
6774 3519 : static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
6775 : {
6776 3519 : int ret = LDB_SUCCESS;
6777 : uint32_t i;
6778 3519 : struct ldb_module *module = ar->module;
6779 3487 : struct replmd_private *replmd_private =
6780 3519 : talloc_get_type(ldb_module_get_private(module), struct replmd_private);
6781 3519 : struct la_group *la_group = NULL;
6782 : struct ldb_context *ldb;
6783 3519 : TALLOC_CTX *tmp_ctx = NULL;
6784 3519 : struct ldb_message *src_msg = NULL;
6785 3519 : const struct dsdb_attribute *attr = NULL;
6786 :
6787 3519 : ldb = ldb_module_get_ctx(module);
6788 :
6789 3519 : DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
6790 :
6791 : /* save away the linked attributes for the end of the transaction */
6792 13515 : for (i = 0; i < ar->objs->linked_attributes_count; i++) {
6793 : struct la_entry *la_entry;
6794 : bool new_srcobj;
6795 :
6796 : /* create an entry to store the received link attribute info */
6797 20008 : la_entry = create_la_entry(replmd_private,
6798 10030 : &ar->objs->linked_attributes[i],
6799 10030 : ar->objs->dsdb_repl_flags);
6800 10030 : if (la_entry == NULL) {
6801 0 : ldb_oom(ldb);
6802 0 : return LDB_ERR_OPERATIONS_ERROR;
6803 : }
6804 :
6805 : /*
6806 : * check if we're still dealing with the same source object
6807 : * as the last link
6808 : */
6809 19325 : new_srcobj = (la_group == NULL ||
6810 9295 : !la_entry_matches_group(la_entry, la_group));
6811 :
6812 10030 : if (new_srcobj) {
6813 :
6814 : /* get a new mem_ctx to lookup the source object */
6815 3915 : TALLOC_FREE(tmp_ctx);
6816 3915 : tmp_ctx = talloc_new(ar);
6817 3915 : if (tmp_ctx == NULL) {
6818 0 : ldb_oom(ldb);
6819 0 : return LDB_ERR_OPERATIONS_ERROR;
6820 : }
6821 :
6822 : /* verify the link source exists */
6823 3915 : ret = replmd_get_la_entry_source(module, la_entry,
6824 : tmp_ctx, &attr,
6825 : &src_msg);
6826 :
6827 : /*
6828 : * When we fail to find the source object, the error
6829 : * code we pass back here is really important. It flags
6830 : * back to the callers to retry this request with
6831 : * DRSUAPI_DRS_GET_ANC. This case should never happen
6832 : * if we're replicating from a Samba DC, but it is
6833 : * needed to talk to a Windows DC
6834 : */
6835 3915 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
6836 0 : WERROR err = WERR_DS_DRA_MISSING_PARENT;
6837 0 : ret = replmd_replicated_request_werror(ar,
6838 : err);
6839 0 : break;
6840 : }
6841 : }
6842 :
6843 20008 : ret = replmd_verify_link_target(ar, tmp_ctx, la_entry,
6844 10030 : src_msg->dn, attr);
6845 10030 : if (ret != LDB_SUCCESS) {
6846 34 : break;
6847 : }
6848 :
6849 : /* group the links together by source-object for efficiency */
6850 9996 : if (new_srcobj) {
6851 3884 : la_group = talloc_zero(replmd_private->la_ctx,
6852 : struct la_group);
6853 3884 : if (la_group == NULL) {
6854 0 : ldb_oom(ldb);
6855 0 : return LDB_ERR_OPERATIONS_ERROR;
6856 : }
6857 3884 : DLIST_ADD(replmd_private->la_list, la_group);
6858 : }
6859 9996 : DLIST_ADD(la_group->la_entries, la_entry);
6860 9996 : replmd_private->total_links++;
6861 : }
6862 :
6863 3519 : TALLOC_FREE(tmp_ctx);
6864 3519 : return ret;
6865 : }
6866 :
6867 : static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
6868 :
6869 400704 : static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
6870 : {
6871 : struct ldb_context *ldb;
6872 : int ret;
6873 : char *tmp_str;
6874 : char *filter;
6875 : struct ldb_request *search_req;
6876 : static const char *attrs[] = { "repsFrom", "replUpToDateVector",
6877 : "parentGUID", "instanceType",
6878 : "replPropertyMetaData", "nTSecurityDescriptor",
6879 : "isDeleted", NULL };
6880 : struct GUID_txt_buf guid_str_buf;
6881 :
6882 400704 : if (ar->index_current >= ar->objs->num_objects) {
6883 :
6884 : /*
6885 : * Now that we've applied all the objects, check the new linked
6886 : * attributes and store them (we apply them in .prepare_commit)
6887 : */
6888 3519 : ret = replmd_store_linked_attributes(ar);
6889 :
6890 3519 : if (ret != LDB_SUCCESS) {
6891 34 : return ret;
6892 : }
6893 :
6894 : /* done applying objects, move on to the next stage */
6895 3485 : return replmd_replicated_uptodate_vector(ar);
6896 : }
6897 :
6898 397185 : ldb = ldb_module_get_ctx(ar->module);
6899 397185 : ar->search_msg = NULL;
6900 397185 : ar->isDeleted = false;
6901 :
6902 397185 : tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
6903 : &guid_str_buf);
6904 :
6905 397185 : filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
6906 397185 : if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
6907 :
6908 787840 : ret = ldb_build_search_req(&search_req,
6909 : ldb,
6910 : ar,
6911 397185 : ar->objs->partition_dn,
6912 : LDB_SCOPE_SUBTREE,
6913 : filter,
6914 : attrs,
6915 : NULL,
6916 : ar,
6917 : replmd_replicated_apply_search_callback,
6918 : ar->req);
6919 397185 : LDB_REQ_SET_LOCATION(search_req);
6920 :
6921 : /*
6922 : * We set DSDB_SEARCH_SHOW_EXTENDED_DN to get the GUID on the
6923 : * DN. This in turn helps our operational module find the
6924 : * record by GUID, not DN lookup which is more error prone if
6925 : * DN indexing changes. We prefer to keep chasing GUIDs
6926 : * around if possible, even within a transaction.
6927 : *
6928 : * The aim here is to keep replication moving and allow a
6929 : * reindex later.
6930 : */
6931 397185 : ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED
6932 : |DSDB_SEARCH_SHOW_EXTENDED_DN);
6933 :
6934 397185 : if (ret != LDB_SUCCESS) {
6935 0 : return ret;
6936 : }
6937 :
6938 397185 : return ldb_next_request(ar->module, search_req);
6939 : }
6940 :
6941 : /*
6942 : * Returns true if we need to do extra processing to handle deleted object
6943 : * changes received via replication
6944 : */
6945 397155 : static bool replmd_should_apply_isDeleted(struct replmd_replicated_request *ar,
6946 : struct ldb_message *msg)
6947 : {
6948 : struct ldb_dn *deleted_objects_dn;
6949 : int ret;
6950 :
6951 397155 : if (!ar->isDeleted) {
6952 :
6953 : /* not a deleted object, so don't set isDeleted */
6954 311198 : return false;
6955 : }
6956 :
6957 85957 : ret = dsdb_get_deleted_objects_dn(ldb_module_get_ctx(ar->module),
6958 : msg, msg->dn,
6959 : &deleted_objects_dn);
6960 :
6961 : /*
6962 : * if the Deleted Object container lookup failed, then just apply
6963 : * isDeleted (note that it doesn't exist for the Schema partition)
6964 : */
6965 85957 : if (ret != LDB_SUCCESS) {
6966 0 : return true;
6967 : }
6968 :
6969 : /*
6970 : * the Deleted Objects container has isDeleted set but is not entirely
6971 : * a deleted object, so DON'T re-apply isDeleted to it
6972 : */
6973 85957 : if (ldb_dn_compare(msg->dn, deleted_objects_dn) == 0) {
6974 380 : return false;
6975 : }
6976 :
6977 85577 : return true;
6978 : }
6979 :
6980 : /*
6981 : * This is essentially a wrapper for replmd_replicated_apply_next()
6982 : *
6983 : * This is needed to ensure that both codepaths call this handler.
6984 : */
6985 397155 : static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar)
6986 : {
6987 397155 : struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
6988 : int ret;
6989 : bool apply_isDeleted;
6990 397155 : struct ldb_request *del_req = NULL;
6991 397155 : struct ldb_result *res = NULL;
6992 397155 : TALLOC_CTX *tmp_ctx = NULL;
6993 :
6994 397155 : apply_isDeleted = replmd_should_apply_isDeleted(ar, msg);
6995 :
6996 397155 : if (!apply_isDeleted) {
6997 :
6998 : /* nothing to do */
6999 311578 : ar->index_current++;
7000 311578 : return replmd_replicated_apply_next(ar);
7001 : }
7002 :
7003 : /*
7004 : * Do a delete here again, so that if there is
7005 : * anything local that conflicts with this
7006 : * object being deleted, it is removed. This
7007 : * includes links. See MS-DRSR 4.1.10.6.9
7008 : * UpdateObject.
7009 : *
7010 : * If the object is already deleted, and there
7011 : * is no more work required, it doesn't do
7012 : * anything.
7013 : */
7014 :
7015 : /* This has been updated to point to the DN we eventually did the modify on */
7016 :
7017 85577 : tmp_ctx = talloc_new(ar);
7018 85577 : if (!tmp_ctx) {
7019 0 : ret = ldb_oom(ldb_module_get_ctx(ar->module));
7020 0 : return ret;
7021 : }
7022 :
7023 85577 : res = talloc_zero(tmp_ctx, struct ldb_result);
7024 85577 : if (!res) {
7025 0 : ret = ldb_oom(ldb_module_get_ctx(ar->module));
7026 0 : talloc_free(tmp_ctx);
7027 0 : return ret;
7028 : }
7029 :
7030 : /* Build a delete request, which hopefully will artually turn into nothing */
7031 85577 : ret = ldb_build_del_req(&del_req, ldb_module_get_ctx(ar->module), tmp_ctx,
7032 : msg->dn,
7033 : NULL,
7034 : res,
7035 : ldb_modify_default_callback,
7036 : ar->req);
7037 85577 : LDB_REQ_SET_LOCATION(del_req);
7038 85577 : if (ret != LDB_SUCCESS) {
7039 0 : talloc_free(tmp_ctx);
7040 0 : return ret;
7041 : }
7042 :
7043 : /*
7044 : * This is the guts of the call, call back
7045 : * into our delete code, but setting the
7046 : * re_delete flag so we delete anything that
7047 : * shouldn't be there on a deleted or recycled
7048 : * object
7049 : */
7050 85577 : ret = replmd_delete_internals(ar->module, del_req, true);
7051 85577 : if (ret == LDB_SUCCESS) {
7052 85577 : ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
7053 : }
7054 :
7055 85577 : talloc_free(tmp_ctx);
7056 85577 : if (ret != LDB_SUCCESS) {
7057 0 : return ret;
7058 : }
7059 :
7060 85577 : ar->index_current++;
7061 85577 : return replmd_replicated_apply_next(ar);
7062 : }
7063 :
7064 3485 : static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
7065 : struct ldb_reply *ares)
7066 : {
7067 : struct ldb_context *ldb;
7068 3485 : struct replmd_replicated_request *ar = talloc_get_type(req->context,
7069 : struct replmd_replicated_request);
7070 3485 : ldb = ldb_module_get_ctx(ar->module);
7071 :
7072 3485 : if (!ares) {
7073 0 : return ldb_module_done(ar->req, NULL, NULL,
7074 : LDB_ERR_OPERATIONS_ERROR);
7075 : }
7076 3485 : if (ares->error != LDB_SUCCESS) {
7077 0 : return ldb_module_done(ar->req, ares->controls,
7078 : ares->response, ares->error);
7079 : }
7080 :
7081 3485 : if (ares->type != LDB_REPLY_DONE) {
7082 0 : ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
7083 0 : return ldb_module_done(ar->req, NULL, NULL,
7084 : LDB_ERR_OPERATIONS_ERROR);
7085 : }
7086 :
7087 3485 : talloc_free(ares);
7088 :
7089 3485 : return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7090 : }
7091 :
7092 3485 : static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
7093 : {
7094 : struct ldb_context *ldb;
7095 : struct ldb_request *change_req;
7096 : enum ndr_err_code ndr_err;
7097 : struct ldb_message *msg;
7098 : struct replUpToDateVectorBlob ouv;
7099 : const struct ldb_val *ouv_value;
7100 : const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
7101 : struct replUpToDateVectorBlob nuv;
7102 : struct ldb_val nuv_value;
7103 3485 : struct ldb_message_element *nuv_el = NULL;
7104 3485 : struct ldb_message_element *orf_el = NULL;
7105 : struct repsFromToBlob nrf;
7106 3485 : struct ldb_val *nrf_value = NULL;
7107 3485 : struct ldb_message_element *nrf_el = NULL;
7108 : unsigned int i;
7109 3485 : uint32_t j,ni=0;
7110 3485 : bool found = false;
7111 3485 : time_t t = time(NULL);
7112 : NTTIME now;
7113 : int ret;
7114 : uint32_t instanceType;
7115 :
7116 3485 : ldb = ldb_module_get_ctx(ar->module);
7117 3485 : ruv = ar->objs->uptodateness_vector;
7118 3485 : ZERO_STRUCT(ouv);
7119 3485 : ouv.version = 2;
7120 3485 : ZERO_STRUCT(nuv);
7121 3485 : nuv.version = 2;
7122 :
7123 3485 : unix_to_nt_time(&now, t);
7124 :
7125 3485 : if (ar->search_msg == NULL) {
7126 : /* this happens for a REPL_OBJ call where we are
7127 : creating the target object by replicating it. The
7128 : subdomain join code does this for the partition DN
7129 : */
7130 0 : DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
7131 0 : return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7132 : }
7133 :
7134 3485 : instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
7135 3485 : if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
7136 0 : DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
7137 : ldb_dn_get_linearized(ar->search_msg->dn)));
7138 0 : return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
7139 : }
7140 :
7141 : /*
7142 : * first create the new replUpToDateVector
7143 : */
7144 3485 : ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
7145 3485 : if (ouv_value) {
7146 3127 : ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
7147 : (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
7148 3127 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7149 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7150 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7151 : }
7152 :
7153 3127 : if (ouv.version != 2) {
7154 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7155 : }
7156 : }
7157 :
7158 : /*
7159 : * the new uptodateness vector will at least
7160 : * contain 1 entry, one for the source_dsa
7161 : *
7162 : * plus optional values from our old vector and the one from the source_dsa
7163 : */
7164 3485 : nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
7165 3485 : if (ruv) nuv.ctr.ctr2.count += ruv->count;
7166 3485 : nuv.ctr.ctr2.cursors = talloc_array(ar,
7167 : struct drsuapi_DsReplicaCursor2,
7168 : nuv.ctr.ctr2.count);
7169 3485 : if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7170 :
7171 : /* first copy the old vector */
7172 6413 : for (i=0; i < ouv.ctr.ctr2.count; i++) {
7173 2928 : nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
7174 2928 : ni++;
7175 : }
7176 :
7177 : /* merge in the source_dsa vector is available */
7178 7698 : for (i=0; (ruv && i < ruv->count); i++) {
7179 4213 : found = false;
7180 :
7181 4213 : if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7182 4213 : &ar->our_invocation_id)) {
7183 1292 : continue;
7184 : }
7185 :
7186 7014 : for (j=0; j < ni; j++) {
7187 3021 : if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
7188 3021 : &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
7189 589 : continue;
7190 : }
7191 :
7192 2432 : found = true;
7193 :
7194 2432 : if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
7195 1959 : nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
7196 : }
7197 2432 : break;
7198 : }
7199 :
7200 2921 : if (found) continue;
7201 :
7202 : /* if it's not there yet, add it */
7203 489 : nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
7204 489 : ni++;
7205 : }
7206 :
7207 : /*
7208 : * finally correct the size of the cursors array
7209 : */
7210 3485 : nuv.ctr.ctr2.count = ni;
7211 :
7212 : /*
7213 : * sort the cursors
7214 : */
7215 3485 : TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
7216 :
7217 : /*
7218 : * create the change ldb_message
7219 : */
7220 3485 : msg = ldb_msg_new(ar);
7221 3485 : if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7222 3485 : msg->dn = ar->search_msg->dn;
7223 :
7224 3485 : ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
7225 : (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
7226 3485 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7227 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7228 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7229 : }
7230 3485 : ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
7231 3485 : if (ret != LDB_SUCCESS) {
7232 0 : return replmd_replicated_request_error(ar, ret);
7233 : }
7234 3485 : nuv_el->flags = LDB_FLAG_MOD_REPLACE;
7235 :
7236 : /*
7237 : * now create the new repsFrom value from the given repsFromTo1 structure
7238 : */
7239 3485 : ZERO_STRUCT(nrf);
7240 3485 : nrf.version = 1;
7241 3485 : nrf.ctr.ctr1 = *ar->objs->source_dsa;
7242 3485 : nrf.ctr.ctr1.last_attempt = now;
7243 3485 : nrf.ctr.ctr1.last_success = now;
7244 3485 : nrf.ctr.ctr1.result_last_attempt = WERR_OK;
7245 :
7246 : /*
7247 : * first see if we already have a repsFrom value for the current source dsa
7248 : * if so we'll later replace this value
7249 : */
7250 3485 : orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
7251 3485 : if (orf_el) {
7252 6318 : for (i=0; i < orf_el->num_values; i++) {
7253 : struct repsFromToBlob *trf;
7254 :
7255 3165 : trf = talloc(ar, struct repsFromToBlob);
7256 3165 : if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7257 :
7258 3165 : ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
7259 : (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
7260 3165 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7261 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7262 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7263 : }
7264 :
7265 3165 : if (trf->version != 1) {
7266 0 : return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
7267 : }
7268 :
7269 : /*
7270 : * we compare the source dsa objectGUID not the invocation_id
7271 : * because we want only one repsFrom value per source dsa
7272 : * and when the invocation_id of the source dsa has changed we don't need
7273 : * the old repsFrom with the old invocation_id
7274 : */
7275 3165 : if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
7276 3165 : &ar->objs->source_dsa->source_dsa_obj_guid)) {
7277 29 : talloc_free(trf);
7278 29 : continue;
7279 : }
7280 :
7281 3136 : talloc_free(trf);
7282 3136 : nrf_value = &orf_el->values[i];
7283 3136 : break;
7284 : }
7285 :
7286 : /*
7287 : * copy over all old values to the new ldb_message
7288 : */
7289 3143 : ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
7290 3143 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7291 3143 : *nrf_el = *orf_el;
7292 : }
7293 :
7294 : /*
7295 : * if we haven't found an old repsFrom value for the current source dsa
7296 : * we'll add a new value
7297 : */
7298 3485 : if (!nrf_value) {
7299 : struct ldb_val zero_value;
7300 349 : ZERO_STRUCT(zero_value);
7301 349 : ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
7302 349 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7303 :
7304 349 : nrf_value = &nrf_el->values[nrf_el->num_values - 1];
7305 : }
7306 :
7307 : /* we now fill the value which is already attached to ldb_message */
7308 3485 : ndr_err = ndr_push_struct_blob(nrf_value, msg,
7309 : &nrf,
7310 : (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
7311 3485 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
7312 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
7313 0 : return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
7314 : }
7315 :
7316 : /*
7317 : * the ldb_message_element for the attribute, has all the old values and the new one
7318 : * so we'll replace the whole attribute with all values
7319 : */
7320 3485 : nrf_el->flags = LDB_FLAG_MOD_REPLACE;
7321 :
7322 3485 : if (CHECK_DEBUGLVL(4)) {
7323 0 : char *s = ldb_ldif_message_redacted_string(ldb, ar,
7324 : LDB_CHANGETYPE_MODIFY,
7325 : msg);
7326 0 : DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
7327 0 : talloc_free(s);
7328 : }
7329 :
7330 : /* prepare the ldb_modify() request */
7331 3485 : ret = ldb_build_mod_req(&change_req,
7332 : ldb,
7333 : ar,
7334 : msg,
7335 : ar->controls,
7336 : ar,
7337 : replmd_replicated_uptodate_modify_callback,
7338 : ar->req);
7339 3485 : LDB_REQ_SET_LOCATION(change_req);
7340 3485 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7341 :
7342 3485 : return ldb_next_request(ar->module, change_req);
7343 : }
7344 :
7345 6970 : static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
7346 : struct ldb_reply *ares)
7347 : {
7348 6970 : struct replmd_replicated_request *ar = talloc_get_type(req->context,
7349 : struct replmd_replicated_request);
7350 : int ret;
7351 :
7352 6970 : if (!ares) {
7353 0 : return ldb_module_done(ar->req, NULL, NULL,
7354 : LDB_ERR_OPERATIONS_ERROR);
7355 : }
7356 6970 : if (ares->error != LDB_SUCCESS &&
7357 0 : ares->error != LDB_ERR_NO_SUCH_OBJECT) {
7358 0 : return ldb_module_done(ar->req, ares->controls,
7359 : ares->response, ares->error);
7360 : }
7361 :
7362 6970 : switch (ares->type) {
7363 3485 : case LDB_REPLY_ENTRY:
7364 3485 : ar->search_msg = talloc_steal(ar, ares->message);
7365 3485 : break;
7366 :
7367 0 : case LDB_REPLY_REFERRAL:
7368 : /* we ignore referrals */
7369 0 : break;
7370 :
7371 3485 : case LDB_REPLY_DONE:
7372 3485 : ret = replmd_replicated_uptodate_modify(ar);
7373 3485 : if (ret != LDB_SUCCESS) {
7374 0 : return ldb_module_done(ar->req, NULL, NULL, ret);
7375 : }
7376 : }
7377 :
7378 6970 : talloc_free(ares);
7379 6970 : return LDB_SUCCESS;
7380 : }
7381 :
7382 :
7383 3485 : static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
7384 : {
7385 3485 : struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
7386 3453 : struct replmd_private *replmd_private =
7387 3485 : talloc_get_type_abort(ldb_module_get_private(ar->module),
7388 : struct replmd_private);
7389 : int ret;
7390 : static const char *attrs[] = {
7391 : "replUpToDateVector",
7392 : "repsFrom",
7393 : "instanceType",
7394 : NULL
7395 : };
7396 : struct ldb_request *search_req;
7397 :
7398 3485 : ar->search_msg = NULL;
7399 :
7400 : /*
7401 : * Let the caller know that we did an originating updates
7402 : */
7403 3485 : ar->objs->originating_updates = replmd_private->originating_updates;
7404 :
7405 6938 : ret = ldb_build_search_req(&search_req,
7406 : ldb,
7407 : ar,
7408 3485 : ar->objs->partition_dn,
7409 : LDB_SCOPE_BASE,
7410 : "(objectClass=*)",
7411 : attrs,
7412 : NULL,
7413 : ar,
7414 : replmd_replicated_uptodate_search_callback,
7415 : ar->req);
7416 3485 : LDB_REQ_SET_LOCATION(search_req);
7417 3485 : if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
7418 :
7419 3485 : return ldb_next_request(ar->module, search_req);
7420 : }
7421 :
7422 :
7423 :
7424 3549 : static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
7425 : {
7426 : struct ldb_context *ldb;
7427 : struct dsdb_extended_replicated_objects *objs;
7428 : struct replmd_replicated_request *ar;
7429 : struct ldb_control **ctrls;
7430 : int ret;
7431 :
7432 3549 : ldb = ldb_module_get_ctx(module);
7433 :
7434 3549 : ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
7435 :
7436 3549 : objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
7437 3549 : if (!objs) {
7438 0 : ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
7439 0 : return LDB_ERR_PROTOCOL_ERROR;
7440 : }
7441 :
7442 3549 : if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
7443 0 : ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
7444 : objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
7445 0 : return LDB_ERR_PROTOCOL_ERROR;
7446 : }
7447 :
7448 3549 : ar = replmd_ctx_init(module, req);
7449 3549 : if (!ar)
7450 0 : return LDB_ERR_OPERATIONS_ERROR;
7451 :
7452 : /* Set the flags to have the replmd_op_callback run over the full set of objects */
7453 3549 : ar->apply_mode = true;
7454 3549 : ar->objs = objs;
7455 3549 : ar->schema = dsdb_get_schema(ldb, ar);
7456 3549 : if (!ar->schema) {
7457 0 : ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
7458 0 : talloc_free(ar);
7459 0 : DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
7460 0 : return LDB_ERR_CONSTRAINT_VIOLATION;
7461 : }
7462 :
7463 3549 : ctrls = req->controls;
7464 :
7465 3549 : if (req->controls) {
7466 3549 : req->controls = talloc_memdup(ar, req->controls,
7467 : talloc_get_size(req->controls));
7468 3549 : if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
7469 : }
7470 :
7471 3549 : ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
7472 3549 : if (ret != LDB_SUCCESS) {
7473 0 : return ret;
7474 : }
7475 :
7476 : /* If this change contained linked attributes in the body
7477 : * (rather than in the links section) we need to update
7478 : * backlinks in linked_attributes */
7479 3549 : ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
7480 3549 : if (ret != LDB_SUCCESS) {
7481 0 : return ret;
7482 : }
7483 :
7484 3549 : ar->controls = req->controls;
7485 3549 : req->controls = ctrls;
7486 :
7487 3549 : return replmd_replicated_apply_next(ar);
7488 : }
7489 :
7490 : /**
7491 : * Checks how to handle an missing target - either we need to fail the
7492 : * replication and retry with GET_TGT, ignore the link and continue, or try to
7493 : * add a partial link to an unknown target.
7494 : */
7495 626 : static int replmd_allow_missing_target(struct ldb_module *module,
7496 : TALLOC_CTX *mem_ctx,
7497 : struct ldb_dn *target_dn,
7498 : struct ldb_dn *source_dn,
7499 : bool is_obj_commit,
7500 : struct GUID *guid,
7501 : uint32_t dsdb_repl_flags,
7502 : bool *ignore_link,
7503 : const char * missing_str)
7504 : {
7505 626 : struct ldb_context *ldb = ldb_module_get_ctx(module);
7506 : bool is_in_same_nc;
7507 :
7508 : /*
7509 : * we may not be able to resolve link targets properly when
7510 : * dealing with subsets of objects, e.g. the source is a
7511 : * critical object and the target isn't
7512 : *
7513 : * TODO:
7514 : * When we implement Trusted Domains we need to consider
7515 : * whether they get treated as an incomplete replica here or not
7516 : */
7517 626 : if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
7518 :
7519 : /*
7520 : * Ignore the link. We don't increase the highwater-mark in
7521 : * the object subset cases, so subsequent replications should
7522 : * resolve any missing links
7523 : */
7524 32 : DEBUG(2, ("%s target %s linked from %s\n", missing_str,
7525 : ldb_dn_get_linearized(target_dn),
7526 : ldb_dn_get_linearized(source_dn)));
7527 32 : *ignore_link = true;
7528 32 : return LDB_SUCCESS;
7529 : }
7530 :
7531 594 : is_in_same_nc = dsdb_objects_have_same_nc(ldb,
7532 : mem_ctx,
7533 : source_dn,
7534 : target_dn);
7535 594 : if (is_in_same_nc) {
7536 : /*
7537 : * We allow the join.py code to point out that all
7538 : * replication is completed, so failing now would just
7539 : * trigger errors, rather than trigger a GET_TGT
7540 : */
7541 171 : int *finished_full_join_ptr =
7542 171 : talloc_get_type(ldb_get_opaque(ldb,
7543 : DSDB_FULL_JOIN_REPLICATION_COMPLETED_OPAQUE_NAME),
7544 : int);
7545 171 : bool finished_full_join = finished_full_join_ptr && *finished_full_join_ptr;
7546 :
7547 : /*
7548 : * if the target is already be up-to-date there's no point in
7549 : * retrying. This could be due to bad timing, or if a target
7550 : * on a one-way link was deleted. We ignore the link rather
7551 : * than failing the replication cycle completely
7552 : */
7553 171 : if (finished_full_join
7554 42 : || dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
7555 136 : *ignore_link = true;
7556 136 : DBG_WARNING("%s is %s "
7557 : "but up to date. Ignoring link from %s\n",
7558 : ldb_dn_get_linearized(target_dn), missing_str,
7559 : ldb_dn_get_linearized(source_dn));
7560 136 : return LDB_SUCCESS;
7561 : }
7562 :
7563 : /* otherwise fail the replication and retry with GET_TGT */
7564 35 : ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
7565 : missing_str,
7566 : ldb_dn_get_linearized(target_dn),
7567 : GUID_string(mem_ctx, guid),
7568 : ldb_dn_get_linearized(source_dn));
7569 35 : return LDB_ERR_NO_SUCH_OBJECT;
7570 : }
7571 :
7572 : /*
7573 : * The target of the cross-partition link is missing. Continue
7574 : * and try to at least add the forward-link. This isn't great,
7575 : * but a partial link can be fixed by dbcheck, so it's better
7576 : * than dropping the link completely.
7577 : */
7578 423 : *ignore_link = false;
7579 :
7580 423 : if (is_obj_commit) {
7581 :
7582 : /*
7583 : * Only log this when we're actually committing the objects.
7584 : * This avoids spurious logs, i.e. if we're just verifying the
7585 : * received link during a join.
7586 : */
7587 36 : DBG_WARNING("%s cross-partition target %s linked from %s\n",
7588 : missing_str, ldb_dn_get_linearized(target_dn),
7589 : ldb_dn_get_linearized(source_dn));
7590 : }
7591 :
7592 423 : return LDB_SUCCESS;
7593 : }
7594 :
7595 : /**
7596 : * Checks that the target object for a linked attribute exists.
7597 : * @param guid returns the target object's GUID (is returned)if it exists)
7598 : * @param ignore_link set to true if the linked attribute should be ignored
7599 : * (i.e. the target doesn't exist, but that it's OK to skip the link)
7600 : */
7601 16033 : static int replmd_check_target_exists(struct ldb_module *module,
7602 : struct dsdb_dn *dsdb_dn,
7603 : struct la_entry *la_entry,
7604 : struct ldb_dn *source_dn,
7605 : bool is_obj_commit,
7606 : struct GUID *guid,
7607 : bool *ignore_link)
7608 : {
7609 16033 : struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7610 16033 : struct ldb_context *ldb = ldb_module_get_ctx(module);
7611 : struct ldb_result *target_res;
7612 16033 : TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
7613 16033 : const char *attrs[] = { "isDeleted", "isRecycled", NULL };
7614 : NTSTATUS ntstatus;
7615 : int ret;
7616 16033 : enum deletion_state target_deletion_state = OBJECT_REMOVED;
7617 16033 : bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
7618 :
7619 16033 : *ignore_link = false;
7620 16033 : ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
7621 :
7622 16033 : if (!NT_STATUS_IS_OK(ntstatus) && !active) {
7623 :
7624 : /*
7625 : * This strange behaviour (allowing a NULL/missing
7626 : * GUID) originally comes from:
7627 : *
7628 : * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
7629 : * Author: Andrew Tridgell <tridge@samba.org>
7630 : * Date: Mon Dec 21 21:21:55 2009 +1100
7631 : *
7632 : * s4-drs: cope better with NULL GUIDS from DRS
7633 : *
7634 : * It is valid to get a NULL GUID over DRS for a deleted forward link. We
7635 : * need to match by DN if possible when seeing if we should update an
7636 : * existing link.
7637 : *
7638 : * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
7639 : */
7640 0 : ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
7641 : dsdb_dn->dn, attrs,
7642 : DSDB_FLAG_NEXT_MODULE |
7643 : DSDB_SEARCH_SHOW_RECYCLED |
7644 : DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7645 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7646 : NULL);
7647 16033 : } else if (!NT_STATUS_IS_OK(ntstatus)) {
7648 0 : ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
7649 0 : la->attid,
7650 : ldb_dn_get_linearized(dsdb_dn->dn),
7651 : ldb_dn_get_linearized(source_dn));
7652 0 : talloc_free(tmp_ctx);
7653 0 : return LDB_ERR_OPERATIONS_ERROR;
7654 : } else {
7655 16033 : ret = dsdb_module_search(module, tmp_ctx, &target_res,
7656 : NULL, LDB_SCOPE_SUBTREE,
7657 : attrs,
7658 : DSDB_FLAG_NEXT_MODULE |
7659 : DSDB_SEARCH_SHOW_RECYCLED |
7660 : DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7661 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
7662 : NULL,
7663 : "objectGUID=%s",
7664 : GUID_string(tmp_ctx, guid));
7665 : }
7666 :
7667 16033 : if (ret != LDB_SUCCESS) {
7668 0 : ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
7669 : GUID_string(tmp_ctx, guid),
7670 : ldb_errstring(ldb));
7671 0 : talloc_free(tmp_ctx);
7672 0 : return ret;
7673 : }
7674 :
7675 16033 : if (target_res->count == 0) {
7676 :
7677 : /*
7678 : * target object is unknown. Check whether to ignore the link,
7679 : * fail the replication, or add a partial link
7680 : */
7681 449 : ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
7682 : source_dn, is_obj_commit, guid,
7683 : la_entry->dsdb_repl_flags,
7684 : ignore_link, "Unknown");
7685 :
7686 15584 : } else if (target_res->count != 1) {
7687 0 : ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
7688 : GUID_string(tmp_ctx, guid));
7689 0 : ret = LDB_ERR_OPERATIONS_ERROR;
7690 : } else {
7691 15584 : struct ldb_message *target_msg = target_res->msgs[0];
7692 :
7693 15584 : dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
7694 :
7695 : /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
7696 15584 : replmd_deletion_state(module, target_msg,
7697 : &target_deletion_state, NULL);
7698 :
7699 : /*
7700 : * Check for deleted objects as per MS-DRSR 4.1.10.6.14
7701 : * ProcessLinkValue(). Link updates should not be sent for
7702 : * recycled and tombstone objects (deleting the links should
7703 : * happen when we delete the object). This probably means our
7704 : * copy of the target object isn't up to date.
7705 : */
7706 15584 : if (target_deletion_state >= OBJECT_RECYCLED) {
7707 :
7708 : /*
7709 : * target object is deleted. Check whether to ignore the
7710 : * link, fail the replication, or add a partial link
7711 : */
7712 177 : ret = replmd_allow_missing_target(module, tmp_ctx,
7713 : dsdb_dn->dn, source_dn,
7714 : is_obj_commit, guid,
7715 : la_entry->dsdb_repl_flags,
7716 : ignore_link, "Deleted");
7717 : }
7718 : }
7719 :
7720 16033 : talloc_free(tmp_ctx);
7721 16033 : return ret;
7722 : }
7723 :
7724 : /**
7725 : * Extracts the key details about the source object for a
7726 : * linked-attribute entry.
7727 : * This returns the following details:
7728 : * @param ret_attr the schema details for the linked attribute
7729 : * @param source_msg the search result for the source object
7730 : */
7731 7760 : static int replmd_get_la_entry_source(struct ldb_module *module,
7732 : struct la_entry *la_entry,
7733 : TALLOC_CTX *mem_ctx,
7734 : const struct dsdb_attribute **ret_attr,
7735 : struct ldb_message **source_msg)
7736 : {
7737 7760 : struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7738 7760 : struct ldb_context *ldb = ldb_module_get_ctx(module);
7739 7760 : const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7740 : int ret;
7741 : const struct dsdb_attribute *attr;
7742 : struct ldb_result *res;
7743 : const char *attrs[4];
7744 :
7745 : /*
7746 : linked_attributes[0]:
7747 : &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
7748 : identifier : *
7749 : identifier: struct drsuapi_DsReplicaObjectIdentifier
7750 : __ndr_size : 0x0000003a (58)
7751 : __ndr_size_sid : 0x00000000 (0)
7752 : guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
7753 : sid : S-0-0
7754 : __ndr_size_dn : 0x00000000 (0)
7755 : dn : ''
7756 : attid : DRSUAPI_ATTID_member (0x1F)
7757 : value: struct drsuapi_DsAttributeValue
7758 : __ndr_size : 0x0000007e (126)
7759 : blob : *
7760 : blob : DATA_BLOB length=126
7761 : flags : 0x00000001 (1)
7762 : 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
7763 : originating_add_time : Wed Sep 2 22:20:01 2009 EST
7764 : meta_data: struct drsuapi_DsReplicaMetaData
7765 : version : 0x00000015 (21)
7766 : originating_change_time : Wed Sep 2 23:39:07 2009 EST
7767 : originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
7768 : originating_usn : 0x000000000001e19c (123292)
7769 :
7770 : (for cases where the link is to a normal DN)
7771 : &target: struct drsuapi_DsReplicaObjectIdentifier3
7772 : __ndr_size : 0x0000007e (126)
7773 : __ndr_size_sid : 0x0000001c (28)
7774 : guid : 7639e594-db75-4086-b0d4-67890ae46031
7775 : sid : S-1-5-21-2848215498-2472035911-1947525656-19924
7776 : __ndr_size_dn : 0x00000022 (34)
7777 : dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
7778 : */
7779 :
7780 : /* find the attribute being modified */
7781 7760 : attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
7782 7760 : if (attr == NULL) {
7783 : struct GUID_txt_buf guid_str;
7784 0 : ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
7785 0 : la->attid,
7786 0 : GUID_buf_string(&la->identifier->guid,
7787 : &guid_str));
7788 0 : return LDB_ERR_OPERATIONS_ERROR;
7789 : }
7790 :
7791 : /*
7792 : * All attributes listed here must be dealt with in some way
7793 : * by replmd_process_linked_attribute() otherwise in the case
7794 : * of isDeleted: FALSE the modify will fail with:
7795 : *
7796 : * Failed to apply linked attribute change 'attribute 'isDeleted':
7797 : * invalid modify flags on
7798 : * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
7799 : * 0x0'
7800 : *
7801 : * This is becaue isDeleted is a Boolean, so FALSE is a
7802 : * legitimate value (set by Samba's deletetest.py)
7803 : */
7804 7760 : attrs[0] = attr->lDAPDisplayName;
7805 7760 : attrs[1] = "isDeleted";
7806 7760 : attrs[2] = "isRecycled";
7807 7760 : attrs[3] = NULL;
7808 :
7809 : /*
7810 : * get the existing message from the db for the object with
7811 : * this GUID, returning attribute being modified. We will then
7812 : * use this msg as the basis for a modify call
7813 : */
7814 7760 : ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
7815 : DSDB_FLAG_NEXT_MODULE |
7816 : DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
7817 : DSDB_SEARCH_SHOW_RECYCLED |
7818 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
7819 : DSDB_SEARCH_REVEAL_INTERNALS,
7820 : NULL,
7821 7760 : "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
7822 7760 : if (ret != LDB_SUCCESS) {
7823 0 : return ret;
7824 : }
7825 7760 : if (res->count != 1) {
7826 0 : ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
7827 0 : GUID_string(mem_ctx, &la->identifier->guid));
7828 0 : return LDB_ERR_NO_SUCH_OBJECT;
7829 : }
7830 :
7831 7760 : *source_msg = res->msgs[0];
7832 7760 : *ret_attr = attr;
7833 :
7834 7760 : return LDB_SUCCESS;
7835 : }
7836 :
7837 : /**
7838 : * Verifies the target object is known for a linked attribute
7839 : */
7840 10030 : static int replmd_verify_link_target(struct replmd_replicated_request *ar,
7841 : TALLOC_CTX *mem_ctx,
7842 : struct la_entry *la_entry,
7843 : struct ldb_dn *src_dn,
7844 : const struct dsdb_attribute *attr)
7845 : {
7846 10030 : int ret = LDB_SUCCESS;
7847 10030 : struct ldb_module *module = ar->module;
7848 10030 : struct dsdb_dn *tgt_dsdb_dn = NULL;
7849 10030 : struct GUID guid = GUID_zero();
7850 : bool dummy;
7851 : WERROR status;
7852 10030 : struct ldb_context *ldb = ldb_module_get_ctx(module);
7853 10030 : struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
7854 10030 : const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
7855 :
7856 : /* the value blob for the attribute holds the target object DN */
7857 10030 : status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
7858 : la->value.blob, &tgt_dsdb_dn);
7859 10030 : if (!W_ERROR_IS_OK(status)) {
7860 0 : ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
7861 0 : attr->lDAPDisplayName,
7862 : ldb_dn_get_linearized(src_dn),
7863 : win_errstr(status));
7864 0 : return LDB_ERR_OPERATIONS_ERROR;
7865 : }
7866 :
7867 : /*
7868 : * We can skip the target object checks if we're only syncing critical
7869 : * objects, or we know the target is up-to-date. If either case, we
7870 : * still continue even if the target doesn't exist
7871 : */
7872 10030 : if ((la_entry->dsdb_repl_flags & (DSDB_REPL_FLAG_OBJECT_SUBSET |
7873 : DSDB_REPL_FLAG_TARGETS_UPTODATE)) == 0) {
7874 :
7875 6166 : ret = replmd_check_target_exists(module, tgt_dsdb_dn, la_entry,
7876 : src_dn, false, &guid, &dummy);
7877 : }
7878 :
7879 : /*
7880 : * When we fail to find the target object, the error code we pass
7881 : * back here is really important. It flags back to the callers to
7882 : * retry this request with DRSUAPI_DRS_GET_TGT
7883 : */
7884 10030 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
7885 34 : ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
7886 : }
7887 :
7888 10030 : return ret;
7889 : }
7890 :
7891 : /**
7892 : * Finds the current active Parsed-DN value for a single-valued linked
7893 : * attribute, if one exists.
7894 : * @param ret_pdn assigned the active Parsed-DN, or NULL if none was found
7895 : * @returns LDB_SUCCESS (regardless of whether a match was found), unless
7896 : * an error occurred
7897 : */
7898 6549 : static int replmd_get_active_singleval_link(struct ldb_module *module,
7899 : TALLOC_CTX *mem_ctx,
7900 : struct parsed_dn pdn_list[],
7901 : unsigned int count,
7902 : const struct dsdb_attribute *attr,
7903 : struct parsed_dn **ret_pdn)
7904 : {
7905 : unsigned int i;
7906 :
7907 6549 : *ret_pdn = NULL;
7908 :
7909 6549 : if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE)) {
7910 :
7911 : /* nothing to do for multi-valued linked attributes */
7912 6015 : return LDB_SUCCESS;
7913 : }
7914 :
7915 1117 : for (i = 0; i < count; i++) {
7916 38 : int ret = LDB_SUCCESS;
7917 38 : struct parsed_dn *pdn = &pdn_list[i];
7918 :
7919 : /* skip any inactive links */
7920 38 : if (dsdb_dn_is_deleted_val(pdn->v)) {
7921 26 : continue;
7922 : }
7923 :
7924 : /* we've found an active value for this attribute */
7925 12 : *ret_pdn = pdn;
7926 :
7927 12 : if (pdn->dsdb_dn == NULL) {
7928 2 : struct ldb_context *ldb = ldb_module_get_ctx(module);
7929 :
7930 2 : ret = really_parse_trusted_dn(mem_ctx, ldb, pdn,
7931 2 : attr->syntax->ldap_oid);
7932 : }
7933 :
7934 12 : return ret;
7935 : }
7936 :
7937 : /* no active link found */
7938 522 : return LDB_SUCCESS;
7939 : }
7940 :
7941 : /**
7942 : * @returns true if the replication linked attribute info is newer than we
7943 : * already have in our DB
7944 : * @param pdn the existing linked attribute info in our DB
7945 : * @param la the new linked attribute info received during replication
7946 : */
7947 9706 : static bool replmd_link_update_is_newer(struct parsed_dn *pdn,
7948 : struct drsuapi_DsReplicaLinkedAttribute *la)
7949 : {
7950 : /* see if this update is newer than what we have already */
7951 9706 : struct GUID invocation_id = GUID_zero();
7952 9706 : uint32_t version = 0;
7953 9706 : NTTIME change_time = 0;
7954 :
7955 9706 : if (pdn == NULL) {
7956 :
7957 : /* no existing info so update is newer */
7958 6577 : return true;
7959 : }
7960 :
7961 3129 : dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
7962 3129 : dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
7963 3129 : dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
7964 :
7965 6258 : return replmd_update_is_newer(&invocation_id,
7966 3129 : &la->meta_data.originating_invocation_id,
7967 : version,
7968 : la->meta_data.version,
7969 : change_time,
7970 : la->meta_data.originating_change_time);
7971 : }
7972 :
7973 : /**
7974 : * Marks an existing linked attribute value as deleted in the DB
7975 : * @param pdn the parsed-DN of the target-value to delete
7976 : */
7977 8 : static int replmd_delete_link_value(struct ldb_module *module,
7978 : struct replmd_private *replmd_private,
7979 : TALLOC_CTX *mem_ctx,
7980 : struct ldb_dn *src_obj_dn,
7981 : const struct dsdb_schema *schema,
7982 : const struct dsdb_attribute *attr,
7983 : uint64_t seq_num,
7984 : bool is_active,
7985 : struct GUID *target_guid,
7986 : struct dsdb_dn *target_dsdb_dn,
7987 : struct ldb_val *output_val)
7988 : {
7989 8 : struct ldb_context *ldb = ldb_module_get_ctx(module);
7990 : time_t t;
7991 : NTTIME now;
7992 8 : const struct GUID *invocation_id = NULL;
7993 : int ret;
7994 :
7995 8 : t = time(NULL);
7996 8 : unix_to_nt_time(&now, t);
7997 :
7998 8 : invocation_id = samdb_ntds_invocation_id(ldb);
7999 8 : if (invocation_id == NULL) {
8000 0 : return LDB_ERR_OPERATIONS_ERROR;
8001 : }
8002 :
8003 : /* if the existing link is active, remove its backlink */
8004 8 : if (is_active) {
8005 :
8006 : /*
8007 : * NOTE WELL: After this we will never (at runtime) be
8008 : * able to find this forward link (for instant
8009 : * removal) if/when the link target is deleted.
8010 : *
8011 : * We have dbcheck rules to cover this and cope otherwise
8012 : * by filtering at runtime (i.e. in the extended_dn module).
8013 : */
8014 4 : ret = replmd_add_backlink(module, replmd_private, schema,
8015 : src_obj_dn, target_guid, false,
8016 : attr, NULL);
8017 4 : if (ret != LDB_SUCCESS) {
8018 0 : return ret;
8019 : }
8020 : }
8021 :
8022 : /* mark the existing value as deleted */
8023 8 : ret = replmd_update_la_val(mem_ctx, output_val, target_dsdb_dn,
8024 : target_dsdb_dn, invocation_id, seq_num,
8025 : seq_num, now, true);
8026 8 : return ret;
8027 : }
8028 :
8029 : /**
8030 : * Checks for a conflict in single-valued link attributes, and tries to
8031 : * resolve the problem if possible.
8032 : *
8033 : * Single-valued links should only ever have one active value. If we already
8034 : * have an active link value, and during replication we receive an active link
8035 : * value for a different target DN, then we need to resolve this inconsistency
8036 : * and determine which value should be active. If the received info is better/
8037 : * newer than the existing link attribute, then we need to set our existing
8038 : * link as deleted. If the received info is worse/older, then we should continue
8039 : * to add it, but set it as an inactive link.
8040 : *
8041 : * Note that this is a corner-case that is unlikely to happen (but if it does
8042 : * happen, we don't want it to break replication completely).
8043 : *
8044 : * @param pdn_being_modified the parsed DN corresponding to the received link
8045 : * target (note this is NULL if the link does not already exist in our DB)
8046 : * @param pdn_list all the source object's Parsed-DNs for this attribute, i.e.
8047 : * any existing active or inactive values for the attribute in our DB.
8048 : * @param dsdb_dn the target DN for the received link attribute
8049 : * @param add_as_inactive gets set to true if the received link is worse than
8050 : * the existing link - it should still be added, but as an inactive link.
8051 : */
8052 6549 : static int replmd_check_singleval_la_conflict(struct ldb_module *module,
8053 : struct replmd_private *replmd_private,
8054 : TALLOC_CTX *mem_ctx,
8055 : struct ldb_dn *src_obj_dn,
8056 : struct drsuapi_DsReplicaLinkedAttribute *la,
8057 : struct dsdb_dn *dsdb_dn,
8058 : struct parsed_dn *pdn_being_modified,
8059 : struct parsed_dn *pdn_list,
8060 : struct ldb_message_element *old_el,
8061 : const struct dsdb_schema *schema,
8062 : const struct dsdb_attribute *attr,
8063 : uint64_t seq_num,
8064 : bool *add_as_inactive)
8065 : {
8066 6549 : struct parsed_dn *active_pdn = NULL;
8067 6549 : bool update_is_newer = false;
8068 : int ret;
8069 :
8070 : /*
8071 : * check if there's a conflict for single-valued links, i.e. an active
8072 : * linked attribute already exists, but it has a different target value
8073 : */
8074 6549 : ret = replmd_get_active_singleval_link(module, mem_ctx, pdn_list,
8075 : old_el->num_values, attr,
8076 : &active_pdn);
8077 :
8078 6549 : if (ret != LDB_SUCCESS) {
8079 0 : return ret;
8080 : }
8081 :
8082 : /*
8083 : * If no active value exists (or the received info is for the currently
8084 : * active value), then no conflict exists
8085 : */
8086 6549 : if (active_pdn == NULL || active_pdn == pdn_being_modified) {
8087 6541 : return LDB_SUCCESS;
8088 : }
8089 :
8090 8 : DBG_WARNING("Link conflict for %s attribute on %s\n",
8091 : attr->lDAPDisplayName, ldb_dn_get_linearized(src_obj_dn));
8092 :
8093 : /* Work out how to resolve the conflict based on which info is better */
8094 8 : update_is_newer = replmd_link_update_is_newer(active_pdn, la);
8095 :
8096 8 : if (update_is_newer) {
8097 4 : DBG_WARNING("Using received value %s, over existing target %s\n",
8098 : ldb_dn_get_linearized(dsdb_dn->dn),
8099 : ldb_dn_get_linearized(active_pdn->dsdb_dn->dn));
8100 :
8101 : /*
8102 : * Delete our existing active link. The received info will then
8103 : * be added (through normal link processing) as the active value
8104 : */
8105 12 : ret = replmd_delete_link_value(module, replmd_private, old_el,
8106 : src_obj_dn, schema, attr,
8107 4 : seq_num, true, &active_pdn->guid,
8108 4 : active_pdn->dsdb_dn,
8109 4 : active_pdn->v);
8110 :
8111 4 : if (ret != LDB_SUCCESS) {
8112 0 : return ret;
8113 : }
8114 : } else {
8115 4 : DBG_WARNING("Using existing target %s, over received value %s\n",
8116 : ldb_dn_get_linearized(active_pdn->dsdb_dn->dn),
8117 : ldb_dn_get_linearized(dsdb_dn->dn));
8118 :
8119 : /*
8120 : * we want to keep our existing active link and add the
8121 : * received link as inactive
8122 : */
8123 4 : *add_as_inactive = true;
8124 : }
8125 :
8126 8 : return LDB_SUCCESS;
8127 : }
8128 :
8129 : /**
8130 : * Processes one linked attribute received via replication.
8131 : * @param src_dn the DN of the source object for the link
8132 : * @param attr schema info for the linked attribute
8133 : * @param la_entry the linked attribute info received via DRS
8134 : * @param element_ctx mem context for msg->element[] (when adding a new value
8135 : * we need to realloc old_el->values)
8136 : * @param old_el the corresponding msg->element[] for the linked attribute
8137 : * @param pdn_list a (binary-searchable) parsed DN array for the existing link
8138 : * values in the msg. E.g. for a group, this is the existing members.
8139 : * @param change what got modified: either nothing, an existing link value was
8140 : * modified, or a new link value was added.
8141 : * @returns LDB_SUCCESS if OK, an error otherwise
8142 : */
8143 9867 : static int replmd_process_linked_attribute(struct ldb_module *module,
8144 : TALLOC_CTX *mem_ctx,
8145 : struct replmd_private *replmd_private,
8146 : struct ldb_dn *src_dn,
8147 : const struct dsdb_attribute *attr,
8148 : struct la_entry *la_entry,
8149 : struct ldb_request *parent,
8150 : TALLOC_CTX *element_ctx,
8151 : struct ldb_message_element *old_el,
8152 : struct parsed_dn *pdn_list,
8153 : replmd_link_changed *change)
8154 : {
8155 9867 : struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
8156 9867 : struct ldb_context *ldb = ldb_module_get_ctx(module);
8157 9867 : const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
8158 : int ret;
8159 9867 : struct dsdb_dn *dsdb_dn = NULL;
8160 9867 : uint64_t seq_num = 0;
8161 : struct parsed_dn *pdn, *next;
8162 9867 : struct GUID guid = GUID_zero();
8163 9867 : bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
8164 : bool ignore_link;
8165 9867 : struct dsdb_dn *old_dsdb_dn = NULL;
8166 9867 : struct ldb_val *val_to_update = NULL;
8167 9867 : bool add_as_inactive = false;
8168 : WERROR status;
8169 :
8170 9867 : *change = LINK_CHANGE_NONE;
8171 :
8172 : /* the value blob for the attribute holds the target object DN */
8173 9867 : status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
8174 : la->value.blob, &dsdb_dn);
8175 9867 : if (!W_ERROR_IS_OK(status)) {
8176 0 : ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
8177 0 : attr->lDAPDisplayName,
8178 : ldb_dn_get_linearized(src_dn),
8179 : win_errstr(status));
8180 0 : return LDB_ERR_OPERATIONS_ERROR;
8181 : }
8182 :
8183 9867 : ret = replmd_check_target_exists(module, dsdb_dn, la_entry, src_dn,
8184 : true, &guid, &ignore_link);
8185 :
8186 9867 : if (ret != LDB_SUCCESS) {
8187 1 : return ret;
8188 : }
8189 :
8190 : /*
8191 : * there are some cases where the target object doesn't exist, but it's
8192 : * OK to ignore the linked attribute
8193 : */
8194 9866 : if (ignore_link) {
8195 168 : return ret;
8196 : }
8197 :
8198 : /* see if this link already exists */
8199 19351 : ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
8200 : &guid,
8201 9698 : dsdb_dn->dn,
8202 9698 : dsdb_dn->extra_part, 0,
8203 : &pdn, &next,
8204 9698 : attr->syntax->ldap_oid,
8205 : true);
8206 9698 : if (ret != LDB_SUCCESS) {
8207 0 : return ret;
8208 : }
8209 :
8210 9698 : if (!replmd_link_update_is_newer(pdn, la)) {
8211 3070 : DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
8212 : old_el->name, ldb_dn_get_linearized(src_dn),
8213 : GUID_string(mem_ctx, &la->meta_data.originating_invocation_id)));
8214 3070 : return LDB_SUCCESS;
8215 : }
8216 :
8217 : /* get a seq_num for this change */
8218 6628 : ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
8219 6628 : if (ret != LDB_SUCCESS) {
8220 0 : return ret;
8221 : }
8222 :
8223 : /*
8224 : * check for single-valued link conflicts, i.e. an active linked
8225 : * attribute already exists, but it has a different target value
8226 : */
8227 6628 : if (active) {
8228 6549 : ret = replmd_check_singleval_la_conflict(module, replmd_private,
8229 : mem_ctx, src_dn, la,
8230 : dsdb_dn, pdn, pdn_list,
8231 : old_el, schema, attr,
8232 : seq_num,
8233 : &add_as_inactive);
8234 6549 : if (ret != LDB_SUCCESS) {
8235 0 : return ret;
8236 : }
8237 : }
8238 :
8239 6628 : if (pdn != NULL) {
8240 51 : uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
8241 :
8242 51 : if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
8243 : /* remove the existing backlink */
8244 27 : ret = replmd_add_backlink(module, replmd_private,
8245 : schema,
8246 : src_dn,
8247 27 : &pdn->guid, false, attr,
8248 : parent);
8249 27 : if (ret != LDB_SUCCESS) {
8250 0 : return ret;
8251 : }
8252 : }
8253 :
8254 51 : val_to_update = pdn->v;
8255 51 : old_dsdb_dn = pdn->dsdb_dn;
8256 51 : *change = LINK_CHANGE_MODIFIED;
8257 :
8258 : } else {
8259 : unsigned offset;
8260 :
8261 : /*
8262 : * We know where the new one needs to be, from the *next
8263 : * pointer into pdn_list.
8264 : */
8265 6577 : if (next == NULL) {
8266 5203 : offset = old_el->num_values;
8267 : } else {
8268 1374 : if (next->dsdb_dn == NULL) {
8269 0 : ret = really_parse_trusted_dn(mem_ctx, ldb, next,
8270 0 : attr->syntax->ldap_oid);
8271 0 : if (ret != LDB_SUCCESS) {
8272 0 : return ret;
8273 : }
8274 : }
8275 1374 : offset = next - pdn_list;
8276 1374 : if (offset > old_el->num_values) {
8277 0 : return LDB_ERR_OPERATIONS_ERROR;
8278 : }
8279 : }
8280 :
8281 6577 : old_el->values = talloc_realloc(element_ctx, old_el->values,
8282 : struct ldb_val, old_el->num_values+1);
8283 6577 : if (!old_el->values) {
8284 0 : ldb_module_oom(module);
8285 0 : return LDB_ERR_OPERATIONS_ERROR;
8286 : }
8287 :
8288 6577 : if (offset != old_el->num_values) {
8289 1374 : memmove(&old_el->values[offset + 1], &old_el->values[offset],
8290 1374 : (old_el->num_values - offset) * sizeof(old_el->values[0]));
8291 : }
8292 :
8293 6577 : old_el->num_values++;
8294 :
8295 6577 : val_to_update = &old_el->values[offset];
8296 6577 : old_dsdb_dn = NULL;
8297 6577 : *change = LINK_CHANGE_ADDED;
8298 : }
8299 :
8300 : /* set the link attribute's value to the info that was received */
8301 13211 : ret = replmd_set_la_val(mem_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
8302 6628 : &la->meta_data.originating_invocation_id,
8303 : la->meta_data.originating_usn, seq_num,
8304 : la->meta_data.originating_change_time,
8305 : la->meta_data.version,
8306 6628 : !active);
8307 6628 : if (ret != LDB_SUCCESS) {
8308 0 : return ret;
8309 : }
8310 :
8311 6628 : if (add_as_inactive) {
8312 :
8313 : /* Set the new link as inactive/deleted to avoid conflicts */
8314 4 : ret = replmd_delete_link_value(module, replmd_private, old_el,
8315 : src_dn, schema, attr, seq_num,
8316 : false, &guid, dsdb_dn,
8317 : val_to_update);
8318 :
8319 4 : if (ret != LDB_SUCCESS) {
8320 0 : return ret;
8321 : }
8322 :
8323 6624 : } else if (active) {
8324 :
8325 : /* if the new link is active, then add the new backlink */
8326 6545 : ret = replmd_add_backlink(module, replmd_private,
8327 : schema,
8328 : src_dn,
8329 : &guid, true, attr,
8330 : parent);
8331 6545 : if (ret != LDB_SUCCESS) {
8332 0 : return ret;
8333 : }
8334 : }
8335 :
8336 6628 : ret = dsdb_check_single_valued_link(attr, old_el);
8337 6628 : if (ret != LDB_SUCCESS) {
8338 0 : return ret;
8339 : }
8340 :
8341 6628 : old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
8342 :
8343 6628 : return ret;
8344 : }
8345 :
8346 891908 : static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
8347 : {
8348 891908 : if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
8349 3549 : return replmd_extended_replicated_objects(module, req);
8350 : }
8351 :
8352 888359 : return ldb_next_request(module, req);
8353 : }
8354 :
8355 :
8356 : /*
8357 : we hook into the transaction operations to allow us to
8358 : perform the linked attribute updates at the end of the whole
8359 : transaction. This allows a forward linked attribute to be created
8360 : before the object is created. During a vampire, w2k8 sends us linked
8361 : attributes before the objects they are part of.
8362 : */
8363 244616 : static int replmd_start_transaction(struct ldb_module *module)
8364 : {
8365 : /* create our private structure for this transaction */
8366 244616 : struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
8367 : struct replmd_private);
8368 244616 : replmd_txn_cleanup(replmd_private);
8369 :
8370 : /* free any leftover mod_usn records from cancelled
8371 : transactions */
8372 432586 : while (replmd_private->ncs) {
8373 176 : struct nc_entry *e = replmd_private->ncs;
8374 176 : DLIST_REMOVE(replmd_private->ncs, e);
8375 176 : talloc_free(e);
8376 : }
8377 :
8378 244616 : replmd_private->originating_updates = false;
8379 :
8380 244616 : return ldb_next_start_trans(module);
8381 : }
8382 :
8383 : /**
8384 : * Processes a group of linked attributes that apply to the same source-object
8385 : * and attribute-ID (and were received in the same replication chunk).
8386 : */
8387 3845 : static int replmd_process_la_group(struct ldb_module *module,
8388 : struct replmd_private *replmd_private,
8389 : struct la_group *la_group)
8390 : {
8391 3845 : struct la_entry *la = NULL;
8392 3845 : struct la_entry *prev = NULL;
8393 : int ret;
8394 3845 : TALLOC_CTX *tmp_ctx = NULL;
8395 3845 : struct la_entry *first_la = DLIST_TAIL(la_group->la_entries);
8396 3845 : struct ldb_message *msg = NULL;
8397 3845 : enum deletion_state deletion_state = OBJECT_NOT_DELETED;
8398 3845 : struct ldb_context *ldb = ldb_module_get_ctx(module);
8399 3845 : const struct dsdb_attribute *attr = NULL;
8400 3845 : struct ldb_message_element *old_el = NULL;
8401 3845 : struct parsed_dn *pdn_list = NULL;
8402 : replmd_link_changed change_type;
8403 3845 : uint32_t num_changes = 0;
8404 : time_t t;
8405 3845 : uint64_t seq_num = 0;
8406 :
8407 3845 : tmp_ctx = talloc_new(la_group);
8408 3845 : if (tmp_ctx == NULL) {
8409 0 : return ldb_oom(ldb);
8410 : }
8411 :
8412 : /*
8413 : * get the attribute being modified and the search result for the
8414 : * source object
8415 : */
8416 3845 : ret = replmd_get_la_entry_source(module, first_la, tmp_ctx, &attr,
8417 : &msg);
8418 :
8419 3845 : if (ret != LDB_SUCCESS) {
8420 0 : return ret;
8421 : }
8422 :
8423 : /*
8424 : * Check for deleted objects per MS-DRSR 4.1.10.6.14
8425 : * ProcessLinkValue, because link updates are not applied to
8426 : * recycled and tombstone objects. We don't have to delete
8427 : * any existing link, that should have happened when the
8428 : * object deletion was replicated or initiated.
8429 : *
8430 : * This needs isDeleted and isRecycled to be included as
8431 : * attributes in the search and so in msg if set.
8432 : */
8433 3845 : replmd_deletion_state(module, msg, &deletion_state, NULL);
8434 :
8435 3845 : if (deletion_state >= OBJECT_RECYCLED) {
8436 2 : TALLOC_FREE(tmp_ctx);
8437 2 : return LDB_SUCCESS;
8438 : }
8439 :
8440 : /*
8441 : * Now that we know the deletion_state, remove the extra
8442 : * attributes added for that purpose. We need to do this
8443 : * otherwise in the case of isDeleted: FALSE the modify will
8444 : * fail with:
8445 : *
8446 : * Failed to apply linked attribute change 'attribute 'isDeleted':
8447 : * invalid modify flags on
8448 : * 'CN=g1_1527570609273,CN=Users,DC=samba,DC=example,DC=com':
8449 : * 0x0'
8450 : *
8451 : * This is becaue isDeleted is a Boolean, so FALSE is a
8452 : * legitimate value (set by Samba's deletetest.py)
8453 : */
8454 3843 : ldb_msg_remove_attr(msg, "isDeleted");
8455 3843 : ldb_msg_remove_attr(msg, "isRecycled");
8456 :
8457 : /* get the msg->element[] for the link attribute being processed */
8458 3843 : old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
8459 3843 : if (old_el == NULL) {
8460 2229 : ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName,
8461 : LDB_FLAG_MOD_REPLACE, &old_el);
8462 2229 : if (ret != LDB_SUCCESS) {
8463 0 : ldb_module_oom(module);
8464 0 : return LDB_ERR_OPERATIONS_ERROR;
8465 : }
8466 : } else {
8467 1614 : old_el->flags = LDB_FLAG_MOD_REPLACE;
8468 : }
8469 :
8470 : /*
8471 : * go through and process the link target value(s) for this particular
8472 : * source object and attribute. For optimization, the same msg is used
8473 : * across multiple calls to replmd_process_linked_attribute().
8474 : * Note that we should not add or remove any msg attributes inside the
8475 : * loop (we should only add/modify *values* for the attribute being
8476 : * processed). Otherwise msg->elements is realloc'd and old_el/pdn_list
8477 : * pointers will be invalidated
8478 : */
8479 13709 : for (la = DLIST_TAIL(la_group->la_entries); la; la=prev) {
8480 9867 : prev = DLIST_PREV(la);
8481 9867 : DLIST_REMOVE(la_group->la_entries, la);
8482 :
8483 : /*
8484 : * parse the existing links (this can be costly for a large
8485 : * group, so we try to minimize the times we do it)
8486 : */
8487 9867 : if (pdn_list == NULL) {
8488 8008 : ret = get_parsed_dns_trusted_fallback(module,
8489 : replmd_private,
8490 : tmp_ctx, old_el,
8491 : &pdn_list,
8492 8008 : attr->syntax->ldap_oid,
8493 : NULL);
8494 :
8495 8008 : if (ret != LDB_SUCCESS) {
8496 0 : return ret;
8497 : }
8498 : }
8499 29497 : ret = replmd_process_linked_attribute(module, tmp_ctx,
8500 : replmd_private,
8501 9867 : msg->dn, attr, la, NULL,
8502 9867 : msg->elements, old_el,
8503 : pdn_list, &change_type);
8504 9867 : if (ret != LDB_SUCCESS) {
8505 1 : replmd_txn_cleanup(replmd_private);
8506 1 : return ret;
8507 : }
8508 :
8509 : /*
8510 : * Adding a link reallocs memory, and so invalidates all the
8511 : * pointers in pdn_list. Reparse the PDNs on the next loop
8512 : */
8513 9866 : if (change_type == LINK_CHANGE_ADDED) {
8514 6577 : TALLOC_FREE(pdn_list);
8515 : }
8516 :
8517 9866 : if (change_type != LINK_CHANGE_NONE) {
8518 6628 : num_changes++;
8519 : }
8520 :
8521 9866 : if ((++replmd_private->num_processed % 8192) == 0) {
8522 0 : DBG_NOTICE("Processed %u/%u linked attributes\n",
8523 : replmd_private->num_processed,
8524 : replmd_private->total_links);
8525 : }
8526 : }
8527 :
8528 : /*
8529 : * it's possible we're already up-to-date and so don't need to modify
8530 : * the object at all (e.g. doing a 'drs replicate --full-sync')
8531 : */
8532 3842 : if (num_changes == 0) {
8533 1377 : TALLOC_FREE(tmp_ctx);
8534 1377 : return LDB_SUCCESS;
8535 : }
8536 :
8537 : /*
8538 : * Note that adding the whenChanged/etc attributes below will realloc
8539 : * msg->elements, invalidating the existing element/parsed-DN pointers
8540 : */
8541 2465 : old_el = NULL;
8542 2465 : TALLOC_FREE(pdn_list);
8543 :
8544 : /* update whenChanged/uSNChanged as the object has changed */
8545 2465 : t = time(NULL);
8546 2465 : ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ,
8547 : &seq_num);
8548 2465 : if (ret != LDB_SUCCESS) {
8549 0 : return ret;
8550 : }
8551 :
8552 2465 : ret = add_time_element(msg, "whenChanged", t);
8553 2465 : if (ret != LDB_SUCCESS) {
8554 0 : ldb_operr(ldb);
8555 0 : return ret;
8556 : }
8557 :
8558 2465 : ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
8559 2465 : if (ret != LDB_SUCCESS) {
8560 0 : ldb_operr(ldb);
8561 0 : return ret;
8562 : }
8563 :
8564 : /* apply the link changes to the source object */
8565 2465 : ret = linked_attr_modify(module, msg, NULL);
8566 2465 : if (ret != LDB_SUCCESS) {
8567 0 : ldb_debug(ldb, LDB_DEBUG_WARNING,
8568 : "Failed to apply linked attribute change "
8569 : "Error: '%s' DN: '%s' Attribute: '%s'\n",
8570 : ldb_errstring(ldb),
8571 0 : ldb_dn_get_linearized(msg->dn),
8572 0 : attr->lDAPDisplayName);
8573 0 : TALLOC_FREE(tmp_ctx);
8574 0 : return ret;
8575 : }
8576 :
8577 2465 : TALLOC_FREE(tmp_ctx);
8578 2465 : return LDB_SUCCESS;
8579 : }
8580 :
8581 : /*
8582 : on prepare commit we loop over our queued la_context structures and
8583 : apply each of them
8584 : */
8585 212234 : static int replmd_prepare_commit(struct ldb_module *module)
8586 : {
8587 167039 : struct replmd_private *replmd_private =
8588 212234 : talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8589 : struct la_group *la_group, *prev;
8590 : int ret;
8591 :
8592 212234 : if (replmd_private->la_list != NULL) {
8593 328 : DBG_NOTICE("Processing linked attributes\n");
8594 : }
8595 :
8596 : /*
8597 : * Walk the list of linked attributes from DRS replication.
8598 : *
8599 : * We walk backwards, to do the first entry first, as we
8600 : * added the entries with DLIST_ADD() which puts them at the
8601 : * start of the list
8602 : *
8603 : * Links are grouped together so we process links for the same
8604 : * source object in one go.
8605 : */
8606 383091 : for (la_group = DLIST_TAIL(replmd_private->la_list);
8607 45221 : la_group != NULL;
8608 3844 : la_group = prev) {
8609 :
8610 3845 : prev = DLIST_PREV(la_group);
8611 3845 : DLIST_REMOVE(replmd_private->la_list, la_group);
8612 3845 : ret = replmd_process_la_group(module, replmd_private,
8613 : la_group);
8614 3845 : if (ret != LDB_SUCCESS) {
8615 1 : replmd_txn_cleanup(replmd_private);
8616 1 : return ret;
8617 : }
8618 : }
8619 :
8620 212233 : replmd_txn_cleanup(replmd_private);
8621 :
8622 : /* possibly change @REPLCHANGED */
8623 212233 : ret = replmd_notify_store(module, NULL);
8624 212233 : if (ret != LDB_SUCCESS) {
8625 0 : return ret;
8626 : }
8627 :
8628 212233 : return ldb_next_prepare_commit(module);
8629 : }
8630 :
8631 32114 : static int replmd_del_transaction(struct ldb_module *module)
8632 : {
8633 20574 : struct replmd_private *replmd_private =
8634 32114 : talloc_get_type(ldb_module_get_private(module), struct replmd_private);
8635 32114 : replmd_txn_cleanup(replmd_private);
8636 :
8637 32114 : return ldb_next_del_trans(module);
8638 : }
8639 :
8640 :
8641 : static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
8642 : .name = "repl_meta_data",
8643 : .init_context = replmd_init,
8644 : .add = replmd_add,
8645 : .modify = replmd_modify,
8646 : .rename = replmd_rename,
8647 : .del = replmd_delete,
8648 : .extended = replmd_extended,
8649 : .start_transaction = replmd_start_transaction,
8650 : .prepare_commit = replmd_prepare_commit,
8651 : .del_transaction = replmd_del_transaction,
8652 : };
8653 :
8654 4310 : int ldb_repl_meta_data_module_init(const char *version)
8655 : {
8656 4310 : LDB_MODULE_CHECK_VERSION(version);
8657 4310 : return ldb_register_module(&ldb_repl_meta_data_module_ops);
8658 : }
|