CVE-2020-10700: dsdb: Do not permit the ASQ control for the GUID search in paged_results
[Samba.git] / source4 / dsdb / samdb / ldb_modules / paged_results.c
blobdc211dd18ceba85a1ab5ebf44481b789c3c7689f
1 /*
2 ldb database library
4 Copyright (C) Simo Sorce 2005-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
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
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.
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.
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/>.
26 * Name: paged_result
28 * Component: ldb paged results control module
30 * Description: this module caches a complete search and sends back
31 * results in chunks as asked by the client
33 * Author: Garming Sam and Aaron Haslett
35 * Note: Based on the original paged_results.c by Simo Sorce and
36 * vlv_pagination.c by Douglas Bagnall and Garming Sam.
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"
51 #include "dsdb/common/util.h"
52 #include "lib/util/dlinklist.h"
54 /* Referrals are temporarily stored in a linked list */
55 struct referral_store {
56 char *ref;
57 struct referral_store *next;
60 struct private_data;
62 struct results_store {
63 struct results_store *prev, *next;
65 struct private_data *priv;
67 char *cookie;
68 time_t timestamp;
70 struct referral_store *first_ref;
71 struct referral_store *last_ref;
73 struct ldb_control **controls;
75 /* from VLV */
76 struct GUID *results;
77 size_t num_entries;
78 size_t result_array_size;
80 struct ldb_control **down_controls;
81 const char * const *attrs;
83 unsigned last_i;
84 struct ldb_parse_tree *expr;
85 char *expr_str;
88 struct private_data {
89 uint32_t next_free_id;
90 size_t num_stores;
91 struct results_store *store;
94 static int store_destructor(struct results_store *del)
96 struct private_data *priv = del->priv;
97 DLIST_REMOVE(priv->store, del);
99 priv->num_stores -= 1;
101 return 0;
104 static struct results_store *new_store(struct private_data *priv)
106 struct results_store *newr;
107 uint32_t new_id = priv->next_free_id++;
109 /* TODO: we should have a limit on the number of
110 * outstanding paged searches
113 newr = talloc_zero(priv, struct results_store);
114 if (!newr) return NULL;
116 newr->priv = priv;
118 newr->cookie = talloc_asprintf(newr, "%d", new_id);
119 if (!newr->cookie) {
120 talloc_free(newr);
121 return NULL;
124 newr->timestamp = time(NULL);
126 DLIST_ADD(priv->store, newr);
128 priv->num_stores += 1;
130 talloc_set_destructor(newr, store_destructor);
132 if (priv->num_stores > 10) {
133 struct results_store *last;
135 * 10 is the default for MaxResultSetsPerConn --
136 * possibly need to parameterize it.
138 last = DLIST_TAIL(priv->store);
139 TALLOC_FREE(last);
142 return newr;
145 struct paged_context {
146 struct ldb_module *module;
147 struct ldb_request *req;
149 struct results_store *store;
150 int size;
151 struct ldb_control **controls;
154 static int send_referrals(struct results_store *store,
155 struct ldb_request *req)
157 int ret;
158 struct referral_store *node;
159 while (store->first_ref != NULL) {
160 node = store->first_ref;
161 ret = ldb_module_send_referral(req, node->ref);
162 if (ret != LDB_SUCCESS) {
163 return ret;
165 store->first_ref = node->next;
166 talloc_free(node);
168 return LDB_SUCCESS;
171 /* Start an ldb request for a single object by GUID */
172 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)
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;
185 /* Use controls passed in on the downreq */
186 struct ldb_control **controls = ac->store->down_controls;
188 struct ldb_context *ldb = ldb_module_get_ctx(module);
190 dn = ldb_dn_new_fmt(ac, ldb, "<GUID=%s>",
191 GUID_buf_string(guid, &guid_str));
192 if (dn == NULL) {
193 return ldb_oom(ldb);
196 res = talloc_zero(ac, struct ldb_result);
197 if (res == NULL) {
198 TALLOC_FREE(dn);
199 return ldb_oom(ldb);
202 ret = ldb_build_search_req_ex(&req, ldb, ac,
204 LDB_SCOPE_BASE,
205 expr,
206 attrs,
207 controls,
208 res,
209 ldb_search_default_callback,
210 ac->req);
211 if (ret != LDB_SUCCESS) {
212 TALLOC_FREE(dn);
213 TALLOC_FREE(res);
214 return ret;
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)
223 talloc_steal(req, dn);
225 ret = ldb_request(ldb, req);
226 if (ret == LDB_SUCCESS) {
227 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
230 talloc_free(req);
231 if (ret != LDB_SUCCESS) {
232 talloc_free(res);
233 return ret;
236 *result = res;
237 return ret;
240 static int paged_results(struct paged_context *ac)
242 struct ldb_paged_control *paged;
243 unsigned int i, num_ctrls;
244 int ret;
246 if (ac->store == NULL) {
247 return LDB_ERR_OPERATIONS_ERROR;
250 while (ac->store->last_i < ac->store->num_entries && ac->size > 0) {
251 struct GUID *guid = &ac->store->results[ac->store->last_i++];
252 struct ldb_result *result = NULL;
254 ac->size--;
257 * Note: In the case that an object has been moved to a
258 * different place in the LDAP tree, we might expect the object
259 * to disappear from paged results. If we were going to
260 * implement that behaviour, we would do it here by passing
261 * down the original container DN to the search.
262 * However, testing shows that, on Windows, the moved object
263 * remains in the paged results. So, we are matching Windows
264 * behaviour here by leaving out the scope.
266 ret = paged_search_by_dn_guid(ac->module, ac, &result, guid,
267 ac->req->op.search.attrs,
268 ac->store->expr);
269 if (ret == LDAP_NO_SUCH_OBJECT ||
270 (ret == LDB_SUCCESS && result->count == 0)) {
271 /* The thing isn't there TODO, which we quietly
272 ignore and go on to send an extra one
273 instead. */
274 continue;
275 } else if (ret != LDB_SUCCESS) {
276 return ret;
279 ret = ldb_module_send_entry(ac->req, result->msgs[0],
280 NULL);
281 if (ret != LDB_SUCCESS) {
282 return ret;
286 if (ac->store->first_ref) {
287 /* There is no right place to put references in the sorted
288 results, so we send them as soon as possible.
290 ret = send_referrals(ac->store, ac->req);
291 if (ret != LDB_SUCCESS) {
292 return ret;
296 /* return result done */
297 num_ctrls = 1;
298 i = 0;
300 if (ac->store->controls != NULL) {
301 while (ac->store->controls[i]) i++; /* counting */
303 num_ctrls += i;
306 ac->controls = talloc_array(ac, struct ldb_control *, num_ctrls +1);
307 if (ac->controls == NULL) {
308 return LDB_ERR_OPERATIONS_ERROR;
310 ac->controls[num_ctrls] = NULL;
312 for (i = 0; i < (num_ctrls -1); i++) {
313 ac->controls[i] = talloc_reference(ac->controls,
314 ac->store->controls[i]);
317 ac->controls[i] = talloc(ac->controls, struct ldb_control);
318 if (ac->controls[i] == NULL) {
319 return LDB_ERR_OPERATIONS_ERROR;
322 ac->controls[i]->oid = talloc_strdup(ac->controls[i],
323 LDB_CONTROL_PAGED_RESULTS_OID);
324 if (ac->controls[i]->oid == NULL) {
325 return LDB_ERR_OPERATIONS_ERROR;
328 ac->controls[i]->critical = 0;
330 paged = talloc(ac->controls[i], struct ldb_paged_control);
331 if (paged == NULL) {
332 return LDB_ERR_OPERATIONS_ERROR;
335 ac->controls[i]->data = paged;
337 if (ac->size > 0) {
338 paged->size = 0;
339 paged->cookie = NULL;
340 paged->cookie_len = 0;
341 } else {
342 paged->size = ac->store->num_entries;
343 paged->cookie = talloc_strdup(paged, ac->store->cookie);
344 paged->cookie_len = strlen(paged->cookie) + 1;
347 return LDB_SUCCESS;
350 static int save_referral(struct results_store *store, char *ref)
352 struct referral_store *node = talloc(store,
353 struct referral_store);
354 if (node == NULL) {
355 return LDB_ERR_OPERATIONS_ERROR;
357 node->next = NULL;
358 node->ref = talloc_steal(node, ref);
360 if (store->first_ref == NULL) {
361 store->first_ref = node;
362 } else {
363 store->last_ref->next = node;
365 store->last_ref = node;
366 return LDB_SUCCESS;
369 static int paged_search_callback(struct ldb_request *req,
370 struct ldb_reply *ares)
372 struct paged_context *ac;
373 struct results_store *store;
374 int ret;
375 const struct ldb_val *guid_blob;
376 struct GUID guid;
377 NTSTATUS status;
379 ac = talloc_get_type(req->context, struct paged_context);
380 store = ac->store;
382 if (!ares) {
383 return ldb_module_done(ac->req, NULL, NULL,
384 LDB_ERR_OPERATIONS_ERROR);
386 if (ares->error != LDB_SUCCESS) {
387 return ldb_module_done(ac->req, ares->controls,
388 ares->response, ares->error);
391 switch (ares->type) {
392 case LDB_REPLY_ENTRY:
393 if (store->results == NULL) {
394 store->num_entries = 0;
395 store->result_array_size = 16;
396 store->results = talloc_array(store, struct GUID,
397 store->result_array_size);
398 if (store->results == NULL) {
399 return ldb_module_done(ac->req, NULL, NULL,
400 LDB_ERR_OPERATIONS_ERROR);
402 } else if (store->num_entries == store->result_array_size) {
403 if (store->result_array_size > INT_MAX/2) {
404 return ldb_module_done(ac->req, NULL, NULL,
405 LDB_ERR_OPERATIONS_ERROR);
407 store->result_array_size *= 2;
408 store->results = talloc_realloc(store, store->results,
409 struct GUID,
410 store->result_array_size);
411 if (store->results == NULL) {
412 return ldb_module_done(ac->req, NULL, NULL,
413 LDB_ERR_OPERATIONS_ERROR);
417 guid_blob = ldb_dn_get_extended_component(ares->message->dn,
418 "GUID");
419 status = GUID_from_ndr_blob(guid_blob, &guid);
420 if (!NT_STATUS_IS_OK(status)) {
421 return ldb_module_done(ac->req, NULL, NULL,
422 LDB_ERR_OPERATIONS_ERROR);
425 /* Redundant paranoid check */
426 if (store->num_entries > store->result_array_size) {
427 return ldb_module_done(ac->req, NULL, NULL,
428 LDB_ERR_OPERATIONS_ERROR);
431 store->results[store->num_entries] = guid;
432 store->num_entries++;
433 break;
435 case LDB_REPLY_REFERRAL:
436 ret = save_referral(store, ares->referral);
437 if (ret != LDB_SUCCESS) {
438 return ldb_module_done(ac->req, NULL, NULL, ret);
440 break;
442 case LDB_REPLY_DONE:
443 if (store->num_entries != 0) {
444 store->results = talloc_realloc(store, store->results,
445 struct GUID,
446 store->num_entries);
447 if (store->results == NULL) {
448 return ldb_module_done(ac->req, NULL, NULL,
449 LDB_ERR_OPERATIONS_ERROR);
452 store->result_array_size = store->num_entries;
454 ac->store->controls = talloc_move(ac->store, &ares->controls);
455 ret = paged_results(ac);
456 return ldb_module_done(ac->req, ac->controls,
457 ares->response, ret);
460 return LDB_SUCCESS;
463 static struct ldb_control **
464 paged_results_copy_down_controls(TALLOC_CTX *mem_ctx,
465 struct ldb_control **controls)
468 struct ldb_control **new_controls;
469 unsigned int i, j, num_ctrls;
470 if (controls == NULL) {
471 return NULL;
474 for (num_ctrls = 0; controls[num_ctrls]; num_ctrls++);
476 new_controls = talloc_array(mem_ctx, struct ldb_control *, num_ctrls);
477 if (new_controls == NULL) {
478 return NULL;
481 for (j = 0, i = 0; i < (num_ctrls); i++) {
482 struct ldb_control *control = controls[i];
483 if (control->oid == NULL) {
484 continue;
486 if (strcmp(control->oid, LDB_CONTROL_PAGED_RESULTS_OID) == 0) {
487 continue;
490 * ASQ changes everything, do not copy it down for the
491 * per-GUID search
493 if (strcmp(control->oid, LDB_CONTROL_ASQ_OID) == 0) {
494 continue;
496 new_controls[j] = talloc_steal(new_controls, control);
497 j++;
499 new_controls[j] = NULL;
500 return new_controls;
503 static const char * const *paged_copy_attrs(TALLOC_CTX *mem_ctx,
504 const char * const *attrs) {
505 int i;
506 const char **new_attrs;
507 if (attrs == NULL) {
508 return NULL;
510 new_attrs = ldb_attr_list_copy(mem_ctx, attrs);
512 for (i=0; attrs[i] != NULL; i++) {
513 new_attrs[i] = talloc_strdup(mem_ctx, attrs[i]);
515 new_attrs[i] = NULL;
516 return new_attrs;
520 * Check if two sets of controls are the same except for the paged results
521 * control in the request controls. This function is messy because request
522 * control lists can contain controls that were NULL'd by the rootdse. We
523 * must ignore those entries. This function is not portable.
525 static bool paged_controls_same(struct ldb_request *req,
526 struct ldb_control **down_controls) {
527 int i;
528 unsigned int num_down_controls, num_non_null_req_controls;
529 struct ldb_control *ctrl;
531 num_down_controls = 0;
532 for (i=0; down_controls[i] != NULL; i++) {
533 num_down_controls++;
535 ctrl = ldb_request_get_control(req, down_controls[i]->oid);
536 if (ctrl == NULL) {
537 return false;
541 num_non_null_req_controls = 0;
542 for (i=0; req->controls[i] != NULL; i++) {
543 if (req->controls[i]->oid != NULL &&
544 strcmp(req->controls[i]->oid,
545 LDB_CONTROL_ASQ_OID) != 0) {
546 num_non_null_req_controls++;
550 /* At this point we have the number of non-null entries for both
551 * control lists and we know that:
552 * 1. down_controls does not contain the paged control or ASQ
553 * (because paged_results_copy_down_controls excludes it)
554 * 2. req->controls does contain the paged control
555 * (because this function is only called if this is true)
556 * 3. down_controls is a subset of non-null controls in req->controls
557 * (checked above)
558 * So to confirm that the two lists are identical except for the paged
559 * control and possibly ASQ, all we need to check is: */
560 if (num_non_null_req_controls == num_down_controls + 1) {
561 return true;
563 return false;
566 static bool paged_attrs_same(const char * const *attrs_1,
567 const char * const *attrs_2) {
568 int i;
569 if (attrs_1 == NULL || attrs_2 == NULL) {
570 if (attrs_1 == NULL && attrs_2 == NULL) {
571 return true;
573 return false;
576 for (i=0; attrs_1[i] != NULL; i++) {
577 if (!ldb_attr_in_list(attrs_2, attrs_1[i])) {
578 return false;
581 return true;
584 static int paged_search(struct ldb_module *module, struct ldb_request *req)
586 struct ldb_context *ldb;
587 struct ldb_control *control;
588 struct private_data *private_data;
589 struct ldb_paged_control *paged_ctrl;
590 struct ldb_request *search_req;
591 struct paged_context *ac;
592 int ret;
594 ldb = ldb_module_get_ctx(module);
596 /* check if there's a paged request control */
597 control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID);
598 if (control == NULL) {
599 /* not found go on */
600 return ldb_next_request(module, req);
603 paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
604 if (!paged_ctrl) {
605 return LDB_ERR_PROTOCOL_ERROR;
608 private_data = talloc_get_type(ldb_module_get_private(module),
609 struct private_data);
611 ac = talloc_zero(req, struct paged_context);
612 if (ac == NULL) {
613 ldb_set_errstring(ldb, "Out of Memory");
614 return LDB_ERR_OPERATIONS_ERROR;
617 ac->module = module;
618 ac->req = req;
619 ac->size = paged_ctrl->size;
620 if (ac->size < 0) {
622 * Apparently some clients send more than 2^31. This
623 * violates the ldap standard, but we need to cope.
624 * In the future, if maximum result sizes are implemented in
625 * Samba, we should also clamp the page size to the maximum
626 * result size.
628 ac->size = 0x7FFFFFFF;
631 /* check if it is a continuation search the store */
632 if (paged_ctrl->cookie_len == 0) {
633 struct ldb_control *ext_ctrl;
634 struct ldb_control **controls;
635 static const char * const attrs[1] = { NULL };
637 if (paged_ctrl->size == 0) {
638 return LDB_ERR_OPERATIONS_ERROR;
641 ac->store = new_store(private_data);
642 if (ac->store == NULL) {
643 return LDB_ERR_OPERATIONS_ERROR;
646 controls = req->controls;
647 ext_ctrl = ldb_request_get_control(req,
648 LDB_CONTROL_EXTENDED_DN_OID);
649 if (ext_ctrl == NULL) {
651 * Add extended_dn control to the request if there
652 * isn't already one. We'll get the GUID out of it in
653 * the callback. This is a workaround for the case
654 * where ntsecuritydescriptor forbids fetching GUIDs
655 * for the current user.
657 struct ldb_request *req_extended_dn;
658 struct ldb_extended_dn_control *ext_ctrl_data;
659 req_extended_dn = talloc_zero(req, struct ldb_request);
660 req_extended_dn->controls = req->controls;
661 ext_ctrl_data = talloc_zero(req,
662 struct ldb_extended_dn_control);
663 ext_ctrl_data->type = 1;
665 ret = ldb_request_add_control(req_extended_dn,
666 LDB_CONTROL_EXTENDED_DN_OID,
667 true,
668 ext_ctrl_data);
669 if (ret != LDB_SUCCESS) {
670 return ret;
672 controls = req_extended_dn->controls;
675 ret = ldb_build_search_req_ex(&search_req, ldb, ac,
676 req->op.search.base,
677 req->op.search.scope,
678 req->op.search.tree,
679 attrs,
680 controls,
682 paged_search_callback,
683 req);
684 if (ret != LDB_SUCCESS) {
685 return ret;
688 ac->store->expr = talloc_steal(ac->store, req->op.search.tree);
689 ac->store->expr_str = ldb_filter_from_tree(ac->store,
690 req->op.search.tree);
691 ac->store->attrs = paged_copy_attrs(ac->store,
692 req->op.search.attrs);
694 /* save it locally and remove it from the list */
695 /* we do not need to replace them later as we
696 * are keeping the original req intact */
697 if (!ldb_save_controls(control, search_req, NULL)) {
698 return LDB_ERR_OPERATIONS_ERROR;
700 ac->store->down_controls =
701 paged_results_copy_down_controls(ac->store, req->controls);
702 if (ac->store->down_controls == NULL) {
703 return LDB_ERR_OPERATIONS_ERROR;
706 return ldb_next_request(module, search_req);
708 } else {
709 struct results_store *current = NULL;
710 char *expr_str;
711 bool bool_ret;
713 /* TODO: age out old outstanding requests */
714 for (current = private_data->store; current != NULL;
715 current = current->next) {
716 if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
717 current->timestamp = time(NULL);
718 break;
721 if (current == NULL) {
722 return LDB_ERR_UNWILLING_TO_PERFORM;
725 /* Get the expression string and make sure it didn't change */
726 expr_str = ldb_filter_from_tree(ac, req->op.search.tree);
727 if (expr_str == NULL) {
728 return LDB_ERR_OPERATIONS_ERROR;
731 ret = strcmp(current->expr_str, expr_str);
732 if (ret != 0) {
733 return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
736 bool_ret = paged_controls_same(req, current->down_controls);
737 if (bool_ret == false) {
738 return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
741 bool_ret = paged_attrs_same(req->op.search.attrs,
742 current->attrs);
743 if (bool_ret == false) {
744 return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
747 DLIST_PROMOTE(private_data->store, current);
749 ac->store = current;
751 /* check if it is an abandon */
752 if (ac->size == 0) {
753 return ldb_module_done(req, NULL, NULL,
754 LDB_SUCCESS);
757 ret = paged_results(ac);
758 if (ret != LDB_SUCCESS) {
759 return ldb_module_done(req, NULL, NULL, ret);
761 return ldb_module_done(req, ac->controls, NULL, LDB_SUCCESS);
765 static int paged_request_init(struct ldb_module *module)
767 struct ldb_context *ldb;
768 struct private_data *data;
769 int ret;
771 ldb = ldb_module_get_ctx(module);
773 data = talloc(module, struct private_data);
774 if (data == NULL) {
775 return LDB_ERR_OTHER;
778 data->next_free_id = 1;
779 data->num_stores = 0;
780 data->store = NULL;
781 ldb_module_set_private(module, data);
783 ret = ldb_mod_register_control(module, LDB_CONTROL_PAGED_RESULTS_OID);
784 if (ret != LDB_SUCCESS) {
785 ldb_debug(ldb, LDB_DEBUG_WARNING,
786 "paged_results:"
787 "Unable to register control with rootdse!");
790 return ldb_next_init(module);
793 static const struct ldb_module_ops ldb_paged_results_module_ops = {
794 .name = "dsdb_paged_results",
795 .search = paged_search,
796 .init_context = paged_request_init
799 int ldb_dsdb_paged_results_init(const char *version)
801 LDB_MODULE_CHECK_VERSION(version);
802 return ldb_register_module(&ldb_paged_results_module_ops);