Use extended DN parsing in LDB
[abartlet/samba.git/.git] / source4 / dsdb / samdb / ldb_modules / extended_dn.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce 2005-2008
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-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 extended dn control module
25  *
26  *  Description: this module builds a special dn for returned search
27  *  results nad creates the special DN in the backend store for new
28  *  values.
29  *
30  *  Authors: Simo Sorce
31  *           Andrew Bartlett
32  */
33
34 #include "includes.h"
35 #include "ldb/include/ldb.h"
36 #include "ldb/include/ldb_errors.h"
37 #include "ldb/include/ldb_private.h"
38 #include "librpc/gen_ndr/ndr_misc.h"
39 #include "dsdb/samdb/samdb.h"
40 #include "libcli/security/security.h"
41
42 #include <time.h>
43
44 struct extended_dn_replace_list {
45         struct extended_dn_replace_list *next;
46         struct ldb_dn *dn;
47         TALLOC_CTX *mem_ctx;
48         struct ldb_val *replace_dn;
49         struct extended_dn_context *ac;
50         struct ldb_request *search_req;
51 };
52
53
54 struct extended_dn_context {
55         const struct dsdb_schema *schema;
56         struct ldb_module *module;
57         struct ldb_request *req;
58
59         struct extended_dn_replace_list *ops;
60         struct extended_dn_replace_list *cur;
61 };
62
63
64 static struct extended_dn_context *extended_dn_context_init(struct ldb_module *module,
65                                                             struct ldb_request *req)
66 {
67         struct extended_dn_context *ac;
68
69         ac = talloc_zero(req, struct extended_dn_context);
70         if (ac == NULL) {
71                 ldb_set_errstring(module->ldb, "Out of Memory");
72                 return NULL;
73         }
74
75         ac->schema = dsdb_get_schema(module->ldb);
76         ac->module = module;
77         ac->req = req;
78
79         return ac;
80 }
81
82 static bool is_attr_in_list(const char * const * attrs, const char *attr)
83 {
84         int i;
85
86         for (i = 0; attrs[i]; i++) {
87                 if (strcasecmp(attrs[i], attr) == 0)
88                         return true;
89         }
90
91         return false;
92 }
93
94 static char **copy_attrs(void *mem_ctx, const char * const * attrs)
95 {
96         char **new;
97         int i, num;
98
99         for (num = 0; attrs[num]; num++);
100
101         new = talloc_array(mem_ctx, char *, num + 1);
102         if (!new) return NULL;
103
104         for(i = 0; i < num; i++) {
105                 new[i] = talloc_strdup(new, attrs[i]);
106                 if (!new[i]) {
107                         talloc_free(new);
108                         return NULL;
109                 }
110         }
111         new[i] = NULL;
112
113         return new;
114 }
115
116 static bool add_attrs(void *mem_ctx, char ***attrs, const char *attr)
117 {
118         char **new;
119         int num;
120
121         for (num = 0; (*attrs)[num]; num++);
122
123         new = talloc_realloc(mem_ctx, *attrs, char *, num + 2);
124         if (!new) return false;
125
126         *attrs = new;
127
128         new[num] = talloc_strdup(new, attr);
129         if (!new[num]) return false;
130
131         new[num + 1] = NULL;
132
133         return true;
134 }
135
136 static int inject_extended_dn(struct ldb_reply *ares,
137                               struct ldb_context *ldb,
138                               int type,
139                               bool remove_guid,
140                               bool remove_sid)
141 {
142         int ret;
143         const struct ldb_val *val;
144         const DATA_BLOB *guid_blob;
145         const DATA_BLOB *sid_blob;
146
147         guid_blob = ldb_msg_find_ldb_val(ares->message, "objectGUID");
148         sid_blob = ldb_msg_find_ldb_val(ares->message, "objectSID");
149
150         if (!guid_blob) {
151                 return LDB_ERR_OPERATIONS_ERROR;
152         }
153
154         ret = ldb_dn_set_extended_component(ares->message->dn, "GUID", guid_blob);
155         if (ret != LDB_SUCCESS) {
156                 return ret;
157         }
158         if (sid_blob) {
159                 ret = ldb_dn_set_extended_component(ares->message->dn, "SID", sid_blob);
160                 if (ret != LDB_SUCCESS) {
161                         return ret;
162                 }
163         }
164
165         if (remove_guid) {
166                 ldb_msg_remove_attr(ares->message, "objectGUID");
167         }
168
169         if (sid_blob && remove_sid) {
170                 ldb_msg_remove_attr(ares->message, "objectSID");
171         }
172
173         val = ldb_msg_find_ldb_val(ares->message, "distinguishedName");
174         if (val) {
175                 ldb_msg_remove_attr(ares->message, "distinguishedName");
176                 ret = ldb_msg_add_steal_string(ares->message, "distinguishedName", 
177                                                ldb_dn_extended_linearized(ares->message, ares->message->dn, type));
178                 if (ret != LDB_SUCCESS) {
179                         return LDB_ERR_OPERATIONS_ERROR;
180                 }
181         }
182         return LDB_SUCCESS;
183 }
184
185 /* search */
186 struct extended_search_context {
187         struct ldb_module *module;
188         const struct dsdb_schema *schema;
189         struct ldb_request *req;
190         struct ldb_control *control;
191         struct ldb_dn *basedn;
192         char *wellknown_object;
193         bool inject;
194         bool remove_guid;
195         bool remove_sid;
196         int extended_type;
197         const char * const *cast_attrs;
198 };
199
200 static int extended_callback(struct ldb_request *req, struct ldb_reply *ares)
201 {
202         struct extended_search_context *ac;
203         int ret, i, j;
204         struct ldb_message *msg = ares->message;
205
206         ac = talloc_get_type(req->context, struct extended_search_context);
207
208         if (!ares) {
209                 return ldb_module_done(ac->req, NULL, NULL,
210                                         LDB_ERR_OPERATIONS_ERROR);
211         }
212         if (ares->error != LDB_SUCCESS) {
213                 return ldb_module_done(ac->req, ares->controls,
214                                         ares->response, ares->error);
215         }
216
217         switch (ares->type) {
218         case LDB_REPLY_REFERRAL:
219                 return ldb_module_send_referral(ac->req, ares->referral);
220
221         case LDB_REPLY_DONE:
222                 return ldb_module_done(ac->req, ares->controls,
223                                         ares->response, LDB_SUCCESS);
224         case LDB_REPLY_ENTRY:
225                 break;
226         }
227
228         if (ac->inject) {
229                 /* for each record returned post-process to add any derived
230                    attributes that have been asked for */
231                 ret = inject_extended_dn(ares, ac->module->ldb,
232                                          ac->extended_type, ac->remove_guid,
233                                          ac->remove_sid);
234                 if (ret != LDB_SUCCESS) {
235                         return ldb_module_done(ac->req, NULL, NULL, ret);
236                 }
237         }
238         
239         for (i = 0; i < msg->num_elements; i++) {
240                 const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(ac->schema, msg->elements[i].name);
241                 if (!attribute) {
242                         continue;
243                 }
244                 /* Look to see if this attributeSyntax is a DN */
245                 if (!((strcmp(attribute->attributeSyntax_oid, "2.5.5.1") == 0) ||
246                       (strcmp(attribute->attributeSyntax_oid, "2.5.5.7") == 0))) {
247                         continue;
248                 }
249                 for (j = 0; j < msg->elements[i].num_values; j++) {
250                         const char *dn_str;
251                         struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, ac->module->ldb, &msg->elements[i].values[j]);
252                         if (!dn) {
253                                 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
254                         }
255
256                         if (!ac->inject) {
257                                 dn_str = talloc_steal(msg->elements[i].values, 
258                                                       ldb_dn_get_linearized(dn));
259                         } else {
260                                 dn_str = talloc_steal(msg->elements[i].values, 
261                                                       ldb_dn_extended_linearized(msg->elements[i].values, 
262                                                                                  dn, ac->extended_type));
263                         }
264                         msg->elements[i].values[j] = data_blob_string_const(dn_str);
265                         talloc_free(dn);
266                 }
267         }
268         return ldb_module_send_entry(ac->req, msg);
269 }
270
271 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
272 {
273         struct extended_search_context *ac;
274         struct ldb_request *down_req;
275         struct ldb_control **saved_controls;
276         struct ldb_message_element *el;
277         int ret;
278         size_t i;
279         size_t wkn_len = 0;
280         char *valstr = NULL;
281         const char *found = NULL;
282
283         ac = talloc_get_type(req->context, struct extended_search_context);
284
285         if (!ares) {
286                 return ldb_module_done(ac->req, NULL, NULL,
287                                         LDB_ERR_OPERATIONS_ERROR);
288         }
289         if (ares->error != LDB_SUCCESS) {
290                 return ldb_module_done(ac->req, ares->controls,
291                                         ares->response, ares->error);
292         }
293
294         switch (ares->type) {
295         case LDB_REPLY_ENTRY:
296                 if (!ac->wellknown_object) {
297                         ac->basedn = ares->message->dn;
298                         break;
299                 }
300
301                 wkn_len = strlen(ac->wellknown_object);
302
303                 el = ldb_msg_find_element(ares->message, "wellKnownObjects");
304                 if (!el) {
305                         ac->basedn = NULL;
306                         break;
307                 }
308
309                 for (i=0; i < el->num_values; i++) {
310                         valstr = talloc_strndup(ac,
311                                                 (const char *)el->values[i].data,
312                                                 el->values[i].length);
313                         if (!valstr) {
314                                 ldb_oom(ac->module->ldb);
315                                 return ldb_module_done(ac->req, NULL, NULL,
316                                                        LDB_ERR_OPERATIONS_ERROR);
317                         }
318
319                         if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
320                                 talloc_free(valstr);
321                                 continue;
322                         }
323
324                         found = &valstr[wkn_len];
325                         break;
326                 }
327
328                 if (!found) {
329                         break;
330                 }
331
332                 ac->basedn = ldb_dn_new(ac, ac->module->ldb, found);
333                 talloc_free(valstr);
334                 if (!ac->basedn) {
335                         ldb_oom(ac->module->ldb);
336                         return ldb_module_done(ac->req, NULL, NULL,
337                                                LDB_ERR_OPERATIONS_ERROR);
338                 }
339
340                 break;
341
342         case LDB_REPLY_REFERRAL:
343                 break;
344
345         case LDB_REPLY_DONE:
346
347                 if (!ac->basedn) {
348                         const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
349                                                           ldb_dn_get_linearized(ac->req->op.search.base));
350                         ldb_set_errstring(ac->module->ldb, str);
351                         return ldb_module_done(ac->req, NULL, NULL,
352                                                LDB_ERR_NO_SUCH_OBJECT);
353                 }
354
355                 ret = ldb_build_search_req_ex(&down_req,
356                                                 ac->module->ldb, ac,
357                                                 ac->basedn,
358                                                 ac->req->op.search.scope,
359                                                 ac->req->op.search.tree,
360                                                 ac->cast_attrs,
361                                                 ac->req->controls,
362                                                 ac, extended_callback,
363                                                 ac->req);
364                 if (ret != LDB_SUCCESS) {
365                         return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
366                 }
367
368                 if (ac->control) {
369                         /* save it locally and remove it from the list */
370                         /* we do not need to replace them later as we
371                          * are keeping the original req intact */
372                         if (!save_controls(ac->control, down_req, &saved_controls)) {
373                                 return ldb_module_done(ac->req, NULL, NULL,
374                                                         LDB_ERR_OPERATIONS_ERROR);
375                         }
376                 }
377
378                 /* perform the search */
379                 return ldb_next_request(ac->module, down_req);
380         }
381         return LDB_SUCCESS;
382 }
383
384 static int extended_dn_search(struct ldb_module *module, struct ldb_request *req)
385 {
386         struct ldb_control *control;
387         struct ldb_extended_dn_control *extended_ctrl = NULL;
388         struct ldb_control **saved_controls;
389         struct extended_search_context *ac;
390         struct ldb_request *down_req;
391         char **new_attrs;
392         int ret;
393         struct ldb_dn *base_dn = NULL;
394         enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
395         const char *base_dn_filter = NULL;
396         const char * const *base_dn_attrs = NULL;
397         char *wellknown_object = NULL;
398         static const char *dnattr[] = {
399                 "distinguishedName",
400                 NULL
401         };
402         static const char *wkattr[] = {
403                 "wellKnownObjects",
404                 NULL
405         };
406
407         if (ldb_dn_has_extended(req->op.search.base)) {
408                 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
409                 struct ldb_dn *dn = req->op.search.base;
410
411                 sid_val = ldb_dn_get_extended_component(dn, "SID");
412                 guid_val = ldb_dn_get_extended_component(dn, "GUID");
413                 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
414
415                 if (sid_val) {
416                         /* TODO: do a search over all partitions */
417                         base_dn = ldb_get_default_basedn(module->ldb);
418                         base_dn_filter = talloc_asprintf(req, "(objectSid=%s)", 
419                                                          ldb_binary_encode(req, *sid_val));
420                         if (!base_dn_filter) {
421                                 ldb_oom(module->ldb);
422                                 return LDB_ERR_OPERATIONS_ERROR;
423                         }
424                         base_dn_scope = LDB_SCOPE_SUBTREE;
425                         base_dn_attrs = dnattr;
426
427                 } else if (guid_val) {
428
429                         /* TODO: do a search over all partitions */
430                         base_dn = ldb_get_default_basedn(module->ldb);
431                         base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)", 
432                                                          ldb_binary_encode(req, *guid_val));
433                         if (!base_dn_filter) {
434                                 ldb_oom(module->ldb);
435                                 return LDB_ERR_OPERATIONS_ERROR;
436                         }
437                         base_dn_scope = LDB_SCOPE_SUBTREE;
438                         base_dn_attrs = dnattr;
439
440
441                 } else if (wkguid_val) {
442                         char *wkguid_dup;
443                         char *tail_str;
444                         char *p;
445
446                         wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
447
448                         p = strchr(wkguid_dup, ',');
449                         if (!p) {
450                                 return LDB_ERR_INVALID_DN_SYNTAX;
451                         }
452
453                         p[0] = '\0';
454                         p++;
455
456                         wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
457                         if (!wellknown_object) {
458                                 ldb_oom(module->ldb);
459                                 return LDB_ERR_OPERATIONS_ERROR;
460                         }
461
462                         tail_str = p;
463                         p = strchr(tail_str, '>');
464                         if (!p) {
465                                 return LDB_ERR_INVALID_DN_SYNTAX;
466                         }
467                         p[0] = '\0';
468
469                         base_dn = ldb_dn_new(req, module->ldb, tail_str);
470                         talloc_free(wkguid_dup);
471                         if (!base_dn) {
472                                 ldb_oom(module->ldb);
473                                 return LDB_ERR_OPERATIONS_ERROR;
474                         }
475                         base_dn_filter = talloc_strdup(req, "(objectClass=*)");
476                         if (!base_dn_filter) {
477                                 ldb_oom(module->ldb);
478                                 return LDB_ERR_OPERATIONS_ERROR;
479                         }
480                         base_dn_scope = LDB_SCOPE_BASE;
481                         base_dn_attrs = wkattr;
482                 }
483         }
484
485         /* check if there's an extended dn control */
486         control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
487         if (control && control->data) {
488                 extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control);
489                 if (!extended_ctrl) {
490                         return LDB_ERR_PROTOCOL_ERROR;
491                 }
492         }
493
494         ac = talloc_zero(req, struct extended_search_context);
495         if (ac == NULL) {
496                 ldb_oom(module->ldb);
497                 return LDB_ERR_OPERATIONS_ERROR;
498         }
499
500         ac->module = module;
501         ac->schema = dsdb_get_schema(module->ldb);
502         ac->req = req;
503         ac->control = control;
504         ac->basedn = NULL;
505         ac->wellknown_object = wellknown_object;
506         ac->inject = false;
507         ac->remove_guid = false;
508         ac->remove_sid = false;
509
510         if (!ac->schema) {
511                 /* no schema yet? go on */
512                 talloc_free(ac);
513                 return ldb_next_request(module, req);
514         }
515
516         if (control) {
517                 ac->inject = true;
518                 if (extended_ctrl) {
519                         ac->extended_type = extended_ctrl->type;
520                 } else {
521                         ac->extended_type = 0;
522                 }
523
524                 /* check if attrs only is specified, in that case check wether we need to modify them */
525                 if (req->op.search.attrs) {
526                         if (! is_attr_in_list(req->op.search.attrs, "objectGUID")) {
527                                 ac->remove_guid = true;
528                         }
529                         if (! is_attr_in_list(req->op.search.attrs, "objectSID")) {
530                                 ac->remove_sid = true;
531                         }
532                         if (ac->remove_guid || ac->remove_sid) {
533                                 new_attrs = copy_attrs(ac, req->op.search.attrs);
534                                 if (new_attrs == NULL) {
535                                         ldb_oom(module->ldb);
536                                         return LDB_ERR_OPERATIONS_ERROR;
537                                 }
538
539                                 if (ac->remove_guid) {
540                                         if (!add_attrs(ac, &new_attrs, "objectGUID"))
541                                                 return LDB_ERR_OPERATIONS_ERROR;
542                                 }
543                                 if (ac->remove_sid) {
544                                         if (!add_attrs(ac, &new_attrs, "objectSID"))
545                                                 return LDB_ERR_OPERATIONS_ERROR;
546                                 }
547                                 ac->cast_attrs = (const char * const *)new_attrs;
548                         } else {
549                                 ac->cast_attrs = req->op.search.attrs;
550                         }
551                 }
552         }
553
554         /* If the base DN was an extended DN (perhaps a well known
555          * GUID) then re-create the search */
556         if (base_dn) {
557                 ret = ldb_build_search_req(&down_req,
558                                            module->ldb, ac,
559                                            base_dn,
560                                            base_dn_scope,
561                                            base_dn_filter,
562                                            base_dn_attrs,
563                                            NULL,
564                                            ac, extended_base_callback,
565                                            req);
566                 if (ret != LDB_SUCCESS) {
567                         return LDB_ERR_OPERATIONS_ERROR;
568                 }
569
570                 /* perform the search */
571                 return ldb_next_request(module, down_req);
572         }
573
574         ret = ldb_build_search_req_ex(&down_req,
575                                         module->ldb, ac,
576                                         req->op.search.base,
577                                         req->op.search.scope,
578                                         req->op.search.tree,
579                                         ac->cast_attrs,
580                                         req->controls,
581                                         ac, extended_callback,
582                                         req);
583         if (ret != LDB_SUCCESS) {
584                 return LDB_ERR_OPERATIONS_ERROR;
585         }
586
587         if (ac->control) {
588                 /* save it locally and remove it from the list */
589                 /* we do not need to replace them later as we
590                  * are keeping the original req intact */
591                 if (!save_controls(control, down_req, &saved_controls)) {
592                         return LDB_ERR_OPERATIONS_ERROR;
593                 }
594         }
595
596         /* perform the search */
597         return ldb_next_request(module, down_req);
598 }
599
600 static int extended_replace_dn(struct ldb_request *req, struct ldb_reply *ares)
601 {
602         struct extended_dn_replace_list *os = talloc_get_type(req->context, 
603                                                            struct extended_dn_replace_list);
604
605         if (!ares) {
606                 return ldb_module_done(os->ac->req, NULL, NULL,
607                                         LDB_ERR_OPERATIONS_ERROR);
608         }
609         if (ares->error != LDB_SUCCESS) {
610                 return ldb_module_done(os->ac->req, ares->controls,
611                                         ares->response, ares->error);
612         }
613
614         /* Only entries are interesting, and we only want the olddn */
615         switch (ares->type) {
616         case LDB_REPLY_ENTRY:
617         {
618                 const struct ldb_val *sid = ldb_msg_find_ldb_val(ares->message, "objectSid");
619                 const struct ldb_val *guid = ldb_msg_find_ldb_val(ares->message, "objectGUID");
620                 struct ldb_dn *dn = ares->message->dn;
621                 int ret = ldb_dn_compare(dn, req->op.search.base);
622                 if (ret != 0) {
623                         /* Guh?  We only asked for this DN */
624                         talloc_free(ares);
625                         return ldb_module_done(os->ac->req, NULL, NULL,
626                                                 LDB_ERR_OPERATIONS_ERROR);
627                 }
628
629                 ret = ldb_dn_set_extended_component(dn, "GUID", guid);
630                 if (ret != LDB_SUCCESS) {
631                         return ret;
632                 }
633                 if (sid) {
634                         ret = ldb_dn_set_extended_component(dn, "SID", sid);
635                         if (ret != LDB_SUCCESS) {
636                                 return ret;
637                         }
638                 }
639
640                 *os->replace_dn = data_blob_string_const(
641                         ldb_dn_extended_linearized(os->mem_ctx, 
642                                                    dn, 1));
643                 if (os->replace_dn->data != NULL) {
644                         return LDB_ERR_OPERATIONS_ERROR;
645                 }
646         }
647         case LDB_REPLY_REFERRAL:
648                 /* ignore */
649                 break;
650
651         case LDB_REPLY_DONE:
652
653                 talloc_free(ares);
654                 
655                 /* Run the next search */
656
657                 if (os->next) {
658                         struct extended_dn_replace_list *next;
659
660                         next = os->next;
661
662                         talloc_free(os);
663
664                         os = next;
665                         return ldb_next_request(os->ac->module, next->search_req);
666                 } else {
667                         /* Otherwise, we are done - let's run the
668                          * request now we have swapped the DNs for the
669                          * full versions */
670                         return ldb_next_request(os->ac->module, os->ac->req);
671                 }
672         }
673
674         talloc_free(ares);
675         return LDB_SUCCESS;
676 }
677
678 /* We have a 'normal' DN in the inbound request.  We need to find out
679  * what the GUID and SID are on the DN it points to, so we can
680  * construct an extended DN for storage.
681  *
682  * This creates a list of DNs to look up, and the plain DN to replace
683  */
684
685 static int extended_store_replace(struct extended_dn_context *ac,
686                                   TALLOC_CTX *callback_mem_ctx,
687                                   struct ldb_val *plain_dn)
688 {
689         int ret;
690         struct extended_dn_replace_list *os;
691         static const char *attrs[] = {
692                 "objectSid",
693                 "objectGUID",
694                 NULL
695         };
696
697         struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, ac->module->ldb, plain_dn);
698         if (!dn || !ldb_dn_validate(dn)) {
699                 ldb_asprintf_errstring(ac->module->ldb, 
700                                        "could not parse %.*s as a DN", (int)plain_dn->length, plain_dn->data);
701                 return LDB_ERR_INVALID_DN_SYNTAX;
702         }
703
704         os = talloc_zero(ac, struct extended_dn_replace_list);
705         if (!os) {
706                 return LDB_ERR_OPERATIONS_ERROR;
707         }
708
709         os->ac = ac;
710         
711         os->mem_ctx = callback_mem_ctx;
712
713         os->dn = talloc_steal(os, dn);
714
715         os->replace_dn = plain_dn;
716
717         if (!os->dn) {
718                 return LDB_ERR_OPERATIONS_ERROR;
719         }
720
721         ret = ldb_build_search_req(&os->search_req,
722                                    ac->module->ldb, ac->ops, dn, LDB_SCOPE_BASE, NULL, 
723                                    attrs, NULL, os, extended_replace_dn,
724                                    ac->req);
725
726         if (ac->ops) {
727                 ac->cur->next = os;
728         } else {
729                 ac->ops = os;
730         }
731         ac->cur = os;
732
733         return LDB_SUCCESS;
734 }
735
736
737 /* add */
738 static int extended_dn_add(struct ldb_module *module, struct ldb_request *req)
739 {
740         struct extended_dn_context *ac;
741         int ret;
742         int i, j;
743
744         if (ldb_dn_is_special(req->op.add.message->dn)) {
745                 /* do not manipulate our control entries */
746                 return ldb_next_request(module, req);
747         }
748
749         ac = extended_dn_context_init(module, req);
750         if (!ac) {
751                 return LDB_ERR_OPERATIONS_ERROR;
752         }
753
754         if (!ac->schema) {
755                 /* without schema, this doesn't make any sense */
756                 talloc_free(ac);
757                 return ldb_next_request(module, req);
758         }
759
760         for (i=0; i < req->op.add.message->num_elements; i++) {
761                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
762                 const struct dsdb_attribute *schema_attr
763                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
764                 if (!schema_attr) {
765                         ldb_asprintf_errstring(module->ldb, 
766                                                "attribute %s is not a valid attribute in schema", el->name);
767                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
768                 }
769
770                 /* We only setup an extended DN GUID on these particular DN objects */
771                 if (!((strcmp(schema_attr->attributeSyntax_oid, "2.5.5.1") == 0) ||
772                       (strcmp(schema_attr->attributeSyntax_oid, "2.5.5.7") == 0))) {
773                         continue;
774                 }
775                 
776                 for (j = 0; j < el->num_values; j++) {
777                         ret = extended_store_replace(ac, req->op.add.message->elements, &el->values[j]);
778                         if (ret != LDB_SUCCESS) {
779                                 return ret;
780                         }
781                 }
782         }
783
784         /* if DNs were set continue */
785         if (ac->ops == NULL) {
786                 talloc_free(ac);
787                 return ldb_next_request(module, req);
788         }
789
790         /* start with the searches */
791         return ldb_next_request(module, ac->ops->search_req);
792 }
793
794 /* modify */
795 static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
796 {
797         /* Look over list of modifications */
798         /* Find if any are for linked attributes */
799         /* Determine the effect of the modification */
800         /* Apply the modify to the linked entry */
801
802         int i, j;
803         struct extended_dn_context *ac;
804         int ret;
805
806         if (ldb_dn_is_special(req->op.mod.message->dn)) {
807                 /* do not manipulate our control entries */
808                 return ldb_next_request(module, req);
809         }
810
811         ac = extended_dn_context_init(module, req);
812         if (!ac) {
813                 return LDB_ERR_OPERATIONS_ERROR;
814         }
815
816         if (!ac->schema) {
817                 /* without schema, this doesn't make any sense */
818                 return ldb_next_request(module, req);
819         }
820
821         for (i=0; i < req->op.mod.message->num_elements; i++) {
822                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
823                 const struct dsdb_attribute *schema_attr
824                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
825                 if (!schema_attr) {
826                         ldb_asprintf_errstring(module->ldb, 
827                                                "attribute %s is not a valid attribute in schema", el->name);
828                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
829                 }
830
831                 /* We only setup an extended DN GUID on these particular DN objects */
832                 if (strcmp(schema_attr->attributeSyntax_oid, LDB_SYNTAX_DN) != 0 && 
833                     strcmp(schema_attr->attributeSyntax_oid, "1.2.840.113556.1.4.903") != 0) {
834                         continue;
835                 }
836                 
837                 switch (el->flags & LDB_FLAG_MOD_MASK) {
838                 case LDB_FLAG_MOD_REPLACE:
839                 case LDB_FLAG_MOD_ADD:
840
841                         /* For each value being added, we need to setup the lookups to fill in the extended DN */
842                         for (j = 0; j < el->num_values; j++) {
843                                 struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, module->ldb, &el->values[j]);
844                                 if (!dn || !ldb_dn_validate(dn)) {
845                                         ldb_asprintf_errstring(module->ldb, 
846                                                                "could not parse attribute %s as a DN", el->name);
847                                         return LDB_ERR_INVALID_DN_SYNTAX;
848                                 }
849                                 
850                                 ret = extended_store_replace(ac, req->op.mod.message->elements, &el->values[j]);
851                                 if (ret != LDB_SUCCESS) {
852                                         return ret;
853                                 }
854                         }
855                         break;
856                 }
857         }
858
859
860         return ret;
861 }
862
863 static int extended_dn_init(struct ldb_module *module)
864 {
865         int ret;
866
867         ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID);
868         if (ret != LDB_SUCCESS) {
869                 ldb_debug(module->ldb, LDB_DEBUG_ERROR,
870                         "extended_dn: Unable to register control with rootdse!\n");
871                 return LDB_ERR_OPERATIONS_ERROR;
872         }
873
874         return ldb_next_init(module);
875 }
876
877 _PUBLIC_ const struct ldb_module_ops ldb_extended_dn_module_ops = {
878         .name              = "extended_dn",
879         .search            = extended_dn_search,
880         .init_context      = extended_dn_init,
881         .add               = extended_dn_add,
882         .modify            = extended_dn_modify,
883 };