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