9b56de2ae5b99ef058d7e7ac86e15426a8a8dfdd
[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 "dsdb/samdb/samdb.h"
74 #include "dsdb/samdb/ldb_modules/util.h"
75
76 #include "auth/auth.h"
77
78 #ifndef ARRAY_SIZE
79 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
80 #endif
81
82 #undef strcasecmp
83
84 struct operational_data {
85         struct ldb_dn *aggregate_dn;
86 };
87
88 enum search_type {
89         TOKEN_GROUPS,
90         TOKEN_GROUPS_GLOBAL_AND_UNIVERSAL,
91         TOKEN_GROUPS_NO_GC_ACCEPTABLE,
92
93         /*
94          * MS-DRSR 4.1.8.1.3 RevMembGetAccountGroups: Transitive membership in
95          * all account groups in a given domain, excluding built-in groups.
96          * (Used internally for msDS-ResultantPSO support)
97          */
98         ACCOUNT_GROUPS
99 };
100
101 static int get_pso_for_user(struct ldb_module *module,
102                             struct ldb_message *user_msg,
103                             struct ldb_request *parent,
104                             struct ldb_message **pso_msg);
105
106 /*
107   construct a canonical name from a message
108 */
109 static int construct_canonical_name(struct ldb_module *module,
110                                     struct ldb_message *msg, enum ldb_scope scope,
111                                     struct ldb_request *parent, struct ldb_reply *ares)
112 {
113         char *canonicalName;
114         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
115         if (canonicalName == NULL) {
116                 return ldb_operr(ldb_module_get_ctx(module));
117         }
118         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
119 }
120
121 /*
122   construct a primary group token for groups from a message
123 */
124 static int construct_primary_group_token(struct ldb_module *module,
125                                          struct ldb_message *msg, enum ldb_scope scope,
126                                          struct ldb_request *parent, struct ldb_reply *ares)
127 {
128         struct ldb_context *ldb;
129         uint32_t primary_group_token;
130
131         ldb = ldb_module_get_ctx(module);
132         if (ldb_match_msg_objectclass(msg, "group") == 1) {
133                 primary_group_token
134                         = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
135                 if (primary_group_token == 0) {
136                         return LDB_SUCCESS;
137                 }
138
139                 return samdb_msg_add_uint(ldb, msg, msg, "primaryGroupToken",
140                         primary_group_token);
141         } else {
142                 return LDB_SUCCESS;
143         }
144 }
145
146 /*
147  * Returns the group SIDs for the user in the given LDB message
148  */
149 static int get_group_sids(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
150                           struct ldb_message *msg, const char *attribute_string,
151                           enum search_type type, struct auth_SidAttr **groupSIDs,
152                           uint32_t *num_groupSIDs)
153 {
154         const char *filter = NULL;
155         NTSTATUS status;
156         struct dom_sid *primary_group_sid;
157         const char *primary_group_string;
158         const char *primary_group_dn;
159         DATA_BLOB primary_group_blob;
160         struct dom_sid *account_sid;
161         const char *account_sid_string;
162         const char *account_sid_dn;
163         DATA_BLOB account_sid_blob;
164         struct dom_sid *domain_sid;
165
166         /* If it's not a user, it won't have a primaryGroupID */
167         if (ldb_msg_find_element(msg, "primaryGroupID") == NULL) {
168                 return LDB_SUCCESS;
169         }
170
171         /* Ensure it has an objectSID too */
172         account_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
173         if (account_sid == NULL) {
174                 return LDB_SUCCESS;
175         }
176
177         status = dom_sid_split_rid(mem_ctx, account_sid, &domain_sid, NULL);
178         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
179                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
180         } else if (!NT_STATUS_IS_OK(status)) {
181                 return LDB_ERR_OPERATIONS_ERROR;
182         }
183
184         primary_group_sid = dom_sid_add_rid(mem_ctx,
185                                             domain_sid,
186                                             ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
187         if (!primary_group_sid) {
188                 return ldb_oom(ldb);
189         }
190
191         /* only return security groups */
192         switch(type) {
193         case TOKEN_GROUPS_GLOBAL_AND_UNIVERSAL:
194                 filter = talloc_asprintf(mem_ctx,
195                                          "(&(objectClass=group)"
196                                          "(groupType:"LDB_OID_COMPARATOR_AND":=%u)"
197                                          "(groupType:"LDB_OID_COMPARATOR_OR":=%u))",
198                                          GROUP_TYPE_SECURITY_ENABLED,
199                                          GROUP_TYPE_ACCOUNT_GROUP | GROUP_TYPE_UNIVERSAL_GROUP);
200                 break;
201         case TOKEN_GROUPS_NO_GC_ACCEPTABLE:
202         case TOKEN_GROUPS:
203                 filter = talloc_asprintf(mem_ctx,
204                                          "(&(objectClass=group)"
205                                          "(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
206                                          GROUP_TYPE_SECURITY_ENABLED);
207                 break;
208
209         /* for RevMembGetAccountGroups, exclude built-in groups */
210         case ACCOUNT_GROUPS:
211                 filter = talloc_asprintf(mem_ctx,
212                                          "(&(objectClass=group)"
213                                          "(!(groupType:"LDB_OID_COMPARATOR_AND":=%u))"
214                                          "(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
215                                          GROUP_TYPE_BUILTIN_LOCAL_GROUP, GROUP_TYPE_SECURITY_ENABLED);
216                 break;
217         }
218
219         if (!filter) {
220                 return ldb_oom(ldb);
221         }
222
223         primary_group_string = dom_sid_string(mem_ctx, primary_group_sid);
224         if (!primary_group_string) {
225                 return ldb_oom(ldb);
226         }
227
228         primary_group_dn = talloc_asprintf(mem_ctx, "<SID=%s>", primary_group_string);
229         if (!primary_group_dn) {
230                 return ldb_oom(ldb);
231         }
232
233         primary_group_blob = data_blob_string_const(primary_group_dn);
234
235         account_sid_string = dom_sid_string(mem_ctx, account_sid);
236         if (!account_sid_string) {
237                 return ldb_oom(ldb);
238         }
239
240         account_sid_dn = talloc_asprintf(mem_ctx, "<SID=%s>", account_sid_string);
241         if (!account_sid_dn) {
242                 return ldb_oom(ldb);
243         }
244
245         account_sid_blob = data_blob_string_const(account_sid_dn);
246
247         status = dsdb_expand_nested_groups(ldb, &account_sid_blob,
248                                            true, /* We don't want to add the object's SID itself,
249                                                     it's not returned in this attribute */
250                                            filter,
251                                            mem_ctx, groupSIDs, num_groupSIDs);
252
253         if (!NT_STATUS_IS_OK(status)) {
254                 ldb_asprintf_errstring(ldb, "Failed to construct %s: expanding groups of SID %s failed: %s",
255                                        attribute_string, account_sid_string,
256                                        nt_errstr(status));
257                 return LDB_ERR_OPERATIONS_ERROR;
258         }
259
260         /* Expands the primary group - this function takes in
261          * memberOf-like values, so we fake one up with the
262          * <SID=S-...> format of DN and then let it expand
263          * them, as long as they meet the filter - so only
264          * domain groups, not builtin groups
265          */
266         status = dsdb_expand_nested_groups(ldb, &primary_group_blob, false, filter,
267                                            mem_ctx, groupSIDs, num_groupSIDs);
268         if (!NT_STATUS_IS_OK(status)) {
269                 ldb_asprintf_errstring(ldb, "Failed to construct %s: expanding groups of SID %s failed: %s",
270                                        attribute_string, account_sid_string,
271                                        nt_errstr(status));
272                 return LDB_ERR_OPERATIONS_ERROR;
273         }
274
275         return LDB_SUCCESS;
276 }
277
278 /*
279   construct the token groups for SAM objects from a message
280 */
281 static int construct_generic_token_groups(struct ldb_module *module,
282                                           struct ldb_message *msg, enum ldb_scope scope,
283                                           struct ldb_request *parent,
284                                           const char *attribute_string,
285                                           enum search_type type)
286 {
287         struct ldb_context *ldb = ldb_module_get_ctx(module);
288         TALLOC_CTX *tmp_ctx = talloc_new(msg);
289         uint32_t i;
290         int ret;
291         struct auth_SidAttr *groupSIDs = NULL;
292         uint32_t num_groupSIDs = 0;
293
294         if (scope != LDB_SCOPE_BASE) {
295                 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, this is not a BASE search");
296                 return LDB_ERR_OPERATIONS_ERROR;
297         }
298
299         /* calculate the group SIDs for this object */
300         ret = get_group_sids(ldb, tmp_ctx, msg, attribute_string, type,
301                              &groupSIDs, &num_groupSIDs);
302
303         if (ret != LDB_SUCCESS) {
304                 talloc_free(tmp_ctx);
305                 return LDB_ERR_OPERATIONS_ERROR;
306         }
307
308         /* add these SIDs to the search result */
309         for (i=0; i < num_groupSIDs; i++) {
310                 ret = samdb_msg_add_dom_sid(ldb, msg, msg, attribute_string, &groupSIDs[i].sid);
311                 if (ret) {
312                         talloc_free(tmp_ctx);
313                         return ret;
314                 }
315         }
316
317         return LDB_SUCCESS;
318 }
319
320 static int construct_token_groups(struct ldb_module *module,
321                                   struct ldb_message *msg, enum ldb_scope scope,
322                                   struct ldb_request *parent, struct ldb_reply *ares)
323 {
324         /**
325          * TODO: Add in a limiting domain when we start to support
326          * trusted domains.
327          */
328         return construct_generic_token_groups(module, msg, scope, parent,
329                                               "tokenGroups",
330                                               TOKEN_GROUPS);
331 }
332
333 static int construct_token_groups_no_gc(struct ldb_module *module,
334                                         struct ldb_message *msg, enum ldb_scope scope,
335                                         struct ldb_request *parent, struct ldb_reply *ares)
336 {
337         /**
338          * TODO: Add in a limiting domain when we start to support
339          * trusted domains.
340          */
341         return construct_generic_token_groups(module, msg, scope, parent,
342                                               "tokenGroupsNoGCAcceptable",
343                                               TOKEN_GROUPS);
344 }
345
346 static int construct_global_universal_token_groups(struct ldb_module *module,
347                                                    struct ldb_message *msg, enum ldb_scope scope,
348                                                    struct ldb_request *parent, struct ldb_reply *ares)
349 {
350         return construct_generic_token_groups(module, msg, scope, parent,
351                                               "tokenGroupsGlobalAndUniversal",
352                                               TOKEN_GROUPS_GLOBAL_AND_UNIVERSAL);
353 }
354 /*
355   construct the parent GUID for an entry from a message
356 */
357 static int construct_parent_guid(struct ldb_module *module,
358                                  struct ldb_message *msg, enum ldb_scope scope,
359                                  struct ldb_request *parent, struct ldb_reply *ares)
360 {
361         struct ldb_result *res, *parent_res;
362         const struct ldb_val *parent_guid;
363         const char *attrs[] = { "instanceType", NULL };
364         const char *attrs2[] = { "objectGUID", NULL };
365         uint32_t instanceType;
366         int ret;
367         struct ldb_dn *parent_dn;
368         struct ldb_val v;
369
370         /* determine if the object is NC by instance type */
371         ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
372                                     DSDB_FLAG_NEXT_MODULE |
373                                     DSDB_SEARCH_SHOW_RECYCLED, parent);
374         if (ret != LDB_SUCCESS) {
375                 return ret;
376         }
377
378         instanceType = ldb_msg_find_attr_as_uint(res->msgs[0],
379                                                  "instanceType", 0);
380         talloc_free(res);
381         if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
382                 DEBUG(4,(__location__ ": Object %s is NC\n",
383                          ldb_dn_get_linearized(msg->dn)));
384                 return LDB_SUCCESS;
385         }
386         parent_dn = ldb_dn_get_parent(msg, msg->dn);
387
388         if (parent_dn == NULL) {
389                 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
390                                          ldb_dn_get_linearized(msg->dn)));
391                 return LDB_ERR_OTHER;
392         }
393         ret = dsdb_module_search_dn(module, msg, &parent_res, parent_dn, attrs2,
394                                     DSDB_FLAG_NEXT_MODULE |
395                                     DSDB_SEARCH_SHOW_RECYCLED, parent);
396         /* not NC, so the object should have a parent*/
397         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
398                 ret = ldb_error(ldb_module_get_ctx(module), LDB_ERR_OPERATIONS_ERROR,
399                                  talloc_asprintf(msg, "Parent dn %s for %s does not exist",
400                                                  ldb_dn_get_linearized(parent_dn),
401                                                  ldb_dn_get_linearized(msg->dn)));
402                 talloc_free(parent_dn);
403                 return ret;
404         } else if (ret != LDB_SUCCESS) {
405                 talloc_free(parent_dn);
406                 return ret;
407         }
408         talloc_free(parent_dn);
409
410         parent_guid = ldb_msg_find_ldb_val(parent_res->msgs[0], "objectGUID");
411         if (!parent_guid) {
412                 talloc_free(parent_res);
413                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
414         }
415
416         v = data_blob_dup_talloc(parent_res, *parent_guid);
417         if (!v.data) {
418                 talloc_free(parent_res);
419                 return ldb_oom(ldb_module_get_ctx(module));
420         }
421         ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
422         talloc_free(parent_res);
423         return ret;
424 }
425
426 static int construct_modifyTimeStamp(struct ldb_module *module,
427                                         struct ldb_message *msg, enum ldb_scope scope,
428                                         struct ldb_request *parent, struct ldb_reply *ares)
429 {
430         struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
431         struct ldb_context *ldb = ldb_module_get_ctx(module);
432
433         /* We may be being called before the init function has finished */
434         if (!data) {
435                 return LDB_SUCCESS;
436         }
437
438         /* Try and set this value up, if possible.  Don't worry if it
439          * fails, we may not have the DB set up yet.
440          */
441         if (!data->aggregate_dn) {
442                 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
443         }
444
445         if (data->aggregate_dn && ldb_dn_compare(data->aggregate_dn, msg->dn) == 0) {
446                 /*
447                  * If we have the DN for the object with common name = Aggregate and
448                  * the request is for this DN then let's do the following:
449                  * 1) search the object which changedUSN correspond to the one of the loaded
450                  * schema.
451                  * 2) Get the whenChanged attribute
452                  * 3) Generate the modifyTimestamp out of the whenChanged attribute
453                  */
454                 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
455                 char *value = ldb_timestring(msg, schema->ts_last_change);
456
457                 if (value == NULL) {
458                         return ldb_oom(ldb_module_get_ctx(module));
459                 }
460
461                 return ldb_msg_add_string(msg, "modifyTimeStamp", value);
462         }
463         return ldb_msg_copy_attr(msg, "whenChanged", "modifyTimeStamp");
464 }
465
466 /*
467   construct a subSchemaSubEntry
468 */
469 static int construct_subschema_subentry(struct ldb_module *module,
470                                         struct ldb_message *msg, enum ldb_scope scope,
471                                         struct ldb_request *parent, struct ldb_reply *ares)
472 {
473         struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
474         char *subSchemaSubEntry;
475
476         /* We may be being called before the init function has finished */
477         if (!data) {
478                 return LDB_SUCCESS;
479         }
480
481         /* Try and set this value up, if possible.  Don't worry if it
482          * fails, we may not have the DB set up yet, and it's not
483          * really vital anyway */
484         if (!data->aggregate_dn) {
485                 struct ldb_context *ldb = ldb_module_get_ctx(module);
486                 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
487         }
488
489         if (data->aggregate_dn) {
490                 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
491                 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
492         }
493         return LDB_SUCCESS;
494 }
495
496
497 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
498                                          struct ldb_message *msg,
499                                          struct ldb_message_element *object_category)
500 {
501         struct ldb_context *ldb;
502         struct ldb_dn *dn;
503         const struct ldb_val *val;
504
505         ldb = ldb_module_get_ctx(module);
506         if (!ldb) {
507                 DEBUG(4, (__location__ ": Failed to get ldb \n"));
508                 return LDB_ERR_OPERATIONS_ERROR;
509         }
510
511         dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
512         if (!dn) {
513                 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
514                           (const char *)object_category->values[0].data));
515                 return ldb_operr(ldb);
516         }
517
518         val = ldb_dn_get_rdn_val(dn);
519         if (!val) {
520                 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
521                           ldb_dn_get_linearized(dn)));
522                 return ldb_operr(ldb);
523         }
524
525         if (strequal((const char *)val->data, "NTDS-DSA")) {
526                 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
527         } else {
528                 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
529         }
530         return LDB_SUCCESS;
531 }
532
533 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
534                                                 struct ldb_message *msg,
535                                                 struct ldb_dn *dn,
536                                                 struct ldb_request *parent)
537 {
538         struct ldb_dn *server_dn;
539         const char *attr_obj_cat[] = { "objectCategory", NULL };
540         struct ldb_result *res;
541         struct ldb_message_element *object_category;
542         int ret;
543
544         server_dn = ldb_dn_copy(msg, dn);
545         if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
546                 DEBUG(4, (__location__ ": Failed to add child to %s \n",
547                           ldb_dn_get_linearized(server_dn)));
548                 return ldb_operr(ldb_module_get_ctx(module));
549         }
550
551         ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat,
552                                     DSDB_FLAG_NEXT_MODULE, parent);
553         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
554                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
555                                          ldb_dn_get_linearized(server_dn)));
556                 return LDB_SUCCESS;
557         } else if (ret != LDB_SUCCESS) {
558                 return ret;
559         }
560
561         object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
562         if (!object_category) {
563                 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
564                          ldb_dn_get_linearized(res->msgs[0]->dn)));
565                 return LDB_SUCCESS;
566         }
567         return construct_msds_isrodc_with_dn(module, msg, object_category);
568 }
569
570 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
571                                                   struct ldb_message *msg,
572                                                   struct ldb_request *parent)
573 {
574         int ret;
575         struct ldb_dn *server_dn;
576
577         ret = dsdb_module_reference_dn(module, msg, msg->dn, "serverReferenceBL",
578                                        &server_dn, parent);
579         if (ret == LDB_ERR_NO_SUCH_OBJECT || ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
580                 /* it's OK if we can't find serverReferenceBL attribute */
581                 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
582                          ldb_dn_get_linearized(msg->dn)));
583                 return LDB_SUCCESS;
584         } else if (ret != LDB_SUCCESS) {
585                 return ret;
586         }
587
588         return construct_msds_isrodc_with_server_dn(module, msg, server_dn, parent);
589 }
590
591 /*
592   construct msDS-isRODC attr
593 */
594 static int construct_msds_isrodc(struct ldb_module *module,
595                                  struct ldb_message *msg, enum ldb_scope scope,
596                                  struct ldb_request *parent, struct ldb_reply *ares)
597 {
598         struct ldb_message_element * object_class;
599         struct ldb_message_element * object_category;
600         unsigned int i;
601
602         object_class = ldb_msg_find_element(msg, "objectClass");
603         if (!object_class) {
604                 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
605                          ldb_dn_get_linearized(msg->dn)));
606                 return ldb_operr(ldb_module_get_ctx(module));
607         }
608
609         for (i=0; i<object_class->num_values; i++) {
610                 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
611                         /* If TO!objectCategory  equals the DN of the classSchema  object for the nTDSDSA
612                          * object class, then TO!msDS-isRODC  is false. Otherwise, TO!msDS-isRODC  is true.
613                          */
614                         object_category = ldb_msg_find_element(msg, "objectCategory");
615                         if (!object_category) {
616                                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
617                                          ldb_dn_get_linearized(msg->dn)));
618                                 return LDB_SUCCESS;
619                         }
620                         return construct_msds_isrodc_with_dn(module, msg, object_category);
621                 }
622                 if (strequal((const char*)object_class->values[i].data, "server")) {
623                         /* Let TN be the nTDSDSA  object whose DN is "CN=NTDS Settings," prepended to
624                          * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA  object" case,
625                          * substituting TN for TO.
626                          */
627                         return construct_msds_isrodc_with_server_dn(module, msg, msg->dn, parent);
628                 }
629                 if (strequal((const char*)object_class->values[i].data, "computer")) {
630                         /* Let TS be the server  object named by TO!serverReferenceBL. Apply the previous
631                          * rule for the "TO is a server  object" case, substituting TS for TO.
632                          */
633                         return construct_msds_isrodc_with_computer_dn(module, msg, parent);
634                 }
635         }
636
637         return LDB_SUCCESS;
638 }
639
640
641 /*
642   construct msDS-keyVersionNumber attr
643
644   TODO:  Make this based on the 'win2k' DS heuristics bit...
645
646 */
647 static int construct_msds_keyversionnumber(struct ldb_module *module,
648                                            struct ldb_message *msg,
649                                            enum ldb_scope scope,
650                                            struct ldb_request *parent,
651                                            struct ldb_reply *ares)
652 {
653         uint32_t i;
654         enum ndr_err_code ndr_err;
655         const struct ldb_val *omd_value;
656         struct replPropertyMetaDataBlob *omd;
657         int ret;
658
659         omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
660         if (!omd_value) {
661                 /* We can't make up a key version number without meta data */
662                 return LDB_SUCCESS;
663         }
664
665         omd = talloc(msg, struct replPropertyMetaDataBlob);
666         if (!omd) {
667                 ldb_module_oom(module);
668                 return LDB_SUCCESS;
669         }
670
671         ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
672                                        (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
673         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
674                 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
675                          ldb_dn_get_linearized(msg->dn)));
676                 return ldb_operr(ldb_module_get_ctx(module));
677         }
678
679         if (omd->version != 1) {
680                 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
681                          omd->version, ldb_dn_get_linearized(msg->dn)));
682                 talloc_free(omd);
683                 return LDB_SUCCESS;
684         }
685         for (i=0; i<omd->ctr.ctr1.count; i++) {
686                 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTID_unicodePwd) {
687                         ret = samdb_msg_add_uint(ldb_module_get_ctx(module),
688                                                  msg, msg,
689                                                  "msDS-KeyVersionNumber",
690                                                  omd->ctr.ctr1.array[i].version);
691                         if (ret != LDB_SUCCESS) {
692                                 talloc_free(omd);
693                                 return ret;
694                         }
695                         break;
696                 }
697         }
698         return LDB_SUCCESS;
699
700 }
701
702 #define _UF_TRUST_ACCOUNTS ( \
703         UF_WORKSTATION_TRUST_ACCOUNT | \
704         UF_SERVER_TRUST_ACCOUNT | \
705         UF_INTERDOMAIN_TRUST_ACCOUNT \
706 )
707 #define _UF_NO_EXPIRY_ACCOUNTS ( \
708         UF_SMARTCARD_REQUIRED | \
709         UF_DONT_EXPIRE_PASSWD | \
710         _UF_TRUST_ACCOUNTS \
711 )
712
713
714 /*
715  * Returns the Effective-MaximumPasswordAge for a user
716  */
717 static int64_t get_user_max_pwd_age(struct ldb_module *module,
718                                     struct ldb_message *user_msg,
719                                     struct ldb_request *parent,
720                                     struct ldb_dn *nc_root)
721 {
722         int ret;
723         struct ldb_message *pso = NULL;
724         struct ldb_context *ldb = ldb_module_get_ctx(module);
725
726         /* if a PSO applies to the user, use its maxPwdAge */
727         ret = get_pso_for_user(module, user_msg, parent, &pso);
728         if (ret != LDB_SUCCESS) {
729
730                 /* log the error, but fallback to the domain default */
731                 DBG_ERR("Error retrieving PSO for %s\n",
732                         ldb_dn_get_linearized(user_msg->dn));
733         }
734
735         if (pso != NULL) {
736                 return ldb_msg_find_attr_as_int64(pso,
737                                                   "msDS-MaximumPasswordAge", 0);
738         }
739
740         /* otherwise return the default domain value */
741         return samdb_search_int64(ldb, user_msg, 0, nc_root, "maxPwdAge", NULL);
742 }
743
744 /*
745   calculate msDS-UserPasswordExpiryTimeComputed
746 */
747 static NTTIME get_msds_user_password_expiry_time_computed(struct ldb_module *module,
748                                                 struct ldb_message *msg,
749                                                 struct ldb_request *parent,
750                                                 struct ldb_dn *domain_dn)
751 {
752         int64_t pwdLastSet, maxPwdAge;
753         uint32_t userAccountControl;
754         NTTIME ret;
755
756         userAccountControl = ldb_msg_find_attr_as_uint(msg,
757                                         "userAccountControl",
758                                         0);
759         if (userAccountControl & _UF_NO_EXPIRY_ACCOUNTS) {
760                 return INT64_MAX;
761         }
762
763         pwdLastSet = ldb_msg_find_attr_as_int64(msg, "pwdLastSet", 0);
764         if (pwdLastSet == 0) {
765                 return 0;
766         }
767
768         if (pwdLastSet <= -1) {
769                 /*
770                  * This can't really happen...
771                  */
772                 return INT64_MAX;
773         }
774
775         if (pwdLastSet >= INT64_MAX) {
776                 /*
777                  * Somethings wrong with the clock...
778                  */
779                 return INT64_MAX;
780         }
781
782         /*
783          * Note that maxPwdAge is a stored as negative value.
784          *
785          * Possible values are in the range of:
786          *
787          * maxPwdAge: -864000000001
788          * to
789          * maxPwdAge: -9223372036854775808 (INT64_MIN)
790          *
791          */
792         maxPwdAge = get_user_max_pwd_age(module, msg, parent, domain_dn);
793         if (maxPwdAge >= -864000000000) {
794                 /*
795                  * This is not really possible...
796                  */
797                 return INT64_MAX;
798         }
799
800         if (maxPwdAge == INT64_MIN) {
801                 return INT64_MAX;
802         }
803
804         /*
805          * Note we already caught maxPwdAge == INT64_MIN
806          * and pwdLastSet >= INT64_MAX above.
807          *
808          * Remember maxPwdAge is a negative number,
809          * so it results in the following.
810          *
811          * 0x7FFFFFFFFFFFFFFEULL + INT64_MAX
812          * =
813          * 0xFFFFFFFFFFFFFFFDULL
814          *
815          * or to put it another way, adding two numbers less than 1<<63 can't
816          * ever be more than 1<<64, therefore this result can't wrap.
817          */
818         ret = (NTTIME)pwdLastSet - (NTTIME)maxPwdAge;
819         if (ret >= INT64_MAX) {
820                 return INT64_MAX;
821         }
822
823         return ret;
824 }
825
826 /*
827  * Returns the Effective-LockoutDuration for a user
828  */
829 static int64_t get_user_lockout_duration(struct ldb_module *module,
830                                          struct ldb_message *user_msg,
831                                          struct ldb_request *parent,
832                                          struct ldb_dn *nc_root)
833 {
834         int ret;
835         struct ldb_message *pso = NULL;
836         struct ldb_context *ldb = ldb_module_get_ctx(module);
837
838         /* if a PSO applies to the user, use its lockoutDuration */
839         ret = get_pso_for_user(module, user_msg, parent, &pso);
840         if (ret != LDB_SUCCESS) {
841
842                 /* log the error, but fallback to the domain default */
843                 DBG_ERR("Error retrieving PSO for %s\n",
844                         ldb_dn_get_linearized(user_msg->dn));
845         }
846
847         if (pso != NULL) {
848                 return ldb_msg_find_attr_as_int64(pso,
849                                                   "msDS-LockoutDuration", 0);
850         }
851
852         /* otherwise return the default domain value */
853         return samdb_search_int64(ldb, user_msg, 0, nc_root, "lockoutDuration",
854                                   NULL);
855 }
856
857 /*
858   construct msDS-User-Account-Control-Computed attr
859 */
860 static int construct_msds_user_account_control_computed(struct ldb_module *module,
861                                                         struct ldb_message *msg, enum ldb_scope scope,
862                                                         struct ldb_request *parent, struct ldb_reply *ares)
863 {
864         uint32_t userAccountControl;
865         uint32_t msDS_User_Account_Control_Computed = 0;
866         struct ldb_context *ldb = ldb_module_get_ctx(module);
867         NTTIME now;
868         struct ldb_dn *nc_root;
869         int ret;
870
871         ret = dsdb_find_nc_root(ldb, msg, msg->dn, &nc_root);
872         if (ret != 0) {
873                 ldb_asprintf_errstring(ldb,
874                                        "Failed to find NC root of DN: %s: %s",
875                                        ldb_dn_get_linearized(msg->dn),
876                                        ldb_errstring(ldb_module_get_ctx(module)));
877                 return ret;
878         }
879         if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) != 0) {
880                 /* Only calculate this on our default NC */
881                 return 0;
882         }
883         /* Test account expire time */
884         unix_to_nt_time(&now, time(NULL));
885
886         userAccountControl = ldb_msg_find_attr_as_uint(msg,
887                                                        "userAccountControl",
888                                                        0);
889         if (!(userAccountControl & _UF_TRUST_ACCOUNTS)) {
890
891                 int64_t lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
892                 if (lockoutTime != 0) {
893                         int64_t lockoutDuration;
894
895                         lockoutDuration = get_user_lockout_duration(module, msg,
896                                                                     parent,
897                                                                     nc_root);
898
899                         /* zero locks out until the administrator intervenes */
900                         if (lockoutDuration >= 0) {
901                                 msDS_User_Account_Control_Computed |= UF_LOCKOUT;
902                         } else if (lockoutTime - lockoutDuration >= now) {
903                                 msDS_User_Account_Control_Computed |= UF_LOCKOUT;
904                         }
905                 }
906         }
907
908         if (!(userAccountControl & _UF_NO_EXPIRY_ACCOUNTS)) {
909                 NTTIME must_change_time
910                         = get_msds_user_password_expiry_time_computed(module,
911                                                                       msg,
912                                                                       parent,
913                                                                       nc_root);
914                 /* check for expired password */
915                 if (must_change_time < now) {
916                         msDS_User_Account_Control_Computed |= UF_PASSWORD_EXPIRED;
917                 }
918         }
919
920         return samdb_msg_add_int64(ldb,
921                                    msg->elements, msg,
922                                    "msDS-User-Account-Control-Computed",
923                                    msDS_User_Account_Control_Computed);
924 }
925
926 /*
927   construct msDS-UserPasswordExpiryTimeComputed
928 */
929 static int construct_msds_user_password_expiry_time_computed(struct ldb_module *module,
930                                                              struct ldb_message *msg, enum ldb_scope scope,
931                                                              struct ldb_request *parent, struct ldb_reply *ares)
932 {
933         struct ldb_context *ldb = ldb_module_get_ctx(module);
934         struct ldb_dn *nc_root;
935         int64_t password_expiry_time;
936         int ret;
937
938         ret = dsdb_find_nc_root(ldb, msg, msg->dn, &nc_root);
939         if (ret != 0) {
940                 ldb_asprintf_errstring(ldb,
941                                        "Failed to find NC root of DN: %s: %s",
942                                        ldb_dn_get_linearized(msg->dn),
943                                        ldb_errstring(ldb));
944                 return ret;
945         }
946
947         if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) != 0) {
948                 /* Only calculate this on our default NC */
949                 return 0;
950         }
951
952         password_expiry_time
953                 = get_msds_user_password_expiry_time_computed(module, msg,
954                                                               parent, nc_root);
955
956         return samdb_msg_add_int64(ldb,
957                                    msg->elements, msg,
958                                    "msDS-UserPasswordExpiryTimeComputed",
959                                    password_expiry_time);
960 }
961
962 /*
963  * Checks whether the msDS-ResultantPSO attribute is supported for a given
964  * user object. As per MS-ADTS, section 3.1.1.4.5.36 msDS-ResultantPSO.
965  */
966 static bool pso_is_supported(struct ldb_context *ldb, struct ldb_message *msg)
967 {
968         int functional_level;
969         uint32_t uac;
970         uint32_t user_rid;
971
972         functional_level = dsdb_functional_level(ldb);
973         if (functional_level < DS_DOMAIN_FUNCTION_2008) {
974                 return false;
975         }
976
977         /* msDS-ResultantPSO is only supported for user objects */
978         if (!ldb_match_msg_objectclass(msg, "user")) {
979                 return false;
980         }
981
982         /* ...and only if the ADS_UF_NORMAL_ACCOUNT bit is set */
983         uac = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
984         if (!(uac & UF_NORMAL_ACCOUNT)) {
985                 return false;
986         }
987
988         /* skip it if it's the special KRBTGT default account */
989         user_rid = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
990         if (user_rid == DOMAIN_RID_KRBTGT) {
991                 return false;
992         }
993
994         /* ...or if it's a special KRBTGT account for an RODC KDC */
995         if (ldb_msg_find_ldb_val(msg, "msDS-SecondaryKrbTgtNumber") != NULL) {
996                 return false;
997         }
998
999         return true;
1000 }
1001
1002 /*
1003  * Returns the number of PSO objects that exist in the DB
1004  */
1005 static int get_pso_count(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1006                          struct ldb_request *parent, int *pso_count)
1007 {
1008         static const char * const attrs[] = { NULL };
1009         int ret;
1010         struct ldb_dn *psc_dn = NULL;
1011         struct ldb_result *res = NULL;
1012         struct ldb_context *ldb = ldb_module_get_ctx(module);
1013         bool psc_ok;
1014
1015         *pso_count = 0;
1016         psc_dn = samdb_system_container_dn(ldb, mem_ctx);
1017         if (psc_dn == NULL) {
1018                 return ldb_oom(ldb);
1019         }
1020         psc_ok = ldb_dn_add_child_fmt(psc_dn, "CN=Password Settings Container");
1021         if (psc_ok == false) {
1022                 return ldb_oom(ldb);
1023         }
1024
1025         /* get the number of PSO children */
1026         ret = dsdb_module_search(module, mem_ctx, &res, psc_dn,
1027                                  LDB_SCOPE_ONELEVEL, attrs,
1028                                  DSDB_FLAG_NEXT_MODULE, parent,
1029                                  "(objectClass=msDS-PasswordSettings)");
1030
1031         /*
1032          * Just ignore PSOs if the container doesn't exist. This is a weird
1033          * corner-case where the AD DB was created from a pre-2008 base schema,
1034          * and then the FL was manually upgraded.
1035          */
1036         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1037                 DBG_NOTICE("No Password Settings Container exists\n");
1038                 return LDB_SUCCESS;
1039         }
1040
1041         if (ret != LDB_SUCCESS) {
1042                 return ret;
1043         }
1044
1045         *pso_count = res->count;
1046         talloc_free(res);
1047         talloc_free(psc_dn);
1048
1049         return LDB_SUCCESS;
1050 }
1051
1052 /*
1053  * Compares two PSO objects returned by a search, to work out the better PSO.
1054  * The PSO with the lowest precedence is better, otherwise (if the precedence
1055  * is equal) the PSO with the lower GUID wins.
1056  */
1057 static int pso_compare(struct ldb_message **m1, struct ldb_message **m2)
1058 {
1059         uint32_t prec1;
1060         uint32_t prec2;
1061
1062         prec1 = ldb_msg_find_attr_as_uint(*m1, "msDS-PasswordSettingsPrecedence",
1063                                           0xffffffff);
1064         prec2 = ldb_msg_find_attr_as_uint(*m2, "msDS-PasswordSettingsPrecedence",
1065                                           0xffffffff);
1066
1067         /* if precedence is equal, use the lowest GUID */
1068         if (prec1 == prec2) {
1069                 struct GUID guid1 = samdb_result_guid(*m1, "objectGUID");
1070                 struct GUID guid2 = samdb_result_guid(*m2, "objectGUID");
1071
1072                 return ndr_guid_compare(&guid1, &guid2);
1073         } else {
1074                 return NUMERIC_CMP(prec1, prec2);
1075         }
1076 }
1077
1078 /*
1079  * Search for PSO objects that apply to the object SIDs specified
1080  */
1081 static int pso_search_by_sids(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1082                               struct ldb_request *parent,
1083                               struct auth_SidAttr *sid_array, unsigned int num_sids,
1084                               struct ldb_result **result)
1085 {
1086         int ret;
1087         int i;
1088         struct ldb_context *ldb = ldb_module_get_ctx(module);
1089         char *sid_filter = NULL;
1090         struct ldb_dn *psc_dn = NULL;
1091         bool psc_ok;
1092         const char *attrs[] = {
1093                 "msDS-PasswordSettingsPrecedence",
1094                 "objectGUID",
1095                 "msDS-LockoutDuration",
1096                 "msDS-MaximumPasswordAge",
1097                 NULL
1098         };
1099
1100         /* build a query for PSO objects that apply to any of the SIDs given */
1101         sid_filter = talloc_strdup(mem_ctx, "");
1102         if (sid_filter == NULL) {
1103                 return ldb_oom(ldb);
1104         }
1105
1106         for (i = 0; sid_filter && i < num_sids; i++) {
1107                 struct dom_sid_buf sid_buf;
1108
1109                 sid_filter = talloc_asprintf_append(
1110                         sid_filter,
1111                         "(msDS-PSOAppliesTo=<SID=%s>)",
1112                         dom_sid_str_buf(&sid_array[i].sid, &sid_buf));
1113                 if (sid_filter == NULL) {
1114                         return ldb_oom(ldb);
1115                 }
1116         }
1117
1118         /* only PSOs located in the Password Settings Container are valid */
1119         psc_dn = samdb_system_container_dn(ldb, mem_ctx);
1120         if (psc_dn == NULL) {
1121                 return ldb_oom(ldb);
1122         }
1123         psc_ok = ldb_dn_add_child_fmt(psc_dn, "CN=Password Settings Container");
1124         if (psc_ok == false) {
1125                 return ldb_oom(ldb);
1126         }
1127
1128         ret = dsdb_module_search(module, mem_ctx, result, psc_dn,
1129                                  LDB_SCOPE_ONELEVEL, attrs,
1130                                  DSDB_FLAG_NEXT_MODULE, parent,
1131                                  "(&(objectClass=msDS-PasswordSettings)(|%s))",
1132                                  sid_filter);
1133         talloc_free(sid_filter);
1134         return ret;
1135 }
1136
1137 /*
1138  * Returns the best PSO object that applies to the object SID(s) specified
1139  */
1140 static int pso_find_best(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1141                          struct ldb_request *parent, struct auth_SidAttr *sid_array,
1142                          unsigned int num_sids, struct ldb_message **best_pso)
1143 {
1144         struct ldb_result *res = NULL;
1145         int ret;
1146
1147         *best_pso = NULL;
1148
1149         /* find any PSOs that apply to the SIDs specified */
1150         ret = pso_search_by_sids(module, mem_ctx, parent, sid_array, num_sids,
1151                                  &res);
1152         if (ret != LDB_SUCCESS) {
1153                 DBG_ERR("Error %d retrieving PSO for SID(s)\n", ret);
1154                 return ret;
1155         }
1156
1157         /* sort the list so that the best PSO is first */
1158         TYPESAFE_QSORT(res->msgs, res->count, pso_compare);
1159
1160         if (res->count > 0) {
1161                 *best_pso = res->msgs[0];
1162         }
1163
1164         return LDB_SUCCESS;
1165 }
1166
1167 /*
1168  * Determines the Password Settings Object (PSO) that applies to the given user
1169  */
1170 static int get_pso_for_user(struct ldb_module *module,
1171                             struct ldb_message *user_msg,
1172                             struct ldb_request *parent,
1173                             struct ldb_message **pso_msg)
1174 {
1175         bool pso_supported;
1176         struct auth_SidAttr *groupSIDs = NULL;
1177         uint32_t num_groupSIDs = 0;
1178         struct ldb_context *ldb = ldb_module_get_ctx(module);
1179         struct ldb_message *best_pso = NULL;
1180         struct ldb_dn *pso_dn = NULL;
1181         int ret;
1182         struct ldb_message_element *el = NULL;
1183         TALLOC_CTX *tmp_ctx = NULL;
1184         int pso_count = 0;
1185         struct ldb_result *res = NULL;
1186         static const char *attrs[] = {
1187                 "msDS-LockoutDuration",
1188                 "msDS-MaximumPasswordAge",
1189                 NULL
1190         };
1191
1192         *pso_msg = NULL;
1193
1194         /* first, check msDS-ResultantPSO is supported for this object */
1195         pso_supported = pso_is_supported(ldb, user_msg);
1196
1197         if (!pso_supported) {
1198                 return LDB_SUCCESS;
1199         }
1200
1201         tmp_ctx = talloc_new(user_msg);
1202
1203         /*
1204          * Several different constructed attributes try to use the PSO info. If
1205          * we've already constructed the msDS-ResultantPSO for this user, we can
1206          * just re-use the result, rather than calculating it from scratch again
1207          */
1208         pso_dn = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, user_msg,
1209                                          "msDS-ResultantPSO");
1210
1211         if (pso_dn != NULL) {
1212                 ret = dsdb_module_search_dn(module, tmp_ctx, &res, pso_dn,
1213                                             attrs, DSDB_FLAG_NEXT_MODULE,
1214                                             parent);
1215                 if (ret != LDB_SUCCESS) {
1216                         DBG_ERR("Error %d retrieving PSO %s\n", ret,
1217                                 ldb_dn_get_linearized(pso_dn));
1218                         talloc_free(tmp_ctx);
1219                         return ret;
1220                 }
1221
1222                 if (res->count == 1) {
1223                         *pso_msg = res->msgs[0];
1224                         return LDB_SUCCESS;
1225                 }
1226         }
1227
1228         /*
1229          * if any PSOs apply directly to the user, they are considered first
1230          * before we check group membership PSOs
1231          */
1232         el = ldb_msg_find_element(user_msg, "msDS-PSOApplied");
1233
1234         if (el != NULL && el->num_values > 0) {
1235                 struct auth_SidAttr *user_sid = NULL;
1236
1237                 /* lookup the best PSO object, based on the user's SID */
1238                 user_sid = samdb_result_dom_sid_attrs(
1239                         tmp_ctx, user_msg, "objectSid",
1240                         SE_GROUP_DEFAULT_FLAGS);
1241
1242                 ret = pso_find_best(module, tmp_ctx, parent, user_sid, 1,
1243                                     &best_pso);
1244                 if (ret != LDB_SUCCESS) {
1245                         talloc_free(tmp_ctx);
1246                         return ret;
1247                 }
1248
1249                 if (best_pso != NULL) {
1250                         *pso_msg = best_pso;
1251                         return LDB_SUCCESS;
1252                 }
1253         }
1254
1255         /*
1256          * If no valid PSO applies directly to the user, then try its groups.
1257          * The group expansion is expensive, so check there are actually
1258          * PSOs in the DB first (which is a quick search). Note in the above
1259          * cases we could tell that a PSO applied to the user, based on info
1260          * already retrieved by the user search.
1261          */
1262         ret = get_pso_count(module, tmp_ctx, parent, &pso_count);
1263         if (ret != LDB_SUCCESS) {
1264                 DBG_ERR("Error %d determining PSOs in system\n", ret);
1265                 talloc_free(tmp_ctx);
1266                 return ret;
1267         }
1268
1269         if (pso_count == 0) {
1270                 talloc_free(tmp_ctx);
1271                 return LDB_SUCCESS;
1272         }
1273
1274         /* Work out the SIDs of any account groups the user is a member of */
1275         ret = get_group_sids(ldb, tmp_ctx, user_msg,
1276                              "msDS-ResultantPSO", ACCOUNT_GROUPS,
1277                              &groupSIDs, &num_groupSIDs);
1278         if (ret != LDB_SUCCESS) {
1279                 DBG_ERR("Error %d determining group SIDs for %s\n", ret,
1280                         ldb_dn_get_linearized(user_msg->dn));
1281                 talloc_free(tmp_ctx);
1282                 return ret;
1283         }
1284
1285         /* lookup the best PSO that applies to any of these groups */
1286         ret = pso_find_best(module, tmp_ctx, parent, groupSIDs,
1287                             num_groupSIDs, &best_pso);
1288         if (ret != LDB_SUCCESS) {
1289                 talloc_free(tmp_ctx);
1290                 return ret;
1291         }
1292
1293         *pso_msg = best_pso;
1294         return LDB_SUCCESS;
1295 }
1296
1297 /*
1298  * Constructs the msDS-ResultantPSO attribute, which is the DN of the Password
1299  * Settings Object (PSO) that applies to that user.
1300  */
1301 static int construct_resultant_pso(struct ldb_module *module,
1302                                    struct ldb_message *msg,
1303                                    enum ldb_scope scope,
1304                                    struct ldb_request *parent,
1305                                    struct ldb_reply *ares)
1306 {
1307         struct ldb_message *pso = NULL;
1308         int ret;
1309
1310         /* work out the PSO (if any) that applies to this user */
1311         ret = get_pso_for_user(module, msg, parent, &pso);
1312         if (ret != LDB_SUCCESS) {
1313                 DBG_ERR("Couldn't determine PSO for %s\n",
1314                         ldb_dn_get_linearized(msg->dn));
1315                 return ret;
1316         }
1317
1318         if (pso != NULL) {
1319                 DBG_INFO("%s is resultant PSO for user %s\n",
1320                          ldb_dn_get_linearized(pso->dn),
1321                          ldb_dn_get_linearized(msg->dn));
1322                 return ldb_msg_add_string(msg, "msDS-ResultantPSO",
1323                                           ldb_dn_get_linearized(pso->dn));
1324         }
1325
1326         /* no PSO applies to this user */
1327         return LDB_SUCCESS;
1328 }
1329
1330 struct op_controls_flags {
1331         bool sd;
1332         bool bypassoperational;
1333 };
1334
1335 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
1336         if (controls_flags->bypassoperational && ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 ) {
1337                 return true;
1338         }
1339         return false;
1340 }
1341
1342 /*
1343   a list of attribute names that should be substituted in the parse
1344   tree before the search is done
1345 */
1346 static const struct {
1347         const char *attr;
1348         const char *replace;
1349 } parse_tree_sub[] = {
1350         { "createTimeStamp", "whenCreated" },
1351         { "modifyTimeStamp", "whenChanged" }
1352 };
1353
1354
1355 struct op_attributes_replace {
1356         const char *attr;
1357         const char *replace;
1358         const char * const *extra_attrs;
1359         int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope, struct ldb_request *, struct ldb_reply *);
1360 };
1361
1362 /* the 'extra_attrs' required for msDS-ResultantPSO */
1363 #define RESULTANT_PSO_COMPUTED_ATTRS \
1364         "msDS-PSOApplied", \
1365         "userAccountControl", \
1366         "objectSid", \
1367         "msDS-SecondaryKrbTgtNumber", \
1368         "primaryGroupID"
1369
1370 /*
1371  * any other constructed attributes that want to work out the PSO also need to
1372  * include objectClass (this gets included via 'replace' for msDS-ResultantPSO)
1373  */
1374 #define PSO_ATTR_DEPENDENCIES \
1375         RESULTANT_PSO_COMPUTED_ATTRS, \
1376         "objectClass"
1377
1378 static const char *objectSid_attr[] =
1379 {
1380         "objectSid",
1381         NULL
1382 };
1383
1384
1385 static const char *objectCategory_attr[] =
1386 {
1387         "objectCategory",
1388         NULL
1389 };
1390
1391
1392 static const char *user_account_control_computed_attrs[] =
1393 {
1394         "lockoutTime",
1395         "pwdLastSet",
1396         PSO_ATTR_DEPENDENCIES,
1397         NULL
1398 };
1399
1400
1401 static const char *user_password_expiry_time_computed_attrs[] =
1402 {
1403         "pwdLastSet",
1404         PSO_ATTR_DEPENDENCIES,
1405         NULL
1406 };
1407
1408 static const char *resultant_pso_computed_attrs[] =
1409 {
1410         RESULTANT_PSO_COMPUTED_ATTRS,
1411         NULL
1412 };
1413
1414 /*
1415   a list of attribute names that are hidden, but can be searched for
1416   using another (non-hidden) name to produce the correct result
1417 */
1418 static const struct op_attributes_replace search_sub[] = {
1419         { "createTimeStamp", "whenCreated", NULL , NULL },
1420         { "modifyTimeStamp", "whenChanged", NULL , construct_modifyTimeStamp},
1421         { "structuralObjectClass", "objectClass", NULL , NULL },
1422         { "canonicalName", NULL, NULL , construct_canonical_name },
1423         { "primaryGroupToken", "objectClass", objectSid_attr, construct_primary_group_token },
1424         { "tokenGroups", "primaryGroupID", objectSid_attr, construct_token_groups },
1425         { "tokenGroupsNoGCAcceptable", "primaryGroupID", objectSid_attr, construct_token_groups_no_gc},
1426         { "tokenGroupsGlobalAndUniversal", "primaryGroupID", objectSid_attr, construct_global_universal_token_groups },
1427         { "parentGUID", "objectGUID", NULL, construct_parent_guid },
1428         { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
1429         { "msDS-isRODC", "objectClass", objectCategory_attr, construct_msds_isrodc },
1430         { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber },
1431         { "msDS-User-Account-Control-Computed", "userAccountControl", user_account_control_computed_attrs,
1432           construct_msds_user_account_control_computed },
1433         { "msDS-UserPasswordExpiryTimeComputed", "userAccountControl", user_password_expiry_time_computed_attrs,
1434           construct_msds_user_password_expiry_time_computed },
1435         { "msDS-ResultantPSO", "objectClass", resultant_pso_computed_attrs,
1436           construct_resultant_pso }
1437 };
1438
1439
1440 enum op_remove {
1441         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
1442         OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
1443         OPERATIONAL_SD_FLAGS,      /* show if SD_FLAGS_OID set, or asked for */
1444         OPERATIONAL_REMOVE_UNLESS_CONTROL        /* remove always unless an ad hoc control has been specified */
1445 };
1446
1447 /*
1448   a list of attributes that may need to be removed from the
1449   underlying db return
1450
1451   Some of these are attributes that were once stored, but are now calculated
1452 */
1453 struct op_attributes_operations {
1454         const char *attr;
1455         enum op_remove op;
1456 };
1457
1458 static const struct op_attributes_operations operational_remove[] = {
1459         { "nTSecurityDescriptor",    OPERATIONAL_SD_FLAGS },
1460         { "msDS-KeyVersionNumber",   OPERATIONAL_REMOVE_UNLESS_CONTROL  },
1461         { "parentGUID",              OPERATIONAL_REMOVE_ALWAYS  },
1462         { "replPropertyMetaData",    OPERATIONAL_REMOVE_UNASKED },
1463 #define _SEP ,OPERATIONAL_REMOVE_UNASKED},{
1464         { DSDB_SECRET_ATTRIBUTES_EX(_SEP), OPERATIONAL_REMOVE_UNASKED }
1465 };
1466
1467
1468 /*
1469   post process a search result record. For any search_sub[] attributes that were
1470   asked for, we need to call the appropriate copy routine to copy the result
1471   into the message, then remove any attributes that we added to the search but
1472   were not asked for by the user
1473 */
1474 static int operational_search_post_process(struct ldb_module *module,
1475                                            struct ldb_message *msg,
1476                                            enum ldb_scope scope,
1477                                            const char * const *attrs_from_user,
1478                                            const char * const *attrs_searched_for,
1479                                            struct op_controls_flags* controls_flags,
1480                                            struct op_attributes_operations *list,
1481                                            unsigned int list_size,
1482                                            struct op_attributes_replace *list_replace,
1483                                            unsigned int list_replace_size,
1484                                            struct ldb_request *parent,
1485                                            struct ldb_reply *ares)
1486 {
1487         struct ldb_context *ldb;
1488         unsigned int i, a = 0;
1489         bool constructed_attributes = false;
1490
1491         ldb = ldb_module_get_ctx(module);
1492
1493         /* removed any attrs that should not be shown to the user */
1494         for (i=0; i < list_size; i++) {
1495                 ldb_msg_remove_attr(msg, list[i].attr);
1496         }
1497
1498         for (a=0; a < list_replace_size; a++) {
1499                 if (check_keep_control_for_attribute(controls_flags,
1500                                                      list_replace[a].attr)) {
1501                         continue;
1502                 }
1503
1504                 /* construct the new attribute, using either a supplied
1505                         constructor or a simple copy */
1506                 constructed_attributes = true;
1507                 if (list_replace[a].constructor != NULL) {
1508                         if (list_replace[a].constructor(module, msg, scope, parent, ares) != LDB_SUCCESS) {
1509                                 goto failed;
1510                         }
1511                 } else if (ldb_msg_copy_attr(msg,
1512                                              list_replace[a].replace,
1513                                              list_replace[a].attr) != LDB_SUCCESS) {
1514                         goto failed;
1515                 }
1516         }
1517
1518         /* Deletion of the search helper attributes are needed if:
1519          * - we generated constructed attributes and
1520          * - we aren't requesting all attributes
1521          */
1522         if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
1523                 for (i=0; i < list_replace_size; i++) {
1524                         /* remove the added search helper attributes, unless
1525                          * they were asked for by the user */
1526                         if (list_replace[i].replace != NULL &&
1527                             !ldb_attr_in_list(attrs_from_user, list_replace[i].replace)) {
1528                                 ldb_msg_remove_attr(msg, list_replace[i].replace);
1529                         }
1530                         if (list_replace[i].extra_attrs != NULL) {
1531                                 unsigned int j;
1532                                 for (j=0; list_replace[i].extra_attrs[j]; j++) {
1533                                         if (!ldb_attr_in_list(attrs_from_user, list_replace[i].extra_attrs[j])) {
1534                                                 ldb_msg_remove_attr(msg, list_replace[i].extra_attrs[j]);
1535                                         }
1536                                 }
1537                         }
1538                 }
1539         }
1540
1541         return 0;
1542
1543 failed:
1544         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
1545                       "operational_search_post_process failed for attribute '%s' - %s",
1546                       list_replace[a].attr, ldb_errstring(ldb));
1547         return -1;
1548 }
1549
1550 /*
1551   hook search operations
1552 */
1553
1554 struct operational_context {
1555         struct ldb_module *module;
1556         struct ldb_request *req;
1557         enum ldb_scope scope;
1558         const char * const *attrs;
1559         struct ldb_parse_tree *tree;
1560         struct op_controls_flags* controls_flags;
1561         struct op_attributes_operations *list_operations;
1562         unsigned int list_operations_size;
1563         struct op_attributes_replace *attrs_to_replace;
1564         unsigned int attrs_to_replace_size;
1565 };
1566
1567 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
1568 {
1569         struct operational_context *ac;
1570         int ret;
1571
1572         ac = talloc_get_type(req->context, struct operational_context);
1573
1574         if (!ares) {
1575                 return ldb_module_done(ac->req, NULL, NULL,
1576                                         LDB_ERR_OPERATIONS_ERROR);
1577         }
1578         if (ares->error != LDB_SUCCESS) {
1579                 return ldb_module_done(ac->req, ares->controls,
1580                                         ares->response, ares->error);
1581         }
1582
1583         switch (ares->type) {
1584         case LDB_REPLY_ENTRY:
1585                 /* for each record returned post-process to add any derived
1586                    attributes that have been asked for */
1587                 ret = operational_search_post_process(ac->module,
1588                                                       ares->message,
1589                                                       ac->scope,
1590                                                       ac->attrs,
1591                                                       req->op.search.attrs,
1592                                                       ac->controls_flags,
1593                                                       ac->list_operations,
1594                                                       ac->list_operations_size,
1595                                                       ac->attrs_to_replace,
1596                                                       ac->attrs_to_replace_size,
1597                                                       req,
1598                                                       ares);
1599                 if (ret != 0) {
1600                         return ldb_module_done(ac->req, NULL, NULL,
1601                                                 LDB_ERR_OPERATIONS_ERROR);
1602                 }
1603                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
1604
1605         case LDB_REPLY_REFERRAL:
1606                 return ldb_module_send_referral(ac->req, ares->referral);
1607
1608         case LDB_REPLY_DONE:
1609
1610                 return ldb_module_done(ac->req, ares->controls,
1611                                         ares->response, LDB_SUCCESS);
1612         }
1613
1614         talloc_free(ares);
1615         return LDB_SUCCESS;
1616 }
1617
1618 static struct op_attributes_operations* operation_get_op_list(TALLOC_CTX *ctx,
1619                                                               const char* const* attrs,
1620                                                               const char* const* searched_attrs,
1621                                                               struct op_controls_flags* controls_flags)
1622 {
1623         int idx = 0;
1624         int i;
1625         struct op_attributes_operations *list = talloc_zero_array(ctx,
1626                                                                   struct op_attributes_operations,
1627                                                                   ARRAY_SIZE(operational_remove) + 1);
1628
1629         if (list == NULL) {
1630                 return NULL;
1631         }
1632
1633         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
1634                 switch (operational_remove[i].op) {
1635                 case OPERATIONAL_REMOVE_UNASKED:
1636                         if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
1637                                 continue;
1638                         }
1639                         if (ldb_attr_in_list(searched_attrs, operational_remove[i].attr)) {
1640                                 continue;
1641                         }
1642                         list[idx].attr = operational_remove[i].attr;
1643                         list[idx].op = OPERATIONAL_REMOVE_UNASKED;
1644                         idx++;
1645                         break;
1646
1647                 case OPERATIONAL_REMOVE_ALWAYS:
1648                         list[idx].attr = operational_remove[i].attr;
1649                         list[idx].op = OPERATIONAL_REMOVE_ALWAYS;
1650                         idx++;
1651                         break;
1652
1653                 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
1654                         if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
1655                                 list[idx].attr = operational_remove[i].attr;
1656                                 list[idx].op = OPERATIONAL_REMOVE_UNLESS_CONTROL;
1657                                 idx++;
1658                         }
1659                         break;
1660
1661                 case OPERATIONAL_SD_FLAGS:
1662                         if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
1663                                 continue;
1664                         }
1665                         if (controls_flags->sd) {
1666                                 if (attrs == NULL) {
1667                                         continue;
1668                                 }
1669                                 if (attrs[0] == NULL) {
1670                                         continue;
1671                                 }
1672                                 if (ldb_attr_in_list(attrs, "*")) {
1673                                         continue;
1674                                 }
1675                         }
1676                         list[idx].attr = operational_remove[i].attr;
1677                         list[idx].op = OPERATIONAL_SD_FLAGS;
1678                         idx++;
1679                         break;
1680                 }
1681         }
1682
1683         return list;
1684 }
1685
1686 struct operational_present_ctx {
1687         const char *attr;
1688         bool found_operational;
1689 };
1690
1691 /*
1692   callback to determine if an operational attribute (needing
1693   replacement) is in use at all
1694  */
1695 static int operational_present(struct ldb_parse_tree *tree, void *private_context)
1696 {
1697         struct operational_present_ctx *ctx = private_context;
1698         switch (tree->operation) {
1699         case LDB_OP_EQUALITY:
1700                 if (ldb_attr_cmp(tree->u.equality.attr, ctx->attr) == 0) {
1701                         ctx->found_operational = true;
1702                 }
1703                 break;
1704         case LDB_OP_GREATER:
1705         case LDB_OP_LESS:
1706         case LDB_OP_APPROX:
1707                 if (ldb_attr_cmp(tree->u.comparison.attr, ctx->attr) == 0) {
1708                         ctx->found_operational = true;
1709                 }
1710                 break;
1711         case LDB_OP_SUBSTRING:
1712                 if (ldb_attr_cmp(tree->u.substring.attr, ctx->attr) == 0) {
1713                         ctx->found_operational = true;
1714                 }
1715                 break;
1716         case LDB_OP_PRESENT:
1717                 if (ldb_attr_cmp(tree->u.present.attr, ctx->attr) == 0) {
1718                         ctx->found_operational = true;
1719                 }
1720                 break;
1721         case LDB_OP_EXTENDED:
1722                 if (tree->u.extended.attr &&
1723                     ldb_attr_cmp(tree->u.extended.attr, ctx->attr) == 0) {
1724                         ctx->found_operational = true;
1725                 }
1726                 break;
1727         default:
1728                 break;
1729         }
1730         return LDB_SUCCESS;
1731 }
1732
1733
1734 static int operational_search(struct ldb_module *module, struct ldb_request *req)
1735 {
1736         struct ldb_context *ldb;
1737         struct operational_context *ac;
1738         struct ldb_request *down_req;
1739         const char **search_attrs = NULL;
1740         struct operational_present_ctx ctx;
1741         unsigned int i, a;
1742         int ret;
1743
1744         /* There are no operational attributes on special DNs */
1745         if (ldb_dn_is_special(req->op.search.base)) {
1746                 return ldb_next_request(module, req);
1747         }
1748
1749         ldb = ldb_module_get_ctx(module);
1750
1751         ac = talloc(req, struct operational_context);
1752         if (ac == NULL) {
1753                 return ldb_oom(ldb);
1754         }
1755
1756         ac->module = module;
1757         ac->req = req;
1758         ac->scope = req->op.search.scope;
1759         ac->attrs = req->op.search.attrs;
1760
1761         ctx.found_operational = false;
1762
1763         /*
1764          * find any attributes in the parse tree that are searchable,
1765          * but are stored using a different name in the backend, so we
1766          * only duplicate the memory when needed
1767          */
1768         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
1769                 ctx.attr = parse_tree_sub[i].attr;
1770
1771                 ldb_parse_tree_walk(req->op.search.tree,
1772                                     operational_present,
1773                                     &ctx);
1774                 if (ctx.found_operational) {
1775                         break;
1776                 }
1777         }
1778
1779         if (ctx.found_operational) {
1780
1781                 ac->tree = ldb_parse_tree_copy_shallow(ac,
1782                                                        req->op.search.tree);
1783
1784                 if (ac->tree == NULL) {
1785                         return ldb_operr(ldb);
1786                 }
1787
1788                 /* replace any attributes in the parse tree that are
1789                    searchable, but are stored using a different name in the
1790                    backend */
1791                 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
1792                         ldb_parse_tree_attr_replace(ac->tree,
1793                                                     parse_tree_sub[i].attr,
1794                                                     parse_tree_sub[i].replace);
1795                 }
1796         } else {
1797                 /* Avoid allocating a copy if we do not need to */
1798                 ac->tree = req->op.search.tree;
1799         }
1800
1801         ac->controls_flags = talloc(ac, struct op_controls_flags);
1802         /* remember if the SD_FLAGS_OID was set */
1803         ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
1804         /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
1805         ac->controls_flags->bypassoperational =
1806                 (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
1807
1808         ac->attrs_to_replace = NULL;
1809         ac->attrs_to_replace_size = 0;
1810         /* in the list of attributes we are looking for, rename any
1811            attributes to the alias for any hidden attributes that can
1812            be fetched directly using non-hidden names.
1813            Note that order here can affect performance, e.g. we should process
1814            msDS-ResultantPSO before msDS-User-Account-Control-Computed (as the
1815            latter is also dependent on the PSO information) */
1816         for (a=0;ac->attrs && ac->attrs[a];a++) {
1817                 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
1818                         continue;
1819                 }
1820                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
1821
1822                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) != 0 ) {
1823                                 continue;
1824                         }
1825
1826                         ac->attrs_to_replace = talloc_realloc(ac,
1827                                                               ac->attrs_to_replace,
1828                                                               struct op_attributes_replace,
1829                                                               ac->attrs_to_replace_size + 1);
1830
1831                         ac->attrs_to_replace[ac->attrs_to_replace_size] = search_sub[i];
1832                         ac->attrs_to_replace_size++;
1833
1834                         if (search_sub[i].extra_attrs && search_sub[i].extra_attrs[0]) {
1835                                 unsigned int j;
1836                                 const char **search_attrs2;
1837                                 /* Only adds to the end of the list */
1838                                 for (j = 0; search_sub[i].extra_attrs[j]; j++) {
1839                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
1840                                                                                ? search_attrs
1841                                                                                : ac->attrs,
1842                                                                                search_sub[i].extra_attrs[j]);
1843                                         if (search_attrs2 == NULL) {
1844                                                 return ldb_operr(ldb);
1845                                         }
1846                                         /* may be NULL, talloc_free() doesn't mind */
1847                                         talloc_free(search_attrs);
1848                                         search_attrs = search_attrs2;
1849                                 }
1850                         }
1851
1852                         if (!search_sub[i].replace) {
1853                                 continue;
1854                         }
1855
1856                         if (!search_attrs) {
1857                                 search_attrs = ldb_attr_list_copy(req, ac->attrs);
1858                                 if (search_attrs == NULL) {
1859                                         return ldb_operr(ldb);
1860                                 }
1861                         }
1862                         /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
1863                         search_attrs[a] = search_sub[i].replace;
1864                 }
1865         }
1866         ac->list_operations = operation_get_op_list(ac, ac->attrs,
1867                                                     search_attrs == NULL?req->op.search.attrs:search_attrs,
1868                                                     ac->controls_flags);
1869         ac->list_operations_size = 0;
1870         i = 0;
1871
1872         while (ac->list_operations && ac->list_operations[i].attr != NULL) {
1873                 i++;
1874         }
1875         ac->list_operations_size = i;
1876         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
1877                                         req->op.search.base,
1878                                         req->op.search.scope,
1879                                         ac->tree,
1880                                         /* use new set of attrs if any */
1881                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
1882                                         req->controls,
1883                                         ac, operational_callback,
1884                                         req);
1885         LDB_REQ_SET_LOCATION(down_req);
1886         if (ret != LDB_SUCCESS) {
1887                 return ldb_operr(ldb);
1888         }
1889
1890         /* perform the search */
1891         return ldb_next_request(module, down_req);
1892 }
1893
1894 static int operational_init(struct ldb_module *ctx)
1895 {
1896         struct operational_data *data;
1897         int ret;
1898
1899         ret = ldb_next_init(ctx);
1900
1901         if (ret != LDB_SUCCESS) {
1902                 return ret;
1903         }
1904
1905         data = talloc_zero(ctx, struct operational_data);
1906         if (!data) {
1907                 return ldb_module_oom(ctx);
1908         }
1909
1910         ldb_module_set_private(ctx, data);
1911
1912         return LDB_SUCCESS;
1913 }
1914
1915 static const struct ldb_module_ops ldb_operational_module_ops = {
1916         .name              = "operational",
1917         .search            = operational_search,
1918         .init_context      = operational_init
1919 };
1920
1921 int ldb_operational_module_init(const char *version)
1922 {
1923         LDB_MODULE_CHECK_VERSION(version);
1924         return ldb_register_module(&ldb_operational_module_ops);
1925 }