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