xref: /openbsd-src/usr.sbin/ldapd/index.c (revision 696b58997f75587bd78112ed0b6cdec94a718911)
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