1*696b5899Stb /* $OpenBSD: index.c,v 1.13 2019/10/24 12:39:26 tb Exp $ */
25d465952Smartinh
35d465952Smartinh /*
45d465952Smartinh * Copyright (c) 2009 Martin Hedenfalk <martin@bzero.se>
55d465952Smartinh *
65d465952Smartinh * Permission to use, copy, modify, and distribute this software for any
75d465952Smartinh * purpose with or without fee is hereby granted, provided that the above
85d465952Smartinh * copyright notice and this permission notice appear in all copies.
95d465952Smartinh *
105d465952Smartinh * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
115d465952Smartinh * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
125d465952Smartinh * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
135d465952Smartinh * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
145d465952Smartinh * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
155d465952Smartinh * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
165d465952Smartinh * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
175d465952Smartinh */
185d465952Smartinh
195d465952Smartinh /* Indices are stored as unique keys in a btree. No data is stored.
205d465952Smartinh * The keys are made up of the attribute being indexed, concatenated
215d465952Smartinh * with the distinguished name of the entry. The namespace suffix is
225d465952Smartinh * stripped, however.
235d465952Smartinh *
245d465952Smartinh * Below, the namespace suffix dc=example,dc=com is stripped.
255d465952Smartinh *
265d465952Smartinh * Index b-tree sorts with plain strcmp:
275d465952Smartinh * ...
285d465952Smartinh * cn=Chunky Bacon,cn=chunky bacon,ou=people,
295d465952Smartinh * cn=Chunky Bacon,uid=cbacon,ou=accounts,
305d465952Smartinh * cn=Chunky Beans,cn=chunky beans,ou=people,
315d465952Smartinh * cn=Chunky Beans,uid=cbeans,ou=accounts,
325d465952Smartinh * cn=Crispy Bacon,cn=crispy bacon,ou=people,
335d465952Smartinh * ...
345d465952Smartinh * sn=Bacon,cn=chunky bacon,ou=people,
355d465952Smartinh * sn=Bacon,cn=crispy bacon,ou=people,
365d465952Smartinh * sn=Beans,cn=chunky beans,ou=people,
375d465952Smartinh * ...
385d465952Smartinh * This index can be used for equality, prefix and range searches.
395d465952Smartinh *
405d465952Smartinh * If an indexed attribute sorts numerically (integer), we might be able
415d465952Smartinh * to just pad it with zeros... otherwise this needs to be refined.
425d465952Smartinh *
435d465952Smartinh * Multiple attributes can be indexed in the same database.
445d465952Smartinh *
455d465952Smartinh * Presence index can be stored as:
465d465952Smartinh * !mail,cn=chunky bacon,ou=people,
475d465952Smartinh * !mail,cn=chunky beans,ou=people,
485d465952Smartinh * !mail,cn=crispy bacon,ou=people,
495d465952Smartinh *
505d465952Smartinh * Substring index could probably be stored like a suffix tree:
515d465952Smartinh * sn>Bacon,cn=chunky bacon,ou=people,
525d465952Smartinh * sn>acon,cn=chunky bacon,ou=people,
535d465952Smartinh * sn>con,cn=chunky bacon,ou=people,
545d465952Smartinh * sn>on,cn=chunky bacon,ou=people,
555d465952Smartinh * sn>n,cn=chunky bacon,ou=people,
565d465952Smartinh *
575d465952Smartinh * This facilitates both substring and suffix search.
585d465952Smartinh *
595d465952Smartinh * Approximate index:
605d465952Smartinh * sn~[soundex(bacon)],cn=chunky bacon,ou=people,
615d465952Smartinh *
625d465952Smartinh * One level searches can be indexed as follows:
635d465952Smartinh * @<parent-rdn>,<rdn>
645d465952Smartinh * example:
655d465952Smartinh * @ou=accounts,uid=cbacon
665d465952Smartinh * @ou=accounts,uid=cbeans
675d465952Smartinh * @ou=people,cn=chunky bacon
685d465952Smartinh * @ou=people,cn=chunky beans
695d465952Smartinh * @ou=people,cn=crispy bacon
705d465952Smartinh *
715d465952Smartinh */
725d465952Smartinh
735d465952Smartinh #include <sys/types.h>
745d465952Smartinh #include <sys/queue.h>
755d465952Smartinh
765d465952Smartinh #include <assert.h>
770279e526Smartinh #include <errno.h>
785d465952Smartinh #include <stdlib.h>
795d465952Smartinh #include <string.h>
805d465952Smartinh
815d465952Smartinh #include "ldapd.h"
82fdd30f56Sbenno #include "log.h"
835d465952Smartinh
849fc8a017Smartinh static int
index_attribute(struct namespace * ns,char * attr,struct btval * dn,struct ber_element * a)855d465952Smartinh index_attribute(struct namespace *ns, char *attr, struct btval *dn,
865d465952Smartinh struct ber_element *a)
875d465952Smartinh {
885d465952Smartinh int dnsz, rc;
895d465952Smartinh char *s, *t;
905d465952Smartinh struct ber_element *v;
915d465952Smartinh struct btval key, val;
925d465952Smartinh
935d465952Smartinh assert(ns);
945d465952Smartinh assert(ns->indx_txn);
955d465952Smartinh assert(attr);
965d465952Smartinh assert(dn);
975d465952Smartinh assert(a);
985d465952Smartinh assert(a->be_next);
9937f4b933Smmcc memset(&val, 0, sizeof(val));
1005d465952Smartinh
1015d465952Smartinh log_debug("indexing %.*s on %s", (int)dn->size, (char *)dn->data, attr);
1025d465952Smartinh
1035d465952Smartinh dnsz = dn->size - strlen(ns->suffix);
1045d465952Smartinh
1055d465952Smartinh for (v = a->be_next->be_sub; v; v = v->be_next) {
106*696b5899Stb if (ober_get_string(v, &s) != 0)
1075d465952Smartinh continue;
10837f4b933Smmcc memset(&key, 0, sizeof(key));
1095d465952Smartinh key.size = asprintf(&t, "%s=%s,%.*s", attr, s, dnsz,
1105d465952Smartinh (char *)dn->data);
111aa48e8d1Smillert if (key.size == (size_t)-1)
112aa48e8d1Smillert return -1;
1135d465952Smartinh key.data = t;
1145d465952Smartinh normalize_dn(key.data);
115b91780d1Smartinh rc = btree_txn_put(NULL, ns->indx_txn, &key, &val,
116b91780d1Smartinh BT_NOOVERWRITE);
1175d465952Smartinh free(t);
11869b15837Smartinh if (rc == -1 && errno != EEXIST)
1195d465952Smartinh return -1;
1205d465952Smartinh }
1215d465952Smartinh
1225d465952Smartinh return 0;
1235d465952Smartinh }
1245d465952Smartinh
1255d465952Smartinh static int
index_rdn_key(struct namespace * ns,struct btval * dn,struct btval * key)126b420b98bSmartinh index_rdn_key(struct namespace *ns, struct btval *dn, struct btval *key)
1275d465952Smartinh {
128b420b98bSmartinh int dnsz, rdnsz, pdnsz;
1295d465952Smartinh char *t, *parent_dn;
1305d465952Smartinh
13137f4b933Smmcc memset(key, 0, sizeof(*key));
1325d465952Smartinh
1335d465952Smartinh dnsz = dn->size - strlen(ns->suffix);
1345d465952Smartinh if (dnsz-- == 0)
135b420b98bSmartinh return -1;
1365d465952Smartinh
1375d465952Smartinh parent_dn = memchr(dn->data, ',', dnsz);
1385d465952Smartinh if (parent_dn == NULL) {
1395d465952Smartinh rdnsz = dnsz;
1405d465952Smartinh pdnsz = 0;
1412e79bb2cSgsoares parent_dn = "";
1425d465952Smartinh } else {
1435d465952Smartinh rdnsz = parent_dn - (char *)dn->data;
1445d465952Smartinh pdnsz = dnsz - rdnsz - 1;
1455d465952Smartinh ++parent_dn;
1465d465952Smartinh }
1475d465952Smartinh
148aa48e8d1Smillert if (asprintf(&t, "@%.*s,%.*s", pdnsz, parent_dn, rdnsz,
149aa48e8d1Smillert (char *)dn->data) == -1)
150aa48e8d1Smillert return -1;
151b420b98bSmartinh
152b420b98bSmartinh normalize_dn(t);
153b420b98bSmartinh key->data = t;
154b420b98bSmartinh key->size = strlen(t);
155b420b98bSmartinh key->free_data = 1;
156b420b98bSmartinh
157b420b98bSmartinh return 0;
158b420b98bSmartinh }
159b420b98bSmartinh
160b420b98bSmartinh static int
index_rdn(struct namespace * ns,struct btval * dn)161b420b98bSmartinh index_rdn(struct namespace *ns, struct btval *dn)
162b420b98bSmartinh {
163b420b98bSmartinh struct btval key, val;
164b420b98bSmartinh int rc;
165b420b98bSmartinh
16637f4b933Smmcc memset(&val, 0, sizeof(val));
167b420b98bSmartinh
168b420b98bSmartinh assert(ns);
169b420b98bSmartinh assert(ns->indx_txn);
170b420b98bSmartinh assert(dn);
171b420b98bSmartinh
172b420b98bSmartinh if (index_rdn_key(ns, dn, &key) < 0)
173b420b98bSmartinh return 0;
174b420b98bSmartinh
1755d465952Smartinh log_debug("indexing rdn on %.*s", (int)key.size, (char *)key.data);
176b91780d1Smartinh rc = btree_txn_put(NULL, ns->indx_txn, &key, &val, BT_NOOVERWRITE);
177b420b98bSmartinh btval_reset(&key);
17869b15837Smartinh if (rc == -1 && errno != EEXIST)
1795d465952Smartinh return -1;
1805d465952Smartinh return 0;
1815d465952Smartinh }
1825d465952Smartinh
1839fc8a017Smartinh static int
unindex_attribute(struct namespace * ns,char * attr,struct btval * dn,struct ber_element * a)1845d465952Smartinh unindex_attribute(struct namespace *ns, char *attr, struct btval *dn,
1855d465952Smartinh struct ber_element *a)
1865d465952Smartinh {
1875d465952Smartinh int dnsz, rc;
1885d465952Smartinh char *s, *t;
1895d465952Smartinh struct ber_element *v;
1905d465952Smartinh struct btval key;
1915d465952Smartinh
1925d465952Smartinh assert(ns);
1935d465952Smartinh assert(ns->indx_txn);
1945d465952Smartinh assert(attr);
1955d465952Smartinh assert(dn);
1965d465952Smartinh assert(a);
1975d465952Smartinh assert(a->be_next);
1985d465952Smartinh
1995d465952Smartinh log_debug("unindexing %.*s on %s",
2005d465952Smartinh (int)dn->size, (char *)dn->data, attr);
2015d465952Smartinh
2025d465952Smartinh dnsz = dn->size - strlen(ns->suffix);
2035d465952Smartinh
2045d465952Smartinh for (v = a->be_next->be_sub; v; v = v->be_next) {
205*696b5899Stb if (ober_get_string(v, &s) != 0)
2065d465952Smartinh continue;
20737f4b933Smmcc memset(&key, 0, sizeof(key));
2085d465952Smartinh key.size = asprintf(&t, "%s=%s,%.*s", attr, s, dnsz,
2095d465952Smartinh (char *)dn->data);
2105d465952Smartinh key.data = t;
2115d465952Smartinh normalize_dn(key.data);
212b91780d1Smartinh rc = btree_txn_del(NULL, ns->indx_txn, &key, NULL);
2135d465952Smartinh free(t);
2140279e526Smartinh if (rc == BT_FAIL && errno != ENOENT)
2155d465952Smartinh return -1;
2165d465952Smartinh }
2175d465952Smartinh
2185d465952Smartinh return 0;
2195d465952Smartinh }
2205d465952Smartinh
2215d465952Smartinh int
index_entry(struct namespace * ns,struct btval * dn,struct ber_element * elm)2225d465952Smartinh index_entry(struct namespace *ns, struct btval *dn, struct ber_element *elm)
2235d465952Smartinh {
2245d465952Smartinh struct ber_element *a;
2255d465952Smartinh struct attr_index *ai;
2265d465952Smartinh
2275d465952Smartinh assert(ns);
2285d465952Smartinh assert(dn);
2295d465952Smartinh assert(elm);
2305d465952Smartinh TAILQ_FOREACH(ai, &ns->indices, next) {
2315d465952Smartinh a = ldap_get_attribute(elm, ai->attr);
2325d465952Smartinh if (a && index_attribute(ns, ai->attr, dn, a) < 0)
2335d465952Smartinh return -1;
2345d465952Smartinh }
2355d465952Smartinh
2365d465952Smartinh return index_rdn(ns, dn);
2375d465952Smartinh }
2385d465952Smartinh
2395d465952Smartinh static int
unindex_rdn(struct namespace * ns,struct btval * dn)2405d465952Smartinh unindex_rdn(struct namespace *ns, struct btval *dn)
2415d465952Smartinh {
242b420b98bSmartinh int rc;
243b420b98bSmartinh struct btval key;
2445d465952Smartinh
2455d465952Smartinh assert(ns);
2465d465952Smartinh assert(ns->indx_txn);
2475d465952Smartinh assert(dn);
2485d465952Smartinh
249b420b98bSmartinh if (index_rdn_key(ns, dn, &key) < 0)
250b420b98bSmartinh return 0;
2515d465952Smartinh
2525d465952Smartinh log_debug("unindexing rdn on %.*s", (int)key.size, (char *)key.data);
253b91780d1Smartinh rc = btree_txn_del(NULL, ns->indx_txn, &key, NULL);
254b420b98bSmartinh btval_reset(&key);
2550279e526Smartinh if (rc == BT_FAIL && errno != ENOENT)
2565d465952Smartinh return -1;
2575d465952Smartinh return 0;
2585d465952Smartinh }
2595d465952Smartinh
2605d465952Smartinh int
unindex_entry(struct namespace * ns,struct btval * dn,struct ber_element * elm)2615d465952Smartinh unindex_entry(struct namespace *ns, struct btval *dn, struct ber_element *elm)
2625d465952Smartinh {
2635d465952Smartinh struct ber_element *a;
2645d465952Smartinh struct attr_index *ai;
2655d465952Smartinh
2665d465952Smartinh assert(ns);
2675d465952Smartinh assert(dn);
2685d465952Smartinh assert(elm);
2695d465952Smartinh TAILQ_FOREACH(ai, &ns->indices, next) {
2705d465952Smartinh a = ldap_get_attribute(elm, ai->attr);
2715d465952Smartinh if (a && unindex_attribute(ns, ai->attr, dn, a) < 0)
2725d465952Smartinh return -1;
2735d465952Smartinh }
2745d465952Smartinh
2755d465952Smartinh return unindex_rdn(ns, dn);
2765d465952Smartinh }
2775d465952Smartinh
2785d465952Smartinh /* Reconstruct the full dn from the index key and the namespace suffix.
2795d465952Smartinh *
2805d465952Smartinh * Examples:
2815d465952Smartinh *
2825d465952Smartinh * index key:
2835d465952Smartinh * sn=Bacon,cn=chunky bacon,ou=people,
2845d465952Smartinh * full dn:
2855d465952Smartinh * cn=chunky bacon,ou=people,dc=example,dc=com
2865d465952Smartinh *
2875d465952Smartinh * index key:
2885d465952Smartinh * @ou=people,cn=chunky bacon
2895d465952Smartinh * full dn:
2905d465952Smartinh * cn=chunky bacon,ou=people,dc=example,dc=com
2915d465952Smartinh *
2925d465952Smartinh * index key:
2935d465952Smartinh * @,ou=people
2945d465952Smartinh * full dn:
2955d465952Smartinh * ou=people,dc=example,dc=com
2965d465952Smartinh *
2975d465952Smartinh * index key:
2985d465952Smartinh * @ou=staff,ou=people,cn=chunky bacon
2995d465952Smartinh * full dn:
3005d465952Smartinh * cn=chunky bacon,ou=staff,ou=people,dc=example,dc=com
3015d465952Smartinh *
3025d465952Smartinh * Filled in dn must be freed with btval_reset().
3035d465952Smartinh */
3045d465952Smartinh int
index_to_dn(struct namespace * ns,struct btval * indx,struct btval * dn)3055d465952Smartinh index_to_dn(struct namespace *ns, struct btval *indx, struct btval *dn)
3065d465952Smartinh {
3075d465952Smartinh char *rdn, *parent_rdn, indxtype, *dst;
3085d465952Smartinh int rdn_sz, prdn_sz;
3095d465952Smartinh
3105d465952Smartinh /* Skip past first index part to get the RDN.
3115d465952Smartinh */
3125d465952Smartinh
3135d465952Smartinh indxtype = ((char *)indx->data)[0];
3145d465952Smartinh if (indxtype == '@') {
3155d465952Smartinh /* One-level index.
3165d465952Smartinh * Full DN is made up of rdn + parent rdn + namespace suffix.
3175d465952Smartinh */
3185d465952Smartinh rdn = memrchr(indx->data, ',', indx->size);
3195d465952Smartinh if (rdn++ == NULL)
3205d465952Smartinh return -1;
3215d465952Smartinh rdn_sz = indx->size - (rdn - (char *)indx->data);
3225d465952Smartinh
3235d465952Smartinh parent_rdn = (char *)indx->data + 1;
3245d465952Smartinh prdn_sz = rdn - parent_rdn - 1;
3255d465952Smartinh dn->size = indx->size + strlen(ns->suffix);
3265d465952Smartinh if (prdn_sz == 0)
3275d465952Smartinh dn->size--;
3285d465952Smartinh if ((dn->data = malloc(dn->size)) == NULL) {
3295d465952Smartinh log_warn("conn_search: malloc");
3305d465952Smartinh return -1;
3315d465952Smartinh }
3325d465952Smartinh dst = dn->data;
3335d465952Smartinh bcopy(rdn, dst, rdn_sz);
3345d465952Smartinh dst += rdn_sz;
3355d465952Smartinh *dst++ = ',';
3365d465952Smartinh bcopy(parent_rdn, dst, prdn_sz);
3375d465952Smartinh dst += prdn_sz;
3385d465952Smartinh if (prdn_sz > 0)
3395d465952Smartinh *dst++ = ',';
3405d465952Smartinh bcopy(ns->suffix, dst, strlen(ns->suffix));
3415d465952Smartinh } else {
3425d465952Smartinh /* Construct the full DN by appending namespace suffix.
3435d465952Smartinh */
3445d465952Smartinh rdn = memchr(indx->data, ',', indx->size);
3455d465952Smartinh if (rdn++ == NULL)
3465d465952Smartinh return -1;
3475d465952Smartinh rdn_sz = indx->size - (rdn - (char *)indx->data);
3485d465952Smartinh dn->size = rdn_sz + strlen(ns->suffix);
3495d465952Smartinh if ((dn->data = malloc(dn->size)) == NULL) {
3505d465952Smartinh log_warn("index_to_dn: malloc");
3515d465952Smartinh return -1;
3525d465952Smartinh }
3535d465952Smartinh bcopy(rdn, dn->data, rdn_sz);
3545d465952Smartinh bcopy(ns->suffix, (char *)dn->data + rdn_sz,
3555d465952Smartinh strlen(ns->suffix));
3565d465952Smartinh }
3575d465952Smartinh
3585d465952Smartinh dn->free_data = 1;
3595d465952Smartinh
3605d465952Smartinh return 0;
3615d465952Smartinh }
3625d465952Smartinh
363