LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - acl_read.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 406 485 83.7 %
Date: 2024-06-13 04:01:37 Functions: 22 22 100.0 %

          Line data    Source code
       1             : /*
       2             :   ldb database library
       3             : 
       4             :   Copyright (C) Simo Sorce 2006-2008
       5             :   Copyright (C) Nadezhda Ivanova 2010
       6             : 
       7             :   This program is free software; you can redistribute it and/or modify
       8             :   it under the terms of the GNU General Public License as published by
       9             :   the Free Software Foundation; either version 3 of the License, or
      10             :   (at your option) any later version.
      11             : 
      12             :   This program is distributed in the hope that it will be useful,
      13             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :   GNU General Public License for more details.
      16             : 
      17             :   You should have received a copy of the GNU General Public License
      18             :   along with this program.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : /*
      22             :  *  Name: ldb
      23             :  *
      24             :  *  Component: ldb ACL Read module
      25             :  *
      26             :  *  Description: Module that performs authorisation access checks on read requests
      27             :  *               Only DACL checks implemented at this point
      28             :  *
      29             :  *  Author: Nadezhda Ivanova
      30             :  */
      31             : 
      32             : #include "includes.h"
      33             : #include "ldb_module.h"
      34             : #include "auth/auth.h"
      35             : #include "libcli/security/security.h"
      36             : #include "dsdb/samdb/samdb.h"
      37             : #include "librpc/gen_ndr/ndr_security.h"
      38             : #include "param/param.h"
      39             : #include "dsdb/samdb/ldb_modules/util.h"
      40             : #include "lib/util/binsearch.h"
      41             : 
      42             : #undef strcasecmp
      43             : 
      44             : struct ldb_attr_vec {
      45             :         const char** attrs;
      46             :         size_t len;
      47             :         size_t capacity;
      48             : };
      49             : 
      50             : struct aclread_context {
      51             :         struct ldb_module *module;
      52             :         struct ldb_request *req;
      53             :         const struct dsdb_schema *schema;
      54             :         uint32_t sd_flags;
      55             :         bool added_nTSecurityDescriptor;
      56             :         bool added_instanceType;
      57             :         bool added_objectSid;
      58             :         bool added_objectClass;
      59             : 
      60             :         bool do_list_object_initialized;
      61             :         bool do_list_object;
      62             :         bool base_invisible;
      63             :         uint64_t num_entries;
      64             : 
      65             :         /* cache on the last parent we checked in this search */
      66             :         struct ldb_dn *last_parent_dn;
      67             :         int last_parent_check_ret;
      68             : 
      69             :         bool am_administrator;
      70             : 
      71             :         bool got_tree_attrs;
      72             :         struct ldb_attr_vec tree_attrs;
      73             : };
      74             : 
      75             : struct aclread_private {
      76             :         bool enabled;
      77             : 
      78             :         /* cache of the last SD we read during any search */
      79             :         struct security_descriptor *sd_cached;
      80             :         struct ldb_val sd_cached_blob;
      81             :         const char **password_attrs;
      82             :         size_t num_password_attrs;
      83             : };
      84             : 
      85             : struct access_check_context {
      86             :         struct security_descriptor *sd;
      87             :         struct dom_sid sid_buf;
      88             :         const struct dom_sid *sid;
      89             :         const struct dsdb_class *objectclass;
      90             : };
      91             : 
      92     2832499 : static void acl_element_mark_access_checked(struct ldb_message_element *el)
      93             : {
      94     2832499 :         el->flags |= LDB_FLAG_INTERNAL_ACCESS_CHECKED;
      95     2832499 : }
      96             : 
      97     4480915 : static bool acl_element_is_access_checked(const struct ldb_message_element *el)
      98             : {
      99     4480915 :         return (el->flags & LDB_FLAG_INTERNAL_ACCESS_CHECKED) != 0;
     100             : }
     101             : 
     102    70616913 : static bool attr_in_vec(const struct ldb_attr_vec *vec, const char *attr)
     103             : {
     104    70616913 :         const char **found = NULL;
     105             : 
     106    70616913 :         if (vec == NULL) {
     107           0 :                 return false;
     108             :         }
     109             : 
     110    72278959 :         BINARY_ARRAY_SEARCH_V(vec->attrs,
     111             :                               vec->len,
     112             :                               attr,
     113             :                               ldb_attr_cmp,
     114             :                               found);
     115    70616913 :         return found != NULL;
     116             : }
     117             : 
     118      186795 : static int acl_attr_cmp_fn(const char *a, const char **b)
     119             : {
     120      186795 :         return ldb_attr_cmp(a, *b);
     121             : }
     122             : 
     123      249096 : static int attr_vec_add_unique(TALLOC_CTX *mem_ctx,
     124             :                                struct ldb_attr_vec *vec,
     125             :                                const char *attr)
     126             : {
     127      249096 :         const char **exact = NULL;
     128      249096 :         const char **next = NULL;
     129      249096 :         size_t next_idx = 0;
     130             : 
     131      314639 :         BINARY_ARRAY_SEARCH_GTE(vec->attrs,
     132             :                                 vec->len,
     133             :                                 attr,
     134             :                                 acl_attr_cmp_fn,
     135             :                                 exact,
     136             :                                 next);
     137      249096 :         if (exact != NULL) {
     138       68642 :                 return LDB_SUCCESS;
     139             :         }
     140             : 
     141      180454 :         if (vec->len == SIZE_MAX) {
     142           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     143             :         }
     144             : 
     145      180454 :         if (next != NULL) {
     146       48015 :                 next_idx = next - vec->attrs;
     147             :         }
     148             : 
     149      180454 :         if (vec->len >= vec->capacity) {
     150      129954 :                 const char **attrs = NULL;
     151             : 
     152      129954 :                 if (vec->capacity == 0) {
     153      118894 :                         vec->capacity = 4;
     154             :                 } else {
     155       11060 :                         if (vec->capacity > SIZE_MAX / 2) {
     156           0 :                                 return LDB_ERR_OPERATIONS_ERROR;
     157             :                         }
     158       11060 :                         vec->capacity *= 2;
     159             :                 }
     160             : 
     161      129954 :                 attrs = talloc_realloc(mem_ctx, vec->attrs, const char *, vec->capacity);
     162      129954 :                 if (attrs == NULL) {
     163           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     164             :                 }
     165             : 
     166      129954 :                 vec->attrs = attrs;
     167             :         }
     168      180454 :         SMB_ASSERT(vec->len < vec->capacity);
     169             : 
     170      180454 :         if (next == NULL) {
     171      132439 :                 vec->attrs[vec->len++] = attr;
     172             :         } else {
     173       48015 :                 size_t count = (vec->len - next_idx) * sizeof (vec->attrs[0]);
     174       48015 :                 memmove(&vec->attrs[next_idx + 1],
     175       48015 :                         &vec->attrs[next_idx],
     176             :                         count);
     177             : 
     178       48015 :                 vec->attrs[next_idx] = attr;
     179       48015 :                 ++vec->len;
     180             :         }
     181             : 
     182      180454 :         return LDB_SUCCESS;
     183             : }
     184             : 
     185     1042978 : static bool ldb_attr_always_present(const char *attr)
     186             : {
     187             :         static const char * const attrs_always_present[] = {
     188             :                 "objectClass",
     189             :                 "distinguishedName",
     190             :                 "name",
     191             :                 "objectGUID",
     192             :                 NULL
     193             :         };
     194             : 
     195     1042978 :         return ldb_attr_in_list(attrs_always_present, attr);
     196             : }
     197             : 
     198      809662 : static bool ldb_attr_always_visible(const char *attr)
     199             : {
     200             :         static const char * const attrs_always_visible[] = {
     201             :                 "isDeleted",
     202             :                 "isRecycled",
     203             :                 NULL
     204             :         };
     205             : 
     206      809662 :         return ldb_attr_in_list(attrs_always_visible, attr);
     207             : }
     208             : 
     209             : /* Collect a list of attributes required to match a given parse tree. */
     210     3686632 : static int ldb_parse_tree_collect_acl_attrs(struct ldb_module *module,
     211             :                                             TALLOC_CTX *mem_ctx,
     212             :                                             struct ldb_attr_vec *attrs,
     213             :                                             const struct ldb_parse_tree *tree)
     214             : {
     215     3686632 :         const char *attr = NULL;
     216             :         unsigned int i;
     217             :         int ret;
     218             : 
     219     3686632 :         if (tree == NULL) {
     220           0 :                 return 0;
     221             :         }
     222             : 
     223     3686632 :         switch (tree->operation) {
     224     1186471 :         case LDB_OP_OR:
     225             :         case LDB_OP_AND:                /* attributes stored in list of subtrees */
     226     3615968 :                 for (i = 0; i < tree->u.list.num_elements; i++) {
     227     2429497 :                         ret = ldb_parse_tree_collect_acl_attrs(module, mem_ctx,
     228     2429497 :                                                                attrs, tree->u.list.elements[i]);
     229     2429497 :                         if (ret) {
     230           0 :                                 return ret;
     231             :                         }
     232             :                 }
     233     1186471 :                 return 0;
     234             : 
     235      621022 :         case LDB_OP_NOT:                /* attributes stored in single subtree */
     236      621022 :                 return ldb_parse_tree_collect_acl_attrs(module, mem_ctx, attrs, tree->u.isnot.child);
     237             : 
     238     1042978 :         case LDB_OP_PRESENT:
     239             :                 /*
     240             :                  * If the search filter is checking for an attribute's presence,
     241             :                  * and the attribute is always present, we can skip access
     242             :                  * rights checks. Every object has these attributes, and so
     243             :                  * there's no security reason to hide their presence.
     244             :                  * Note: the acl.py tests (e.g. test_search1()) rely on this
     245             :                  * exception.  I.e. even if we lack Read Property (RP) rights
     246             :                  * for a child object, it should still appear as a visible
     247             :                  * object in 'objectClass=*' searches, so long as we have List
     248             :                  * Contents (LC) rights for the object.
     249             :                  */
     250     1042978 :                 if (ldb_attr_always_present(tree->u.present.attr)) {
     251             :                         /* No need to check this attribute. */
     252     1032717 :                         return 0;
     253             :                 }
     254             : 
     255             :                 FALL_THROUGH;
     256             :         case LDB_OP_EQUALITY:
     257      809662 :                 if (ldb_attr_always_visible(tree->u.present.attr)) {
     258             :                         /* No need to check this attribute. */
     259      597326 :                         return 0;
     260             :                 }
     261             : 
     262             :                 FALL_THROUGH;
     263             :         default:                        /* single attribute in tree */
     264      249096 :                 attr = ldb_parse_tree_get_attr(tree);
     265      249096 :                 return attr_vec_add_unique(mem_ctx, attrs, attr);
     266             :         }
     267             : }
     268             : 
     269             : /*
     270             :  * the object has a parent, so we have to check for visibility
     271             :  *
     272             :  * This helper function uses a per-search cache to avoid checking the
     273             :  * parent object for each of many possible children.  This is likely
     274             :  * to help on SCOPE_ONE searches and on typical tree structures for
     275             :  * SCOPE_SUBTREE, where an OU has many users as children.
     276             :  *
     277             :  * We rely for safety on the DB being locked for reads during the full
     278             :  * search.
     279             :  */
     280     1910344 : static int aclread_check_parent(struct aclread_context *ac,
     281             :                                 struct ldb_message *msg,
     282             :                                 struct ldb_request *req)
     283             : {
     284             :         int ret;
     285     1910344 :         struct ldb_dn *parent_dn = NULL;
     286             : 
     287             :         /* We may have a cached result from earlier in this search */
     288     1910344 :         if (ac->last_parent_dn != NULL) {
     289             :                 /*
     290             :                  * We try the no-allocation ldb_dn_compare_base()
     291             :                  * first however it will not tell parents and
     292             :                  * grand-parents apart
     293             :                  */
     294     1282728 :                 int cmp_base = ldb_dn_compare_base(ac->last_parent_dn,
     295             :                                                    msg->dn);
     296     1282728 :                 if (cmp_base == 0) {
     297             :                         /* Now check if it is a direct parent */
     298     1203009 :                         parent_dn = ldb_dn_get_parent(ac, msg->dn);
     299     1203009 :                         if (parent_dn == NULL) {
     300           0 :                                 return ldb_oom(ldb_module_get_ctx(ac->module));
     301             :                         }
     302     1203009 :                         if (ldb_dn_compare(ac->last_parent_dn,
     303             :                                            parent_dn) == 0) {
     304     1136139 :                                 TALLOC_FREE(parent_dn);
     305             : 
     306             :                                 /*
     307             :                                  * If we checked the same parent last
     308             :                                  * time, then return the cached
     309             :                                  * result.
     310             :                                  *
     311             :                                  * The cache is valid as long as the
     312             :                                  * search as the DB is read locked and
     313             :                                  * the session_info (connected user)
     314             :                                  * is constant.
     315             :                                  */
     316     1136139 :                                 return ac->last_parent_check_ret;
     317             :                         }
     318             :                 }
     319             :         }
     320             : 
     321             :         {
     322      774205 :                 TALLOC_CTX *frame = NULL;
     323      774205 :                 frame = talloc_stackframe();
     324             : 
     325             :                 /*
     326             :                  * This may have been set in the block above, don't
     327             :                  * re-parse
     328             :                  */
     329      774205 :                 if (parent_dn == NULL) {
     330      707335 :                         parent_dn = ldb_dn_get_parent(ac, msg->dn);
     331      707335 :                         if (parent_dn == NULL) {
     332           0 :                                 TALLOC_FREE(frame);
     333           0 :                                 return ldb_oom(ldb_module_get_ctx(ac->module));
     334             :                         }
     335             :                 }
     336      774205 :                 ret = dsdb_module_check_access_on_dn(ac->module,
     337             :                                                      frame,
     338             :                                                      parent_dn,
     339             :                                                      SEC_ADS_LIST,
     340             :                                                      NULL, req);
     341      774205 :                 talloc_unlink(ac, ac->last_parent_dn);
     342      774205 :                 ac->last_parent_dn = parent_dn;
     343      774205 :                 ac->last_parent_check_ret = ret;
     344             : 
     345      774205 :                 TALLOC_FREE(frame);
     346             :         }
     347      774205 :         return ret;
     348             : }
     349             : 
     350     1979066 : static int aclread_check_object_visible(struct aclread_context *ac,
     351             :                                         struct ldb_message *msg,
     352             :                                         struct ldb_request *req)
     353             : {
     354             :         uint32_t instanceType;
     355             :         int ret;
     356             : 
     357             :         /* get the object instance type */
     358     1979066 :         instanceType = ldb_msg_find_attr_as_uint(msg,
     359             :                                                  "instanceType", 0);
     360     1979066 :         if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
     361             :                 /*
     362             :                  * NC_HEAD objects are always visible
     363             :                  */
     364       68722 :                 return LDB_SUCCESS;
     365             :         }
     366             : 
     367     1910344 :         ret = aclread_check_parent(ac, msg, req);
     368     1910344 :         if (ret == LDB_SUCCESS) {
     369             :                 /*
     370             :                  * SEC_ADS_LIST (List Children) alone
     371             :                  * on the parent is enough to make the
     372             :                  * object visible.
     373             :                  */
     374     1869307 :                 return LDB_SUCCESS;
     375             :         }
     376       41037 :         if (ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
     377           0 :                 return ret;
     378             :         }
     379             : 
     380       41037 :         if (!ac->do_list_object_initialized) {
     381             :                 /*
     382             :                  * We only call dsdb_do_list_object() once
     383             :                  * and only when needed in order to
     384             :                  * check the dSHeuristics for fDoListObject.
     385             :                  */
     386       16280 :                 ac->do_list_object = dsdb_do_list_object(ac->module, ac, req);
     387       16280 :                 ac->do_list_object_initialized = true;
     388             :         }
     389             : 
     390       41037 :         if (ac->do_list_object) {
     391       12672 :                 TALLOC_CTX *frame = talloc_stackframe();
     392       12672 :                 struct ldb_dn *parent_dn = NULL;
     393             : 
     394             :                 /*
     395             :                  * Here we're in "List Object" mode (fDoListObject=true).
     396             :                  *
     397             :                  * If SEC_ADS_LIST (List Children) is not
     398             :                  * granted on the parent, we need to check if
     399             :                  * SEC_ADS_LIST_OBJECT (List Object) is granted
     400             :                  * on the parent and also on the object itself.
     401             :                  *
     402             :                  * We could optimize this similar to aclread_check_parent(),
     403             :                  * but that would require quite a bit of restructuring,
     404             :                  * so that we cache the granted access bits instead
     405             :                  * of just the result for 'SEC_ADS_LIST (List Children)'.
     406             :                  *
     407             :                  * But as this is the uncommon case and
     408             :                  * 'SEC_ADS_LIST (List Children)' is most likely granted
     409             :                  * on most of the objects, we'll just implement what
     410             :                  * we have to.
     411             :                  */
     412             : 
     413       12672 :                 parent_dn = ldb_dn_get_parent(frame, msg->dn);
     414       12672 :                 if (parent_dn == NULL) {
     415           0 :                         TALLOC_FREE(frame);
     416           0 :                         return ldb_oom(ldb_module_get_ctx(ac->module));
     417             :                 }
     418       12672 :                 ret = dsdb_module_check_access_on_dn(ac->module,
     419             :                                                      frame,
     420             :                                                      parent_dn,
     421             :                                                      SEC_ADS_LIST_OBJECT,
     422             :                                                      NULL, req);
     423       12672 :                 if (ret != LDB_SUCCESS) {
     424        6048 :                         TALLOC_FREE(frame);
     425        6048 :                         return ret;
     426             :                 }
     427        6624 :                 ret = dsdb_module_check_access_on_dn(ac->module,
     428             :                                                      frame,
     429             :                                                      msg->dn,
     430             :                                                      SEC_ADS_LIST_OBJECT,
     431             :                                                      NULL, req);
     432        6624 :                 if (ret != LDB_SUCCESS) {
     433        1872 :                         TALLOC_FREE(frame);
     434        1872 :                         return ret;
     435             :                 }
     436             : 
     437        4752 :                 TALLOC_FREE(frame);
     438        4752 :                 return LDB_SUCCESS;
     439             :         }
     440             : 
     441       28365 :         return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
     442             : }
     443             : 
     444             : /*
     445             :  * The sd returned from this function is valid until the next call on
     446             :  * this module context
     447             :  *
     448             :  * This helper function uses a cache on the module private data to
     449             :  * speed up repeated use of the same SD.
     450             :  */
     451             : 
     452     3405698 : static int aclread_get_sd_from_ldb_message(struct aclread_context *ac,
     453             :                                            const struct ldb_message *acl_res,
     454             :                                            struct security_descriptor **sd)
     455             : {
     456             :         struct ldb_message_element *sd_element;
     457     3405698 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     458     3309396 :         struct aclread_private *private_data
     459     3405698 :                 = talloc_get_type_abort(ldb_module_get_private(ac->module),
     460             :                                   struct aclread_private);
     461             :         enum ndr_err_code ndr_err;
     462             : 
     463     3405698 :         sd_element = ldb_msg_find_element(acl_res, "nTSecurityDescriptor");
     464     3405698 :         if (sd_element == NULL) {
     465           0 :                 return ldb_error(ldb, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS,
     466             :                                  "nTSecurityDescriptor is missing");
     467             :         }
     468             : 
     469     3405698 :         if (sd_element->num_values != 1) {
     470           0 :                 return ldb_operr(ldb);
     471             :         }
     472             : 
     473             :         /*
     474             :          * The time spent in ndr_pull_security_descriptor() is quite
     475             :          * expensive, so we check if this is the same binary blob as last
     476             :          * time, and if so return the memory tree from that previous parse.
     477             :          */
     478             : 
     479     6703411 :         if (private_data->sd_cached != NULL &&
     480     6780410 :             private_data->sd_cached_blob.data != NULL &&
     481     3390205 :             ldb_val_equal_exact(&sd_element->values[0],
     482     3390205 :                                 &private_data->sd_cached_blob)) {
     483     2323127 :                 *sd = private_data->sd_cached;
     484     2323127 :                 return LDB_SUCCESS;
     485             :         }
     486             : 
     487     1082571 :         *sd = talloc(private_data, struct security_descriptor);
     488     1082571 :         if(!*sd) {
     489           0 :                 return ldb_oom(ldb);
     490             :         }
     491     1082571 :         ndr_err = ndr_pull_struct_blob(&sd_element->values[0], *sd, *sd,
     492             :                              (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
     493             : 
     494     1082571 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     495           0 :                 TALLOC_FREE(*sd);
     496           0 :                 return ldb_operr(ldb);
     497             :         }
     498             : 
     499     1082571 :         talloc_unlink(private_data, private_data->sd_cached_blob.data);
     500     1082571 :         private_data->sd_cached_blob = ldb_val_dup(private_data,
     501     1082571 :                                                    &sd_element->values[0]);
     502     1082571 :         if (private_data->sd_cached_blob.data == NULL) {
     503           0 :                 TALLOC_FREE(*sd);
     504           0 :                 return ldb_operr(ldb);
     505             :         }
     506             : 
     507     1082571 :         talloc_unlink(private_data, private_data->sd_cached);
     508     1082571 :         private_data->sd_cached = *sd;
     509             : 
     510     1082571 :         return LDB_SUCCESS;
     511             : }
     512             : 
     513             : /* Check whether the attribute is a password attribute. */
     514     6114350 : static bool attr_is_secret(const char *attr, const struct aclread_private *private_data)
     515             : {
     516     6114350 :         const char **found = NULL;
     517             : 
     518     6114350 :         if (private_data->password_attrs == NULL) {
     519           0 :                 return false;
     520             :         }
     521             : 
     522     7601828 :         BINARY_ARRAY_SEARCH_V(private_data->password_attrs,
     523             :                               private_data->num_password_attrs,
     524             :                               attr,
     525             :                               ldb_attr_cmp,
     526             :                               found);
     527     6114350 :         return found != NULL;
     528             : }
     529             : 
     530             : /*
     531             :  * Returns the access mask required to read a given attribute
     532             :  */
     533     6114057 : static uint32_t get_attr_access_mask(const struct dsdb_attribute *attr,
     534             :                                      uint32_t sd_flags)
     535             : {
     536             : 
     537     6114057 :         uint32_t access_mask = 0;
     538             :         bool is_sd;
     539             : 
     540             :         /* nTSecurityDescriptor is a special case */
     541     6114057 :         is_sd = (ldb_attr_cmp("nTSecurityDescriptor",
     542             :                               attr->lDAPDisplayName) == 0);
     543             : 
     544     6114057 :         if (is_sd) {
     545       20653 :                 if (sd_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
     546       20552 :                         access_mask |= SEC_STD_READ_CONTROL;
     547             :                 }
     548       20653 :                 if (sd_flags & SECINFO_DACL) {
     549       20626 :                         access_mask |= SEC_STD_READ_CONTROL;
     550             :                 }
     551       20653 :                 if (sd_flags & SECINFO_SACL) {
     552       20385 :                         access_mask |= SEC_FLAG_SYSTEM_SECURITY;
     553             :                 }
     554             :         } else {
     555     6093404 :                 access_mask = SEC_ADS_READ_PROP;
     556             :         }
     557             : 
     558     6114057 :         if (attr->searchFlags & SEARCH_FLAG_CONFIDENTIAL) {
     559        5112 :                 access_mask |= SEC_ADS_CONTROL_ACCESS;
     560             :         }
     561             : 
     562     6114057 :         return access_mask;
     563             : }
     564             : 
     565             : /*
     566             :  * Checks that the user has sufficient access rights to view an attribute, else
     567             :  * marks it as inaccessible.
     568             :  */
     569     6114350 : static int acl_redact_attr(TALLOC_CTX *mem_ctx,
     570             :                            struct ldb_message_element *el,
     571             :                            struct aclread_context *ac,
     572             :                            const struct aclread_private *private_data,
     573             :                            const struct ldb_message *msg,
     574             :                            const struct dsdb_schema *schema,
     575             :                            const struct security_descriptor *sd,
     576             :                            const struct dom_sid *sid,
     577             :                            const struct dsdb_class *objectclass)
     578             : {
     579             :         int ret;
     580     6114350 :         const struct dsdb_attribute *attr = NULL;
     581             :         uint32_t access_mask;
     582     6114350 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     583             : 
     584     6114350 :         if (attr_is_secret(el->name, private_data)) {
     585         293 :                 ldb_msg_element_mark_inaccessible(el);
     586         293 :                 return LDB_SUCCESS;
     587             :         }
     588             : 
     589             :         /* Look up the attribute in the schema. */
     590     6114057 :         attr = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
     591     6114057 :         if (!attr) {
     592           0 :                 ldb_debug_set(ldb,
     593             :                               LDB_DEBUG_FATAL,
     594             :                               "acl_read: %s cannot find attr[%s] in schema\n",
     595           0 :                               ldb_dn_get_linearized(msg->dn), el->name);
     596           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     597             :         }
     598             : 
     599     6114057 :         access_mask = get_attr_access_mask(attr, ac->sd_flags);
     600     6114057 :         if (access_mask == 0) {
     601           0 :                 DBG_ERR("Could not determine access mask for attribute %s\n",
     602             :                         el->name);
     603           0 :                 ldb_msg_element_mark_inaccessible(el);
     604           0 :                 return LDB_SUCCESS;
     605             :         }
     606             : 
     607             :         /* We must check whether the user has rights to view the attribute. */
     608             : 
     609     6114057 :         ret = acl_check_access_on_attribute(ac->module, mem_ctx, sd, sid,
     610             :                                             access_mask, attr, objectclass);
     611             : 
     612     6114057 :         if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
     613       29750 :                 ldb_msg_element_mark_inaccessible(el);
     614     6084307 :         } else if (ret != LDB_SUCCESS) {
     615           0 :                 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
     616             :                               "acl_read: %s check attr[%s] gives %s - %s\n",
     617           0 :                               ldb_dn_get_linearized(msg->dn), el->name,
     618             :                               ldb_strerror(ret), ldb_errstring(ldb));
     619           0 :                 return ret;
     620             :         }
     621             : 
     622     6114057 :         return LDB_SUCCESS;
     623             : }
     624             : 
     625     3405698 : static int setup_access_check_context(struct aclread_context *ac,
     626             :                                       const struct ldb_message *msg,
     627             :                                       struct access_check_context *ctx)
     628             : {
     629             :         int ret;
     630             : 
     631             :         /*
     632             :          * Fetch the schema so we can check which attributes are
     633             :          * considered confidential.
     634             :          */
     635     3405698 :         if (ac->schema == NULL) {
     636      494318 :                 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     637             : 
     638             :                 /* Cache the schema for later use. */
     639      494318 :                 ac->schema = dsdb_get_schema(ldb, ac);
     640             : 
     641      494318 :                 if (ac->schema == NULL) {
     642           0 :                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
     643             :                                          "aclread_callback: Error obtaining schema.");
     644             :                 }
     645             :         }
     646             : 
     647             :         /* Fetch the object's security descriptor. */
     648     3405698 :         ret = aclread_get_sd_from_ldb_message(ac, msg, &ctx->sd);
     649     3405698 :         if (ret != LDB_SUCCESS) {
     650           0 :                 ldb_debug_set(ldb_module_get_ctx(ac->module), LDB_DEBUG_FATAL,
     651             :                               "acl_read: cannot get descriptor of %s: %s\n",
     652           0 :                               ldb_dn_get_linearized(msg->dn), ldb_strerror(ret));
     653           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     654     3405698 :         } else if (ctx->sd == NULL) {
     655           0 :                 ldb_debug_set(ldb_module_get_ctx(ac->module), LDB_DEBUG_FATAL,
     656             :                               "acl_read: cannot get descriptor of %s (attribute not found)\n",
     657           0 :                               ldb_dn_get_linearized(msg->dn));
     658           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     659             :         }
     660             :         /*
     661             :          * Get the most specific structural object class for the ACL check
     662             :          */
     663     3405698 :         ctx->objectclass = dsdb_get_structural_oc_from_msg(ac->schema, msg);
     664     3405698 :         if (ctx->objectclass == NULL) {
     665           0 :                 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
     666             :                                        "acl_read: Failed to find a structural class for %s",
     667           0 :                                        ldb_dn_get_linearized(msg->dn));
     668           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     669             :         }
     670             : 
     671             :         /* Fetch the object's SID. */
     672     3405698 :         ret = samdb_result_dom_sid_buf(msg, "objectSid", &ctx->sid_buf);
     673     3405698 :         if (ret == LDB_SUCCESS) {
     674     2920243 :                 ctx->sid = &ctx->sid_buf;
     675      485455 :         } else if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
     676             :                 /* This is expected. */
     677      485455 :                 ctx->sid = NULL;
     678             :         } else {
     679           0 :                 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
     680             :                                        "acl_read: Failed to parse objectSid as dom_sid for %s",
     681           0 :                                        ldb_dn_get_linearized(msg->dn));
     682           0 :                 return ret;
     683             :         }
     684             : 
     685     3405698 :         return LDB_SUCCESS;
     686             : }
     687             : 
     688             : /*
     689             :  * Whether this attribute was added to perform access checks and must be
     690             :  * removed.
     691             :  */
     692     9022342 : static bool should_remove_attr(const char *attr, const struct aclread_context *ac)
     693             : {
     694    17152402 :         if (ac->added_nTSecurityDescriptor &&
     695     8754178 :             ldb_attr_cmp("nTSecurityDescriptor", attr) == 0)
     696             :         {
     697     1285938 :                 return true;
     698             :         }
     699             : 
     700    13040402 :         if (ac->added_objectSid &&
     701     5612373 :             ldb_attr_cmp("objectSid", attr) == 0)
     702             :         {
     703      825296 :                 return true;
     704             :         }
     705             : 
     706    11601557 :         if (ac->added_instanceType &&
     707     4982299 :             ldb_attr_cmp("instanceType", attr) == 0)
     708             :         {
     709     1215293 :                 return true;
     710             :         }
     711             : 
     712     9254191 :         if (ac->added_objectClass &&
     713     3765008 :             ldb_attr_cmp("objectClass", attr) == 0)
     714             :         {
     715     1214900 :                 return true;
     716             :         }
     717             : 
     718     4480915 :         return false;
     719             : }
     720             : 
     721     2067053 : static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares)
     722             : {
     723             :         struct aclread_context *ac;
     724     2067053 :         struct aclread_private *private_data = NULL;
     725             :         struct ldb_message *msg;
     726             :         int ret;
     727             :         unsigned int i;
     728             :         struct access_check_context acl_ctx;
     729             : 
     730     2067053 :         ac = talloc_get_type_abort(req->context, struct aclread_context);
     731     2067053 :         if (!ares) {
     732           0 :                 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR );
     733             :         }
     734     2067053 :         if (ares->error != LDB_SUCCESS) {
     735           8 :                 return ldb_module_done(ac->req, ares->controls,
     736             :                                        ares->response, ares->error);
     737             :         }
     738     2067045 :         switch (ares->type) {
     739     1334227 :         case LDB_REPLY_ENTRY:
     740     1334227 :                 msg = ares->message;
     741             : 
     742     1334227 :                 if (!ldb_dn_is_null(msg->dn)) {
     743             :                         /*
     744             :                          * this is a real object, so we have
     745             :                          * to check for visibility
     746             :                          */
     747     1334227 :                         ret = aclread_check_object_visible(ac, msg, req);
     748     1334227 :                         if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
     749       27636 :                                 return LDB_SUCCESS;
     750     1306591 :                         } else if (ret != LDB_SUCCESS) {
     751           0 :                                 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     752           0 :                                 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
     753             :                                               "acl_read: %s check parent %s - %s\n",
     754             :                                               ldb_dn_get_linearized(msg->dn),
     755             :                                               ldb_strerror(ret),
     756             :                                               ldb_errstring(ldb));
     757           0 :                                 return ldb_module_done(ac->req, NULL, NULL, ret);
     758             :                         }
     759             :                 }
     760             : 
     761             :                 /* for every element in the message check RP */
     762     4706257 :                 for (i = 0; i < msg->num_elements; ++i) {
     763     4467321 :                         struct ldb_message_element *el = &msg->elements[i];
     764             : 
     765             :                         /* Remove attributes added to perform access checks. */
     766     4467321 :                         if (should_remove_attr(el->name, ac)) {
     767     3361153 :                                 ldb_msg_element_mark_inaccessible(el);
     768     3361153 :                                 continue;
     769             :                         }
     770             : 
     771     1106168 :                         if (acl_element_is_access_checked(el)) {
     772             :                                 /* We will have already checked this attribute. */
     773       38513 :                                 continue;
     774             :                         }
     775             : 
     776             :                         /*
     777             :                          * We need to fetch the security descriptor to check
     778             :                          * this attribute.
     779             :                          */
     780     1067655 :                         break;
     781             :                 }
     782             : 
     783     1306591 :                 if (i == msg->num_elements) {
     784             :                         /* All elements have been checked. */
     785      238936 :                         goto reply_entry_done;
     786             :                 }
     787             : 
     788     1067655 :                 ret = setup_access_check_context(ac, msg, &acl_ctx);
     789     1067655 :                 if (ret != LDB_SUCCESS) {
     790           0 :                         return ret;
     791             :                 }
     792             : 
     793     1067655 :                 private_data = talloc_get_type_abort(ldb_module_get_private(ac->module),
     794             :                                                      struct aclread_private);
     795             : 
     796     4414054 :                 for (/* begin where we left off */; i < msg->num_elements; ++i) {
     797     4555021 :                         struct ldb_message_element *el = &msg->elements[i];
     798             : 
     799             :                         /* Remove attributes added to perform access checks. */
     800     4555021 :                         if (should_remove_attr(el->name, ac)) {
     801     1180274 :                                 ldb_msg_element_mark_inaccessible(el);
     802     1180274 :                                 continue;
     803             :                         }
     804             : 
     805     3374747 :                         if (acl_element_is_access_checked(el)) {
     806             :                                 /* We will have already checked this attribute. */
     807       92896 :                                 continue;
     808             :                         }
     809             : 
     810             :                         /*
     811             :                          * We need to check whether the attribute is secret,
     812             :                          * confidential, or access-controlled.
     813             :                          */
     814     6293439 :                         ret = acl_redact_attr(ac,
     815             :                                               el,
     816             :                                               ac,
     817             :                                               private_data,
     818             :                                               msg,
     819             :                                               ac->schema,
     820     3281851 :                                               acl_ctx.sd,
     821             :                                               acl_ctx.sid,
     822             :                                               acl_ctx.objectclass);
     823     3281851 :                         if (ret != LDB_SUCCESS) {
     824           0 :                                 return ldb_module_done(ac->req, NULL, NULL, ret);
     825             :                         }
     826             :                 }
     827             : 
     828     1067655 :         reply_entry_done:
     829     1306591 :                 ldb_msg_remove_inaccessible(msg);
     830             : 
     831     1306591 :                 ac->num_entries++;
     832     1306591 :                 return ldb_module_send_entry(ac->req, msg, ares->controls);
     833       90639 :         case LDB_REPLY_REFERRAL:
     834       90639 :                 return ldb_module_send_referral(ac->req, ares->referral);
     835      642179 :         case LDB_REPLY_DONE:
     836      642179 :                 if (ac->base_invisible && ac->num_entries == 0) {
     837             :                         /*
     838             :                          * If the base is invisible and we didn't
     839             :                          * returned any object, we need to return
     840             :                          * NO_SUCH_OBJECT.
     841             :                          */
     842        3258 :                         return ldb_module_done(ac->req,
     843             :                                                NULL, NULL,
     844             :                                                LDB_ERR_NO_SUCH_OBJECT);
     845             :                 }
     846      638921 :                 return ldb_module_done(ac->req, ares->controls,
     847             :                                         ares->response, LDB_SUCCESS);
     848             : 
     849             :         }
     850           0 :         return LDB_SUCCESS;
     851             : }
     852             : 
     853             : 
     854    20590916 : static int aclread_search(struct ldb_module *module, struct ldb_request *req)
     855             : {
     856             :         struct ldb_context *ldb;
     857             :         int ret;
     858             :         struct aclread_context *ac;
     859             :         struct ldb_request *down_req;
     860             :         bool am_system;
     861             :         struct ldb_result *res;
     862             :         struct aclread_private *p;
     863    20590916 :         bool need_sd = false;
     864    20590916 :         bool explicit_sd_flags = false;
     865    20590916 :         bool is_untrusted = ldb_req_is_untrusted(req);
     866             :         static const char * const _all_attrs[] = { "*", NULL };
     867    20590916 :         bool all_attrs = false;
     868    20590916 :         const char * const *attrs = NULL;
     869             :         static const char *acl_attrs[] = {
     870             :                 "instanceType",
     871             :                 NULL
     872             :         };
     873             : 
     874    20590916 :         ldb = ldb_module_get_ctx(module);
     875    20590916 :         p = talloc_get_type(ldb_module_get_private(module), struct aclread_private);
     876             : 
     877    20590916 :         am_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID) != NULL;
     878    20590916 :         if (!am_system) {
     879    10731186 :                 am_system = dsdb_module_am_system(module);
     880             :         }
     881             : 
     882             :         /* skip access checks if we are system or system control is supplied
     883             :          * or this is not LDAP server request */
     884    20590916 :         if (!p || !p->enabled ||
     885     3281336 :             am_system ||
     886     3281336 :             !is_untrusted) {
     887    19899368 :                 return ldb_next_request(module, req);
     888             :         }
     889             :         /* no checks on special dn */
     890      691548 :         if (ldb_dn_is_special(req->op.search.base)) {
     891       46231 :                 return ldb_next_request(module, req);
     892             :         }
     893             : 
     894      645317 :         ac = talloc_zero(req, struct aclread_context);
     895      645317 :         if (ac == NULL) {
     896           0 :                 return ldb_oom(ldb);
     897             :         }
     898      645317 :         ac->module = module;
     899      645317 :         ac->req = req;
     900             : 
     901      645317 :         attrs = req->op.search.attrs;
     902      645317 :         if (attrs == NULL) {
     903       54413 :                 all_attrs = true;
     904       54413 :                 attrs = _all_attrs;
     905      590904 :         } else if (ldb_attr_in_list(attrs, "*")) {
     906       16874 :                 all_attrs = true;
     907             :         }
     908             : 
     909             :         /*
     910             :          * In theory we should also check for the SD control but control verification is
     911             :          * expensive so we'd better had the ntsecuritydescriptor to the list of
     912             :          * searched attribute and then remove it !
     913             :          */
     914      645317 :         ac->sd_flags = dsdb_request_sd_flags(ac->req, &explicit_sd_flags);
     915             : 
     916      645317 :         if (ldb_attr_in_list(attrs, "nTSecurityDescriptor")) {
     917       18158 :                 need_sd = false;
     918      627159 :         } else if (explicit_sd_flags && all_attrs) {
     919         306 :                 need_sd = false;
     920             :         } else {
     921      626853 :                 need_sd = true;
     922             :         }
     923             : 
     924      645317 :         if (!all_attrs) {
     925      574030 :                 if (!ldb_attr_in_list(attrs, "instanceType")) {
     926      562737 :                         attrs = ldb_attr_list_copy_add(ac, attrs, "instanceType");
     927      562737 :                         if (attrs == NULL) {
     928           0 :                                 return ldb_oom(ldb);
     929             :                         }
     930      562737 :                         ac->added_instanceType = true;
     931             :                 }
     932      574030 :                 if (!ldb_attr_in_list(req->op.search.attrs, "objectSid")) {
     933      558982 :                         attrs = ldb_attr_list_copy_add(ac, attrs, "objectSid");
     934      558982 :                         if (attrs == NULL) {
     935           0 :                                 return ldb_oom(ldb);
     936             :                         }
     937      558982 :                         ac->added_objectSid = true;
     938             :                 }
     939      574030 :                 if (!ldb_attr_in_list(req->op.search.attrs, "objectClass")) {
     940      562656 :                         attrs = ldb_attr_list_copy_add(ac, attrs, "objectClass");
     941      562656 :                         if (attrs == NULL) {
     942           0 :                                 return ldb_oom(ldb);
     943             :                         }
     944      562656 :                         ac->added_objectClass = true;
     945             :                 }
     946             :         }
     947             : 
     948      645317 :         if (need_sd) {
     949      626853 :                 attrs = ldb_attr_list_copy_add(ac, attrs, "nTSecurityDescriptor");
     950      626853 :                 if (attrs == NULL) {
     951           0 :                         return ldb_oom(ldb);
     952             :                 }
     953      626853 :                 ac->added_nTSecurityDescriptor = true;
     954             :         }
     955             : 
     956      645317 :         ac->am_administrator = dsdb_module_am_administrator(module);
     957             : 
     958             :         /* check accessibility of base */
     959      645317 :         if (!ldb_dn_is_null(req->op.search.base)) {
     960      645070 :                 ret = dsdb_module_search_dn(module, req, &res, req->op.search.base,
     961             :                                             acl_attrs,
     962             :                                             DSDB_FLAG_NEXT_MODULE |
     963             :                                             DSDB_FLAG_AS_SYSTEM |
     964             :                                             DSDB_SEARCH_SHOW_RECYCLED,
     965             :                                             req);
     966      645070 :                 if (ret != LDB_SUCCESS) {
     967         231 :                         return ldb_error(ldb, ret,
     968             :                                         "acl_read: Error retrieving instanceType for base.");
     969             :                 }
     970      644839 :                 ret = aclread_check_object_visible(ac, res->msgs[0], req);
     971      644839 :                 if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
     972        8649 :                         if (req->op.search.scope == LDB_SCOPE_BASE) {
     973        2889 :                                 return ldb_module_done(req, NULL, NULL,
     974             :                                                        LDB_ERR_NO_SUCH_OBJECT);
     975             :                         }
     976             :                         /*
     977             :                          * Defer LDB_ERR_NO_SUCH_OBJECT,
     978             :                          * we may return sub objects
     979             :                          */
     980        5760 :                         ac->base_invisible = true;
     981      636190 :                 } else if (ret != LDB_SUCCESS) {
     982           0 :                         return ldb_module_done(req, NULL, NULL, ret);
     983             :                 }
     984             :         }
     985             : 
     986      642197 :         ret = ldb_build_search_req_ex(&down_req,
     987             :                                       ldb, ac,
     988             :                                       req->op.search.base,
     989             :                                       req->op.search.scope,
     990             :                                       req->op.search.tree,
     991             :                                       attrs,
     992             :                                       req->controls,
     993             :                                       ac, aclread_callback,
     994             :                                       req);
     995             : 
     996      642197 :         if (ret != LDB_SUCCESS) {
     997           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     998             :         }
     999             : 
    1000             :         /*
    1001             :          * We provide 'ac' as the control value, which is then used by the
    1002             :          * callback to avoid double-work.
    1003             :          */
    1004      642197 :         ret = ldb_request_add_control(down_req, DSDB_CONTROL_ACL_READ_OID, false, ac);
    1005      642197 :         if (ret != LDB_SUCCESS) {
    1006           0 :                         return ldb_error(ldb, ret,
    1007             :                                         "acl_read: Error adding acl_read control.");
    1008             :         }
    1009             : 
    1010      642197 :         return ldb_next_request(module, down_req);
    1011             : }
    1012             : 
    1013             : /*
    1014             :  * Here we mark inaccessible attributes known to be looked for in the
    1015             :  * filter. This only redacts attributes found in the search expression. If any
    1016             :  * extended attribute match rules examine different attributes without their own
    1017             :  * access control checks, a security bypass is possible.
    1018             :  */
    1019    82897463 : static int acl_redact_msg_for_filter(struct ldb_module *module, struct ldb_request *req, struct ldb_message *msg)
    1020             : {
    1021    82897463 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    1022    82897463 :         const struct aclread_private *private_data = NULL;
    1023    82897463 :         struct ldb_control *control = NULL;
    1024    82897463 :         struct aclread_context *ac = NULL;
    1025             :         struct access_check_context acl_ctx;
    1026             :         int ret;
    1027             :         unsigned i;
    1028             : 
    1029             :         /*
    1030             :          * The private data contains a list of attributes which are to be
    1031             :          * considered secret.
    1032             :          */
    1033    82897463 :         private_data = talloc_get_type(ldb_module_get_private(module), struct aclread_private);
    1034    82897463 :         if (private_data == NULL) {
    1035           0 :                 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
    1036             :                                  "aclread_private data is missing");
    1037             :         }
    1038    82897463 :         if (!private_data->enabled) {
    1039           0 :                 return LDB_SUCCESS;
    1040             :         }
    1041             : 
    1042    82897463 :         control = ldb_request_get_control(req, DSDB_CONTROL_ACL_READ_OID);
    1043    82897463 :         if (control == NULL) {
    1044             :                 /*
    1045             :                  * We've bypassed the acl_read module for this request, and
    1046             :                  * should skip redaction in this case.
    1047             :                  */
    1048    79420169 :                 return LDB_SUCCESS;
    1049             :         }
    1050             : 
    1051     3477294 :         ac = talloc_get_type_abort(control->data, struct aclread_context);
    1052             : 
    1053     3477294 :         if (!ac->got_tree_attrs) {
    1054      636113 :                 ret = ldb_parse_tree_collect_acl_attrs(module, ac, &ac->tree_attrs, req->op.search.tree);
    1055      636113 :                 if (ret != LDB_SUCCESS) {
    1056           0 :                         return ret;
    1057             :                 }
    1058      636113 :                 ac->got_tree_attrs = true;
    1059             :         }
    1060             : 
    1061    50392176 :         for (i = 0; i < msg->num_elements; ++i) {
    1062    49252925 :                 struct ldb_message_element *el = &msg->elements[i];
    1063             : 
    1064             :                 /* Is the attribute mentioned in the search expression? */
    1065    49252925 :                 if (attr_in_vec(&ac->tree_attrs, el->name)) {
    1066             :                         /*
    1067             :                          * We need to fetch the security descriptor to check
    1068             :                          * this element.
    1069             :                          */
    1070     2338043 :                         break;
    1071             :                 }
    1072             : 
    1073             :                 /*
    1074             :                  * This attribute is not in the search filter, so we can leave
    1075             :                  * handling it till aclread_callback(), by which time we know
    1076             :                  * this object is a match. This saves work checking ACLs if the
    1077             :                  * search is unindexed and most objects don't match the filter.
    1078             :                  */
    1079             :         }
    1080             : 
    1081     3477294 :         if (i == msg->num_elements) {
    1082             :                 /* All elements have been checked. */
    1083     1139251 :                 return LDB_SUCCESS;
    1084             :         }
    1085             : 
    1086     2338043 :         ret = setup_access_check_context(ac, msg, &acl_ctx);
    1087     2338043 :         if (ret != LDB_SUCCESS) {
    1088           0 :                 return ret;
    1089             :         }
    1090             : 
    1091             :         /* For every element in the message and the parse tree, check RP. */
    1092             : 
    1093    23702031 :         for (/* begin where we left off */; i < msg->num_elements; ++i) {
    1094    21363988 :                 struct ldb_message_element *el = &msg->elements[i];
    1095             : 
    1096             :                 /* Is the attribute mentioned in the search expression? */
    1097    21363988 :                 if (!attr_in_vec(&ac->tree_attrs, el->name)) {
    1098             :                         /*
    1099             :                          * If not, leave it for later and check the next
    1100             :                          * attribute.
    1101             :                          */
    1102    18531489 :                         continue;
    1103             :                 }
    1104             : 
    1105             :                 /*
    1106             :                  * We need to check whether the attribute is secret,
    1107             :                  * confidential, or access-controlled.
    1108             :                  */
    1109     5603331 :                 ret = acl_redact_attr(ac,
    1110             :                                       el,
    1111             :                                       ac,
    1112             :                                       private_data,
    1113             :                                       msg,
    1114             :                                       ac->schema,
    1115     2832499 :                                       acl_ctx.sd,
    1116             :                                       acl_ctx.sid,
    1117             :                                       acl_ctx.objectclass);
    1118     2832499 :                 if (ret != LDB_SUCCESS) {
    1119           0 :                         return ret;
    1120             :                 }
    1121             : 
    1122     2832499 :                 acl_element_mark_access_checked(el);
    1123             :         }
    1124             : 
    1125     2338043 :         return LDB_SUCCESS;
    1126             : }
    1127             : 
    1128     7236871 : static int ldb_attr_cmp_fn(const void *_a, const void *_b)
    1129             : {
    1130     7236871 :         const char * const *a = _a;
    1131     7236871 :         const char * const *b = _b;
    1132             : 
    1133     7236871 :         return ldb_attr_cmp(*a, *b);
    1134             : }
    1135             : 
    1136      108013 : static int aclread_init(struct ldb_module *module)
    1137             : {
    1138      108013 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    1139             :         unsigned int i, n, j;
    1140      108013 :         TALLOC_CTX *mem_ctx = NULL;
    1141             :         int ret;
    1142             :         bool userPassword_support;
    1143             :         static const char * const attrs[] = { "passwordAttribute", NULL };
    1144             :         static const char * const secret_attrs[] = {
    1145             :                 DSDB_SECRET_ATTRIBUTES
    1146             :         };
    1147             :         struct ldb_result *res;
    1148             :         struct ldb_message *msg;
    1149             :         struct ldb_message_element *password_attributes;
    1150      108013 :         struct aclread_private *p = talloc_zero(module, struct aclread_private);
    1151      108013 :         if (p == NULL) {
    1152           0 :                 return ldb_module_oom(module);
    1153             :         }
    1154      108013 :         p->enabled = lpcfg_parm_bool(ldb_get_opaque(ldb, "loadparm"), NULL, "acl", "search", true);
    1155             : 
    1156      108013 :         ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID);
    1157      108013 :         if (ret != LDB_SUCCESS) {
    1158           0 :                 ldb_debug(ldb, LDB_DEBUG_ERROR,
    1159             :                           "acl_module_init: Unable to register sd_flags control with rootdse!\n");
    1160           0 :                 return ldb_operr(ldb);
    1161             :         }
    1162             : 
    1163      108013 :         ldb_module_set_private(module, p);
    1164             : 
    1165      108013 :         mem_ctx = talloc_new(module);
    1166      108013 :         if (!mem_ctx) {
    1167           0 :                 return ldb_oom(ldb);
    1168             :         }
    1169             : 
    1170      108013 :         ret = dsdb_module_search_dn(module, mem_ctx, &res,
    1171             :                                     ldb_dn_new(mem_ctx, ldb, "@KLUDGEACL"),
    1172             :                                     attrs,
    1173             :                                     DSDB_FLAG_NEXT_MODULE |
    1174             :                                     DSDB_FLAG_AS_SYSTEM,
    1175             :                                     NULL);
    1176      108013 :         if (ret != LDB_SUCCESS) {
    1177           0 :                 goto done;
    1178             :         }
    1179      108013 :         if (res->count == 0) {
    1180           0 :                 goto done;
    1181             :         }
    1182             : 
    1183      108013 :         if (res->count > 1) {
    1184           0 :                 talloc_free(mem_ctx);
    1185           0 :                 return LDB_ERR_CONSTRAINT_VIOLATION;
    1186             :         }
    1187             : 
    1188      108013 :         msg = res->msgs[0];
    1189             : 
    1190      108013 :         password_attributes = ldb_msg_find_element(msg, "passwordAttribute");
    1191      108013 :         if (!password_attributes) {
    1192           0 :                 goto done;
    1193             :         }
    1194      108013 :         p->password_attrs = talloc_array(p, const char *,
    1195             :                         password_attributes->num_values +
    1196             :                         ARRAY_SIZE(secret_attrs));
    1197      108013 :         if (!p->password_attrs) {
    1198           0 :                 talloc_free(mem_ctx);
    1199           0 :                 return ldb_oom(ldb);
    1200             :         }
    1201             : 
    1202      108013 :         n = 0;
    1203     2268223 :         for (i=0; i < password_attributes->num_values; i++) {
    1204     2160210 :                 p->password_attrs[n] = (const char *)password_attributes->values[i].data;
    1205     2160210 :                 talloc_steal(p->password_attrs, password_attributes->values[i].data);
    1206     2160210 :                 n++;
    1207             :         }
    1208             : 
    1209     1620195 :         for (i=0; i < ARRAY_SIZE(secret_attrs); i++) {
    1210     1512182 :                 bool found = false;
    1211             : 
    1212    19766379 :                 for (j=0; j < n; j++) {
    1213    19766329 :                         if (strcasecmp(p->password_attrs[j], secret_attrs[i]) == 0) {
    1214     1512132 :                                 found = true;
    1215     1512132 :                                 break;
    1216             :                         }
    1217             :                 }
    1218             : 
    1219     1512182 :                 if (found) {
    1220     1512132 :                         continue;
    1221             :                 }
    1222             : 
    1223          50 :                 p->password_attrs[n] = talloc_strdup(p->password_attrs,
    1224           0 :                                                      secret_attrs[i]);
    1225          50 :                 if (p->password_attrs[n] == NULL) {
    1226           0 :                         talloc_free(mem_ctx);
    1227           0 :                         return ldb_oom(ldb);
    1228             :                 }
    1229          50 :                 n++;
    1230             :         }
    1231      108013 :         p->num_password_attrs = n;
    1232             : 
    1233             :         /* Sort the password attributes so we can use binary search. */
    1234      108013 :         TYPESAFE_QSORT(p->password_attrs, p->num_password_attrs, ldb_attr_cmp_fn);
    1235             : 
    1236      108013 :         ret = ldb_register_redact_callback(ldb, acl_redact_msg_for_filter, module);
    1237      108013 :         if (ret != LDB_SUCCESS) {
    1238           0 :                 return ret;
    1239             :         }
    1240             : 
    1241      108013 : done:
    1242      108013 :         talloc_free(mem_ctx);
    1243      108013 :         ret = ldb_next_init(module);
    1244             : 
    1245      108013 :         if (ret != LDB_SUCCESS) {
    1246           0 :                 return ret;
    1247             :         }
    1248             : 
    1249      108013 :         if (p->password_attrs != NULL) {
    1250             :                 /*
    1251             :                  * Check this after the modules have be initialised so we can
    1252             :                  * actually read the backend DB.
    1253             :                  */
    1254      108013 :                 userPassword_support = dsdb_user_password_support(module,
    1255             :                                                                   module,
    1256             :                                                                   NULL);
    1257      108013 :                 if (!userPassword_support) {
    1258      101925 :                         const char **found = NULL;
    1259             : 
    1260             :                         /*
    1261             :                          * Remove the userPassword attribute, as it is not
    1262             :                          * considered secret.
    1263             :                          */
    1264      205009 :                         BINARY_ARRAY_SEARCH_V(p->password_attrs,
    1265             :                                               p->num_password_attrs,
    1266             :                                               "userPassword",
    1267             :                                               ldb_attr_cmp,
    1268             :                                               found);
    1269      101925 :                         if (found != NULL) {
    1270      101925 :                                 size_t found_idx = found - p->password_attrs;
    1271             : 
    1272             :                                 /* Shift following elements backwards by one. */
    1273      101925 :                                 for (i = found_idx; i < p->num_password_attrs - 1; ++i) {
    1274           0 :                                         p->password_attrs[i] = p->password_attrs[i + 1];
    1275             :                                 }
    1276      101925 :                                 --p->num_password_attrs;
    1277             :                         }
    1278             :                 }
    1279             :         }
    1280      108013 :         return ret;
    1281             : }
    1282             : 
    1283             : static const struct ldb_module_ops ldb_aclread_module_ops = {
    1284             :         .name              = "aclread",
    1285             :         .search            = aclread_search,
    1286             :         .init_context      = aclread_init
    1287             : };
    1288             : 
    1289        4310 : int ldb_aclread_module_init(const char *version)
    1290             : {
    1291        4310 :         LDB_MODULE_CHECK_VERSION(version);
    1292        4310 :         return ldb_register_module(&ldb_aclread_module_ops);
    1293             : }

Generated by: LCOV version 1.13