c204571133c7746272615bfbfff900cee1482713
[kamenim/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
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 "util/dlinklist.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "librpc/gen_ndr/ndr_misc.h"
36 #include "dsdb/samdb/ldb_modules/util.h"
37
38 struct la_private {
39         struct la_context *la_list;
40 };
41
42 struct la_op_store {
43         struct la_op_store *next;
44         struct la_op_store *prev;
45         enum la_op {LA_OP_ADD, LA_OP_DEL} op;
46         struct GUID guid;
47         char *name;
48         char *value;
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 *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
70 static 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
76         ldb = ldb_module_get_ctx(module);
77
78         ac = talloc_zero(req, struct la_context);
79         if (ac == NULL) {
80                 ldb_oom(ldb);
81                 return NULL;
82         }
83
84         ac->schema = dsdb_get_schema(ldb, ac);
85         ac->module = module;
86         ac->req = req;
87
88         return ac;
89 }
90
91 /*
92   turn a DN into a GUID
93  */
94 static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
95 {
96         NTSTATUS status;
97         int ret;
98
99         status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
100         if (NT_STATUS_IS_OK(status)) {
101                 return LDB_SUCCESS;
102         }
103         if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
104                 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
105                          ldb_dn_get_linearized(dn)));
106                 return LDB_ERR_OPERATIONS_ERROR;
107         }
108
109         ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid);
110         if (ret != LDB_SUCCESS) {
111                 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
112                          ldb_dn_get_linearized(dn)));
113                 return ret;
114         }
115         return LDB_SUCCESS;
116 }
117
118
119 /* Common routine to handle reading the attributes and creating a
120  * series of modify requests */
121 static int la_store_op(struct la_context *ac,
122                        enum la_op op, struct ldb_val *dn,
123                        const char *name)
124 {
125         struct ldb_context *ldb;
126         struct la_op_store *os;
127         struct ldb_dn *op_dn;
128         int ret;
129
130         ldb = ldb_module_get_ctx(ac->module);
131
132         op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
133         if (!op_dn) {
134                 ldb_asprintf_errstring(ldb,
135                                        "could not parse attribute as a DN");
136                 return LDB_ERR_INVALID_DN_SYNTAX;
137         }
138
139         os = talloc_zero(ac, struct la_op_store);
140         if (!os) {
141                 ldb_oom(ldb);
142                 return LDB_ERR_OPERATIONS_ERROR;
143         }
144
145         os->op = op;
146
147         ret = la_guid_from_dn(ac, op_dn, &os->guid);
148         talloc_free(op_dn);
149         if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
150                 /* we are deleting an object, and we've found it has a
151                  * forward link to a target that no longer
152                  * exists. This is not an error in the delete, and we
153                  * should just not do the deferred delete of the
154                  * target attribute
155                  */
156                 talloc_free(os);
157                 return LDB_SUCCESS;
158         }
159         if (ret != LDB_SUCCESS) {
160                 return ret;
161         }
162
163         os->name = talloc_strdup(os, name);
164         if (!os->name) {
165                 ldb_oom(ldb);
166                 return LDB_ERR_OPERATIONS_ERROR;
167         }
168
169         /* Do deletes before adds */
170         if (op == LA_OP_ADD) {
171                 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
172         } else {
173                 /* By adding to the head of the list, we do deletes before
174                  * adds when processing a replace */
175                 DLIST_ADD(ac->ops, os);
176         }
177
178         return LDB_SUCCESS;
179 }
180
181 static int la_queue_mod_request(struct la_context *ac);
182 static int la_down_req(struct la_context *ac);
183
184
185
186 /* add */
187 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
188 {
189         struct ldb_context *ldb;
190         const struct dsdb_attribute *target_attr;
191         struct la_context *ac;
192         const char *attr_name;
193         struct ldb_control *ctrl;
194         unsigned int i, j;
195         int ret;
196
197         ldb = ldb_module_get_ctx(module);
198
199         if (ldb_dn_is_special(req->op.add.message->dn)) {
200                 /* do not manipulate our control entries */
201                 return ldb_next_request(module, req);
202         }
203
204         if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
205                 /* don't do anything special for linked attributes, repl_meta_data has done it */
206                 return ldb_next_request(module, req);
207         }
208         ctrl->critical = false;
209
210         ac = linked_attributes_init(module, req);
211         if (!ac) {
212                 return LDB_ERR_OPERATIONS_ERROR;
213         }
214
215         if (!ac->schema) {
216                 /* without schema, this doesn't make any sense */
217                 talloc_free(ac);
218                 return ldb_next_request(module, req);
219         }
220
221         /* Need to ensure we only have forward links being specified */
222         for (i=0; i < req->op.add.message->num_elements; i++) {
223                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
224                 const struct dsdb_attribute *schema_attr
225                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
226                 if (!schema_attr) {
227                         ldb_asprintf_errstring(ldb,
228                                                "attribute %s is not a valid attribute in schema", el->name);
229                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
230                 }
231                 /* We have a valid attribute, now find out if it is a forward link */
232                 if ((schema_attr->linkID == 0)) {
233                         continue;
234                 }
235
236                 if ((schema_attr->linkID & 1) == 1) {
237                         unsigned int functional_level;
238
239                         functional_level = dsdb_functional_level(ldb);
240                         SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
241                 }
242
243                 /* Even link IDs are for the originating attribute */
244                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
245                 if (!target_attr) {
246                         /*
247                          * windows 2003 has a broken schema where
248                          * the definition of msDS-IsDomainFor
249                          * is missing (which is supposed to be
250                          * the backlink of the msDS-HasDomainNCs
251                          * attribute
252                          */
253                         continue;
254                 }
255
256                 attr_name = target_attr->lDAPDisplayName;
257
258                 for (j = 0; j < el->num_values; j++) {
259                         ret = la_store_op(ac, LA_OP_ADD,
260                                           &el->values[j],
261                                           attr_name);
262                         if (ret != LDB_SUCCESS) {
263                                 return ret;
264                         }
265                 }
266         }
267
268         /* if no linked attributes are present continue */
269         if (ac->ops == NULL) {
270                 /* nothing to do for this module, proceed */
271                 talloc_free(ac);
272                 return ldb_next_request(module, req);
273         }
274
275         /* start with the original request */
276         return la_down_req(ac);
277 }
278
279 /* For a delete or rename, we need to find out what linked attributes
280  * are currently on this DN, and then deal with them.  This is the
281  * callback to the base search */
282
283 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
284 {
285         struct ldb_context *ldb;
286         const struct dsdb_attribute *schema_attr;
287         const struct dsdb_attribute *target_attr;
288         struct ldb_message_element *search_el;
289         struct replace_context *rc;
290         struct la_context *ac;
291         const char *attr_name;
292         unsigned int i, j;
293         int ret = LDB_SUCCESS;
294
295         ac = talloc_get_type(req->context, struct la_context);
296         ldb = ldb_module_get_ctx(ac->module);
297         rc = ac->rc;
298
299         if (!ares) {
300                 return ldb_module_done(ac->req, NULL, NULL,
301                                         LDB_ERR_OPERATIONS_ERROR);
302         }
303         if (ares->error != LDB_SUCCESS) {
304                 return ldb_module_done(ac->req, ares->controls,
305                                         ares->response, ares->error);
306         }
307
308         /* Only entries are interesting, and we only want the olddn */
309         switch (ares->type) {
310         case LDB_REPLY_ENTRY:
311
312                 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
313                         ldb_asprintf_errstring(ldb,
314                                                "linked_attributes: %s is not the DN we were looking for",
315                                                ldb_dn_get_linearized(ares->message->dn));
316                         /* Guh?  We only asked for this DN */
317                         talloc_free(ares);
318                         return ldb_module_done(ac->req, NULL, NULL,
319                                                 LDB_ERR_OPERATIONS_ERROR);
320                 }
321
322                 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
323
324                 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
325                 for (i = 0; rc && i < rc->num_elements; i++) {
326
327                         schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
328                         if (!schema_attr) {
329                                 ldb_asprintf_errstring(ldb,
330                                         "attribute %s is not a valid attribute in schema",
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_ERR_OPERATIONS_ERROR;
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                 ldb_oom(ldb);
448                 return LDB_ERR_OPERATIONS_ERROR;
449         }
450
451         for (i=0; i < req->op.mod.message->num_elements; i++) {
452                 bool store_el = false;
453                 const char *attr_name;
454                 const struct dsdb_attribute *target_attr;
455                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
456                 const struct dsdb_attribute *schema_attr
457                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
458                 if (!schema_attr) {
459                         ldb_asprintf_errstring(ldb,
460                                                "attribute %s is not a valid attribute in schema", el->name);
461                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
462                 }
463                 /* We have a valid attribute, now find out if it is a forward link
464                    (Even link IDs are for the originating attribute) */
465                 if (schema_attr->linkID == 0) {
466                         continue;
467                 }
468
469                 if ((schema_attr->linkID & 1) == 1) {
470                         unsigned int functional_level;
471
472                         functional_level = dsdb_functional_level(ldb);
473                         SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
474                 }
475                 /* Now find the target attribute */
476                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
477                 if (!target_attr) {
478                         /*
479                          * windows 2003 has a broken schema where
480                          * the definition of msDS-IsDomainFor
481                          * is missing (which is supposed to be
482                          * the backlink of the msDS-HasDomainNCs
483                          * attribute
484                          */
485                         continue;
486                 }
487
488                 attr_name = target_attr->lDAPDisplayName;
489
490                 switch (el->flags & LDB_FLAG_MOD_MASK) {
491                 case LDB_FLAG_MOD_REPLACE:
492                         /* treat as just a normal add the delete part is handled by the callback */
493                         store_el = true;
494
495                         /* break intentionally missing */
496
497                 case LDB_FLAG_MOD_ADD:
498
499                         /* For each value being added, we need to setup the adds */
500                         for (j = 0; j < el->num_values; j++) {
501                                 ret = la_store_op(ac, LA_OP_ADD,
502                                                   &el->values[j],
503                                                   attr_name);
504                                 if (ret != LDB_SUCCESS) {
505                                         return ret;
506                                 }
507                         }
508                         break;
509
510                 case LDB_FLAG_MOD_DELETE:
511
512                         if (el->num_values) {
513                                 /* For each value being deleted, we need to setup the delete */
514                                 for (j = 0; j < el->num_values; j++) {
515                                         ret = la_store_op(ac, LA_OP_DEL,
516                                                           &el->values[j],
517                                                           attr_name);
518                                         if (ret != LDB_SUCCESS) {
519                                                 return ret;
520                                         }
521                                 }
522                         } else {
523                                 /* Flag that there was a DELETE
524                                  * without a value specified, so we
525                                  * need to look for the old value */
526                                 store_el = true;
527                         }
528
529                         break;
530                 }
531
532                 if (store_el) {
533                         struct ldb_message_element *search_el;
534
535                         search_el = talloc_realloc(ac->rc, ac->rc->el,
536                                                    struct ldb_message_element,
537                                                    ac->rc->num_elements +1);
538                         if (!search_el) {
539                                 ldb_oom(ldb);
540                                 return LDB_ERR_OPERATIONS_ERROR;
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                         ldb_oom(ldb);
556                         return LDB_ERR_OPERATIONS_ERROR;
557                 }
558                 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
559                         attrs[i] = ac->rc->el[i].name;
560                 }
561                 attrs[i] = NULL;
562
563                 /* The callback does all the hard work here */
564                 ret = ldb_build_search_req(&search_req, ldb, ac,
565                                            req->op.mod.message->dn,
566                                            LDB_SCOPE_BASE,
567                                            "(objectClass=*)", attrs,
568                                            NULL,
569                                            ac, la_mod_search_callback,
570                                            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 = ldb_request_add_control(search_req,
575                                                       LDB_CONTROL_EXTENDED_DN_OID,
576                                                       false, NULL);
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 {
598         unsigned int i, j;
599         TALLOC_CTX *tmp_ctx = talloc_new(module);
600         struct ldb_context *ldb = ldb_module_get_ctx(module);
601         const struct dsdb_attribute *target;
602         const char *attrs[2];
603         int ret;
604
605         target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
606         if (target == NULL) {
607                 /* there is no counterpart link to change */
608                 return LDB_SUCCESS;
609         }
610
611         attrs[0] = target->lDAPDisplayName;
612         attrs[1] = NULL;
613
614         for (i=0; i<el->num_values; i++) {
615                 struct dsdb_dn *dsdb_dn;
616                 struct ldb_result *res;
617                 struct ldb_message *msg;
618                 struct ldb_message_element *el2;
619
620                 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
621                 if (dsdb_dn == NULL) {
622                         talloc_free(tmp_ctx);
623                         return LDB_ERR_INVALID_DN_SYNTAX;
624                 }
625
626                 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
627                                             attrs,
628                                             DSDB_SEARCH_SHOW_DELETED |
629                                             DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
630                                             DSDB_SEARCH_REVEAL_INTERNALS);
631                 if (ret != LDB_SUCCESS) {
632                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
633                                                el->name, target->lDAPDisplayName,
634                                                ldb_dn_get_linearized(old_dn),
635                                                ldb_dn_get_linearized(dsdb_dn->dn),
636                                                ldb_errstring(ldb));
637                         talloc_free(tmp_ctx);
638                         return ret;
639                 }
640                 msg = res->msgs[0];
641
642                 if (msg->num_elements == 0) {
643                         /* Forward link without backlink remaining - nothing to do here */
644                         continue;
645                 } else if (msg->num_elements != 1) {
646                         ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
647                                                msg->num_elements, ldb_dn_get_linearized(msg->dn));
648                         talloc_free(tmp_ctx);
649                         return LDB_ERR_OPERATIONS_ERROR;
650                 }
651                 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
652                         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));
653                         talloc_free(tmp_ctx);
654                         return LDB_ERR_OPERATIONS_ERROR;
655                 }
656                 el2 = &msg->elements[0];
657
658                 el2->flags = LDB_FLAG_MOD_REPLACE;
659
660                 /* find our DN in the values */
661                 for (j=0; j<el2->num_values; j++) {
662                         struct dsdb_dn *dsdb_dn2;
663                         dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
664                         if (dsdb_dn2 == NULL) {
665                                 talloc_free(tmp_ctx);
666                                 return LDB_ERR_INVALID_DN_SYNTAX;
667                         }
668                         if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
669                                 continue;
670                         }
671                         ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
672                         if (ret != LDB_SUCCESS) {
673                                 talloc_free(tmp_ctx);
674                                 return ret;
675                         }
676
677                         el2->values[j] = data_blob_string_const(
678                                 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
679                 }
680
681                 ret = dsdb_check_single_valued_link(target, el2);
682                 if (ret != LDB_SUCCESS) {
683                         talloc_free(tmp_ctx);
684                         return ret;
685                 }
686
687                 ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
688                 if (ret != LDB_SUCCESS) {
689                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
690                                                el->name, target->lDAPDisplayName,
691                                                ldb_dn_get_linearized(old_dn),
692                                                ldb_dn_get_linearized(dsdb_dn->dn),
693                                                ldb_errstring(ldb));
694                         talloc_free(tmp_ctx);
695                         return ret;
696                 }
697         }
698
699         talloc_free(tmp_ctx);
700         return LDB_SUCCESS;
701 }
702
703
704 /* rename */
705 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
706 {
707         struct ldb_result *res;
708         struct ldb_message *msg;
709         unsigned int i;
710         struct ldb_context *ldb = ldb_module_get_ctx(module);
711         struct dsdb_schema *schema;
712         int ret;
713         /*
714            - load the current msg
715            - find any linked attributes
716            - if its a link then find the target object
717            - modify the target linked attributes with the new DN
718         */
719         ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
720                                     NULL, DSDB_SEARCH_SHOW_DELETED);
721         if (ret != LDB_SUCCESS) {
722                 return ret;
723         }
724
725         schema = dsdb_get_schema(ldb, res);
726         if (!schema) {
727                 ldb_oom(ldb);
728                 return LDB_ERR_OPERATIONS_ERROR;
729         }
730
731         msg = res->msgs[0];
732
733         for (i=0; i<msg->num_elements; i++) {
734                 struct ldb_message_element *el = &msg->elements[i];
735                 const struct dsdb_attribute *schema_attr
736                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
737                 if (!schema_attr || schema_attr->linkID == 0) {
738                         continue;
739                 }
740                 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
741                                                   schema, schema_attr);
742                 if (ret != LDB_SUCCESS) {
743                         talloc_free(res);
744                         return ret;
745                 }
746         }
747
748         talloc_free(res);
749
750         return ldb_next_request(module, req);
751 }
752
753
754 /* queue a linked attributes modify request in the la_private
755    structure */
756 static int la_queue_mod_request(struct la_context *ac)
757 {
758         struct la_private *la_private =
759                 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
760
761         if (la_private == NULL) {
762                 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
763                 return LDB_ERR_OPERATIONS_ERROR;
764         }
765
766         talloc_steal(la_private, ac);
767         DLIST_ADD(la_private->la_list, ac);
768
769         return ldb_module_done(ac->req, ac->op_controls,
770                                ac->op_response, LDB_SUCCESS);
771 }
772
773 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
774 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
775 {
776         struct la_context *ac;
777         struct ldb_context *ldb;
778         int ret;
779
780         ac = talloc_get_type(req->context, struct la_context);
781         ldb = ldb_module_get_ctx(ac->module);
782
783         if (!ares) {
784                 return ldb_module_done(ac->req, NULL, NULL,
785                                         LDB_ERR_OPERATIONS_ERROR);
786         }
787         if (ares->error != LDB_SUCCESS) {
788                 return ldb_module_done(ac->req, ares->controls,
789                                         ares->response, ares->error);
790         }
791
792         if (ares->type != LDB_REPLY_DONE) {
793                 ldb_set_errstring(ldb,
794                                   "invalid ldb_reply_type in callback");
795                 talloc_free(ares);
796                 return ldb_module_done(ac->req, NULL, NULL,
797                                         LDB_ERR_OPERATIONS_ERROR);
798         }
799
800         ac->op_controls = talloc_steal(ac, ares->controls);
801         ac->op_response = talloc_steal(ac, ares->response);
802
803         /* If we have modfies to make, this is the time to do them for modify and delete */
804         ret = la_queue_mod_request(ac);
805
806         if (ret != LDB_SUCCESS) {
807                 return ldb_module_done(ac->req, NULL, NULL, ret);
808         }
809         talloc_free(ares);
810
811         /* la_queue_mod_request has already sent the callbacks */
812         return LDB_SUCCESS;
813
814 }
815
816 /* Having done the original add, then try to fix up all the linked attributes
817
818   This is done after the add so the links can get the extended DNs correctly.
819  */
820 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
821 {
822         struct la_context *ac;
823         struct ldb_context *ldb;
824         int ret;
825
826         ac = talloc_get_type(req->context, struct la_context);
827         ldb = ldb_module_get_ctx(ac->module);
828
829         if (!ares) {
830                 return ldb_module_done(ac->req, NULL, NULL,
831                                         LDB_ERR_OPERATIONS_ERROR);
832         }
833         if (ares->error != LDB_SUCCESS) {
834                 return ldb_module_done(ac->req, ares->controls,
835                                         ares->response, ares->error);
836         }
837
838         if (ares->type != LDB_REPLY_DONE) {
839                 ldb_set_errstring(ldb,
840                                   "invalid ldb_reply_type in callback");
841                 talloc_free(ares);
842                 return ldb_module_done(ac->req, NULL, NULL,
843                                         LDB_ERR_OPERATIONS_ERROR);
844         }
845
846         if (ac->ops) {
847                 struct ldb_request *search_req;
848                 static const char *attrs[] = { NULL };
849
850                 /* The callback does all the hard work here - we need
851                  * the objectGUID and SID of the added record */
852                 ret = ldb_build_search_req(&search_req, ldb, ac,
853                                            ac->req->op.add.message->dn,
854                                            LDB_SCOPE_BASE,
855                                            "(objectClass=*)", attrs,
856                                            NULL,
857                                            ac, la_mod_search_callback,
858                                            ac->req);
859
860                 if (ret == LDB_SUCCESS) {
861                         ret = ldb_request_add_control(search_req,
862                                                       LDB_CONTROL_EXTENDED_DN_OID,
863                                                       false, NULL);
864                 }
865                 if (ret != LDB_SUCCESS) {
866                         return ldb_module_done(ac->req, NULL, NULL,
867                                                ret);
868                 }
869
870                 ac->op_controls = talloc_steal(ac, ares->controls);
871                 ac->op_response = talloc_steal(ac, ares->response);
872
873                 return ldb_next_request(ac->module, search_req);
874
875         } else {
876                 return ldb_module_done(ac->req, ares->controls,
877                                        ares->response, ares->error);
878         }
879 }
880
881 /* Reconstruct the original request, but pointing at our local callback to finish things off */
882 static int la_down_req(struct la_context *ac)
883 {
884         struct ldb_request *down_req;
885         struct ldb_context *ldb;
886         int ret;
887
888         ldb = ldb_module_get_ctx(ac->module);
889
890         switch (ac->req->operation) {
891         case LDB_ADD:
892                 ret = ldb_build_add_req(&down_req, ldb, ac,
893                                         ac->req->op.add.message,
894                                         ac->req->controls,
895                                         ac, la_add_callback,
896                                         ac->req);
897                 break;
898         case LDB_MODIFY:
899                 ret = ldb_build_mod_req(&down_req, ldb, ac,
900                                         ac->req->op.mod.message,
901                                         ac->req->controls,
902                                         ac, la_mod_del_callback,
903                                         ac->req);
904                 break;
905         default:
906                 ret = LDB_ERR_OPERATIONS_ERROR;
907         }
908         if (ret != LDB_SUCCESS) {
909                 return ret;
910         }
911
912         return ldb_next_request(ac->module, down_req);
913 }
914
915 /*
916   use the GUID part of an extended DN to find the target DN, in case
917   it has moved
918  */
919 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
920                              struct GUID *guid, struct ldb_dn **dn)
921 {
922         return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn);
923 }
924
925 /* apply one la_context op change */
926 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
927 {
928         struct ldb_message_element *ret_el;
929         struct ldb_message *new_msg;
930         struct ldb_context *ldb;
931         int ret;
932
933         ldb = ldb_module_get_ctx(ac->module);
934
935         /* Create the modify request */
936         new_msg = ldb_msg_new(ac);
937         if (!new_msg) {
938                 ldb_oom(ldb);
939                 return LDB_ERR_OPERATIONS_ERROR;
940         }
941
942         ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
943         if (ret != LDB_SUCCESS) {
944                 return ret;
945         }
946
947         if (op->op == LA_OP_ADD) {
948                 ret = ldb_msg_add_empty(new_msg, op->name,
949                                         LDB_FLAG_MOD_ADD, &ret_el);
950         } else {
951                 ret = ldb_msg_add_empty(new_msg, op->name,
952                                         LDB_FLAG_MOD_DELETE, &ret_el);
953         }
954         if (ret != LDB_SUCCESS) {
955                 return ret;
956         }
957         ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
958         if (!ret_el->values) {
959                 ldb_oom(ldb);
960                 return LDB_ERR_OPERATIONS_ERROR;
961         }
962         ret_el->num_values = 1;
963         if (op->op == LA_OP_ADD) {
964                 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
965         } else {
966                 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
967         }
968
969 #if 0
970         ldb_debug(ldb, LDB_DEBUG_WARNING,
971                   "link on %s %s: %s %s\n",
972                   ldb_dn_get_linearized(new_msg->dn), ret_el->name,
973                   ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
974 #endif
975
976         if (DEBUGLVL(4)) {
977                 DEBUG(4,("Applying linked attribute change:\n%s\n",
978                          ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
979         }
980
981         ret = dsdb_module_modify(module, new_msg, 0);
982         if (ret != LDB_SUCCESS) {
983                 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
984                           ldb_errstring(ldb),
985                           ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
986         }
987
988         return ret;
989 }
990
991 /* apply one set of la_context changes */
992 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
993 {
994         struct la_op_store *op;
995
996         for (op = ac->ops; op; op=op->next) {
997                 int ret = la_do_op_request(module, ac, op);
998                 if (ret != LDB_SUCCESS) {
999                         if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1000                                 return ret;
1001                         }
1002                 }
1003         }
1004
1005         return LDB_SUCCESS;
1006 }
1007
1008
1009 /*
1010   we hook into the transaction operations to allow us to
1011   perform the linked attribute updates at the end of the whole
1012   transaction. This allows a forward linked attribute to be created
1013   before the target is created, as long as the target is created
1014   in the same transaction
1015  */
1016 static int linked_attributes_start_transaction(struct ldb_module *module)
1017 {
1018         /* create our private structure for this transaction */
1019         struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1020                                                         struct la_private);
1021         talloc_free(la_private);
1022         la_private = talloc(module, struct la_private);
1023         if (la_private == NULL) {
1024                 return LDB_ERR_OPERATIONS_ERROR;
1025         }
1026         la_private->la_list = NULL;
1027         ldb_module_set_private(module, la_private);
1028         return ldb_next_start_trans(module);
1029 }
1030
1031 /*
1032   on prepare commit we loop over our queued la_context structures
1033   and apply each of them
1034  */
1035 static int linked_attributes_prepare_commit(struct ldb_module *module)
1036 {
1037         struct la_private *la_private =
1038                 talloc_get_type(ldb_module_get_private(module), struct la_private);
1039         struct la_context *ac;
1040
1041         if (!la_private) {
1042                 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1043                 return ldb_next_prepare_commit(module);
1044         }
1045         /* walk the list backwards, to do the first entry first, as we
1046          * added the entries with DLIST_ADD() which puts them at the
1047          * start of the list */
1048
1049         /* Start at the end of the list - so we can start
1050          * there, but ensure we don't create a loop by NULLing
1051          * it out in the first element */
1052         ac = DLIST_TAIL(la_private->la_list);
1053
1054         for (; ac; ac=DLIST_PREV(ac)) {
1055                 int ret;
1056                 ac->req = NULL;
1057                 ret = la_do_mod_request(module, ac);
1058                 if (ret != LDB_SUCCESS) {
1059                         DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1060                         talloc_free(la_private);
1061                         ldb_module_set_private(module, NULL);
1062                         return ret;
1063                 }
1064         }
1065
1066         talloc_free(la_private);
1067         ldb_module_set_private(module, NULL);
1068
1069         return ldb_next_prepare_commit(module);
1070 }
1071
1072 static int linked_attributes_del_transaction(struct ldb_module *module)
1073 {
1074         struct la_private *la_private =
1075                 talloc_get_type(ldb_module_get_private(module), struct la_private);
1076         talloc_free(la_private);
1077         ldb_module_set_private(module, NULL);
1078         return ldb_next_del_trans(module);
1079 }
1080
1081
1082 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1083         .name              = "linked_attributes",
1084         .add               = linked_attributes_add,
1085         .modify            = linked_attributes_modify,
1086         .rename            = linked_attributes_rename,
1087         .start_transaction = linked_attributes_start_transaction,
1088         .prepare_commit    = linked_attributes_prepare_commit,
1089         .del_transaction   = linked_attributes_del_transaction,
1090 };