LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - operational.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 575 736 78.1 %
Date: 2024-05-31 13:13:24 Functions: 34 35 97.1 %

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

Generated by: LCOV version 1.14