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