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