s4-dsdb extended_dn_out: Move lazy dereference control creation to lazy-init
[abartlet/samba.git/.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/include/ldb.h"
36 #include "ldb/include/ldb_errors.h"
37 #include "ldb/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 "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 (dsdb_dn_oid_to_format(cur->syntax->ldap_oid) == DSDB_INVALID_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) {
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 /* Fix the DN so that the relative attribute names are in upper case so that the DN:
144    cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com becomes
145    CN=Adminstrator,CN=users,DC=samba,DC=example,DC=com
146 */
147
148
149 static int fix_dn(struct ldb_context *ldb, struct ldb_dn *dn)
150 {
151         int i, ret;
152         char *upper_rdn_attr;
153
154         for (i=0; i < ldb_dn_get_comp_num(dn); i++) {
155                 /* We need the attribute name in upper case */
156                 upper_rdn_attr = strupper_talloc(dn,
157                                                  ldb_dn_get_component_name(dn, i));
158                 if (!upper_rdn_attr) {
159                         return ldb_oom(ldb);
160                 }
161                 
162                 /* And replace it with CN=foo (we need the attribute in upper case */
163                 ret = ldb_dn_set_component(dn, i, upper_rdn_attr,
164                                            *ldb_dn_get_component_val(dn, i));
165                 talloc_free(upper_rdn_attr);
166                 if (ret != LDB_SUCCESS) {
167                         return ret;
168                 }
169         }
170         return LDB_SUCCESS;
171 }
172
173 /* Inject the extended DN components, so the DN cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com becomes
174    <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 */
175
176 static int inject_extended_dn_out(struct ldb_reply *ares,
177                                   struct ldb_context *ldb,
178                                   int type,
179                                   bool remove_guid,
180                                   bool remove_sid)
181 {
182         int ret;
183         const DATA_BLOB *guid_blob;
184         const DATA_BLOB *sid_blob;
185
186         guid_blob = ldb_msg_find_ldb_val(ares->message, "objectGUID");
187         sid_blob = ldb_msg_find_ldb_val(ares->message, "objectSid");
188
189         if (!guid_blob) {
190                 ldb_set_errstring(ldb, "Did not find objectGUID to inject into extended DN");
191                 return LDB_ERR_OPERATIONS_ERROR;
192         }
193
194         ret = ldb_dn_set_extended_component(ares->message->dn, "GUID", guid_blob);
195         if (ret != LDB_SUCCESS) {
196                 return ret;
197         }
198         if (sid_blob) {
199                 ret = ldb_dn_set_extended_component(ares->message->dn, "SID", sid_blob);
200                 if (ret != LDB_SUCCESS) {
201                         return ret;
202                 }
203         }
204
205         if (remove_guid) {
206                 ldb_msg_remove_attr(ares->message, "objectGUID");
207         }
208
209         if (sid_blob && remove_sid) {
210                 ldb_msg_remove_attr(ares->message, "objectSid");
211         }
212
213         return LDB_SUCCESS;
214 }
215
216 static int handle_dereference_openldap(struct ldb_dn *dn,
217                               struct dsdb_openldap_dereference_result **dereference_attrs, 
218                               const char *attr, const DATA_BLOB *val)
219 {
220         const struct ldb_val *entryUUIDblob, *sid_blob;
221         struct ldb_message fake_msg; /* easier to use routines that expect an ldb_message */
222         unsigned int j;
223         
224         fake_msg.num_elements = 0;
225                         
226         /* Look for this attribute in the returned control */
227         for (j = 0; dereference_attrs && dereference_attrs[j]; j++) {
228                 struct ldb_val source_dn = data_blob_string_const(dereference_attrs[j]->dereferenced_dn);
229                 if (ldb_attr_cmp(dereference_attrs[j]->source_attribute, attr) == 0
230                     && data_blob_cmp(&source_dn, val) == 0) {
231                         fake_msg.num_elements = dereference_attrs[j]->num_attributes;
232                         fake_msg.elements = dereference_attrs[j]->attributes;
233                         break;
234                 }
235         }
236         if (!fake_msg.num_elements) {
237                 return LDB_SUCCESS;
238         }
239         /* Look for an OpenLDAP entryUUID */
240         
241         entryUUIDblob = ldb_msg_find_ldb_val(&fake_msg, "entryUUID");
242         if (entryUUIDblob) {
243                 NTSTATUS status;
244                 struct ldb_val guid_blob;
245                 struct GUID guid;
246                 
247                 status = GUID_from_data_blob(entryUUIDblob, &guid);
248                 
249                 if (!NT_STATUS_IS_OK(status)) {
250                         return LDB_ERR_INVALID_DN_SYNTAX;
251                 }
252                 status = GUID_to_ndr_blob(&guid, dn, &guid_blob);
253                 if (!NT_STATUS_IS_OK(status)) {
254                         return LDB_ERR_INVALID_DN_SYNTAX;
255                 }
256                 
257                 ldb_dn_set_extended_component(dn, "GUID", &guid_blob);
258         }
259         
260         sid_blob = ldb_msg_find_ldb_val(&fake_msg, "objectSid");
261         
262         /* Look for the objectSid */
263         if (sid_blob) {
264                 ldb_dn_set_extended_component(dn, "SID", sid_blob);
265         }
266         return LDB_SUCCESS;
267 }
268
269 static int handle_dereference_fds(struct ldb_dn *dn,
270                               struct dsdb_openldap_dereference_result **dereference_attrs, 
271                               const char *attr, const DATA_BLOB *val)
272 {
273         const struct ldb_val *nsUniqueIdBlob, *sidBlob;
274         struct ldb_message fake_msg; /* easier to use routines that expect an ldb_message */
275         unsigned int j;
276         
277         fake_msg.num_elements = 0;
278                         
279         /* Look for this attribute in the returned control */
280         for (j = 0; dereference_attrs && dereference_attrs[j]; j++) {
281                 struct ldb_val source_dn = data_blob_string_const(dereference_attrs[j]->dereferenced_dn);
282                 if (ldb_attr_cmp(dereference_attrs[j]->source_attribute, attr) == 0
283                     && data_blob_cmp(&source_dn, val) == 0) {
284                         fake_msg.num_elements = dereference_attrs[j]->num_attributes;
285                         fake_msg.elements = dereference_attrs[j]->attributes;
286                         break;
287                 }
288         }
289         if (!fake_msg.num_elements) {
290                 return LDB_SUCCESS;
291         }
292
293         /* Look for the nsUniqueId */
294         
295         nsUniqueIdBlob = ldb_msg_find_ldb_val(&fake_msg, "nsUniqueId");
296         if (nsUniqueIdBlob) {
297                 NTSTATUS status;
298                 struct ldb_val guid_blob;
299                 struct GUID guid;
300                 
301                 status = NS_GUID_from_string((char *)nsUniqueIdBlob->data, &guid);
302                 
303                 if (!NT_STATUS_IS_OK(status)) {
304                         return LDB_ERR_INVALID_DN_SYNTAX;
305                 }
306                 status = GUID_to_ndr_blob(&guid, dn, &guid_blob);
307                 if (!NT_STATUS_IS_OK(status)) {
308                         return LDB_ERR_INVALID_DN_SYNTAX;
309                 }
310                 
311                 ldb_dn_set_extended_component(dn, "GUID", &guid_blob);
312         }
313         
314         /* Look for the objectSid */
315
316         sidBlob = ldb_msg_find_ldb_val(&fake_msg, "sambaSID");
317         if (sidBlob) {
318                 enum ndr_err_code ndr_err;
319
320                 struct ldb_val sid_blob;
321                 struct dom_sid *sid;
322
323                 sid = dom_sid_parse_length(NULL, sidBlob);
324
325                 if (sid == NULL) {
326                         return LDB_ERR_INVALID_DN_SYNTAX;
327                 }
328
329                 ndr_err = ndr_push_struct_blob(&sid_blob, NULL, sid,
330                                                 (ndr_push_flags_fn_t)ndr_push_dom_sid);
331                 talloc_free(sid);
332                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
333                         return LDB_ERR_INVALID_DN_SYNTAX;
334                 }
335
336                 ldb_dn_set_extended_component(dn, "SID", &sid_blob);
337         }
338         return LDB_SUCCESS;
339 }
340
341 /* search */
342 struct extended_search_context {
343         struct ldb_module *module;
344         const struct dsdb_schema *schema;
345         struct ldb_request *req;
346         bool inject;
347         bool remove_guid;
348         bool remove_sid;
349         int extended_type;
350 };
351
352 static int extended_callback(struct ldb_request *req, struct ldb_reply *ares,
353                 int (*handle_dereference)(struct ldb_dn *dn,
354                                 struct dsdb_openldap_dereference_result **dereference_attrs, 
355                                 const char *attr, const DATA_BLOB *val))
356 {
357         struct extended_search_context *ac;
358         struct ldb_control *control;
359         struct dsdb_openldap_dereference_result_control *dereference_control = NULL;
360         int ret;
361         unsigned int i, j;
362         struct ldb_message *msg;
363         struct extended_dn_out_private *p;
364         struct ldb_context *ldb;
365         bool have_reveal_control, checked_reveal_control=false;
366
367         ac = talloc_get_type(req->context, struct extended_search_context);
368         p = talloc_get_type(ldb_module_get_private(ac->module), struct extended_dn_out_private);
369         ldb = ldb_module_get_ctx(ac->module);
370         if (!ares) {
371                 return ldb_module_done(ac->req, NULL, NULL,
372                                         LDB_ERR_OPERATIONS_ERROR);
373         }
374         if (ares->error != LDB_SUCCESS) {
375                 return ldb_module_done(ac->req, ares->controls,
376                                         ares->response, ares->error);
377         }
378
379         msg = ares->message;
380
381         switch (ares->type) {
382         case LDB_REPLY_REFERRAL:
383                 return ldb_module_send_referral(ac->req, ares->referral);
384
385         case LDB_REPLY_DONE:
386                 return ldb_module_done(ac->req, ares->controls,
387                                         ares->response, LDB_SUCCESS);
388         case LDB_REPLY_ENTRY:
389                 break;
390         }
391
392         if (p && p->normalise) {
393                 ret = fix_dn(ldb, ares->message->dn);
394                 if (ret != LDB_SUCCESS) {
395                         return ldb_module_done(ac->req, NULL, NULL, ret);
396                 }
397         }
398                         
399         if (ac->inject) {
400                 /* for each record returned post-process to add any derived
401                    attributes that have been asked for */
402                 ret = inject_extended_dn_out(ares, ldb,
403                                              ac->extended_type, ac->remove_guid,
404                                              ac->remove_sid);
405                 if (ret != LDB_SUCCESS) {
406                         return ldb_module_done(ac->req, NULL, NULL, ret);
407                 }
408         }
409
410         if ((p && p->normalise) || ac->inject) {
411                 const struct ldb_val *val = ldb_msg_find_ldb_val(ares->message, "distinguishedName");
412                 if (val) {
413                         ldb_msg_remove_attr(ares->message, "distinguishedName");
414                         if (ac->inject) {
415                                 ret = ldb_msg_add_steal_string(ares->message, "distinguishedName", 
416                                                                ldb_dn_get_extended_linearized(ares->message, ares->message->dn, ac->extended_type));
417                         } else {
418                                 ret = ldb_msg_add_linearized_dn(ares->message,
419                                                                 "distinguishedName",
420                                                                 ares->message->dn);
421                         }
422                         if (ret != LDB_SUCCESS) {
423                                 return ldb_oom(ldb);
424                         }
425                 }
426         }
427
428         if (p && p->dereference) {
429                 control = ldb_reply_get_control(ares, DSDB_OPENLDAP_DEREFERENCE_CONTROL);
430         
431                 if (control && control->data) {
432                         dereference_control = talloc_get_type(control->data, struct dsdb_openldap_dereference_result_control);
433                 }
434         }
435
436         /* Walk the returned elements (but only if we have a schema to
437          * interpret the list with) */
438         for (i = 0; ac->schema && i < msg->num_elements; i++) {
439                 bool make_extended_dn;
440                 const struct dsdb_attribute *attribute;
441                 attribute = dsdb_attribute_by_lDAPDisplayName(ac->schema, msg->elements[i].name);
442                 if (!attribute) {
443                         continue;
444                 }
445
446                 if (p->normalise) {
447                         /* If we are also in 'normalise' mode, then
448                          * fix the attribute names to be in the
449                          * correct case */
450                         msg->elements[i].name = talloc_strdup(msg->elements, attribute->lDAPDisplayName);
451                         if (!msg->elements[i].name) {
452                                 ldb_oom(ldb);
453                                 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
454                         }
455                 }
456
457                 /* distinguishedName has been dealt with above */
458                 if (ldb_attr_cmp(msg->elements[i].name, "distinguishedName") == 0) {
459                         continue;
460                 }
461
462                 /* Look to see if this attributeSyntax is a DN */
463                 if (dsdb_dn_oid_to_format(attribute->syntax->ldap_oid) == DSDB_INVALID_DN) {
464                         continue;
465                 }
466
467                 make_extended_dn = ac->inject;
468
469                 /* Always show plain DN in case of Object(OR-Name) syntax */
470                 if (make_extended_dn) {
471                         make_extended_dn = (strcmp(attribute->syntax->ldap_oid, DSDB_SYNTAX_OR_NAME) != 0);
472                 }
473
474                 for (j = 0; j < msg->elements[i].num_values; j++) {
475                         const char *dn_str;
476                         struct ldb_dn *dn;
477                         struct dsdb_dn *dsdb_dn = NULL;
478                         struct ldb_val *plain_dn = &msg->elements[i].values[j];         
479
480                         if (!checked_reveal_control) {
481                                 have_reveal_control =
482                                         ldb_request_get_control(req, LDB_CONTROL_REVEAL_INTERNALS) != NULL;
483                                 checked_reveal_control = true;
484                         }
485
486                         /* this is a fast method for detecting deleted
487                            linked attributes, working on the unparsed
488                            ldb_val */
489                         if (dsdb_dn_is_deleted_val(plain_dn) && !have_reveal_control) {
490                                 /* it's a deleted linked attribute,
491                                   and we don't have the reveal control */
492                                 memmove(&msg->elements[i].values[j],
493                                         &msg->elements[i].values[j+1],
494                                         (msg->elements[i].num_values-(j+1))*sizeof(struct ldb_val));
495                                 msg->elements[i].num_values--;
496                                 j--;
497                                 continue;
498                         }
499
500
501                         dsdb_dn = dsdb_dn_parse(msg, ldb, plain_dn, attribute->syntax->ldap_oid);
502
503                         if (!dsdb_dn || !ldb_dn_validate(dsdb_dn->dn)) {
504                                 ldb_asprintf_errstring(ldb, 
505                                                        "could not parse %.*s in %s on %s as a %s DN", 
506                                                        (int)plain_dn->length, plain_dn->data,
507                                                        msg->elements[i].name, ldb_dn_get_linearized(msg->dn),
508                                                        attribute->syntax->ldap_oid);
509                                 talloc_free(dsdb_dn);
510                                 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_INVALID_DN_SYNTAX);
511                         }
512                         dn = dsdb_dn->dn;
513
514                         /* don't let users see the internal extended
515                            GUID components */
516                         if (!have_reveal_control) {
517                                 const char *accept[] = { "GUID", "SID", "WKGUID", NULL };
518                                 ldb_dn_extended_filter(dn, accept);
519                         }
520
521                         if (p->normalise) {
522                                 ret = fix_dn(ldb, dn);
523                                 if (ret != LDB_SUCCESS) {
524                                         talloc_free(dsdb_dn);
525                                         return ldb_module_done(ac->req, NULL, NULL, ret);
526                                 }
527                         }
528                         
529                         /* If we are running in dereference mode (such
530                          * as against OpenLDAP) then the DN in the msg
531                          * above does not contain the extended values,
532                          * and we need to look in the dereference
533                          * result */
534
535                         /* Look for this value in the attribute */
536
537                         if (dereference_control) {
538                                 ret = handle_dereference(dn, 
539                                                          dereference_control->attributes,
540                                                          msg->elements[i].name,
541                                                          &msg->elements[i].values[j]);
542                                 if (ret != LDB_SUCCESS) {
543                                         talloc_free(dsdb_dn);
544                                         return ldb_module_done(ac->req, NULL, NULL, ret);
545                                 }
546                         }
547                         
548                         if (make_extended_dn) {
549                                 dn_str = dsdb_dn_get_extended_linearized(msg->elements[i].values,
550                                                                          dsdb_dn, ac->extended_type);
551                         } else {
552                                 dn_str = dsdb_dn_get_linearized(msg->elements[i].values, 
553                                                                 dsdb_dn);
554                         }
555                         
556                         if (!dn_str) {
557                                 ldb_oom(ldb);
558                                 talloc_free(dsdb_dn);
559                                 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
560                         }
561                         msg->elements[i].values[j] = data_blob_string_const(dn_str);
562                         talloc_free(dsdb_dn);
563                 }
564                 if (msg->elements[i].num_values == 0) {
565                         /* we've deleted all of the values from this
566                          * element - remove the element */
567                         memmove(&msg->elements[i],
568                                 &msg->elements[i+1],
569                                 (msg->num_elements-(i+1))*sizeof(struct ldb_message_element));
570                         msg->num_elements--;
571                         i--;
572                 }
573         }
574         return ldb_module_send_entry(ac->req, msg, ares->controls);
575 }
576
577 static int extended_callback_ldb(struct ldb_request *req, struct ldb_reply *ares)
578 {
579         return extended_callback(req, ares, NULL);
580 }
581
582 static int extended_callback_openldap(struct ldb_request *req, struct ldb_reply *ares)
583 {
584         return extended_callback(req, ares, handle_dereference_openldap);
585 }
586
587 static int extended_callback_fds(struct ldb_request *req, struct ldb_reply *ares)
588 {
589         return extended_callback(req, ares, handle_dereference_fds);
590 }
591
592 static int extended_dn_out_search(struct ldb_module *module, struct ldb_request *req,
593                 int (*callback)(struct ldb_request *req, struct ldb_reply *ares))
594 {
595         struct ldb_control *control;
596         struct ldb_control *storage_format_control;
597         struct ldb_extended_dn_control *extended_ctrl = NULL;
598         struct extended_search_context *ac;
599         struct ldb_request *down_req;
600         char **new_attrs;
601         const char * const *const_attrs;
602         struct ldb_context *ldb = ldb_module_get_ctx(module);
603         int ret;
604         bool critical;
605
606         struct extended_dn_out_private *p = talloc_get_type(ldb_module_get_private(module), struct extended_dn_out_private);
607
608         /* The schema manipulation does not apply to special DNs */
609         if (ldb_dn_is_special(req->op.search.base)) {
610                 return ldb_next_request(module, req);
611         }
612
613         /* check if there's an extended dn control */
614         control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
615         if (control && control->data) {
616                 extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control);
617                 if (!extended_ctrl) {
618                         return LDB_ERR_PROTOCOL_ERROR;
619                 }
620         }
621
622         /* Look to see if, as we are in 'store DN+GUID+SID' mode, the
623          * client is after the storage format (to fill in linked
624          * attributes) */
625         storage_format_control = ldb_request_get_control(req, DSDB_CONTROL_DN_STORAGE_FORMAT_OID);
626         if (!control && storage_format_control && storage_format_control->data) {
627                 extended_ctrl = talloc_get_type(storage_format_control->data, struct ldb_extended_dn_control);
628                 if (!extended_ctrl) {
629                         ldb_set_errstring(ldb, "extended_dn_out: extended_ctrl was of the wrong data type");
630                         return LDB_ERR_PROTOCOL_ERROR;
631                 }
632         }
633
634         ac = talloc_zero(req, struct extended_search_context);
635         if (ac == NULL) {
636                 return ldb_oom(ldb);
637         }
638
639         ac->module = module;
640         ac->schema = dsdb_get_schema(ldb, ac);
641         ac->req = req;
642         ac->inject = false;
643         ac->remove_guid = false;
644         ac->remove_sid = false;
645         
646         const_attrs = req->op.search.attrs;
647
648         /* We only need to do special processing if we were asked for
649          * the extended DN, or we are 'store DN+GUID+SID'
650          * (!dereference) mode.  (This is the normal mode for LDB on
651          * tdb). */
652         if (control || (storage_format_control && p && !p->dereference)) {
653                 ac->inject = true;
654                 if (extended_ctrl) {
655                         ac->extended_type = extended_ctrl->type;
656                 } else {
657                         ac->extended_type = 0;
658                 }
659
660                 /* check if attrs only is specified, in that case check wether we need to modify them */
661                 if (req->op.search.attrs && !is_attr_in_list(req->op.search.attrs, "*")) {
662                         if (! is_attr_in_list(req->op.search.attrs, "objectGUID")) {
663                                 ac->remove_guid = true;
664                         }
665                         if (! is_attr_in_list(req->op.search.attrs, "objectSid")) {
666                                 ac->remove_sid = true;
667                         }
668                         if (ac->remove_guid || ac->remove_sid) {
669                                 new_attrs = copy_attrs(ac, req->op.search.attrs);
670                                 if (new_attrs == NULL) {
671                                         return ldb_oom(ldb);
672                                 }
673
674                                 if (ac->remove_guid) {
675                                         if (!add_attrs(ac, &new_attrs, "objectGUID"))
676                                                 return ldb_operr(ldb);
677                                 }
678                                 if (ac->remove_sid) {
679                                         if (!add_attrs(ac, &new_attrs, "objectSid"))
680                                                 return ldb_operr(ldb);
681                                 }
682                                 const_attrs = (const char * const *)new_attrs;
683                         }
684                 }
685         }
686
687         ret = ldb_build_search_req_ex(&down_req,
688                                       ldb, ac,
689                                       req->op.search.base,
690                                       req->op.search.scope,
691                                       req->op.search.tree,
692                                       const_attrs,
693                                       req->controls,
694                                       ac, callback,
695                                       req);
696         LDB_REQ_SET_LOCATION(down_req);
697         if (ret != LDB_SUCCESS) {
698                 return ret;
699         }
700
701         /* mark extended DN and storage format controls as done */
702         if (control) {
703                 critical = control->critical;
704                 control->critical = 0;
705         }
706
707         if (storage_format_control) {
708                 storage_format_control->critical = 0;
709         }
710
711         /* Add in dereference control, if we were asked to, we are
712          * using the 'dereference' mode (such as with an OpenLDAP
713          * backend) and have the control prepared */
714         if (control && p && p->dereference) {
715                 ret = extended_dn_out_dereference_setup_control(ldb, p);
716                 if (ret != LDB_SUCCESS) {
717                         return ret;
718                 }
719
720                 /* We should always have this, but before the schema
721                  * is with us, things get tricky */
722                 if (p->dereference_control) {
723                         ret = ldb_request_add_control(down_req,
724                                                       DSDB_OPENLDAP_DEREFERENCE_CONTROL,
725                                                       critical, p->dereference_control);
726                         if (ret != LDB_SUCCESS) {
727                                 return ret;
728                         }
729                 }
730         }
731
732         /* perform the search */
733         return ldb_next_request(module, down_req);
734 }
735
736 static int extended_dn_out_ldb_search(struct ldb_module *module, struct ldb_request *req)
737 {
738         return extended_dn_out_search(module, req, extended_callback_ldb);
739 }
740
741 static int extended_dn_out_openldap_search(struct ldb_module *module, struct ldb_request *req)
742 {
743         return extended_dn_out_search(module, req, extended_callback_openldap);
744 }
745
746 static int extended_dn_out_fds_search(struct ldb_module *module, struct ldb_request *req)
747 {
748         return extended_dn_out_search(module, req, extended_callback_fds);
749 }
750
751 static int extended_dn_out_ldb_init(struct ldb_module *module)
752 {
753         int ret;
754
755         struct extended_dn_out_private *p = talloc(module, struct extended_dn_out_private);
756         struct dsdb_extended_dn_store_format *dn_format;
757
758         ldb_module_set_private(module, p);
759
760         if (!p) {
761                 return ldb_oom(ldb_module_get_ctx(module));
762         }
763
764         dn_format = talloc(p, struct dsdb_extended_dn_store_format);
765         if (!dn_format) {
766                 talloc_free(p);
767                 return ldb_oom(ldb_module_get_ctx(module));
768         }
769
770         dn_format->store_extended_dn_in_ldb = true;
771         ret = ldb_set_opaque(ldb_module_get_ctx(module), DSDB_EXTENDED_DN_STORE_FORMAT_OPAQUE_NAME, dn_format);
772         if (ret != LDB_SUCCESS) {
773                 talloc_free(p);
774                 return ret;
775         }
776
777         p->dereference = false;
778         p->normalise = false;
779
780         ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID);
781         if (ret != LDB_SUCCESS) {
782                 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
783                         "extended_dn_out: Unable to register control with rootdse!\n");
784                 return ldb_operr(ldb_module_get_ctx(module));
785         }
786
787         return ldb_next_init(module);
788 }
789
790 static int extended_dn_out_dereference_init(struct ldb_module *module, const char *attrs[])
791 {
792         int ret;
793         struct extended_dn_out_private *p = talloc_zero(module, struct extended_dn_out_private);
794         struct dsdb_extended_dn_store_format *dn_format;
795
796         ldb_module_set_private(module, p);
797
798         if (!p) {
799                 return ldb_module_oom(module);
800         }
801
802         dn_format = talloc(p, struct dsdb_extended_dn_store_format);
803         if (!dn_format) {
804                 talloc_free(p);
805                 return ldb_module_oom(module);
806         }
807
808         dn_format->store_extended_dn_in_ldb = false;
809
810         ret = ldb_set_opaque(ldb_module_get_ctx(module), DSDB_EXTENDED_DN_STORE_FORMAT_OPAQUE_NAME, dn_format);
811         if (ret != LDB_SUCCESS) {
812                 talloc_free(p);
813                 return ret;
814         }
815
816         p->dereference = true;
817
818         p->attrs = attrs;
819         /* At the moment, servers that need dereference also need the
820          * DN and attribute names to be normalised */
821         p->normalise = true;
822
823         ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID);
824         if (ret != LDB_SUCCESS) {
825                 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
826                           "extended_dn_out: Unable to register control with rootdse!\n");
827                 return ldb_operr(ldb_module_get_ctx(module));
828         }
829
830         return ldb_next_init(module);
831 }
832
833 static int extended_dn_out_openldap_init(struct ldb_module *module)
834 {
835         static const char *attrs[] = {
836                 "entryUUID",
837                 "objectSid",
838                 NULL
839         };
840
841         return extended_dn_out_dereference_init(module, attrs);
842 }
843
844 static int extended_dn_out_fds_init(struct ldb_module *module)
845 {
846         static const char *attrs[] = {
847                 "nsUniqueId",
848                 "sambaSID",
849                 NULL
850         };
851
852         return extended_dn_out_dereference_init(module, attrs);
853 }
854
855 _PUBLIC_ const struct ldb_module_ops ldb_extended_dn_out_ldb_module_ops = {
856         .name              = "extended_dn_out_ldb",
857         .search            = extended_dn_out_ldb_search,
858         .init_context      = extended_dn_out_ldb_init,
859 };
860
861 _PUBLIC_ const struct ldb_module_ops ldb_extended_dn_out_openldap_module_ops = {
862         .name              = "extended_dn_out_openldap",
863         .search            = extended_dn_out_openldap_search,
864         .init_context      = extended_dn_out_openldap_init,
865 };
866
867 _PUBLIC_ const struct ldb_module_ops ldb_extended_dn_out_fds_module_ops = {
868         .name              = "extended_dn_out_fds",
869         .search            = extended_dn_out_fds_search,
870         .init_context      = extended_dn_out_fds_init,
871 };