LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - paged_results.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 269 323 83.3 %
Date: 2021-09-23 10:06:22 Functions: 15 16 93.8 %

          Line data    Source code
       1             : /*
       2             :    ldb database library
       3             : 
       4             :    Copyright (C) Simo Sorce  2005-2008
       5             :    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
       6             : 
       7             :      ** NOTE! The following LGPL license applies to the ldb
       8             :      ** library. This does NOT imply that all of Samba is released
       9             :      ** under the LGPL
      10             : 
      11             :    This library is free software; you can redistribute it and/or
      12             :    modify it under the terms of the GNU Lesser General Public
      13             :    License as published by the Free Software Foundation; either
      14             :    version 3 of the License, or (at your option) any later version.
      15             : 
      16             :    This library is distributed in the hope that it will be useful,
      17             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      18             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      19             :    Lesser General Public License for more details.
      20             : 
      21             :    You should have received a copy of the GNU Lesser General Public
      22             :    License along with this library; if not, see <http://www.gnu.org/licenses/>.
      23             : */
      24             : 
      25             : /*
      26             :  *  Name: paged_result
      27             :  *
      28             :  *  Component: ldb paged results control module
      29             :  *
      30             :  *  Description: this module caches a complete search and sends back
      31             :  *               results in chunks as asked by the client
      32             :  *
      33             :  *  Author: Garming Sam and Aaron Haslett
      34             :  *
      35             :  *  Note: Based on the original paged_results.c by Simo Sorce and
      36             :  *        vlv_pagination.c by Douglas Bagnall and Garming Sam.
      37             :  */
      38             : 
      39             : #include "includes.h"
      40             : #include "auth/auth.h"
      41             : #include <ldb.h>
      42             : #include "dsdb/samdb/samdb.h"
      43             : #include "libcli/security/security.h"
      44             : #include "libcli/ldap/ldap_errors.h"
      45             : #include "replace.h"
      46             : #include "system/filesys.h"
      47             : #include "system/time.h"
      48             : #include "ldb_module.h"
      49             : #include "dsdb/samdb/samdb.h"
      50             : 
      51             : #include "dsdb/common/util.h"
      52             : #include "lib/util/dlinklist.h"
      53             : 
      54             : /* Referrals are temporarily stored in a linked list */
      55             : struct referral_store {
      56             :         char *ref;
      57             :         struct referral_store *next;
      58             : };
      59             : 
      60             : struct private_data;
      61             : 
      62             : struct results_store {
      63             :         struct results_store *prev, *next;
      64             : 
      65             :         struct private_data *priv;
      66             : 
      67             :         char *cookie;
      68             :         time_t timestamp;
      69             : 
      70             :         struct referral_store *first_ref;
      71             :         struct referral_store *last_ref;
      72             : 
      73             :         struct ldb_control **controls;
      74             : 
      75             :         /* from VLV */
      76             :         struct GUID *results;
      77             :         size_t num_entries;
      78             :         size_t result_array_size;
      79             : 
      80             :         struct ldb_control **down_controls;
      81             :         const char * const *attrs;
      82             : 
      83             :         unsigned last_i;
      84             :         struct ldb_parse_tree *expr;
      85             :         char *expr_str;
      86             : };
      87             : 
      88             : struct private_data {
      89             :         uint32_t next_free_id;
      90             :         size_t num_stores;
      91             :         struct results_store *store;
      92             : };
      93             : 
      94       57893 : static int store_destructor(struct results_store *del)
      95             : {
      96       57893 :         struct private_data *priv = del->priv;
      97       57893 :         DLIST_REMOVE(priv->store, del);
      98             : 
      99       57893 :         priv->num_stores -= 1;
     100             : 
     101       57893 :         return 0;
     102             : }
     103             : 
     104       57893 : static struct results_store *new_store(struct private_data *priv)
     105             : {
     106             :         struct results_store *newr;
     107       57893 :         uint32_t new_id = priv->next_free_id++;
     108             : 
     109             :         /* TODO: we should have a limit on the number of
     110             :          * outstanding paged searches
     111             :          */
     112             : 
     113       57893 :         newr = talloc_zero(priv, struct results_store);
     114       57893 :         if (!newr) return NULL;
     115             : 
     116       57893 :         newr->priv = priv;
     117             : 
     118       57893 :         newr->cookie = talloc_asprintf(newr, "%d", new_id);
     119       57893 :         if (!newr->cookie) {
     120           0 :                 talloc_free(newr);
     121           0 :                 return NULL;
     122             :         }
     123             : 
     124       57893 :         newr->timestamp = time(NULL);
     125             : 
     126       57893 :         DLIST_ADD(priv->store, newr);
     127             : 
     128       57893 :         priv->num_stores += 1;
     129             : 
     130       57893 :         talloc_set_destructor(newr, store_destructor);
     131             : 
     132       57893 :         if (priv->num_stores > 10) {
     133             :                 struct results_store *last;
     134             :                 /*
     135             :                  * 10 is the default for MaxResultSetsPerConn --
     136             :                  * possibly need to parameterize it.
     137             :                  */
     138       48576 :                 last = DLIST_TAIL(priv->store);
     139       48576 :                 TALLOC_FREE(last);
     140             :         }
     141             : 
     142       57872 :         return newr;
     143             : }
     144             : 
     145             : struct paged_context {
     146             :         struct ldb_module *module;
     147             :         struct ldb_request *req;
     148             : 
     149             :         struct results_store *store;
     150             :         int size;
     151             :         struct ldb_control **controls;
     152             : };
     153             : 
     154       14290 : static int send_referrals(struct results_store *store,
     155             :                           struct ldb_request *req)
     156             : {
     157             :         int ret;
     158             :         struct referral_store *node;
     159       58966 :         while (store->first_ref != NULL) {
     160       36598 :                 node = store->first_ref;
     161       36598 :                 ret = ldb_module_send_referral(req, node->ref);
     162       36598 :                 if (ret != LDB_SUCCESS) {
     163           0 :                         return ret;
     164             :                 }
     165       36598 :                 store->first_ref = node->next;
     166       36598 :                 talloc_free(node);
     167             :         }
     168       14290 :         return LDB_SUCCESS;
     169             : }
     170             : 
     171             : /* Start an ldb request for a single object by GUID */
     172      137583 : static int paged_search_by_dn_guid(struct ldb_module *module,
     173             :                                  struct paged_context *ac,
     174             :                                  struct ldb_result **result,
     175             :                                  const struct GUID *guid,
     176             :                                  const char * const *attrs,
     177             :                                  struct ldb_parse_tree *expr)
     178             : {
     179             :         struct ldb_dn *dn;
     180             :         struct ldb_request *req;
     181             :         struct ldb_result *res;
     182             :         int ret;
     183             :         struct GUID_txt_buf guid_str;
     184             : 
     185             :         /* Use controls passed in on the downreq */
     186      137583 :         struct ldb_control **controls = ac->store->down_controls;
     187             : 
     188      137583 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
     189             : 
     190      137583 :         dn = ldb_dn_new_fmt(ac, ldb, "<GUID=%s>",
     191             :                             GUID_buf_string(guid, &guid_str));
     192      137583 :         if (dn == NULL) {
     193           0 :                 return ldb_oom(ldb);
     194             :         }
     195             : 
     196      137583 :         res = talloc_zero(ac, struct ldb_result);
     197      137583 :         if (res == NULL) {
     198           0 :                 TALLOC_FREE(dn);
     199           0 :                 return ldb_oom(ldb);
     200             :         }
     201             : 
     202      137583 :         ret = ldb_build_search_req_ex(&req, ldb, ac,
     203             :                                    dn,
     204             :                                    LDB_SCOPE_BASE,
     205             :                                    expr,
     206             :                                    attrs,
     207             :                                    controls,
     208             :                                    res,
     209             :                                    ldb_search_default_callback,
     210             :                                    ac->req);
     211      137583 :         if (ret != LDB_SUCCESS) {
     212           0 :                 TALLOC_FREE(dn);
     213           0 :                 TALLOC_FREE(res);
     214           0 :                 return ret;
     215             :         }
     216             : 
     217             :         /*
     218             :          * Ensure the dn lasts only as long as the request,
     219             :          * as we will have a lot of these (one per object
     220             :          * being returned)
     221             :          */
     222             : 
     223      137583 :         talloc_steal(req, dn);
     224             : 
     225      137583 :         ret = ldb_request(ldb, req);
     226      137583 :         if (ret == LDB_SUCCESS) {
     227      137583 :                 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
     228             :         }
     229             : 
     230      137583 :         talloc_free(req);
     231      137583 :         if (ret != LDB_SUCCESS) {
     232           2 :                 talloc_free(res);
     233           2 :                 return ret;
     234             :         }
     235             : 
     236      137581 :         *result = res;
     237      137581 :         return ret;
     238             : }
     239             : 
     240       57997 : static int paged_results(struct paged_context *ac, struct ldb_reply *ares)
     241             : {
     242             :         struct ldb_paged_control *paged;
     243             :         unsigned int i, num_ctrls;
     244             :         int ret;
     245             : 
     246       57997 :         if (ac->store == NULL) {
     247           0 :                 ret = LDB_ERR_OPERATIONS_ERROR;
     248           0 :                 return ldb_module_done(
     249             :                         ac->req, ac->controls, ares->response, ret);
     250             :         }
     251             : 
     252      244618 :         while (ac->store->last_i < ac->store->num_entries && ac->size > 0) {
     253      137583 :                 struct GUID *guid = &ac->store->results[ac->store->last_i++];
     254      137583 :                 struct ldb_result *result = NULL;
     255             : 
     256      137583 :                 ac->size--;
     257             : 
     258             :                 /*
     259             :                  * Note: In the case that an object has been moved to a
     260             :                  * different place in the LDAP tree, we might expect the object
     261             :                  * to disappear from paged results.  If we were going to
     262             :                  * implement that behaviour, we would do it here by passing
     263             :                  * down the original container DN to the search.
     264             :                  * However, testing shows that, on Windows, the moved object
     265             :                  * remains in the paged results. So, we are matching Windows
     266             :                  * behaviour here by leaving out the scope.
     267             :                  */
     268      259621 :                 ret = paged_search_by_dn_guid(ac->module, ac, &result, guid,
     269      137583 :                                             ac->req->op.search.attrs,
     270      137562 :                                             ac->store->expr);
     271      137583 :                 if (ret == LDAP_NO_SUCH_OBJECT ||
     272      137581 :                     (ret == LDB_SUCCESS && result->count == 0)) {
     273             :                         /* The thing isn't there TODO, which we quietly
     274             :                            ignore and go on to send an extra one
     275             :                            instead. */
     276           7 :                         continue;
     277      137576 :                 } else if (ret != LDB_SUCCESS) {
     278           0 :                         return ldb_module_done(
     279             :                                 ac->req, ac->controls, ares->response, ret);
     280             :                 }
     281             : 
     282      137576 :                 ret = ldb_module_send_entry(ac->req, result->msgs[0],
     283             :                                             NULL);
     284      137576 :                 if (ret != LDB_SUCCESS) {
     285             :                         /*
     286             :                          * ldb_module_send_entry will have called
     287             :                          * ldb_module_done if an error occurred.
     288             :                          */
     289           0 :                         return ret;
     290             :                 }
     291             :         }
     292             : 
     293       57997 :         if (ac->store->first_ref) {
     294             :                 /* There is no right place to put references in the sorted
     295             :                    results, so we send them as soon as possible.
     296             :                 */
     297       14290 :                 ret = send_referrals(ac->store, ac->req);
     298       14290 :                 if (ret != LDB_SUCCESS) {
     299             :                         /*
     300             :                          * send_referrals will have called ldb_module_done
     301             :                          * if an error occurred.
     302             :                          */
     303           0 :                         return ret;
     304             :                 }
     305             :         }
     306             : 
     307             :         /* return result done */
     308       57997 :         num_ctrls = 1;
     309       57997 :         i = 0;
     310             : 
     311       57997 :         if (ac->store->controls != NULL) {
     312           1 :                 while (ac->store->controls[i]) i++; /* counting */
     313             : 
     314           1 :                 num_ctrls += i;
     315             :         }
     316             : 
     317       57997 :         ac->controls = talloc_array(ac, struct ldb_control *, num_ctrls +1);
     318       57997 :         if (ac->controls == NULL) {
     319           0 :                 ret = LDB_ERR_OPERATIONS_ERROR;
     320           0 :                 return ldb_module_done(
     321             :                         ac->req, ac->controls, ares->response, ret);
     322             :         }
     323       57997 :         ac->controls[num_ctrls] = NULL;
     324             : 
     325       57998 :         for (i = 0; i < (num_ctrls -1); i++) {
     326           1 :                 ac->controls[i] = talloc_reference(ac->controls,
     327             :                                                    ac->store->controls[i]);
     328             :         }
     329             : 
     330       57997 :         ac->controls[i] = talloc(ac->controls, struct ldb_control);
     331       57997 :         if (ac->controls[i] == NULL) {
     332           0 :                 ret = LDB_ERR_OPERATIONS_ERROR;
     333           0 :                 return ldb_module_done(
     334             :                         ac->req, ac->controls, ares->response, ret);
     335             :         }
     336             : 
     337       57997 :         ac->controls[i]->oid = talloc_strdup(ac->controls[i],
     338             :                                                 LDB_CONTROL_PAGED_RESULTS_OID);
     339       57997 :         if (ac->controls[i]->oid == NULL) {
     340           0 :                 ret = LDB_ERR_OPERATIONS_ERROR;
     341           0 :                 return ldb_module_done(
     342             :                         ac->req, ac->controls, ares->response, ret);
     343             :         }
     344             : 
     345       57997 :         ac->controls[i]->critical = 0;
     346             : 
     347       57997 :         paged = talloc(ac->controls[i], struct ldb_paged_control);
     348       57997 :         if (paged == NULL) {
     349           0 :                 ret = LDB_ERR_OPERATIONS_ERROR;
     350           0 :                 return ldb_module_done(
     351             :                         ac->req, ac->controls, ares->response, ret);
     352             :         }
     353             : 
     354       57997 :         ac->controls[i]->data = paged;
     355             : 
     356       57997 :         if (ac->size > 0) {
     357       57592 :                 paged->size = 0;
     358       57592 :                 paged->cookie = NULL;
     359       57592 :                 paged->cookie_len = 0;
     360             :         } else {
     361         405 :                 paged->size = ac->store->num_entries;
     362         405 :                 paged->cookie = talloc_strdup(paged, ac->store->cookie);
     363         405 :                 paged->cookie_len = strlen(paged->cookie) + 1;
     364             :         }
     365             : 
     366       57976 :         return LDB_SUCCESS;
     367             : }
     368             : 
     369       36598 : static int save_referral(struct results_store *store, char *ref)
     370             : {
     371       36598 :         struct referral_store *node = talloc(store,
     372             :                                              struct referral_store);
     373       36598 :         if (node == NULL) {
     374           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     375             :         }
     376       36598 :         node->next = NULL;
     377       36598 :         node->ref = talloc_steal(node, ref);
     378             : 
     379       36598 :         if (store->first_ref == NULL) {
     380       14290 :                 store->first_ref = node;
     381             :         } else {
     382       22308 :                 store->last_ref->next = node;
     383             :         }
     384       36598 :         store->last_ref = node;
     385       36598 :         return LDB_SUCCESS;
     386             : }
     387             : 
     388      705232 : static int paged_search_callback(struct ldb_request *req,
     389             :                                  struct ldb_reply *ares)
     390             : {
     391             :         struct paged_context *ac;
     392             :         struct results_store *store;
     393             :         int ret;
     394             :         const struct ldb_val *guid_blob;
     395             :         struct GUID guid;
     396             :         NTSTATUS status;
     397             : 
     398      705232 :         ac = talloc_get_type(req->context, struct paged_context);
     399      705232 :         store = ac->store;
     400             : 
     401      705232 :         if (!ares) {
     402           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     403             :                                         LDB_ERR_OPERATIONS_ERROR);
     404             :         }
     405      705232 :         if (ares->error != LDB_SUCCESS) {
     406         156 :                 return ldb_module_done(ac->req, ares->controls,
     407             :                                         ares->response, ares->error);
     408             :         }
     409             : 
     410      705076 :         switch (ares->type) {
     411      610741 :         case LDB_REPLY_ENTRY:
     412      610741 :                 if (store->results == NULL) {
     413       57577 :                         store->num_entries = 0;
     414       57577 :                         store->result_array_size = 16;
     415       57577 :                         store->results = talloc_array(store, struct GUID,
     416             :                                                      store->result_array_size);
     417       57577 :                         if (store->results == NULL) {
     418           0 :                                 return ldb_module_done(ac->req, NULL, NULL,
     419             :                                                      LDB_ERR_OPERATIONS_ERROR);
     420             :                         }
     421      553164 :                 } else if (store->num_entries == store->result_array_size) {
     422        1824 :                         if (store->result_array_size > INT_MAX/2) {
     423           0 :                                 return ldb_module_done(ac->req, NULL, NULL,
     424             :                                                      LDB_ERR_OPERATIONS_ERROR);
     425             :                         }
     426        1824 :                         store->result_array_size *= 2;
     427        1824 :                         store->results = talloc_realloc(store, store->results,
     428             :                                                         struct GUID,
     429             :                                                 store->result_array_size);
     430        1824 :                         if (store->results == NULL) {
     431           0 :                                 return ldb_module_done(ac->req, NULL, NULL,
     432             :                                                      LDB_ERR_OPERATIONS_ERROR);
     433             :                         }
     434             :                 }
     435             : 
     436      610741 :                 guid_blob = ldb_dn_get_extended_component(ares->message->dn,
     437             :                                                           "GUID");
     438      610741 :                 if (guid_blob == NULL) {
     439           0 :                         return ldb_module_done(ac->req, NULL, NULL,
     440             :                                                LDB_ERR_OPERATIONS_ERROR);
     441             :                 }
     442      610741 :                 status = GUID_from_ndr_blob(guid_blob, &guid);
     443      610741 :                 if (!NT_STATUS_IS_OK(status)) {
     444           0 :                         return ldb_module_done(ac->req, NULL, NULL,
     445             :                                                LDB_ERR_OPERATIONS_ERROR);
     446             :                 }
     447             : 
     448             :                 /* Redundant paranoid check */
     449      610741 :                 if (store->num_entries > store->result_array_size) {
     450           0 :                         return ldb_module_done(ac->req, NULL, NULL,
     451             :                                                LDB_ERR_OPERATIONS_ERROR);
     452             :                 }
     453             : 
     454      610741 :                 store->results[store->num_entries] = guid;
     455      610741 :                 store->num_entries++;
     456      610741 :                 break;
     457             : 
     458       36598 :         case LDB_REPLY_REFERRAL:
     459       36598 :                 ret = save_referral(store, ares->referral);
     460       36598 :                 if (ret != LDB_SUCCESS) {
     461           0 :                         return ldb_module_done(ac->req, NULL, NULL, ret);
     462             :                 }
     463       36598 :                 break;
     464             : 
     465       57737 :         case LDB_REPLY_DONE:
     466       57737 :                 if (store->num_entries != 0) {
     467       57577 :                         store->results = talloc_realloc(store, store->results,
     468             :                                                         struct GUID,
     469             :                                                         store->num_entries);
     470       57577 :                         if (store->results == NULL) {
     471           0 :                                 return ldb_module_done(ac->req, NULL, NULL,
     472             :                                                      LDB_ERR_OPERATIONS_ERROR);
     473             :                         }
     474             :                 }
     475       57737 :                 store->result_array_size = store->num_entries;
     476             : 
     477       57737 :                 ac->store->controls = talloc_move(ac->store, &ares->controls);
     478       57737 :                 ret = paged_results(ac, ares);
     479       57737 :                 if (ret != LDB_SUCCESS) {
     480             :                         /* paged_results will have called ldb_module_done
     481             :                          * if an error occurred
     482             :                          */
     483           0 :                         return ret;
     484             :                 }
     485       57737 :                 return ldb_module_done(ac->req, ac->controls,
     486             :                                         ares->response, ret);
     487             :         }
     488             : 
     489      571247 :         return LDB_SUCCESS;
     490             : }
     491             : 
     492             : static struct ldb_control **
     493       57893 : paged_results_copy_down_controls(TALLOC_CTX *mem_ctx,
     494             :                                  struct ldb_control **controls)
     495             : {
     496             : 
     497             :         struct ldb_control **new_controls;
     498             :         unsigned int i, j, num_ctrls;
     499       57893 :         if (controls == NULL) {
     500           0 :                 return NULL;
     501             :         }
     502             : 
     503       76928 :         for (num_ctrls = 0; controls[num_ctrls]; num_ctrls++);
     504             : 
     505       57893 :         new_controls = talloc_array(mem_ctx, struct ldb_control *, num_ctrls);
     506       57893 :         if (new_controls == NULL) {
     507           0 :                 return NULL;
     508             :         }
     509             : 
     510      200180 :         for (j = 0, i = 0; i < (num_ctrls); i++) {
     511      142308 :                 struct ldb_control *control = controls[i];
     512      142308 :                 if (control->oid == NULL) {
     513       57755 :                         continue;
     514             :                 }
     515       84553 :                 if (strcmp(control->oid, LDB_CONTROL_PAGED_RESULTS_OID) == 0) {
     516       57893 :                         continue;
     517             :                 }
     518             :                 /*
     519             :                  * ASQ changes everything, do not copy it down for the
     520             :                  * per-GUID search
     521             :                  */
     522       26660 :                 if (strcmp(control->oid, LDB_CONTROL_ASQ_OID) == 0) {
     523           1 :                         continue;
     524             :                 }
     525       26659 :                 new_controls[j] = talloc_steal(new_controls, control);
     526             : 
     527             :                 /*
     528             :                  * Sadly the caller is not obliged to make this a
     529             :                  * proper talloc tree, so we do so here.
     530             :                  */
     531       26659 :                 if (control->data) {
     532       12862 :                         talloc_steal(control, control->data);
     533             :                 }
     534       26659 :                 j++;
     535             :         }
     536       57893 :         new_controls[j] = NULL;
     537       57893 :         return new_controls;
     538             : }
     539             : 
     540       57893 : static const char * const *paged_copy_attrs(TALLOC_CTX *mem_ctx,
     541             :                                             const char * const *attrs) {
     542             :         int i;
     543             :         const char **new_attrs;
     544       57893 :         if (attrs == NULL) {
     545       17698 :                 return NULL;
     546             :         }
     547       40195 :         new_attrs = ldb_attr_list_copy(mem_ctx, attrs);
     548             : 
     549      237733 :         for (i=0; attrs[i] != NULL; i++) {
     550      197538 :                 new_attrs[i] = talloc_strdup(mem_ctx, attrs[i]);
     551             :         }
     552       40195 :         new_attrs[i] = NULL;
     553       40195 :         return new_attrs;
     554             : }
     555             : 
     556             : /*
     557             :  * Check if two sets of controls are the same except for the paged results
     558             :  * control in the request controls.  This function is messy because request
     559             :  * control lists can contain controls that were NULL'd by the rootdse.  We
     560             :  * must ignore those entries.  This function is not portable.
     561             :  */
     562         265 : static bool paged_controls_same(struct ldb_request *req,
     563             :                                 struct ldb_control **down_controls) {
     564             :         int i;
     565             :         unsigned int num_down_controls, num_non_null_req_controls;
     566             :         struct ldb_control *ctrl;
     567             : 
     568         265 :         num_down_controls = 0;
     569         286 :         for (i=0; down_controls[i] != NULL; i++) {
     570          24 :                 num_down_controls++;
     571             : 
     572          24 :                 ctrl = ldb_request_get_control(req, down_controls[i]->oid);
     573          24 :                 if (ctrl == NULL) {
     574           3 :                         return false;
     575             :                 }
     576             :         }
     577             : 
     578         262 :         num_non_null_req_controls = 0;
     579         805 :         for (i=0; req->controls[i] != NULL; i++) {
     580         776 :                 if (req->controls[i]->oid != NULL &&
     581         285 :                     strcmp(req->controls[i]->oid,
     582             :                            LDB_CONTROL_ASQ_OID) != 0) {
     583         285 :                         num_non_null_req_controls++;
     584             :                 }
     585             :         }
     586             : 
     587             :         /* At this point we have the number of non-null entries for both
     588             :          * control lists and we know that:
     589             :          * 1. down_controls does not contain the paged control or ASQ
     590             :          *      (because paged_results_copy_down_controls excludes it)
     591             :          * 2. req->controls does contain the paged control
     592             :          *      (because this function is only called if this is true)
     593             :          * 3. down_controls is a subset of non-null controls in req->controls
     594             :          *      (checked above)
     595             :          * So to confirm that the two lists are identical except for the paged
     596             :          * control and possibly ASQ, all we need to check is: */
     597         262 :         if (num_non_null_req_controls == num_down_controls + 1) {
     598         260 :                 return true;
     599             :         }
     600           2 :         return false;
     601             : }
     602             : 
     603         260 : static bool paged_attrs_same(const char * const *attrs_1,
     604             :                              const char * const *attrs_2) {
     605             :         int i;
     606         260 :         if (attrs_1 == NULL || attrs_2 == NULL) {
     607         148 :                 if (attrs_1 == NULL && attrs_2 == NULL) {
     608         148 :                         return true;
     609             :                 }
     610           0 :                 return false;
     611             :         }
     612             : 
     613         321 :         for (i=0; attrs_1[i] != NULL; i++) {
     614         209 :                if (!ldb_attr_in_list(attrs_2, attrs_1[i])) {
     615           0 :                        return false;
     616             :                }
     617             :         }
     618         112 :         return true;
     619             : }
     620             : 
     621    14386266 : static int paged_search(struct ldb_module *module, struct ldb_request *req)
     622             : {
     623             :         struct ldb_context *ldb;
     624             :         struct ldb_control *control;
     625             :         struct ldb_control *vlv_control;
     626             :         struct private_data *private_data;
     627             :         struct ldb_paged_control *paged_ctrl;
     628             :         struct ldb_request *search_req;
     629             :         struct paged_context *ac;
     630             :         int ret;
     631             : 
     632    14386266 :         ldb = ldb_module_get_ctx(module);
     633             : 
     634             :         /* check if there's a paged request control */
     635    14386266 :         control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID);
     636    14386266 :         if (control == NULL) {
     637             :                 /* not found go on */
     638    14328105 :                 return ldb_next_request(module, req);
     639             :         }
     640             : 
     641       58161 :         paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
     642       58161 :         if (!paged_ctrl) {
     643           0 :                 return LDB_ERR_PROTOCOL_ERROR;
     644             :         }
     645             : 
     646       58161 :         private_data = talloc_get_type(ldb_module_get_private(module),
     647             :                                         struct private_data);
     648             : 
     649       58161 :         vlv_control = ldb_request_get_control(req, LDB_CONTROL_VLV_REQ_OID);
     650       58161 :         if (vlv_control != NULL) {
     651             :                 /*
     652             :                  * VLV and paged_results are not allowed at the same
     653             :                  * time
     654             :                  */
     655           2 :                 return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
     656             :         }
     657             : 
     658       58159 :         ac = talloc_zero(req, struct paged_context);
     659       58159 :         if (ac == NULL) {
     660           0 :                 ldb_set_errstring(ldb, "Out of Memory");
     661           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     662             :         }
     663             : 
     664       58159 :         ac->module = module;
     665       58159 :         ac->req = req;
     666       58159 :         ac->size = paged_ctrl->size;
     667       58159 :         if (ac->size < 0) {
     668             :                 /*
     669             :                  * Apparently some clients send more than 2^31. This
     670             :                  * violates the ldap standard, but we need to cope.
     671             :                  * In the future, if maximum result sizes are implemented in
     672             :                  * Samba, we should also clamp the page size to the maximum
     673             :                  * result size.
     674             :                  */
     675           0 :                 ac->size = 0x7FFFFFFF;
     676             :         }
     677             : 
     678             :         /* check if it is a continuation search the store */
     679       58159 :         if (paged_ctrl->cookie_len == 0) {
     680             :                 struct ldb_control *ext_ctrl;
     681             :                 struct ldb_control **controls;
     682             :                 static const char * const attrs[1] = { NULL };
     683             : 
     684       57893 :                 if (paged_ctrl->size == 0) {
     685           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     686             :                 }
     687             : 
     688       57893 :                 ac->store = new_store(private_data);
     689       57893 :                 if (ac->store == NULL) {
     690           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     691             :                 }
     692             : 
     693       57893 :                 controls = req->controls;
     694       57893 :                 ext_ctrl = ldb_request_get_control(req,
     695             :                                         LDB_CONTROL_EXTENDED_DN_OID);
     696       57893 :                 if (ext_ctrl == NULL) {
     697             :                         /*
     698             :                          * Add extended_dn control to the request if there
     699             :                          * isn't already one.  We'll get the GUID out of it in
     700             :                          * the callback.  This is a workaround for the case
     701             :                          * where ntsecuritydescriptor forbids fetching GUIDs
     702             :                          * for the current user.
     703             :                          */
     704             :                         struct ldb_request *req_extended_dn;
     705             :                         struct ldb_extended_dn_control *ext_ctrl_data;
     706       45620 :                         req_extended_dn = talloc_zero(req, struct ldb_request);
     707       45620 :                         req_extended_dn->controls = req->controls;
     708       45620 :                         ext_ctrl_data = talloc_zero(req,
     709             :                                         struct ldb_extended_dn_control);
     710       45620 :                         ext_ctrl_data->type = 1;
     711             : 
     712       45620 :                         ret = ldb_request_add_control(req_extended_dn,
     713             :                                               LDB_CONTROL_EXTENDED_DN_OID,
     714             :                                                       true,
     715             :                                                       ext_ctrl_data);
     716       45620 :                         if (ret != LDB_SUCCESS) {
     717           0 :                                 return ret;
     718             :                         }
     719       45620 :                         controls = req_extended_dn->controls;
     720             :                 }
     721             : 
     722       57893 :                 ret = ldb_build_search_req_ex(&search_req, ldb, ac,
     723             :                                                 req->op.search.base,
     724             :                                                 req->op.search.scope,
     725             :                                                 req->op.search.tree,
     726             :                                                 attrs,
     727             :                                                 controls,
     728             :                                                 ac,
     729             :                                                 paged_search_callback,
     730             :                                                 req);
     731       57893 :                 if (ret != LDB_SUCCESS) {
     732           0 :                         return ret;
     733             :                 }
     734             : 
     735       57893 :                 ac->store->expr = talloc_steal(ac->store, req->op.search.tree);
     736       66845 :                 ac->store->expr_str = ldb_filter_from_tree(ac->store,
     737       57893 :                                                           req->op.search.tree);
     738       57893 :                 ac->store->attrs = paged_copy_attrs(ac->store,
     739             :                                                     req->op.search.attrs);
     740             : 
     741             :                 /* save it locally and remove it from the list */
     742             :                 /* we do not need to replace them later as we
     743             :                  * are keeping the original req intact */
     744       57893 :                 if (!ldb_save_controls(control, search_req, NULL)) {
     745           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     746             :                 }
     747      115765 :                 ac->store->down_controls =
     748      106813 :                     paged_results_copy_down_controls(ac->store, req->controls);
     749       57893 :                 if (ac->store->down_controls == NULL) {
     750           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     751             :                 }
     752             : 
     753       57893 :                 return ldb_next_request(module, search_req);
     754             : 
     755             :         } else {
     756         266 :                 struct results_store *current = NULL;
     757             :                 char *expr_str;
     758             :                 bool bool_ret;
     759             : 
     760             :                 /* TODO: age out old outstanding requests */
     761         494 :                 for (current = private_data->store; current != NULL;
     762          14 :                      current = current->next) {
     763         280 :                         if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
     764         266 :                                 current->timestamp = time(NULL);
     765         266 :                                 break;
     766             :                         }
     767             :                 }
     768         266 :                 if (current == NULL) {
     769           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     770             :                 }
     771             : 
     772             :                 /* Get the expression string and make sure it didn't change */
     773         266 :                 expr_str = ldb_filter_from_tree(ac, req->op.search.tree);
     774         266 :                 if (expr_str == NULL) {
     775           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     776             :                 }
     777             : 
     778         266 :                 ret = strcmp(current->expr_str, expr_str);
     779         266 :                 if (ret != 0) {
     780           1 :                         return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
     781             :                 }
     782             : 
     783         265 :                 bool_ret = paged_controls_same(req, current->down_controls);
     784         265 :                 if (bool_ret == false) {
     785           5 :                         return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
     786             :                 }
     787             : 
     788         260 :                 bool_ret = paged_attrs_same(req->op.search.attrs,
     789             :                                             current->attrs);
     790         260 :                 if (bool_ret == false) {
     791           0 :                         return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
     792             :                 }
     793             : 
     794         260 :                 DLIST_PROMOTE(private_data->store, current);
     795             : 
     796         260 :                 ac->store = current;
     797             : 
     798             :                 /* check if it is an abandon */
     799         260 :                 if (ac->size == 0) {
     800           0 :                         return ldb_module_done(req, NULL, NULL,
     801             :                                                                 LDB_SUCCESS);
     802             :                 }
     803             : 
     804         260 :                 ret = paged_results(ac, NULL);
     805         260 :                 if (ret != LDB_SUCCESS) {
     806           0 :                         return ldb_module_done(req, NULL, NULL, ret);
     807             :                 }
     808         260 :                 return ldb_module_done(req, ac->controls, NULL, LDB_SUCCESS);
     809             :         }
     810             : }
     811             : 
     812      132464 : static int paged_request_init(struct ldb_module *module)
     813             : {
     814             :         struct ldb_context *ldb;
     815             :         struct private_data *data;
     816             :         int ret;
     817             : 
     818      132464 :         ldb = ldb_module_get_ctx(module);
     819             : 
     820      132464 :         data = talloc(module, struct private_data);
     821      132464 :         if (data == NULL) {
     822           0 :                 return LDB_ERR_OTHER;
     823             :         }
     824             : 
     825      132464 :         data->next_free_id = 1;
     826      132464 :         data->num_stores = 0;
     827      132464 :         data->store = NULL;
     828      132464 :         ldb_module_set_private(module, data);
     829             : 
     830      132464 :         ret = ldb_mod_register_control(module, LDB_CONTROL_PAGED_RESULTS_OID);
     831      132464 :         if (ret != LDB_SUCCESS) {
     832           0 :                 ldb_debug(ldb, LDB_DEBUG_WARNING,
     833             :                         "paged_results:"
     834             :                         "Unable to register control with rootdse!");
     835             :         }
     836             : 
     837      132464 :         return ldb_next_init(module);
     838             : }
     839             : 
     840             : static const struct ldb_module_ops ldb_paged_results_module_ops = {
     841             :         .name           = "dsdb_paged_results",
     842             :         .search         = paged_search,
     843             :         .init_context   = paged_request_init
     844             : };
     845             : 
     846        5536 : int ldb_dsdb_paged_results_init(const char *version)
     847             : {
     848        5536 :         LDB_MODULE_CHECK_VERSION(version);
     849        5536 :         return ldb_register_module(&ldb_paged_results_module_ops);
     850             : }

Generated by: LCOV version 1.13