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