s4:security Change struct security_token->sids from struct dom_sid * to struct dom_sid
[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/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_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;
201         const struct ldb_val *parent_guid;
202         const char *attrs[] = { "objectGUID", NULL };
203         int ret;
204         struct ldb_val v;
205
206         /* TODO:  In the future, this needs to honour the partition boundaries */
207         struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
208
209         if (parent_dn == NULL) {
210                 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
211                                          ldb_dn_get_linearized(msg->dn)));
212                 return LDB_SUCCESS;
213         }
214
215         ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs,
216                                     DSDB_FLAG_NEXT_MODULE |
217                                     DSDB_SEARCH_SHOW_DELETED);
218         talloc_free(parent_dn);
219
220         /* if there is no parent 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_oom(ldb_module_get_ctx(module));
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_operr(ldb);
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_operr(ldb);
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_operr(ldb);
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_operr(ldb_module_get_ctx(module));
326         }
327
328         ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat,
329                                     DSDB_FLAG_NEXT_MODULE);
330         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
331                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
332                                          ldb_dn_get_linearized(server_dn)));
333                 return LDB_SUCCESS;
334         } else if (ret != LDB_SUCCESS) {
335                 return ret;
336         }
337
338         object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
339         if (!object_category) {
340                 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
341                          ldb_dn_get_linearized(res->msgs[0]->dn)));
342                 return LDB_SUCCESS;
343         }
344         return construct_msds_isrodc_with_dn(module, msg, object_category);
345 }
346
347 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
348                                                   struct ldb_message *msg)
349 {
350         struct ldb_context *ldb;
351         const char *attr[] = { "serverReferenceBL", NULL };
352         struct ldb_result *res;
353         int ret;
354         struct ldb_dn *server_dn;
355
356         ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attr,
357                                     DSDB_FLAG_NEXT_MODULE);
358         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
359                 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
360                          ldb_dn_get_linearized(msg->dn)));
361                 return LDB_SUCCESS;
362         } else if (ret != LDB_SUCCESS) {
363                 return ret;
364         }
365
366         ldb = ldb_module_get_ctx(module);
367         if (!ldb) {
368                 return LDB_SUCCESS;
369         }
370
371         server_dn = ldb_msg_find_attr_as_dn(ldb, msg, res->msgs[0], "serverReferenceBL");
372         if (!server_dn) {
373                 DEBUG(4,(__location__ ": Can't find serverReferenceBL for %s \n",
374                          ldb_dn_get_linearized(res->msgs[0]->dn)));
375                 return LDB_SUCCESS;
376         }
377         return construct_msds_isrodc_with_server_dn(module, msg, server_dn);
378 }
379
380 /*
381   construct msDS-isRODC attr
382 */
383 static int construct_msds_isrodc(struct ldb_module *module,
384                                  struct ldb_message *msg, enum ldb_scope scope)
385 {
386         struct ldb_message_element * object_class;
387         struct ldb_message_element * object_category;
388         unsigned int i;
389
390         object_class = ldb_msg_find_element(msg, "objectClass");
391         if (!object_class) {
392                 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
393                          ldb_dn_get_linearized(msg->dn)));
394                 return ldb_operr(ldb_module_get_ctx(module));
395         }
396
397         for (i=0; i<object_class->num_values; i++) {
398                 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
399                         /* If TO!objectCategory  equals the DN of the classSchema  object for the nTDSDSA
400                          * object class, then TO!msDS-isRODC  is false. Otherwise, TO!msDS-isRODC  is true.
401                          */
402                         object_category = ldb_msg_find_element(msg, "objectCategory");
403                         if (!object_category) {
404                                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
405                                          ldb_dn_get_linearized(msg->dn)));
406                                 return LDB_SUCCESS;
407                         }
408                         return construct_msds_isrodc_with_dn(module, msg, object_category);
409                 }
410                 if (strequal((const char*)object_class->values[i].data, "server")) {
411                         /* Let TN be the nTDSDSA  object whose DN is "CN=NTDS Settings," prepended to
412                          * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA  object" case,
413                          * substituting TN for TO.
414                          */
415                         return construct_msds_isrodc_with_server_dn(module, msg, msg->dn);
416                 }
417                 if (strequal((const char*)object_class->values[i].data, "computer")) {
418                         /* Let TS be the server  object named by TO!serverReferenceBL. Apply the previous
419                          * rule for the "TO is a server  object" case, substituting TS for TO.
420                          */
421                         return construct_msds_isrodc_with_computer_dn(module, msg);
422                 }
423         }
424
425         return LDB_SUCCESS;
426 }
427
428
429 /*
430   construct msDS-keyVersionNumber attr
431
432   TODO:  Make this based on the 'win2k' DS huristics bit...
433
434 */
435 static int construct_msds_keyversionnumber(struct ldb_module *module,
436                                            struct ldb_message *msg,
437                                            enum ldb_scope scope)
438 {
439         uint32_t i;
440         enum ndr_err_code ndr_err;
441         const struct ldb_val *omd_value;
442         struct replPropertyMetaDataBlob *omd;
443
444         omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
445         if (!omd_value) {
446                 /* We can't make up a key version number without meta data */
447                 return LDB_SUCCESS;
448         }
449         if (!omd_value) {
450                 return LDB_SUCCESS;
451         }
452
453         omd = talloc(msg, struct replPropertyMetaDataBlob);
454         if (!omd) {
455                 ldb_module_oom(module);
456                 return LDB_SUCCESS;
457         }
458
459         ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
460                                        (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
461         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
462                 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
463                          ldb_dn_get_linearized(msg->dn)));
464                 return ldb_operr(ldb_module_get_ctx(module));
465         }
466
467         if (omd->version != 1) {
468                 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
469                          omd->version, ldb_dn_get_linearized(msg->dn)));
470                 talloc_free(omd);
471                 return LDB_SUCCESS;
472         }
473         for (i=0; i<omd->ctr.ctr1.count; i++) {
474                 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTRIBUTE_unicodePwd) {
475                         ldb_msg_add_fmt(msg, "msDS-KeyVersionNumber", "%u", omd->ctr.ctr1.array[i].version);
476                         break;
477                 }
478         }
479         return LDB_SUCCESS;
480
481 }
482
483 struct op_controls_flags {
484         bool sd;
485         bool bypassoperational;
486 };
487
488 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
489         if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
490                 return true;
491         }
492         return false;
493 }
494
495 /*
496   a list of attribute names that should be substituted in the parse
497   tree before the search is done
498 */
499 static const struct {
500         const char *attr;
501         const char *replace;
502 } parse_tree_sub[] = {
503         { "createTimestamp", "whenCreated" },
504         { "modifyTimestamp", "whenChanged" }
505 };
506
507
508 /*
509   a list of attribute names that are hidden, but can be searched for
510   using another (non-hidden) name to produce the correct result
511 */
512 static const struct {
513         const char *attr;
514         const char *replace;
515         const char *extra_attr;
516         int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope);
517 } search_sub[] = {
518         { "createTimestamp", "whenCreated", NULL , NULL },
519         { "modifyTimestamp", "whenChanged", NULL , NULL },
520         { "structuralObjectClass", "objectClass", NULL , NULL },
521         { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
522         { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
523         { "tokenGroups", "objectClass", NULL, construct_token_groups },
524         { "parentGUID", NULL, NULL, construct_parent_guid },
525         { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
526         { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
527         { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
528 };
529
530
531 enum op_remove {
532         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
533         OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
534         OPERATIONAL_SD_FLAGS,      /* show if SD_FLAGS_OID set, or asked for */
535         OPERATIONAL_REMOVE_UNLESS_CONTROL        /* remove always unless an adhoc control has been specified */
536 };
537
538 /*
539   a list of attributes that may need to be removed from the
540   underlying db return
541
542   Some of these are attributes that were once stored, but are now calculated
543 */
544 static const struct {
545         const char *attr;
546         enum op_remove op;
547 } operational_remove[] = {
548         { "nTSecurityDescriptor",    OPERATIONAL_SD_FLAGS },
549         { "msDS-KeyVersionNumber",   OPERATIONAL_REMOVE_UNLESS_CONTROL  },
550         { "parentGUID",              OPERATIONAL_REMOVE_ALWAYS  },
551         { "replPropertyMetaData",    OPERATIONAL_REMOVE_UNASKED },
552         { "unicodePwd",              OPERATIONAL_REMOVE_UNASKED },
553         { "dBCSPwd",                 OPERATIONAL_REMOVE_UNASKED },
554         { "ntPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
555         { "lmPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
556         { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
557 };
558
559
560 /*
561   post process a search result record. For any search_sub[] attributes that were
562   asked for, we need to call the appropriate copy routine to copy the result
563   into the message, then remove any attributes that we added to the search but
564   were not asked for by the user
565 */
566 static int operational_search_post_process(struct ldb_module *module,
567                                            struct ldb_message *msg,
568                                            enum ldb_scope scope,
569                                            const char * const *attrs_from_user,
570                                            const char * const *attrs_searched_for,
571                                            struct op_controls_flags* controls_flags)
572 {
573         struct ldb_context *ldb;
574         unsigned int i, a = 0;
575         bool constructed_attributes = false;
576
577         ldb = ldb_module_get_ctx(module);
578
579         /* removed any attrs that should not be shown to the user */
580         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
581                 switch (operational_remove[i].op) {
582                 case OPERATIONAL_REMOVE_UNASKED:
583                         if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
584                                 continue;
585                         }
586                         if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
587                                 continue;
588                         }
589                 case OPERATIONAL_REMOVE_ALWAYS:
590                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
591                         break;
592                 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
593                         if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
594                                 ldb_msg_remove_attr(msg, operational_remove[i].attr);
595                                 break;
596                         } else {
597                                 continue;
598                         }
599                 case OPERATIONAL_SD_FLAGS:
600                         if (controls_flags->sd ||
601                             ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
602                                 continue;
603                         }
604                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
605                         break;
606                 }
607         }
608
609         for (a=0;attrs_from_user && attrs_from_user[a];a++) {
610                 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
611                         continue;
612                 }
613                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
614                         if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
615                                 continue;
616                         }
617
618                         /* construct the new attribute, using either a supplied
619                            constructor or a simple copy */
620                         constructed_attributes = true;
621                         if (search_sub[i].constructor != NULL) {
622                                 if (search_sub[i].constructor(module, msg, scope) != LDB_SUCCESS) {
623                                         goto failed;
624                                 }
625                         } else if (ldb_msg_copy_attr(msg,
626                                                      search_sub[i].replace,
627                                                      search_sub[i].attr) != LDB_SUCCESS) {
628                                 goto failed;
629                         }
630                 }
631         }
632
633         /* Deletion of the search helper attributes are needed if:
634          * - we generated constructed attributes and
635          * - we aren't requesting all attributes
636          */
637         if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
638                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
639                         /* remove the added search helper attributes, unless
640                          * they were asked for by the user */
641                         if (search_sub[i].replace != NULL && 
642                             !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
643                                 ldb_msg_remove_attr(msg, search_sub[i].replace);
644                         }
645                         if (search_sub[i].extra_attr != NULL && 
646                             !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
647                                 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
648                         }
649                 }
650         }
651
652         return 0;
653
654 failed:
655         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
656                       "operational_search_post_process failed for attribute '%s'",
657                       attrs_from_user[a]);
658         return -1;
659 }
660
661 /*
662   hook search operations
663 */
664
665 struct operational_context {
666         struct ldb_module *module;
667         struct ldb_request *req;
668         enum ldb_scope scope;
669         const char * const *attrs;
670         struct op_controls_flags* controls_flags;
671 };
672
673 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
674 {
675         struct operational_context *ac;
676         int ret;
677
678         ac = talloc_get_type(req->context, struct operational_context);
679
680         if (!ares) {
681                 return ldb_module_done(ac->req, NULL, NULL,
682                                         LDB_ERR_OPERATIONS_ERROR);
683         }
684         if (ares->error != LDB_SUCCESS) {
685                 return ldb_module_done(ac->req, ares->controls,
686                                         ares->response, ares->error);
687         }
688
689         switch (ares->type) {
690         case LDB_REPLY_ENTRY:
691                 /* for each record returned post-process to add any derived
692                    attributes that have been asked for */
693                 ret = operational_search_post_process(ac->module,
694                                                       ares->message,
695                                                       ac->scope,
696                                                       ac->attrs,
697                                                       req->op.search.attrs,
698                                                       ac->controls_flags);
699                 if (ret != 0) {
700                         return ldb_module_done(ac->req, NULL, NULL,
701                                                 LDB_ERR_OPERATIONS_ERROR);
702                 }
703                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
704
705         case LDB_REPLY_REFERRAL:
706                 return ldb_module_send_referral(ac->req, ares->referral);
707
708         case LDB_REPLY_DONE:
709
710                 return ldb_module_done(ac->req, ares->controls,
711                                         ares->response, LDB_SUCCESS);
712         }
713
714         talloc_free(ares);
715         return LDB_SUCCESS;
716 }
717
718 static int operational_search(struct ldb_module *module, struct ldb_request *req)
719 {
720         struct ldb_context *ldb;
721         struct operational_context *ac;
722         struct ldb_request *down_req;
723         const char **search_attrs = NULL;
724         unsigned int i, a;
725         int ret;
726
727         /* There are no operational attributes on special DNs */
728         if (ldb_dn_is_special(req->op.search.base)) {
729                 return ldb_next_request(module, req);
730         }
731
732         ldb = ldb_module_get_ctx(module);
733
734         ac = talloc(req, struct operational_context);
735         if (ac == NULL) {
736                 return ldb_oom(ldb);
737         }
738
739         ac->module = module;
740         ac->req = req;
741         ac->scope = req->op.search.scope;
742         ac->attrs = req->op.search.attrs;
743
744         /*  FIXME: We must copy the tree and keep the original
745          *  unmodified. SSS */
746         /* replace any attributes in the parse tree that are
747            searchable, but are stored using a different name in the
748            backend */
749         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
750                 ldb_parse_tree_attr_replace(req->op.search.tree,
751                                             parse_tree_sub[i].attr,
752                                             parse_tree_sub[i].replace);
753         }
754
755         ac->controls_flags = talloc(ac, struct op_controls_flags);
756         /* remember if the SD_FLAGS_OID was set */
757         ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
758         /* remember if the LDB_CONTROL_BYPASSOPERATIONAL_OID */
759         ac->controls_flags->bypassoperational = (ldb_request_get_control(req,
760                                                         LDB_CONTROL_BYPASSOPERATIONAL_OID) != NULL);
761
762         /* in the list of attributes we are looking for, rename any
763            attributes to the alias for any hidden attributes that can
764            be fetched directly using non-hidden names */
765         for (a=0;ac->attrs && ac->attrs[a];a++) {
766                 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
767                         continue;
768                 }
769                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
770                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
771                             search_sub[i].replace) {
772
773                                 if (search_sub[i].extra_attr) {
774                                         const char **search_attrs2;
775                                         /* Only adds to the end of the list */
776                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
777                                                                                ? search_attrs
778                                                                                : ac->attrs, 
779                                                                                search_sub[i].extra_attr);
780                                         if (search_attrs2 == NULL) {
781                                                 return ldb_operr(ldb);
782                                         }
783                                         /* may be NULL, talloc_free() doesn't mind */
784                                         talloc_free(search_attrs);
785                                         search_attrs = search_attrs2;
786                                 }
787
788                                 if (!search_attrs) {
789                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
790                                         if (search_attrs == NULL) {
791                                                 return ldb_operr(ldb);
792                                         }
793                                 }
794                                 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
795                                 search_attrs[a] = search_sub[i].replace;
796                         }
797                 }
798         }
799
800         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
801                                         req->op.search.base,
802                                         req->op.search.scope,
803                                         req->op.search.tree,
804                                         /* use new set of attrs if any */
805                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
806                                         req->controls,
807                                         ac, operational_callback,
808                                         req);
809         if (ret != LDB_SUCCESS) {
810                 return ldb_operr(ldb);
811         }
812
813         /* perform the search */
814         return ldb_next_request(module, down_req);
815 }
816
817 static int operational_init(struct ldb_module *ctx)
818 {
819         struct operational_data *data;
820         int ret;
821         auth_init();
822
823         ret = ldb_next_init(ctx);
824
825         if (ret != LDB_SUCCESS) {
826                 return ret;
827         }
828
829         data = talloc_zero(ctx, struct operational_data);
830         if (!data) {
831                 return ldb_module_oom(ctx);
832         }
833
834         ldb_module_set_private(ctx, data);
835
836         return LDB_SUCCESS;
837 }
838
839 _PUBLIC_ const struct ldb_module_ops ldb_operational_module_ops = {
840         .name              = "operational",
841         .search            = operational_search,
842         .init_context      = operational_init
843 };