idl: Use DRSUAPI_ATTID_ prefix instead of DRSUAPI_ATTRIBUTE_ for ATTID values
[sfrench/samba-autobuild/.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         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", "objectClass", NULL , NULL },
543         { "canonicalName", "distinguishedName", 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         { "unicodePwd",              OPERATIONAL_REMOVE_UNASKED },
575         { "dBCSPwd",                 OPERATIONAL_REMOVE_UNASKED },
576         { "ntPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
577         { "lmPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
578         { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
579 };
580
581
582 /*
583   post process a search result record. For any search_sub[] attributes that were
584   asked for, we need to call the appropriate copy routine to copy the result
585   into the message, then remove any attributes that we added to the search but
586   were not asked for by the user
587 */
588 static int operational_search_post_process(struct ldb_module *module,
589                                            struct ldb_message *msg,
590                                            enum ldb_scope scope,
591                                            const char * const *attrs_from_user,
592                                            const char * const *attrs_searched_for,
593                                            struct op_controls_flags* controls_flags)
594 {
595         struct ldb_context *ldb;
596         unsigned int i, a = 0;
597         bool constructed_attributes = false;
598
599         ldb = ldb_module_get_ctx(module);
600
601         /* removed any attrs that should not be shown to the user */
602         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
603                 switch (operational_remove[i].op) {
604                 case OPERATIONAL_REMOVE_UNASKED:
605                         if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
606                                 continue;
607                         }
608                         if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
609                                 continue;
610                         }
611                 case OPERATIONAL_REMOVE_ALWAYS:
612                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
613                         break;
614                 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
615                         if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
616                                 ldb_msg_remove_attr(msg, operational_remove[i].attr);
617                                 break;
618                         } else {
619                                 continue;
620                         }
621                 case OPERATIONAL_SD_FLAGS:
622                         if (controls_flags->sd ||
623                             ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
624                                 continue;
625                         }
626                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
627                         break;
628                 }
629         }
630
631         for (a=0;attrs_from_user && attrs_from_user[a];a++) {
632                 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
633                         continue;
634                 }
635                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
636                         if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
637                                 continue;
638                         }
639
640                         /* construct the new attribute, using either a supplied
641                            constructor or a simple copy */
642                         constructed_attributes = true;
643                         if (search_sub[i].constructor != NULL) {
644                                 if (search_sub[i].constructor(module, msg, scope) != LDB_SUCCESS) {
645                                         goto failed;
646                                 }
647                         } else if (ldb_msg_copy_attr(msg,
648                                                      search_sub[i].replace,
649                                                      search_sub[i].attr) != LDB_SUCCESS) {
650                                 goto failed;
651                         }
652                 }
653         }
654
655         /* Deletion of the search helper attributes are needed if:
656          * - we generated constructed attributes and
657          * - we aren't requesting all attributes
658          */
659         if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
660                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
661                         /* remove the added search helper attributes, unless
662                          * they were asked for by the user */
663                         if (search_sub[i].replace != NULL && 
664                             !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
665                                 ldb_msg_remove_attr(msg, search_sub[i].replace);
666                         }
667                         if (search_sub[i].extra_attr != NULL && 
668                             !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
669                                 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
670                         }
671                 }
672         }
673
674         return 0;
675
676 failed:
677         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
678                       "operational_search_post_process failed for attribute '%s'",
679                       attrs_from_user[a]);
680         return -1;
681 }
682
683 /*
684   hook search operations
685 */
686
687 struct operational_context {
688         struct ldb_module *module;
689         struct ldb_request *req;
690         enum ldb_scope scope;
691         const char * const *attrs;
692         struct op_controls_flags* controls_flags;
693 };
694
695 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
696 {
697         struct operational_context *ac;
698         int ret;
699
700         ac = talloc_get_type(req->context, struct operational_context);
701
702         if (!ares) {
703                 return ldb_module_done(ac->req, NULL, NULL,
704                                         LDB_ERR_OPERATIONS_ERROR);
705         }
706         if (ares->error != LDB_SUCCESS) {
707                 return ldb_module_done(ac->req, ares->controls,
708                                         ares->response, ares->error);
709         }
710
711         switch (ares->type) {
712         case LDB_REPLY_ENTRY:
713                 /* for each record returned post-process to add any derived
714                    attributes that have been asked for */
715                 ret = operational_search_post_process(ac->module,
716                                                       ares->message,
717                                                       ac->scope,
718                                                       ac->attrs,
719                                                       req->op.search.attrs,
720                                                       ac->controls_flags);
721                 if (ret != 0) {
722                         return ldb_module_done(ac->req, NULL, NULL,
723                                                 LDB_ERR_OPERATIONS_ERROR);
724                 }
725                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
726
727         case LDB_REPLY_REFERRAL:
728                 return ldb_module_send_referral(ac->req, ares->referral);
729
730         case LDB_REPLY_DONE:
731
732                 return ldb_module_done(ac->req, ares->controls,
733                                         ares->response, LDB_SUCCESS);
734         }
735
736         talloc_free(ares);
737         return LDB_SUCCESS;
738 }
739
740 static int operational_search(struct ldb_module *module, struct ldb_request *req)
741 {
742         struct ldb_context *ldb;
743         struct operational_context *ac;
744         struct ldb_request *down_req;
745         const char **search_attrs = NULL;
746         unsigned int i, a;
747         int ret;
748
749         /* There are no operational attributes on special DNs */
750         if (ldb_dn_is_special(req->op.search.base)) {
751                 return ldb_next_request(module, req);
752         }
753
754         ldb = ldb_module_get_ctx(module);
755
756         ac = talloc(req, struct operational_context);
757         if (ac == NULL) {
758                 return ldb_oom(ldb);
759         }
760
761         ac->module = module;
762         ac->req = req;
763         ac->scope = req->op.search.scope;
764         ac->attrs = req->op.search.attrs;
765
766         /*  FIXME: We must copy the tree and keep the original
767          *  unmodified. SSS */
768         /* replace any attributes in the parse tree that are
769            searchable, but are stored using a different name in the
770            backend */
771         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
772                 ldb_parse_tree_attr_replace(req->op.search.tree,
773                                             parse_tree_sub[i].attr,
774                                             parse_tree_sub[i].replace);
775         }
776
777         ac->controls_flags = talloc(ac, struct op_controls_flags);
778         /* remember if the SD_FLAGS_OID was set */
779         ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
780         /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
781         ac->controls_flags->bypassoperational =
782                 (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
783
784         /* in the list of attributes we are looking for, rename any
785            attributes to the alias for any hidden attributes that can
786            be fetched directly using non-hidden names */
787         for (a=0;ac->attrs && ac->attrs[a];a++) {
788                 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
789                         continue;
790                 }
791                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
792                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
793                             search_sub[i].replace) {
794
795                                 if (search_sub[i].extra_attr) {
796                                         const char **search_attrs2;
797                                         /* Only adds to the end of the list */
798                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
799                                                                                ? search_attrs
800                                                                                : ac->attrs, 
801                                                                                search_sub[i].extra_attr);
802                                         if (search_attrs2 == NULL) {
803                                                 return ldb_operr(ldb);
804                                         }
805                                         /* may be NULL, talloc_free() doesn't mind */
806                                         talloc_free(search_attrs);
807                                         search_attrs = search_attrs2;
808                                 }
809
810                                 if (!search_attrs) {
811                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
812                                         if (search_attrs == NULL) {
813                                                 return ldb_operr(ldb);
814                                         }
815                                 }
816                                 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
817                                 search_attrs[a] = search_sub[i].replace;
818                         }
819                 }
820         }
821
822         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
823                                         req->op.search.base,
824                                         req->op.search.scope,
825                                         req->op.search.tree,
826                                         /* use new set of attrs if any */
827                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
828                                         req->controls,
829                                         ac, operational_callback,
830                                         req);
831         LDB_REQ_SET_LOCATION(down_req);
832         if (ret != LDB_SUCCESS) {
833                 return ldb_operr(ldb);
834         }
835
836         /* perform the search */
837         return ldb_next_request(module, down_req);
838 }
839
840 static int operational_init(struct ldb_module *ctx)
841 {
842         struct operational_data *data;
843         int ret;
844         auth_init();
845
846         ret = ldb_next_init(ctx);
847
848         if (ret != LDB_SUCCESS) {
849                 return ret;
850         }
851
852         data = talloc_zero(ctx, struct operational_data);
853         if (!data) {
854                 return ldb_module_oom(ctx);
855         }
856
857         ldb_module_set_private(ctx, data);
858
859         return LDB_SUCCESS;
860 }
861
862 _PUBLIC_ const struct ldb_module_ops ldb_operational_module_ops = {
863         .name              = "operational",
864         .search            = operational_search,
865         .init_context      = operational_init
866 };