libcli/security Use common security.h
[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, 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
458         omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
459         if (!omd_value) {
460                 /* We can't make up a key version number without meta data */
461                 return LDB_SUCCESS;
462         }
463         if (!omd_value) {
464                 return LDB_SUCCESS;
465         }
466
467         omd = talloc(msg, struct replPropertyMetaDataBlob);
468         if (!omd) {
469                 ldb_module_oom(module);
470                 return LDB_SUCCESS;
471         }
472
473         ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
474                                        (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
475         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
476                 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
477                          ldb_dn_get_linearized(msg->dn)));
478                 return ldb_operr(ldb_module_get_ctx(module));
479         }
480
481         if (omd->version != 1) {
482                 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
483                          omd->version, ldb_dn_get_linearized(msg->dn)));
484                 talloc_free(omd);
485                 return LDB_SUCCESS;
486         }
487         for (i=0; i<omd->ctr.ctr1.count; i++) {
488                 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTRIBUTE_unicodePwd) {
489                         ldb_msg_add_fmt(msg, "msDS-KeyVersionNumber", "%u", omd->ctr.ctr1.array[i].version);
490                         break;
491                 }
492         }
493         return LDB_SUCCESS;
494
495 }
496
497 struct op_controls_flags {
498         bool sd;
499         bool bypassoperational;
500 };
501
502 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
503         if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
504                 return true;
505         }
506         return false;
507 }
508
509 /*
510   a list of attribute names that should be substituted in the parse
511   tree before the search is done
512 */
513 static const struct {
514         const char *attr;
515         const char *replace;
516 } parse_tree_sub[] = {
517         { "createTimestamp", "whenCreated" },
518         { "modifyTimestamp", "whenChanged" }
519 };
520
521
522 /*
523   a list of attribute names that are hidden, but can be searched for
524   using another (non-hidden) name to produce the correct result
525 */
526 static const struct {
527         const char *attr;
528         const char *replace;
529         const char *extra_attr;
530         int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope);
531 } search_sub[] = {
532         { "createTimestamp", "whenCreated", NULL , NULL },
533         { "modifyTimestamp", "whenChanged", NULL , NULL },
534         { "structuralObjectClass", "objectClass", NULL , NULL },
535         { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
536         { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
537         { "tokenGroups", "objectClass", NULL, construct_token_groups },
538         { "parentGUID", NULL, NULL, construct_parent_guid },
539         { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
540         { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
541         { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
542 };
543
544
545 enum op_remove {
546         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
547         OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
548         OPERATIONAL_SD_FLAGS,      /* show if SD_FLAGS_OID set, or asked for */
549         OPERATIONAL_REMOVE_UNLESS_CONTROL        /* remove always unless an adhoc control has been specified */
550 };
551
552 /*
553   a list of attributes that may need to be removed from the
554   underlying db return
555
556   Some of these are attributes that were once stored, but are now calculated
557 */
558 static const struct {
559         const char *attr;
560         enum op_remove op;
561 } operational_remove[] = {
562         { "nTSecurityDescriptor",    OPERATIONAL_SD_FLAGS },
563         { "msDS-KeyVersionNumber",   OPERATIONAL_REMOVE_UNLESS_CONTROL  },
564         { "parentGUID",              OPERATIONAL_REMOVE_ALWAYS  },
565         { "replPropertyMetaData",    OPERATIONAL_REMOVE_UNASKED },
566         { "unicodePwd",              OPERATIONAL_REMOVE_UNASKED },
567         { "dBCSPwd",                 OPERATIONAL_REMOVE_UNASKED },
568         { "ntPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
569         { "lmPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
570         { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
571 };
572
573
574 /*
575   post process a search result record. For any search_sub[] attributes that were
576   asked for, we need to call the appropriate copy routine to copy the result
577   into the message, then remove any attributes that we added to the search but
578   were not asked for by the user
579 */
580 static int operational_search_post_process(struct ldb_module *module,
581                                            struct ldb_message *msg,
582                                            enum ldb_scope scope,
583                                            const char * const *attrs_from_user,
584                                            const char * const *attrs_searched_for,
585                                            struct op_controls_flags* controls_flags)
586 {
587         struct ldb_context *ldb;
588         unsigned int i, a = 0;
589         bool constructed_attributes = false;
590
591         ldb = ldb_module_get_ctx(module);
592
593         /* removed any attrs that should not be shown to the user */
594         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
595                 switch (operational_remove[i].op) {
596                 case OPERATIONAL_REMOVE_UNASKED:
597                         if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
598                                 continue;
599                         }
600                         if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
601                                 continue;
602                         }
603                 case OPERATIONAL_REMOVE_ALWAYS:
604                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
605                         break;
606                 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
607                         if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
608                                 ldb_msg_remove_attr(msg, operational_remove[i].attr);
609                                 break;
610                         } else {
611                                 continue;
612                         }
613                 case OPERATIONAL_SD_FLAGS:
614                         if (controls_flags->sd ||
615                             ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
616                                 continue;
617                         }
618                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
619                         break;
620                 }
621         }
622
623         for (a=0;attrs_from_user && attrs_from_user[a];a++) {
624                 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
625                         continue;
626                 }
627                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
628                         if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
629                                 continue;
630                         }
631
632                         /* construct the new attribute, using either a supplied
633                            constructor or a simple copy */
634                         constructed_attributes = true;
635                         if (search_sub[i].constructor != NULL) {
636                                 if (search_sub[i].constructor(module, msg, scope) != LDB_SUCCESS) {
637                                         goto failed;
638                                 }
639                         } else if (ldb_msg_copy_attr(msg,
640                                                      search_sub[i].replace,
641                                                      search_sub[i].attr) != LDB_SUCCESS) {
642                                 goto failed;
643                         }
644                 }
645         }
646
647         /* Deletion of the search helper attributes are needed if:
648          * - we generated constructed attributes and
649          * - we aren't requesting all attributes
650          */
651         if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
652                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
653                         /* remove the added search helper attributes, unless
654                          * they were asked for by the user */
655                         if (search_sub[i].replace != NULL && 
656                             !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
657                                 ldb_msg_remove_attr(msg, search_sub[i].replace);
658                         }
659                         if (search_sub[i].extra_attr != NULL && 
660                             !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
661                                 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
662                         }
663                 }
664         }
665
666         return 0;
667
668 failed:
669         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
670                       "operational_search_post_process failed for attribute '%s'",
671                       attrs_from_user[a]);
672         return -1;
673 }
674
675 /*
676   hook search operations
677 */
678
679 struct operational_context {
680         struct ldb_module *module;
681         struct ldb_request *req;
682         enum ldb_scope scope;
683         const char * const *attrs;
684         struct op_controls_flags* controls_flags;
685 };
686
687 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
688 {
689         struct operational_context *ac;
690         int ret;
691
692         ac = talloc_get_type(req->context, struct operational_context);
693
694         if (!ares) {
695                 return ldb_module_done(ac->req, NULL, NULL,
696                                         LDB_ERR_OPERATIONS_ERROR);
697         }
698         if (ares->error != LDB_SUCCESS) {
699                 return ldb_module_done(ac->req, ares->controls,
700                                         ares->response, ares->error);
701         }
702
703         switch (ares->type) {
704         case LDB_REPLY_ENTRY:
705                 /* for each record returned post-process to add any derived
706                    attributes that have been asked for */
707                 ret = operational_search_post_process(ac->module,
708                                                       ares->message,
709                                                       ac->scope,
710                                                       ac->attrs,
711                                                       req->op.search.attrs,
712                                                       ac->controls_flags);
713                 if (ret != 0) {
714                         return ldb_module_done(ac->req, NULL, NULL,
715                                                 LDB_ERR_OPERATIONS_ERROR);
716                 }
717                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
718
719         case LDB_REPLY_REFERRAL:
720                 return ldb_module_send_referral(ac->req, ares->referral);
721
722         case LDB_REPLY_DONE:
723
724                 return ldb_module_done(ac->req, ares->controls,
725                                         ares->response, LDB_SUCCESS);
726         }
727
728         talloc_free(ares);
729         return LDB_SUCCESS;
730 }
731
732 static int operational_search(struct ldb_module *module, struct ldb_request *req)
733 {
734         struct ldb_context *ldb;
735         struct operational_context *ac;
736         struct ldb_request *down_req;
737         const char **search_attrs = NULL;
738         unsigned int i, a;
739         int ret;
740
741         /* There are no operational attributes on special DNs */
742         if (ldb_dn_is_special(req->op.search.base)) {
743                 return ldb_next_request(module, req);
744         }
745
746         ldb = ldb_module_get_ctx(module);
747
748         ac = talloc(req, struct operational_context);
749         if (ac == NULL) {
750                 return ldb_oom(ldb);
751         }
752
753         ac->module = module;
754         ac->req = req;
755         ac->scope = req->op.search.scope;
756         ac->attrs = req->op.search.attrs;
757
758         /*  FIXME: We must copy the tree and keep the original
759          *  unmodified. SSS */
760         /* replace any attributes in the parse tree that are
761            searchable, but are stored using a different name in the
762            backend */
763         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
764                 ldb_parse_tree_attr_replace(req->op.search.tree,
765                                             parse_tree_sub[i].attr,
766                                             parse_tree_sub[i].replace);
767         }
768
769         ac->controls_flags = talloc(ac, struct op_controls_flags);
770         /* remember if the SD_FLAGS_OID was set */
771         ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
772         /* remember if the LDB_CONTROL_BYPASSOPERATIONAL_OID */
773         ac->controls_flags->bypassoperational = (ldb_request_get_control(req,
774                                                         LDB_CONTROL_BYPASSOPERATIONAL_OID) != NULL);
775
776         /* in the list of attributes we are looking for, rename any
777            attributes to the alias for any hidden attributes that can
778            be fetched directly using non-hidden names */
779         for (a=0;ac->attrs && ac->attrs[a];a++) {
780                 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
781                         continue;
782                 }
783                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
784                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
785                             search_sub[i].replace) {
786
787                                 if (search_sub[i].extra_attr) {
788                                         const char **search_attrs2;
789                                         /* Only adds to the end of the list */
790                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
791                                                                                ? search_attrs
792                                                                                : ac->attrs, 
793                                                                                search_sub[i].extra_attr);
794                                         if (search_attrs2 == NULL) {
795                                                 return ldb_operr(ldb);
796                                         }
797                                         /* may be NULL, talloc_free() doesn't mind */
798                                         talloc_free(search_attrs);
799                                         search_attrs = search_attrs2;
800                                 }
801
802                                 if (!search_attrs) {
803                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
804                                         if (search_attrs == NULL) {
805                                                 return ldb_operr(ldb);
806                                         }
807                                 }
808                                 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
809                                 search_attrs[a] = search_sub[i].replace;
810                         }
811                 }
812         }
813
814         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
815                                         req->op.search.base,
816                                         req->op.search.scope,
817                                         req->op.search.tree,
818                                         /* use new set of attrs if any */
819                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
820                                         req->controls,
821                                         ac, operational_callback,
822                                         req);
823         LDB_REQ_SET_LOCATION(down_req);
824         if (ret != LDB_SUCCESS) {
825                 return ldb_operr(ldb);
826         }
827
828         /* perform the search */
829         return ldb_next_request(module, down_req);
830 }
831
832 static int operational_init(struct ldb_module *ctx)
833 {
834         struct operational_data *data;
835         int ret;
836         auth_init();
837
838         ret = ldb_next_init(ctx);
839
840         if (ret != LDB_SUCCESS) {
841                 return ret;
842         }
843
844         data = talloc_zero(ctx, struct operational_data);
845         if (!data) {
846                 return ldb_module_oom(ctx);
847         }
848
849         ldb_module_set_private(ctx, data);
850
851         return LDB_SUCCESS;
852 }
853
854 _PUBLIC_ const struct ldb_module_ops ldb_operational_module_ops = {
855         .name              = "operational",
856         .search            = operational_search,
857         .init_context      = operational_init
858 };