]> git.sur5r.net Git - openldap/blob - servers/slapd/filterentry.c
fe4e1c1b54a5395d00ae1c91b758b595135fb425
[openldap] / servers / slapd / filterentry.c
1 /* filterentry.c - apply a filter to an entry */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/socket.h>
13 #include <ac/string.h>
14
15 #ifndef SLAPD_SCHEMA_NOT_COMPAT
16 #include <ac/regex.h>
17 #endif
18
19 #include "slap.h"
20
21 static int      test_filter_and( Backend *be,
22         Connection *conn, Operation *op,
23         Entry *e, Filter *flist );
24 static int      test_filter_or( Backend *be,
25         Connection *conn, Operation *op,
26         Entry *e, Filter *flist );
27 static int      test_substring_filter( Backend *be,
28         Connection *conn, Operation *op,
29         Entry *e, Filter *f);
30 #ifdef SLAPD_SCHEMA_NOT_COMPAT
31 static int      test_ava_filter( Backend *be,
32         Connection *conn, Operation *op,
33         Entry *e, AttributeAssertion *ava, int type );
34 static int      test_mra_filter( Backend *be,
35         Connection *conn, Operation *op,
36         Entry *e, MatchingRuleAssertion *mra );
37 static int      test_presence_filter( Backend *be,
38         Connection *conn, Operation *op,
39         Entry *e, AttributeDescription *desc );
40 #else
41 static int      test_ava_filter(Backend *be,
42         Connection *conn, Operation *op,
43         Entry *e, Ava *ava, int type);
44 static int      test_approx_filter(Backend *be,
45         Connection *conn, Operation *op,
46         Entry *e, Ava *ava);
47 static int      test_presence_filter(Backend *be,
48         Connection *conn, Operation *op,
49         Entry *e, char *type);
50 #endif
51
52
53 /*
54  * test_filter - test a filter against a single entry.
55  * returns:
56  *              LDAP_COMPARE_TRUE               filter matched
57  *              LDAP_COMPARE_FALSE              filter did not match
58  *              SLAPD_COMPARE_UNDEFINED filter is undefined
59  *      or an ldap result code indicating error
60  */
61
62 int
63 test_filter(
64     Backend     *be,
65     Connection  *conn,
66     Operation   *op,
67     Entry       *e,
68     Filter      *f
69 )
70 {
71         int     rc;
72
73         Debug( LDAP_DEBUG_FILTER, "=> test_filter\n", 0, 0, 0 );
74
75         switch ( f->f_choice ) {
76 #ifdef SLAPD_SCHEMA_NOT_COMPAT
77         case SLAPD_FILTER_COMPUTED:
78                 Debug( LDAP_DEBUG_FILTER, "    COMPUTED %s (%d)\n",
79                         f->f_result == LDAP_COMPARE_FALSE ? "false" :
80                         f->f_result == LDAP_COMPARE_TRUE ? "true" :
81                         f->f_result == SLAPD_COMPARE_UNDEFINED ? "undefined" : "error",
82                         f->f_result, 0 );
83                 rc = f->f_result;
84                 break;
85 #endif
86
87         case LDAP_FILTER_EQUALITY:
88                 Debug( LDAP_DEBUG_FILTER, "    EQUALITY\n", 0, 0, 0 );
89 #ifdef SLAPD_SCHEMA_NOT_COMPAT
90                 rc = test_ava_filter( be, conn, op, e, f->f_ava,
91                     LDAP_FILTER_EQUALITY );
92 #else
93                 rc = test_ava_filter( be, conn, op, e, &f->f_ava,
94                     LDAP_FILTER_EQUALITY );
95 #endif
96                 break;
97
98         case LDAP_FILTER_SUBSTRINGS:
99                 Debug( LDAP_DEBUG_FILTER, "    SUBSTRINGS\n", 0, 0, 0 );
100                 rc = test_substring_filter( be, conn, op, e, f );
101                 break;
102
103         case LDAP_FILTER_GE:
104 #ifdef SLAPD_SCHEMA_NOT_COMPAT
105                 rc = test_ava_filter( be, conn, op, e, f->f_ava,
106                     LDAP_FILTER_GE );
107 #else
108                 Debug( LDAP_DEBUG_FILTER, "    GE\n", 0, 0, 0 );
109                 rc = test_ava_filter( be, conn, op, e, &f->f_ava,
110                     LDAP_FILTER_GE );
111 #endif
112                 break;
113
114         case LDAP_FILTER_LE:
115 #ifdef SLAPD_SCHEMA_NOT_COMPAT
116                 rc = test_ava_filter( be, conn, op, e, f->f_ava,
117                     LDAP_FILTER_LE );
118 #else
119                 Debug( LDAP_DEBUG_FILTER, "    LE\n", 0, 0, 0 );
120                 rc = test_ava_filter( be, conn, op, e, &f->f_ava,
121                     LDAP_FILTER_LE );
122 #endif
123                 break;
124
125         case LDAP_FILTER_PRESENT:
126                 Debug( LDAP_DEBUG_FILTER, "    PRESENT\n", 0, 0, 0 );
127 #ifdef SLAPD_SCHEMA_NOT_COMPAT
128                 rc = test_presence_filter( be, conn, op, e, f->f_desc );
129 #else
130                 rc = test_presence_filter( be, conn, op, e, f->f_type );
131 #endif
132                 break;
133
134         case LDAP_FILTER_APPROX:
135                 Debug( LDAP_DEBUG_FILTER, "    APPROX\n", 0, 0, 0 );
136 #ifdef SLAPD_SCHEMA_NOT_COMPAT
137                 rc = test_ava_filter( be, conn, op, e, f->f_ava,
138                     LDAP_FILTER_APPROX );
139 #else
140                 rc = test_approx_filter( be, conn, op, e, &f->f_ava );
141 #endif
142                 break;
143
144         case LDAP_FILTER_AND:
145                 Debug( LDAP_DEBUG_FILTER, "    AND\n", 0, 0, 0 );
146                 rc = test_filter_and( be, conn, op, e, f->f_and );
147                 break;
148
149         case LDAP_FILTER_OR:
150                 Debug( LDAP_DEBUG_FILTER, "    OR\n", 0, 0, 0 );
151                 rc = test_filter_or( be, conn, op, e, f->f_or );
152                 break;
153
154         case LDAP_FILTER_NOT:
155                 Debug( LDAP_DEBUG_FILTER, "    NOT\n", 0, 0, 0 );
156                 rc = test_filter( be, conn, op, e, f->f_not );
157
158                 switch( rc ) {
159                 case LDAP_COMPARE_TRUE:
160                         rc = LDAP_COMPARE_FALSE;
161                         break;
162                 case LDAP_COMPARE_FALSE:
163                         rc = LDAP_COMPARE_TRUE;
164                         break;
165                 }
166                 break;
167
168 #ifdef SLAPD_EXT_FILTERS
169         case LDAP_FILTER_EXT:
170                 Debug( LDAP_DEBUG_FILTER, "    EXT\n", 0, 0, 0 );
171 #if SLAPD_SCHEMA_NOT_COMPAT
172                 rc = test_mra_filter( be, conn, op, e, f->f_mra );
173 #else
174                 rc = LDAP_UNWILLING_TO_PERFORM;
175 #endif
176                 break;
177 #endif
178
179         default:
180                 Debug( LDAP_DEBUG_ANY, "    unknown filter type %lu\n",
181                     f->f_choice, 0, 0 );
182                 rc = LDAP_PROTOCOL_ERROR;
183         }
184
185         Debug( LDAP_DEBUG_FILTER, "<= test_filter %d\n", rc, 0, 0 );
186         return( rc );
187 }
188
189
190 static int
191 test_ava_filter(
192     Backend     *be,
193     Connection  *conn,
194     Operation   *op,
195     Entry       *e,
196 #ifdef SLAPD_SCHEMA_NOT_COMPAT
197         AttributeAssertion *ava,
198 #else
199     Ava         *ava,
200 #endif
201     int         type
202 )
203 {
204         int             i;
205         Attribute       *a;
206
207 #ifdef SLAPD_SCHEMA_NOT_COMPAT
208         if ( be != NULL && ! access_allowed( be, conn, op, e,
209                 ava->aa_desc, ava->aa_value, ACL_SEARCH ) )
210 #else
211
212         if ( be != NULL && ! access_allowed( be, conn, op, e,
213                 ava->ava_type, &ava->ava_value, ACL_SEARCH ) )
214 #endif
215         {
216                 return LDAP_INSUFFICIENT_ACCESS;
217         }
218
219 #ifdef SLAPD_SCHEMA_NOT_COMPAT
220         for(a = attrs_find( e->e_attrs, ava->aa_desc );
221                 a != NULL;
222                 a = attrs_find( a->a_next, ava->aa_desc ) )
223 #else
224         a = attr_find( e->e_attrs, ava->ava_type );
225         if ( a != NULL )
226 #endif
227         {
228 #ifdef SLAPD_SCHEMA_NOT_COMPAT
229                 MatchingRule *mr;
230
231                 switch ( type ) {
232                 case LDAP_FILTER_EQUALITY:
233                 case LDAP_FILTER_APPROX:
234                         mr = a->a_desc->ad_type->sat_equality;
235                         break;
236
237                 case LDAP_FILTER_GE:
238                 case LDAP_FILTER_LE:
239                         mr = a->a_desc->ad_type->sat_ordering;
240                         break;
241
242                 default:
243                         mr = NULL;
244                 }
245
246                 if( mr == NULL ) {
247                         continue;
248                 }
249 #else
250                 if ( a->a_syntax == 0 ) {
251                         a->a_syntax = attr_syntax( ava->ava_type );
252                 }
253 #endif
254
255                 for ( i = 0; a->a_vals[i] != NULL; i++ ) {
256                         int ret;
257 #ifdef SLAPD_SCHEMA_NOT_COMPAT
258                         int rc;
259                         const char *text;
260
261                         rc = value_match( &ret, a->a_desc, mr,
262                                 a->a_vals[i], ava->aa_value,
263                                 &text );
264
265                         if( rc != LDAP_SUCCESS ) {
266                                 return rc;
267                         }
268 #else
269                         ret = value_cmp( a->a_vals[i], &ava->ava_value, a->a_syntax,
270                                 3 );
271 #endif
272
273                         switch ( type ) {
274                         case LDAP_FILTER_EQUALITY:
275                         case LDAP_FILTER_APPROX:
276                                 if ( ret == 0 ) {
277                                         return LDAP_COMPARE_TRUE;
278                                 }
279                                 break;
280
281                         case LDAP_FILTER_GE:
282                                 if ( ret >= 0 ) {
283                                         return LDAP_COMPARE_TRUE;
284                                 }
285                                 break;
286
287                         case LDAP_FILTER_LE:
288                                 if ( ret <= 0 ) {
289                                         return LDAP_COMPARE_TRUE;
290                                 }
291                                 break;
292                         }
293                 }
294         }
295
296         return( LDAP_COMPARE_FALSE );
297 }
298
299
300 static int
301 test_presence_filter(
302     Backend     *be,
303     Connection  *conn,
304     Operation   *op,
305     Entry       *e,
306 #ifdef SLAPD_SCHEMA_NOT_COMPAT
307         AttributeDescription *desc
308 #else
309     char *desc
310 #endif
311 )
312 {
313         if ( be != NULL && ! access_allowed( be, conn, op, e,
314                 desc, NULL, ACL_SEARCH ) )
315         {
316                 return LDAP_INSUFFICIENT_ACCESS;
317         }
318
319 #ifdef SLAPD_SCHEMA_NOT_COMPAT
320         return attrs_find( e->e_attrs, desc ) != NULL
321 #else
322         return attr_find( e->e_attrs, desc ) != NULL
323 #endif
324                 ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
325 }
326
327 #ifndef SLAPD_SCHEMA_NOT_COMPAT
328 static int
329 test_approx_filter(
330     Backend     *be,
331     Connection  *conn,
332     Operation   *op,
333     Entry       *e,
334     Ava         *ava
335 )
336 {
337         char            *w1, *w2, *c1, *c2;
338         int             i;
339         Attribute       *a;
340
341         if ( be != NULL && ! access_allowed( be, conn, op, e,
342                 ava->ava_type, NULL, ACL_SEARCH ) )
343         {
344                 return LDAP_INSUFFICIENT_ACCESS;
345         }
346
347         a = attr_find( e->e_attrs, ava->ava_type );
348         if ( a != NULL ) {
349                 /* for each value in the attribute */
350                 for ( i = 0; a->a_vals[i] != NULL; i++ ) {
351                         /*
352                          * try to match words in the filter value in order
353                          * in the attribute value.
354                          */
355
356                         w2 = a->a_vals[i]->bv_val;
357                         /* for each word in the filter value */
358                         for ( w1 = first_word( ava->ava_value.bv_val ); w1 != NULL;
359                                 w1 = next_word( w1 ) ) {
360                                 if ( (c1 = phonetic( w1 )) == NULL ) {
361                                         break;
362                                 }
363
364                                 /*
365                                  * for each word in the attribute value from
366                                  * where we left off...
367                                  */
368                                 for ( w2 = first_word( w2 ); w2 != NULL;
369                                         w2 = next_word( w2 ) ) {
370                                         c2 = phonetic( w2 );
371                                         if ( strcmp( c1, c2 ) == 0 ) {
372                                                 free( c2 );
373                                                 break;
374                                         }
375                                         free( c2 );
376                                 }
377                                 free( c1 );
378
379                                 /*
380                                  * if we stopped because we ran out of words
381                                  * before making a match, go on to the next
382                                  * value.  otherwise try to keep matching
383                                  * words in this value from where we left off.
384                                  */
385                                 if ( w2 == NULL ) {
386                                         break;
387                                 } else {
388                                         w2 = next_word( w2 );
389                                 }
390                         }
391                         /*
392                          * if we stopped because we ran out of words we
393                          * have a match.
394                          */
395                         if ( w1 == NULL ) {
396                                 return LDAP_COMPARE_TRUE;
397                         }
398                 }
399         }
400
401         return LDAP_COMPARE_FALSE;
402 }
403 #endif
404
405 static int
406 test_filter_and(
407     Backend     *be,
408     Connection  *conn,
409     Operation   *op,
410     Entry       *e,
411     Filter      *flist
412 )
413 {
414         Filter  *f;
415         int rtn = LDAP_COMPARE_TRUE;
416
417         Debug( LDAP_DEBUG_FILTER, "=> test_filter_and\n", 0, 0, 0 );
418
419         for ( f = flist; f != NULL; f = f->f_next ) {
420                 int rc = test_filter( be, conn, op, e, f );
421
422                 if ( rc == LDAP_COMPARE_FALSE ) {
423                         rtn = LDAP_COMPARE_FALSE;
424                         break;
425                 }
426                 if ( rc != LDAP_COMPARE_TRUE ) {
427                         rtn = rc;
428                 }
429         }
430
431         Debug( LDAP_DEBUG_FILTER, "<= test_filter_and %d\n", rtn, 0, 0 );
432         return rtn;
433 }
434
435 static int
436 test_filter_or(
437     Backend     *be,
438     Connection  *conn,
439     Operation   *op,
440     Entry       *e,
441     Filter      *flist
442 )
443 {
444         Filter  *f;
445         int rtn = LDAP_COMPARE_FALSE;
446
447         Debug( LDAP_DEBUG_FILTER, "=> test_filter_or\n", 0, 0, 0 );
448
449         for ( f = flist; f != NULL; f = f->f_next ) {
450                 int rc = test_filter( be, conn, op, e, f );
451
452                 if ( rc == LDAP_COMPARE_TRUE ) {
453                         rtn = LDAP_COMPARE_TRUE;
454                         break;
455                 }
456                 if ( rc != LDAP_COMPARE_TRUE ) {
457                         rtn = rc;
458                 }
459         }
460
461         Debug( LDAP_DEBUG_FILTER, "<= test_filter_or %d\n", rtn, 0, 0 );
462         return rtn;
463 }
464
465 #ifndef SLAPD_SCHEMA_NOT_COMPAT
466 static void
467 strcpy_regex( char *d, char *s )
468 {
469         for ( ; *s; s++ ) {
470                 switch ( *s ) {
471                 case '^':
472                 case '.':
473                 case '[':
474                 case ']': /* ? */
475                 case '$':
476                 case '(':
477                 case ')': /* ? */
478                 case '|':
479                 case '*':
480                 case '+':
481                 case '?':
482                 case '{':
483                 case '}': /* ? */
484                 case '\\':
485                         *d++ = '\\';
486                         /* FALL */
487                 default:
488                         *d++ = *s;
489                 }
490         }
491         *d = '\0';
492 }
493 #endif
494
495 static int
496 test_substring_filter(
497     Backend     *be,
498     Connection  *conn,
499     Operation   *op,
500     Entry       *e,
501     Filter      *f
502 )
503 {
504 #ifndef SLAPD_SCHEMA_NOT_COMPAT
505         Attribute       *a;
506         int             i, rc;
507         char            *p, *end, *realval, *tmp;
508         char            pat[BUFSIZ];
509         char            buf[BUFSIZ];
510         struct berval   *val;
511         regex_t         re;
512
513         Debug( LDAP_DEBUG_FILTER, "begin test_substring_filter\n", 0, 0, 0 );
514
515         if ( be != NULL && ! access_allowed( be, conn, op, e,
516                 f->f_sub_type, NULL, ACL_SEARCH ) )
517         {
518                 return LDAP_INSUFFICIENT_ACCESS;
519         }
520
521         if ( (a = attr_find( e->e_attrs, f->f_sub_type )) == NULL ) {
522                 return LDAP_COMPARE_FALSE;
523         }
524
525         if ( a->a_syntax & SYNTAX_BIN ) {
526                 Debug( LDAP_DEBUG_FILTER, "test_substring_filter bin attr\n",
527                     0, 0, 0 );
528                 return LDAP_INAPPROPRIATE_MATCHING;
529         }
530
531         /*
532          * construct a regular expression corresponding to the
533          * filter and let regex do the work
534          */
535
536         pat[0] = '\0';
537         p = pat;
538         end = pat + sizeof(pat) - 2;    /* leave room for null */
539         if ( f->f_sub_initial != NULL ) {
540                 strcpy( p, "^" );
541                 p = strchr( p, '\0' );
542                 /* 2 * in case every char is special */
543                 if ( p + 2 * f->f_sub_initial->bv_len > end ) {
544                         Debug( LDAP_DEBUG_ANY, "not enough pattern space\n",
545                             0, 0, 0 );
546                         return LDAP_OTHER;
547                 }
548                 strcpy_regex( p, f->f_sub_initial->bv_val );
549                 p = strchr( p, '\0' );
550         }
551         if ( f->f_sub_any != NULL ) {
552                 for ( i = 0; f->f_sub_any[i] != NULL; i++ ) {
553                         /* ".*" + value */
554                         if ( p + 2 * f->f_sub_any[i]->bv_len + 2 > end ) {
555                                 Debug( LDAP_DEBUG_ANY,
556                                     "not enough pattern space\n", 0, 0, 0 );
557                                 return LDAP_OTHER;
558                         }
559                         strcpy( p, ".*" );
560                         p = strchr( p, '\0' );
561                         strcpy_regex( p, f->f_sub_any[i]->bv_val );
562                         p = strchr( p, '\0' );
563                 }
564         }
565         if ( f->f_sub_final != NULL ) {
566                 /* ".*" + value */
567                 if ( p + 2 * f->f_sub_final->bv_len + 2 > end ) {
568                         Debug( LDAP_DEBUG_ANY, "not enough pattern space\n",
569                             0, 0, 0 );
570                         return( -1 );
571                 }
572                 strcpy( p, ".*" );
573                 p = strchr( p, '\0' );
574                 strcpy_regex( p, f->f_sub_final->bv_val );
575                 p = strchr( p, '\0' );
576                 strcpy( p, "$" );
577         }
578
579         /* compile the regex */
580         Debug( LDAP_DEBUG_FILTER, "test_substring_filter: regcomp pat: %s\n",
581                 pat, 0, 0 );
582         if ((rc = regcomp(&re, pat, REG_EXTENDED|REG_NOSUB))) {
583                 char error[512];
584
585                 regerror(rc, &re, error, sizeof(error));
586                 Debug( LDAP_DEBUG_ANY, "regcomp failed (%s) %s\n",
587                         p, error, 0 );
588                 return LDAP_OTHER;
589         }
590
591         /* for each value in the attribute see if regex matches */
592         for ( i = 0; a->a_vals[i] != NULL; i++ ) {
593                 val = a->a_vals[i];
594                 tmp = NULL;
595                 if ( val->bv_len < sizeof(buf) ) {
596                         strcpy( buf, val->bv_val );
597                         realval = buf;
598                 } else {
599                         tmp = (char *) ch_malloc( val->bv_len + 1 );
600                         strcpy( tmp, val->bv_val );
601                         realval = tmp;
602                 }
603
604                 value_normalize( realval, a->a_syntax );
605
606                 rc = !regexec(&re, realval, 0, NULL, 0);
607
608                 if ( tmp != NULL ) {
609                         free( tmp );
610                 }
611                 if ( rc == 1 ) {
612                         regfree(&re);
613                         return LDAP_COMPARE_TRUE;
614                 }
615         }
616
617         regfree(&re);
618 #endif
619
620         Debug( LDAP_DEBUG_FILTER, "end test_substring_filter 1\n", 0, 0, 0 );
621         return LDAP_COMPARE_FALSE;
622 }