s4-auth rework session_info handling not to require an auth context
[samba.git] / source4 / dsdb / samdb / ldb_modules / operational.c
1 /*
2    ldb database library
3
4    Copyright (C) Andrew Tridgell 2005
5    Copyright (C) Simo Sorce 2006-2008
6    Copyright (C) Matthias Dieter Wallnöfer 2009
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /*
23   handle operational attributes
24  */
25
26 /*
27   createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
28   modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
29
30      for the above two, we do the search as normal, and if
31      createTimestamp or modifyTimestamp is asked for, then do
32      additional searches for whenCreated and whenChanged and fill in
33      the resulting values
34
35      we also need to replace these with the whenCreated/whenChanged
36      equivalent in the search expression trees
37
38   whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
39   whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
40
41      on init we need to setup attribute handlers for these so
42      comparisons are done correctly. The resolution is 1 second.
43
44      on add we need to add both the above, for current time
45
46      on modify we need to change whenChanged
47
48   structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
49
50      for this one we do the search as normal, then if requested ask
51      for objectclass, change the attribute name, and add it
52
53   primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
54
55      contains the RID of a certain group object
56     
57
58   attributeTypes: in schema only
59   objectClasses: in schema only
60   matchingRules: in schema only
61   matchingRuleUse: in schema only
62   creatorsName: not supported by w2k3?
63   modifiersName: not supported by w2k3?
64 */
65
66 #include "includes.h"
67 #include <ldb.h>
68 #include <ldb_module.h>
69
70 #include "librpc/gen_ndr/ndr_misc.h"
71 #include "librpc/gen_ndr/ndr_drsblobs.h"
72 #include "param/param.h"
73 #include "dsdb/samdb/samdb.h"
74 #include "dsdb/samdb/ldb_modules/util.h"
75
76 #include "auth/auth.h"
77 #include "libcli/security/security.h"
78
79 #ifndef ARRAY_SIZE
80 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
81 #endif
82
83 struct operational_data {
84         struct ldb_dn *aggregate_dn;
85 };
86
87 /*
88   construct a canonical name from a message
89 */
90 static int construct_canonical_name(struct ldb_module *module,
91         struct ldb_message *msg, enum ldb_scope scope)
92 {
93         char *canonicalName;
94         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
95         if (canonicalName == NULL) {
96                 return ldb_operr(ldb_module_get_ctx(module));
97         }
98         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
99 }
100
101 /*
102   construct a primary group token for groups from a message
103 */
104 static int construct_primary_group_token(struct ldb_module *module,
105                                          struct ldb_message *msg, enum ldb_scope scope)
106 {
107         struct ldb_context *ldb;
108         uint32_t primary_group_token;
109         
110         ldb = ldb_module_get_ctx(module);
111         if (ldb_match_msg_objectclass(msg, "group") == 1) {
112                 primary_group_token
113                         = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
114                 if (primary_group_token == 0) {
115                         return LDB_SUCCESS;
116                 }
117
118                 return samdb_msg_add_int(ldb, msg, msg, "primaryGroupToken",
119                         primary_group_token);
120         } else {
121                 return LDB_SUCCESS;
122         }
123 }
124
125 /*
126   construct the token groups for SAM objects from a message
127 */
128 static int construct_token_groups(struct ldb_module *module,
129                                   struct ldb_message *msg, enum ldb_scope scope)
130 {
131         struct ldb_context *ldb = ldb_module_get_ctx(module);;
132         struct auth_context *auth_context;
133         struct auth_serversupplied_info *server_info;
134         struct auth_session_info *session_info;
135         TALLOC_CTX *tmp_ctx = talloc_new(msg);
136         uint32_t i;
137         int ret;
138
139         NTSTATUS status;
140
141         if (scope != LDB_SCOPE_BASE) {
142                 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, this is not a BASE search");
143                 return LDB_ERR_OPERATIONS_ERROR;
144         }
145
146         status = auth_context_create_from_ldb(tmp_ctx, ldb, &auth_context);
147         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
148                 talloc_free(tmp_ctx);
149                 return ldb_module_oom(module);
150         } else if (!NT_STATUS_IS_OK(status)) {
151                 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, could not create authContext");
152                 talloc_free(tmp_ctx);
153                 return LDB_ERR_OPERATIONS_ERROR;
154         }
155
156         status = auth_get_server_info_principal(tmp_ctx, auth_context, NULL, msg->dn, &server_info);
157         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
158                 talloc_free(tmp_ctx);
159                 return ldb_module_oom(module);
160         } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
161                 /* Not a user, we have no tokenGroups */
162                 talloc_free(tmp_ctx);
163                 return LDB_SUCCESS;
164         } else if (!NT_STATUS_IS_OK(status)) {
165                 talloc_free(tmp_ctx);
166                 ldb_asprintf_errstring(ldb, "Cannot provide tokenGroups attribute: auth_get_server_info_principal failed: %s", nt_errstr(status));
167                 return LDB_ERR_OPERATIONS_ERROR;
168         }
169
170         status = auth_generate_session_info(tmp_ctx, auth_context->lp_ctx, ldb, server_info, 0, &session_info);
171         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
172                 talloc_free(tmp_ctx);
173                 return ldb_module_oom(module);
174         } else if (!NT_STATUS_IS_OK(status)) {
175                 talloc_free(tmp_ctx);
176                 ldb_asprintf_errstring(ldb, "Cannot provide tokenGroups attribute: auth_generate_session_info failed: %s", nt_errstr(status));
177                 return LDB_ERR_OPERATIONS_ERROR;
178         }
179
180         /* We start at 1, as the first SID is the user's SID, not included in the tokenGroups */
181         for (i = 1; i < session_info->security_token->num_sids; i++) {
182                 ret = samdb_msg_add_dom_sid(ldb, msg, msg,
183                                             "tokenGroups",
184                                             &session_info->security_token->sids[i]);
185                 if (ret != LDB_SUCCESS) {
186                         talloc_free(tmp_ctx);
187                         return ret;
188                 }
189         }
190
191         return LDB_SUCCESS;
192 }
193
194 /*
195   construct the parent GUID for an entry from a message
196 */
197 static int construct_parent_guid(struct ldb_module *module,
198                                  struct ldb_message *msg, enum ldb_scope scope)
199 {
200         struct ldb_result *res, *parent_res;
201         const struct ldb_val *parent_guid;
202         const char *attrs[] = { "instanceType", NULL };
203         const char *attrs2[] = { "objectGUID", NULL };
204         uint32_t instanceType;
205         int ret;
206         struct ldb_dn *parent_dn;
207         struct ldb_val v;
208
209         /* determine if the object is NC by instance type */
210         ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
211                                     DSDB_FLAG_NEXT_MODULE |
212                                     DSDB_SEARCH_SHOW_RECYCLED);
213
214         instanceType = ldb_msg_find_attr_as_uint(res->msgs[0],
215                                                  "instanceType", 0);
216         talloc_free(res);
217         if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
218                 DEBUG(4,(__location__ ": Object %s is NC\n",
219                          ldb_dn_get_linearized(msg->dn)));
220                 return LDB_SUCCESS;
221         }
222         parent_dn = ldb_dn_get_parent(msg, msg->dn);
223
224         if (parent_dn == NULL) {
225                 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
226                                          ldb_dn_get_linearized(msg->dn)));
227                 return LDB_SUCCESS;
228         }
229         ret = dsdb_module_search_dn(module, msg, &parent_res, parent_dn, attrs2,
230                                     DSDB_FLAG_NEXT_MODULE |
231                                     DSDB_SEARCH_SHOW_RECYCLED);
232         talloc_free(parent_dn);
233
234         /* not NC, so the object should have a parent*/
235         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
236                 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
237                          ldb_dn_get_linearized(msg->dn)));
238                 return ldb_operr(ldb_module_get_ctx(module));
239         } else if (ret != LDB_SUCCESS) {
240                 return ret;
241         }
242
243         parent_guid = ldb_msg_find_ldb_val(parent_res->msgs[0], "objectGUID");
244         if (!parent_guid) {
245                 talloc_free(parent_res);
246                 return LDB_SUCCESS;
247         }
248
249         v = data_blob_dup_talloc(parent_res, parent_guid);
250         if (!v.data) {
251                 talloc_free(parent_res);
252                 return ldb_oom(ldb_module_get_ctx(module));
253         }
254         ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
255         talloc_free(parent_res);
256         return ret;
257 }
258
259 /*
260   construct a subSchemaSubEntry
261 */
262 static int construct_subschema_subentry(struct ldb_module *module,
263                                         struct ldb_message *msg, enum ldb_scope scope)
264 {
265         struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
266         char *subSchemaSubEntry;
267
268         /* We may be being called before the init function has finished */
269         if (!data) {
270                 return LDB_SUCCESS;
271         }
272
273         /* Try and set this value up, if possible.  Don't worry if it
274          * fails, we may not have the DB set up yet, and it's not
275          * really vital anyway */
276         if (!data->aggregate_dn) {
277                 struct ldb_context *ldb = ldb_module_get_ctx(module);
278                 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
279         }
280
281         if (data->aggregate_dn) {
282                 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
283                 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
284         }
285         return LDB_SUCCESS;
286 }
287
288
289 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
290                                          struct ldb_message *msg,
291                                          struct ldb_message_element *object_category)
292 {
293         struct ldb_context *ldb;
294         struct ldb_dn *dn;
295         const struct ldb_val *val;
296
297         ldb = ldb_module_get_ctx(module);
298         if (!ldb) {
299                 DEBUG(4, (__location__ ": Failed to get ldb \n"));
300                 return ldb_operr(ldb);
301         }
302
303         dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
304         if (!dn) {
305                 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
306                           (const char *)object_category->values[0].data));
307                 return ldb_operr(ldb);
308         }
309
310         val = ldb_dn_get_rdn_val(dn);
311         if (!val) {
312                 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
313                           ldb_dn_get_linearized(dn)));
314                 return ldb_operr(ldb);
315         }
316
317         if (strequal((const char *)val->data, "NTDS-DSA")) {
318                 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
319         } else {
320                 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
321         }
322         return LDB_SUCCESS;
323 }
324
325 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
326                                                 struct ldb_message *msg,
327                                                 struct ldb_dn *dn)
328 {
329         struct ldb_dn *server_dn;
330         const char *attr_obj_cat[] = { "objectCategory", NULL };
331         struct ldb_result *res;
332         struct ldb_message_element *object_category;
333         int ret;
334
335         server_dn = ldb_dn_copy(msg, dn);
336         if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
337                 DEBUG(4, (__location__ ": Failed to add child to %s \n",
338                           ldb_dn_get_linearized(server_dn)));
339                 return ldb_operr(ldb_module_get_ctx(module));
340         }
341
342         ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat,
343                                     DSDB_FLAG_NEXT_MODULE);
344         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
345                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
346                                          ldb_dn_get_linearized(server_dn)));
347                 return LDB_SUCCESS;
348         } else if (ret != LDB_SUCCESS) {
349                 return ret;
350         }
351
352         object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
353         if (!object_category) {
354                 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
355                          ldb_dn_get_linearized(res->msgs[0]->dn)));
356                 return LDB_SUCCESS;
357         }
358         return construct_msds_isrodc_with_dn(module, msg, object_category);
359 }
360
361 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
362                                                   struct ldb_message *msg)
363 {
364         struct ldb_context *ldb;
365         const char *attr[] = { "serverReferenceBL", NULL };
366         struct ldb_result *res;
367         int ret;
368         struct ldb_dn *server_dn;
369
370         ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attr,
371                                     DSDB_FLAG_NEXT_MODULE);
372         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
373                 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
374                          ldb_dn_get_linearized(msg->dn)));
375                 return LDB_SUCCESS;
376         } else if (ret != LDB_SUCCESS) {
377                 return ret;
378         }
379
380         ldb = ldb_module_get_ctx(module);
381         if (!ldb) {
382                 return LDB_SUCCESS;
383         }
384
385         server_dn = ldb_msg_find_attr_as_dn(ldb, msg, res->msgs[0], "serverReferenceBL");
386         if (!server_dn) {
387                 DEBUG(4,(__location__ ": Can't find serverReferenceBL for %s \n",
388                          ldb_dn_get_linearized(res->msgs[0]->dn)));
389                 return LDB_SUCCESS;
390         }
391         return construct_msds_isrodc_with_server_dn(module, msg, server_dn);
392 }
393
394 /*
395   construct msDS-isRODC attr
396 */
397 static int construct_msds_isrodc(struct ldb_module *module,
398                                  struct ldb_message *msg, enum ldb_scope scope)
399 {
400         struct ldb_message_element * object_class;
401         struct ldb_message_element * object_category;
402         unsigned int i;
403
404         object_class = ldb_msg_find_element(msg, "objectClass");
405         if (!object_class) {
406                 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
407                          ldb_dn_get_linearized(msg->dn)));
408                 return ldb_operr(ldb_module_get_ctx(module));
409         }
410
411         for (i=0; i<object_class->num_values; i++) {
412                 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
413                         /* If TO!objectCategory  equals the DN of the classSchema  object for the nTDSDSA
414                          * object class, then TO!msDS-isRODC  is false. Otherwise, TO!msDS-isRODC  is true.
415                          */
416                         object_category = ldb_msg_find_element(msg, "objectCategory");
417                         if (!object_category) {
418                                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
419                                          ldb_dn_get_linearized(msg->dn)));
420                                 return LDB_SUCCESS;
421                         }
422                         return construct_msds_isrodc_with_dn(module, msg, object_category);
423                 }
424                 if (strequal((const char*)object_class->values[i].data, "server")) {
425                         /* Let TN be the nTDSDSA  object whose DN is "CN=NTDS Settings," prepended to
426                          * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA  object" case,
427                          * substituting TN for TO.
428                          */
429                         return construct_msds_isrodc_with_server_dn(module, msg, msg->dn);
430                 }
431                 if (strequal((const char*)object_class->values[i].data, "computer")) {
432                         /* Let TS be the server  object named by TO!serverReferenceBL. Apply the previous
433                          * rule for the "TO is a server  object" case, substituting TS for TO.
434                          */
435                         return construct_msds_isrodc_with_computer_dn(module, msg);
436                 }
437         }
438
439         return LDB_SUCCESS;
440 }
441
442
443 /*
444   construct msDS-keyVersionNumber attr
445
446   TODO:  Make this based on the 'win2k' DS huristics bit...
447
448 */
449 static int construct_msds_keyversionnumber(struct ldb_module *module,
450                                            struct ldb_message *msg,
451                                            enum ldb_scope scope)
452 {
453         uint32_t i;
454         enum ndr_err_code ndr_err;
455         const struct ldb_val *omd_value;
456         struct replPropertyMetaDataBlob *omd;
457         int ret;
458
459         omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
460         if (!omd_value) {
461                 /* We can't make up a key version number without meta data */
462                 return LDB_SUCCESS;
463         }
464         if (!omd_value) {
465                 return LDB_SUCCESS;
466         }
467
468         omd = talloc(msg, struct replPropertyMetaDataBlob);
469         if (!omd) {
470                 ldb_module_oom(module);
471                 return LDB_SUCCESS;
472         }
473
474         ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
475                                        (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
476         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
477                 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
478                          ldb_dn_get_linearized(msg->dn)));
479                 return ldb_operr(ldb_module_get_ctx(module));
480         }
481
482         if (omd->version != 1) {
483                 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
484                          omd->version, ldb_dn_get_linearized(msg->dn)));
485                 talloc_free(omd);
486                 return LDB_SUCCESS;
487         }
488         for (i=0; i<omd->ctr.ctr1.count; i++) {
489                 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTID_unicodePwd) {
490                         ret = samdb_msg_add_uint(ldb_module_get_ctx(module),
491                                                  msg, msg,
492                                                  "msDS-KeyVersionNumber",
493                                                  omd->ctr.ctr1.array[i].version);
494                         if (ret != LDB_SUCCESS) {
495                                 talloc_free(omd);
496                                 return ret;
497                         }
498                         break;
499                 }
500         }
501         return LDB_SUCCESS;
502
503 }
504
505 struct op_controls_flags {
506         bool sd;
507         bool bypassoperational;
508 };
509
510 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
511         if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
512                 return true;
513         }
514         return false;
515 }
516
517 /*
518   a list of attribute names that should be substituted in the parse
519   tree before the search is done
520 */
521 static const struct {
522         const char *attr;
523         const char *replace;
524 } parse_tree_sub[] = {
525         { "createTimestamp", "whenCreated" },
526         { "modifyTimestamp", "whenChanged" }
527 };
528
529
530 /*
531   a list of attribute names that are hidden, but can be searched for
532   using another (non-hidden) name to produce the correct result
533 */
534 static const struct {
535         const char *attr;
536         const char *replace;
537         const char *extra_attr;
538         int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope);
539 } search_sub[] = {
540         { "createTimestamp", "whenCreated", NULL , NULL },
541         { "modifyTimestamp", "whenChanged", NULL , NULL },
542         { "structuralObjectClass", NULL, NULL , NULL },
543         { "canonicalName", NULL, NULL , construct_canonical_name },
544         { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
545         { "tokenGroups", "objectClass", NULL, construct_token_groups },
546         { "parentGUID", NULL, NULL, construct_parent_guid },
547         { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
548         { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
549         { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
550 };
551
552
553 enum op_remove {
554         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
555         OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
556         OPERATIONAL_SD_FLAGS,      /* show if SD_FLAGS_OID set, or asked for */
557         OPERATIONAL_REMOVE_UNLESS_CONTROL        /* remove always unless an adhoc control has been specified */
558 };
559
560 /*
561   a list of attributes that may need to be removed from the
562   underlying db return
563
564   Some of these are attributes that were once stored, but are now calculated
565 */
566 static const struct {
567         const char *attr;
568         enum op_remove op;
569 } operational_remove[] = {
570         { "nTSecurityDescriptor",    OPERATIONAL_SD_FLAGS },
571         { "msDS-KeyVersionNumber",   OPERATIONAL_REMOVE_UNLESS_CONTROL  },
572         { "parentGUID",              OPERATIONAL_REMOVE_ALWAYS  },
573         { "replPropertyMetaData",    OPERATIONAL_REMOVE_UNASKED },
574 #define _SEP ,OPERATIONAL_REMOVE_UNASKED},{
575         { DSDB_SECRET_ATTRIBUTES_EX(_SEP), OPERATIONAL_REMOVE_UNASKED }
576 };
577
578
579 /*
580   post process a search result record. For any search_sub[] attributes that were
581   asked for, we need to call the appropriate copy routine to copy the result
582   into the message, then remove any attributes that we added to the search but
583   were not asked for by the user
584 */
585 static int operational_search_post_process(struct ldb_module *module,
586                                            struct ldb_message *msg,
587                                            enum ldb_scope scope,
588                                            const char * const *attrs_from_user,
589                                            const char * const *attrs_searched_for,
590                                            struct op_controls_flags* controls_flags)
591 {
592         struct ldb_context *ldb;
593         unsigned int i, a = 0;
594         bool constructed_attributes = false;
595
596         ldb = ldb_module_get_ctx(module);
597
598         /* removed any attrs that should not be shown to the user */
599         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
600                 switch (operational_remove[i].op) {
601                 case OPERATIONAL_REMOVE_UNASKED:
602                         if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
603                                 continue;
604                         }
605                         if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
606                                 continue;
607                         }
608                 case OPERATIONAL_REMOVE_ALWAYS:
609                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
610                         break;
611                 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
612                         if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
613                                 ldb_msg_remove_attr(msg, operational_remove[i].attr);
614                                 break;
615                         } else {
616                                 continue;
617                         }
618                 case OPERATIONAL_SD_FLAGS:
619                         if (controls_flags->sd ||
620                             ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
621                                 continue;
622                         }
623                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
624                         break;
625                 }
626         }
627
628         for (a=0;attrs_from_user && attrs_from_user[a];a++) {
629                 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
630                         continue;
631                 }
632                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
633                         if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
634                                 continue;
635                         }
636
637                         /* construct the new attribute, using either a supplied
638                            constructor or a simple copy */
639                         constructed_attributes = true;
640                         if (search_sub[i].constructor != NULL) {
641                                 if (search_sub[i].constructor(module, msg, scope) != LDB_SUCCESS) {
642                                         goto failed;
643                                 }
644                         } else if (ldb_msg_copy_attr(msg,
645                                                      search_sub[i].attr,
646                                                      search_sub[i].replace) != LDB_SUCCESS) {
647                                 goto failed;
648                         }
649                 }
650         }
651
652         /* Deletion of the search helper attributes are needed if:
653          * - we generated constructed attributes and
654          * - we aren't requesting all attributes
655          */
656         if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
657                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
658                         /* remove the added search helper attributes, unless
659                          * they were asked for by the user */
660                         if (search_sub[i].replace != NULL && 
661                             !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
662                                 ldb_msg_remove_attr(msg, search_sub[i].replace);
663                         }
664                         if (search_sub[i].extra_attr != NULL && 
665                             !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
666                                 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
667                         }
668                 }
669         }
670
671         return 0;
672
673 failed:
674         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
675                       "operational_search_post_process failed for attribute '%s' - %s",
676                       attrs_from_user[a], ldb_errstring(ldb));
677         return -1;
678 }
679
680 /*
681   hook search operations
682 */
683
684 struct operational_context {
685         struct ldb_module *module;
686         struct ldb_request *req;
687         enum ldb_scope scope;
688         const char * const *attrs;
689         struct op_controls_flags* controls_flags;
690 };
691
692 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
693 {
694         struct operational_context *ac;
695         int ret;
696
697         ac = talloc_get_type(req->context, struct operational_context);
698
699         if (!ares) {
700                 return ldb_module_done(ac->req, NULL, NULL,
701                                         LDB_ERR_OPERATIONS_ERROR);
702         }
703         if (ares->error != LDB_SUCCESS) {
704                 return ldb_module_done(ac->req, ares->controls,
705                                         ares->response, ares->error);
706         }
707
708         switch (ares->type) {
709         case LDB_REPLY_ENTRY:
710                 /* for each record returned post-process to add any derived
711                    attributes that have been asked for */
712                 ret = operational_search_post_process(ac->module,
713                                                       ares->message,
714                                                       ac->scope,
715                                                       ac->attrs,
716                                                       req->op.search.attrs,
717                                                       ac->controls_flags);
718                 if (ret != 0) {
719                         return ldb_module_done(ac->req, NULL, NULL,
720                                                 LDB_ERR_OPERATIONS_ERROR);
721                 }
722                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
723
724         case LDB_REPLY_REFERRAL:
725                 return ldb_module_send_referral(ac->req, ares->referral);
726
727         case LDB_REPLY_DONE:
728
729                 return ldb_module_done(ac->req, ares->controls,
730                                         ares->response, LDB_SUCCESS);
731         }
732
733         talloc_free(ares);
734         return LDB_SUCCESS;
735 }
736
737 static int operational_search(struct ldb_module *module, struct ldb_request *req)
738 {
739         struct ldb_context *ldb;
740         struct operational_context *ac;
741         struct ldb_request *down_req;
742         const char **search_attrs = NULL;
743         unsigned int i, a;
744         int ret;
745
746         /* There are no operational attributes on special DNs */
747         if (ldb_dn_is_special(req->op.search.base)) {
748                 return ldb_next_request(module, req);
749         }
750
751         ldb = ldb_module_get_ctx(module);
752
753         ac = talloc(req, struct operational_context);
754         if (ac == NULL) {
755                 return ldb_oom(ldb);
756         }
757
758         ac->module = module;
759         ac->req = req;
760         ac->scope = req->op.search.scope;
761         ac->attrs = req->op.search.attrs;
762
763         /*  FIXME: We must copy the tree and keep the original
764          *  unmodified. SSS */
765         /* replace any attributes in the parse tree that are
766            searchable, but are stored using a different name in the
767            backend */
768         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
769                 ldb_parse_tree_attr_replace(req->op.search.tree,
770                                             parse_tree_sub[i].attr,
771                                             parse_tree_sub[i].replace);
772         }
773
774         ac->controls_flags = talloc(ac, struct op_controls_flags);
775         /* remember if the SD_FLAGS_OID was set */
776         ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
777         /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
778         ac->controls_flags->bypassoperational =
779                 (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
780
781         /* in the list of attributes we are looking for, rename any
782            attributes to the alias for any hidden attributes that can
783            be fetched directly using non-hidden names */
784         for (a=0;ac->attrs && ac->attrs[a];a++) {
785                 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
786                         continue;
787                 }
788                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
789                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
790                             search_sub[i].replace) {
791
792                                 if (search_sub[i].extra_attr) {
793                                         const char **search_attrs2;
794                                         /* Only adds to the end of the list */
795                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
796                                                                                ? search_attrs
797                                                                                : ac->attrs, 
798                                                                                search_sub[i].extra_attr);
799                                         if (search_attrs2 == NULL) {
800                                                 return ldb_operr(ldb);
801                                         }
802                                         /* may be NULL, talloc_free() doesn't mind */
803                                         talloc_free(search_attrs);
804                                         search_attrs = search_attrs2;
805                                 }
806
807                                 if (!search_attrs) {
808                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
809                                         if (search_attrs == NULL) {
810                                                 return ldb_operr(ldb);
811                                         }
812                                 }
813                                 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
814                                 search_attrs[a] = search_sub[i].replace;
815                         }
816                 }
817         }
818
819         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
820                                         req->op.search.base,
821                                         req->op.search.scope,
822                                         req->op.search.tree,
823                                         /* use new set of attrs if any */
824                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
825                                         req->controls,
826                                         ac, operational_callback,
827                                         req);
828         LDB_REQ_SET_LOCATION(down_req);
829         if (ret != LDB_SUCCESS) {
830                 return ldb_operr(ldb);
831         }
832
833         /* perform the search */
834         return ldb_next_request(module, down_req);
835 }
836
837 static int operational_init(struct ldb_module *ctx)
838 {
839         struct operational_data *data;
840         int ret;
841         auth_init();
842
843         ret = ldb_next_init(ctx);
844
845         if (ret != LDB_SUCCESS) {
846                 return ret;
847         }
848
849         data = talloc_zero(ctx, struct operational_data);
850         if (!data) {
851                 return ldb_module_oom(ctx);
852         }
853
854         ldb_module_set_private(ctx, data);
855
856         return LDB_SUCCESS;
857 }
858
859 static const struct ldb_module_ops ldb_operational_module_ops = {
860         .name              = "operational",
861         .search            = operational_search,
862         .init_context      = operational_init
863 };
864
865 int ldb_operational_module_init(const char *version)
866 {
867         LDB_MODULE_CHECK_VERSION(version);
868         return ldb_register_module(&ldb_operational_module_ops);
869 }