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