• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/samba-3.5.8/source4/dsdb/samdb/ldb_modules/
1/*
2   ldb database library
3
4   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
5   Copyright (C) Simo Sorce <idra@samba.org> 2008
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19*/
20
21/*
22 *  Name: ldb
23 *
24 *  Component: ldb linked_attributes module
25 *
26 *  Description: Module to ensure linked attribute pairs remain in sync
27 *
28 *  Author: Andrew Bartlett
29 */
30
31#include "includes.h"
32#include "ldb_module.h"
33#include "dlinklist.h"
34#include "dsdb/samdb/samdb.h"
35#include "librpc/gen_ndr/ndr_misc.h"
36
37struct la_private {
38	struct la_context *la_list;
39};
40
41struct la_op_store {
42	struct la_op_store *next;
43	struct la_op_store *prev;
44	enum la_op {LA_OP_ADD, LA_OP_DEL} op;
45	struct GUID guid;
46	char *name;
47	char *value;
48};
49
50struct replace_context {
51	struct la_context *ac;
52	unsigned int num_elements;
53	struct ldb_message_element *el;
54};
55
56struct la_context {
57	struct la_context *next, *prev;
58	const struct dsdb_schema *schema;
59	struct ldb_module *module;
60	struct ldb_request *req;
61	struct ldb_dn *partition_dn;
62	struct ldb_dn *add_dn;
63	struct ldb_dn *del_dn;
64	struct replace_context *rc;
65	struct la_op_store *ops;
66	struct ldb_extended *op_response;
67	struct ldb_control **op_controls;
68};
69
70static struct la_context *linked_attributes_init(struct ldb_module *module,
71						 struct ldb_request *req)
72{
73	struct ldb_context *ldb;
74	struct la_context *ac;
75	const struct ldb_control *partition_ctrl;
76
77	ldb = ldb_module_get_ctx(module);
78
79	ac = talloc_zero(req, struct la_context);
80	if (ac == NULL) {
81		ldb_oom(ldb);
82		return NULL;
83	}
84
85	ac->schema = dsdb_get_schema(ldb);
86	ac->module = module;
87	ac->req = req;
88
89	/* remember the partition DN that came in, if given */
90	partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
91	if (partition_ctrl) {
92		const struct dsdb_control_current_partition *partition;
93		partition = talloc_get_type(partition_ctrl->data,
94					    struct dsdb_control_current_partition);
95		SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
96
97		ac->partition_dn = ldb_dn_copy(ac, partition->dn);
98	}
99
100	return ac;
101}
102
103/*
104  turn a DN into a GUID
105 */
106static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
107{
108	const struct ldb_val *guid_val;
109	int ret;
110
111	guid_val = ldb_dn_get_extended_component(dn, "GUID");
112	if (guid_val) {
113		/* there is a GUID embedded in the DN */
114		enum ndr_err_code ndr_err;
115		ndr_err = ndr_pull_struct_blob(guid_val, ac, NULL, guid,
116					       (ndr_pull_flags_fn_t)ndr_pull_GUID);
117		if (ndr_err != NDR_ERR_SUCCESS) {
118			DEBUG(0,(__location__ ": Failed to parse GUID\n"));
119			return LDB_ERR_OPERATIONS_ERROR;
120		}
121	} else {
122		ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid);
123		if (ret != LDB_SUCCESS) {
124			DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
125				 ldb_dn_get_linearized(dn)));
126			return ret;
127		}
128	}
129	return LDB_SUCCESS;
130}
131
132
133/* Common routine to handle reading the attributes and creating a
134 * series of modify requests */
135static int la_store_op(struct la_context *ac,
136		       enum la_op op, struct ldb_val *dn,
137		       const char *name)
138{
139	struct ldb_context *ldb;
140	struct la_op_store *os;
141	struct ldb_dn *op_dn;
142	int ret;
143
144	ldb = ldb_module_get_ctx(ac->module);
145
146	op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
147	if (!op_dn) {
148		ldb_asprintf_errstring(ldb,
149				       "could not parse attribute as a DN");
150		return LDB_ERR_INVALID_DN_SYNTAX;
151	}
152
153	os = talloc_zero(ac, struct la_op_store);
154	if (!os) {
155		ldb_oom(ldb);
156		return LDB_ERR_OPERATIONS_ERROR;
157	}
158
159	os->op = op;
160
161	ret = la_guid_from_dn(ac, op_dn, &os->guid);
162	if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
163		/* we are deleting an object, and we've found it has a
164		 * forward link to a target that no longer
165		 * exists. This is not an error in the delete, and we
166		 * should just not do the deferred delete of the
167		 * target attribute
168		 */
169		talloc_free(os);
170		return LDB_SUCCESS;
171	}
172	if (ret != LDB_SUCCESS) {
173		return ret;
174	}
175
176	os->name = talloc_strdup(os, name);
177	if (!os->name) {
178		ldb_oom(ldb);
179		return LDB_ERR_OPERATIONS_ERROR;
180	}
181
182	/* Do deletes before adds */
183	if (op == LA_OP_ADD) {
184		DLIST_ADD_END(ac->ops, os, struct la_op_store *);
185	} else {
186		/* By adding to the head of the list, we do deletes before
187		 * adds when processing a replace */
188		DLIST_ADD(ac->ops, os);
189	}
190
191	return LDB_SUCCESS;
192}
193
194static int la_op_search_callback(struct ldb_request *req,
195				 struct ldb_reply *ares);
196static int la_queue_mod_request(struct la_context *ac);
197static int la_down_req(struct la_context *ac);
198
199
200
201/* add */
202static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
203{
204	struct ldb_context *ldb;
205	const struct dsdb_attribute *target_attr;
206	struct la_context *ac;
207	const char *attr_name;
208	int ret;
209	int i, j;
210
211	ldb = ldb_module_get_ctx(module);
212
213	if (ldb_dn_is_special(req->op.add.message->dn)) {
214		/* do not manipulate our control entries */
215		return ldb_next_request(module, req);
216	}
217
218	ac = linked_attributes_init(module, req);
219	if (!ac) {
220		return LDB_ERR_OPERATIONS_ERROR;
221	}
222
223	if (!ac->schema) {
224		/* without schema, this doesn't make any sense */
225		talloc_free(ac);
226		return ldb_next_request(module, req);
227	}
228
229	/* Need to ensure we only have forward links being specified */
230	for (i=0; i < req->op.add.message->num_elements; i++) {
231		const struct ldb_message_element *el = &req->op.add.message->elements[i];
232		const struct dsdb_attribute *schema_attr
233			= dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
234		if (!schema_attr) {
235			ldb_asprintf_errstring(ldb,
236					       "attribute %s is not a valid attribute in schema", el->name);
237			return LDB_ERR_OBJECT_CLASS_VIOLATION;
238		}
239		/* We have a valid attribute, now find out if it is linked */
240		if (schema_attr->linkID == 0) {
241			continue;
242		}
243
244		if ((schema_attr->linkID & 1) == 1) {
245			/* Odd is for the target.  Illegal to modify */
246			ldb_asprintf_errstring(ldb,
247					       "attribute %s must not be modified directly, it is a linked attribute", el->name);
248			return LDB_ERR_UNWILLING_TO_PERFORM;
249		}
250
251		/* Even link IDs are for the originating attribute */
252		target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
253		if (!target_attr) {
254			/*
255			 * windows 2003 has a broken schema where
256			 * the definition of msDS-IsDomainFor
257			 * is missing (which is supposed to be
258			 * the backlink of the msDS-HasDomainNCs
259			 * attribute
260			 */
261			continue;
262		}
263
264		attr_name = target_attr->lDAPDisplayName;
265
266		for (j = 0; j < el->num_values; j++) {
267			ret = la_store_op(ac, LA_OP_ADD,
268					  &el->values[j],
269					  attr_name);
270			if (ret != LDB_SUCCESS) {
271				return ret;
272			}
273		}
274	}
275
276	/* if no linked attributes are present continue */
277	if (ac->ops == NULL) {
278		/* nothing to do for this module, proceed */
279		talloc_free(ac);
280		return ldb_next_request(module, req);
281	}
282
283	/* start with the original request */
284	return la_down_req(ac);
285}
286
287/* For a delete or rename, we need to find out what linked attributes
288 * are currently on this DN, and then deal with them.  This is the
289 * callback to the base search */
290
291static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
292{
293	struct ldb_context *ldb;
294	const struct dsdb_attribute *schema_attr;
295	const struct dsdb_attribute *target_attr;
296	struct ldb_message_element *search_el;
297	struct replace_context *rc;
298	struct la_context *ac;
299	const char *attr_name;
300	int i, j;
301	int ret = LDB_SUCCESS;
302
303	ac = talloc_get_type(req->context, struct la_context);
304	ldb = ldb_module_get_ctx(ac->module);
305	rc = ac->rc;
306
307	if (!ares) {
308		return ldb_module_done(ac->req, NULL, NULL,
309					LDB_ERR_OPERATIONS_ERROR);
310	}
311	if (ares->error != LDB_SUCCESS) {
312		return ldb_module_done(ac->req, ares->controls,
313					ares->response, ares->error);
314	}
315
316	/* Only entries are interesting, and we only want the olddn */
317	switch (ares->type) {
318	case LDB_REPLY_ENTRY:
319
320		if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
321			ldb_asprintf_errstring(ldb,
322					       "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn));
323			/* Guh?  We only asked for this DN */
324			talloc_free(ares);
325			return ldb_module_done(ac->req, NULL, NULL,
326						LDB_ERR_OPERATIONS_ERROR);
327		}
328
329		ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
330
331		/* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
332		for (i = 0; rc && i < rc->num_elements; i++) {
333
334			schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
335			if (!schema_attr) {
336				ldb_asprintf_errstring(ldb,
337					"attribute %s is not a valid attribute in schema",
338					rc->el[i].name);
339				talloc_free(ares);
340				return ldb_module_done(ac->req, NULL, NULL,
341						LDB_ERR_OBJECT_CLASS_VIOLATION);
342			}
343
344			search_el = ldb_msg_find_element(ares->message,
345							 rc->el[i].name);
346
347			/* See if this element already exists */
348			/* otherwise just ignore as
349			 * the add has already been scheduled */
350			if ( ! search_el) {
351				continue;
352			}
353
354			target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
355			if (!target_attr) {
356				/*
357				 * windows 2003 has a broken schema where
358				 * the definition of msDS-IsDomainFor
359				 * is missing (which is supposed to be
360				 * the backlink of the msDS-HasDomainNCs
361				 * attribute
362				 */
363				continue;
364			}
365			attr_name = target_attr->lDAPDisplayName;
366
367			/* Now we know what was there, we can remove it for the re-add */
368			for (j = 0; j < search_el->num_values; j++) {
369				ret = la_store_op(ac, LA_OP_DEL,
370						  &search_el->values[j],
371						  attr_name);
372				if (ret != LDB_SUCCESS) {
373					talloc_free(ares);
374					return ldb_module_done(ac->req,
375							       NULL, NULL, ret);
376				}
377			}
378		}
379
380		break;
381
382	case LDB_REPLY_REFERRAL:
383		/* ignore */
384		break;
385
386	case LDB_REPLY_DONE:
387
388		talloc_free(ares);
389
390		if (ac->req->operation == LDB_ADD) {
391			/* Start the modifies to the backlinks */
392			ret = la_queue_mod_request(ac);
393
394			if (ret != LDB_SUCCESS) {
395				return ldb_module_done(ac->req, NULL, NULL,
396						       ret);
397			}
398		} else {
399			/* Start with the original request */
400			ret = la_down_req(ac);
401			if (ret != LDB_SUCCESS) {
402				return ldb_module_done(ac->req, NULL, NULL, ret);
403			}
404		}
405		return LDB_SUCCESS;
406	}
407
408	talloc_free(ares);
409	return ret;
410}
411
412
413/* modify */
414static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
415{
416	/* Look over list of modifications */
417	/* Find if any are for linked attributes */
418	/* Determine the effect of the modification */
419	/* Apply the modify to the linked entry */
420
421	struct ldb_context *ldb;
422	int i, j;
423	struct la_context *ac;
424	struct ldb_request *search_req;
425	const char **attrs;
426
427	int ret;
428
429	ldb = ldb_module_get_ctx(module);
430
431	if (ldb_dn_is_special(req->op.mod.message->dn)) {
432		/* do not manipulate our control entries */
433		return ldb_next_request(module, req);
434	}
435
436	ac = linked_attributes_init(module, req);
437	if (!ac) {
438		return LDB_ERR_OPERATIONS_ERROR;
439	}
440
441	if (!ac->schema) {
442		/* without schema, this doesn't make any sense */
443		return ldb_next_request(module, req);
444	}
445
446	ac->rc = talloc_zero(ac, struct replace_context);
447	if (!ac->rc) {
448		ldb_oom(ldb);
449		return LDB_ERR_OPERATIONS_ERROR;
450	}
451
452	for (i=0; i < req->op.mod.message->num_elements; i++) {
453		bool store_el = false;
454		const char *attr_name;
455		const struct dsdb_attribute *target_attr;
456		const struct ldb_message_element *el = &req->op.mod.message->elements[i];
457		const struct dsdb_attribute *schema_attr
458			= dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
459		if (!schema_attr) {
460			ldb_asprintf_errstring(ldb,
461					       "attribute %s is not a valid attribute in schema", el->name);
462			return LDB_ERR_OBJECT_CLASS_VIOLATION;
463		}
464		/* We have a valid attribute, now find out if it is linked */
465		if (schema_attr->linkID == 0) {
466			continue;
467		}
468
469		if ((schema_attr->linkID & 1) == 1) {
470			/* Odd is for the target.  Illegal to modify */
471			ldb_asprintf_errstring(ldb,
472					       "attribute %s must not be modified directly, it is a linked attribute", el->name);
473			return LDB_ERR_UNWILLING_TO_PERFORM;
474		}
475
476		/* Even link IDs are for the originating attribute */
477
478		/* Now find the target attribute */
479		target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
480		if (!target_attr) {
481			/*
482			 * windows 2003 has a broken schema where
483			 * the definition of msDS-IsDomainFor
484			 * is missing (which is supposed to be
485			 * the backlink of the msDS-HasDomainNCs
486			 * attribute
487			 */
488			continue;
489		}
490
491		attr_name = target_attr->lDAPDisplayName;
492
493		switch (el->flags & LDB_FLAG_MOD_MASK) {
494		case LDB_FLAG_MOD_REPLACE:
495			/* treat as just a normal add the delete part is handled by the callback */
496			store_el = true;
497
498			/* break intentionally missing */
499
500		case LDB_FLAG_MOD_ADD:
501
502			/* For each value being added, we need to setup the adds */
503			for (j = 0; j < el->num_values; j++) {
504				ret = la_store_op(ac, LA_OP_ADD,
505						  &el->values[j],
506						  attr_name);
507				if (ret != LDB_SUCCESS) {
508					return ret;
509				}
510			}
511			break;
512
513		case LDB_FLAG_MOD_DELETE:
514
515			if (el->num_values) {
516				/* For each value being deleted, we need to setup the delete */
517				for (j = 0; j < el->num_values; j++) {
518					ret = la_store_op(ac, LA_OP_DEL,
519							  &el->values[j],
520							  attr_name);
521					if (ret != LDB_SUCCESS) {
522						return ret;
523					}
524				}
525			} else {
526				/* Flag that there was a DELETE
527				 * without a value specified, so we
528				 * need to look for the old value */
529				store_el = true;
530			}
531
532			break;
533		}
534
535		if (store_el) {
536			struct ldb_message_element *search_el;
537
538			search_el = talloc_realloc(ac->rc, ac->rc->el,
539						   struct ldb_message_element,
540						   ac->rc->num_elements +1);
541			if (!search_el) {
542				ldb_oom(ldb);
543				return LDB_ERR_OPERATIONS_ERROR;
544			}
545			ac->rc->el = search_el;
546
547			ac->rc->el[ac->rc->num_elements] = *el;
548			ac->rc->num_elements++;
549		}
550	}
551
552	if (ac->ops || ac->rc->el) {
553		/* both replace and delete without values are handled in the callback
554		 * after the search on the entry to be modified is performed */
555
556		attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
557		if (!attrs) {
558			ldb_oom(ldb);
559			return LDB_ERR_OPERATIONS_ERROR;
560		}
561		for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
562			attrs[i] = ac->rc->el[i].name;
563		}
564		attrs[i] = NULL;
565
566		/* The callback does all the hard work here */
567		ret = ldb_build_search_req(&search_req, ldb, ac,
568					   req->op.mod.message->dn,
569					   LDB_SCOPE_BASE,
570					   "(objectClass=*)", attrs,
571					   NULL,
572					   ac, la_mod_search_callback,
573					   req);
574
575		/* We need to figure out our own extended DN, to fill in as the backlink target */
576		if (ret == LDB_SUCCESS) {
577			ret = ldb_request_add_control(search_req,
578						      LDB_CONTROL_EXTENDED_DN_OID,
579						      false, NULL);
580		}
581		if (ret == LDB_SUCCESS) {
582			talloc_steal(search_req, attrs);
583
584			ret = ldb_next_request(module, search_req);
585		}
586
587	} else {
588		/* nothing to do for this module, proceed */
589		talloc_free(ac);
590		ret = ldb_next_request(module, req);
591	}
592
593	return ret;
594}
595
596/* delete */
597static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req)
598{
599	struct ldb_context *ldb;
600	struct ldb_request *search_req;
601	struct la_context *ac;
602	const char **attrs;
603	WERROR werr;
604	int ret;
605
606	/* This gets complex:  We need to:
607	   - Do a search for the entry
608	   - Wait for these result to appear
609	   - In the callback for the result, issue a modify
610		request based on the linked attributes found
611	   - Wait for each modify result
612	   - Regain our sainity
613	*/
614
615	ldb = ldb_module_get_ctx(module);
616
617	ac = linked_attributes_init(module, req);
618	if (!ac) {
619		return LDB_ERR_OPERATIONS_ERROR;
620	}
621
622	if (!ac->schema) {
623		/* without schema, this doesn't make any sense */
624		return ldb_next_request(module, req);
625	}
626
627	werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
628	if (!W_ERROR_IS_OK(werr)) {
629		return LDB_ERR_OPERATIONS_ERROR;
630	}
631
632	ret = ldb_build_search_req(&search_req, ldb, req,
633				   req->op.del.dn, LDB_SCOPE_BASE,
634				   "(objectClass=*)", attrs,
635				   NULL,
636				   ac, la_op_search_callback,
637				   req);
638
639	if (ret != LDB_SUCCESS) {
640		return ret;
641	}
642
643	talloc_steal(search_req, attrs);
644
645	return ldb_next_request(module, search_req);
646}
647
648/* rename */
649static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
650{
651	struct la_context *ac;
652
653	/* This gets complex:  We need to:
654	   - Do a search for the entry
655	   - Wait for these result to appear
656	   - In the callback for the result, issue a modify
657		request based on the linked attributes found
658	   - Wait for each modify result
659	   - Regain our sainity
660	*/
661
662	ac = linked_attributes_init(module, req);
663	if (!ac) {
664		return LDB_ERR_OPERATIONS_ERROR;
665	}
666
667	if (!ac->schema) {
668		/* without schema, this doesn't make any sense */
669		return ldb_next_request(module, req);
670	}
671
672	/* start with the original request */
673	return la_down_req(ac);
674}
675
676
677static int la_op_search_callback(struct ldb_request *req,
678				 struct ldb_reply *ares)
679{
680	struct ldb_context *ldb;
681	struct la_context *ac;
682	const struct dsdb_attribute *schema_attr;
683	const struct dsdb_attribute *target_attr;
684	const struct ldb_message_element *el;
685	const char *attr_name;
686	int i, j;
687	int ret;
688
689	ac = talloc_get_type(req->context, struct la_context);
690	ldb = ldb_module_get_ctx(ac->module);
691
692	if (!ares) {
693		return ldb_module_done(ac->req, NULL, NULL,
694					LDB_ERR_OPERATIONS_ERROR);
695	}
696	if (ares->error != LDB_SUCCESS) {
697		return ldb_module_done(ac->req, ares->controls,
698					ares->response, ares->error);
699	}
700
701	/* Only entries are interesting, and we only want the olddn */
702	switch (ares->type) {
703	case LDB_REPLY_ENTRY:
704		ret = ldb_dn_compare(ares->message->dn, req->op.search.base);
705		if (ret != 0) {
706			/* Guh?  We only asked for this DN */
707			talloc_free(ares);
708			return ldb_module_done(ac->req, NULL, NULL,
709						LDB_ERR_OPERATIONS_ERROR);
710		}
711		if (ares->message->num_elements == 0) {
712			/* only bother at all if there were some
713			 * linked attributes found */
714			talloc_free(ares);
715			return LDB_SUCCESS;
716		}
717
718		switch (ac->req->operation) {
719		case LDB_DELETE:
720			ac->del_dn = talloc_steal(ac, ares->message->dn);
721			break;
722		case LDB_RENAME:
723			ac->add_dn = talloc_steal(ac, ares->message->dn);
724			ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn);
725			break;
726		default:
727			talloc_free(ares);
728			ldb_set_errstring(ldb,
729					  "operations must be delete or rename");
730			return ldb_module_done(ac->req, NULL, NULL,
731						LDB_ERR_OPERATIONS_ERROR);
732		}
733
734		for (i = 0; i < ares->message->num_elements; i++) {
735			el = &ares->message->elements[i];
736
737			schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
738			if (!schema_attr) {
739				ldb_asprintf_errstring(ldb,
740					"attribute %s is not a valid attribute"
741					" in schema", el->name);
742				talloc_free(ares);
743				return ldb_module_done(ac->req, NULL, NULL,
744						LDB_ERR_OBJECT_CLASS_VIOLATION);
745			}
746
747			/* Valid attribute, now find out if it is linked */
748			if (schema_attr->linkID == 0) {
749				/* Not a linked attribute, skip */
750				continue;
751			}
752
753			if ((schema_attr->linkID & 1) == 0) {
754				/* Odd is for the target. */
755				target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
756				if (!target_attr) {
757					continue;
758				}
759				attr_name = target_attr->lDAPDisplayName;
760			} else {
761				target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
762				if (!target_attr) {
763					continue;
764				}
765				attr_name = target_attr->lDAPDisplayName;
766			}
767			for (j = 0; j < el->num_values; j++) {
768				ret = la_store_op(ac, LA_OP_DEL,
769						  &el->values[j],
770						  attr_name);
771
772				/* for renames, ensure we add it back */
773				if (ret == LDB_SUCCESS
774				    && ac->req->operation == LDB_RENAME) {
775					ret = la_store_op(ac, LA_OP_ADD,
776							  &el->values[j],
777							  attr_name);
778				}
779				if (ret != LDB_SUCCESS) {
780					talloc_free(ares);
781					return ldb_module_done(ac->req,
782							       NULL, NULL, ret);
783				}
784			}
785		}
786
787		break;
788
789	case LDB_REPLY_REFERRAL:
790		/* ignore */
791		break;
792
793	case LDB_REPLY_DONE:
794
795		talloc_free(ares);
796
797
798		switch (ac->req->operation) {
799		case LDB_DELETE:
800			/* start the mod requests chain */
801			ret = la_down_req(ac);
802			if (ret != LDB_SUCCESS) {
803				return ldb_module_done(ac->req, NULL, NULL, ret);
804			}
805			return ret;
806
807		case LDB_RENAME:
808			/* start the mod requests chain */
809			ret = la_queue_mod_request(ac);
810			if (ret != LDB_SUCCESS) {
811				return ldb_module_done(ac->req, NULL, NULL,
812						       ret);
813			}
814			return ret;
815
816		default:
817			talloc_free(ares);
818			ldb_set_errstring(ldb,
819					  "operations must be delete or rename");
820			return ldb_module_done(ac->req, NULL, NULL,
821						LDB_ERR_OPERATIONS_ERROR);
822		}
823	}
824
825	talloc_free(ares);
826	return LDB_SUCCESS;
827}
828
829/* queue a linked attributes modify request in the la_private
830   structure */
831static int la_queue_mod_request(struct la_context *ac)
832{
833	struct la_private *la_private =
834		talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
835
836	if (la_private == NULL) {
837		ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
838		return LDB_ERR_OPERATIONS_ERROR;
839	}
840
841	talloc_steal(la_private, ac);
842	DLIST_ADD(la_private->la_list, ac);
843
844	return ldb_module_done(ac->req, ac->op_controls,
845			       ac->op_response, LDB_SUCCESS);
846}
847
848/* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
849static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
850{
851	int ret;
852	struct la_context *ac;
853	struct ldb_context *ldb;
854
855	ac = talloc_get_type(req->context, struct la_context);
856	ldb = ldb_module_get_ctx(ac->module);
857
858	if (!ares) {
859		return ldb_module_done(ac->req, NULL, NULL,
860					LDB_ERR_OPERATIONS_ERROR);
861	}
862	if (ares->error != LDB_SUCCESS) {
863		return ldb_module_done(ac->req, ares->controls,
864					ares->response, ares->error);
865	}
866
867	if (ares->type != LDB_REPLY_DONE) {
868		ldb_set_errstring(ldb,
869				  "invalid ldb_reply_type in callback");
870		talloc_free(ares);
871		return ldb_module_done(ac->req, NULL, NULL,
872					LDB_ERR_OPERATIONS_ERROR);
873	}
874
875	ac->op_controls = talloc_steal(ac, ares->controls);
876	ac->op_response = talloc_steal(ac, ares->response);
877
878	/* If we have modfies to make, this is the time to do them for modify and delete */
879	ret = la_queue_mod_request(ac);
880
881	if (ret != LDB_SUCCESS) {
882		return ldb_module_done(ac->req, NULL, NULL, ret);
883	}
884	talloc_free(ares);
885
886	/* la_queue_mod_request has already sent the callbacks */
887	return LDB_SUCCESS;
888
889}
890
891/* Having done the original rename try to fix up all the linked attributes */
892static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
893{
894	int ret;
895	struct la_context *ac;
896	struct ldb_request *search_req;
897	const char **attrs;
898	WERROR werr;
899	struct ldb_context *ldb;
900
901	ac = talloc_get_type(req->context, struct la_context);
902	ldb = ldb_module_get_ctx(ac->module);
903
904	if (!ares) {
905		return ldb_module_done(ac->req, NULL, NULL,
906					LDB_ERR_OPERATIONS_ERROR);
907	}
908	if (ares->error != LDB_SUCCESS) {
909		return ldb_module_done(ac->req, ares->controls,
910					ares->response, ares->error);
911	}
912
913	if (ares->type != LDB_REPLY_DONE) {
914		ldb_set_errstring(ldb,
915				  "invalid ldb_reply_type in callback");
916		talloc_free(ares);
917		return ldb_module_done(ac->req, NULL, NULL,
918					LDB_ERR_OPERATIONS_ERROR);
919	}
920
921	werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
922	if (!W_ERROR_IS_OK(werr)) {
923		return LDB_ERR_OPERATIONS_ERROR;
924	}
925
926	ret = ldb_build_search_req(&search_req, ldb, req,
927				   ac->req->op.rename.newdn, LDB_SCOPE_BASE,
928				   "(objectClass=*)", attrs,
929				   NULL,
930				   ac, la_op_search_callback,
931				   req);
932
933	if (ret != LDB_SUCCESS) {
934		return ret;
935	}
936
937	talloc_steal(search_req, attrs);
938
939	if (ret == LDB_SUCCESS) {
940		ret = ldb_request_add_control(search_req,
941					      LDB_CONTROL_EXTENDED_DN_OID,
942					      false, NULL);
943	}
944	if (ret != LDB_SUCCESS) {
945		return ldb_module_done(ac->req, NULL, NULL,
946				       ret);
947	}
948
949	ac->op_controls = talloc_steal(ac, ares->controls);
950	ac->op_response = talloc_steal(ac, ares->response);
951
952	return ldb_next_request(ac->module, search_req);
953}
954
955/* Having done the original add, then try to fix up all the linked attributes
956
957  This is done after the add so the links can get the extended DNs correctly.
958 */
959static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
960{
961	int ret;
962	struct la_context *ac;
963	struct ldb_context *ldb;
964
965	ac = talloc_get_type(req->context, struct la_context);
966	ldb = ldb_module_get_ctx(ac->module);
967
968	if (!ares) {
969		return ldb_module_done(ac->req, NULL, NULL,
970					LDB_ERR_OPERATIONS_ERROR);
971	}
972	if (ares->error != LDB_SUCCESS) {
973		return ldb_module_done(ac->req, ares->controls,
974					ares->response, ares->error);
975	}
976
977	if (ares->type != LDB_REPLY_DONE) {
978		ldb_set_errstring(ldb,
979				  "invalid ldb_reply_type in callback");
980		talloc_free(ares);
981		return ldb_module_done(ac->req, NULL, NULL,
982					LDB_ERR_OPERATIONS_ERROR);
983	}
984
985	if (ac->ops) {
986		struct ldb_request *search_req;
987		static const char *attrs[] = { NULL };
988
989		/* The callback does all the hard work here - we need
990		 * the objectGUID and SID of the added record */
991		ret = ldb_build_search_req(&search_req, ldb, ac,
992					   ac->req->op.add.message->dn,
993					   LDB_SCOPE_BASE,
994					   "(objectClass=*)", attrs,
995					   NULL,
996					   ac, la_mod_search_callback,
997					   ac->req);
998
999		if (ret == LDB_SUCCESS) {
1000			ret = ldb_request_add_control(search_req,
1001						      LDB_CONTROL_EXTENDED_DN_OID,
1002						      false, NULL);
1003		}
1004		if (ret != LDB_SUCCESS) {
1005			return ldb_module_done(ac->req, NULL, NULL,
1006					       ret);
1007		}
1008
1009		ac->op_controls = talloc_steal(ac, ares->controls);
1010		ac->op_response = talloc_steal(ac, ares->response);
1011
1012		return ldb_next_request(ac->module, search_req);
1013
1014	} else {
1015		return ldb_module_done(ac->req, ares->controls,
1016				       ares->response, ares->error);
1017	}
1018}
1019
1020/* Reconstruct the original request, but pointing at our local callback to finish things off */
1021static int la_down_req(struct la_context *ac)
1022{
1023	struct ldb_request *down_req;
1024	int ret;
1025	struct ldb_context *ldb;
1026
1027	ldb = ldb_module_get_ctx(ac->module);
1028
1029	switch (ac->req->operation) {
1030	case LDB_ADD:
1031		ret = ldb_build_add_req(&down_req, ldb, ac,
1032					ac->req->op.add.message,
1033					ac->req->controls,
1034					ac, la_add_callback,
1035					ac->req);
1036		break;
1037	case LDB_MODIFY:
1038		ret = ldb_build_mod_req(&down_req, ldb, ac,
1039					ac->req->op.mod.message,
1040					ac->req->controls,
1041					ac, la_mod_del_callback,
1042					ac->req);
1043		break;
1044	case LDB_DELETE:
1045		ret = ldb_build_del_req(&down_req, ldb, ac,
1046					ac->req->op.del.dn,
1047					ac->req->controls,
1048					ac, la_mod_del_callback,
1049					ac->req);
1050		break;
1051	case LDB_RENAME:
1052		ret = ldb_build_rename_req(&down_req, ldb, ac,
1053					   ac->req->op.rename.olddn,
1054					   ac->req->op.rename.newdn,
1055					   ac->req->controls,
1056					   ac, la_rename_callback,
1057					   ac->req);
1058		break;
1059	default:
1060		ret = LDB_ERR_OPERATIONS_ERROR;
1061	}
1062	if (ret != LDB_SUCCESS) {
1063		return ret;
1064	}
1065
1066	return ldb_next_request(ac->module, down_req);
1067}
1068
1069/*
1070  use the GUID part of an extended DN to find the target DN, in case
1071  it has moved
1072 */
1073static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
1074			     struct GUID *guid, struct ldb_dn **dn)
1075{
1076	return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, GUID_string(ac, guid), dn);
1077}
1078
1079/* apply one la_context op change */
1080static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
1081{
1082	struct ldb_message_element *ret_el;
1083	struct ldb_request *mod_req;
1084	struct ldb_message *new_msg;
1085	struct ldb_context *ldb;
1086	int ret;
1087
1088	ldb = ldb_module_get_ctx(ac->module);
1089
1090	/* Create the modify request */
1091	new_msg = ldb_msg_new(ac);
1092	if (!new_msg) {
1093		ldb_oom(ldb);
1094		return LDB_ERR_OPERATIONS_ERROR;
1095	}
1096
1097	ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
1098	if (ret != LDB_SUCCESS) {
1099		return ret;
1100	}
1101
1102	if (op->op == LA_OP_ADD) {
1103		ret = ldb_msg_add_empty(new_msg, op->name,
1104					LDB_FLAG_MOD_ADD, &ret_el);
1105	} else {
1106		ret = ldb_msg_add_empty(new_msg, op->name,
1107					LDB_FLAG_MOD_DELETE, &ret_el);
1108	}
1109	if (ret != LDB_SUCCESS) {
1110		return ret;
1111	}
1112	ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
1113	if (!ret_el->values) {
1114		ldb_oom(ldb);
1115		return LDB_ERR_OPERATIONS_ERROR;
1116	}
1117	ret_el->num_values = 1;
1118	if (op->op == LA_OP_ADD) {
1119		ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
1120	} else {
1121		ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
1122	}
1123
1124#if 0
1125	ldb_debug(ldb, LDB_DEBUG_WARNING,
1126		  "link on %s %s: %s %s\n",
1127		  ldb_dn_get_linearized(new_msg->dn), ret_el->name,
1128		  ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
1129#endif
1130
1131	ret = ldb_build_mod_req(&mod_req, ldb, op,
1132				new_msg,
1133				NULL,
1134				NULL,
1135				ldb_op_default_callback,
1136				NULL);
1137	if (ret != LDB_SUCCESS) {
1138		return ret;
1139	}
1140	talloc_steal(mod_req, new_msg);
1141
1142	if (DEBUGLVL(4)) {
1143		DEBUG(4,("Applying linked attribute change:\n%s\n",
1144			 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
1145	}
1146
1147	/* Run the new request */
1148	ret = ldb_next_request(module, mod_req);
1149
1150	/* we need to wait for this to finish, as we are being called
1151	   from the synchronous end_transaction hook of this module */
1152	if (ret == LDB_SUCCESS) {
1153		ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
1154	}
1155
1156	if (ret != LDB_SUCCESS) {
1157		ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n",
1158			  ldb_errstring(ldb),
1159			  ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
1160	}
1161
1162	return ret;
1163}
1164
1165/* apply one set of la_context changes */
1166static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1167{
1168	struct la_op_store *op;
1169
1170	for (op = ac->ops; op; op=op->next) {
1171		int ret = la_do_op_request(module, ac, op);
1172		if (ret != LDB_SUCCESS) {
1173			if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1174				return ret;
1175			}
1176		}
1177	}
1178
1179	return LDB_SUCCESS;
1180}
1181
1182
1183/*
1184  we hook into the transaction operations to allow us to
1185  perform the linked attribute updates at the end of the whole
1186  transaction. This allows a forward linked attribute to be created
1187  before the target is created, as long as the target is created
1188  in the same transaction
1189 */
1190static int linked_attributes_start_transaction(struct ldb_module *module)
1191{
1192	/* create our private structure for this transaction */
1193	struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1194							struct la_private);
1195	talloc_free(la_private);
1196	la_private = talloc(module, struct la_private);
1197	if (la_private == NULL) {
1198		return LDB_ERR_OPERATIONS_ERROR;
1199	}
1200	la_private->la_list = NULL;
1201	ldb_module_set_private(module, la_private);
1202	return ldb_next_start_trans(module);
1203}
1204
1205/*
1206  on prepare commit we loop over our queued la_context structures
1207  and apply each of them
1208 */
1209static int linked_attributes_prepare_commit(struct ldb_module *module)
1210{
1211	struct la_private *la_private =
1212		talloc_get_type(ldb_module_get_private(module), struct la_private);
1213	struct la_context *ac;
1214
1215	/* walk the list backwards, to do the first entry first, as we
1216	 * added the entries with DLIST_ADD() which puts them at the
1217	 * start of the list */
1218	for (ac = la_private->la_list; ac && ac->next; ac=ac->next) ;
1219
1220	for (; ac; ac=ac->prev) {
1221		int ret;
1222		ac->req = NULL;
1223		ret = la_do_mod_request(module, ac);
1224		if (ret != LDB_SUCCESS) {
1225			DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1226			talloc_free(la_private);
1227			ldb_module_set_private(module, NULL);
1228			return ret;
1229		}
1230	}
1231
1232	talloc_free(la_private);
1233	ldb_module_set_private(module, NULL);
1234
1235	return ldb_next_prepare_commit(module);
1236}
1237
1238static int linked_attributes_del_transaction(struct ldb_module *module)
1239{
1240	struct la_private *la_private =
1241		talloc_get_type(ldb_module_get_private(module), struct la_private);
1242	talloc_free(la_private);
1243	ldb_module_set_private(module, NULL);
1244	return ldb_next_del_trans(module);
1245}
1246
1247
1248_PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1249	.name		   = "linked_attributes",
1250	.add               = linked_attributes_add,
1251	.modify            = linked_attributes_modify,
1252	.del               = linked_attributes_del,
1253	.rename            = linked_attributes_rename,
1254	.start_transaction = linked_attributes_start_transaction,
1255	.prepare_commit    = linked_attributes_prepare_commit,
1256	.del_transaction   = linked_attributes_del_transaction,
1257};
1258