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 : }
|