1 /*
2    ldb database library
3 
4    Copyright (C) Andrew Tridgell  2004-2005
5    Copyright (C) Simo Sorce            2005
6 
7      ** NOTE! The following LGPL license applies to the ldb
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10 
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Lesser General Public
13    License as published by the Free Software Foundation; either
14    version 2 of the License, or (at your option) any later version.
15 
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Lesser General Public License for more details.
20 
21    You should have received a copy of the GNU Lesser General Public
22    License along with this library; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 */
25 
26 /*
27  *  Name: ldb
28  *
29  *  Component: ldb expression matching
30  *
31  *  Description: ldb expression matching
32  *
33  *  Author: Andrew Tridgell
34  */
35 
36 #include "includes.h"
37 #include "ldb/include/includes.h"
38 
39 /*
40   check if the scope matches in a search result
41 */
ldb_match_scope(struct ldb_context * ldb,struct ldb_dn * base,struct ldb_dn * dn,enum ldb_scope scope)42 static int ldb_match_scope(struct ldb_context *ldb,
43 			   struct ldb_dn *base,
44 			   struct ldb_dn *dn,
45 			   enum ldb_scope scope)
46 {
47 	int ret = 0;
48 
49 	if (base == NULL || dn == NULL) {
50 		return 1;
51 	}
52 
53 	switch (scope) {
54 	case LDB_SCOPE_BASE:
55 		if (ldb_dn_compare(base, dn) == 0) {
56 			ret = 1;
57 		}
58 		break;
59 
60 	case LDB_SCOPE_ONELEVEL:
61 		if (ldb_dn_get_comp_num(dn) == (ldb_dn_get_comp_num(base) + 1)) {
62 			if (ldb_dn_compare_base(base, dn) == 0) {
63 				ret = 1;
64 			}
65 		}
66 		break;
67 
68 	case LDB_SCOPE_SUBTREE:
69 	default:
70 		if (ldb_dn_compare_base(base, dn) == 0) {
71 			ret = 1;
72 		}
73 		break;
74 	}
75 
76 	return ret;
77 }
78 
79 
80 /*
81   match if node is present
82 */
ldb_match_present(struct ldb_context * ldb,const struct ldb_message * msg,const struct ldb_parse_tree * tree,enum ldb_scope scope)83 static int ldb_match_present(struct ldb_context *ldb,
84 			     const struct ldb_message *msg,
85 			     const struct ldb_parse_tree *tree,
86 			     enum ldb_scope scope)
87 {
88 	if (ldb_attr_dn(tree->u.present.attr) == 0) {
89 		return 1;
90 	}
91 
92 	if (ldb_msg_find_element(msg, tree->u.present.attr)) {
93 		return 1;
94 	}
95 
96 	return 0;
97 }
98 
ldb_match_comparison(struct ldb_context * ldb,const struct ldb_message * msg,const struct ldb_parse_tree * tree,enum ldb_scope scope,enum ldb_parse_op comp_op)99 static int ldb_match_comparison(struct ldb_context *ldb,
100 				const struct ldb_message *msg,
101 				const struct ldb_parse_tree *tree,
102 				enum ldb_scope scope,
103 				enum ldb_parse_op comp_op)
104 {
105 	unsigned int i;
106 	struct ldb_message_element *el;
107 	const struct ldb_attrib_handler *h;
108 	int ret;
109 
110 	/* FIXME: APPROX comparison not handled yet */
111 	if (comp_op == LDB_OP_APPROX) return 0;
112 
113 	el = ldb_msg_find_element(msg, tree->u.comparison.attr);
114 	if (el == NULL) {
115 		return 0;
116 	}
117 
118 	h = ldb_attrib_handler(ldb, el->name);
119 
120 	for (i = 0; i < el->num_values; i++) {
121 		ret = h->comparison_fn(ldb, ldb, &el->values[i], &tree->u.comparison.value);
122 
123 		if (ret == 0) {
124 			return 1;
125 		}
126 		if (ret > 0 && comp_op == LDB_OP_GREATER) {
127 			return 1;
128 		}
129 		if (ret < 0 && comp_op == LDB_OP_LESS) {
130 			return 1;
131 		}
132 	}
133 
134 	return 0;
135 }
136 
137 /*
138   match a simple leaf node
139 */
ldb_match_equality(struct ldb_context * ldb,const struct ldb_message * msg,const struct ldb_parse_tree * tree,enum ldb_scope scope)140 static int ldb_match_equality(struct ldb_context *ldb,
141 			      const struct ldb_message *msg,
142 			      const struct ldb_parse_tree *tree,
143 			      enum ldb_scope scope)
144 {
145 	unsigned int i;
146 	struct ldb_message_element *el;
147 	const struct ldb_attrib_handler *h;
148 	struct ldb_dn *valuedn;
149 	int ret;
150 
151 	if (ldb_attr_dn(tree->u.equality.attr) == 0) {
152 		valuedn = ldb_dn_new(ldb, ldb, (char *)tree->u.equality.value.data);
153 		if (valuedn == NULL) {
154 			return 0;
155 		}
156 
157 		ret = ldb_dn_compare(msg->dn, valuedn);
158 
159 		talloc_free(valuedn);
160 
161 		if (ret == 0) return 1;
162 		return 0;
163 	}
164 
165 	/* TODO: handle the "*" case derived from an extended search
166 	   operation without the attibute type defined */
167 	el = ldb_msg_find_element(msg, tree->u.equality.attr);
168 	if (el == NULL) {
169 		return 0;
170 	}
171 
172 	h = ldb_attrib_handler(ldb, el->name);
173 
174 	for (i=0;i<el->num_values;i++) {
175 		if (h->comparison_fn(ldb, ldb, &tree->u.equality.value,
176 				     &el->values[i]) == 0) {
177 			return 1;
178 		}
179 	}
180 
181 	return 0;
182 }
183 
ldb_wildcard_compare(struct ldb_context * ldb,const struct ldb_parse_tree * tree,const struct ldb_val value)184 static int ldb_wildcard_compare(struct ldb_context *ldb,
185 				const struct ldb_parse_tree *tree,
186 				const struct ldb_val value)
187 {
188 	const struct ldb_attrib_handler *h;
189 	struct ldb_val val;
190 	struct ldb_val cnk;
191 	struct ldb_val *chunk;
192 	char *p, *g;
193 	uint8_t *save_p = NULL;
194 	int c = 0;
195 
196 	h = ldb_attrib_handler(ldb, tree->u.substring.attr);
197 
198 	if(h->canonicalise_fn(ldb, ldb, &value, &val) != 0)
199 		return -1;
200 
201 	save_p = val.data;
202 	cnk.data = NULL;
203 
204 	if ( ! tree->u.substring.start_with_wildcard ) {
205 
206 		chunk = tree->u.substring.chunks[c];
207 		if(h->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) goto failed;
208 
209 		/* This deals with wildcard prefix searches on binary attributes (eg objectGUID) */
210 		if (cnk.length > val.length) {
211 			goto failed;
212 		}
213 		if (memcmp((char *)val.data, (char *)cnk.data, cnk.length) != 0) goto failed;
214 		val.length -= cnk.length;
215 		val.data += cnk.length;
216 		c++;
217 		talloc_free(cnk.data);
218 		cnk.data = NULL;
219 	}
220 
221 	while (tree->u.substring.chunks[c]) {
222 
223 		chunk = tree->u.substring.chunks[c];
224 		if(h->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) goto failed;
225 
226 		/* FIXME: case of embedded nulls */
227 		p = strstr((char *)val.data, (char *)cnk.data);
228 		if (p == NULL) goto failed;
229 		if ( (! tree->u.substring.chunks[c + 1]) && (! tree->u.substring.end_with_wildcard) ) {
230 			do { /* greedy */
231 				g = strstr((char *)p + cnk.length, (char *)cnk.data);
232 				if (g) p = g;
233 			} while(g);
234 		}
235 		val.length = val.length - (p - (char *)(val.data)) - cnk.length;
236 		val.data = (uint8_t *)(p + cnk.length);
237 		c++;
238 		talloc_free(cnk.data);
239 		cnk.data = NULL;
240 	}
241 
242 	if ( (! tree->u.substring.end_with_wildcard) && (*(val.data) != 0) ) goto failed; /* last chunk have not reached end of string */
243 	talloc_free(save_p);
244 	return 1;
245 
246 failed:
247 	talloc_free(save_p);
248 	talloc_free(cnk.data);
249 	return 0;
250 }
251 
252 /*
253   match a simple leaf node
254 */
ldb_match_substring(struct ldb_context * ldb,const struct ldb_message * msg,const struct ldb_parse_tree * tree,enum ldb_scope scope)255 static int ldb_match_substring(struct ldb_context *ldb,
256 			       const struct ldb_message *msg,
257 			       const struct ldb_parse_tree *tree,
258 			       enum ldb_scope scope)
259 {
260 	unsigned int i;
261 	struct ldb_message_element *el;
262 
263 	el = ldb_msg_find_element(msg, tree->u.substring.attr);
264 	if (el == NULL) {
265 		return 0;
266 	}
267 
268 	for (i = 0; i < el->num_values; i++) {
269 		if (ldb_wildcard_compare(ldb, tree, el->values[i]) == 1) {
270 			return 1;
271 		}
272 	}
273 
274 	return 0;
275 }
276 
277 
278 /*
279   bitwise-and comparator
280 */
ldb_comparator_and(const struct ldb_val * v1,const struct ldb_val * v2)281 static int ldb_comparator_and(const struct ldb_val *v1, const struct ldb_val *v2)
282 {
283 	uint64_t i1, i2;
284 	i1 = strtoull((char *)v1->data, NULL, 0);
285 	i2 = strtoull((char *)v2->data, NULL, 0);
286 	return ((i1 & i2) == i2);
287 }
288 
289 /*
290   bitwise-or comparator
291 */
ldb_comparator_or(const struct ldb_val * v1,const struct ldb_val * v2)292 static int ldb_comparator_or(const struct ldb_val *v1, const struct ldb_val *v2)
293 {
294 	uint64_t i1, i2;
295 	i1 = strtoull((char *)v1->data, NULL, 0);
296 	i2 = strtoull((char *)v2->data, NULL, 0);
297 	return ((i1 & i2) != 0);
298 }
299 
300 
301 /*
302   extended match, handles things like bitops
303 */
ldb_match_extended(struct ldb_context * ldb,const struct ldb_message * msg,const struct ldb_parse_tree * tree,enum ldb_scope scope)304 static int ldb_match_extended(struct ldb_context *ldb,
305 			      const struct ldb_message *msg,
306 			      const struct ldb_parse_tree *tree,
307 			      enum ldb_scope scope)
308 {
309 	int i;
310 	const struct {
311 		const char *oid;
312 		int (*comparator)(const struct ldb_val *, const struct ldb_val *);
313 	} rules[] = {
314 		{ LDB_OID_COMPARATOR_AND, ldb_comparator_and},
315 		{ LDB_OID_COMPARATOR_OR, ldb_comparator_or}
316 	};
317 	int (*comp)(const struct ldb_val *, const struct ldb_val *) = NULL;
318 	struct ldb_message_element *el;
319 
320 	if (tree->u.extended.dnAttributes) {
321 		ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: dnAttributes extended match not supported yet");
322 		return -1;
323 	}
324 	if (tree->u.extended.rule_id == NULL) {
325 		ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: no-rule extended matches not supported yet");
326 		return -1;
327 	}
328 	if (tree->u.extended.attr == NULL) {
329 		ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: no-attribute extended matches not supported yet");
330 		return -1;
331 	}
332 
333 	for (i=0;i<ARRAY_SIZE(rules);i++) {
334 		if (strcmp(rules[i].oid, tree->u.extended.rule_id) == 0) {
335 			comp = rules[i].comparator;
336 			break;
337 		}
338 	}
339 	if (comp == NULL) {
340 		ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: unknown extended rule_id %s\n",
341 			  tree->u.extended.rule_id);
342 		return -1;
343 	}
344 
345 	/* find the message element */
346 	el = ldb_msg_find_element(msg, tree->u.extended.attr);
347 	if (el == NULL) {
348 		return 0;
349 	}
350 
351 	for (i=0;i<el->num_values;i++) {
352 		int ret = comp(&el->values[i], &tree->u.extended.value);
353 		if (ret == -1 || ret == 1) return ret;
354 	}
355 
356 	return 0;
357 }
358 
359 /*
360   return 0 if the given parse tree matches the given message. Assumes
361   the message is in sorted order
362 
363   return 1 if it matches, and 0 if it doesn't match
364 
365   this is a recursive function, and does short-circuit evaluation
366  */
ldb_match_message(struct ldb_context * ldb,const struct ldb_message * msg,const struct ldb_parse_tree * tree,enum ldb_scope scope)367 static int ldb_match_message(struct ldb_context *ldb,
368 			     const struct ldb_message *msg,
369 			     const struct ldb_parse_tree *tree,
370 			     enum ldb_scope scope)
371 {
372 	unsigned int i;
373 	int v;
374 
375 	switch (tree->operation) {
376 	case LDB_OP_AND:
377 		for (i=0;i<tree->u.list.num_elements;i++) {
378 			v = ldb_match_message(ldb, msg, tree->u.list.elements[i], scope);
379 			if (!v) return 0;
380 		}
381 		return 1;
382 
383 	case LDB_OP_OR:
384 		for (i=0;i<tree->u.list.num_elements;i++) {
385 			v = ldb_match_message(ldb, msg, tree->u.list.elements[i], scope);
386 			if (v) return 1;
387 		}
388 		return 0;
389 
390 	case LDB_OP_NOT:
391 		return ! ldb_match_message(ldb, msg, tree->u.isnot.child, scope);
392 
393 	case LDB_OP_EQUALITY:
394 		return ldb_match_equality(ldb, msg, tree, scope);
395 
396 	case LDB_OP_SUBSTRING:
397 		return ldb_match_substring(ldb, msg, tree, scope);
398 
399 	case LDB_OP_GREATER:
400 		return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_GREATER);
401 
402 	case LDB_OP_LESS:
403 		return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_LESS);
404 
405 	case LDB_OP_PRESENT:
406 		return ldb_match_present(ldb, msg, tree, scope);
407 
408 	case LDB_OP_APPROX:
409 		return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_APPROX);
410 
411 	case LDB_OP_EXTENDED:
412 		return ldb_match_extended(ldb, msg, tree, scope);
413 
414 	}
415 
416 	return 0;
417 }
418 
ldb_match_msg(struct ldb_context * ldb,const struct ldb_message * msg,const struct ldb_parse_tree * tree,struct ldb_dn * base,enum ldb_scope scope)419 int ldb_match_msg(struct ldb_context *ldb,
420 		  const struct ldb_message *msg,
421 		  const struct ldb_parse_tree *tree,
422 		  struct ldb_dn *base,
423 		  enum ldb_scope scope)
424 {
425 	if ( ! ldb_match_scope(ldb, base, msg->dn, scope) ) {
426 		return 0;
427 	}
428 
429 	return ldb_match_message(ldb, msg, tree, scope);
430 }
431