]> git.sur5r.net Git - openldap/blob - servers/slapd/filterentry.c
97918c731f396e59943388395bde76542c1a1b20
[openldap] / servers / slapd / filterentry.c
1 /* filterentry.c - apply a filter to an entry */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-1999 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 = -1;
175 #endif
176                 break;
177 #endif
178
179         case 0:
180                 Debug( LDAP_DEBUG_FILTER, "    UNDEFINED\n", 0, 0, 0 );
181                 rc = -1;
182                 break;
183
184         default:
185                 Debug( LDAP_DEBUG_ANY, "    unknown filter type %lu\n",
186                     f->f_choice, 0, 0 );
187                 rc = -1;
188         }
189
190         Debug( LDAP_DEBUG_FILTER, "<= test_filter %d\n", rc, 0, 0 );
191         return( rc );
192 }
193
194
195 static int
196 test_ava_filter(
197     Backend     *be,
198     Connection  *conn,
199     Operation   *op,
200     Entry       *e,
201 #ifdef SLAPD_SCHEMA_NOT_COMPAT
202         AttributeAssertion *ava,
203 #else
204     Ava         *ava,
205 #endif
206     int         type
207 )
208 {
209         int             i;
210         Attribute       *a;
211
212 #ifdef SLAPD_SCHEMA_NOT_COMPAT
213         if ( be != NULL && ! access_allowed( be, conn, op, e,
214                 ava->aa_desc, ava->aa_value, ACL_SEARCH ) )
215 #else
216
217         if ( be != NULL && ! access_allowed( be, conn, op, e,
218                 ava->ava_type, &ava->ava_value, ACL_SEARCH ) )
219 #endif
220         {
221                 return( -2 );
222         }
223
224 #ifdef SLAPD_SCHEMA_NOT_COMPAT
225         for(a = attrs_find( e->e_attrs, ava->aa_desc );
226                 a != NULL;
227                 a = attrs_find( a, ava->aa_desc ) )
228 #else
229         a = attr_find( e->e_attrs, ava->ava_type );
230         if ( a != NULL )
231 #endif
232         {
233 #ifndef SLAPD_SCHEMA_NOT_COMPAT
234                 if ( a->a_syntax == 0 ) {
235                         a->a_syntax = attr_syntax( ava->ava_type );
236                 }
237 #endif
238
239                 for ( i = 0; a->a_vals[i] != NULL; i++ ) {
240                         int rc;
241
242 #ifdef SLAPD_SCHEMA_NOT_COMPAT
243                         /* not yet implemented */
244                         rc = 0;
245 #else
246                         rc = value_cmp( a->a_vals[i], &ava->ava_value, a->a_syntax,
247                                 3 );
248 #endif
249
250                         switch ( type ) {
251                         case LDAP_FILTER_EQUALITY:
252                         case LDAP_FILTER_APPROX:
253                                 if ( rc == 0 ) {
254                                         return LDAP_COMPARE_TRUE;
255                                 }
256                                 break;
257
258                         case LDAP_FILTER_GE:
259                                 if ( rc >= 0 ) {
260                                         return LDAP_COMPARE_TRUE;
261                                 }
262                                 break;
263
264                         case LDAP_FILTER_LE:
265                                 if ( rc <= 0 ) {
266                                         return LDAP_COMPARE_TRUE;
267                                 }
268                                 break;
269                         }
270                 }
271         }
272
273         return( LDAP_COMPARE_FALSE );
274 }
275
276
277 static int
278 test_presence_filter(
279     Backend     *be,
280     Connection  *conn,
281     Operation   *op,
282     Entry       *e,
283 #ifdef SLAPD_SCHEMA_NOT_COMPAT
284         AttributeDescription *desc
285 #else
286     char        *desc
287 #endif
288 )
289 {
290         if ( be != NULL && ! access_allowed( be, conn, op, e,
291                 desc, NULL, ACL_SEARCH ) )
292         {
293                 return( -2 );
294         }
295
296 #ifdef SLAPD_SCHEMA_NOT_COMPAT
297         return attrs_find( e->e_attrs, desc ) != NULL
298 #else
299         return attr_find( e->e_attrs, desc ) != NULL
300 #endif
301                 ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
302 }
303
304 #ifndef SLAPD_SCHEMA_NOT_COMPAT
305 static int
306 test_approx_filter(
307     Backend     *be,
308     Connection  *conn,
309     Operation   *op,
310     Entry       *e,
311     Ava         *ava
312 )
313 {
314         char            *w1, *w2, *c1, *c2;
315         int             i;
316         Attribute       *a;
317
318         if ( be != NULL && ! access_allowed( be, conn, op, e,
319                 ava->ava_type, NULL, ACL_SEARCH ) )
320         {
321                 return( -2 );
322         }
323
324         a = attr_find( e->e_attrs, ava->ava_type );
325         if ( a != NULL ) {
326                 /* for each value in the attribute */
327                 for ( i = 0; a->a_vals[i] != NULL; i++ ) {
328                         /*
329                          * try to match words in the filter value in order
330                          * in the attribute value.
331                          */
332
333                         w2 = a->a_vals[i]->bv_val;
334                         /* for each word in the filter value */
335                         for ( w1 = first_word( ava->ava_value.bv_val ); w1 != NULL;
336                                 w1 = next_word( w1 ) ) {
337                                 if ( (c1 = phonetic( w1 )) == NULL ) {
338                                         break;
339                                 }
340
341                                 /*
342                                  * for each word in the attribute value from
343                                  * where we left off...
344                                  */
345                                 for ( w2 = first_word( w2 ); w2 != NULL;
346                                         w2 = next_word( w2 ) ) {
347                                         c2 = phonetic( w2 );
348                                         if ( strcmp( c1, c2 ) == 0 ) {
349                                                 free( c2 );
350                                                 break;
351                                         }
352                                         free( c2 );
353                                 }
354                                 free( c1 );
355
356                                 /*
357                                  * if we stopped because we ran out of words
358                                  * before making a match, go on to the next
359                                  * value.  otherwise try to keep matching
360                                  * words in this value from where we left off.
361                                  */
362                                 if ( w2 == NULL ) {
363                                         break;
364                                 } else {
365                                         w2 = next_word( w2 );
366                                 }
367                         }
368                         /*
369                          * if we stopped because we ran out of words we
370                          * have a match.
371                          */
372                         if ( w1 == NULL ) {
373                                 return LDAP_COMPARE_TRUE;
374                         }
375                 }
376         }
377
378         return LDAP_COMPARE_FALSE;
379 }
380 #endif
381
382 static int
383 test_filter_and(
384     Backend     *be,
385     Connection  *conn,
386     Operation   *op,
387     Entry       *e,
388     Filter      *flist
389 )
390 {
391         Filter  *f;
392         int rtn = LDAP_COMPARE_TRUE;
393
394         Debug( LDAP_DEBUG_FILTER, "=> test_filter_and\n", 0, 0, 0 );
395
396         for ( f = flist; f != NULL; f = f->f_next ) {
397                 int rc = test_filter( be, conn, op, e, f );
398
399                 if ( rc == LDAP_COMPARE_FALSE ) {
400                         rtn = LDAP_COMPARE_FALSE;
401                         break;
402                 }
403                 if ( rc != LDAP_COMPARE_TRUE ) {
404                         rtn = rc;
405                 }
406         }
407
408         Debug( LDAP_DEBUG_FILTER, "<= test_filter_and %d\n", rtn, 0, 0 );
409         return rtn;
410 }
411
412 static int
413 test_filter_or(
414     Backend     *be,
415     Connection  *conn,
416     Operation   *op,
417     Entry       *e,
418     Filter      *flist
419 )
420 {
421         Filter  *f;
422         int rtn = LDAP_COMPARE_FALSE;
423
424         Debug( LDAP_DEBUG_FILTER, "=> test_filter_or\n", 0, 0, 0 );
425
426         for ( f = flist; f != NULL; f = f->f_next ) {
427                 int rc = test_filter( be, conn, op, e, f );
428
429                 if ( rc == LDAP_COMPARE_TRUE ) {
430                         rtn = LDAP_COMPARE_TRUE;
431                         break;
432                 }
433                 if ( rc != LDAP_COMPARE_TRUE ) {
434                         rtn = rc;
435                 }
436         }
437
438         Debug( LDAP_DEBUG_FILTER, "<= test_filter_or %d\n", rtn, 0, 0 );
439         return rtn;
440 }
441
442 #ifndef SLAPD_SCHEMA_NOT_COMPAT
443 static void
444 strcpy_regex( char *d, char *s )
445 {
446         for ( ; *s; s++ ) {
447                 switch ( *s ) {
448                 case '^':
449                 case '.':
450                 case '[':
451                 case ']': /* ? */
452                 case '$':
453                 case '(':
454                 case ')': /* ? */
455                 case '|':
456                 case '*':
457                 case '+':
458                 case '?':
459                 case '{':
460                 case '}': /* ? */
461                 case '\\':
462                         *d++ = '\\';
463                         /* FALL */
464                 default:
465                         *d++ = *s;
466                 }
467         }
468         *d = '\0';
469 }
470 #endif
471
472 static int
473 test_substring_filter(
474     Backend     *be,
475     Connection  *conn,
476     Operation   *op,
477     Entry       *e,
478     Filter      *f
479 )
480 {
481 #ifndef SLAPD_SCHEMA_NOT_COMPAT
482         Attribute       *a;
483         int             i, rc;
484         char            *p, *end, *realval, *tmp;
485         char            pat[BUFSIZ];
486         char            buf[BUFSIZ];
487         struct berval   *val;
488         regex_t         re;
489
490         Debug( LDAP_DEBUG_FILTER, "begin test_substring_filter\n", 0, 0, 0 );
491
492         if ( be != NULL && ! access_allowed( be, conn, op, e,
493                 f->f_sub_type, NULL, ACL_SEARCH ) )
494         {
495                 return( -2 );
496         }
497
498         if ( (a = attr_find( e->e_attrs, f->f_sub_type )) == NULL ) {
499                 return LDAP_COMPARE_FALSE;
500         }
501
502         if ( a->a_syntax & SYNTAX_BIN ) {
503                 Debug( LDAP_DEBUG_FILTER, "test_substring_filter bin attr\n",
504                     0, 0, 0 );
505                 return( -1 );
506         }
507
508         /*
509          * construct a regular expression corresponding to the
510          * filter and let regex do the work
511          */
512
513         pat[0] = '\0';
514         p = pat;
515         end = pat + sizeof(pat) - 2;    /* leave room for null */
516         if ( f->f_sub_initial != NULL ) {
517                 strcpy( p, "^" );
518                 p = strchr( p, '\0' );
519                 /* 2 * in case every char is special */
520                 if ( p + 2 * f->f_sub_initial->bv_len > end ) {
521                         Debug( LDAP_DEBUG_ANY, "not enough pattern space\n",
522                             0, 0, 0 );
523                         return( -1 );
524                 }
525                 strcpy_regex( p, f->f_sub_initial->bv_val );
526                 p = strchr( p, '\0' );
527         }
528         if ( f->f_sub_any != NULL ) {
529                 for ( i = 0; f->f_sub_any[i] != NULL; i++ ) {
530                         /* ".*" + value */
531                         if ( p + 2 * f->f_sub_any[i]->bv_len + 2 > end ) {
532                                 Debug( LDAP_DEBUG_ANY,
533                                     "not enough pattern space\n", 0, 0, 0 );
534                                 return( -1 );
535                         }
536                         strcpy( p, ".*" );
537                         p = strchr( p, '\0' );
538                         strcpy_regex( p, f->f_sub_any[i]->bv_val );
539                         p = strchr( p, '\0' );
540                 }
541         }
542         if ( f->f_sub_final != NULL ) {
543                 /* ".*" + value */
544                 if ( p + 2 * f->f_sub_final->bv_len + 2 > end ) {
545                         Debug( LDAP_DEBUG_ANY, "not enough pattern space\n",
546                             0, 0, 0 );
547                         return( -1 );
548                 }
549                 strcpy( p, ".*" );
550                 p = strchr( p, '\0' );
551                 strcpy_regex( p, f->f_sub_final->bv_val );
552                 p = strchr( p, '\0' );
553                 strcpy( p, "$" );
554         }
555
556         /* compile the regex */
557         Debug( LDAP_DEBUG_FILTER, "test_substring_filter: regcomp pat: %s\n",
558                 pat, 0, 0 );
559         if ((rc = regcomp(&re, pat, REG_EXTENDED|REG_NOSUB))) {
560                 char error[512];
561
562                 regerror(rc, &re, error, sizeof(error));
563                 Debug( LDAP_DEBUG_ANY, "regcomp failed (%s) %s\n",
564                         p, error, 0 );
565                 return( -1 );
566         }
567
568         /* for each value in the attribute see if regex matches */
569         for ( i = 0; a->a_vals[i] != NULL; i++ ) {
570                 val = a->a_vals[i];
571                 tmp = NULL;
572                 if ( val->bv_len < sizeof(buf) ) {
573                         strcpy( buf, val->bv_val );
574                         realval = buf;
575                 } else {
576                         tmp = (char *) ch_malloc( val->bv_len + 1 );
577                         strcpy( tmp, val->bv_val );
578                         realval = tmp;
579                 }
580
581                 value_normalize( realval, a->a_syntax );
582
583                 rc = !regexec(&re, realval, 0, NULL, 0);
584
585                 if ( tmp != NULL ) {
586                         free( tmp );
587                 }
588                 if ( rc == 1 ) {
589                         regfree(&re);
590                         return LDAP_COMPARE_TRUE;
591                 }
592         }
593
594         regfree(&re);
595 #endif
596
597         Debug( LDAP_DEBUG_FILTER, "end test_substring_filter 1\n", 0, 0, 0 );
598         return LDAP_COMPARE_FALSE;
599 }