LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - operational.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 484 612 79.1 %
Date: 2024-06-13 04:01:37 Functions: 33 34 97.1 %

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

Generated by: LCOV version 1.13