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