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