LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - operational.c (source / functions) Hit Total Coverage
Test: coverage report for master 70ed9daf Lines: 569 736 77.3 %
Date: 2024-01-11 09:59:51 Functions: 34 35 97.1 %

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

Generated by: LCOV version 1.14