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