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

Generated by: LCOV version 1.14