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