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