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