Fix typos and ban the rams from samba
[cs/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / extended_dn_out.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce 2005-2008
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2009
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, and fixes some other aspects of the result (returned case issues)
28  *  values.
29  *
30  *  Authors: Simo Sorce
31  *           Andrew Bartlett
32  */
33
34 #include "includes.h"
35 #include <ldb.h>
36 #include <ldb_errors.h>
37 #include <ldb_module.h>
38 #include "libcli/security/security.h"
39 #include "librpc/gen_ndr/ndr_misc.h"
40 #include "librpc/gen_ndr/ndr_security.h"
41 #include "librpc/ndr/libndr.h"
42 #include "dsdb/samdb/samdb.h"
43 #include "dsdb/samdb/ldb_modules/util.h"
44
45 struct extended_dn_out_private {
46         bool dereference;
47         bool normalise;
48         struct dsdb_openldap_dereference_control *dereference_control;
49         const char **attrs;
50 };
51
52 /* Do the lazy init of the derererence control */
53
54 static int extended_dn_out_dereference_setup_control(struct ldb_context *ldb, struct extended_dn_out_private *p)
55 {
56         const struct dsdb_schema *schema;
57         struct dsdb_openldap_dereference_control *dereference_control;
58         struct dsdb_attribute *cur;
59
60         unsigned int i = 0;
61         if (p->dereference_control) {
62                 return LDB_SUCCESS;
63         }
64
65         schema = dsdb_get_schema(ldb, p);
66         if (!schema) {
67                 /* No schema on this DB (yet) */
68                 return LDB_SUCCESS;
69         }
70
71         p->dereference_control = dereference_control
72                 = talloc_zero(p, struct dsdb_openldap_dereference_control);
73
74         if (!p->dereference_control) {
75                 return ldb_oom(ldb);
76         }
77
78         for (cur = schema->attributes; cur; cur = cur->next) {
79                 if (cur->dn_format != DSDB_NORMAL_DN) {
80                         continue;
81                 }
82                 dereference_control->dereference
83                         = talloc_realloc(p, dereference_control->dereference,
84                                          struct dsdb_openldap_dereference *, i + 2);
85                 if (!dereference_control->dereference) {
86                         return ldb_oom(ldb);
87                 }
88                 dereference_control->dereference[i] = talloc(dereference_control->dereference,
89                                          struct dsdb_openldap_dereference);
90                 if (!dereference_control->dereference[i]) {
91                         return ldb_oom(ldb);
92                 }
93                 dereference_control->dereference[i]->source_attribute = cur->lDAPDisplayName;
94                 dereference_control->dereference[i]->dereference_attribute = p->attrs;
95                 i++;
96                 dereference_control->dereference[i] = NULL;
97         }
98         return LDB_SUCCESS;
99 }
100
101 static char **copy_attrs(void *mem_ctx, const char * const * attrs)
102 {
103         char **nattrs;
104         unsigned int i, num;
105
106         for (num = 0; attrs[num]; num++);
107
108         nattrs = talloc_array(mem_ctx, char *, num + 1);
109         if (!nattrs) return NULL;
110
111         for(i = 0; i < num; i++) {
112                 nattrs[i] = talloc_strdup(nattrs, attrs[i]);
113                 if (!nattrs[i]) {
114                         talloc_free(nattrs);
115                         return NULL;
116                 }
117         }
118         nattrs[i] = NULL;
119
120         return nattrs;
121 }
122
123 static bool add_attrs(void *mem_ctx, char ***attrs, const char *attr)
124 {
125         char **nattrs;
126         unsigned int num;
127
128         for (num = 0; (*attrs)[num]; num++);
129
130         nattrs = talloc_realloc(mem_ctx, *attrs, char *, num + 2);
131         if (!nattrs) return false;
132
133         *attrs = nattrs;
134
135         nattrs[num] = talloc_strdup(nattrs, attr);
136         if (!nattrs[num]) return false;
137
138         nattrs[num + 1] = NULL;
139
140         return true;
141 }
142
143 /* Inject the extended DN components, so the DN cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com becomes
144    <GUID=541203ae-f7d6-47ef-8390-bfcf019f9583>;<SID=S-1-5-21-4177067393-1453636373-93818737-500>;cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com */
145
146 static int inject_extended_dn_out(struct ldb_reply *ares,
147                                   struct ldb_context *ldb,
148                                   int type,
149                                   bool remove_guid,
150                                   bool remove_sid)
151 {
152         int ret;
153         const DATA_BLOB *guid_blob;
154         const DATA_BLOB *sid_blob;
155
156         guid_blob = ldb_msg_find_ldb_val(ares->message, "objectGUID");
157         sid_blob = ldb_msg_find_ldb_val(ares->message, "objectSid");
158
159         if (!guid_blob) {
160                 ldb_set_errstring(ldb, "Did not find objectGUID to inject into extended DN");
161                 return LDB_ERR_OPERATIONS_ERROR;
162         }
163
164         ret = ldb_dn_set_extended_component(ares->message->dn, "GUID", guid_blob);
165         if (ret != LDB_SUCCESS) {
166                 return ret;
167         }
168         if (sid_blob) {
169                 ret = ldb_dn_set_extended_component(ares->message->dn, "SID", sid_blob);
170                 if (ret != LDB_SUCCESS) {
171                         return ret;
172                 }
173         }
174
175         if (remove_guid) {
176                 ldb_msg_remove_attr(ares->message, "objectGUID");
177         }
178
179         if (sid_blob && remove_sid) {
180                 ldb_msg_remove_attr(ares->message, "objectSid");
181         }
182
183         return LDB_SUCCESS;
184 }
185
186 static int handle_dereference_openldap(struct ldb_dn *dn,
187                               struct dsdb_openldap_dereference_result **dereference_attrs, 
188                               const char *attr, const DATA_BLOB *val)
189 {
190         const struct ldb_val *entryUUIDblob, *sid_blob;
191         struct ldb_message fake_msg; /* easier to use routines that expect an ldb_message */
192         unsigned int j;
193         
194         fake_msg.num_elements = 0;
195                         
196         /* Look for this attribute in the returned control */
197         for (j = 0; dereference_attrs && dereference_attrs[j]; j++) {
198                 struct ldb_val source_dn = data_blob_string_const(dereference_attrs[j]->dereferenced_dn);
199                 if (ldb_attr_cmp(dereference_attrs[j]->source_attribute, attr) == 0
200                     && data_blob_cmp(&source_dn, val) == 0) {
201                         fake_msg.num_elements = dereference_attrs[j]->num_attributes;
202                         fake_msg.elements = dereference_attrs[j]->attributes;
203                         break;
204                 }
205         }
206         if (!fake_msg.num_elements) {
207                 return LDB_SUCCESS;
208         }
209         /* Look for an OpenLDAP entryUUID */
210         
211         entryUUIDblob = ldb_msg_find_ldb_val(&fake_msg, "entryUUID");
212         if (entryUUIDblob) {
213                 NTSTATUS status;
214                 struct ldb_val guid_blob;
215                 struct GUID guid;
216                 
217                 status = GUID_from_data_blob(entryUUIDblob, &guid);
218                 
219                 if (!NT_STATUS_IS_OK(status)) {
220                         return LDB_ERR_INVALID_DN_SYNTAX;
221                 }
222                 status = GUID_to_ndr_blob(&guid, dn, &guid_blob);
223                 if (!NT_STATUS_IS_OK(status)) {
224                         return LDB_ERR_INVALID_DN_SYNTAX;
225                 }
226                 
227                 ldb_dn_set_extended_component(dn, "GUID", &guid_blob);
228         }
229         
230         sid_blob = ldb_msg_find_ldb_val(&fake_msg, "objectSid");
231         
232         /* Look for the objectSid */
233         if (sid_blob) {
234                 ldb_dn_set_extended_component(dn, "SID", sid_blob);
235         }
236         return LDB_SUCCESS;
237 }
238
239 static int handle_dereference_fds(struct ldb_dn *dn,
240                               struct dsdb_openldap_dereference_result **dereference_attrs, 
241                               const char *attr, const DATA_BLOB *val)
242 {
243         const struct ldb_val *nsUniqueIdBlob, *sidBlob;
244         struct ldb_message fake_msg; /* easier to use routines that expect an ldb_message */
245         unsigned int j;
246         
247         fake_msg.num_elements = 0;
248                         
249         /* Look for this attribute in the returned control */
250         for (j = 0; dereference_attrs && dereference_attrs[j]; j++) {
251                 struct ldb_val source_dn = data_blob_string_const(dereference_attrs[j]->dereferenced_dn);
252                 if (ldb_attr_cmp(dereference_attrs[j]->source_attribute, attr) == 0
253                     && data_blob_cmp(&source_dn, val) == 0) {
254                         fake_msg.num_elements = dereference_attrs[j]->num_attributes;
255                         fake_msg.elements = dereference_attrs[j]->attributes;
256                         break;
257                 }
258         }
259         if (!fake_msg.num_elements) {
260                 return LDB_SUCCESS;
261         }
262
263         /* Look for the nsUniqueId */
264         
265         nsUniqueIdBlob = ldb_msg_find_ldb_val(&fake_msg, "nsUniqueId");
266         if (nsUniqueIdBlob) {
267                 NTSTATUS status;
268                 struct ldb_val guid_blob;
269                 struct GUID guid;
270                 
271                 status = NS_GUID_from_string((char *)nsUniqueIdBlob->data, &guid);
272                 
273                 if (!NT_STATUS_IS_OK(status)) {
274                         return LDB_ERR_INVALID_DN_SYNTAX;
275                 }
276                 status = GUID_to_ndr_blob(&guid, dn, &guid_blob);
277                 if (!NT_STATUS_IS_OK(status)) {
278                         return LDB_ERR_INVALID_DN_SYNTAX;
279                 }
280                 
281                 ldb_dn_set_extended_component(dn, "GUID", &guid_blob);
282         }
283         
284         /* Look for the objectSid */
285
286         sidBlob = ldb_msg_find_ldb_val(&fake_msg, "sambaSID");
287         if (sidBlob) {
288                 enum ndr_err_code ndr_err;
289
290                 struct ldb_val sid_blob;
291                 struct dom_sid *sid;
292
293                 sid = dom_sid_parse_length(NULL, sidBlob);
294
295                 if (sid == NULL) {
296                         return LDB_ERR_INVALID_DN_SYNTAX;
297                 }
298
299                 ndr_err = ndr_push_struct_blob(&sid_blob, NULL, sid,
300                                                 (ndr_push_flags_fn_t)ndr_push_dom_sid);
301                 talloc_free(sid);
302                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
303                         return LDB_ERR_INVALID_DN_SYNTAX;
304                 }
305
306                 ldb_dn_set_extended_component(dn, "SID", &sid_blob);
307         }
308         return LDB_SUCCESS;
309 }
310
311 /* search */
312 struct extended_search_context {
313         struct ldb_module *module;
314         const struct dsdb_schema *schema;
315         struct ldb_request *req;
316         bool inject;
317         bool remove_guid;
318         bool remove_sid;
319         int extended_type;
320 };
321
322
323 /*
324    fix one-way links to have the right string DN, to cope with
325    renames of the target
326 */
327 static int fix_one_way_link(struct extended_search_context *ac, struct ldb_dn *dn,
328                             bool is_deleted_objects, bool *remove_value,
329                             uint32_t linkID)
330 {
331         struct GUID guid;
332         NTSTATUS status;
333         int ret;
334         struct ldb_dn *real_dn;
335         uint32_t search_flags;
336         TALLOC_CTX *tmp_ctx = talloc_new(ac);
337         const char *attrs[] = { NULL };
338         struct ldb_result *res;
339
340         (*remove_value) = false;
341
342         status = dsdb_get_extended_dn_guid(dn, &guid, "GUID");
343         if (!NT_STATUS_IS_OK(status)) {
344                 /* this is a strange DN that doesn't have a GUID! just
345                    return the current DN string?? */
346                 talloc_free(tmp_ctx);
347                 return LDB_SUCCESS;
348         }
349
350         search_flags = DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SEARCH_ALL_PARTITIONS | DSDB_SEARCH_ONE_ONLY;
351
352         if (linkID == 0) {
353                 /* You must ALWAYS show one-way links regardless of the state of the target */
354                 search_flags |= (DSDB_SEARCH_SHOW_DELETED | DSDB_SEARCH_SHOW_RECYCLED);
355         }
356
357         ret = dsdb_module_search(ac->module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
358                                  search_flags, ac->req, "objectguid=%s", GUID_string(tmp_ctx, &guid));
359         if (ret != LDB_SUCCESS || res->count != 1) {
360                 /* if we can't resolve this GUID, then we don't
361                    display the link. This could be a link to a NC that we don't
362                    have, or it could be a link to a deleted object
363                 */
364                 (*remove_value) = true;
365                 talloc_free(tmp_ctx);
366                 return LDB_SUCCESS;
367         }
368         real_dn = res->msgs[0]->dn;
369
370         if (strcmp(ldb_dn_get_linearized(dn), ldb_dn_get_linearized(real_dn)) == 0) {
371                 /* its already correct */
372                 talloc_free(tmp_ctx);
373                 return LDB_SUCCESS;
374         }
375
376         /* fix the DN by replacing its components with those from the
377          * real DN
378          */
379         if (!ldb_dn_replace_components(dn, real_dn)) {
380                 talloc_free(tmp_ctx);
381                 return ldb_operr(ldb_module_get_ctx(ac->module));
382         }
383         talloc_free(tmp_ctx);
384
385         return LDB_SUCCESS;
386 }
387
388
389 /*
390   this is called to post-process the results from the search
391  */
392 static int extended_callback(struct ldb_request *req, struct ldb_reply *ares,
393                 int (*handle_dereference)(struct ldb_dn *dn,
394                                 struct dsdb_openldap_dereference_result **dereference_attrs, 
395                                 const char *attr, const DATA_BLOB *val))
396 {
397         struct extended_search_context *ac;
398         struct ldb_control *control;
399         struct dsdb_openldap_dereference_result_control *dereference_control = NULL;
400         int ret;
401         unsigned int i, j;
402         struct ldb_message *msg;
403         struct extended_dn_out_private *p;
404         struct ldb_context *ldb;
405         bool have_reveal_control=false;
406
407         ac = talloc_get_type(req->context, struct extended_search_context);
408         p = talloc_get_type(ldb_module_get_private(ac->module), struct extended_dn_out_private);
409         ldb = ldb_module_get_ctx(ac->module);
410         if (!ares) {
411                 return ldb_module_done(ac->req, NULL, NULL,
412                                         LDB_ERR_OPERATIONS_ERROR);
413         }
414         if (ares->error != LDB_SUCCESS) {
415                 return ldb_module_done(ac->req, ares->controls,
416                                         ares->response, ares->error);
417         }
418
419         msg = ares->message;
420
421         switch (ares->type) {
422         case LDB_REPLY_REFERRAL:
423                 return ldb_module_send_referral(ac->req, ares->referral);
424
425         case LDB_REPLY_DONE:
426                 return ldb_module_done(ac->req, ares->controls,
427                                         ares->response, LDB_SUCCESS);
428         case LDB_REPLY_ENTRY:
429                 break;
430         }
431
432         if (p && p->normalise) {
433                 ret = dsdb_fix_dn_rdncase(ldb, ares->message->dn);
434                 if (ret != LDB_SUCCESS) {
435                         return ldb_module_done(ac->req, NULL, NULL, ret);
436                 }
437         }
438                         
439         if (ac->inject) {
440                 /* for each record returned post-process to add any derived
441                    attributes that have been asked for */
442                 ret = inject_extended_dn_out(ares, ldb,
443                                              ac->extended_type, ac->remove_guid,
444                                              ac->remove_sid);
445                 if (ret != LDB_SUCCESS) {
446                         return ldb_module_done(ac->req, NULL, NULL, ret);
447                 }
448         }
449
450         if ((p && p->normalise) || ac->inject) {
451                 const struct ldb_val *val = ldb_msg_find_ldb_val(ares->message, "distinguishedName");
452                 if (val) {
453                         ldb_msg_remove_attr(ares->message, "distinguishedName");
454                         if (ac->inject) {
455                                 ret = ldb_msg_add_steal_string(ares->message, "distinguishedName", 
456                                                                ldb_dn_get_extended_linearized(ares->message, ares->message->dn, ac->extended_type));
457                         } else {
458                                 ret = ldb_msg_add_linearized_dn(ares->message,
459                                                                 "distinguishedName",
460                                                                 ares->message->dn);
461                         }
462                         if (ret != LDB_SUCCESS) {
463                                 return ldb_oom(ldb);
464                         }
465                 }
466         }
467
468         if (p && p->dereference) {
469                 control = ldb_reply_get_control(ares, DSDB_OPENLDAP_DEREFERENCE_CONTROL);
470         
471                 if (control && control->data) {
472                         dereference_control = talloc_get_type(control->data, struct dsdb_openldap_dereference_result_control);
473                 }
474         }
475
476         have_reveal_control =
477                 dsdb_request_has_control(req, LDB_CONTROL_REVEAL_INTERNALS);
478
479         /* 
480          * Shortcut for repl_meta_data.  We asked for the data
481          * 'as-is', so stop processing here!
482          */
483         if (have_reveal_control && p->normalise == false && ac->inject == true) {
484                 return ldb_module_send_entry(ac->req, msg, ares->controls);
485         }
486         
487         /* Walk the returned elements (but only if we have a schema to
488          * interpret the list with) */
489         for (i = 0; ac->schema && i < msg->num_elements; i++) {
490                 bool make_extended_dn;
491                 const struct dsdb_attribute *attribute;
492
493                 attribute = dsdb_attribute_by_lDAPDisplayName(ac->schema, msg->elements[i].name);
494                 if (!attribute) {
495                         continue;
496                 }
497
498                 if (p && p->normalise) {
499                         /* If we are also in 'normalise' mode, then
500                          * fix the attribute names to be in the
501                          * correct case */
502                         msg->elements[i].name = talloc_strdup(msg->elements, attribute->lDAPDisplayName);
503                         if (!msg->elements[i].name) {
504                                 ldb_oom(ldb);
505                                 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
506                         }
507                 }
508
509                 /* distinguishedName has been dealt with above */
510                 if (ldb_attr_cmp(msg->elements[i].name, "distinguishedName") == 0) {
511                         continue;
512                 }
513
514                 /* Look to see if this attributeSyntax is a DN */
515                 if (attribute->dn_format == DSDB_INVALID_DN) {
516                         continue;
517                 }
518
519                 make_extended_dn = ac->inject;
520
521                 /* Always show plain DN in case of Object(OR-Name) syntax */
522                 if (make_extended_dn) {
523                         make_extended_dn = (strcmp(attribute->syntax->ldap_oid, DSDB_SYNTAX_OR_NAME) != 0);
524                 }
525
526                 for (j = 0; j < msg->elements[i].num_values; j++) {
527                         const char *dn_str;
528                         struct ldb_dn *dn;
529                         struct dsdb_dn *dsdb_dn = NULL;
530                         struct ldb_val *plain_dn = &msg->elements[i].values[j];         
531                         bool is_deleted_objects = false;
532
533                         /* this is a fast method for detecting deleted
534                            linked attributes, working on the unparsed
535                            ldb_val */
536                         if (dsdb_dn_is_deleted_val(plain_dn) && !have_reveal_control) {
537                                 /* it's a deleted linked attribute,
538                                   and we don't have the reveal control */
539                                 memmove(&msg->elements[i].values[j],
540                                         &msg->elements[i].values[j+1],
541                                         (msg->elements[i].num_values-(j+1))*sizeof(struct ldb_val));
542                                 msg->elements[i].num_values--;
543                                 j--;
544                                 continue;
545                         }
546
547
548                         dsdb_dn = dsdb_dn_parse_trusted(msg, ldb, plain_dn, attribute->syntax->ldap_oid);
549
550                         if (!dsdb_dn) {
551                                 ldb_asprintf_errstring(ldb, 
552                                                        "could not parse %.*s in %s on %s as a %s DN", 
553                                                        (int)plain_dn->length, plain_dn->data,
554                                                        msg->elements[i].name, ldb_dn_get_linearized(msg->dn),
555                                                        attribute->syntax->ldap_oid);
556                                 talloc_free(dsdb_dn);
557                                 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_INVALID_DN_SYNTAX);
558                         }
559                         dn = dsdb_dn->dn;
560
561                         /* we need to know if this is a link to the
562                            deleted objects container for fixing one way
563                            links */
564                         if (dsdb_dn->extra_part.length == 16) {
565                                 char *hex_string = data_blob_hex_string_upper(req, &dsdb_dn->extra_part);
566                                 if (hex_string && strcmp(hex_string, DS_GUID_DELETED_OBJECTS_CONTAINER) == 0) {
567                                         is_deleted_objects = true;
568                                 }
569                                 talloc_free(hex_string);
570                         }
571
572                         if (p->normalise) {
573                                 ret = dsdb_fix_dn_rdncase(ldb, dn);
574                                 if (ret != LDB_SUCCESS) {
575                                         talloc_free(dsdb_dn);
576                                         return ldb_module_done(ac->req, NULL, NULL, ret);
577                                 }
578                         }
579                         
580                         /* If we are running in dereference mode (such
581                          * as against OpenLDAP) then the DN in the msg
582                          * above does not contain the extended values,
583                          * and we need to look in the dereference
584                          * result */
585
586                         /* Look for this value in the attribute */
587
588                         if (dereference_control) {
589                                 ret = handle_dereference(dn, 
590                                                          dereference_control->attributes,
591                                                          msg->elements[i].name,
592                                                          &msg->elements[i].values[j]);
593                                 if (ret != LDB_SUCCESS) {
594                                         talloc_free(dsdb_dn);
595                                         return ldb_module_done(ac->req, NULL, NULL, ret);
596                                 }
597                         }
598
599                         /* note that we don't fixup objectCategory as
600                            it should not be possible to move
601                            objectCategory elements in the schema */
602                         if (attribute->one_way_link &&
603                             strcasecmp(attribute->lDAPDisplayName, "objectCategory") != 0) {
604                                 bool remove_value;
605                                 ret = fix_one_way_link(ac, dn, is_deleted_objects, &remove_value,
606                                                        attribute->linkID);
607                                 if (ret != LDB_SUCCESS) {
608                                         talloc_free(dsdb_dn);
609                                         return ldb_module_done(ac->req, NULL, NULL, ret);
610                                 }
611                                 if (remove_value &&
612                                     !ldb_request_get_control(req, LDB_CONTROL_REVEAL_INTERNALS)) {
613                                         /* we show these with REVEAL
614                                            to allow dbcheck to find and
615                                            cleanup these orphaned links */
616                                         memmove(&msg->elements[i].values[j],
617                                                 &msg->elements[i].values[j+1],
618                                                 (msg->elements[i].num_values-(j+1))*sizeof(struct ldb_val));
619                                         msg->elements[i].num_values--;
620                                         j--;
621                                         continue;
622                                 }
623                         }
624                         
625                         if (make_extended_dn) {
626                                 if (!ldb_dn_validate(dsdb_dn->dn)) {
627                                         ldb_asprintf_errstring(ldb, 
628                                                                "could not parse %.*s in %s on %s as a %s DN", 
629                                                                (int)plain_dn->length, plain_dn->data,
630                                                                msg->elements[i].name, ldb_dn_get_linearized(msg->dn),
631                                                                attribute->syntax->ldap_oid);
632                                         talloc_free(dsdb_dn);
633                                         return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_INVALID_DN_SYNTAX);
634                                 }
635                                 /* don't let users see the internal extended
636                                    GUID components */
637                                 if (!have_reveal_control) {
638                                         const char *accept[] = { "GUID", "SID", NULL };
639                                         ldb_dn_extended_filter(dn, accept);
640                                 }
641                                 dn_str = dsdb_dn_get_extended_linearized(msg->elements[i].values,
642                                                                          dsdb_dn, ac->extended_type);
643                         } else {
644                                 dn_str = dsdb_dn_get_linearized(msg->elements[i].values, 
645                                                                 dsdb_dn);
646                         }
647                         
648                         if (!dn_str) {
649                                 ldb_oom(ldb);
650                                 talloc_free(dsdb_dn);
651                                 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
652                         }
653                         msg->elements[i].values[j] = data_blob_string_const(dn_str);
654                         talloc_free(dsdb_dn);
655                 }
656                 if (msg->elements[i].num_values == 0) {
657                         /* we've deleted all of the values from this
658                          * element - remove the element */
659                         memmove(&msg->elements[i],
660                                 &msg->elements[i+1],
661                                 (msg->num_elements-(i+1))*sizeof(struct ldb_message_element));
662                         msg->num_elements--;
663                         i--;
664                 }
665         }
666         return ldb_module_send_entry(ac->req, msg, ares->controls);
667 }
668
669 static int extended_callback_ldb(struct ldb_request *req, struct ldb_reply *ares)
670 {
671         return extended_callback(req, ares, NULL);
672 }
673
674 static int extended_callback_openldap(struct ldb_request *req, struct ldb_reply *ares)
675 {
676         return extended_callback(req, ares, handle_dereference_openldap);
677 }
678
679 static int extended_callback_fds(struct ldb_request *req, struct ldb_reply *ares)
680 {
681         return extended_callback(req, ares, handle_dereference_fds);
682 }
683
684 static int extended_dn_out_search(struct ldb_module *module, struct ldb_request *req,
685                 int (*callback)(struct ldb_request *req, struct ldb_reply *ares))
686 {
687         struct ldb_control *control;
688         struct ldb_control *storage_format_control;
689         struct ldb_extended_dn_control *extended_ctrl = NULL;
690         struct extended_search_context *ac;
691         struct ldb_request *down_req;
692         char **new_attrs;
693         const char * const *const_attrs;
694         struct ldb_context *ldb = ldb_module_get_ctx(module);
695         int ret;
696
697         struct extended_dn_out_private *p = talloc_get_type(ldb_module_get_private(module), struct extended_dn_out_private);
698
699         /* The schema manipulation does not apply to special DNs */
700         if (ldb_dn_is_special(req->op.search.base)) {
701                 return ldb_next_request(module, req);
702         }
703
704         /* check if there's an extended dn control */
705         control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
706         if (control && control->data) {
707                 extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control);
708                 if (!extended_ctrl) {
709                         return LDB_ERR_PROTOCOL_ERROR;
710                 }
711         }
712
713         /* Look to see if, as we are in 'store DN+GUID+SID' mode, the
714          * client is after the storage format (to fill in linked
715          * attributes) */
716         storage_format_control = ldb_request_get_control(req, DSDB_CONTROL_DN_STORAGE_FORMAT_OID);
717         if (!control && storage_format_control && storage_format_control->data) {
718                 extended_ctrl = talloc_get_type(storage_format_control->data, struct ldb_extended_dn_control);
719                 if (!extended_ctrl) {
720                         ldb_set_errstring(ldb, "extended_dn_out: extended_ctrl was of the wrong data type");
721                         return LDB_ERR_PROTOCOL_ERROR;
722                 }
723         }
724
725         ac = talloc_zero(req, struct extended_search_context);
726         if (ac == NULL) {
727                 return ldb_oom(ldb);
728         }
729
730         ac->module = module;
731         ac->schema = dsdb_get_schema(ldb, ac);
732         ac->req = req;
733         ac->inject = false;
734         ac->remove_guid = false;
735         ac->remove_sid = false;
736         
737         const_attrs = req->op.search.attrs;
738
739         /* We only need to do special processing if we were asked for
740          * the extended DN, or we are 'store DN+GUID+SID'
741          * (!dereference) mode.  (This is the normal mode for LDB on
742          * tdb). */
743         if (control || (storage_format_control && p && !p->dereference)) {
744                 ac->inject = true;
745                 if (extended_ctrl) {
746                         ac->extended_type = extended_ctrl->type;
747                 } else {
748                         ac->extended_type = 0;
749                 }
750
751                 /* check if attrs only is specified, in that case check whether we need to modify them */
752                 if (req->op.search.attrs && !is_attr_in_list(req->op.search.attrs, "*")) {
753                         if (! is_attr_in_list(req->op.search.attrs, "objectGUID")) {
754                                 ac->remove_guid = true;
755                         }
756                         if (! is_attr_in_list(req->op.search.attrs, "objectSid")) {
757                                 ac->remove_sid = true;
758                         }
759                         if (ac->remove_guid || ac->remove_sid) {
760                                 new_attrs = copy_attrs(ac, req->op.search.attrs);
761                                 if (new_attrs == NULL) {
762                                         return ldb_oom(ldb);
763                                 }
764
765                                 if (ac->remove_guid) {
766                                         if (!add_attrs(ac, &new_attrs, "objectGUID"))
767                                                 return ldb_operr(ldb);
768                                 }
769                                 if (ac->remove_sid) {
770                                         if (!add_attrs(ac, &new_attrs, "objectSid"))
771                                                 return ldb_operr(ldb);
772                                 }
773                                 const_attrs = (const char * const *)new_attrs;
774                         }
775                 }
776         }
777
778         ret = ldb_build_search_req_ex(&down_req,
779                                       ldb, ac,
780                                       req->op.search.base,
781                                       req->op.search.scope,
782                                       req->op.search.tree,
783                                       const_attrs,
784                                       req->controls,
785                                       ac, callback,
786                                       req);
787         LDB_REQ_SET_LOCATION(down_req);
788         if (ret != LDB_SUCCESS) {
789                 return ret;
790         }
791
792         /* mark extended DN and storage format controls as done */
793         if (control) {
794                 control->critical = 0;
795         }
796
797         if (storage_format_control) {
798                 storage_format_control->critical = 0;
799         }
800
801         /* Add in dereference control, if we were asked to, we are
802          * using the 'dereference' mode (such as with an OpenLDAP
803          * backend) and have the control prepared */
804         if (control && p && p->dereference) {
805                 ret = extended_dn_out_dereference_setup_control(ldb, p);
806                 if (ret != LDB_SUCCESS) {
807                         return ret;
808                 }
809
810                 /* We should always have this, but before the schema
811                  * is with us, things get tricky */
812                 if (p->dereference_control) {
813
814                         /* This control must *not* be critical,
815                          * because if this particular request did not
816                          * return any dereferencable attributes in the
817                          * end, then OpenLDAP will reply with
818                          * unavailableCriticalExtension, rather than
819                          * just an empty return control */
820                         ret = ldb_request_add_control(down_req,
821                                                       DSDB_OPENLDAP_DEREFERENCE_CONTROL,
822                                                       false, p->dereference_control);
823                         if (ret != LDB_SUCCESS) {
824                                 return ret;
825                         }
826                 }
827         }
828
829         /* perform the search */
830         return ldb_next_request(module, down_req);
831 }
832
833 static int extended_dn_out_ldb_search(struct ldb_module *module, struct ldb_request *req)
834 {
835         return extended_dn_out_search(module, req, extended_callback_ldb);
836 }
837
838 static int extended_dn_out_openldap_search(struct ldb_module *module, struct ldb_request *req)
839 {
840         return extended_dn_out_search(module, req, extended_callback_openldap);
841 }
842
843 static int extended_dn_out_fds_search(struct ldb_module *module, struct ldb_request *req)
844 {
845         return extended_dn_out_search(module, req, extended_callback_fds);
846 }
847
848 static int extended_dn_out_ldb_init(struct ldb_module *module)
849 {
850         int ret;
851
852         struct extended_dn_out_private *p = talloc(module, struct extended_dn_out_private);
853         struct dsdb_extended_dn_store_format *dn_format;
854
855         ldb_module_set_private(module, p);
856
857         if (!p) {
858                 return ldb_oom(ldb_module_get_ctx(module));
859         }
860
861         dn_format = talloc(p, struct dsdb_extended_dn_store_format);
862         if (!dn_format) {
863                 talloc_free(p);
864                 return ldb_oom(ldb_module_get_ctx(module));
865         }
866
867         dn_format->store_extended_dn_in_ldb = true;
868         ret = ldb_set_opaque(ldb_module_get_ctx(module), DSDB_EXTENDED_DN_STORE_FORMAT_OPAQUE_NAME, dn_format);
869         if (ret != LDB_SUCCESS) {
870                 talloc_free(p);
871                 return ret;
872         }
873
874         p->dereference = false;
875         p->normalise = false;
876
877         ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID);
878         if (ret != LDB_SUCCESS) {
879                 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
880                         "extended_dn_out: Unable to register control with rootdse!\n");
881                 return ldb_operr(ldb_module_get_ctx(module));
882         }
883
884         return ldb_next_init(module);
885 }
886
887 static int extended_dn_out_dereference_init(struct ldb_module *module, const char *attrs[])
888 {
889         int ret;
890         struct extended_dn_out_private *p = talloc_zero(module, struct extended_dn_out_private);
891         struct dsdb_extended_dn_store_format *dn_format;
892
893         ldb_module_set_private(module, p);
894
895         if (!p) {
896                 return ldb_module_oom(module);
897         }
898
899         dn_format = talloc(p, struct dsdb_extended_dn_store_format);
900         if (!dn_format) {
901                 talloc_free(p);
902                 return ldb_module_oom(module);
903         }
904
905         dn_format->store_extended_dn_in_ldb = false;
906
907         ret = ldb_set_opaque(ldb_module_get_ctx(module), DSDB_EXTENDED_DN_STORE_FORMAT_OPAQUE_NAME, dn_format);
908         if (ret != LDB_SUCCESS) {
909                 talloc_free(p);
910                 return ret;
911         }
912
913         p->dereference = true;
914
915         p->attrs = attrs;
916         /* At the moment, servers that need dereference also need the
917          * DN and attribute names to be normalised */
918         p->normalise = true;
919
920         ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID);
921         if (ret != LDB_SUCCESS) {
922                 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
923                           "extended_dn_out: Unable to register control with rootdse!\n");
924                 return ldb_operr(ldb_module_get_ctx(module));
925         }
926
927         return ldb_next_init(module);
928 }
929
930 static int extended_dn_out_openldap_init(struct ldb_module *module)
931 {
932         static const char *attrs[] = {
933                 "entryUUID",
934                 "objectSid",
935                 NULL
936         };
937
938         return extended_dn_out_dereference_init(module, attrs);
939 }
940
941 static int extended_dn_out_fds_init(struct ldb_module *module)
942 {
943         static const char *attrs[] = {
944                 "nsUniqueId",
945                 "sambaSID",
946                 NULL
947         };
948
949         return extended_dn_out_dereference_init(module, attrs);
950 }
951
952 static const struct ldb_module_ops ldb_extended_dn_out_ldb_module_ops = {
953         .name              = "extended_dn_out_ldb",
954         .search            = extended_dn_out_ldb_search,
955         .init_context      = extended_dn_out_ldb_init,
956 };
957
958 static const struct ldb_module_ops ldb_extended_dn_out_openldap_module_ops = {
959         .name              = "extended_dn_out_openldap",
960         .search            = extended_dn_out_openldap_search,
961         .init_context      = extended_dn_out_openldap_init,
962 };
963
964 static const struct ldb_module_ops ldb_extended_dn_out_fds_module_ops = {
965         .name              = "extended_dn_out_fds",
966         .search            = extended_dn_out_fds_search,
967         .init_context      = extended_dn_out_fds_init,
968 };
969
970 /*
971   initialise the module
972  */
973 _PUBLIC_ int ldb_extended_dn_out_module_init(const char *version)
974 {
975         int ret;
976         LDB_MODULE_CHECK_VERSION(version);
977         ret = ldb_register_module(&ldb_extended_dn_out_ldb_module_ops);
978         if (ret != LDB_SUCCESS) {
979                 return ret;
980         }
981         ret = ldb_register_module(&ldb_extended_dn_out_openldap_module_ops);
982         if (ret != LDB_SUCCESS) {
983                 return ret;
984         }
985         ret = ldb_register_module(&ldb_extended_dn_out_fds_module_ops);
986         if (ret != LDB_SUCCESS) {
987                 return ret;
988         }
989         return LDB_SUCCESS;
990 }