s4:auth Change auth_generate_session_info to take flags
[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_includes.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/dom_sid.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_ERR_OPERATIONS_ERROR;
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                 ldb_module_oom(module);
150                 return LDB_ERR_OPERATIONS_ERROR;
151         } else if (!NT_STATUS_IS_OK(status)) {
152                 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, could not create authContext");
153                 talloc_free(tmp_ctx);
154                 return LDB_ERR_OPERATIONS_ERROR;
155         }
156
157         status = auth_get_server_info_principal(tmp_ctx, auth_context, NULL, msg->dn, &server_info);
158         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
159                 talloc_free(tmp_ctx);
160                 ldb_module_oom(module);
161                 return LDB_ERR_OPERATIONS_ERROR;
162         } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
163                 /* Not a user, we have no tokenGroups */
164                 talloc_free(tmp_ctx);
165                 return LDB_SUCCESS;
166         } else if (!NT_STATUS_IS_OK(status)) {
167                 talloc_free(tmp_ctx);
168                 ldb_asprintf_errstring(ldb, "Cannot provide tokenGroups attribute: auth_get_server_info_principal failed: %s", nt_errstr(status));
169                 return LDB_ERR_OPERATIONS_ERROR;
170         }
171
172         status = auth_generate_session_info(tmp_ctx, auth_context, server_info, 0, &session_info);
173         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
174                 talloc_free(tmp_ctx);
175                 ldb_module_oom(module);
176                 return LDB_ERR_OPERATIONS_ERROR;
177         } else if (!NT_STATUS_IS_OK(status)) {
178                 talloc_free(tmp_ctx);
179                 ldb_asprintf_errstring(ldb, "Cannot provide tokenGroups attribute: auth_generate_session_info failed: %s", nt_errstr(status));
180                 return LDB_ERR_OPERATIONS_ERROR;
181         }
182
183         /* We start at 1, as the first SID is the user's SID, not included in the tokenGroups */
184         for (i = 1; i < session_info->security_token->num_sids; i++) {
185                 ret = samdb_msg_add_dom_sid(ldb, msg, msg,
186                                             "tokenGroups",
187                                             session_info->security_token->sids[i]);
188                 if (ret != LDB_SUCCESS) {
189                         talloc_free(tmp_ctx);
190                         return ret;
191                 }
192         }
193
194         return LDB_SUCCESS;
195 }
196
197 /*
198   construct the parent GUID for an entry from a message
199 */
200 static int construct_parent_guid(struct ldb_module *module,
201                                  struct ldb_message *msg, enum ldb_scope scope)
202 {
203         struct ldb_result *res;
204         const struct ldb_val *parent_guid;
205         const char *attrs[] = { "objectGUID", NULL };
206         int ret;
207         struct ldb_val v;
208
209         /* TODO:  In the future, this needs to honour the partition boundaries */
210         struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
211
212         if (parent_dn == NULL) {
213                 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
214                                          ldb_dn_get_linearized(msg->dn)));
215                 return LDB_SUCCESS;
216         }
217
218         ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs, DSDB_SEARCH_SHOW_DELETED);
219         talloc_free(parent_dn);
220         /* if there is no parentGUID for this object, then return */
221         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
222                 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
223                          ldb_dn_get_linearized(msg->dn)));
224                 return LDB_SUCCESS;
225         } else if (ret != LDB_SUCCESS) {
226                 return ret;
227         }
228
229         parent_guid = ldb_msg_find_ldb_val(res->msgs[0], "objectGUID");
230         if (!parent_guid) {
231                 talloc_free(res);
232                 return LDB_SUCCESS;
233         }
234
235         v = data_blob_dup_talloc(res, parent_guid);
236         if (!v.data) {
237                 talloc_free(res);
238                 return LDB_ERR_OPERATIONS_ERROR;
239         }
240         ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
241         talloc_free(res);
242         return ret;
243 }
244
245 /*
246   construct a subSchemaSubEntry
247 */
248 static int construct_subschema_subentry(struct ldb_module *module,
249                                         struct ldb_message *msg, enum ldb_scope scope)
250 {
251         struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
252         char *subSchemaSubEntry;
253
254         /* We may be being called before the init function has finished */
255         if (!data) {
256                 return LDB_SUCCESS;
257         }
258
259         /* Try and set this value up, if possible.  Don't worry if it
260          * fails, we may not have the DB set up yet, and it's not
261          * really vital anyway */
262         if (!data->aggregate_dn) {
263                 struct ldb_context *ldb = ldb_module_get_ctx(module);
264                 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
265         }
266
267         if (data->aggregate_dn) {
268                 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
269                 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
270         }
271         return LDB_SUCCESS;
272 }
273
274
275 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
276                                          struct ldb_message *msg,
277                                          struct ldb_message_element *object_category)
278 {
279         struct ldb_context *ldb;
280         struct ldb_dn *dn;
281         const struct ldb_val *val;
282
283         ldb = ldb_module_get_ctx(module);
284         if (!ldb) {
285                 DEBUG(4, (__location__ ": Failed to get ldb \n"));
286                 return LDB_ERR_OPERATIONS_ERROR;
287         }
288
289         dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
290         if (!dn) {
291                 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
292                           (const char *)object_category->values[0].data));
293                 return LDB_ERR_OPERATIONS_ERROR;
294         }
295
296         val = ldb_dn_get_rdn_val(dn);
297         if (!val) {
298                 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
299                           ldb_dn_get_linearized(dn)));
300                 return LDB_ERR_OPERATIONS_ERROR;
301         }
302
303         if (strequal((const char *)val->data, "NTDS-DSA")) {
304                 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
305         } else {
306                 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
307         }
308         return LDB_SUCCESS;
309 }
310
311 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
312                                                 struct ldb_message *msg,
313                                                 struct ldb_dn *dn)
314 {
315         struct ldb_dn *server_dn;
316         const char *attr_obj_cat[] = { "objectCategory", NULL };
317         struct ldb_result *res;
318         struct ldb_message_element *object_category;
319         int ret;
320
321         server_dn = ldb_dn_copy(msg, dn);
322         if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
323                 DEBUG(4, (__location__ ": Failed to add child to %s \n",
324                           ldb_dn_get_linearized(server_dn)));
325                 return LDB_ERR_OPERATIONS_ERROR;
326         }
327
328         ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat, 0);
329         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
330                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
331                                          ldb_dn_get_linearized(server_dn)));
332                 return LDB_SUCCESS;
333         } else if (ret != LDB_SUCCESS) {
334                 return ret;
335         }
336
337         object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
338         if (!object_category) {
339                 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
340                          ldb_dn_get_linearized(res->msgs[0]->dn)));
341                 return LDB_SUCCESS;
342         }
343         return construct_msds_isrodc_with_dn(module, msg, object_category);
344 }
345
346 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
347                                                   struct ldb_message *msg)
348 {
349         struct ldb_context *ldb;
350         const char *attr[] = { "serverReferenceBL", NULL };
351         struct ldb_result *res;
352         int ret;
353         struct ldb_dn *server_dn;
354
355         ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attr, 0);
356         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
357                 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
358                          ldb_dn_get_linearized(msg->dn)));
359                 return LDB_SUCCESS;
360         } else if (ret != LDB_SUCCESS) {
361                 return ret;
362         }
363
364         ldb = ldb_module_get_ctx(module);
365         if (!ldb) {
366                 return LDB_SUCCESS;
367         }
368
369         server_dn = ldb_msg_find_attr_as_dn(ldb, msg, res->msgs[0], "serverReferenceBL");
370         if (!server_dn) {
371                 DEBUG(4,(__location__ ": Can't find serverReferenceBL for %s \n",
372                          ldb_dn_get_linearized(res->msgs[0]->dn)));
373                 return LDB_SUCCESS;
374         }
375         return construct_msds_isrodc_with_server_dn(module, msg, server_dn);
376 }
377
378 /*
379   construct msDS-isRODC attr
380 */
381 static int construct_msds_isrodc(struct ldb_module *module, struct ldb_message *msg)
382 {
383         struct ldb_message_element * object_class;
384         struct ldb_message_element * object_category;
385         unsigned int i;
386
387         object_class = ldb_msg_find_element(msg, "objectClass");
388         if (!object_class) {
389                 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
390                          ldb_dn_get_linearized(msg->dn)));
391                 return LDB_ERR_OPERATIONS_ERROR;
392         }
393
394         for (i=0; i<object_class->num_values; i++) {
395                 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
396                         /* If TO!objectCategory  equals the DN of the classSchema  object for the nTDSDSA
397                          * object class, then TO!msDS-isRODC  is false. Otherwise, TO!msDS-isRODC  is true.
398                          */
399                         object_category = ldb_msg_find_element(msg, "objectCategory");
400                         if (!object_category) {
401                                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
402                                          ldb_dn_get_linearized(msg->dn)));
403                                 return LDB_SUCCESS;
404                         }
405                         return construct_msds_isrodc_with_dn(module, msg, object_category);
406                 }
407                 if (strequal((const char*)object_class->values[i].data, "server")) {
408                         /* Let TN be the nTDSDSA  object whose DN is "CN=NTDS Settings," prepended to
409                          * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA  object" case,
410                          * substituting TN for TO.
411                          */
412                         return construct_msds_isrodc_with_server_dn(module, msg, msg->dn);
413                 }
414                 if (strequal((const char*)object_class->values[i].data, "computer")) {
415                         /* Let TS be the server  object named by TO!serverReferenceBL. Apply the previous
416                          * rule for the "TO is a server  object" case, substituting TS for TO.
417                          */
418                         return construct_msds_isrodc_with_computer_dn(module, msg);
419                 }
420         }
421
422         return LDB_SUCCESS;
423 }
424
425
426 /*
427   construct msDS-keyVersionNumber attr
428
429   TODO:  Make this based on the 'win2k' DS huristics bit...
430
431 */
432 static int construct_msds_keyversionnumber(struct ldb_module *module, struct ldb_message *msg)
433 {
434         uint32_t i;
435         enum ndr_err_code ndr_err;
436         const struct ldb_val *omd_value;
437         struct replPropertyMetaDataBlob *omd;
438         struct ldb_context *ldb = ldb_module_get_ctx(module);
439
440         omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
441         if (!omd_value) {
442                 /* We can't make up a key version number without meta data */
443                 return LDB_SUCCESS;
444         }
445         if (!omd_value) {
446                 return LDB_SUCCESS;
447         }
448
449         omd = talloc(msg, struct replPropertyMetaDataBlob);
450         if (!omd) {
451                 ldb_module_oom(module);
452                 return LDB_SUCCESS;
453         }
454
455         ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
456                                        (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
457         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
458                 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
459                          ldb_dn_get_linearized(msg->dn)));
460                 return LDB_ERR_OPERATIONS_ERROR;
461         }
462
463         if (omd->version != 1) {
464                 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
465                          omd->version, ldb_dn_get_linearized(msg->dn)));
466                 talloc_free(omd);
467                 return LDB_SUCCESS;
468         }
469         for (i=0; i<omd->ctr.ctr1.count; i++) {
470                 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTRIBUTE_unicodePwd) {
471                         ldb_msg_add_fmt(msg, "msDS-KeyVersionNumber", "%u", omd->ctr.ctr1.array[i].version);
472                         break;
473                 }
474         }
475         return LDB_SUCCESS;
476
477 }
478
479 /*
480   a list of attribute names that should be substituted in the parse
481   tree before the search is done
482 */
483 static const struct {
484         const char *attr;
485         const char *replace;
486 } parse_tree_sub[] = {
487         { "createTimestamp", "whenCreated" },
488         { "modifyTimestamp", "whenChanged" }
489 };
490
491
492 /*
493   a list of attribute names that are hidden, but can be searched for
494   using another (non-hidden) name to produce the correct result
495 */
496 static const struct {
497         const char *attr;
498         const char *replace;
499         const char *extra_attr;
500         int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope);
501 } search_sub[] = {
502         { "createTimestamp", "whenCreated", NULL , NULL },
503         { "modifyTimestamp", "whenChanged", NULL , NULL },
504         { "structuralObjectClass", "objectClass", NULL , NULL },
505         { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
506         { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
507         { "tokenGroups", "objectClass", NULL, construct_token_groups },
508         { "parentGUID", NULL, NULL, construct_parent_guid },
509         { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
510         { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
511         { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
512 };
513
514
515 enum op_remove {
516         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
517         OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
518         OPERATIONAL_SD_FLAGS       /* show if SD_FLAGS_OID set, or asked for */
519 };
520
521 /*
522   a list of attributes that may need to be removed from the
523   underlying db return
524
525   Some of these are attributes that were once stored, but are now calculated
526 */
527 static const struct {
528         const char *attr;
529         enum op_remove op;
530 } operational_remove[] = {
531         { "nTSecurityDescriptor",    OPERATIONAL_SD_FLAGS },
532         { "msDS-KeyVersionNumber",   OPERATIONAL_REMOVE_ALWAYS  },
533         { "parentGUID",              OPERATIONAL_REMOVE_ALWAYS  },
534         { "replPropertyMetaData",    OPERATIONAL_REMOVE_UNASKED },
535         { "unicodePwd",              OPERATIONAL_REMOVE_UNASKED },
536         { "dBCSPwd",                 OPERATIONAL_REMOVE_UNASKED },
537         { "ntPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
538         { "lmPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
539         { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
540 };
541
542
543 /*
544   post process a search result record. For any search_sub[] attributes that were
545   asked for, we need to call the appropriate copy routine to copy the result
546   into the message, then remove any attributes that we added to the search but
547   were not asked for by the user
548 */
549 static int operational_search_post_process(struct ldb_module *module,
550                                            struct ldb_message *msg,
551                                            enum ldb_scope scope,
552                                            const char * const *attrs_from_user,
553                                            const char * const *attrs_searched_for,
554                                            bool sd_flags_set)
555 {
556         struct ldb_context *ldb;
557         unsigned int i, a = 0;
558         bool constructed_attributes = false;
559
560         ldb = ldb_module_get_ctx(module);
561
562         /* removed any attrs that should not be shown to the user */
563         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
564                 switch (operational_remove[i].op) {
565                 case OPERATIONAL_REMOVE_UNASKED:
566                         if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
567                                 continue;
568                         }
569                         if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
570                                 continue;
571                         }
572                 case OPERATIONAL_REMOVE_ALWAYS:
573                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
574                         break;
575                 case OPERATIONAL_SD_FLAGS:
576                         if (sd_flags_set ||
577                             ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
578                                 continue;
579                         }
580                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
581                         break;
582                 }
583         }
584
585         for (a=0;attrs_from_user && attrs_from_user[a];a++) {
586                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
587                         if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
588                                 continue;
589                         }
590
591                         /* construct the new attribute, using either a supplied
592                            constructor or a simple copy */
593                         constructed_attributes = true;
594                         if (search_sub[i].constructor != NULL) {
595                                 if (search_sub[i].constructor(module, msg, scope) != LDB_SUCCESS) {
596                                         goto failed;
597                                 }
598                         } else if (ldb_msg_copy_attr(msg,
599                                                      search_sub[i].replace,
600                                                      search_sub[i].attr) != LDB_SUCCESS) {
601                                 goto failed;
602                         }
603                 }
604         }
605
606         /* Deletion of the search helper attributes are needed if:
607          * - we generated constructed attributes and
608          * - we aren't requesting all attributes
609          */
610         if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
611                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
612                         /* remove the added search helper attributes, unless
613                          * they were asked for by the user */
614                         if (search_sub[i].replace != NULL && 
615                             !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
616                                 ldb_msg_remove_attr(msg, search_sub[i].replace);
617                         }
618                         if (search_sub[i].extra_attr != NULL && 
619                             !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
620                                 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
621                         }
622                 }
623         }
624
625         return 0;
626
627 failed:
628         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
629                       "operational_search_post_process failed for attribute '%s'",
630                       attrs_from_user[a]);
631         return -1;
632 }
633
634
635 /*
636   hook search operations
637 */
638
639 struct operational_context {
640         struct ldb_module *module;
641         struct ldb_request *req;
642         enum ldb_scope scope;
643         const char * const *attrs;
644         bool sd_flags_set;
645 };
646
647 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
648 {
649         struct operational_context *ac;
650         int ret;
651
652         ac = talloc_get_type(req->context, struct operational_context);
653
654         if (!ares) {
655                 return ldb_module_done(ac->req, NULL, NULL,
656                                         LDB_ERR_OPERATIONS_ERROR);
657         }
658         if (ares->error != LDB_SUCCESS) {
659                 return ldb_module_done(ac->req, ares->controls,
660                                         ares->response, ares->error);
661         }
662
663         switch (ares->type) {
664         case LDB_REPLY_ENTRY:
665                 /* for each record returned post-process to add any derived
666                    attributes that have been asked for */
667                 ret = operational_search_post_process(ac->module,
668                                                       ares->message,
669                                                       ac->scope,
670                                                       ac->attrs,
671                                                       req->op.search.attrs,
672                                                       ac->sd_flags_set);
673                 if (ret != 0) {
674                         return ldb_module_done(ac->req, NULL, NULL,
675                                                 LDB_ERR_OPERATIONS_ERROR);
676                 }
677                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
678
679         case LDB_REPLY_REFERRAL:
680                 return ldb_module_send_referral(ac->req, ares->referral);
681
682         case LDB_REPLY_DONE:
683
684                 return ldb_module_done(ac->req, ares->controls,
685                                         ares->response, LDB_SUCCESS);
686         }
687
688         talloc_free(ares);
689         return LDB_SUCCESS;
690 }
691
692 static int operational_search(struct ldb_module *module, struct ldb_request *req)
693 {
694         struct ldb_context *ldb;
695         struct operational_context *ac;
696         struct ldb_request *down_req;
697         const char **search_attrs = NULL;
698         unsigned int i, a;
699         int ret;
700
701         /* There are no operational attributes on special DNs */
702         if (ldb_dn_is_special(req->op.search.base)) {
703                 return ldb_next_request(module, req);
704         }
705
706         ldb = ldb_module_get_ctx(module);
707
708         ac = talloc(req, struct operational_context);
709         if (ac == NULL) {
710                 return LDB_ERR_OPERATIONS_ERROR;
711         }
712
713         ac->module = module;
714         ac->req = req;
715         ac->scope = req->op.search.scope;
716         ac->attrs = req->op.search.attrs;
717
718         /*  FIXME: We must copy the tree and keep the original
719          *  unmodified. SSS */
720         /* replace any attributes in the parse tree that are
721            searchable, but are stored using a different name in the
722            backend */
723         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
724                 ldb_parse_tree_attr_replace(req->op.search.tree,
725                                             parse_tree_sub[i].attr,
726                                             parse_tree_sub[i].replace);
727         }
728
729         /* in the list of attributes we are looking for, rename any
730            attributes to the alias for any hidden attributes that can
731            be fetched directly using non-hidden names */
732         for (a=0;ac->attrs && ac->attrs[a];a++) {
733                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
734                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
735                             search_sub[i].replace) {
736
737                                 if (search_sub[i].extra_attr) {
738                                         const char **search_attrs2;
739                                         /* Only adds to the end of the list */
740                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
741                                                                                ? search_attrs
742                                                                                : ac->attrs, 
743                                                                                search_sub[i].extra_attr);
744                                         if (search_attrs2 == NULL) {
745                                                 return LDB_ERR_OPERATIONS_ERROR;
746                                         }
747                                         /* may be NULL, talloc_free() doesn't mind */
748                                         talloc_free(search_attrs);
749                                         search_attrs = search_attrs2;
750                                 }
751
752                                 if (!search_attrs) {
753                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
754                                         if (search_attrs == NULL) {
755                                                 return LDB_ERR_OPERATIONS_ERROR;
756                                         }
757                                 }
758                                 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
759                                 search_attrs[a] = search_sub[i].replace;
760                         }
761                 }
762         }
763
764         /* remember if the SD_FLAGS_OID was set */
765         ac->sd_flags_set = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
766
767         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
768                                         req->op.search.base,
769                                         req->op.search.scope,
770                                         req->op.search.tree,
771                                         /* use new set of attrs if any */
772                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
773                                         req->controls,
774                                         ac, operational_callback,
775                                         req);
776         if (ret != LDB_SUCCESS) {
777                 return LDB_ERR_OPERATIONS_ERROR;
778         }
779
780         /* perform the search */
781         return ldb_next_request(module, down_req);
782 }
783
784 static int operational_init(struct ldb_module *ctx)
785 {
786         struct operational_data *data;
787         int ret;
788         auth_init();
789
790         ret = ldb_next_init(ctx);
791
792         if (ret != LDB_SUCCESS) {
793                 return ret;
794         }
795
796         data = talloc_zero(ctx, struct operational_data);
797         if (!data) {
798                 ldb_module_oom(ctx);
799                 return LDB_ERR_OPERATIONS_ERROR;
800         }
801
802         ldb_module_set_private(ctx, data);
803
804         return LDB_SUCCESS;
805 }
806
807 const struct ldb_module_ops ldb_operational_module_ops = {
808         .name              = "operational",
809         .search            = operational_search,
810         .init_context      = operational_init
811 };