s4:dsdb use dsdb_module_modify() rather than ldb_next_request()
[obnox/samba/samba-obnox.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         int ret;
97         NTSTATUS status;
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         int ret;
195         int i, j;
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) || ((schema_attr->linkID & 1) == 1)) {
233                         continue;
234                 }
235                 
236                 /* Even link IDs are for the originating attribute */
237                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
238                 if (!target_attr) {
239                         /*
240                          * windows 2003 has a broken schema where
241                          * the definition of msDS-IsDomainFor
242                          * is missing (which is supposed to be
243                          * the backlink of the msDS-HasDomainNCs
244                          * attribute
245                          */
246                         continue;
247                 }
248
249                 attr_name = target_attr->lDAPDisplayName;
250
251                 for (j = 0; j < el->num_values; j++) {
252                         ret = la_store_op(ac, LA_OP_ADD,
253                                           &el->values[j],
254                                           attr_name);
255                         if (ret != LDB_SUCCESS) {
256                                 return ret;
257                         }
258                 }
259         }
260
261         /* if no linked attributes are present continue */
262         if (ac->ops == NULL) {
263                 /* nothing to do for this module, proceed */
264                 talloc_free(ac);
265                 return ldb_next_request(module, req);
266         }
267
268         /* start with the original request */
269         return la_down_req(ac);
270 }
271
272 /* For a delete or rename, we need to find out what linked attributes
273  * are currently on this DN, and then deal with them.  This is the
274  * callback to the base search */
275
276 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
277 {
278         struct ldb_context *ldb;
279         const struct dsdb_attribute *schema_attr;
280         const struct dsdb_attribute *target_attr;
281         struct ldb_message_element *search_el;
282         struct replace_context *rc;
283         struct la_context *ac;
284         const char *attr_name;
285         int i, j;
286         int ret = LDB_SUCCESS;
287
288         ac = talloc_get_type(req->context, struct la_context);
289         ldb = ldb_module_get_ctx(ac->module);
290         rc = ac->rc;
291
292         if (!ares) {
293                 return ldb_module_done(ac->req, NULL, NULL,
294                                         LDB_ERR_OPERATIONS_ERROR);
295         }
296         if (ares->error != LDB_SUCCESS) {
297                 return ldb_module_done(ac->req, ares->controls,
298                                         ares->response, ares->error);
299         }
300
301         /* Only entries are interesting, and we only want the olddn */
302         switch (ares->type) {
303         case LDB_REPLY_ENTRY:
304
305                 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
306                         ldb_asprintf_errstring(ldb, 
307                                                "linked_attributes: %s is not the DN we were looking for", 
308                                                ldb_dn_get_linearized(ares->message->dn));
309                         /* Guh?  We only asked for this DN */
310                         talloc_free(ares);
311                         return ldb_module_done(ac->req, NULL, NULL,
312                                                 LDB_ERR_OPERATIONS_ERROR);
313                 }
314
315                 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
316
317                 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
318                 for (i = 0; rc && i < rc->num_elements; i++) {
319
320                         schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
321                         if (!schema_attr) {
322                                 ldb_asprintf_errstring(ldb,
323                                         "attribute %s is not a valid attribute in schema",
324                                         rc->el[i].name);
325                                 talloc_free(ares);
326                                 return ldb_module_done(ac->req, NULL, NULL,
327                                                 LDB_ERR_OBJECT_CLASS_VIOLATION);
328                         }
329
330                         search_el = ldb_msg_find_element(ares->message,
331                                                          rc->el[i].name);
332
333                         /* See if this element already exists */
334                         /* otherwise just ignore as
335                          * the add has already been scheduled */
336                         if ( ! search_el) {
337                                 continue;
338                         }
339
340                         target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
341                         if (!target_attr) {
342                                 /*
343                                  * windows 2003 has a broken schema where
344                                  * the definition of msDS-IsDomainFor
345                                  * is missing (which is supposed to be
346                                  * the backlink of the msDS-HasDomainNCs
347                                  * attribute
348                                  */
349                                 continue;
350                         }
351                         attr_name = target_attr->lDAPDisplayName;
352
353                         /* Now we know what was there, we can remove it for the re-add */
354                         for (j = 0; j < search_el->num_values; j++) {
355                                 ret = la_store_op(ac, LA_OP_DEL,
356                                                   &search_el->values[j],
357                                                   attr_name);
358                                 if (ret != LDB_SUCCESS) {
359                                         talloc_free(ares);
360                                         return ldb_module_done(ac->req,
361                                                                NULL, NULL, ret);
362                                 }
363                         }
364                 }
365
366                 break;
367
368         case LDB_REPLY_REFERRAL:
369                 /* ignore */
370                 break;
371
372         case LDB_REPLY_DONE:
373
374                 talloc_free(ares);
375
376                 if (ac->req->operation == LDB_ADD) {
377                         /* Start the modifies to the backlinks */
378                         ret = la_queue_mod_request(ac);
379
380                         if (ret != LDB_SUCCESS) {
381                                 return ldb_module_done(ac->req, NULL, NULL,
382                                                        ret);
383                         }
384                 } else {
385                         /* Start with the original request */
386                         ret = la_down_req(ac);
387                         if (ret != LDB_SUCCESS) {
388                                 return ldb_module_done(ac->req, NULL, NULL, ret);
389                         }
390                 }
391                 return LDB_SUCCESS;
392         }
393
394         talloc_free(ares);
395         return ret;
396 }
397
398
399 /* modify */
400 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
401 {
402         /* Look over list of modifications */
403         /* Find if any are for linked attributes */
404         /* Determine the effect of the modification */
405         /* Apply the modify to the linked entry */
406
407         struct ldb_context *ldb;
408         int i, j;
409         struct la_context *ac;
410         struct ldb_request *search_req;
411         const char **attrs;
412         struct ldb_control *ctrl;
413
414         int ret;
415
416         ldb = ldb_module_get_ctx(module);
417
418         if (ldb_dn_is_special(req->op.mod.message->dn)) {
419                 /* do not manipulate our control entries */
420                 return ldb_next_request(module, req);
421         }
422
423         if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
424                 /* don't do anything special for linked attributes, repl_meta_data has done it */
425                 return ldb_next_request(module, req);
426         }
427         ctrl->critical = false;
428
429         ac = linked_attributes_init(module, req);
430         if (!ac) {
431                 return LDB_ERR_OPERATIONS_ERROR;
432         }
433
434         if (!ac->schema) {
435                 /* without schema, this doesn't make any sense */
436                 return ldb_next_request(module, req);
437         }
438
439         ac->rc = talloc_zero(ac, struct replace_context);
440         if (!ac->rc) {
441                 ldb_oom(ldb);
442                 return LDB_ERR_OPERATIONS_ERROR;
443         }
444
445         for (i=0; i < req->op.mod.message->num_elements; i++) {
446                 bool store_el = false;
447                 const char *attr_name;
448                 const struct dsdb_attribute *target_attr;
449                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
450                 const struct dsdb_attribute *schema_attr
451                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
452                 if (!schema_attr) {
453                         ldb_asprintf_errstring(ldb, 
454                                                "attribute %s is not a valid attribute in schema", el->name);
455                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
456                 }
457                 /* We have a valid attribute, now find out if it is a forward link
458                    (Even link IDs are for the originating attribute) */
459                 if ((schema_attr->linkID == 0) || ((schema_attr->linkID & 1) == 1)) {
460                         continue;
461                 }
462
463                 /* Now find the target attribute */
464                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
465                 if (!target_attr) {
466                         /*
467                          * windows 2003 has a broken schema where
468                          * the definition of msDS-IsDomainFor
469                          * is missing (which is supposed to be
470                          * the backlink of the msDS-HasDomainNCs
471                          * attribute
472                          */
473                         continue;
474                 }
475
476                 attr_name = target_attr->lDAPDisplayName;
477         
478                 switch (el->flags & LDB_FLAG_MOD_MASK) {
479                 case LDB_FLAG_MOD_REPLACE:
480                         /* treat as just a normal add the delete part is handled by the callback */
481                         store_el = true;
482
483                         /* break intentionally missing */
484
485                 case LDB_FLAG_MOD_ADD:
486
487                         /* For each value being added, we need to setup the adds */
488                         for (j = 0; j < el->num_values; j++) {
489                                 ret = la_store_op(ac, LA_OP_ADD,
490                                                   &el->values[j],
491                                                   attr_name);
492                                 if (ret != LDB_SUCCESS) {
493                                         return ret;
494                                 }
495                         }
496                         break;
497
498                 case LDB_FLAG_MOD_DELETE:
499
500                         if (el->num_values) {
501                                 /* For each value being deleted, we need to setup the delete */
502                                 for (j = 0; j < el->num_values; j++) {
503                                         ret = la_store_op(ac, LA_OP_DEL,
504                                                           &el->values[j],
505                                                           attr_name);
506                                         if (ret != LDB_SUCCESS) {
507                                                 return ret;
508                                         }
509                                 }
510                         } else {
511                                 /* Flag that there was a DELETE
512                                  * without a value specified, so we
513                                  * need to look for the old value */
514                                 store_el = true;
515                         }
516
517                         break;
518                 }
519
520                 if (store_el) {
521                         struct ldb_message_element *search_el;
522
523                         search_el = talloc_realloc(ac->rc, ac->rc->el,
524                                                    struct ldb_message_element,
525                                                    ac->rc->num_elements +1);
526                         if (!search_el) {
527                                 ldb_oom(ldb);
528                                 return LDB_ERR_OPERATIONS_ERROR;
529                         }
530                         ac->rc->el = search_el;
531
532                         ac->rc->el[ac->rc->num_elements] = *el;
533                         ac->rc->num_elements++;
534                 }
535         }
536         
537         if (ac->ops || ac->rc->el) {
538                 /* both replace and delete without values are handled in the callback
539                  * after the search on the entry to be modified is performed */
540                 
541                 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
542                 if (!attrs) {
543                         ldb_oom(ldb);
544                         return LDB_ERR_OPERATIONS_ERROR;
545                 }
546                 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
547                         attrs[i] = ac->rc->el[i].name;
548                 }
549                 attrs[i] = NULL;
550                 
551                 /* The callback does all the hard work here */
552                 ret = ldb_build_search_req(&search_req, ldb, ac,
553                                            req->op.mod.message->dn,
554                                            LDB_SCOPE_BASE,
555                                            "(objectClass=*)", attrs,
556                                            NULL,
557                                            ac, la_mod_search_callback,
558                                            req);
559
560                 /* We need to figure out our own extended DN, to fill in as the backlink target */
561                 if (ret == LDB_SUCCESS) {
562                         ret = ldb_request_add_control(search_req,
563                                                       LDB_CONTROL_EXTENDED_DN_OID,
564                                                       false, NULL);
565                 }
566                 if (ret == LDB_SUCCESS) {
567                         talloc_steal(search_req, attrs);
568                         
569                         ret = ldb_next_request(module, search_req);
570                 }
571
572         } else {
573                 /* nothing to do for this module, proceed */
574                 talloc_free(ac);
575                 ret = ldb_next_request(module, req);
576         }
577
578         return ret;
579 }
580
581 static int linked_attributes_fix_links(struct ldb_module *module,
582                                        struct ldb_dn *old_dn, struct ldb_dn *new_dn,
583                                        struct ldb_message_element *el, struct dsdb_schema *schema,
584                                        const struct dsdb_attribute *schema_attr)
585 {
586         unsigned int i;
587         TALLOC_CTX *tmp_ctx = talloc_new(module);
588         struct ldb_context *ldb = ldb_module_get_ctx(module);
589         const struct dsdb_attribute *target;
590         const char *attrs[2];
591
592         target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
593         if (target == NULL) {
594                 /* there is no counterpart link to change */
595                 return LDB_SUCCESS;
596         }
597
598         attrs[0] = target->lDAPDisplayName;
599         attrs[1] = NULL;
600
601         for (i=0; i<el->num_values; i++) {
602                 struct dsdb_dn *dsdb_dn;
603                 unsigned int j;
604                 int ret;
605                 struct ldb_result *res;
606                 struct ldb_message *msg;
607                 struct ldb_message_element *el2;
608
609                 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
610                 if (dsdb_dn == NULL) {
611                         talloc_free(tmp_ctx);
612                         return LDB_ERR_INVALID_DN_SYNTAX;
613                 }
614
615                 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
616                                             attrs,
617                                             DSDB_SEARCH_SHOW_DELETED |
618                                             DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
619                                             DSDB_SEARCH_REVEAL_INTERNALS);
620                 if (ret != LDB_SUCCESS) {
621                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
622                                                el->name, target->lDAPDisplayName,
623                                                ldb_dn_get_linearized(old_dn),
624                                                ldb_dn_get_linearized(dsdb_dn->dn),
625                                                ldb_errstring(ldb));
626                         talloc_free(tmp_ctx);
627                         return ret;
628                 }
629                 msg = res->msgs[0];
630
631                 if (msg->num_elements != 1 ||
632                     ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
633                         ldb_set_errstring(ldb, "Bad msg elements in linked_attributes_fix_links");
634                         talloc_free(tmp_ctx);
635                         return LDB_ERR_OPERATIONS_ERROR;
636                 }
637                 el2 = &msg->elements[0];
638
639                 el2->flags = LDB_FLAG_MOD_REPLACE;
640
641                 /* find our DN in the values */
642                 for (j=0; j<el2->num_values; j++) {
643                         struct dsdb_dn *dsdb_dn2;
644                         dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
645                         if (dsdb_dn2 == NULL) {
646                                 talloc_free(tmp_ctx);
647                                 return LDB_ERR_INVALID_DN_SYNTAX;
648                         }
649                         if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
650                                 continue;
651                         }
652                         ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
653                         if (ret != LDB_SUCCESS) {
654                                 talloc_free(tmp_ctx);
655                                 return ret;
656                         }
657
658                         el2->values[j] = data_blob_string_const(
659                                 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
660                 }
661
662                 ret = dsdb_check_single_valued_link(target, el2);
663                 if (ret != LDB_SUCCESS) {
664                         talloc_free(tmp_ctx);
665                         return ret;
666                 }
667
668                 ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
669                 if (ret != LDB_SUCCESS) {
670                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
671                                                el->name, target->lDAPDisplayName,
672                                                ldb_dn_get_linearized(old_dn),
673                                                ldb_dn_get_linearized(dsdb_dn->dn),
674                                                ldb_errstring(ldb));
675                         talloc_free(tmp_ctx);
676                         return ret;
677                 }
678         }
679
680         talloc_free(tmp_ctx);
681         return LDB_SUCCESS;
682 }
683
684
685 /* rename */
686 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
687 {
688         struct ldb_result *res;
689         struct ldb_message *msg;
690         unsigned int i;
691         int ret;
692         struct ldb_context *ldb = ldb_module_get_ctx(module);
693         struct dsdb_schema *schema;
694         /*
695            - load the current msg
696            - find any linked attributes
697            - if its a link then find the target object
698            - modify the target linked attributes with the new DN
699         */
700         ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
701                                     NULL, DSDB_SEARCH_SHOW_DELETED);
702         if (ret != LDB_SUCCESS) {
703                 return ret;
704         }
705
706         schema = dsdb_get_schema(ldb, res);
707         if (!schema) {
708                 ldb_oom(ldb);
709                 return LDB_ERR_OPERATIONS_ERROR;
710         }
711
712         msg = res->msgs[0];
713
714         for (i=0; i<msg->num_elements; i++) {
715                 struct ldb_message_element *el = &msg->elements[i];
716                 const struct dsdb_attribute *schema_attr
717                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
718                 if (!schema_attr || schema_attr->linkID == 0) {
719                         continue;
720                 }
721                 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
722                                                   schema, schema_attr);
723                 if (ret != LDB_SUCCESS) {
724                         talloc_free(res);
725                         return ret;
726                 }
727         }
728
729         talloc_free(res);
730
731         return ldb_next_request(module, req);
732 }
733
734
735 /* queue a linked attributes modify request in the la_private
736    structure */
737 static int la_queue_mod_request(struct la_context *ac)
738 {
739         struct la_private *la_private = 
740                 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
741
742         if (la_private == NULL) {
743                 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
744                 return LDB_ERR_OPERATIONS_ERROR;
745         }
746
747         talloc_steal(la_private, ac);
748         DLIST_ADD(la_private->la_list, ac);
749
750         return ldb_module_done(ac->req, ac->op_controls,
751                                ac->op_response, LDB_SUCCESS);
752 }
753
754 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
755 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
756 {
757         int ret;
758         struct la_context *ac;
759         struct ldb_context *ldb;
760
761         ac = talloc_get_type(req->context, struct la_context);
762         ldb = ldb_module_get_ctx(ac->module);
763
764         if (!ares) {
765                 return ldb_module_done(ac->req, NULL, NULL,
766                                         LDB_ERR_OPERATIONS_ERROR);
767         }
768         if (ares->error != LDB_SUCCESS) {
769                 return ldb_module_done(ac->req, ares->controls,
770                                         ares->response, ares->error);
771         }
772
773         if (ares->type != LDB_REPLY_DONE) {
774                 ldb_set_errstring(ldb,
775                                   "invalid ldb_reply_type in callback");
776                 talloc_free(ares);
777                 return ldb_module_done(ac->req, NULL, NULL,
778                                         LDB_ERR_OPERATIONS_ERROR);
779         }
780         
781         ac->op_controls = talloc_steal(ac, ares->controls);
782         ac->op_response = talloc_steal(ac, ares->response);
783
784         /* If we have modfies to make, this is the time to do them for modify and delete */
785         ret = la_queue_mod_request(ac);
786         
787         if (ret != LDB_SUCCESS) {
788                 return ldb_module_done(ac->req, NULL, NULL, ret);
789         }
790         talloc_free(ares);
791
792         /* la_queue_mod_request has already sent the callbacks */
793         return LDB_SUCCESS;
794
795 }
796
797 /* Having done the original add, then try to fix up all the linked attributes
798
799   This is done after the add so the links can get the extended DNs correctly.
800  */
801 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
802 {
803         int ret;
804         struct la_context *ac;
805         struct ldb_context *ldb;
806
807         ac = talloc_get_type(req->context, struct la_context);
808         ldb = ldb_module_get_ctx(ac->module);
809
810         if (!ares) {
811                 return ldb_module_done(ac->req, NULL, NULL,
812                                         LDB_ERR_OPERATIONS_ERROR);
813         }
814         if (ares->error != LDB_SUCCESS) {
815                 return ldb_module_done(ac->req, ares->controls,
816                                         ares->response, ares->error);
817         }
818
819         if (ares->type != LDB_REPLY_DONE) {
820                 ldb_set_errstring(ldb,
821                                   "invalid ldb_reply_type in callback");
822                 talloc_free(ares);
823                 return ldb_module_done(ac->req, NULL, NULL,
824                                         LDB_ERR_OPERATIONS_ERROR);
825         }
826         
827         if (ac->ops) {
828                 struct ldb_request *search_req;
829                 static const char *attrs[] = { NULL };
830                 
831                 /* The callback does all the hard work here - we need
832                  * the objectGUID and SID of the added record */
833                 ret = ldb_build_search_req(&search_req, ldb, ac,
834                                            ac->req->op.add.message->dn,
835                                            LDB_SCOPE_BASE,
836                                            "(objectClass=*)", attrs,
837                                            NULL,
838                                            ac, la_mod_search_callback,
839                                            ac->req);
840                 
841                 if (ret == LDB_SUCCESS) {
842                         ret = ldb_request_add_control(search_req,
843                                                       LDB_CONTROL_EXTENDED_DN_OID,
844                                                       false, NULL);
845                 }
846                 if (ret != LDB_SUCCESS) {
847                         return ldb_module_done(ac->req, NULL, NULL,
848                                                ret);
849                 }
850
851                 ac->op_controls = talloc_steal(ac, ares->controls);
852                 ac->op_response = talloc_steal(ac, ares->response);
853
854                 return ldb_next_request(ac->module, search_req);
855                 
856         } else {
857                 return ldb_module_done(ac->req, ares->controls,
858                                        ares->response, ares->error);
859         }
860 }
861
862 /* Reconstruct the original request, but pointing at our local callback to finish things off */
863 static int la_down_req(struct la_context *ac)
864 {
865         struct ldb_request *down_req;
866         int ret;
867         struct ldb_context *ldb;
868
869         ldb = ldb_module_get_ctx(ac->module);
870
871         switch (ac->req->operation) {
872         case LDB_ADD:
873                 ret = ldb_build_add_req(&down_req, ldb, ac,
874                                         ac->req->op.add.message,
875                                         ac->req->controls,
876                                         ac, la_add_callback,
877                                         ac->req);
878                 break;
879         case LDB_MODIFY:
880                 ret = ldb_build_mod_req(&down_req, ldb, ac,
881                                         ac->req->op.mod.message,
882                                         ac->req->controls,
883                                         ac, la_mod_del_callback,
884                                         ac->req);
885                 break;
886         default:
887                 ret = LDB_ERR_OPERATIONS_ERROR;
888         }
889         if (ret != LDB_SUCCESS) {
890                 return ret;
891         }
892
893         return ldb_next_request(ac->module, down_req);
894 }
895
896 /*
897   use the GUID part of an extended DN to find the target DN, in case
898   it has moved
899  */
900 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac, 
901                              struct GUID *guid, struct ldb_dn **dn)
902 {
903         return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn);
904 }
905
906 /* apply one la_context op change */
907 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
908 {
909         struct ldb_message_element *ret_el;
910         struct ldb_message *new_msg;
911         struct ldb_context *ldb;
912         int ret;
913
914         ldb = ldb_module_get_ctx(ac->module);
915
916         /* Create the modify request */
917         new_msg = ldb_msg_new(ac);
918         if (!new_msg) {
919                 ldb_oom(ldb);
920                 return LDB_ERR_OPERATIONS_ERROR;
921         }
922
923         ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
924         if (ret != LDB_SUCCESS) {
925                 return ret;
926         }
927
928         if (op->op == LA_OP_ADD) {
929                 ret = ldb_msg_add_empty(new_msg, op->name,
930                                         LDB_FLAG_MOD_ADD, &ret_el);
931         } else {
932                 ret = ldb_msg_add_empty(new_msg, op->name,
933                                         LDB_FLAG_MOD_DELETE, &ret_el);
934         }
935         if (ret != LDB_SUCCESS) {
936                 return ret;
937         }
938         ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
939         if (!ret_el->values) {
940                 ldb_oom(ldb);
941                 return LDB_ERR_OPERATIONS_ERROR;
942         }
943         ret_el->num_values = 1;
944         if (op->op == LA_OP_ADD) {
945                 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
946         } else {
947                 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
948         }
949
950 #if 0
951         ldb_debug(ldb, LDB_DEBUG_WARNING,
952                   "link on %s %s: %s %s\n", 
953                   ldb_dn_get_linearized(new_msg->dn), ret_el->name, 
954                   ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
955 #endif  
956
957         if (DEBUGLVL(4)) {
958                 DEBUG(4,("Applying linked attribute change:\n%s\n",
959                          ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
960         }
961
962         ret = dsdb_module_modify(module, new_msg, 0);
963         if (ret != LDB_SUCCESS) {
964                 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
965                           ldb_errstring(ldb),
966                           ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
967         }
968
969         return ret;
970 }
971
972 /* apply one set of la_context changes */
973 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
974 {
975         struct la_op_store *op;
976
977         for (op = ac->ops; op; op=op->next) {
978                 int ret = la_do_op_request(module, ac, op);
979                 if (ret != LDB_SUCCESS) {
980                         if (ret != LDB_ERR_NO_SUCH_OBJECT) {
981                                 return ret;
982                         }
983                 }
984         }
985
986         return LDB_SUCCESS;
987 }
988
989
990 /*
991   we hook into the transaction operations to allow us to 
992   perform the linked attribute updates at the end of the whole
993   transaction. This allows a forward linked attribute to be created
994   before the target is created, as long as the target is created
995   in the same transaction
996  */
997 static int linked_attributes_start_transaction(struct ldb_module *module)
998 {
999         /* create our private structure for this transaction */
1000         struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1001                                                         struct la_private);
1002         talloc_free(la_private);
1003         la_private = talloc(module, struct la_private);
1004         if (la_private == NULL) {
1005                 return LDB_ERR_OPERATIONS_ERROR;
1006         }
1007         la_private->la_list = NULL;
1008         ldb_module_set_private(module, la_private);
1009         return ldb_next_start_trans(module);
1010 }
1011
1012 /*
1013   on prepare commit we loop over our queued la_context structures
1014   and apply each of them
1015  */
1016 static int linked_attributes_prepare_commit(struct ldb_module *module)
1017 {
1018         struct la_private *la_private = 
1019                 talloc_get_type(ldb_module_get_private(module), struct la_private);
1020         struct la_context *ac;
1021
1022         if (!la_private) {
1023                 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1024                 return ldb_next_prepare_commit(module);
1025         }
1026         /* walk the list backwards, to do the first entry first, as we
1027          * added the entries with DLIST_ADD() which puts them at the
1028          * start of the list */
1029         for (ac = la_private->la_list; ac && ac->next; ac=ac->next) ;
1030
1031         for (; ac; ac=ac->prev) {
1032                 int ret;
1033                 ac->req = NULL;
1034                 ret = la_do_mod_request(module, ac);
1035                 if (ret != LDB_SUCCESS) {
1036                         DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1037                         talloc_free(la_private);
1038                         ldb_module_set_private(module, NULL);   
1039                         return ret;
1040                 }
1041         }
1042
1043         talloc_free(la_private);
1044         ldb_module_set_private(module, NULL);   
1045
1046         return ldb_next_prepare_commit(module);
1047 }
1048
1049 static int linked_attributes_del_transaction(struct ldb_module *module)
1050 {
1051         struct la_private *la_private = 
1052                 talloc_get_type(ldb_module_get_private(module), struct la_private);
1053         talloc_free(la_private);
1054         ldb_module_set_private(module, NULL);
1055         return ldb_next_del_trans(module);
1056 }
1057
1058
1059 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1060         .name              = "linked_attributes",
1061         .add               = linked_attributes_add,
1062         .modify            = linked_attributes_modify,
1063         .rename            = linked_attributes_rename,
1064         .start_transaction = linked_attributes_start_transaction,
1065         .prepare_commit    = linked_attributes_prepare_commit,
1066         .del_transaction   = linked_attributes_del_transaction,
1067 };