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 master 70ed9daf Lines: 314 384 81.8 %
Date: 2024-01-11 09:59:51 Functions: 14 14 100.0 %

          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       64146 : static int store_destructor(struct results_store *del)
      95             : {
      96       64146 :         struct private_data *priv = del->priv;
      97       64146 :         DLIST_REMOVE(priv->store, del);
      98             : 
      99       64146 :         priv->num_stores -= 1;
     100             : 
     101       64146 :         return 0;
     102             : }
     103             : 
     104       64146 : static struct results_store *new_store(struct private_data *priv)
     105             : {
     106          22 :         struct results_store *newr;
     107       64146 :         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       64146 :         newr = talloc_zero(priv, struct results_store);
     114       64146 :         if (!newr) return NULL;
     115             : 
     116       64146 :         newr->priv = priv;
     117             : 
     118       64146 :         newr->cookie = talloc_asprintf(newr, "%d", new_id);
     119       64146 :         if (!newr->cookie) {
     120           0 :                 talloc_free(newr);
     121           0 :                 return NULL;
     122             :         }
     123             : 
     124       64146 :         newr->timestamp = time(NULL);
     125             : 
     126       64146 :         DLIST_ADD(priv->store, newr);
     127             : 
     128       64146 :         priv->num_stores += 1;
     129             : 
     130       64146 :         talloc_set_destructor(newr, store_destructor);
     131             : 
     132       64146 :         if (priv->num_stores > 10) {
     133           0 :                 struct results_store *last;
     134             :                 /*
     135             :                  * 10 is the default for MaxResultSetsPerConn --
     136             :                  * possibly need to parameterize it.
     137             :                  */
     138       53303 :                 last = DLIST_TAIL(priv->store);
     139       53303 :                 TALLOC_FREE(last);
     140             :         }
     141             : 
     142       64124 :         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       14481 : static int send_referrals(struct results_store *store,
     155             :                           struct ldb_request *req)
     156             : {
     157           0 :         int ret;
     158           0 :         struct referral_store *node;
     159       51576 :         while (store->first_ref != NULL) {
     160       37095 :                 node = store->first_ref;
     161       37095 :                 ret = ldb_module_send_referral(req, node->ref);
     162       37095 :                 if (ret != LDB_SUCCESS) {
     163           0 :                         return ret;
     164             :                 }
     165       37095 :                 store->first_ref = node->next;
     166       37095 :                 talloc_free(node);
     167             :         }
     168       14481 :         return LDB_SUCCESS;
     169             : }
     170             : 
     171             : /* Start an ldb request for a single object by GUID */
     172      142957 : 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          22 :         struct ldb_dn *dn;
     180          22 :         struct ldb_request *req;
     181          22 :         struct ldb_result *res;
     182          22 :         int ret;
     183          22 :         struct GUID_txt_buf guid_str;
     184             : 
     185             :         /* Use controls passed in on the downreq */
     186      142957 :         struct ldb_control **controls = ac->store->down_controls;
     187             : 
     188      142957 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
     189             : 
     190      142957 :         dn = ldb_dn_new_fmt(ac, ldb, "<GUID=%s>",
     191             :                             GUID_buf_string(guid, &guid_str));
     192      142957 :         if (dn == NULL) {
     193           0 :                 return ldb_oom(ldb);
     194             :         }
     195             : 
     196      142957 :         res = talloc_zero(ac, struct ldb_result);
     197      142957 :         if (res == NULL) {
     198           0 :                 TALLOC_FREE(dn);
     199           0 :                 return ldb_oom(ldb);
     200             :         }
     201             : 
     202      142957 :         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      142957 :         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      142957 :         talloc_steal(req, dn);
     224             : 
     225      142957 :         ret = ldb_request(ldb, req);
     226      142957 :         if (ret == LDB_SUCCESS) {
     227      142957 :                 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
     228             :         }
     229             : 
     230      142957 :         talloc_free(req);
     231      142957 :         if (ret != LDB_SUCCESS) {
     232           2 :                 talloc_free(res);
     233           2 :                 return ret;
     234             :         }
     235             : 
     236      142955 :         *result = res;
     237      142955 :         return ret;
     238             : }
     239             : 
     240       64263 : static int paged_results(struct paged_context *ac, struct ldb_reply *ares)
     241             : {
     242       64263 :         struct ldb_extended *response = (ares != NULL ? ares->response : NULL);
     243          22 :         struct ldb_paged_control *paged;
     244          22 :         unsigned int i, num_ctrls;
     245          22 :         int ret;
     246             : 
     247       64263 :         if (ac->store == NULL) {
     248           0 :                 ret = LDB_ERR_OPERATIONS_ERROR;
     249           0 :                 return ldb_module_done(
     250             :                         ac->req, ac->controls, response, ret);
     251             :         }
     252             : 
     253      207220 :         while (ac->store->last_i < ac->store->num_entries && ac->size > 0) {
     254      142957 :                 struct GUID *guid = &ac->store->results[ac->store->last_i++];
     255      142957 :                 struct ldb_result *result = NULL;
     256             : 
     257      142957 :                 ac->size--;
     258             : 
     259             :                 /*
     260             :                  * Note: In the case that an object has been moved to a
     261             :                  * different place in the LDAP tree, we might expect the object
     262             :                  * to disappear from paged results.  If we were going to
     263             :                  * implement that behaviour, we would do it here by passing
     264             :                  * down the original container DN to the search.
     265             :                  * However, testing shows that, on Windows, the moved object
     266             :                  * remains in the paged results. So, we are matching Windows
     267             :                  * behaviour here by leaving out the scope.
     268             :                  */
     269      142979 :                 ret = paged_search_by_dn_guid(ac->module, ac, &result, guid,
     270      142957 :                                             ac->req->op.search.attrs,
     271      142935 :                                             ac->store->expr);
     272      142957 :                 if (ret == LDAP_NO_SUCH_OBJECT ||
     273      142955 :                     (ret == LDB_SUCCESS && result->count == 0)) {
     274             :                         /* The thing isn't there TODO, which we quietly
     275             :                            ignore and go on to send an extra one
     276             :                            instead. */
     277           7 :                         continue;
     278      142950 :                 } else if (ret != LDB_SUCCESS) {
     279           0 :                         return ldb_module_done(
     280             :                                 ac->req, ac->controls, response, ret);
     281             :                 }
     282             : 
     283      142950 :                 ret = ldb_module_send_entry(ac->req, result->msgs[0],
     284             :                                             NULL);
     285      142950 :                 if (ret != LDB_SUCCESS) {
     286             :                         /*
     287             :                          * ldb_module_send_entry will have called
     288             :                          * ldb_module_done if an error occurred.
     289             :                          */
     290           0 :                         return ret;
     291             :                 }
     292             :         }
     293             : 
     294       64263 :         if (ac->store->first_ref) {
     295             :                 /* There is no right place to put references in the sorted
     296             :                    results, so we send them as soon as possible.
     297             :                 */
     298       14481 :                 ret = send_referrals(ac->store, ac->req);
     299       14481 :                 if (ret != LDB_SUCCESS) {
     300             :                         /*
     301             :                          * send_referrals will have called ldb_module_done
     302             :                          * if an error occurred.
     303             :                          */
     304           0 :                         return ret;
     305             :                 }
     306             :         }
     307             : 
     308             :         /* return result done */
     309       64263 :         num_ctrls = 1;
     310       64263 :         i = 0;
     311             : 
     312       64263 :         if (ac->store->controls != NULL) {
     313           2 :                 while (ac->store->controls[i]) i++; /* counting */
     314             : 
     315           1 :                 num_ctrls += i;
     316             :         }
     317             : 
     318       64263 :         ac->controls = talloc_array(ac, struct ldb_control *, num_ctrls +1);
     319       64263 :         if (ac->controls == NULL) {
     320           0 :                 ret = LDB_ERR_OPERATIONS_ERROR;
     321           0 :                 return ldb_module_done(
     322             :                         ac->req, ac->controls, response, ret);
     323             :         }
     324       64263 :         ac->controls[num_ctrls] = NULL;
     325             : 
     326       64264 :         for (i = 0; i < (num_ctrls -1); i++) {
     327           1 :                 ac->controls[i] = talloc_reference(ac->controls,
     328             :                                                    ac->store->controls[i]);
     329             :         }
     330             : 
     331       64263 :         ac->controls[i] = talloc(ac->controls, struct ldb_control);
     332       64263 :         if (ac->controls[i] == NULL) {
     333           0 :                 ret = LDB_ERR_OPERATIONS_ERROR;
     334           0 :                 return ldb_module_done(
     335             :                         ac->req, ac->controls, response, ret);
     336             :         }
     337             : 
     338       64263 :         ac->controls[i]->oid = talloc_strdup(ac->controls[i],
     339             :                                                 LDB_CONTROL_PAGED_RESULTS_OID);
     340       64263 :         if (ac->controls[i]->oid == NULL) {
     341           0 :                 ret = LDB_ERR_OPERATIONS_ERROR;
     342           0 :                 return ldb_module_done(
     343             :                         ac->req, ac->controls, response, ret);
     344             :         }
     345             : 
     346       64263 :         ac->controls[i]->critical = 0;
     347             : 
     348       64263 :         paged = talloc(ac->controls[i], struct ldb_paged_control);
     349       64263 :         if (paged == NULL) {
     350           0 :                 ret = LDB_ERR_OPERATIONS_ERROR;
     351           0 :                 return ldb_module_done(
     352             :                         ac->req, ac->controls, response, ret);
     353             :         }
     354             : 
     355       64263 :         ac->controls[i]->data = paged;
     356             : 
     357       64263 :         if (ac->size > 0) {
     358       63838 :                 paged->size = 0;
     359       63838 :                 paged->cookie = NULL;
     360       63838 :                 paged->cookie_len = 0;
     361             :         } else {
     362         425 :                 paged->size = ac->store->num_entries;
     363         425 :                 paged->cookie = talloc_strdup(paged, ac->store->cookie);
     364         425 :                 paged->cookie_len = strlen(paged->cookie) + 1;
     365             :         }
     366             : 
     367       64241 :         return LDB_SUCCESS;
     368             : }
     369             : 
     370       37095 : static int save_referral(struct results_store *store, char *ref)
     371             : {
     372       37095 :         struct referral_store *node = talloc(store,
     373             :                                              struct referral_store);
     374       37095 :         if (node == NULL) {
     375           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     376             :         }
     377       37095 :         node->next = NULL;
     378       37095 :         node->ref = talloc_steal(node, ref);
     379             : 
     380       37095 :         if (store->first_ref == NULL) {
     381       14481 :                 store->first_ref = node;
     382             :         } else {
     383       22614 :                 store->last_ref->next = node;
     384             :         }
     385       37095 :         store->last_ref = node;
     386       37095 :         return LDB_SUCCESS;
     387             : }
     388             : 
     389      740492 : static int paged_search_callback(struct ldb_request *req,
     390             :                                  struct ldb_reply *ares)
     391             : {
     392       80445 :         struct paged_context *ac;
     393       80445 :         struct results_store *store;
     394       80445 :         int ret;
     395       80445 :         const struct ldb_val *guid_blob;
     396       80445 :         struct GUID guid;
     397       80445 :         NTSTATUS status;
     398             : 
     399      740492 :         ac = talloc_get_type(req->context, struct paged_context);
     400      740492 :         store = ac->store;
     401             : 
     402      740492 :         if (!ares) {
     403           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     404             :                                         LDB_ERR_OPERATIONS_ERROR);
     405             :         }
     406      740492 :         if (ares->error != LDB_SUCCESS) {
     407         156 :                 return ldb_module_done(ac->req, ares->controls,
     408             :                                         ares->response, ares->error);
     409             :         }
     410             : 
     411      740336 :         switch (ares->type) {
     412      639251 :         case LDB_REPLY_ENTRY:
     413      639251 :                 if (store->results == NULL) {
     414       63648 :                         store->num_entries = 0;
     415       63648 :                         store->result_array_size = 16;
     416       63648 :                         store->results = talloc_array(store, struct GUID,
     417             :                                                      store->result_array_size);
     418       63648 :                         if (store->results == NULL) {
     419           0 :                                 return ldb_module_done(ac->req, NULL, NULL,
     420             :                                                      LDB_ERR_OPERATIONS_ERROR);
     421             :                         }
     422      575603 :                 } else if (store->num_entries == store->result_array_size) {
     423        1882 :                         if (store->result_array_size > INT_MAX/2) {
     424           0 :                                 return ldb_module_done(ac->req, NULL, NULL,
     425             :                                                      LDB_ERR_OPERATIONS_ERROR);
     426             :                         }
     427        1882 :                         store->result_array_size *= 2;
     428        1882 :                         store->results = talloc_realloc(store, store->results,
     429             :                                                         struct GUID,
     430             :                                                 store->result_array_size);
     431        1882 :                         if (store->results == NULL) {
     432           0 :                                 return ldb_module_done(ac->req, NULL, NULL,
     433             :                                                      LDB_ERR_OPERATIONS_ERROR);
     434             :                         }
     435             :                 }
     436             : 
     437      639251 :                 guid_blob = ldb_dn_get_extended_component(ares->message->dn,
     438             :                                                           "GUID");
     439      639251 :                 if (guid_blob == NULL) {
     440           0 :                         return ldb_module_done(ac->req, NULL, NULL,
     441             :                                                LDB_ERR_OPERATIONS_ERROR);
     442             :                 }
     443      639251 :                 status = GUID_from_ndr_blob(guid_blob, &guid);
     444      639251 :                 if (!NT_STATUS_IS_OK(status)) {
     445           0 :                         return ldb_module_done(ac->req, NULL, NULL,
     446             :                                                LDB_ERR_OPERATIONS_ERROR);
     447             :                 }
     448             : 
     449             :                 /* Redundant paranoid check */
     450      639251 :                 if (store->num_entries > store->result_array_size) {
     451           0 :                         return ldb_module_done(ac->req, NULL, NULL,
     452             :                                                LDB_ERR_OPERATIONS_ERROR);
     453             :                 }
     454             : 
     455      639251 :                 store->results[store->num_entries] = guid;
     456      639251 :                 store->num_entries++;
     457      639251 :                 break;
     458             : 
     459       37095 :         case LDB_REPLY_REFERRAL:
     460       37095 :                 ret = save_referral(store, ares->referral);
     461       37095 :                 if (ret != LDB_SUCCESS) {
     462           0 :                         return ldb_module_done(ac->req, NULL, NULL, ret);
     463             :                 }
     464       37095 :                 break;
     465             : 
     466       63990 :         case LDB_REPLY_DONE:
     467       63990 :                 if (store->num_entries != 0) {
     468       63648 :                         store->results = talloc_realloc(store, store->results,
     469             :                                                         struct GUID,
     470             :                                                         store->num_entries);
     471       63648 :                         if (store->results == NULL) {
     472           0 :                                 return ldb_module_done(ac->req, NULL, NULL,
     473             :                                                      LDB_ERR_OPERATIONS_ERROR);
     474             :                         }
     475             :                 }
     476       63990 :                 store->result_array_size = store->num_entries;
     477             : 
     478       63990 :                 ac->store->controls = talloc_move(ac->store, &ares->controls);
     479       63990 :                 ret = paged_results(ac, ares);
     480       63990 :                 if (ret != LDB_SUCCESS) {
     481             :                         /* paged_results will have called ldb_module_done
     482             :                          * if an error occurred
     483             :                          */
     484           0 :                         return ret;
     485             :                 }
     486       63990 :                 return ldb_module_done(ac->req, ac->controls,
     487             :                                         ares->response, ret);
     488             :         }
     489             : 
     490      595923 :         return LDB_SUCCESS;
     491             : }
     492             : 
     493             : static struct ldb_control **
     494       64146 : paged_results_copy_down_controls(TALLOC_CTX *mem_ctx,
     495             :                                  struct ldb_control **controls)
     496             : {
     497             : 
     498          22 :         struct ldb_control **new_controls;
     499          22 :         unsigned int i, j, num_ctrls;
     500       64146 :         if (controls == NULL) {
     501           0 :                 return NULL;
     502             :         }
     503             : 
     504      219409 :         for (num_ctrls = 0; controls[num_ctrls]; num_ctrls++);
     505             : 
     506       64146 :         new_controls = talloc_array(mem_ctx, struct ldb_control *, num_ctrls);
     507       64146 :         if (new_controls == NULL) {
     508           0 :                 return NULL;
     509             :         }
     510             : 
     511      219409 :         for (j = 0, i = 0; i < (num_ctrls); i++) {
     512      155263 :                 struct ldb_control *control = controls[i];
     513      155263 :                 if (control->oid == NULL) {
     514       64002 :                         continue;
     515             :                 }
     516       91261 :                 if (strcmp(control->oid, LDB_CONTROL_PAGED_RESULTS_OID) == 0) {
     517       64146 :                         continue;
     518             :                 }
     519             :                 /*
     520             :                  * ASQ changes everything, do not copy it down for the
     521             :                  * per-GUID search
     522             :                  */
     523       27115 :                 if (strcmp(control->oid, LDB_CONTROL_ASQ_OID) == 0) {
     524           1 :                         continue;
     525             :                 }
     526       27114 :                 new_controls[j] = talloc_steal(new_controls, control);
     527             : 
     528             :                 /*
     529             :                  * Sadly the caller is not obliged to make this a
     530             :                  * proper talloc tree, so we do so here.
     531             :                  */
     532       27114 :                 if (control->data) {
     533       13057 :                         talloc_steal(control, control->data);
     534             :                 }
     535       27114 :                 j++;
     536             :         }
     537       64146 :         new_controls[j] = NULL;
     538       64146 :         return new_controls;
     539             : }
     540             : 
     541       42377 : static const char * const *paged_copy_attrs(TALLOC_CTX *mem_ctx,
     542             :                                             const char * const *attrs) {
     543          22 :         int i;
     544          22 :         const char **new_attrs;
     545       42377 :         if (attrs == NULL) {
     546           0 :                 return NULL;
     547             :         }
     548       42377 :         new_attrs = ldb_attr_list_copy(mem_ctx, attrs);
     549             : 
     550      230117 :         for (i=0; attrs[i] != NULL; i++) {
     551      187718 :                 new_attrs[i] = talloc_strdup(mem_ctx, attrs[i]);
     552             :         }
     553       42377 :         new_attrs[i] = NULL;
     554       42377 :         return new_attrs;
     555             : }
     556             : 
     557             : /*
     558             :  * Check if two sets of controls are the same except for the paged results
     559             :  * control in the request controls.  This function is messy because request
     560             :  * control lists can contain controls that were NULL'd by the rootdse.  We
     561             :  * must ignore those entries.  This function is not portable.
     562             :  */
     563         278 : static bool paged_controls_same(struct ldb_request *req,
     564             :                                 struct ldb_control **down_controls) {
     565           0 :         int i;
     566           0 :         unsigned int num_down_controls, num_non_null_req_controls;
     567           0 :         struct ldb_control *ctrl;
     568             : 
     569         278 :         num_down_controls = 0;
     570         301 :         for (i=0; down_controls[i] != NULL; i++) {
     571          26 :                 num_down_controls++;
     572             : 
     573          26 :                 ctrl = ldb_request_get_control(req, down_controls[i]->oid);
     574          26 :                 if (ctrl == NULL) {
     575           3 :                         return false;
     576             :                 }
     577             :         }
     578             : 
     579         275 :         num_non_null_req_controls = 0;
     580         846 :         for (i=0; req->controls[i] != NULL; i++) {
     581         571 :                 if (req->controls[i]->oid != NULL &&
     582         300 :                     strcmp(req->controls[i]->oid,
     583             :                            LDB_CONTROL_ASQ_OID) != 0) {
     584         300 :                         num_non_null_req_controls++;
     585             :                 }
     586             :         }
     587             : 
     588             :         /* At this point we have the number of non-null entries for both
     589             :          * control lists and we know that:
     590             :          * 1. down_controls does not contain the paged control or ASQ
     591             :          *      (because paged_results_copy_down_controls excludes it)
     592             :          * 2. req->controls does contain the paged control
     593             :          *      (because this function is only called if this is true)
     594             :          * 3. down_controls is a subset of non-null controls in req->controls
     595             :          *      (checked above)
     596             :          * So to confirm that the two lists are identical except for the paged
     597             :          * control and possibly ASQ, all we need to check is: */
     598         275 :         if (num_non_null_req_controls == num_down_controls + 1) {
     599         273 :                 return true;
     600             :         }
     601           2 :         return false;
     602             : }
     603             : 
     604         273 : static bool paged_attrs_same(const char * const *attrs_1,
     605             :                              const char * const *attrs_2) {
     606           0 :         int i;
     607         273 :         if (attrs_1 == NULL || attrs_2 == NULL) {
     608         156 :                 if (attrs_1 == NULL && attrs_2 == NULL) {
     609         156 :                         return true;
     610             :                 }
     611           0 :                 return false;
     612             :         }
     613             : 
     614         335 :         for (i=0; attrs_1[i] != NULL; i++) {
     615         218 :                if (!ldb_attr_in_list(attrs_2, attrs_1[i])) {
     616           0 :                        return false;
     617             :                }
     618             :         }
     619         117 :         return true;
     620             : }
     621             : 
     622    19768037 : static int paged_search(struct ldb_module *module, struct ldb_request *req)
     623             : {
     624     1147004 :         struct ldb_context *ldb;
     625     1147004 :         struct ldb_control *control;
     626     1147004 :         struct ldb_control *vlv_control;
     627     1147004 :         struct private_data *private_data;
     628     1147004 :         struct ldb_paged_control *paged_ctrl;
     629     1147004 :         struct ldb_request *search_req;
     630     1147004 :         struct paged_context *ac;
     631     1147004 :         int ret;
     632             : 
     633    19768037 :         ldb = ldb_module_get_ctx(module);
     634             : 
     635             :         /* check if there's a paged request control */
     636    19768037 :         control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID);
     637    19768037 :         if (control == NULL) {
     638             :                 /* not found go on */
     639    19703610 :                 return ldb_next_request(module, req);
     640             :         }
     641             : 
     642       64427 :         paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
     643       64427 :         if (!paged_ctrl) {
     644           0 :                 return LDB_ERR_PROTOCOL_ERROR;
     645             :         }
     646             : 
     647       64427 :         private_data = talloc_get_type(ldb_module_get_private(module),
     648             :                                         struct private_data);
     649             : 
     650       64427 :         vlv_control = ldb_request_get_control(req, LDB_CONTROL_VLV_REQ_OID);
     651       64427 :         if (vlv_control != NULL) {
     652             :                 /*
     653             :                  * VLV and paged_results are not allowed at the same
     654             :                  * time
     655             :                  */
     656           2 :                 return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
     657             :         }
     658             : 
     659       64425 :         ac = talloc_zero(req, struct paged_context);
     660       64425 :         if (ac == NULL) {
     661           0 :                 ldb_set_errstring(ldb, "Out of Memory");
     662           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     663             :         }
     664             : 
     665       64425 :         ac->module = module;
     666       64425 :         ac->req = req;
     667       64425 :         ac->size = paged_ctrl->size;
     668       64425 :         if (ac->size < 0) {
     669             :                 /*
     670             :                  * Apparently some clients send more than 2^31. This
     671             :                  * violates the ldap standard, but we need to cope.
     672             :                  * In the future, if maximum result sizes are implemented in
     673             :                  * Samba, we should also clamp the page size to the maximum
     674             :                  * result size.
     675             :                  */
     676           0 :                 ac->size = 0x7FFFFFFF;
     677             :         }
     678             : 
     679             :         /* check if it is a continuation search the store */
     680       64425 :         if (paged_ctrl->cookie_len == 0) {
     681          22 :                 struct ldb_control *ext_ctrl;
     682          22 :                 struct ldb_control **controls;
     683          22 :                 static const char * const attrs[1] = { NULL };
     684       64146 :                 void *ref = NULL;
     685             : 
     686       64146 :                 if (paged_ctrl->size == 0) {
     687           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     688             :                 }
     689             : 
     690       64146 :                 ac->store = new_store(private_data);
     691       64146 :                 if (ac->store == NULL) {
     692           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     693             :                 }
     694             : 
     695       64146 :                 controls = req->controls;
     696       64146 :                 ext_ctrl = ldb_request_get_control(req,
     697             :                                         LDB_CONTROL_EXTENDED_DN_OID);
     698       64146 :                 if (ext_ctrl == NULL) {
     699             :                         /*
     700             :                          * Add extended_dn control to the request if there
     701             :                          * isn't already one.  We'll get the GUID out of it in
     702             :                          * the callback.  This is a workaround for the case
     703             :                          * where ntsecuritydescriptor forbids fetching GUIDs
     704             :                          * for the current user.
     705             :                          */
     706          22 :                         struct ldb_request *req_extended_dn;
     707          22 :                         struct ldb_extended_dn_control *ext_ctrl_data;
     708       52445 :                         req_extended_dn = talloc_zero(req, struct ldb_request);
     709       52445 :                         if (req_extended_dn == NULL) {
     710           0 :                                 return ldb_module_oom(module);
     711             :                         }
     712       52445 :                         req_extended_dn->controls = req->controls;
     713       52445 :                         ext_ctrl_data = talloc_zero(req,
     714             :                                         struct ldb_extended_dn_control);
     715       52445 :                         if (ext_ctrl_data == NULL) {
     716           0 :                                 return ldb_module_oom(module);
     717             :                         }
     718       52445 :                         ext_ctrl_data->type = 1;
     719             : 
     720       52445 :                         ret = ldb_request_add_control(req_extended_dn,
     721             :                                               LDB_CONTROL_EXTENDED_DN_OID,
     722             :                                                       true,
     723             :                                                       ext_ctrl_data);
     724       52445 :                         if (ret != LDB_SUCCESS) {
     725           0 :                                 return ret;
     726             :                         }
     727       52445 :                         controls = req_extended_dn->controls;
     728             :                 }
     729             : 
     730       64146 :                 ret = ldb_build_search_req_ex(&search_req, ldb, ac,
     731             :                                                 req->op.search.base,
     732             :                                                 req->op.search.scope,
     733             :                                                 req->op.search.tree,
     734             :                                                 attrs,
     735             :                                                 controls,
     736             :                                                 ac,
     737             :                                                 paged_search_callback,
     738             :                                                 req);
     739       64146 :                 if (ret != LDB_SUCCESS) {
     740           0 :                         return ret;
     741             :                 }
     742             : 
     743             :                 /*
     744             :                  * LDB does not have a function to take a full copy of
     745             :                  * this, but at least take a shallow copy
     746             :                  */
     747      128292 :                 ac->store->expr = ldb_parse_tree_copy_shallow(ac->store,
     748       64146 :                                                               req->op.search.tree);
     749             : 
     750       64146 :                 if (ac->store->expr == NULL) {
     751           0 :                         return ldb_operr(ldb);
     752             :                 }
     753             : 
     754             :                 /*
     755             :                  * As the above is only a shallow copy, take a
     756             :                  * reference to ensure the values are kept around
     757             :                  */
     758       64146 :                 ref = talloc_reference(ac->store, req->op.search.tree);
     759       64146 :                 if (ref == NULL) {
     760           0 :                         return ldb_module_oom(module);
     761             :                 }
     762      128292 :                 ac->store->expr_str = ldb_filter_from_tree(ac->store,
     763       64146 :                                                           req->op.search.tree);
     764       64146 :                 if (ac->store->expr_str == NULL) {
     765           0 :                         return ldb_module_oom(module);
     766             :                 }
     767       64146 :                 if (req->op.search.attrs != NULL) {
     768       42377 :                         ac->store->attrs = paged_copy_attrs(ac->store,
     769             :                                                             req->op.search.attrs);
     770       42377 :                         if (ac->store->attrs == NULL) {
     771           0 :                                 return ldb_module_oom(module);
     772             :                         }
     773             :                 }
     774             : 
     775             :                 /* save it locally and remove it from the list */
     776             :                 /* we do not need to replace them later as we
     777             :                  * are keeping the original req intact */
     778       64146 :                 if (!ldb_save_controls(control, search_req, NULL)) {
     779           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     780             :                 }
     781      128292 :                 ac->store->down_controls =
     782       64146 :                     paged_results_copy_down_controls(ac->store, req->controls);
     783       64146 :                 if (ac->store->down_controls == NULL) {
     784           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     785             :                 }
     786             : 
     787       64146 :                 return ldb_next_request(module, search_req);
     788             : 
     789             :         } else {
     790         279 :                 struct results_store *current = NULL;
     791           0 :                 char *expr_str;
     792           0 :                 bool bool_ret;
     793             : 
     794             :                 /* TODO: age out old outstanding requests */
     795         293 :                 for (current = private_data->store; current != NULL;
     796          14 :                      current = current->next) {
     797         293 :                         if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
     798         279 :                                 current->timestamp = time(NULL);
     799         279 :                                 break;
     800             :                         }
     801             :                 }
     802         279 :                 if (current == NULL) {
     803           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     804             :                 }
     805             : 
     806             :                 /* Get the expression string and make sure it didn't change */
     807         279 :                 expr_str = ldb_filter_from_tree(ac, req->op.search.tree);
     808         279 :                 if (expr_str == NULL) {
     809           0 :                         return LDB_ERR_OPERATIONS_ERROR;
     810             :                 }
     811             : 
     812         279 :                 ret = strcmp(current->expr_str, expr_str);
     813         279 :                 if (ret != 0) {
     814           1 :                         return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
     815             :                 }
     816             : 
     817         278 :                 bool_ret = paged_controls_same(req, current->down_controls);
     818         278 :                 if (bool_ret == false) {
     819           5 :                         return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
     820             :                 }
     821             : 
     822         273 :                 bool_ret = paged_attrs_same(req->op.search.attrs,
     823             :                                             current->attrs);
     824         273 :                 if (bool_ret == false) {
     825           0 :                         return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
     826             :                 }
     827             : 
     828         273 :                 DLIST_PROMOTE(private_data->store, current);
     829             : 
     830         273 :                 ac->store = current;
     831             : 
     832             :                 /* check if it is an abandon */
     833         273 :                 if (ac->size == 0) {
     834           0 :                         return ldb_module_done(req, NULL, NULL,
     835             :                                                                 LDB_SUCCESS);
     836             :                 }
     837             : 
     838         273 :                 ret = paged_results(ac, NULL);
     839         273 :                 if (ret != LDB_SUCCESS) {
     840             :                         /*
     841             :                          * paged_results() will have called ldb_module_done
     842             :                          * if an error occurred
     843             :                          */
     844           0 :                         return ret;
     845             :                 }
     846         273 :                 return ldb_module_done(req, ac->controls, NULL, LDB_SUCCESS);
     847             :         }
     848             : }
     849             : 
     850      180751 : static int paged_request_init(struct ldb_module *module)
     851             : {
     852        6019 :         struct ldb_context *ldb;
     853        6019 :         struct private_data *data;
     854        6019 :         int ret;
     855             : 
     856      180751 :         ldb = ldb_module_get_ctx(module);
     857             : 
     858      180751 :         data = talloc(module, struct private_data);
     859      180751 :         if (data == NULL) {
     860           0 :                 return LDB_ERR_OTHER;
     861             :         }
     862             : 
     863      180751 :         data->next_free_id = 1;
     864      180751 :         data->num_stores = 0;
     865      180751 :         data->store = NULL;
     866      180751 :         ldb_module_set_private(module, data);
     867             : 
     868      180751 :         ret = ldb_mod_register_control(module, LDB_CONTROL_PAGED_RESULTS_OID);
     869      180751 :         if (ret != LDB_SUCCESS) {
     870           0 :                 ldb_debug(ldb, LDB_DEBUG_WARNING,
     871             :                         "paged_results:"
     872             :                         "Unable to register control with rootdse!");
     873             :         }
     874             : 
     875      180751 :         return ldb_next_init(module);
     876             : }
     877             : 
     878             : static const struct ldb_module_ops ldb_paged_results_module_ops = {
     879             :         .name           = "dsdb_paged_results",
     880             :         .search         = paged_search,
     881             :         .init_context   = paged_request_init
     882             : };
     883             : 
     884        5903 : int ldb_dsdb_paged_results_init(const char *version)
     885             : {
     886        5903 :         LDB_MODULE_CHECK_VERSION(version);
     887        5903 :         return ldb_register_module(&ldb_paged_results_module_ops);
     888             : }

Generated by: LCOV version 1.14