xref: /openbsd/usr.sbin/ldapd/filter.c (revision 76d0caae)
1 /*	$OpenBSD: filter.c,v 1.9 2019/10/24 12:39:26 tb Exp $ */
2 
3 /*
4  * Copyright (c) 2009, 2010 Martin Hedenfalk <martinh@openbsd.org>
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 
22 #include <string.h>
23 #include <stdint.h>
24 
25 #include "ldapd.h"
26 #include "log.h"
27 
28 static int	 ldap_filt_eq(struct ber_element *root, struct plan *plan);
29 static int	 ldap_filt_subs(struct ber_element *root, struct plan *plan);
30 static int	 ldap_filt_and(struct ber_element *root, struct plan *plan);
31 static int	 ldap_filt_or(struct ber_element *root, struct plan *plan);
32 static int	 ldap_filt_not(struct ber_element *root, struct plan *plan);
33 
34 static int
35 ldap_filt_eq(struct ber_element *root, struct plan *plan)
36 {
37 	char			*vs;
38 	struct ber_element	*a, *vals, *v;
39 
40 	if (plan->undefined)
41 		return -1;
42 	else if (plan->adesc != NULL)
43 		a = ldap_get_attribute(root, plan->adesc);
44 	else
45 		a = ldap_find_attribute(root, plan->at);
46 	if (a == NULL) {
47 		log_debug("no attribute [%s] found",
48 		    plan->adesc ? plan->adesc : ATTR_NAME(plan->at));
49 		return -1;
50 	}
51 
52 	vals = a->be_next;
53 	if (vals == NULL)
54 		return -1;
55 
56 	for (v = vals->be_sub; v; v = v->be_next) {
57 		if (ober_get_string(v, &vs) != 0)
58 			continue;
59 		if (strcasecmp(plan->assert.value, vs) == 0)
60 			return 0;
61 	}
62 
63 	return -1;
64 }
65 
66 static int
67 ldap_filt_subs_value(struct ber_element *v, struct ber_element *sub)
68 {
69 	int		 class;
70 	unsigned int	 type;
71 	const char	*cmpval;
72 	char		*vs, *p, *end;
73 
74 	if (ober_get_string(v, &vs) != 0)
75 		return -1;
76 
77 	for (; sub; sub = sub->be_next) {
78 		if (ober_scanf_elements(sub, "ts", &class, &type, &cmpval) != 0)
79 			return -1;
80 
81 		if (class != BER_CLASS_CONTEXT)
82 			return -1;
83 
84 		switch (type) {
85 		case LDAP_FILT_SUBS_INIT:
86 			if (strncasecmp(cmpval, vs, strlen(cmpval)) == 0)
87 				vs += strlen(cmpval);
88 			else
89 				return 1; /* no match */
90 			break;
91 		case LDAP_FILT_SUBS_ANY:
92 			if ((p = strcasestr(vs, cmpval)) != NULL)
93 				vs = p + strlen(cmpval);
94 			else
95 				return 1; /* no match */
96 			break;
97 		case LDAP_FILT_SUBS_FIN:
98 			if (strlen(vs) < strlen(cmpval))
99 				return 1; /* no match */
100 			end = vs + strlen(vs) - strlen(cmpval);
101 			if (strcasecmp(end, cmpval) == 0)
102 				vs = end + strlen(cmpval);
103 			else
104 				return 1; /* no match */
105 			break;
106 		default:
107 			log_warnx("invalid subfilter type %u", type);
108 			return -1;
109 		}
110 	}
111 
112 	return 0; /* match */
113 }
114 
115 static int
116 ldap_filt_subs(struct ber_element *root, struct plan *plan)
117 {
118 	const char		*attr;
119 	struct ber_element	*a, *v;
120 
121 	if (plan->undefined)
122 		return -1;
123 	else if (plan->adesc != NULL)
124 		a = ldap_get_attribute(root, plan->adesc);
125 	else
126 		a = ldap_find_attribute(root, plan->at);
127 	if (a == NULL) {
128 		log_debug("no attribute [%s] found",
129 		    plan->adesc ? plan->adesc : ATTR_NAME(plan->at));
130 		return -1;
131 	}
132 
133 	if (ober_scanf_elements(a, "s(e", &attr, &v) != 0)
134 		return -1; /* internal failure, false or undefined? */
135 
136 	/* Loop through all values, stop if any matches.
137 	 */
138 	for (; v; v = v->be_next) {
139 		/* All substrings must match. */
140 		switch (ldap_filt_subs_value(v, plan->assert.substring)) {
141 		case 0:
142 			return 0;
143 		case -1:
144 			return -1;
145 		default:
146 			break;
147 		}
148 	}
149 
150 	/* All values checked, no match. */
151 	return -1;
152 }
153 
154 static int
155 ldap_filt_and(struct ber_element *root, struct plan *plan)
156 {
157 	struct plan	*arg;
158 
159 	TAILQ_FOREACH(arg, &plan->args, next)
160 		if (ldap_matches_filter(root, arg) != 0)
161 			return -1;
162 
163 	return 0;
164 }
165 
166 static int
167 ldap_filt_or(struct ber_element *root, struct plan *plan)
168 {
169 	struct plan	*arg;
170 
171 	TAILQ_FOREACH(arg, &plan->args, next)
172 		if (ldap_matches_filter(root, arg) == 0)
173 			return 0;
174 
175 	return -1;
176 }
177 
178 static int
179 ldap_filt_not(struct ber_element *root, struct plan *plan)
180 {
181 	struct plan	*arg;
182 
183 	TAILQ_FOREACH(arg, &plan->args, next)
184 		if (ldap_matches_filter(root, arg) != 0)
185 			return 0;
186 
187 	return -1;
188 }
189 
190 static int
191 ldap_filt_presence(struct ber_element *root, struct plan *plan)
192 {
193 	struct ber_element	*a;
194 
195 	if (plan->undefined)
196 		return -1;
197 	else if (plan->adesc != NULL)
198 		a = ldap_get_attribute(root, plan->adesc);
199 	else
200 		a = ldap_find_attribute(root, plan->at);
201 	if (a == NULL) {
202 		log_debug("no attribute [%s] found",
203 		    plan->adesc ? plan->adesc : ATTR_NAME(plan->at));
204 		return -1;
205 	}
206 
207 	return 0;
208 }
209 
210 int
211 ldap_matches_filter(struct ber_element *root, struct plan *plan)
212 {
213 	if (plan == NULL)
214 		return 0;
215 
216 	switch (plan->op) {
217 	case LDAP_FILT_EQ:
218 	case LDAP_FILT_APPR:
219 		return ldap_filt_eq(root, plan);
220 	case LDAP_FILT_SUBS:
221 		return ldap_filt_subs(root, plan);
222 	case LDAP_FILT_AND:
223 		return ldap_filt_and(root, plan);
224 	case LDAP_FILT_OR:
225 		return ldap_filt_or(root, plan);
226 	case LDAP_FILT_NOT:
227 		return ldap_filt_not(root, plan);
228 	case LDAP_FILT_PRES:
229 		return ldap_filt_presence(root, plan);
230 	default:
231 		log_warnx("filter type %d not implemented", plan->op);
232 		return -1;
233 	}
234 }
235 
236