dsdb: Change LDB_TYPESAFE_QSORT() to TYPESAFE_QSORT() in operational module
[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         *pso_count = 0;
998         domain_dn = ldb_get_default_basedn(ldb);
999         psc_dn = ldb_dn_new_fmt(mem_ctx, ldb,
1000                                 "CN=Password Settings Container,CN=System,%s",
1001                                 ldb_dn_get_linearized(domain_dn));
1002         if (psc_dn == NULL) {
1003                 return ldb_oom(ldb);
1004         }
1005
1006         /* get the number of PSO children */
1007         ret = dsdb_module_search(module, mem_ctx, &res, psc_dn,
1008                                  LDB_SCOPE_ONELEVEL, attrs,
1009                                  DSDB_FLAG_NEXT_MODULE, parent,
1010                                  "(objectClass=msDS-PasswordSettings)");
1011
1012         /*
1013          * Just ignore PSOs if the container doesn't exist. This is a weird
1014          * corner-case where the AD DB was created from a pre-2008 base schema,
1015          * and then the FL was manually upgraded.
1016          */
1017         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1018                 DBG_NOTICE("No Password Settings Container exists\n");
1019                 return LDB_SUCCESS;
1020         }
1021
1022         if (ret != LDB_SUCCESS) {
1023                 return ret;
1024         }
1025
1026         *pso_count = res->count;
1027         talloc_free(res);
1028         talloc_free(psc_dn);
1029
1030         return LDB_SUCCESS;
1031 }
1032
1033 /*
1034  * Compares two PSO objects returned by a search, to work out the better PSO.
1035  * The PSO with the lowest precedence is better, otherwise (if the precedence
1036  * is equal) the PSO with the lower GUID wins.
1037  */
1038 static int pso_compare(struct ldb_message **m1, struct ldb_message **m2)
1039 {
1040         uint32_t prec1;
1041         uint32_t prec2;
1042
1043         prec1 = ldb_msg_find_attr_as_uint(*m1, "msDS-PasswordSettingsPrecedence",
1044                                           0xffffffff);
1045         prec2 = ldb_msg_find_attr_as_uint(*m2, "msDS-PasswordSettingsPrecedence",
1046                                           0xffffffff);
1047
1048         /* if precedence is equal, use the lowest GUID */
1049         if (prec1 == prec2) {
1050                 struct GUID guid1 = samdb_result_guid(*m1, "objectGUID");
1051                 struct GUID guid2 = samdb_result_guid(*m2, "objectGUID");
1052
1053                 return ndr_guid_compare(&guid1, &guid2);
1054         } else {
1055                 return prec1 - prec2;
1056         }
1057 }
1058
1059 /*
1060  * Search for PSO objects that apply to the object SIDs specified
1061  */
1062 static int pso_search_by_sids(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1063                               struct ldb_request *parent,
1064                               struct dom_sid *sid_array, unsigned int num_sids,
1065                               struct ldb_result **result)
1066 {
1067         int ret;
1068         int i;
1069         struct ldb_context *ldb = ldb_module_get_ctx(module);
1070         char *sid_filter = NULL;
1071         struct ldb_dn *domain_dn = NULL;
1072         struct ldb_dn *psc_dn = NULL;
1073         const char *attrs[] = {
1074                 "msDS-PasswordSettingsPrecedence",
1075                 "objectGUID",
1076                 "msDS-LockoutDuration",
1077                 "msDS-MaximumPasswordAge",
1078                 NULL
1079         };
1080
1081         /* build a query for PSO objects that apply to any of the SIDs given */
1082         sid_filter = talloc_strdup(mem_ctx, "");
1083
1084         for (i = 0; sid_filter && i < num_sids; i++) {
1085                 struct dom_sid_buf sid_buf;
1086
1087                 sid_filter = talloc_asprintf_append(
1088                         sid_filter,
1089                         "(msDS-PSOAppliesTo=<SID=%s>)",
1090                         dom_sid_str_buf(&sid_array[i], &sid_buf));
1091         }
1092
1093         if (sid_filter == NULL) {
1094                 return ldb_oom(ldb);
1095         }
1096
1097         /* only PSOs located in the Password Settings Container are valid */
1098         domain_dn = ldb_get_default_basedn(ldb);
1099         psc_dn = ldb_dn_new_fmt(mem_ctx, ldb,
1100                                 "CN=Password Settings Container,CN=System,%s",
1101                                 ldb_dn_get_linearized(domain_dn));
1102         if (psc_dn == NULL) {
1103                 return ldb_oom(ldb);
1104         }
1105
1106         ret = dsdb_module_search(module, mem_ctx, result, psc_dn,
1107                                  LDB_SCOPE_ONELEVEL, attrs,
1108                                  DSDB_FLAG_NEXT_MODULE, parent,
1109                                  "(&(objectClass=msDS-PasswordSettings)(|%s))",
1110                                  sid_filter);
1111         talloc_free(sid_filter);
1112         return ret;
1113 }
1114
1115 /*
1116  * Returns the best PSO object that applies to the object SID(s) specified
1117  */
1118 static int pso_find_best(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1119                          struct ldb_request *parent, struct dom_sid *sid_array,
1120                          unsigned int num_sids, struct ldb_message **best_pso)
1121 {
1122         struct ldb_result *res = NULL;
1123         int ret;
1124
1125         *best_pso = NULL;
1126
1127         /* find any PSOs that apply to the SIDs specified */
1128         ret = pso_search_by_sids(module, mem_ctx, parent, sid_array, num_sids,
1129                                  &res);
1130         if (ret != LDB_SUCCESS) {
1131                 DBG_ERR("Error %d retrieving PSO for SID(s)\n", ret);
1132                 return ret;
1133         }
1134
1135         /* sort the list so that the best PSO is first */
1136         TYPESAFE_QSORT(res->msgs, res->count, pso_compare);
1137
1138         if (res->count > 0) {
1139                 *best_pso = res->msgs[0];
1140         }
1141
1142         return LDB_SUCCESS;
1143 }
1144
1145 /*
1146  * Determines the Password Settings Object (PSO) that applies to the given user
1147  */
1148 static int get_pso_for_user(struct ldb_module *module,
1149                             struct ldb_message *user_msg,
1150                             struct ldb_request *parent,
1151                             struct ldb_message **pso_msg)
1152 {
1153         bool pso_supported;
1154         struct dom_sid *groupSIDs = NULL;
1155         unsigned int num_groupSIDs = 0;
1156         struct ldb_context *ldb = ldb_module_get_ctx(module);
1157         struct ldb_message *best_pso = NULL;
1158         struct ldb_dn *pso_dn = NULL;
1159         int ret;
1160         struct ldb_message_element *el = NULL;
1161         TALLOC_CTX *tmp_ctx = NULL;
1162         int pso_count = 0;
1163         struct ldb_result *res = NULL;
1164         static const char *attrs[] = {
1165                 "msDS-LockoutDuration",
1166                 "msDS-MaximumPasswordAge",
1167                 NULL
1168         };
1169
1170         *pso_msg = NULL;
1171
1172         /* first, check msDS-ResultantPSO is supported for this object */
1173         pso_supported = pso_is_supported(ldb, user_msg);
1174
1175         if (!pso_supported) {
1176                 return LDB_SUCCESS;
1177         }
1178
1179         tmp_ctx = talloc_new(user_msg);
1180
1181         /*
1182          * Several different constructed attributes try to use the PSO info. If
1183          * we've already constructed the msDS-ResultantPSO for this user, we can
1184          * just re-use the result, rather than calculating it from scratch again
1185          */
1186         pso_dn = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, user_msg,
1187                                          "msDS-ResultantPSO");
1188
1189         if (pso_dn != NULL) {
1190                 ret = dsdb_module_search_dn(module, tmp_ctx, &res, pso_dn,
1191                                             attrs, DSDB_FLAG_NEXT_MODULE,
1192                                             parent);
1193                 if (ret != LDB_SUCCESS) {
1194                         DBG_ERR("Error %d retrieving PSO %s\n", ret,
1195                                 ldb_dn_get_linearized(pso_dn));
1196                         talloc_free(tmp_ctx);
1197                         return ret;
1198                 }
1199
1200                 if (res->count == 1) {
1201                         *pso_msg = res->msgs[0];
1202                         return LDB_SUCCESS;
1203                 }
1204         }
1205
1206         /*
1207          * if any PSOs apply directly to the user, they are considered first
1208          * before we check group membership PSOs
1209          */
1210         el = ldb_msg_find_element(user_msg, "msDS-PSOApplied");
1211
1212         if (el != NULL && el->num_values > 0) {
1213                 struct dom_sid *user_sid = NULL;
1214
1215                 /* lookup the best PSO object, based on the user's SID */
1216                 user_sid = samdb_result_dom_sid(tmp_ctx, user_msg, "objectSid");
1217
1218                 ret = pso_find_best(module, tmp_ctx, parent, user_sid, 1,
1219                                     &best_pso);
1220                 if (ret != LDB_SUCCESS) {
1221                         talloc_free(tmp_ctx);
1222                         return ret;
1223                 }
1224
1225                 if (best_pso != NULL) {
1226                         *pso_msg = best_pso;
1227                         return LDB_SUCCESS;
1228                 }
1229         }
1230
1231         /*
1232          * If no valid PSO applies directly to the user, then try its groups.
1233          * The group expansion is expensive, so check there are actually
1234          * PSOs in the DB first (which is a quick search). Note in the above
1235          * cases we could tell that a PSO applied to the user, based on info
1236          * already retrieved by the user search.
1237          */
1238         ret = get_pso_count(module, tmp_ctx, parent, &pso_count);
1239         if (ret != LDB_SUCCESS) {
1240                 DBG_ERR("Error %d determining PSOs in system\n", ret);
1241                 talloc_free(tmp_ctx);
1242                 return ret;
1243         }
1244
1245         if (pso_count == 0) {
1246                 talloc_free(tmp_ctx);
1247                 return LDB_SUCCESS;
1248         }
1249
1250         /* Work out the SIDs of any account groups the user is a member of */
1251         ret = get_group_sids(ldb, tmp_ctx, user_msg,
1252                              "msDS-ResultantPSO", ACCOUNT_GROUPS,
1253                              &groupSIDs, &num_groupSIDs);
1254         if (ret != LDB_SUCCESS) {
1255                 DBG_ERR("Error %d determining group SIDs for %s\n", ret,
1256                         ldb_dn_get_linearized(user_msg->dn));
1257                 talloc_free(tmp_ctx);
1258                 return ret;
1259         }
1260
1261         /* lookup the best PSO that applies to any of these groups */
1262         ret = pso_find_best(module, tmp_ctx, parent, groupSIDs,
1263                             num_groupSIDs, &best_pso);
1264         if (ret != LDB_SUCCESS) {
1265                 talloc_free(tmp_ctx);
1266                 return ret;
1267         }
1268
1269         *pso_msg = best_pso;
1270         return LDB_SUCCESS;
1271 }
1272
1273 /*
1274  * Constructs the msDS-ResultantPSO attribute, which is the DN of the Password
1275  * Settings Object (PSO) that applies to that user.
1276  */
1277 static int construct_resultant_pso(struct ldb_module *module,
1278                                    struct ldb_message *msg,
1279                                    enum ldb_scope scope,
1280                                    struct ldb_request *parent)
1281 {
1282         struct ldb_message *pso = NULL;
1283         int ret;
1284
1285         /* work out the PSO (if any) that applies to this user */
1286         ret = get_pso_for_user(module, msg, parent, &pso);
1287         if (ret != LDB_SUCCESS) {
1288                 DBG_ERR("Couldn't determine PSO for %s\n",
1289                         ldb_dn_get_linearized(msg->dn));
1290                 return ret;
1291         }
1292
1293         if (pso != NULL) {
1294                 DBG_INFO("%s is resultant PSO for user %s\n",
1295                          ldb_dn_get_linearized(pso->dn),
1296                          ldb_dn_get_linearized(msg->dn));
1297                 return ldb_msg_add_string(msg, "msDS-ResultantPSO",
1298                                           ldb_dn_get_linearized(pso->dn));
1299         }
1300
1301         /* no PSO applies to this user */
1302         return LDB_SUCCESS;
1303 }
1304
1305 struct op_controls_flags {
1306         bool sd;
1307         bool bypassoperational;
1308 };
1309
1310 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
1311         if (controls_flags->bypassoperational && ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 ) {
1312                 return true;
1313         }
1314         return false;
1315 }
1316
1317 /*
1318   a list of attribute names that should be substituted in the parse
1319   tree before the search is done
1320 */
1321 static const struct {
1322         const char *attr;
1323         const char *replace;
1324 } parse_tree_sub[] = {
1325         { "createTimeStamp", "whenCreated" },
1326         { "modifyTimeStamp", "whenChanged" }
1327 };
1328
1329
1330 struct op_attributes_replace {
1331         const char *attr;
1332         const char *replace;
1333         const char * const *extra_attrs;
1334         int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope, struct ldb_request *);
1335 };
1336
1337 /* the 'extra_attrs' required for msDS-ResultantPSO */
1338 #define RESULTANT_PSO_COMPUTED_ATTRS \
1339         "msDS-PSOApplied", \
1340         "userAccountControl", \
1341         "objectSid", \
1342         "msDS-SecondaryKrbTgtNumber", \
1343         "primaryGroupID"
1344
1345 /*
1346  * any other constructed attributes that want to work out the PSO also need to
1347  * include objectClass (this gets included via 'replace' for msDS-ResultantPSO)
1348  */
1349 #define PSO_ATTR_DEPENDENCIES \
1350         RESULTANT_PSO_COMPUTED_ATTRS, \
1351         "objectClass"
1352
1353 static const char *objectSid_attr[] =
1354 {
1355         "objectSid",
1356         NULL
1357 };
1358
1359
1360 static const char *objectCategory_attr[] =
1361 {
1362         "objectCategory",
1363         NULL
1364 };
1365
1366
1367 static const char *user_account_control_computed_attrs[] =
1368 {
1369         "lockoutTime",
1370         "pwdLastSet",
1371         PSO_ATTR_DEPENDENCIES,
1372         NULL
1373 };
1374
1375
1376 static const char *user_password_expiry_time_computed_attrs[] =
1377 {
1378         "pwdLastSet",
1379         PSO_ATTR_DEPENDENCIES,
1380         NULL
1381 };
1382
1383 static const char *resultant_pso_computed_attrs[] =
1384 {
1385         RESULTANT_PSO_COMPUTED_ATTRS,
1386         NULL
1387 };
1388
1389 /*
1390   a list of attribute names that are hidden, but can be searched for
1391   using another (non-hidden) name to produce the correct result
1392 */
1393 static const struct op_attributes_replace search_sub[] = {
1394         { "createTimeStamp", "whenCreated", NULL , NULL },
1395         { "modifyTimeStamp", "whenChanged", NULL , construct_modifyTimeStamp},
1396         { "structuralObjectClass", "objectClass", NULL , NULL },
1397         { "canonicalName", NULL, NULL , construct_canonical_name },
1398         { "primaryGroupToken", "objectClass", objectSid_attr, construct_primary_group_token },
1399         { "tokenGroups", "primaryGroupID", objectSid_attr, construct_token_groups },
1400         { "tokenGroupsNoGCAcceptable", "primaryGroupID", objectSid_attr, construct_token_groups_no_gc},
1401         { "tokenGroupsGlobalAndUniversal", "primaryGroupID", objectSid_attr, construct_global_universal_token_groups },
1402         { "parentGUID", NULL, NULL, construct_parent_guid },
1403         { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
1404         { "msDS-isRODC", "objectClass", objectCategory_attr, construct_msds_isrodc },
1405         { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber },
1406         { "msDS-User-Account-Control-Computed", "userAccountControl", user_account_control_computed_attrs,
1407           construct_msds_user_account_control_computed },
1408         { "msDS-UserPasswordExpiryTimeComputed", "userAccountControl", user_password_expiry_time_computed_attrs,
1409           construct_msds_user_password_expiry_time_computed },
1410         { "msDS-ResultantPSO", "objectClass", resultant_pso_computed_attrs,
1411           construct_resultant_pso }
1412 };
1413
1414
1415 enum op_remove {
1416         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
1417         OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
1418         OPERATIONAL_SD_FLAGS,      /* show if SD_FLAGS_OID set, or asked for */
1419         OPERATIONAL_REMOVE_UNLESS_CONTROL        /* remove always unless an adhoc control has been specified */
1420 };
1421
1422 /*
1423   a list of attributes that may need to be removed from the
1424   underlying db return
1425
1426   Some of these are attributes that were once stored, but are now calculated
1427 */
1428 struct op_attributes_operations {
1429         const char *attr;
1430         enum op_remove op;
1431 };
1432
1433 static const struct op_attributes_operations operational_remove[] = {
1434         { "nTSecurityDescriptor",    OPERATIONAL_SD_FLAGS },
1435         { "msDS-KeyVersionNumber",   OPERATIONAL_REMOVE_UNLESS_CONTROL  },
1436         { "parentGUID",              OPERATIONAL_REMOVE_ALWAYS  },
1437         { "replPropertyMetaData",    OPERATIONAL_REMOVE_UNASKED },
1438 #define _SEP ,OPERATIONAL_REMOVE_UNASKED},{
1439         { DSDB_SECRET_ATTRIBUTES_EX(_SEP), OPERATIONAL_REMOVE_UNASKED }
1440 };
1441
1442
1443 /*
1444   post process a search result record. For any search_sub[] attributes that were
1445   asked for, we need to call the appropriate copy routine to copy the result
1446   into the message, then remove any attributes that we added to the search but
1447   were not asked for by the user
1448 */
1449 static int operational_search_post_process(struct ldb_module *module,
1450                                            struct ldb_message *msg,
1451                                            enum ldb_scope scope,
1452                                            const char * const *attrs_from_user,
1453                                            const char * const *attrs_searched_for,
1454                                            struct op_controls_flags* controls_flags,
1455                                            struct op_attributes_operations *list,
1456                                            unsigned int list_size,
1457                                            struct op_attributes_replace *list_replace,
1458                                            unsigned int list_replace_size,
1459                                            struct ldb_request *parent)
1460 {
1461         struct ldb_context *ldb;
1462         unsigned int i, a = 0;
1463         bool constructed_attributes = false;
1464
1465         ldb = ldb_module_get_ctx(module);
1466
1467         /* removed any attrs that should not be shown to the user */
1468         for (i=0; i < list_size; i++) {
1469                 ldb_msg_remove_attr(msg, list[i].attr);
1470         }
1471
1472         for (a=0; a < list_replace_size; a++) {
1473                 if (check_keep_control_for_attribute(controls_flags,
1474                                                      list_replace[a].attr)) {
1475                         continue;
1476                 }
1477
1478                 /* construct the new attribute, using either a supplied
1479                         constructor or a simple copy */
1480                 constructed_attributes = true;
1481                 if (list_replace[a].constructor != NULL) {
1482                         if (list_replace[a].constructor(module, msg, scope, parent) != LDB_SUCCESS) {
1483                                 goto failed;
1484                         }
1485                 } else if (ldb_msg_copy_attr(msg,
1486                                              list_replace[a].replace,
1487                                              list_replace[a].attr) != LDB_SUCCESS) {
1488                         goto failed;
1489                 }
1490         }
1491
1492         /* Deletion of the search helper attributes are needed if:
1493          * - we generated constructed attributes and
1494          * - we aren't requesting all attributes
1495          */
1496         if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
1497                 for (i=0; i < list_replace_size; i++) {
1498                         /* remove the added search helper attributes, unless
1499                          * they were asked for by the user */
1500                         if (list_replace[i].replace != NULL &&
1501                             !ldb_attr_in_list(attrs_from_user, list_replace[i].replace)) {
1502                                 ldb_msg_remove_attr(msg, list_replace[i].replace);
1503                         }
1504                         if (list_replace[i].extra_attrs != NULL) {
1505                                 unsigned int j;
1506                                 for (j=0; list_replace[i].extra_attrs[j]; j++) {
1507                                         if (!ldb_attr_in_list(attrs_from_user, list_replace[i].extra_attrs[j])) {
1508                                                 ldb_msg_remove_attr(msg, list_replace[i].extra_attrs[j]);
1509                                         }
1510                                 }
1511                         }
1512                 }
1513         }
1514
1515         return 0;
1516
1517 failed:
1518         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
1519                       "operational_search_post_process failed for attribute '%s' - %s",
1520                       list_replace[a].attr, ldb_errstring(ldb));
1521         return -1;
1522 }
1523
1524 /*
1525   hook search operations
1526 */
1527
1528 struct operational_context {
1529         struct ldb_module *module;
1530         struct ldb_request *req;
1531         enum ldb_scope scope;
1532         const char * const *attrs;
1533         struct op_controls_flags* controls_flags;
1534         struct op_attributes_operations *list_operations;
1535         unsigned int list_operations_size;
1536         struct op_attributes_replace *attrs_to_replace;
1537         unsigned int attrs_to_replace_size;
1538 };
1539
1540 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
1541 {
1542         struct operational_context *ac;
1543         int ret;
1544
1545         ac = talloc_get_type(req->context, struct operational_context);
1546
1547         if (!ares) {
1548                 return ldb_module_done(ac->req, NULL, NULL,
1549                                         LDB_ERR_OPERATIONS_ERROR);
1550         }
1551         if (ares->error != LDB_SUCCESS) {
1552                 return ldb_module_done(ac->req, ares->controls,
1553                                         ares->response, ares->error);
1554         }
1555
1556         switch (ares->type) {
1557         case LDB_REPLY_ENTRY:
1558                 /* for each record returned post-process to add any derived
1559                    attributes that have been asked for */
1560                 ret = operational_search_post_process(ac->module,
1561                                                       ares->message,
1562                                                       ac->scope,
1563                                                       ac->attrs,
1564                                                       req->op.search.attrs,
1565                                                       ac->controls_flags,
1566                                                       ac->list_operations,
1567                                                       ac->list_operations_size,
1568                                                       ac->attrs_to_replace,
1569                                                       ac->attrs_to_replace_size,
1570                                                       req);
1571                 if (ret != 0) {
1572                         return ldb_module_done(ac->req, NULL, NULL,
1573                                                 LDB_ERR_OPERATIONS_ERROR);
1574                 }
1575                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
1576
1577         case LDB_REPLY_REFERRAL:
1578                 return ldb_module_send_referral(ac->req, ares->referral);
1579
1580         case LDB_REPLY_DONE:
1581
1582                 return ldb_module_done(ac->req, ares->controls,
1583                                         ares->response, LDB_SUCCESS);
1584         }
1585
1586         talloc_free(ares);
1587         return LDB_SUCCESS;
1588 }
1589
1590 static struct op_attributes_operations* operation_get_op_list(TALLOC_CTX *ctx,
1591                                                               const char* const* attrs,
1592                                                               const char* const* searched_attrs,
1593                                                               struct op_controls_flags* controls_flags)
1594 {
1595         int idx = 0;
1596         int i;
1597         struct op_attributes_operations *list = talloc_zero_array(ctx,
1598                                                                   struct op_attributes_operations,
1599                                                                   ARRAY_SIZE(operational_remove) + 1);
1600
1601         if (list == NULL) {
1602                 return NULL;
1603         }
1604
1605         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
1606                 switch (operational_remove[i].op) {
1607                 case OPERATIONAL_REMOVE_UNASKED:
1608                         if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
1609                                 continue;
1610                         }
1611                         if (ldb_attr_in_list(searched_attrs, operational_remove[i].attr)) {
1612                                 continue;
1613                         }
1614                         list[idx].attr = operational_remove[i].attr;
1615                         list[idx].op = OPERATIONAL_REMOVE_UNASKED;
1616                         idx++;
1617                         break;
1618
1619                 case OPERATIONAL_REMOVE_ALWAYS:
1620                         list[idx].attr = operational_remove[i].attr;
1621                         list[idx].op = OPERATIONAL_REMOVE_ALWAYS;
1622                         idx++;
1623                         break;
1624
1625                 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
1626                         if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
1627                                 list[idx].attr = operational_remove[i].attr;
1628                                 list[idx].op = OPERATIONAL_REMOVE_UNLESS_CONTROL;
1629                                 idx++;
1630                         }
1631                         break;
1632
1633                 case OPERATIONAL_SD_FLAGS:
1634                         if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
1635                                 continue;
1636                         }
1637                         if (controls_flags->sd) {
1638                                 if (attrs == NULL) {
1639                                         continue;
1640                                 }
1641                                 if (attrs[0] == NULL) {
1642                                         continue;
1643                                 }
1644                                 if (ldb_attr_in_list(attrs, "*")) {
1645                                         continue;
1646                                 }
1647                         }
1648                         list[idx].attr = operational_remove[i].attr;
1649                         list[idx].op = OPERATIONAL_SD_FLAGS;
1650                         idx++;
1651                         break;
1652                 }
1653         }
1654
1655         return list;
1656 }
1657
1658 static int operational_search(struct ldb_module *module, struct ldb_request *req)
1659 {
1660         struct ldb_context *ldb;
1661         struct operational_context *ac;
1662         struct ldb_request *down_req;
1663         const char **search_attrs = NULL;
1664         unsigned int i, a;
1665         int ret;
1666
1667         /* There are no operational attributes on special DNs */
1668         if (ldb_dn_is_special(req->op.search.base)) {
1669                 return ldb_next_request(module, req);
1670         }
1671
1672         ldb = ldb_module_get_ctx(module);
1673
1674         ac = talloc(req, struct operational_context);
1675         if (ac == NULL) {
1676                 return ldb_oom(ldb);
1677         }
1678
1679         ac->module = module;
1680         ac->req = req;
1681         ac->scope = req->op.search.scope;
1682         ac->attrs = req->op.search.attrs;
1683
1684         /*  FIXME: We must copy the tree and keep the original
1685          *  unmodified. SSS */
1686         /* replace any attributes in the parse tree that are
1687            searchable, but are stored using a different name in the
1688            backend */
1689         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
1690                 ldb_parse_tree_attr_replace(req->op.search.tree,
1691                                             parse_tree_sub[i].attr,
1692                                             parse_tree_sub[i].replace);
1693         }
1694
1695         ac->controls_flags = talloc(ac, struct op_controls_flags);
1696         /* remember if the SD_FLAGS_OID was set */
1697         ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
1698         /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
1699         ac->controls_flags->bypassoperational =
1700                 (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
1701
1702         ac->attrs_to_replace = NULL;
1703         ac->attrs_to_replace_size = 0;
1704         /* in the list of attributes we are looking for, rename any
1705            attributes to the alias for any hidden attributes that can
1706            be fetched directly using non-hidden names.
1707            Note that order here can affect performance, e.g. we should process
1708            msDS-ResultantPSO before msDS-User-Account-Control-Computed (as the
1709            latter is also dependent on the PSO information) */
1710         for (a=0;ac->attrs && ac->attrs[a];a++) {
1711                 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
1712                         continue;
1713                 }
1714                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
1715
1716                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) != 0 ) {
1717                                 continue;
1718                         }
1719
1720                         ac->attrs_to_replace = talloc_realloc(ac,
1721                                                               ac->attrs_to_replace,
1722                                                               struct op_attributes_replace,
1723                                                               ac->attrs_to_replace_size + 1);
1724
1725                         ac->attrs_to_replace[ac->attrs_to_replace_size] = search_sub[i];
1726                         ac->attrs_to_replace_size++;
1727                         if (!search_sub[i].replace) {
1728                                 continue;
1729                         }
1730
1731                         if (search_sub[i].extra_attrs && search_sub[i].extra_attrs[0]) {
1732                                 unsigned int j;
1733                                 const char **search_attrs2;
1734                                 /* Only adds to the end of the list */
1735                                 for (j = 0; search_sub[i].extra_attrs[j]; j++) {
1736                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
1737                                                                                ? search_attrs
1738                                                                                : ac->attrs, 
1739                                                                                search_sub[i].extra_attrs[j]);
1740                                         if (search_attrs2 == NULL) {
1741                                                 return ldb_operr(ldb);
1742                                         }
1743                                         /* may be NULL, talloc_free() doesn't mind */
1744                                         talloc_free(search_attrs);
1745                                         search_attrs = search_attrs2;
1746                                 }
1747                         }
1748
1749                         if (!search_attrs) {
1750                                 search_attrs = ldb_attr_list_copy(req, ac->attrs);
1751                                 if (search_attrs == NULL) {
1752                                         return ldb_operr(ldb);
1753                                 }
1754                         }
1755                         /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
1756                         search_attrs[a] = search_sub[i].replace;
1757                 }
1758         }
1759         ac->list_operations = operation_get_op_list(ac, ac->attrs,
1760                                                     search_attrs == NULL?req->op.search.attrs:search_attrs,
1761                                                     ac->controls_flags);
1762         ac->list_operations_size = 0;
1763         i = 0;
1764
1765         while (ac->list_operations && ac->list_operations[i].attr != NULL) {
1766                 i++;
1767         }
1768         ac->list_operations_size = i;
1769         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
1770                                         req->op.search.base,
1771                                         req->op.search.scope,
1772                                         req->op.search.tree,
1773                                         /* use new set of attrs if any */
1774                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
1775                                         req->controls,
1776                                         ac, operational_callback,
1777                                         req);
1778         LDB_REQ_SET_LOCATION(down_req);
1779         if (ret != LDB_SUCCESS) {
1780                 return ldb_operr(ldb);
1781         }
1782
1783         /* perform the search */
1784         return ldb_next_request(module, down_req);
1785 }
1786
1787 static int operational_init(struct ldb_module *ctx)
1788 {
1789         struct operational_data *data;
1790         int ret;
1791
1792         ret = ldb_next_init(ctx);
1793
1794         if (ret != LDB_SUCCESS) {
1795                 return ret;
1796         }
1797
1798         data = talloc_zero(ctx, struct operational_data);
1799         if (!data) {
1800                 return ldb_module_oom(ctx);
1801         }
1802
1803         ldb_module_set_private(ctx, data);
1804
1805         return LDB_SUCCESS;
1806 }
1807
1808 static const struct ldb_module_ops ldb_operational_module_ops = {
1809         .name              = "operational",
1810         .search            = operational_search,
1811         .init_context      = operational_init
1812 };
1813
1814 int ldb_operational_module_init(const char *version)
1815 {
1816         LDB_MODULE_CHECK_VERSION(version);
1817         return ldb_register_module(&ldb_operational_module_ops);
1818 }