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 67748 : static int store_destructor(struct results_store *del)
95 : {
96 67748 : struct private_data *priv = del->priv;
97 67748 : DLIST_REMOVE(priv->store, del);
98 :
99 67748 : priv->num_stores -= 1;
100 :
101 67748 : return 0;
102 : }
103 :
104 67748 : static struct results_store *new_store(struct private_data *priv)
105 : {
106 22 : struct results_store *newr;
107 67748 : 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 67748 : newr = talloc_zero(priv, struct results_store);
114 67748 : if (!newr) return NULL;
115 :
116 67748 : newr->priv = priv;
117 :
118 67748 : newr->cookie = talloc_asprintf(newr, "%d", new_id);
119 67748 : if (!newr->cookie) {
120 0 : talloc_free(newr);
121 0 : return NULL;
122 : }
123 :
124 67748 : newr->timestamp = time(NULL);
125 :
126 67748 : DLIST_ADD(priv->store, newr);
127 :
128 67748 : priv->num_stores += 1;
129 :
130 67748 : talloc_set_destructor(newr, store_destructor);
131 :
132 67748 : 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 56895 : last = DLIST_TAIL(priv->store);
139 56895 : TALLOC_FREE(last);
140 : }
141 :
142 67726 : 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 14480 : 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 51572 : while (store->first_ref != NULL) {
160 37092 : node = store->first_ref;
161 37092 : ret = ldb_module_send_referral(req, node->ref);
162 37092 : if (ret != LDB_SUCCESS) {
163 0 : return ret;
164 : }
165 37092 : store->first_ref = node->next;
166 37092 : talloc_free(node);
167 : }
168 14480 : return LDB_SUCCESS;
169 : }
170 :
171 : /* Start an ldb request for a single object by GUID */
172 146547 : 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 146547 : struct ldb_control **controls = ac->store->down_controls;
187 :
188 146547 : struct ldb_context *ldb = ldb_module_get_ctx(module);
189 :
190 146547 : dn = ldb_dn_new_fmt(ac, ldb, "<GUID=%s>",
191 : GUID_buf_string(guid, &guid_str));
192 146547 : if (dn == NULL) {
193 0 : return ldb_oom(ldb);
194 : }
195 :
196 146547 : res = talloc_zero(ac, struct ldb_result);
197 146547 : if (res == NULL) {
198 0 : TALLOC_FREE(dn);
199 0 : return ldb_oom(ldb);
200 : }
201 :
202 146547 : 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 146547 : 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 146547 : talloc_steal(req, dn);
224 :
225 146547 : ret = ldb_request(ldb, req);
226 146547 : if (ret == LDB_SUCCESS) {
227 146547 : ret = ldb_wait(req->handle, LDB_WAIT_ALL);
228 : }
229 :
230 146547 : talloc_free(req);
231 146547 : if (ret != LDB_SUCCESS) {
232 2 : talloc_free(res);
233 2 : return ret;
234 : }
235 :
236 146545 : *result = res;
237 146545 : return ret;
238 : }
239 :
240 67865 : static int paged_results(struct paged_context *ac, struct ldb_reply *ares)
241 : {
242 67865 : 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 67865 : 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 214412 : while (ac->store->last_i < ac->store->num_entries && ac->size > 0) {
254 146547 : struct GUID *guid = &ac->store->results[ac->store->last_i++];
255 146547 : struct ldb_result *result = NULL;
256 :
257 146547 : 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 146569 : ret = paged_search_by_dn_guid(ac->module, ac, &result, guid,
270 146547 : ac->req->op.search.attrs,
271 146525 : ac->store->expr);
272 146547 : if (ret == LDAP_NO_SUCH_OBJECT ||
273 146545 : (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 146540 : } else if (ret != LDB_SUCCESS) {
279 0 : return ldb_module_done(
280 : ac->req, ac->controls, response, ret);
281 : }
282 :
283 146540 : ret = ldb_module_send_entry(ac->req, result->msgs[0],
284 : NULL);
285 146540 : 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 67865 : 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 14480 : ret = send_referrals(ac->store, ac->req);
299 14480 : 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 67865 : num_ctrls = 1;
310 67865 : i = 0;
311 :
312 67865 : if (ac->store->controls != NULL) {
313 2 : while (ac->store->controls[i]) i++; /* counting */
314 :
315 1 : num_ctrls += i;
316 : }
317 :
318 67865 : ac->controls = talloc_array(ac, struct ldb_control *, num_ctrls +1);
319 67865 : 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 67865 : ac->controls[num_ctrls] = NULL;
325 :
326 67866 : 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 67865 : ac->controls[i] = talloc(ac->controls, struct ldb_control);
332 67865 : 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 67865 : ac->controls[i]->oid = talloc_strdup(ac->controls[i],
339 : LDB_CONTROL_PAGED_RESULTS_OID);
340 67865 : 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 67865 : ac->controls[i]->critical = 0;
347 :
348 67865 : paged = talloc(ac->controls[i], struct ldb_paged_control);
349 67865 : 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 67865 : ac->controls[i]->data = paged;
356 :
357 67865 : if (ac->size > 0) {
358 67440 : paged->size = 0;
359 67440 : paged->cookie = NULL;
360 67440 : 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 67843 : return LDB_SUCCESS;
368 : }
369 :
370 37092 : static int save_referral(struct results_store *store, char *ref)
371 : {
372 37092 : struct referral_store *node = talloc(store,
373 : struct referral_store);
374 37092 : if (node == NULL) {
375 0 : return LDB_ERR_OPERATIONS_ERROR;
376 : }
377 37092 : node->next = NULL;
378 37092 : node->ref = talloc_steal(node, ref);
379 :
380 37092 : if (store->first_ref == NULL) {
381 14480 : store->first_ref = node;
382 : } else {
383 22612 : store->last_ref->next = node;
384 : }
385 37092 : store->last_ref = node;
386 37092 : return LDB_SUCCESS;
387 : }
388 :
389 747683 : 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 747683 : ac = talloc_get_type(req->context, struct paged_context);
400 747683 : store = ac->store;
401 :
402 747683 : if (!ares) {
403 0 : return ldb_module_done(ac->req, NULL, NULL,
404 : LDB_ERR_OPERATIONS_ERROR);
405 : }
406 747683 : if (ares->error != LDB_SUCCESS) {
407 156 : return ldb_module_done(ac->req, ares->controls,
408 : ares->response, ares->error);
409 : }
410 :
411 747527 : switch (ares->type) {
412 642843 : case LDB_REPLY_ENTRY:
413 642843 : if (store->results == NULL) {
414 67250 : store->num_entries = 0;
415 67250 : store->result_array_size = 16;
416 67250 : store->results = talloc_array(store, struct GUID,
417 : store->result_array_size);
418 67250 : if (store->results == NULL) {
419 0 : return ldb_module_done(ac->req, NULL, NULL,
420 : LDB_ERR_OPERATIONS_ERROR);
421 : }
422 575593 : } 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 642843 : guid_blob = ldb_dn_get_extended_component(ares->message->dn,
438 : "GUID");
439 642843 : if (guid_blob == NULL) {
440 0 : return ldb_module_done(ac->req, NULL, NULL,
441 : LDB_ERR_OPERATIONS_ERROR);
442 : }
443 642843 : status = GUID_from_ndr_blob(guid_blob, &guid);
444 642843 : 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 642843 : 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 642843 : store->results[store->num_entries] = guid;
456 642843 : store->num_entries++;
457 642843 : break;
458 :
459 37092 : case LDB_REPLY_REFERRAL:
460 37092 : ret = save_referral(store, ares->referral);
461 37092 : if (ret != LDB_SUCCESS) {
462 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
463 : }
464 37092 : break;
465 :
466 67592 : case LDB_REPLY_DONE:
467 67592 : if (store->num_entries != 0) {
468 67250 : store->results = talloc_realloc(store, store->results,
469 : struct GUID,
470 : store->num_entries);
471 67250 : if (store->results == NULL) {
472 0 : return ldb_module_done(ac->req, NULL, NULL,
473 : LDB_ERR_OPERATIONS_ERROR);
474 : }
475 : }
476 67592 : store->result_array_size = store->num_entries;
477 :
478 67592 : ac->store->controls = talloc_move(ac->store, &ares->controls);
479 67592 : ret = paged_results(ac, ares);
480 67592 : 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 67592 : return ldb_module_done(ac->req, ac->controls,
487 : ares->response, ret);
488 : }
489 :
490 599512 : return LDB_SUCCESS;
491 : }
492 :
493 : static struct ldb_control **
494 67748 : 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 67748 : if (controls == NULL) {
501 0 : return NULL;
502 : }
503 :
504 237404 : for (num_ctrls = 0; controls[num_ctrls]; num_ctrls++);
505 :
506 67748 : new_controls = talloc_array(mem_ctx, struct ldb_control *, num_ctrls);
507 67748 : if (new_controls == NULL) {
508 0 : return NULL;
509 : }
510 :
511 237404 : for (j = 0, i = 0; i < (num_ctrls); i++) {
512 169656 : struct ldb_control *control = controls[i];
513 169656 : if (control->oid == NULL) {
514 67604 : continue;
515 : }
516 102052 : if (strcmp(control->oid, LDB_CONTROL_PAGED_RESULTS_OID) == 0) {
517 67748 : continue;
518 : }
519 : /*
520 : * ASQ changes everything, do not copy it down for the
521 : * per-GUID search
522 : */
523 34304 : if (strcmp(control->oid, LDB_CONTROL_ASQ_OID) == 0) {
524 1 : continue;
525 : }
526 34303 : 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 34303 : if (control->data) {
533 13057 : talloc_steal(control, control->data);
534 : }
535 34303 : j++;
536 : }
537 67748 : new_controls[j] = NULL;
538 67748 : return new_controls;
539 : }
540 :
541 42387 : 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 42387 : if (attrs == NULL) {
546 0 : return NULL;
547 : }
548 42387 : new_attrs = ldb_attr_list_copy(mem_ctx, attrs);
549 :
550 230167 : for (i=0; attrs[i] != NULL; i++) {
551 187758 : new_attrs[i] = talloc_strdup(mem_ctx, attrs[i]);
552 : }
553 42387 : new_attrs[i] = NULL;
554 42387 : 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 20015700 : static int paged_search(struct ldb_module *module, struct ldb_request *req)
623 : {
624 1144731 : struct ldb_context *ldb;
625 1144731 : struct ldb_control *control;
626 1144731 : struct ldb_control *vlv_control;
627 1144731 : struct private_data *private_data;
628 1144731 : struct ldb_paged_control *paged_ctrl;
629 1144731 : struct ldb_request *search_req;
630 1144731 : struct paged_context *ac;
631 1144731 : int ret;
632 :
633 20015700 : ldb = ldb_module_get_ctx(module);
634 :
635 : /* check if there's a paged request control */
636 20015700 : control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID);
637 20015700 : if (control == NULL) {
638 : /* not found go on */
639 19947671 : return ldb_next_request(module, req);
640 : }
641 :
642 68029 : paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
643 68029 : if (!paged_ctrl) {
644 0 : return LDB_ERR_PROTOCOL_ERROR;
645 : }
646 :
647 68029 : private_data = talloc_get_type(ldb_module_get_private(module),
648 : struct private_data);
649 :
650 68029 : vlv_control = ldb_request_get_control(req, LDB_CONTROL_VLV_REQ_OID);
651 68029 : 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 68027 : ac = talloc_zero(req, struct paged_context);
660 68027 : if (ac == NULL) {
661 0 : ldb_set_errstring(ldb, "Out of Memory");
662 0 : return LDB_ERR_OPERATIONS_ERROR;
663 : }
664 :
665 68027 : ac->module = module;
666 68027 : ac->req = req;
667 68027 : ac->size = paged_ctrl->size;
668 68027 : 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 68027 : 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 67748 : void *ref = NULL;
685 :
686 67748 : if (paged_ctrl->size == 0) {
687 0 : return LDB_ERR_OPERATIONS_ERROR;
688 : }
689 :
690 67748 : ac->store = new_store(private_data);
691 67748 : if (ac->store == NULL) {
692 0 : return LDB_ERR_OPERATIONS_ERROR;
693 : }
694 :
695 67748 : controls = req->controls;
696 67748 : ext_ctrl = ldb_request_get_control(req,
697 : LDB_CONTROL_EXTENDED_DN_OID);
698 67748 : 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 56043 : req_extended_dn = talloc_zero(req, struct ldb_request);
709 56043 : if (req_extended_dn == NULL) {
710 0 : return ldb_module_oom(module);
711 : }
712 56043 : req_extended_dn->controls = req->controls;
713 56043 : ext_ctrl_data = talloc_zero(req,
714 : struct ldb_extended_dn_control);
715 56043 : if (ext_ctrl_data == NULL) {
716 0 : return ldb_module_oom(module);
717 : }
718 56043 : ext_ctrl_data->type = 1;
719 :
720 56043 : ret = ldb_request_add_control(req_extended_dn,
721 : LDB_CONTROL_EXTENDED_DN_OID,
722 : true,
723 : ext_ctrl_data);
724 56043 : if (ret != LDB_SUCCESS) {
725 0 : return ret;
726 : }
727 56043 : controls = req_extended_dn->controls;
728 : }
729 :
730 67748 : 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 67748 : 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 135496 : ac->store->expr = ldb_parse_tree_copy_shallow(ac->store,
748 67748 : req->op.search.tree);
749 :
750 67748 : 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 67748 : ref = talloc_reference(ac->store, req->op.search.tree);
759 67748 : if (ref == NULL) {
760 0 : return ldb_module_oom(module);
761 : }
762 135496 : ac->store->expr_str = ldb_filter_from_tree(ac->store,
763 67748 : req->op.search.tree);
764 67748 : if (ac->store->expr_str == NULL) {
765 0 : return ldb_module_oom(module);
766 : }
767 67748 : if (req->op.search.attrs != NULL) {
768 42387 : ac->store->attrs = paged_copy_attrs(ac->store,
769 : req->op.search.attrs);
770 42387 : 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 67748 : if (!ldb_save_controls(control, search_req, NULL)) {
779 0 : return LDB_ERR_OPERATIONS_ERROR;
780 : }
781 135496 : ac->store->down_controls =
782 67748 : paged_results_copy_down_controls(ac->store, req->controls);
783 67748 : if (ac->store->down_controls == NULL) {
784 0 : return LDB_ERR_OPERATIONS_ERROR;
785 : }
786 :
787 67748 : 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 182015 : static int paged_request_init(struct ldb_module *module)
851 : {
852 6027 : struct ldb_context *ldb;
853 6027 : struct private_data *data;
854 6027 : int ret;
855 :
856 182015 : ldb = ldb_module_get_ctx(module);
857 :
858 182015 : data = talloc(module, struct private_data);
859 182015 : if (data == NULL) {
860 0 : return LDB_ERR_OTHER;
861 : }
862 :
863 182015 : data->next_free_id = 1;
864 182015 : data->num_stores = 0;
865 182015 : data->store = NULL;
866 182015 : ldb_module_set_private(module, data);
867 :
868 182015 : ret = ldb_mod_register_control(module, LDB_CONTROL_PAGED_RESULTS_OID);
869 182015 : 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 182015 : 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 6040 : int ldb_dsdb_paged_results_init(const char *version)
885 : {
886 6040 : LDB_MODULE_CHECK_VERSION(version);
887 6040 : return ldb_register_module(&ldb_paged_results_module_ops);
888 : }
|