s4-dsdb: pass parent request to dsdb_module_*() functions
[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         NTSTATUS status;
97         int ret;
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_operr(ldb_module_get_ctx(ac->module));
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                 return ldb_oom(ldb);
142         }
143
144         os->op = op;
145
146         ret = la_guid_from_dn(ac, op_dn, &os->guid);
147         talloc_free(op_dn);
148         if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
149                 /* we are deleting an object, and we've found it has a
150                  * forward link to a target that no longer
151                  * exists. This is not an error in the delete, and we
152                  * should just not do the deferred delete of the
153                  * target attribute
154                  */
155                 talloc_free(os);
156                 return LDB_SUCCESS;
157         }
158         if (ret != LDB_SUCCESS) {
159                 return ret;
160         }
161
162         os->name = talloc_strdup(os, name);
163         if (!os->name) {
164                 return ldb_oom(ldb);
165         }
166
167         /* Do deletes before adds */
168         if (op == LA_OP_ADD) {
169                 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
170         } else {
171                 /* By adding to the head of the list, we do deletes before
172                  * adds when processing a replace */
173                 DLIST_ADD(ac->ops, os);
174         }
175
176         return LDB_SUCCESS;
177 }
178
179 static int la_queue_mod_request(struct la_context *ac);
180 static int la_down_req(struct la_context *ac);
181
182
183
184 /* add */
185 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
186 {
187         struct ldb_context *ldb;
188         const struct dsdb_attribute *target_attr;
189         struct la_context *ac;
190         const char *attr_name;
191         struct ldb_control *ctrl;
192         unsigned int i, j;
193         int ret;
194
195         ldb = ldb_module_get_ctx(module);
196
197         if (ldb_dn_is_special(req->op.add.message->dn)) {
198                 /* do not manipulate our control entries */
199                 return ldb_next_request(module, req);
200         }
201
202         if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
203                 /* don't do anything special for linked attributes, repl_meta_data has done it */
204                 return ldb_next_request(module, req);
205         }
206         ctrl->critical = false;
207
208         ac = linked_attributes_init(module, req);
209         if (!ac) {
210                 return ldb_operr(ldb);
211         }
212
213         if (!ac->schema) {
214                 /* without schema, this doesn't make any sense */
215                 talloc_free(ac);
216                 return ldb_next_request(module, req);
217         }
218
219         /* Need to ensure we only have forward links being specified */
220         for (i=0; i < req->op.add.message->num_elements; i++) {
221                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
222                 const struct dsdb_attribute *schema_attr
223                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
224                 if (!schema_attr) {
225                         ldb_asprintf_errstring(ldb,
226                                                "%s: attribute %s is not a valid attribute in schema",
227                                                __FUNCTION__,
228                                                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         unsigned 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                                         "%s: attribute %s is not a valid attribute in schema",
331                                         __FUNCTION__,
332                                         rc->el[i].name);
333                                 talloc_free(ares);
334                                 return ldb_module_done(ac->req, NULL, NULL,
335                                                 LDB_ERR_OBJECT_CLASS_VIOLATION);
336                         }
337
338                         search_el = ldb_msg_find_element(ares->message,
339                                                          rc->el[i].name);
340
341                         /* See if this element already exists */
342                         /* otherwise just ignore as
343                          * the add has already been scheduled */
344                         if ( ! search_el) {
345                                 continue;
346                         }
347
348                         target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
349                         if (!target_attr) {
350                                 /*
351                                  * windows 2003 has a broken schema where
352                                  * the definition of msDS-IsDomainFor
353                                  * is missing (which is supposed to be
354                                  * the backlink of the msDS-HasDomainNCs
355                                  * attribute
356                                  */
357                                 continue;
358                         }
359                         attr_name = target_attr->lDAPDisplayName;
360
361                         /* Now we know what was there, we can remove it for the re-add */
362                         for (j = 0; j < search_el->num_values; j++) {
363                                 ret = la_store_op(ac, LA_OP_DEL,
364                                                   &search_el->values[j],
365                                                   attr_name);
366                                 if (ret != LDB_SUCCESS) {
367                                         talloc_free(ares);
368                                         return ldb_module_done(ac->req,
369                                                                NULL, NULL, ret);
370                                 }
371                         }
372                 }
373
374                 break;
375
376         case LDB_REPLY_REFERRAL:
377                 /* ignore */
378                 break;
379
380         case LDB_REPLY_DONE:
381
382                 talloc_free(ares);
383
384                 if (ac->req->operation == LDB_ADD) {
385                         /* Start the modifies to the backlinks */
386                         ret = la_queue_mod_request(ac);
387
388                         if (ret != LDB_SUCCESS) {
389                                 return ldb_module_done(ac->req, NULL, NULL,
390                                                        ret);
391                         }
392                 } else {
393                         /* Start with the original request */
394                         ret = la_down_req(ac);
395                         if (ret != LDB_SUCCESS) {
396                                 return ldb_module_done(ac->req, NULL, NULL, ret);
397                         }
398                 }
399                 return LDB_SUCCESS;
400         }
401
402         talloc_free(ares);
403         return ret;
404 }
405
406
407 /* modify */
408 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
409 {
410         /* Look over list of modifications */
411         /* Find if any are for linked attributes */
412         /* Determine the effect of the modification */
413         /* Apply the modify to the linked entry */
414
415         struct ldb_context *ldb;
416         unsigned int i, j;
417         struct la_context *ac;
418         struct ldb_request *search_req;
419         const char **attrs;
420         struct ldb_control *ctrl;
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_operr(ldb);
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                 return ldb_oom(ldb);
449         }
450
451         for (i=0; i < req->op.mod.message->num_elements; i++) {
452                 bool store_el = false;
453                 const char *attr_name;
454                 const struct dsdb_attribute *target_attr;
455                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
456                 const struct dsdb_attribute *schema_attr
457                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
458                 if (!schema_attr) {
459                         ldb_asprintf_errstring(ldb,
460                                                "%s: attribute %s is not a valid attribute in schema",
461                                                __FUNCTION__,
462                                                el->name);
463                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
464                 }
465                 /* We have a valid attribute, now find out if it is a forward link
466                    (Even link IDs are for the originating attribute) */
467                 if (schema_attr->linkID == 0) {
468                         continue;
469                 }
470
471                 if ((schema_attr->linkID & 1) == 1) {
472                         unsigned int functional_level;
473
474                         functional_level = dsdb_functional_level(ldb);
475                         SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
476                 }
477                 /* Now find the target attribute */
478                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
479                 if (!target_attr) {
480                         /*
481                          * windows 2003 has a broken schema where
482                          * the definition of msDS-IsDomainFor
483                          * is missing (which is supposed to be
484                          * the backlink of the msDS-HasDomainNCs
485                          * attribute
486                          */
487                         continue;
488                 }
489
490                 attr_name = target_attr->lDAPDisplayName;
491
492                 switch (el->flags & LDB_FLAG_MOD_MASK) {
493                 case LDB_FLAG_MOD_REPLACE:
494                         /* treat as just a normal add the delete part is handled by the callback */
495                         store_el = true;
496
497                         /* break intentionally missing */
498
499                 case LDB_FLAG_MOD_ADD:
500
501                         /* For each value being added, we need to setup the adds */
502                         for (j = 0; j < el->num_values; j++) {
503                                 ret = la_store_op(ac, LA_OP_ADD,
504                                                   &el->values[j],
505                                                   attr_name);
506                                 if (ret != LDB_SUCCESS) {
507                                         return ret;
508                                 }
509                         }
510                         break;
511
512                 case LDB_FLAG_MOD_DELETE:
513
514                         if (el->num_values) {
515                                 /* For each value being deleted, we need to setup the delete */
516                                 for (j = 0; j < el->num_values; j++) {
517                                         ret = la_store_op(ac, LA_OP_DEL,
518                                                           &el->values[j],
519                                                           attr_name);
520                                         if (ret != LDB_SUCCESS) {
521                                                 return ret;
522                                         }
523                                 }
524                         } else {
525                                 /* Flag that there was a DELETE
526                                  * without a value specified, so we
527                                  * need to look for the old value */
528                                 store_el = true;
529                         }
530
531                         break;
532                 }
533
534                 if (store_el) {
535                         struct ldb_message_element *search_el;
536
537                         search_el = talloc_realloc(ac->rc, ac->rc->el,
538                                                    struct ldb_message_element,
539                                                    ac->rc->num_elements +1);
540                         if (!search_el) {
541                                 return ldb_oom(ldb);
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                         return ldb_oom(ldb);
557                 }
558                 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
559                         attrs[i] = ac->rc->el[i].name;
560                 }
561                 attrs[i] = NULL;
562
563                 /* The callback does all the hard work here */
564                 ret = ldb_build_search_req(&search_req, ldb, ac,
565                                            req->op.mod.message->dn,
566                                            LDB_SCOPE_BASE,
567                                            "(objectClass=*)", attrs,
568                                            NULL,
569                                            ac, la_mod_search_callback,
570                                            req);
571                 LDB_REQ_SET_LOCATION(search_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                                        struct ldb_request *parent)
599 {
600         unsigned int i, j;
601         TALLOC_CTX *tmp_ctx = talloc_new(module);
602         struct ldb_context *ldb = ldb_module_get_ctx(module);
603         const struct dsdb_attribute *target;
604         const char *attrs[2];
605         int ret;
606
607         target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
608         if (target == NULL) {
609                 /* there is no counterpart link to change */
610                 return LDB_SUCCESS;
611         }
612
613         attrs[0] = target->lDAPDisplayName;
614         attrs[1] = NULL;
615
616         for (i=0; i<el->num_values; i++) {
617                 struct dsdb_dn *dsdb_dn;
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_FLAG_NEXT_MODULE |
631                                             DSDB_SEARCH_SHOW_RECYCLED |
632                                             DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
633                                             DSDB_SEARCH_REVEAL_INTERNALS, parent);
634                 if (ret != LDB_SUCCESS) {
635                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
636                                                el->name, target->lDAPDisplayName,
637                                                ldb_dn_get_linearized(old_dn),
638                                                ldb_dn_get_linearized(dsdb_dn->dn),
639                                                ldb_errstring(ldb));
640                         talloc_free(tmp_ctx);
641                         return ret;
642                 }
643                 msg = res->msgs[0];
644
645                 if (msg->num_elements == 0) {
646                         /* Forward link without backlink remaining - nothing to do here */
647                         continue;
648                 } else if (msg->num_elements != 1) {
649                         ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
650                                                msg->num_elements, ldb_dn_get_linearized(msg->dn));
651                         talloc_free(tmp_ctx);
652                         return LDB_ERR_OPERATIONS_ERROR;
653                 }
654                 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
655                         ldb_asprintf_errstring(ldb, "Bad returned attribute in linked_attributes_fix_links: got %s, expected %s for %s", msg->elements[0].name, target->lDAPDisplayName, ldb_dn_get_linearized(msg->dn));
656                         talloc_free(tmp_ctx);
657                         return LDB_ERR_OPERATIONS_ERROR;
658                 }
659                 el2 = &msg->elements[0];
660
661                 el2->flags = LDB_FLAG_MOD_REPLACE;
662
663                 /* find our DN in the values */
664                 for (j=0; j<el2->num_values; j++) {
665                         struct dsdb_dn *dsdb_dn2;
666                         dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
667                         if (dsdb_dn2 == NULL) {
668                                 talloc_free(tmp_ctx);
669                                 return LDB_ERR_INVALID_DN_SYNTAX;
670                         }
671                         if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
672                                 continue;
673                         }
674                         ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
675                         if (ret != LDB_SUCCESS) {
676                                 talloc_free(tmp_ctx);
677                                 return ret;
678                         }
679
680                         el2->values[j] = data_blob_string_const(
681                                 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
682                 }
683
684                 ret = dsdb_check_single_valued_link(target, el2);
685                 if (ret != LDB_SUCCESS) {
686                         talloc_free(tmp_ctx);
687                         return ret;
688                 }
689
690                 /* we may be putting multiple values in an attribute -
691                    disable checking for this attribute */
692                 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
693
694                 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
695                 if (ret != LDB_SUCCESS) {
696                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
697                                                el->name, target->lDAPDisplayName,
698                                                ldb_dn_get_linearized(old_dn),
699                                                ldb_dn_get_linearized(dsdb_dn->dn),
700                                                ldb_errstring(ldb));
701                         talloc_free(tmp_ctx);
702                         return ret;
703                 }
704         }
705
706         talloc_free(tmp_ctx);
707         return LDB_SUCCESS;
708 }
709
710
711 /* rename */
712 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
713 {
714         struct ldb_result *res;
715         struct ldb_message *msg;
716         unsigned int i;
717         struct ldb_context *ldb = ldb_module_get_ctx(module);
718         struct dsdb_schema *schema;
719         int ret;
720         /*
721            - load the current msg
722            - find any linked attributes
723            - if its a link then find the target object
724            - modify the target linked attributes with the new DN
725         */
726         ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
727                                     NULL,
728                                     DSDB_FLAG_NEXT_MODULE |
729                                     DSDB_SEARCH_SHOW_RECYCLED, req);
730         if (ret != LDB_SUCCESS) {
731                 return ret;
732         }
733
734         schema = dsdb_get_schema(ldb, res);
735         if (!schema) {
736                 return ldb_oom(ldb);
737         }
738
739         msg = res->msgs[0];
740
741         for (i=0; i<msg->num_elements; i++) {
742                 struct ldb_message_element *el = &msg->elements[i];
743                 const struct dsdb_attribute *schema_attr
744                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
745                 if (!schema_attr || schema_attr->linkID == 0) {
746                         continue;
747                 }
748                 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
749                                                   schema, schema_attr, req);
750                 if (ret != LDB_SUCCESS) {
751                         talloc_free(res);
752                         return ret;
753                 }
754         }
755
756         talloc_free(res);
757
758         return ldb_next_request(module, req);
759 }
760
761
762 /* queue a linked attributes modify request in the la_private
763    structure */
764 static int la_queue_mod_request(struct la_context *ac)
765 {
766         struct la_private *la_private =
767                 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
768
769         if (la_private == NULL) {
770                 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
771                 return ldb_operr(ldb_module_get_ctx(ac->module));
772         }
773
774         talloc_steal(la_private, ac);
775         DLIST_ADD(la_private->la_list, ac);
776
777         return ldb_module_done(ac->req, ac->op_controls,
778                                ac->op_response, LDB_SUCCESS);
779 }
780
781 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
782 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
783 {
784         struct la_context *ac;
785         struct ldb_context *ldb;
786         int ret;
787
788         ac = talloc_get_type(req->context, struct la_context);
789         ldb = ldb_module_get_ctx(ac->module);
790
791         if (!ares) {
792                 return ldb_module_done(ac->req, NULL, NULL,
793                                         LDB_ERR_OPERATIONS_ERROR);
794         }
795         if (ares->error != LDB_SUCCESS) {
796                 return ldb_module_done(ac->req, ares->controls,
797                                         ares->response, ares->error);
798         }
799
800         if (ares->type != LDB_REPLY_DONE) {
801                 ldb_set_errstring(ldb,
802                                   "invalid ldb_reply_type in callback");
803                 talloc_free(ares);
804                 return ldb_module_done(ac->req, NULL, NULL,
805                                         LDB_ERR_OPERATIONS_ERROR);
806         }
807
808         ac->op_controls = talloc_steal(ac, ares->controls);
809         ac->op_response = talloc_steal(ac, ares->response);
810
811         /* If we have modfies to make, this is the time to do them for modify and delete */
812         ret = la_queue_mod_request(ac);
813
814         if (ret != LDB_SUCCESS) {
815                 return ldb_module_done(ac->req, NULL, NULL, ret);
816         }
817         talloc_free(ares);
818
819         /* la_queue_mod_request has already sent the callbacks */
820         return LDB_SUCCESS;
821
822 }
823
824 /* Having done the original add, then try to fix up all the linked attributes
825
826   This is done after the add so the links can get the extended DNs correctly.
827  */
828 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
829 {
830         struct la_context *ac;
831         struct ldb_context *ldb;
832         int ret;
833
834         ac = talloc_get_type(req->context, struct la_context);
835         ldb = ldb_module_get_ctx(ac->module);
836
837         if (!ares) {
838                 return ldb_module_done(ac->req, NULL, NULL,
839                                         LDB_ERR_OPERATIONS_ERROR);
840         }
841         if (ares->error != LDB_SUCCESS) {
842                 return ldb_module_done(ac->req, ares->controls,
843                                         ares->response, ares->error);
844         }
845
846         if (ares->type != LDB_REPLY_DONE) {
847                 ldb_set_errstring(ldb,
848                                   "invalid ldb_reply_type in callback");
849                 talloc_free(ares);
850                 return ldb_module_done(ac->req, NULL, NULL,
851                                         LDB_ERR_OPERATIONS_ERROR);
852         }
853
854         if (ac->ops) {
855                 struct ldb_request *search_req;
856                 static const char *attrs[] = { NULL };
857
858                 /* The callback does all the hard work here - we need
859                  * the objectGUID and SID of the added record */
860                 ret = ldb_build_search_req(&search_req, ldb, ac,
861                                            ac->req->op.add.message->dn,
862                                            LDB_SCOPE_BASE,
863                                            "(objectClass=*)", attrs,
864                                            NULL,
865                                            ac, la_mod_search_callback,
866                                            ac->req);
867                 LDB_REQ_SET_LOCATION(search_req);
868
869                 if (ret == LDB_SUCCESS) {
870                         ret = ldb_request_add_control(search_req,
871                                                       LDB_CONTROL_EXTENDED_DN_OID,
872                                                       false, NULL);
873                 }
874                 if (ret != LDB_SUCCESS) {
875                         return ldb_module_done(ac->req, NULL, NULL,
876                                                ret);
877                 }
878
879                 ac->op_controls = talloc_steal(ac, ares->controls);
880                 ac->op_response = talloc_steal(ac, ares->response);
881
882                 return ldb_next_request(ac->module, search_req);
883
884         } else {
885                 return ldb_module_done(ac->req, ares->controls,
886                                        ares->response, ares->error);
887         }
888 }
889
890 /* Reconstruct the original request, but pointing at our local callback to finish things off */
891 static int la_down_req(struct la_context *ac)
892 {
893         struct ldb_request *down_req;
894         struct ldb_context *ldb;
895         int ret;
896
897         ldb = ldb_module_get_ctx(ac->module);
898
899         switch (ac->req->operation) {
900         case LDB_ADD:
901                 ret = ldb_build_add_req(&down_req, ldb, ac,
902                                         ac->req->op.add.message,
903                                         ac->req->controls,
904                                         ac, la_add_callback,
905                                         ac->req);
906                 LDB_REQ_SET_LOCATION(down_req);
907                 break;
908         case LDB_MODIFY:
909                 ret = ldb_build_mod_req(&down_req, ldb, ac,
910                                         ac->req->op.mod.message,
911                                         ac->req->controls,
912                                         ac, la_mod_del_callback,
913                                         ac->req);
914                 LDB_REQ_SET_LOCATION(down_req);
915                 break;
916         default:
917                 ret = LDB_ERR_OPERATIONS_ERROR;
918         }
919         if (ret != LDB_SUCCESS) {
920                 return ret;
921         }
922
923         return ldb_next_request(ac->module, down_req);
924 }
925
926 /*
927   use the GUID part of an extended DN to find the target DN, in case
928   it has moved
929  */
930 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
931                              struct GUID *guid, struct ldb_dn **dn)
932 {
933         return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn);
934 }
935
936 /* apply one la_context op change */
937 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
938 {
939         struct ldb_message_element *ret_el;
940         struct ldb_message *new_msg;
941         struct ldb_context *ldb;
942         int ret;
943
944         ldb = ldb_module_get_ctx(ac->module);
945
946         /* Create the modify request */
947         new_msg = ldb_msg_new(ac);
948         if (!new_msg) {
949                 return ldb_oom(ldb);
950         }
951
952         ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
953         if (ret != LDB_SUCCESS) {
954                 return ret;
955         }
956
957         if (op->op == LA_OP_ADD) {
958                 ret = ldb_msg_add_empty(new_msg, op->name,
959                                         LDB_FLAG_MOD_ADD, &ret_el);
960         } else {
961                 ret = ldb_msg_add_empty(new_msg, op->name,
962                                         LDB_FLAG_MOD_DELETE, &ret_el);
963         }
964         if (ret != LDB_SUCCESS) {
965                 return ret;
966         }
967         ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
968         if (!ret_el->values) {
969                 return ldb_oom(ldb);
970         }
971         ret_el->num_values = 1;
972         if (op->op == LA_OP_ADD) {
973                 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
974         } else {
975                 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
976         }
977
978 #if 0
979         ldb_debug(ldb, LDB_DEBUG_WARNING,
980                   "link on %s %s: %s %s\n",
981                   ldb_dn_get_linearized(new_msg->dn), ret_el->name,
982                   ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
983 #endif
984
985         if (DEBUGLVL(4)) {
986                 DEBUG(4,("Applying linked attribute change:\n%s\n",
987                          ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
988         }
989
990         ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
991         if (ret != LDB_SUCCESS) {
992                 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
993                           ldb_errstring(ldb),
994                           ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
995         }
996
997         return ret;
998 }
999
1000 /* apply one set of la_context changes */
1001 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1002 {
1003         struct la_op_store *op;
1004
1005         for (op = ac->ops; op; op=op->next) {
1006                 int ret = la_do_op_request(module, ac, op);
1007                 if (ret != LDB_SUCCESS) {
1008                         if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1009                                 return ret;
1010                         }
1011                 }
1012         }
1013
1014         return LDB_SUCCESS;
1015 }
1016
1017
1018 /*
1019   we hook into the transaction operations to allow us to
1020   perform the linked attribute updates at the end of the whole
1021   transaction. This allows a forward linked attribute to be created
1022   before the target is created, as long as the target is created
1023   in the same transaction
1024  */
1025 static int linked_attributes_start_transaction(struct ldb_module *module)
1026 {
1027         /* create our private structure for this transaction */
1028         struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1029                                                         struct la_private);
1030         talloc_free(la_private);
1031         la_private = talloc(module, struct la_private);
1032         if (la_private == NULL) {
1033                 return ldb_oom(ldb_module_get_ctx(module));
1034         }
1035         la_private->la_list = NULL;
1036         ldb_module_set_private(module, la_private);
1037         return ldb_next_start_trans(module);
1038 }
1039
1040 /*
1041   on prepare commit we loop over our queued la_context structures
1042   and apply each of them
1043  */
1044 static int linked_attributes_prepare_commit(struct ldb_module *module)
1045 {
1046         struct la_private *la_private =
1047                 talloc_get_type(ldb_module_get_private(module), struct la_private);
1048         struct la_context *ac;
1049
1050         if (!la_private) {
1051                 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1052                 return ldb_next_prepare_commit(module);
1053         }
1054         /* walk the list backwards, to do the first entry first, as we
1055          * added the entries with DLIST_ADD() which puts them at the
1056          * start of the list */
1057
1058         /* Start at the end of the list - so we can start
1059          * there, but ensure we don't create a loop by NULLing
1060          * it out in the first element */
1061         ac = DLIST_TAIL(la_private->la_list);
1062
1063         for (; ac; ac=DLIST_PREV(ac)) {
1064                 int ret;
1065                 ac->req = NULL;
1066                 ret = la_do_mod_request(module, ac);
1067                 if (ret != LDB_SUCCESS) {
1068                         DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1069                         talloc_free(la_private);
1070                         ldb_module_set_private(module, NULL);
1071                         return ret;
1072                 }
1073         }
1074
1075         talloc_free(la_private);
1076         ldb_module_set_private(module, NULL);
1077
1078         return ldb_next_prepare_commit(module);
1079 }
1080
1081 static int linked_attributes_del_transaction(struct ldb_module *module)
1082 {
1083         struct la_private *la_private =
1084                 talloc_get_type(ldb_module_get_private(module), struct la_private);
1085         talloc_free(la_private);
1086         ldb_module_set_private(module, NULL);
1087         return ldb_next_del_trans(module);
1088 }
1089
1090
1091 static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1092         .name              = "linked_attributes",
1093         .add               = linked_attributes_add,
1094         .modify            = linked_attributes_modify,
1095         .rename            = linked_attributes_rename,
1096         .start_transaction = linked_attributes_start_transaction,
1097         .prepare_commit    = linked_attributes_prepare_commit,
1098         .del_transaction   = linked_attributes_del_transaction,
1099 };
1100
1101 int ldb_linked_attributes_module_init(const char *version)
1102 {
1103         LDB_MODULE_CHECK_VERSION(version);
1104         return ldb_register_module(&ldb_linked_attributes_module_ops);
1105 }