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