| File: | src/usr.sbin/ldapd/search.c |
| Warning: | line 155, column 3 Potential leak of memory pointed to by 'searchdn' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: search.c,v 1.26 2020/01/28 15:51:26 bket Exp $ */ | |||
| 2 | ||||
| 3 | /* | |||
| 4 | * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> | |||
| 5 | * | |||
| 6 | * Permission to use, copy, modify, and distribute this software for any | |||
| 7 | * purpose with or without fee is hereby granted, provided that the above | |||
| 8 | * copyright notice and this permission notice appear in all copies. | |||
| 9 | * | |||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| 17 | */ | |||
| 18 | ||||
| 19 | #include <sys/queue.h> | |||
| 20 | #include <sys/types.h> | |||
| 21 | #include <sys/tree.h> | |||
| 22 | ||||
| 23 | #include <errno(*__errno()).h> | |||
| 24 | #include <event.h> | |||
| 25 | #include <stdlib.h> | |||
| 26 | #include <string.h> | |||
| 27 | #include <time.h> | |||
| 28 | ||||
| 29 | #include "ldapd.h" | |||
| 30 | #include "log.h" | |||
| 31 | ||||
| 32 | #define MAX_SEARCHES200 200 | |||
| 33 | ||||
| 34 | void filter_free(struct plan *filter); | |||
| 35 | static int search_result(const char *dn, | |||
| 36 | size_t dnlen, | |||
| 37 | struct ber_element *attrs, | |||
| 38 | struct search *search); | |||
| 39 | ||||
| 40 | static int | |||
| 41 | uniqdn_cmp(struct uniqdn *a, struct uniqdn *b) | |||
| 42 | { | |||
| 43 | if (a->key.size < b->key.size) | |||
| 44 | return -1; | |||
| 45 | if (a->key.size > b->key.size) | |||
| 46 | return +1; | |||
| 47 | return memcmp(a->key.data, b->key.data, a->key.size); | |||
| 48 | } | |||
| 49 | ||||
| 50 | RB_GENERATE(dn_tree, uniqdn, link, uniqdn_cmp)void dn_tree_RB_INSERT_COLOR(struct dn_tree *head, struct uniqdn *elm) { struct uniqdn *parent, *gparent, *tmp; while ((parent = (elm)->link.rbe_parent) && (parent)->link.rbe_color == 1) { gparent = (parent)->link.rbe_parent; if (parent == (gparent)->link.rbe_left) { tmp = (gparent)->link.rbe_right ; if (tmp && (tmp)->link.rbe_color == 1) { (tmp)-> link.rbe_color = 0; do { (parent)->link.rbe_color = 0; (gparent )->link.rbe_color = 1; } while (0); elm = gparent; continue ; } if ((parent)->link.rbe_right == elm) { do { (tmp) = (parent )->link.rbe_right; if (((parent)->link.rbe_right = (tmp )->link.rbe_left)) { ((tmp)->link.rbe_left)->link.rbe_parent = (parent); } do {} while (0); if (((tmp)->link.rbe_parent = (parent)->link.rbe_parent)) { if ((parent) == ((parent) ->link.rbe_parent)->link.rbe_left) ((parent)->link.rbe_parent )->link.rbe_left = (tmp); else ((parent)->link.rbe_parent )->link.rbe_right = (tmp); } else (head)->rbh_root = (tmp ); (tmp)->link.rbe_left = (parent); (parent)->link.rbe_parent = (tmp); do {} while (0); if (((tmp)->link.rbe_parent)) do {} while (0); } while (0); tmp = parent; parent = elm; elm = tmp; } do { (parent)->link.rbe_color = 0; (gparent)->link .rbe_color = 1; } while (0); do { (tmp) = (gparent)->link. rbe_left; if (((gparent)->link.rbe_left = (tmp)->link.rbe_right )) { ((tmp)->link.rbe_right)->link.rbe_parent = (gparent ); } do {} while (0); if (((tmp)->link.rbe_parent = (gparent )->link.rbe_parent)) { if ((gparent) == ((gparent)->link .rbe_parent)->link.rbe_left) ((gparent)->link.rbe_parent )->link.rbe_left = (tmp); else ((gparent)->link.rbe_parent )->link.rbe_right = (tmp); } else (head)->rbh_root = (tmp ); (tmp)->link.rbe_right = (gparent); (gparent)->link.rbe_parent = (tmp); do {} while (0); if (((tmp)->link.rbe_parent)) do {} while (0); } while (0); } else { tmp = (gparent)->link .rbe_left; if (tmp && (tmp)->link.rbe_color == 1) { (tmp)->link.rbe_color = 0; do { (parent)->link.rbe_color = 0; (gparent)->link.rbe_color = 1; } while (0); elm = gparent ; continue; } if ((parent)->link.rbe_left == elm) { do { ( tmp) = (parent)->link.rbe_left; if (((parent)->link.rbe_left = (tmp)->link.rbe_right)) { ((tmp)->link.rbe_right)-> link.rbe_parent = (parent); } do {} while (0); if (((tmp)-> link.rbe_parent = (parent)->link.rbe_parent)) { if ((parent ) == ((parent)->link.rbe_parent)->link.rbe_left) ((parent )->link.rbe_parent)->link.rbe_left = (tmp); else ((parent )->link.rbe_parent)->link.rbe_right = (tmp); } else (head )->rbh_root = (tmp); (tmp)->link.rbe_right = (parent); ( parent)->link.rbe_parent = (tmp); do {} while (0); if (((tmp )->link.rbe_parent)) do {} while (0); } while (0); tmp = parent ; parent = elm; elm = tmp; } do { (parent)->link.rbe_color = 0; (gparent)->link.rbe_color = 1; } while (0); do { (tmp ) = (gparent)->link.rbe_right; if (((gparent)->link.rbe_right = (tmp)->link.rbe_left)) { ((tmp)->link.rbe_left)-> link.rbe_parent = (gparent); } do {} while (0); if (((tmp)-> link.rbe_parent = (gparent)->link.rbe_parent)) { if ((gparent ) == ((gparent)->link.rbe_parent)->link.rbe_left) ((gparent )->link.rbe_parent)->link.rbe_left = (tmp); else ((gparent )->link.rbe_parent)->link.rbe_right = (tmp); } else (head )->rbh_root = (tmp); (tmp)->link.rbe_left = (gparent); ( gparent)->link.rbe_parent = (tmp); do {} while (0); if ((( tmp)->link.rbe_parent)) do {} while (0); } while (0); } } ( head->rbh_root)->link.rbe_color = 0; } void dn_tree_RB_REMOVE_COLOR (struct dn_tree *head, struct uniqdn *parent, struct uniqdn * elm) { struct uniqdn *tmp; while ((elm == ((void*)0) || (elm) ->link.rbe_color == 0) && elm != (head)->rbh_root ) { if ((parent)->link.rbe_left == elm) { tmp = (parent)-> link.rbe_right; if ((tmp)->link.rbe_color == 1) { do { (tmp )->link.rbe_color = 0; (parent)->link.rbe_color = 1; } while (0); do { (tmp) = (parent)->link.rbe_right; if (((parent) ->link.rbe_right = (tmp)->link.rbe_left)) { ((tmp)-> link.rbe_left)->link.rbe_parent = (parent); } do {} while ( 0); if (((tmp)->link.rbe_parent = (parent)->link.rbe_parent )) { if ((parent) == ((parent)->link.rbe_parent)->link. rbe_left) ((parent)->link.rbe_parent)->link.rbe_left = ( tmp); else ((parent)->link.rbe_parent)->link.rbe_right = (tmp); } else (head)->rbh_root = (tmp); (tmp)->link.rbe_left = (parent); (parent)->link.rbe_parent = (tmp); do {} while (0); if (((tmp)->link.rbe_parent)) do {} while (0); } while (0); tmp = (parent)->link.rbe_right; } if (((tmp)->link .rbe_left == ((void*)0) || ((tmp)->link.rbe_left)->link .rbe_color == 0) && ((tmp)->link.rbe_right == ((void *)0) || ((tmp)->link.rbe_right)->link.rbe_color == 0)) { (tmp)->link.rbe_color = 1; elm = parent; parent = (elm)-> link.rbe_parent; } else { if ((tmp)->link.rbe_right == ((void *)0) || ((tmp)->link.rbe_right)->link.rbe_color == 0) { struct uniqdn *oleft; if ((oleft = (tmp)->link.rbe_left)) (oleft)->link.rbe_color = 0; (tmp)->link.rbe_color = 1 ; do { (oleft) = (tmp)->link.rbe_left; if (((tmp)->link .rbe_left = (oleft)->link.rbe_right)) { ((oleft)->link. rbe_right)->link.rbe_parent = (tmp); } do {} while (0); if (((oleft)->link.rbe_parent = (tmp)->link.rbe_parent)) { if ((tmp) == ((tmp)->link.rbe_parent)->link.rbe_left) ( (tmp)->link.rbe_parent)->link.rbe_left = (oleft); else ( (tmp)->link.rbe_parent)->link.rbe_right = (oleft); } else (head)->rbh_root = (oleft); (oleft)->link.rbe_right = ( tmp); (tmp)->link.rbe_parent = (oleft); do {} while (0); if (((oleft)->link.rbe_parent)) do {} while (0); } while (0) ; tmp = (parent)->link.rbe_right; } (tmp)->link.rbe_color = (parent)->link.rbe_color; (parent)->link.rbe_color = 0; if ((tmp)->link.rbe_right) ((tmp)->link.rbe_right)-> link.rbe_color = 0; do { (tmp) = (parent)->link.rbe_right; if (((parent)->link.rbe_right = (tmp)->link.rbe_left)) { ((tmp)->link.rbe_left)->link.rbe_parent = (parent); } do {} while (0); if (((tmp)->link.rbe_parent = (parent)-> link.rbe_parent)) { if ((parent) == ((parent)->link.rbe_parent )->link.rbe_left) ((parent)->link.rbe_parent)->link. rbe_left = (tmp); else ((parent)->link.rbe_parent)->link .rbe_right = (tmp); } else (head)->rbh_root = (tmp); (tmp) ->link.rbe_left = (parent); (parent)->link.rbe_parent = (tmp); do {} while (0); if (((tmp)->link.rbe_parent)) do { } while (0); } while (0); elm = (head)->rbh_root; break; } } else { tmp = (parent)->link.rbe_left; if ((tmp)->link .rbe_color == 1) { do { (tmp)->link.rbe_color = 0; (parent )->link.rbe_color = 1; } while (0); do { (tmp) = (parent)-> link.rbe_left; if (((parent)->link.rbe_left = (tmp)->link .rbe_right)) { ((tmp)->link.rbe_right)->link.rbe_parent = (parent); } do {} while (0); if (((tmp)->link.rbe_parent = (parent)->link.rbe_parent)) { if ((parent) == ((parent) ->link.rbe_parent)->link.rbe_left) ((parent)->link.rbe_parent )->link.rbe_left = (tmp); else ((parent)->link.rbe_parent )->link.rbe_right = (tmp); } else (head)->rbh_root = (tmp ); (tmp)->link.rbe_right = (parent); (parent)->link.rbe_parent = (tmp); do {} while (0); if (((tmp)->link.rbe_parent)) do {} while (0); } while (0); tmp = (parent)->link.rbe_left; } if (((tmp)->link.rbe_left == ((void*)0) || ((tmp)->link .rbe_left)->link.rbe_color == 0) && ((tmp)->link .rbe_right == ((void*)0) || ((tmp)->link.rbe_right)->link .rbe_color == 0)) { (tmp)->link.rbe_color = 1; elm = parent ; parent = (elm)->link.rbe_parent; } else { if ((tmp)-> link.rbe_left == ((void*)0) || ((tmp)->link.rbe_left)-> link.rbe_color == 0) { struct uniqdn *oright; if ((oright = ( tmp)->link.rbe_right)) (oright)->link.rbe_color = 0; (tmp )->link.rbe_color = 1; do { (oright) = (tmp)->link.rbe_right ; if (((tmp)->link.rbe_right = (oright)->link.rbe_left) ) { ((oright)->link.rbe_left)->link.rbe_parent = (tmp); } do {} while (0); if (((oright)->link.rbe_parent = (tmp) ->link.rbe_parent)) { if ((tmp) == ((tmp)->link.rbe_parent )->link.rbe_left) ((tmp)->link.rbe_parent)->link.rbe_left = (oright); else ((tmp)->link.rbe_parent)->link.rbe_right = (oright); } else (head)->rbh_root = (oright); (oright)-> link.rbe_left = (tmp); (tmp)->link.rbe_parent = (oright); do {} while (0); if (((oright)->link.rbe_parent)) do {} while (0); } while (0); tmp = (parent)->link.rbe_left; } (tmp)-> link.rbe_color = (parent)->link.rbe_color; (parent)->link .rbe_color = 0; if ((tmp)->link.rbe_left) ((tmp)->link. rbe_left)->link.rbe_color = 0; do { (tmp) = (parent)->link .rbe_left; if (((parent)->link.rbe_left = (tmp)->link.rbe_right )) { ((tmp)->link.rbe_right)->link.rbe_parent = (parent ); } do {} while (0); if (((tmp)->link.rbe_parent = (parent )->link.rbe_parent)) { if ((parent) == ((parent)->link. rbe_parent)->link.rbe_left) ((parent)->link.rbe_parent) ->link.rbe_left = (tmp); else ((parent)->link.rbe_parent )->link.rbe_right = (tmp); } else (head)->rbh_root = (tmp ); (tmp)->link.rbe_right = (parent); (parent)->link.rbe_parent = (tmp); do {} while (0); if (((tmp)->link.rbe_parent)) do {} while (0); } while (0); elm = (head)->rbh_root; break; } } } if (elm) (elm)->link.rbe_color = 0; } struct uniqdn * dn_tree_RB_REMOVE(struct dn_tree *head, struct uniqdn *elm ) { struct uniqdn *child, *parent, *old = elm; int color; if ( (elm)->link.rbe_left == ((void*)0)) child = (elm)->link .rbe_right; else if ((elm)->link.rbe_right == ((void*)0)) child = (elm)->link.rbe_left; else { struct uniqdn *left; elm = (elm)->link.rbe_right; while ((left = (elm)->link.rbe_left )) elm = left; child = (elm)->link.rbe_right; parent = (elm )->link.rbe_parent; color = (elm)->link.rbe_color; if ( child) (child)->link.rbe_parent = parent; if (parent) { if ((parent)->link.rbe_left == elm) (parent)->link.rbe_left = child; else (parent)->link.rbe_right = child; do {} while (0); } else (head)->rbh_root = child; if ((elm)->link. rbe_parent == old) parent = elm; (elm)->link = (old)->link ; if ((old)->link.rbe_parent) { if (((old)->link.rbe_parent )->link.rbe_left == old) ((old)->link.rbe_parent)->link .rbe_left = elm; else ((old)->link.rbe_parent)->link.rbe_right = elm; do {} while (0); } else (head)->rbh_root = elm; (( old)->link.rbe_left)->link.rbe_parent = elm; if ((old)-> link.rbe_right) ((old)->link.rbe_right)->link.rbe_parent = elm; if (parent) { left = parent; do { do {} while (0); } while ((left = (left)->link.rbe_parent)); } goto color; } parent = (elm)->link.rbe_parent; color = (elm)->link.rbe_color ; if (child) (child)->link.rbe_parent = parent; if (parent ) { if ((parent)->link.rbe_left == elm) (parent)->link. rbe_left = child; else (parent)->link.rbe_right = child; do {} while (0); } else (head)->rbh_root = child; color: if ( color == 0) dn_tree_RB_REMOVE_COLOR(head, parent, child); return (old); } struct uniqdn * dn_tree_RB_INSERT(struct dn_tree *head , struct uniqdn *elm) { struct uniqdn *tmp; struct uniqdn *parent = ((void*)0); int comp = 0; tmp = (head)->rbh_root; while (tmp) { parent = tmp; comp = (uniqdn_cmp)(elm, parent); if ( comp < 0) tmp = (tmp)->link.rbe_left; else if (comp > 0) tmp = (tmp)->link.rbe_right; else return (tmp); } do { (elm)->link.rbe_parent = parent; (elm)->link.rbe_left = (elm)->link.rbe_right = ((void*)0); (elm)->link.rbe_color = 1; } while (0); if (parent != ((void*)0)) { if (comp < 0 ) (parent)->link.rbe_left = elm; else (parent)->link.rbe_right = elm; do {} while (0); } else (head)->rbh_root = elm; dn_tree_RB_INSERT_COLOR (head, elm); return (((void*)0)); } struct uniqdn * dn_tree_RB_FIND (struct dn_tree *head, struct uniqdn *elm) { struct uniqdn *tmp = (head)->rbh_root; int comp; while (tmp) { comp = uniqdn_cmp (elm, tmp); if (comp < 0) tmp = (tmp)->link.rbe_left; else if (comp > 0) tmp = (tmp)->link.rbe_right; else return (tmp); } return (((void*)0)); } struct uniqdn * dn_tree_RB_NFIND (struct dn_tree *head, struct uniqdn *elm) { struct uniqdn *tmp = (head)->rbh_root; struct uniqdn *res = ((void*)0); int comp ; while (tmp) { comp = uniqdn_cmp(elm, tmp); if (comp < 0) { res = tmp; tmp = (tmp)->link.rbe_left; } else if (comp > 0) tmp = (tmp)->link.rbe_right; else return (tmp); } return (res); } struct uniqdn * dn_tree_RB_NEXT(struct uniqdn *elm) { if ((elm)->link.rbe_right) { elm = (elm)->link.rbe_right ; while ((elm)->link.rbe_left) elm = (elm)->link.rbe_left ; } else { if ((elm)->link.rbe_parent && (elm == ( (elm)->link.rbe_parent)->link.rbe_left)) elm = (elm)-> link.rbe_parent; else { while ((elm)->link.rbe_parent && (elm == ((elm)->link.rbe_parent)->link.rbe_right)) elm = (elm)->link.rbe_parent; elm = (elm)->link.rbe_parent ; } } return (elm); } struct uniqdn * dn_tree_RB_PREV(struct uniqdn *elm) { if ((elm)->link.rbe_left) { elm = (elm)->link. rbe_left; while ((elm)->link.rbe_right) elm = (elm)->link .rbe_right; } else { if ((elm)->link.rbe_parent && (elm == ((elm)->link.rbe_parent)->link.rbe_right)) elm = (elm)->link.rbe_parent; else { while ((elm)->link.rbe_parent && (elm == ((elm)->link.rbe_parent)->link.rbe_left )) elm = (elm)->link.rbe_parent; elm = (elm)->link.rbe_parent ; } } return (elm); } struct uniqdn * dn_tree_RB_MINMAX(struct dn_tree *head, int val) { struct uniqdn *tmp = (head)->rbh_root ; struct uniqdn *parent = ((void*)0); while (tmp) { parent = tmp ; if (val < 0) tmp = (tmp)->link.rbe_left; else tmp = ( tmp)->link.rbe_right; } return (parent); }; | |||
| 51 | ||||
| 52 | /* Return true if the attribute is operational. | |||
| 53 | */ | |||
| 54 | static int | |||
| 55 | is_operational(char *adesc) | |||
| 56 | { | |||
| 57 | struct attr_type *at; | |||
| 58 | ||||
| 59 | at = lookup_attribute(conf->schema, adesc); | |||
| 60 | if (at) | |||
| 61 | return at->usage != USAGE_USER_APP; | |||
| 62 | ||||
| 63 | return 0; | |||
| 64 | } | |||
| 65 | ||||
| 66 | /* Return true if attr should be included in search entry. | |||
| 67 | */ | |||
| 68 | static int | |||
| 69 | should_include_attribute(char *adesc, struct search *search, int explicit) | |||
| 70 | { | |||
| 71 | char *fdesc; | |||
| 72 | struct ber_element *elm; | |||
| 73 | ||||
| 74 | if (search->attrlist->be_subbe_union.bv_sub == NULL((void*)0) || | |||
| 75 | search->attrlist->be_subbe_union.bv_sub->be_encoding == BER_TYPE_EOC0) { | |||
| 76 | /* An empty list with no attributes requests the return of | |||
| 77 | * all user attributes. */ | |||
| 78 | return !is_operational(adesc); | |||
| 79 | } | |||
| 80 | ||||
| 81 | for (elm = search->attrlist->be_subbe_union.bv_sub; elm; elm = elm->be_next) { | |||
| 82 | if (ober_get_string(elm, &fdesc) != 0) | |||
| 83 | continue; | |||
| 84 | if (strcasecmp(fdesc, adesc) == 0) | |||
| 85 | return 1; | |||
| 86 | if (strcmp(fdesc, "*") == 0 && !is_operational(adesc)) | |||
| 87 | return 1; | |||
| 88 | if (strcmp(fdesc, "+") == 0 && is_operational(adesc) && | |||
| 89 | !explicit) | |||
| 90 | return 1; | |||
| 91 | } | |||
| 92 | ||||
| 93 | return 0; | |||
| 94 | } | |||
| 95 | ||||
| 96 | static int | |||
| 97 | search_result(const char *dn, size_t dnlen, struct ber_element *attrs, | |||
| 98 | struct search *search) | |||
| 99 | { | |||
| 100 | int rc; | |||
| 101 | struct conn *conn = search->conn; | |||
| 102 | struct ber_element *root, *elm, *filtered_attrs = NULL((void*)0), *link, *a; | |||
| 103 | struct ber_element *prev, *next; | |||
| 104 | char *adesc; | |||
| 105 | void *buf, *searchdn = NULL((void*)0); | |||
| 106 | ||||
| 107 | if ((root = ober_add_sequence(NULL((void*)0))) == NULL((void*)0)) | |||
| 108 | goto fail; | |||
| 109 | ||||
| 110 | if ((filtered_attrs = ober_add_sequence(NULL((void*)0))) == NULL((void*)0)) | |||
| 111 | goto fail; | |||
| 112 | link = filtered_attrs; | |||
| 113 | ||||
| 114 | if ((searchdn = strndup(dn, dnlen)) == NULL((void*)0)) | |||
| 115 | goto fail; | |||
| 116 | ||||
| 117 | for (prev = NULL((void*)0), a = attrs->be_subbe_union.bv_sub; a; a = next) { | |||
| 118 | if (ober_get_string(a->be_subbe_union.bv_sub, &adesc) != 0) | |||
| 119 | goto fail; | |||
| 120 | /* | |||
| 121 | * Check if read access to the attribute is allowed and if it | |||
| 122 | * should be included in the search result. The attribute is | |||
| 123 | * filtered out in the result if one of these conditions fails. | |||
| 124 | */ | |||
| 125 | if (authorized(search->conn, search->ns, ACI_READ0x01, | |||
| 126 | searchdn, adesc, LDAP_SCOPE_BASE) && | |||
| 127 | should_include_attribute(adesc, search, 0)) { | |||
| 128 | next = a->be_next; | |||
| 129 | if (prev != NULL((void*)0)) | |||
| 130 | prev->be_next = a->be_next; /* unlink a */ | |||
| 131 | else | |||
| 132 | attrs->be_subbe_union.bv_sub = a->be_next; | |||
| 133 | a->be_next = NULL((void*)0); /* break chain*/ | |||
| 134 | ober_link_elements(link, a); | |||
| 135 | link = a; | |||
| 136 | } else { | |||
| 137 | prev = a; | |||
| 138 | next = a->be_next; | |||
| 139 | } | |||
| 140 | } | |||
| 141 | ||||
| 142 | elm = ober_printf_elements(root, "i{txe", search->req->msgid, | |||
| 143 | BER_CLASS_APP0x1, LDAP_RES_SEARCH_ENTRY, | |||
| 144 | dn, dnlen, filtered_attrs); | |||
| 145 | if (elm == NULL((void*)0)) | |||
| 146 | goto fail; | |||
| 147 | ||||
| 148 | ldap_debug_elements(root, LDAP_RES_SEARCH_ENTRY, | |||
| 149 | "sending search entry on fd %d", conn->fd); | |||
| 150 | ||||
| 151 | rc = ober_write_elements(&conn->ber, root); | |||
| 152 | ober_free_elements(root); | |||
| 153 | ||||
| 154 | if (rc < 0) { | |||
| 155 | log_warn("failed to create search-entry response"); | |||
| ||||
| 156 | return -1; | |||
| 157 | } | |||
| 158 | ||||
| 159 | ober_get_writebuf(&conn->ber, &buf); | |||
| 160 | if (bufferevent_write(conn->bev, buf, rc) != 0) { | |||
| 161 | log_warn("failed to send ldap result"); | |||
| 162 | return -1; | |||
| 163 | } | |||
| 164 | ||||
| 165 | free(searchdn); | |||
| 166 | return 0; | |||
| 167 | fail: | |||
| 168 | log_warn("search result"); | |||
| 169 | if (root) | |||
| 170 | ober_free_elements(root); | |||
| 171 | free(searchdn); | |||
| 172 | return -1; | |||
| 173 | } | |||
| 174 | ||||
| 175 | void | |||
| 176 | search_close(struct search *search) | |||
| 177 | { | |||
| 178 | struct uniqdn *dn, *next; | |||
| 179 | ||||
| 180 | for (dn = RB_MIN(dn_tree, &search->uniqdns)dn_tree_RB_MINMAX(&search->uniqdns, -1); dn; dn = next) { | |||
| 181 | next = RB_NEXT(dn_tree, &search->uniqdns, dn)dn_tree_RB_NEXT(dn); | |||
| 182 | RB_REMOVE(dn_tree, &search->uniqdns, dn)dn_tree_RB_REMOVE(&search->uniqdns, dn); | |||
| 183 | free(dn->key.data); | |||
| 184 | free(dn); | |||
| 185 | } | |||
| 186 | ||||
| 187 | btree_cursor_close(search->cursor); | |||
| 188 | btree_txn_abort(search->data_txn); | |||
| 189 | btree_txn_abort(search->indx_txn); | |||
| 190 | ||||
| 191 | if (search->req != NULL((void*)0)) { | |||
| 192 | log_debug("finished search on msgid %lld", search->req->msgid); | |||
| 193 | request_free(search->req); | |||
| 194 | } | |||
| 195 | TAILQ_REMOVE(&search->conn->searches, search, next)do { if (((search)->next.tqe_next) != ((void*)0)) (search) ->next.tqe_next->next.tqe_prev = (search)->next.tqe_prev ; else (&search->conn->searches)->tqh_last = (search )->next.tqe_prev; *(search)->next.tqe_prev = (search)-> next.tqe_next; ; ; } while (0); | |||
| 196 | filter_free(search->plan); | |||
| 197 | free(search); | |||
| 198 | --stats.searches; | |||
| 199 | } | |||
| 200 | ||||
| 201 | /* Returns true (1) if key is a direct subordinate of base. | |||
| 202 | */ | |||
| 203 | int | |||
| 204 | is_child_of(struct btval *key, const char *base) | |||
| 205 | { | |||
| 206 | size_t ksz, bsz; | |||
| 207 | char *p; | |||
| 208 | ||||
| 209 | if ((p = memchr(key->data, ',', key->size)) == NULL((void*)0)) | |||
| 210 | return 0; | |||
| 211 | p++; | |||
| 212 | ksz = key->size - (p - (char *)key->data); | |||
| 213 | bsz = strlen(base); | |||
| 214 | return (ksz == bsz && bcmp(p, base, ksz) == 0); | |||
| 215 | } | |||
| 216 | ||||
| 217 | static int | |||
| 218 | check_search_entry(struct btval *key, struct btval *val, struct search *search) | |||
| 219 | { | |||
| 220 | int rc; | |||
| 221 | char *dn0; | |||
| 222 | struct ber_element *elm; | |||
| 223 | ||||
| 224 | /* verify entry is a direct subordinate of basedn */ | |||
| 225 | if (search->scope
| |||
| 226 | !is_child_of(key, search->basedn)) { | |||
| 227 | log_debug("not a direct subordinate of base"); | |||
| 228 | return 0; | |||
| 229 | } | |||
| 230 | ||||
| 231 | if ((dn0 = strndup(key->data, key->size)) == NULL((void*)0)) { | |||
| 232 | log_warn("malloc"); | |||
| 233 | return 0; | |||
| 234 | } | |||
| 235 | ||||
| 236 | if (!authorized(search->conn, search->ns, ACI_READ0x01, dn0, | |||
| 237 | NULL((void*)0), LDAP_SCOPE_BASE)) { | |||
| 238 | /* LDAP_INSUFFICIENT_ACCESS */ | |||
| 239 | free(dn0); | |||
| 240 | return 0; | |||
| 241 | } | |||
| 242 | free(dn0); | |||
| 243 | ||||
| 244 | if ((elm = namespace_db2ber(search->ns, val)) == NULL((void*)0)) { | |||
| 245 | log_warnx("failed to parse entry [%.*s]", | |||
| 246 | (int)key->size, (char *)key->data); | |||
| 247 | return 0; | |||
| 248 | } | |||
| 249 | ||||
| 250 | if (ldap_matches_filter(elm, search->plan) != 0) { | |||
| 251 | ober_free_elements(elm); | |||
| 252 | return 0; | |||
| 253 | } | |||
| 254 | ||||
| 255 | rc = search_result(key->data, key->size, elm, search); | |||
| 256 | ober_free_elements(elm); | |||
| 257 | ||||
| 258 | if (rc == 0) | |||
| 259 | search->nmatched++; | |||
| 260 | ||||
| 261 | return rc; | |||
| 262 | } | |||
| 263 | ||||
| 264 | static int | |||
| 265 | mk_dup(struct search *search, struct btval *key) | |||
| 266 | { | |||
| 267 | struct uniqdn *udn; | |||
| 268 | ||||
| 269 | if ((udn = calloc(1, sizeof(*udn))) == NULL((void*)0)) | |||
| 270 | return BT_FAIL-1; | |||
| 271 | ||||
| 272 | if ((udn->key.data = malloc(key->size)) == NULL((void*)0)) { | |||
| 273 | free(udn); | |||
| 274 | return BT_FAIL-1; | |||
| 275 | } | |||
| 276 | bcopy(key->data, udn->key.data, key->size); | |||
| 277 | udn->key.size = key->size; | |||
| 278 | RB_INSERT(dn_tree, &search->uniqdns, udn)dn_tree_RB_INSERT(&search->uniqdns, udn); | |||
| 279 | return BT_SUCCESS0; | |||
| 280 | } | |||
| 281 | ||||
| 282 | /* check if this entry was already sent */ | |||
| 283 | static int | |||
| 284 | is_dup(struct search *search, struct btval *key) | |||
| 285 | { | |||
| 286 | struct uniqdn find; | |||
| 287 | ||||
| 288 | find.key.data = key->data; | |||
| 289 | find.key.size = key->size; | |||
| 290 | return RB_FIND(dn_tree, &search->uniqdns, &find)dn_tree_RB_FIND(&search->uniqdns, &find) != NULL((void*)0); | |||
| 291 | } | |||
| 292 | ||||
| 293 | void | |||
| 294 | conn_search(struct search *search) | |||
| 295 | { | |||
| 296 | int i, rc = BT_SUCCESS0; | |||
| 297 | unsigned int reason = LDAP_SUCCESS; | |||
| 298 | unsigned int op = BT_NEXT; | |||
| 299 | time_t now; | |||
| 300 | struct conn *conn; | |||
| 301 | struct btree_txn *txn; | |||
| 302 | struct btval key, ikey, val; | |||
| 303 | ||||
| 304 | conn = search->conn; | |||
| 305 | ||||
| 306 | memset(&key, 0, sizeof(key)); | |||
| 307 | memset(&val, 0, sizeof(val)); | |||
| 308 | ||||
| 309 | if (search->plan->indexed) | |||
| 310 | txn = search->indx_txn; | |||
| 311 | else | |||
| 312 | txn = search->data_txn; | |||
| 313 | ||||
| 314 | if (!search->init) { | |||
| 315 | search->cursor = btree_txn_cursor_open(NULL((void*)0), txn); | |||
| 316 | if (search->cursor == NULL((void*)0)) { | |||
| 317 | log_warn("btree_cursor_open"); | |||
| 318 | search_close(search); | |||
| 319 | return; | |||
| 320 | } | |||
| 321 | ||||
| 322 | if (search->plan->indexed) { | |||
| 323 | search->cindx = TAILQ_FIRST(&search->plan->indices)((&search->plan->indices)->tqh_first); | |||
| 324 | key.data = search->cindx->prefix; | |||
| 325 | log_debug("init index scan on [%s]", | |||
| 326 | search->cindx->prefix); | |||
| 327 | } else { | |||
| 328 | if (*search->basedn) | |||
| 329 | key.data = search->basedn; | |||
| 330 | log_debug("init full scan"); | |||
| 331 | } | |||
| 332 | ||||
| 333 | if (key.data) { | |||
| 334 | key.size = strlen(key.data); | |||
| 335 | op = BT_CURSOR; | |||
| 336 | } | |||
| 337 | ||||
| 338 | search->init = 1; | |||
| 339 | } | |||
| 340 | ||||
| 341 | for (i = 0; i < 10 && rc == BT_SUCCESS0; i++) { | |||
| 342 | rc = btree_cursor_get(search->cursor, &key, &val, op); | |||
| 343 | op = BT_NEXT; | |||
| 344 | ||||
| 345 | if (rc == BT_SUCCESS0 && search->plan->indexed) { | |||
| 346 | log_debug("found index %.*s", (int)key.size, | |||
| 347 | (char *)key.data); | |||
| 348 | ||||
| 349 | if (!has_prefix(&key, search->cindx->prefix)) { | |||
| 350 | log_debug("scanned past index prefix [%s]", | |||
| 351 | search->cindx->prefix); | |||
| 352 | btval_reset(&val); | |||
| 353 | btval_reset(&key); | |||
| 354 | rc = BT_FAIL-1; | |||
| 355 | errno(*__errno()) = ENOENT2; | |||
| 356 | } | |||
| 357 | } | |||
| 358 | ||||
| 359 | if (rc == BT_FAIL-1 && errno(*__errno()) == ENOENT2 && | |||
| 360 | search->plan->indexed > 1) { | |||
| 361 | search->cindx = TAILQ_NEXT(search->cindx, next)((search->cindx)->next.tqe_next); | |||
| 362 | if (search->cindx != NULL((void*)0)) { | |||
| 363 | rc = BT_SUCCESS0; | |||
| 364 | memset(&key, 0, sizeof(key)); | |||
| 365 | key.data = search->cindx->prefix; | |||
| 366 | key.size = strlen(key.data); | |||
| 367 | log_debug("re-init cursor on [%s]", | |||
| 368 | search->cindx->prefix); | |||
| 369 | op = BT_CURSOR; | |||
| 370 | continue; | |||
| 371 | } | |||
| 372 | } | |||
| 373 | ||||
| 374 | if (rc != BT_SUCCESS0) { | |||
| 375 | if (errno(*__errno()) != ENOENT2) { | |||
| 376 | log_warnx("btree failure"); | |||
| 377 | reason = LDAP_OTHER; | |||
| 378 | } | |||
| 379 | break; | |||
| 380 | } | |||
| 381 | ||||
| 382 | search->nscanned++; | |||
| 383 | ||||
| 384 | if (search->plan->indexed) { | |||
| 385 | bcopy(&key, &ikey, sizeof(key)); | |||
| 386 | memset(&key, 0, sizeof(key)); | |||
| 387 | btval_reset(&val); | |||
| 388 | ||||
| 389 | rc = index_to_dn(search->ns, &ikey, &key); | |||
| 390 | btval_reset(&ikey); | |||
| 391 | if (rc != 0) { | |||
| 392 | reason = LDAP_OTHER; | |||
| 393 | break; | |||
| 394 | } | |||
| 395 | ||||
| 396 | log_debug("lookup indexed key [%.*s]", | |||
| 397 | (int)key.size, (char *)key.data); | |||
| 398 | ||||
| 399 | /* verify entry is a direct subordinate */ | |||
| 400 | if (search->scope == LDAP_SCOPE_ONELEVEL && | |||
| 401 | !is_child_of(&key, search->basedn)) { | |||
| 402 | log_debug("not a direct subordinate of base"); | |||
| 403 | btval_reset(&key); | |||
| 404 | continue; | |||
| 405 | } | |||
| 406 | ||||
| 407 | if (search->plan->indexed > 1 && is_dup(search, &key)) { | |||
| 408 | log_debug("skipping duplicate dn %.*s", | |||
| 409 | (int)key.size, (char *)key.data); | |||
| 410 | search->ndups++; | |||
| 411 | btval_reset(&key); | |||
| 412 | continue; | |||
| 413 | } | |||
| 414 | ||||
| 415 | rc = btree_txn_get(NULL((void*)0), search->data_txn, &key, &val); | |||
| 416 | if (rc == BT_FAIL-1) { | |||
| 417 | if (errno(*__errno()) == ENOENT2) { | |||
| 418 | log_warnx("indexed key [%.*s]" | |||
| 419 | " doesn't exist!", | |||
| 420 | (int)key.size, (char *)key.data); | |||
| 421 | btval_reset(&key); | |||
| 422 | rc = BT_SUCCESS0; | |||
| 423 | continue; | |||
| 424 | } | |||
| 425 | log_warnx("btree failure"); | |||
| 426 | btval_reset(&key); | |||
| 427 | reason = LDAP_OTHER; | |||
| 428 | break; | |||
| 429 | } | |||
| 430 | } | |||
| 431 | ||||
| 432 | log_debug("found dn %.*s", (int)key.size, (char *)key.data); | |||
| 433 | ||||
| 434 | if (!has_suffix(&key, search->basedn)) { | |||
| 435 | btval_reset(&val); | |||
| 436 | btval_reset(&key); | |||
| 437 | if (search->plan->indexed) | |||
| 438 | continue; | |||
| 439 | else { | |||
| 440 | log_debug("scanned past basedn suffix"); | |||
| 441 | rc = 1; | |||
| 442 | break; | |||
| 443 | } | |||
| 444 | } | |||
| 445 | ||||
| 446 | rc = check_search_entry(&key, &val, search); | |||
| 447 | btval_reset(&val); | |||
| 448 | if (rc == BT_SUCCESS0 && search->plan->indexed > 1) | |||
| 449 | rc = mk_dup(search, &key); | |||
| 450 | ||||
| 451 | btval_reset(&key); | |||
| 452 | ||||
| 453 | /* Check if we have passed the size limit. */ | |||
| 454 | if (rc == BT_SUCCESS0 && search->szlim > 0 && | |||
| 455 | search->nmatched > search->szlim) { | |||
| 456 | log_debug("search %d/%lld has exceeded size limit (%lld)", | |||
| 457 | search->conn->fd, search->req->msgid, | |||
| 458 | search->szlim); | |||
| 459 | reason = LDAP_SIZELIMIT_EXCEEDED; | |||
| 460 | rc = BT_FAIL-1; | |||
| 461 | } | |||
| 462 | } | |||
| 463 | ||||
| 464 | /* Check if we have passed the time limit. */ | |||
| 465 | now = time(0); | |||
| 466 | if (rc == 0 && search->tmlim > 0 && | |||
| 467 | search->started_at + search->tmlim < now) { | |||
| 468 | log_debug("search %d/%lld has exceeded time limit (%lld)", | |||
| 469 | search->conn->fd, search->req->msgid, | |||
| 470 | search->tmlim); | |||
| 471 | reason = LDAP_TIMELIMIT_EXCEEDED; | |||
| 472 | rc = 1; | |||
| 473 | ++stats.timeouts; | |||
| 474 | } | |||
| 475 | ||||
| 476 | if (rc == 0) { | |||
| 477 | bufferevent_enable(search->conn->bev, EV_WRITE0x04); | |||
| 478 | } else { | |||
| 479 | log_debug("%u scanned, %u matched, %u dups", | |||
| 480 | search->nscanned, search->nmatched, search->ndups); | |||
| 481 | send_ldap_result(conn, search->req->msgid, | |||
| 482 | LDAP_RES_SEARCH_RESULT, reason); | |||
| 483 | if (errno(*__errno()) != ENOENT2) | |||
| 484 | log_debug("search failed: %s", strerror(errno(*__errno()))); | |||
| 485 | search_close(search); | |||
| 486 | } | |||
| 487 | } | |||
| 488 | ||||
| 489 | static void | |||
| 490 | ldap_search_root_dse(struct search *search) | |||
| 491 | { | |||
| 492 | struct namespace *ns; | |||
| 493 | struct ber_element *root, *elm, *key, *val; | |||
| 494 | ||||
| 495 | if ((root = ober_add_sequence(NULL((void*)0))) == NULL((void*)0)) { | |||
| 496 | return; | |||
| 497 | } | |||
| 498 | ||||
| 499 | elm = ober_add_sequence(root); | |||
| 500 | key = ober_add_string(elm, "objectClass"); | |||
| 501 | val = ober_add_set(key); | |||
| 502 | ober_add_string(val, "top"); | |||
| 503 | ||||
| 504 | elm = ober_add_sequence(elm); | |||
| 505 | key = ober_add_string(elm, "supportedLDAPVersion"); | |||
| 506 | val = ober_add_set(key); | |||
| 507 | ober_add_string(val, "3"); | |||
| 508 | ||||
| 509 | elm = ober_add_sequence(elm); | |||
| 510 | key = ober_add_string(elm, "namingContexts"); | |||
| 511 | val = ober_add_set(key); | |||
| 512 | TAILQ_FOREACH(ns, &conf->namespaces, next)for((ns) = ((&conf->namespaces)->tqh_first); (ns) != ((void*)0); (ns) = ((ns)->next.tqe_next)) | |||
| 513 | val = ober_add_string(val, ns->suffix); | |||
| 514 | ||||
| 515 | elm = ober_add_sequence(elm); | |||
| 516 | key = ober_add_string(elm, "supportedExtension"); | |||
| 517 | val = ober_add_set(key); | |||
| 518 | ober_add_string(val, "1.3.6.1.4.1.1466.20037"); /* StartTLS */ | |||
| 519 | ||||
| 520 | elm = ober_add_sequence(elm); | |||
| 521 | key = ober_add_string(elm, "supportedFeatures"); | |||
| 522 | val = ober_add_set(key); | |||
| 523 | /* All Operational Attributes (RFC 3673) */ | |||
| 524 | ober_add_string(val, "1.3.6.1.4.1.4203.1.5.1"); | |||
| 525 | ||||
| 526 | elm = ober_add_sequence(elm); | |||
| 527 | key = ober_add_string(elm, "subschemaSubentry"); | |||
| 528 | val = ober_add_set(key); | |||
| 529 | ober_add_string(val, "cn=schema"); | |||
| 530 | ||||
| 531 | if ((search->conn->s_flags & F_SECURE0x04) == F_SECURE0x04) { | |||
| 532 | elm = ober_add_sequence(elm); | |||
| 533 | key = ober_add_string(elm, "supportedSASLMechanisms"); | |||
| 534 | val = ober_add_set(key); | |||
| 535 | ober_add_string(val, "PLAIN"); | |||
| 536 | } | |||
| 537 | ||||
| 538 | search_result("", 0, root, search); | |||
| 539 | ober_free_elements(root); | |||
| 540 | send_ldap_result(search->conn, search->req->msgid, | |||
| 541 | LDAP_RES_SEARCH_RESULT, LDAP_SUCCESS); | |||
| 542 | search_close(search); | |||
| 543 | } | |||
| 544 | ||||
| 545 | static void | |||
| 546 | ldap_search_subschema(struct search *search) | |||
| 547 | { | |||
| 548 | char buf[1024]; | |||
| 549 | struct ber_element *root, *elm, *key, *val; | |||
| 550 | struct object *obj; | |||
| 551 | struct attr_type *at; | |||
| 552 | int rc, i; | |||
| 553 | ||||
| 554 | if ((root = ober_add_sequence(NULL((void*)0))) == NULL((void*)0)) { | |||
| 555 | return; | |||
| 556 | } | |||
| 557 | ||||
| 558 | elm = ober_add_sequence(root); | |||
| 559 | key = ober_add_string(elm, "objectClass"); | |||
| 560 | val = ober_add_set(key); | |||
| 561 | val = ober_add_string(val, "top"); | |||
| 562 | ober_add_string(val, "subschema"); | |||
| 563 | ||||
| 564 | elm = ober_add_sequence(elm); | |||
| 565 | key = ober_add_string(elm, "createTimestamp"); | |||
| 566 | val = ober_add_set(key); | |||
| 567 | ober_add_string(val, ldap_strftime(stats.started_at)); | |||
| 568 | ||||
| 569 | elm = ober_add_sequence(elm); | |||
| 570 | key = ober_add_string(elm, "modifyTimestamp"); | |||
| 571 | val = ober_add_set(key); | |||
| 572 | ober_add_string(val, ldap_strftime(stats.started_at)); | |||
| 573 | ||||
| 574 | elm = ober_add_sequence(elm); | |||
| 575 | key = ober_add_string(elm, "subschemaSubentry"); | |||
| 576 | val = ober_add_set(key); | |||
| 577 | ober_add_string(val, "cn=schema"); | |||
| 578 | ||||
| 579 | if (should_include_attribute("objectClasses", search, 1)) { | |||
| 580 | elm = ober_add_sequence(elm); | |||
| 581 | key = ober_add_string(elm, "objectClasses"); | |||
| 582 | val = ober_add_set(key); | |||
| 583 | ||||
| 584 | RB_FOREACH(obj, object_tree, &conf->schema->objects)for ((obj) = object_tree_RB_MINMAX(&conf->schema->objects , -1); (obj) != ((void*)0); (obj) = object_tree_RB_NEXT(obj)) { | |||
| 585 | if (schema_dump_object(obj, buf, sizeof(buf)) != 0) { | |||
| 586 | rc = LDAP_OTHER; | |||
| 587 | goto done; | |||
| 588 | } | |||
| 589 | val = ober_add_string(val, buf); | |||
| 590 | } | |||
| 591 | } | |||
| 592 | ||||
| 593 | if (should_include_attribute("attributeTypes", search, 1)) { | |||
| 594 | elm = ober_add_sequence(elm); | |||
| 595 | key = ober_add_string(elm, "attributeTypes"); | |||
| 596 | val = ober_add_set(key); | |||
| 597 | ||||
| 598 | RB_FOREACH(at, attr_type_tree, &conf->schema->attr_types)for ((at) = attr_type_tree_RB_MINMAX(&conf->schema-> attr_types, -1); (at) != ((void*)0); (at) = attr_type_tree_RB_NEXT (at)) { | |||
| 599 | if (schema_dump_attribute(at, buf, sizeof(buf)) != 0) { | |||
| 600 | rc = LDAP_OTHER; | |||
| 601 | goto done; | |||
| 602 | } | |||
| 603 | val = ober_add_string(val, buf); | |||
| 604 | } | |||
| 605 | } | |||
| 606 | ||||
| 607 | if (should_include_attribute("matchingRules", search, 1)) { | |||
| 608 | elm = ober_add_sequence(elm); | |||
| 609 | key = ober_add_string(elm, "matchingRules"); | |||
| 610 | val = ober_add_set(key); | |||
| 611 | ||||
| 612 | for (i = 0; i < num_match_rules; i++) { | |||
| 613 | if (schema_dump_match_rule(&match_rules[i], buf, | |||
| 614 | sizeof(buf)) != 0) { | |||
| 615 | rc = LDAP_OTHER; | |||
| 616 | goto done; | |||
| 617 | } | |||
| 618 | val = ober_add_string(val, buf); | |||
| 619 | } | |||
| 620 | } | |||
| 621 | ||||
| 622 | search_result("cn=schema", 9, root, search); | |||
| 623 | rc = LDAP_SUCCESS; | |||
| 624 | ||||
| 625 | done: | |||
| 626 | ober_free_elements(root); | |||
| 627 | send_ldap_result(search->conn, search->req->msgid, | |||
| 628 | LDAP_RES_SEARCH_RESULT, rc); | |||
| 629 | search_close(search); | |||
| 630 | } | |||
| 631 | ||||
| 632 | static int | |||
| 633 | add_index(struct plan *plan, const char *fmt, ...) | |||
| 634 | { | |||
| 635 | struct index *indx; | |||
| 636 | va_list ap; | |||
| 637 | int rc; | |||
| 638 | ||||
| 639 | if ((indx = calloc(1, sizeof(*indx))) == NULL((void*)0)) | |||
| 640 | return -1; | |||
| 641 | ||||
| 642 | va_start(ap, fmt)__builtin_va_start(ap, fmt); | |||
| 643 | rc = vasprintf(&indx->prefix, fmt, ap); | |||
| 644 | va_end(ap)__builtin_va_end(ap); | |||
| 645 | if (rc == -1) { | |||
| 646 | free(indx); | |||
| 647 | return -1; | |||
| 648 | } | |||
| 649 | ||||
| 650 | normalize_dn(indx->prefix); | |||
| 651 | ||||
| 652 | TAILQ_INSERT_TAIL(&plan->indices, indx, next)do { (indx)->next.tqe_next = ((void*)0); (indx)->next.tqe_prev = (&plan->indices)->tqh_last; *(&plan->indices )->tqh_last = (indx); (&plan->indices)->tqh_last = &(indx)->next.tqe_next; } while (0); | |||
| 653 | plan->indexed++; | |||
| 654 | ||||
| 655 | return 0; | |||
| 656 | } | |||
| 657 | ||||
| 658 | static int | |||
| 659 | plan_get_attr(struct plan *plan, struct namespace *ns, char *attr) | |||
| 660 | { | |||
| 661 | if (ns->relax) { | |||
| 662 | /* | |||
| 663 | * Under relaxed schema checking, all attributes | |||
| 664 | * are considered directory strings with case-insensitive | |||
| 665 | * matching. | |||
| 666 | */ | |||
| 667 | plan->at = lookup_attribute(conf->schema, "name"); | |||
| 668 | plan->adesc = attr; | |||
| 669 | } else | |||
| 670 | plan->at = lookup_attribute(conf->schema, attr); | |||
| 671 | ||||
| 672 | if (plan->at == NULL((void*)0)) { | |||
| 673 | log_debug("%s: no such attribute, undefined term", attr); | |||
| 674 | return -1; | |||
| 675 | } | |||
| 676 | ||||
| 677 | return 0; | |||
| 678 | } | |||
| 679 | ||||
| 680 | static struct plan * | |||
| 681 | search_planner(struct namespace *ns, struct ber_element *filter) | |||
| 682 | { | |||
| 683 | int class; | |||
| 684 | unsigned int type; | |||
| 685 | char *s, *attr; | |||
| 686 | struct ber_element *elm; | |||
| 687 | struct index *indx; | |||
| 688 | struct plan *plan, *arg = NULL((void*)0); | |||
| 689 | ||||
| 690 | if (filter->be_class != BER_CLASS_CONTEXT0x2) { | |||
| 691 | log_warnx("invalid class %d in filter", filter->be_class); | |||
| 692 | return NULL((void*)0); | |||
| 693 | } | |||
| 694 | ||||
| 695 | if ((plan = calloc(1, sizeof(*plan))) == NULL((void*)0)) { | |||
| 696 | log_warn("search_planner: calloc"); | |||
| 697 | return NULL((void*)0); | |||
| 698 | } | |||
| 699 | plan->op = filter->be_type; | |||
| 700 | TAILQ_INIT(&plan->args)do { (&plan->args)->tqh_first = ((void*)0); (&plan ->args)->tqh_last = &(&plan->args)->tqh_first ; } while (0); | |||
| 701 | TAILQ_INIT(&plan->indices)do { (&plan->indices)->tqh_first = ((void*)0); (& plan->indices)->tqh_last = &(&plan->indices) ->tqh_first; } while (0); | |||
| 702 | ||||
| 703 | switch (filter->be_type) { | |||
| 704 | case LDAP_FILT_EQ: | |||
| 705 | case LDAP_FILT_APPR: | |||
| 706 | if (ober_scanf_elements(filter, "{ss", &attr, &s) != 0) | |||
| 707 | goto fail; | |||
| 708 | if (plan_get_attr(plan, ns, attr) == -1) | |||
| 709 | plan->undefined = 1; | |||
| 710 | else if (plan->at->equality == NULL((void*)0)) { | |||
| 711 | log_debug("'%s' doesn't define equality matching", | |||
| 712 | attr); | |||
| 713 | plan->undefined = 1; | |||
| 714 | } else { | |||
| 715 | plan->assert.value = s; | |||
| 716 | if (namespace_has_index(ns, attr, INDEX_EQUAL)) | |||
| 717 | add_index(plan, "%s=%s,", attr, s); | |||
| 718 | } | |||
| 719 | break; | |||
| 720 | case LDAP_FILT_SUBS: | |||
| 721 | if (ober_scanf_elements(filter, "{s{ets", | |||
| 722 | &attr, &plan->assert.substring, &class, &type, &s) != 0) | |||
| 723 | goto fail; | |||
| 724 | if (plan_get_attr(plan, ns, attr) == -1) | |||
| 725 | plan->undefined = 1; | |||
| 726 | else if (plan->at->substr == NULL((void*)0)) { | |||
| 727 | log_debug("'%s' doesn't define substring matching", | |||
| 728 | attr); | |||
| 729 | plan->undefined = 1; | |||
| 730 | } else if (class == BER_CLASS_CONTEXT0x2 && | |||
| 731 | type == LDAP_FILT_SUBS_INIT) { | |||
| 732 | /* Only prefix substrings are usable as index. */ | |||
| 733 | if (namespace_has_index(ns, attr, INDEX_EQUAL)) | |||
| 734 | add_index(plan, "%s=%s", attr, s); | |||
| 735 | } | |||
| 736 | break; | |||
| 737 | case LDAP_FILT_PRES: | |||
| 738 | if (ober_scanf_elements(filter, "s", &attr) != 0) | |||
| 739 | goto fail; | |||
| 740 | if (plan_get_attr(plan, ns, attr) == -1) | |||
| 741 | plan->undefined = 1; | |||
| 742 | else if (strcasecmp(attr, "objectClass") != 0) { | |||
| 743 | if (namespace_has_index(ns, attr, INDEX_PRESENCE)) | |||
| 744 | add_index(plan, "%s=", attr); | |||
| 745 | } | |||
| 746 | break; | |||
| 747 | case LDAP_FILT_AND: | |||
| 748 | if (ober_scanf_elements(filter, "(e", &elm) != 0) | |||
| 749 | goto fail; | |||
| 750 | for (; elm; elm = elm->be_next) { | |||
| 751 | if ((arg = search_planner(ns, elm)) == NULL((void*)0)) | |||
| 752 | goto fail; | |||
| 753 | if (arg->undefined) { | |||
| 754 | plan->undefined = 1; | |||
| 755 | break; | |||
| 756 | } | |||
| 757 | TAILQ_INSERT_TAIL(&plan->args, arg, next)do { (arg)->next.tqe_next = ((void*)0); (arg)->next.tqe_prev = (&plan->args)->tqh_last; *(&plan->args)-> tqh_last = (arg); (&plan->args)->tqh_last = &(arg )->next.tqe_next; } while (0); | |||
| 758 | } | |||
| 759 | ||||
| 760 | /* The term is undefined if any arg is undefined. */ | |||
| 761 | if (plan->undefined) | |||
| 762 | break; | |||
| 763 | ||||
| 764 | /* Select an index to use. */ | |||
| 765 | TAILQ_FOREACH(arg, &plan->args, next)for((arg) = ((&plan->args)->tqh_first); (arg) != (( void*)0); (arg) = ((arg)->next.tqe_next)) { | |||
| 766 | if (arg->indexed) { | |||
| 767 | TAILQ_CONCAT(&plan->indices, &arg->indices,do { if (!(((&arg->indices)->tqh_first) == ((void*) 0))) { *(&plan->indices)->tqh_last = (&arg-> indices)->tqh_first; (&arg->indices)->tqh_first-> next.tqe_prev = (&plan->indices)->tqh_last; (&plan ->indices)->tqh_last = (&arg->indices)->tqh_last ; do { ((&arg->indices))->tqh_first = ((void*)0); ( (&arg->indices))->tqh_last = &((&arg->indices ))->tqh_first; } while (0); } } while (0) | |||
| 768 | next)do { if (!(((&arg->indices)->tqh_first) == ((void*) 0))) { *(&plan->indices)->tqh_last = (&arg-> indices)->tqh_first; (&arg->indices)->tqh_first-> next.tqe_prev = (&plan->indices)->tqh_last; (&plan ->indices)->tqh_last = (&arg->indices)->tqh_last ; do { ((&arg->indices))->tqh_first = ((void*)0); ( (&arg->indices))->tqh_last = &((&arg->indices ))->tqh_first; } while (0); } } while (0); | |||
| 769 | plan->indexed = arg->indexed; | |||
| 770 | break; | |||
| 771 | } | |||
| 772 | } | |||
| 773 | break; | |||
| 774 | case LDAP_FILT_OR: | |||
| 775 | if (ober_scanf_elements(filter, "(e", &elm) != 0) | |||
| 776 | goto fail; | |||
| 777 | for (; elm; elm = elm->be_next) { | |||
| 778 | if ((arg = search_planner(ns, elm)) == NULL((void*)0)) | |||
| 779 | goto fail; | |||
| 780 | TAILQ_INSERT_TAIL(&plan->args, arg, next)do { (arg)->next.tqe_next = ((void*)0); (arg)->next.tqe_prev = (&plan->args)->tqh_last; *(&plan->args)-> tqh_last = (arg); (&plan->args)->tqh_last = &(arg )->next.tqe_next; } while (0); | |||
| 781 | } | |||
| 782 | ||||
| 783 | /* The term is undefined iff all args are undefined. */ | |||
| 784 | plan->undefined = 1; | |||
| 785 | TAILQ_FOREACH(arg, &plan->args, next)for((arg) = ((&plan->args)->tqh_first); (arg) != (( void*)0); (arg) = ((arg)->next.tqe_next)) | |||
| 786 | if (!arg->undefined) { | |||
| 787 | plan->undefined = 0; | |||
| 788 | break; | |||
| 789 | } | |||
| 790 | ||||
| 791 | TAILQ_FOREACH(arg, &plan->args, next)for((arg) = ((&plan->args)->tqh_first); (arg) != (( void*)0); (arg) = ((arg)->next.tqe_next)) { | |||
| 792 | if (!arg->indexed) { | |||
| 793 | plan->indexed = 0; | |||
| 794 | break; | |||
| 795 | } | |||
| 796 | TAILQ_FOREACH(indx, &arg->indices, next)for((indx) = ((&arg->indices)->tqh_first); (indx) != ((void*)0); (indx) = ((indx)->next.tqe_next)) | |||
| 797 | plan->indexed++; | |||
| 798 | TAILQ_CONCAT(&plan->indices, &arg->indices, next)do { if (!(((&arg->indices)->tqh_first) == ((void*) 0))) { *(&plan->indices)->tqh_last = (&arg-> indices)->tqh_first; (&arg->indices)->tqh_first-> next.tqe_prev = (&plan->indices)->tqh_last; (&plan ->indices)->tqh_last = (&arg->indices)->tqh_last ; do { ((&arg->indices))->tqh_first = ((void*)0); ( (&arg->indices))->tqh_last = &((&arg->indices ))->tqh_first; } while (0); } } while (0); | |||
| 799 | } | |||
| 800 | break; | |||
| 801 | case LDAP_FILT_NOT: | |||
| 802 | if (ober_scanf_elements(filter, "{e", &elm) != 0) | |||
| 803 | goto fail; | |||
| 804 | if ((arg = search_planner(ns, elm)) == NULL((void*)0)) | |||
| 805 | goto fail; | |||
| 806 | TAILQ_INSERT_TAIL(&plan->args, arg, next)do { (arg)->next.tqe_next = ((void*)0); (arg)->next.tqe_prev = (&plan->args)->tqh_last; *(&plan->args)-> tqh_last = (arg); (&plan->args)->tqh_last = &(arg )->next.tqe_next; } while (0); | |||
| 807 | ||||
| 808 | plan->undefined = arg->undefined; | |||
| 809 | if (plan->indexed) { | |||
| 810 | log_debug("NOT filter forced unindexed search"); | |||
| 811 | plan->indexed = 0; | |||
| 812 | } | |||
| 813 | break; | |||
| 814 | ||||
| 815 | default: | |||
| 816 | log_warnx("filter type %u not implemented", filter->be_type); | |||
| 817 | plan->undefined = 1; | |||
| 818 | break; | |||
| 819 | } | |||
| 820 | ||||
| 821 | return plan; | |||
| 822 | ||||
| 823 | fail: | |||
| 824 | free(plan); | |||
| 825 | return NULL((void*)0); | |||
| 826 | } | |||
| 827 | ||||
| 828 | void | |||
| 829 | filter_free(struct plan *filter) | |||
| 830 | { | |||
| 831 | struct index *indx; | |||
| 832 | struct plan *arg; | |||
| 833 | ||||
| 834 | if (filter) { | |||
| 835 | while ((arg = TAILQ_FIRST(&filter->args)((&filter->args)->tqh_first)) != NULL((void*)0)) { | |||
| 836 | TAILQ_REMOVE(&filter->args, arg, next)do { if (((arg)->next.tqe_next) != ((void*)0)) (arg)->next .tqe_next->next.tqe_prev = (arg)->next.tqe_prev; else ( &filter->args)->tqh_last = (arg)->next.tqe_prev; *(arg)->next.tqe_prev = (arg)->next.tqe_next; ; ; } while (0); | |||
| 837 | filter_free(arg); | |||
| 838 | } | |||
| 839 | while ((indx = TAILQ_FIRST(&filter->indices)((&filter->indices)->tqh_first)) != NULL((void*)0)) { | |||
| 840 | TAILQ_REMOVE(&filter->indices, indx, next)do { if (((indx)->next.tqe_next) != ((void*)0)) (indx)-> next.tqe_next->next.tqe_prev = (indx)->next.tqe_prev; else (&filter->indices)->tqh_last = (indx)->next.tqe_prev ; *(indx)->next.tqe_prev = (indx)->next.tqe_next; ; ; } while (0); | |||
| 841 | free(indx->prefix); | |||
| 842 | free(indx); | |||
| 843 | } | |||
| 844 | free(filter); | |||
| 845 | } | |||
| 846 | } | |||
| 847 | ||||
| 848 | int | |||
| 849 | ldap_search(struct request *req) | |||
| 850 | { | |||
| 851 | long long reason = LDAP_OTHER; | |||
| 852 | struct referrals *refs; | |||
| 853 | struct search *search = NULL((void*)0); | |||
| 854 | ||||
| 855 | if (stats.searches > MAX_SEARCHES200) { | |||
| ||||
| 856 | log_warnx("refusing more than %u concurrent searches", | |||
| 857 | MAX_SEARCHES200); | |||
| 858 | reason = LDAP_BUSY; | |||
| 859 | goto done; | |||
| 860 | } | |||
| 861 | ++stats.searches; | |||
| 862 | ++stats.req_search; | |||
| 863 | ||||
| 864 | if ((search = calloc(1, sizeof(*search))) == NULL((void*)0)) | |||
| 865 | return -1; | |||
| 866 | search->req = req; | |||
| 867 | search->conn = req->conn; | |||
| 868 | search->init = 0; | |||
| 869 | search->started_at = time(0); | |||
| 870 | TAILQ_INSERT_HEAD(&req->conn->searches, search, next)do { if (((search)->next.tqe_next = (&req->conn-> searches)->tqh_first) != ((void*)0)) (&req->conn-> searches)->tqh_first->next.tqe_prev = &(search)-> next.tqe_next; else (&req->conn->searches)->tqh_last = &(search)->next.tqe_next; (&req->conn->searches )->tqh_first = (search); (search)->next.tqe_prev = & (&req->conn->searches)->tqh_first; } while (0); | |||
| 871 | RB_INIT(&search->uniqdns)do { (&search->uniqdns)->rbh_root = ((void*)0); } while (0); | |||
| 872 | ||||
| 873 | if (ober_scanf_elements(req->op, "{sEEiibeSeS", | |||
| 874 | &search->basedn, | |||
| 875 | &search->scope, | |||
| 876 | &search->deref, | |||
| 877 | &search->szlim, | |||
| 878 | &search->tmlim, | |||
| 879 | &search->typesonly, | |||
| 880 | &search->filter, | |||
| 881 | &search->attrlist) != 0) { | |||
| 882 | log_warnx("failed to parse search request"); | |||
| 883 | reason = LDAP_PROTOCOL_ERROR; | |||
| 884 | goto done; | |||
| 885 | } | |||
| 886 | ||||
| 887 | normalize_dn(search->basedn); | |||
| 888 | log_debug("base dn = %s, scope = %lld", search->basedn, search->scope); | |||
| 889 | ||||
| 890 | if (*search->basedn == '\0') { | |||
| 891 | /* request for the root DSE */ | |||
| 892 | if (!authorized(req->conn, NULL((void*)0), ACI_READ0x01, "", | |||
| 893 | NULL((void*)0), LDAP_SCOPE_BASE)) { | |||
| 894 | reason = LDAP_INSUFFICIENT_ACCESS; | |||
| 895 | goto done; | |||
| 896 | } | |||
| 897 | if (search->scope != LDAP_SCOPE_BASE) { | |||
| 898 | /* only base searches are valid */ | |||
| 899 | reason = LDAP_NO_SUCH_OBJECT; | |||
| 900 | goto done; | |||
| 901 | } | |||
| 902 | /* TODO: verify filter is (objectClass=*) */ | |||
| 903 | ldap_search_root_dse(search); | |||
| 904 | return 0; | |||
| 905 | } | |||
| 906 | ||||
| 907 | if (strcasecmp(search->basedn, "cn=schema") == 0) { | |||
| 908 | /* request for the subschema subentries */ | |||
| 909 | if (!authorized(req->conn, NULL((void*)0), ACI_READ0x01, | |||
| 910 | "cn=schema", NULL((void*)0), LDAP_SCOPE_BASE)) { | |||
| 911 | reason = LDAP_INSUFFICIENT_ACCESS; | |||
| 912 | goto done; | |||
| 913 | } | |||
| 914 | if (search->scope != LDAP_SCOPE_BASE) { | |||
| 915 | /* only base searches are valid */ | |||
| 916 | reason = LDAP_NO_SUCH_OBJECT; | |||
| 917 | goto done; | |||
| 918 | } | |||
| 919 | /* TODO: verify filter is (objectClass=subschema) */ | |||
| 920 | ldap_search_subschema(search); | |||
| 921 | return 0; | |||
| 922 | } | |||
| 923 | ||||
| 924 | if ((search->ns = namespace_for_base(search->basedn)) == NULL((void*)0)) { | |||
| 925 | refs = namespace_referrals(search->basedn); | |||
| 926 | if (refs != NULL((void*)0)) { | |||
| 927 | ldap_refer(req, search->basedn, search, refs); | |||
| 928 | search->req = NULL((void*)0); /* request free'd by ldap_refer */ | |||
| 929 | search_close(search); | |||
| 930 | return LDAP_REFERRAL; | |||
| 931 | } | |||
| 932 | log_debug("no database configured for suffix %s", | |||
| 933 | search->basedn); | |||
| 934 | reason = LDAP_NO_SUCH_OBJECT; | |||
| 935 | goto done; | |||
| 936 | } | |||
| 937 | ||||
| 938 | if (!authorized(req->conn, search->ns, ACI_READ0x01, | |||
| 939 | search->basedn, NULL((void*)0), search->scope)) { | |||
| 940 | reason = LDAP_INSUFFICIENT_ACCESS; | |||
| 941 | goto done; | |||
| 942 | } | |||
| 943 | ||||
| 944 | if (namespace_begin_txn(search->ns, &search->data_txn, | |||
| 945 | &search->indx_txn, 1) != BT_SUCCESS0) { | |||
| 946 | if (errno(*__errno()) == EBUSY16) { | |||
| 947 | if (namespace_queue_request(search->ns, req) != 0) { | |||
| 948 | reason = LDAP_BUSY; | |||
| 949 | goto done; | |||
| 950 | } | |||
| 951 | search->req = NULL((void*)0); /* keep the scheduled request */ | |||
| 952 | search_close(search); | |||
| 953 | return 0; | |||
| 954 | } | |||
| 955 | reason = LDAP_OTHER; | |||
| 956 | goto done; | |||
| 957 | } | |||
| 958 | ||||
| 959 | if (search->scope == LDAP_SCOPE_BASE) { | |||
| 960 | struct btval key, val; | |||
| 961 | ||||
| 962 | memset(&key, 0, sizeof(key)); | |||
| 963 | memset(&val, 0, sizeof(val)); | |||
| 964 | key.data = search->basedn; | |||
| 965 | key.size = strlen(key.data); | |||
| 966 | ||||
| 967 | if (btree_txn_get(NULL((void*)0), search->data_txn, &key, &val) == 0) { | |||
| 968 | check_search_entry(&key, &val, search); | |||
| 969 | btval_reset(&val); | |||
| 970 | reason = LDAP_SUCCESS; | |||
| 971 | } else if (errno(*__errno()) == ENOENT2) | |||
| 972 | reason = LDAP_NO_SUCH_OBJECT; | |||
| 973 | else | |||
| 974 | reason = LDAP_OTHER; | |||
| 975 | goto done; | |||
| 976 | } | |||
| 977 | ||||
| 978 | if (!namespace_exists(search->ns, search->basedn)) { | |||
| 979 | reason = LDAP_NO_SUCH_OBJECT; | |||
| 980 | goto done; | |||
| 981 | } | |||
| 982 | ||||
| 983 | search->plan = search_planner(search->ns, search->filter); | |||
| 984 | if (search->plan == NULL((void*)0)) { | |||
| 985 | reason = LDAP_PROTOCOL_ERROR; | |||
| 986 | goto done; | |||
| 987 | } | |||
| 988 | ||||
| 989 | if (search->plan->undefined) { | |||
| 990 | log_debug("whole search filter is undefined"); | |||
| 991 | reason = LDAP_SUCCESS; | |||
| 992 | goto done; | |||
| 993 | } | |||
| 994 | ||||
| 995 | if (!search->plan->indexed && search->scope == LDAP_SCOPE_ONELEVEL) { | |||
| 996 | int sz; | |||
| 997 | sz = strlen(search->basedn) - strlen(search->ns->suffix); | |||
| 998 | if (sz > 0 && search->basedn[sz - 1] == ',') | |||
| 999 | sz--; | |||
| 1000 | add_index(search->plan, "@%.*s,", sz, search->basedn); | |||
| 1001 | } | |||
| 1002 | ||||
| 1003 | if (!search->plan->indexed) | |||
| 1004 | ++stats.unindexed; | |||
| 1005 | ||||
| 1006 | bufferevent_enable(req->conn->bev, EV_WRITE0x04); | |||
| 1007 | return 0; | |||
| 1008 | ||||
| 1009 | done: | |||
| 1010 | send_ldap_result(req->conn, req->msgid, LDAP_RES_SEARCH_RESULT, reason); | |||
| 1011 | if (search) | |||
| 1012 | search_close(search); | |||
| 1013 | return 0; | |||
| 1014 | } | |||
| 1015 |