]> git.sur5r.net Git - openldap/blob - servers/slapd/filterentry.c
ac0a943d02e4d7b58d5ed811a8ef0c569e157c28
[openldap] / servers / slapd / filterentry.c
1 /* filterentry.c - apply a filter to an entry */
2
3 #include "portable.h"
4
5 #include <stdio.h>
6
7 #include <ac/socket.h>
8 #include <ac/string.h>
9 #include <ac/regex.h>
10
11 #include "slap.h"
12
13 extern Attribute        *attr_find();
14 extern char             *first_word();
15 extern char             *next_word();
16 extern char             *phonetic();
17
18 static int      test_filter_list();
19 static int      test_substring_filter();
20 static int      test_ava_filter();
21 static int      test_approx_filter();
22 static int      test_presence_filter();
23
24 /*
25  * test_filter - test a filter against a single entry.
26  * returns      0       filter matched
27  *              -1      filter did not match
28  *              >0      an ldap error code
29  */
30
31 int
32 test_filter(
33     Backend     *be,
34     Connection  *conn,
35     Operation   *op,
36     Entry       *e,
37     Filter      *f
38 )
39 {
40         int     rc;
41
42         Debug( LDAP_DEBUG_FILTER, "=> test_filter\n", 0, 0, 0 );
43
44         switch ( f->f_choice ) {
45         case LDAP_FILTER_EQUALITY:
46                 Debug( LDAP_DEBUG_FILTER, "    EQUALITY\n", 0, 0, 0 );
47                 rc = test_ava_filter( be, conn, op, e, &f->f_ava,
48                     LDAP_FILTER_EQUALITY );
49                 break;
50
51         case LDAP_FILTER_SUBSTRINGS:
52                 Debug( LDAP_DEBUG_FILTER, "    SUBSTRINGS\n", 0, 0, 0 );
53                 rc = test_substring_filter( be, conn, op, e, f );
54                 break;
55
56         case LDAP_FILTER_GE:
57                 Debug( LDAP_DEBUG_FILTER, "    GE\n", 0, 0, 0 );
58                 rc = test_ava_filter( be, conn, op, e, &f->f_ava,
59                     LDAP_FILTER_GE );
60                 break;
61
62         case LDAP_FILTER_LE:
63                 Debug( LDAP_DEBUG_FILTER, "    LE\n", 0, 0, 0 );
64                 rc = test_ava_filter( be, conn, op, e, &f->f_ava,
65                     LDAP_FILTER_LE );
66                 break;
67
68         case LDAP_FILTER_PRESENT:
69                 Debug( LDAP_DEBUG_FILTER, "    PRESENT\n", 0, 0, 0 );
70                 rc = test_presence_filter( be, conn, op, e, f->f_type );
71                 break;
72
73         case LDAP_FILTER_APPROX:
74                 Debug( LDAP_DEBUG_FILTER, "    APPROX\n", 0, 0, 0 );
75                 rc = test_approx_filter( be, conn, op, e, &f->f_ava );
76                 break;
77
78         case LDAP_FILTER_AND:
79                 Debug( LDAP_DEBUG_FILTER, "    AND\n", 0, 0, 0 );
80                 rc = test_filter_list( be, conn, op, e, f->f_and,
81                     LDAP_FILTER_AND );
82                 break;
83
84         case LDAP_FILTER_OR:
85                 Debug( LDAP_DEBUG_FILTER, "    OR\n", 0, 0, 0 );
86                 rc = test_filter_list( be, conn, op, e, f->f_or,
87                     LDAP_FILTER_OR );
88                 break;
89
90         case LDAP_FILTER_NOT:
91                 Debug( LDAP_DEBUG_FILTER, "    NOT\n", 0, 0, 0 );
92                 rc = (! test_filter( be, conn, op, e, f->f_not ) );
93                 break;
94
95         default:
96                 Debug( LDAP_DEBUG_ANY, "    unknown filter type %d\n",
97                     f->f_choice, 0, 0 );
98                 rc = -1;
99         }
100
101         Debug( LDAP_DEBUG_FILTER, "<= test_filter %d\n", rc, 0, 0 );
102         return( rc );
103 }
104
105 static int
106 test_ava_filter(
107     Backend     *be,
108     Connection  *conn,
109     Operation   *op,
110     Entry       *e,
111     Ava         *ava,
112     int         type
113 )
114 {
115         int             i, rc;
116         Attribute       *a;
117
118         if ( be != NULL && ! access_allowed( be, conn, op, e, ava->ava_type,
119             &ava->ava_value, op->o_dn, ACL_SEARCH ) ) {
120                 return( -2 );
121         }
122
123         if ( (a = attr_find( e->e_attrs, ava->ava_type )) == NULL ) {
124                 return( -1 );
125         }
126
127         if ( a->a_syntax == 0 ) {
128                 a->a_syntax = attr_syntax( ava->ava_type );
129         }
130         for ( i = 0; a->a_vals[i] != NULL; i++ ) {
131                 rc = value_cmp( a->a_vals[i], &ava->ava_value, a->a_syntax,
132                     3 );
133
134                 switch ( type ) {
135                 case LDAP_FILTER_EQUALITY:
136                         if ( rc == 0 ) {
137                                 return( 0 );
138                         }
139                         break;
140
141                 case LDAP_FILTER_GE:
142                         if ( rc > 0 ) {
143                                 return( 0 );
144                         }
145                         break;
146
147                 case LDAP_FILTER_LE:
148                         if ( rc < 0 ) {
149                                 return( 0 );
150                         }
151                         break;
152                 }
153         }
154
155         return( 1 );
156 }
157
158 static int
159 test_presence_filter(
160     Backend     *be,
161     Connection  *conn,
162     Operation   *op,
163     Entry       *e,
164     char        *type
165 )
166 {
167         if ( be != NULL && ! access_allowed( be, conn, op, e, type, NULL,
168             op->o_dn, ACL_SEARCH ) ) {
169                 return( -2 );
170         }
171
172         return( attr_find( e->e_attrs, type ) != NULL ? 0 : -1 );
173 }
174
175 static int
176 test_approx_filter(
177     Backend     *be,
178     Connection  *conn,
179     Operation   *op,
180     Entry       *e,
181     Ava         *ava
182 )
183 {
184         char            *w1, *w2, *c1, *c2;
185         int             i, rc, match;
186         Attribute       *a;
187
188         if ( be != NULL && ! access_allowed( be, conn, op, e, ava->ava_type,
189             NULL, op->o_dn, ACL_SEARCH ) ) {
190                 return( -2 );
191         }
192
193         if ( (a = attr_find( e->e_attrs, ava->ava_type )) == NULL ) {
194                 return( -1 );
195         }
196
197         /* for each value in the attribute */
198         for ( i = 0; a->a_vals[i] != NULL; i++ ) {
199                 /*
200                  * try to match words in the filter value in order
201                  * in the attribute value.
202                  */
203
204                 w2 = a->a_vals[i]->bv_val;
205                 /* for each word in the filter value */
206                 for ( w1 = first_word( ava->ava_value.bv_val ); w1 != NULL;
207                     w1 = next_word( w1 ) ) {
208                         if ( (c1 = phonetic( w1 )) == NULL ) {
209                                 break;
210                         }
211
212                         /*
213                          * for each word in the attribute value from
214                          * where we left off...
215                          */
216                         for ( w2 = first_word( w2 ); w2 != NULL;
217                             w2 = next_word( w2 ) ) {
218                                 c2 = phonetic( w2 );
219                                 if ( strcmp( c1, c2 ) == 0 ) {
220                                         free( c2 );
221                                         break;
222                                 }
223                                 free( c2 );
224                         }
225                         free( c1 );
226
227                         /*
228                          * if we stopped because we ran out of words
229                          * before making a match, go on to the next
230                          * value.  otherwise try to keep matching
231                          * words in this value from where we left off.
232                          */
233                         if ( w2 == NULL ) {
234                                 break;
235                         } else {
236                                 w2 = next_word( w2 );
237                         }
238                 }
239                 /*
240                  * if we stopped because we ran out of words we
241                  * have a match.
242                  */
243                 if ( w1 == NULL ) {
244                         return( 0 );
245                 }
246         }
247
248         return( 1 );
249 }
250
251 static int
252 test_filter_list(
253     Backend     *be,
254     Connection  *conn,
255     Operation   *op,
256     Entry       *e,
257     Filter      *flist,
258     int         ftype
259 )
260 {
261         int     rc, nomatch;
262         Filter  *f;
263
264         Debug( LDAP_DEBUG_FILTER, "=> test_filter_list\n", 0, 0, 0 );
265
266         nomatch = 1;
267         for ( f = flist; f != NULL; f = f->f_next ) {
268                 if ( test_filter( be, conn, op, e, f ) != 0 ) {
269                         if ( ftype == LDAP_FILTER_AND ) {
270                                 Debug( LDAP_DEBUG_FILTER,
271                                     "<= test_filter_list 1\n", 0, 0, 0 );
272                                 return( 1 );
273                         }
274                 } else {
275                         nomatch = 0;
276                 }
277         }
278
279         Debug( LDAP_DEBUG_FILTER, "<= test_filter_list %d\n", nomatch, 0, 0 );
280         return( nomatch );
281 }
282
283 static void
284 strcpy_special( char *d, char *s )
285 {
286         for ( ; *s; s++ ) {
287                 switch ( *s ) {
288                 case '.':
289                 case '\\':
290                 case '[':
291                 case ']':
292                 case '*':
293                 case '+':
294                 case '^':
295                 case '$':
296                         *d++ = '\\';
297                         /* FALL */
298                 default:
299                         *d++ = *s;
300                 }
301         }
302         *d = '\0';
303 }
304
305 static int
306 test_substring_filter(
307     Backend     *be,
308     Connection  *conn,
309     Operation   *op,
310     Entry       *e,
311     Filter      *f
312 )
313 {
314         Attribute       *a;
315         int             i, rc;
316         char            *p, *end, *realval, *tmp;
317         char            pat[BUFSIZ];
318         char            buf[BUFSIZ];
319         struct berval   *val;
320         regex_t         re;
321
322         Debug( LDAP_DEBUG_FILTER, "begin test_substring_filter\n", 0, 0, 0 );
323
324         if ( be != NULL && ! access_allowed( be, conn, op, e, f->f_sub_type,
325             NULL, op->o_dn, ACL_SEARCH ) ) {
326                 return( -2 );
327         }
328
329         if ( (a = attr_find( e->e_attrs, f->f_sub_type )) == NULL ) {
330                 return( -1 );
331         }
332
333         if ( a->a_syntax & SYNTAX_BIN ) {
334                 Debug( LDAP_DEBUG_FILTER, "test_substring_filter bin attr\n",
335                     0, 0, 0 );
336                 return( -1 );
337         }
338
339         /*
340          * construct a regular expression corresponding to the
341          * filter and let regex do the work
342          */
343
344         pat[0] = '\0';
345         p = pat;
346         end = pat + sizeof(pat) - 2;    /* leave room for null */
347         if ( f->f_sub_initial != NULL ) {
348                 strcpy( p, "^" );
349                 p = strchr( p, '\0' );
350                 /* 2 * in case every char is special */
351                 if ( p + 2 * strlen( f->f_sub_initial ) > end ) {
352                         Debug( LDAP_DEBUG_ANY, "not enough pattern space\n",
353                             0, 0, 0 );
354                         return( -1 );
355                 }
356                 strcpy_special( p, f->f_sub_initial );
357                 p = strchr( p, '\0' );
358         }
359         if ( f->f_sub_any != NULL ) {
360                 for ( i = 0; f->f_sub_any[i] != NULL; i++ ) {
361                         /* ".*" + value */
362                         if ( p + 2 * strlen( f->f_sub_any[i] ) + 2 > end ) {
363                                 Debug( LDAP_DEBUG_ANY,
364                                     "not enough pattern space\n", 0, 0, 0 );
365                                 return( -1 );
366                         }
367                         strcpy( p, ".*" );
368                         p = strchr( p, '\0' );
369                         strcpy_special( p, f->f_sub_any[i] );
370                         p = strchr( p, '\0' );
371                 }
372         }
373         if ( f->f_sub_final != NULL ) {
374                 /* ".*" + value */
375                 if ( p + 2 * strlen( f->f_sub_final ) + 2 > end ) {
376                         Debug( LDAP_DEBUG_ANY, "not enough pattern space\n",
377                             0, 0, 0 );
378                         return( -1 );
379                 }
380                 strcpy( p, ".*" );
381                 p = strchr( p, '\0' );
382                 strcpy_special( p, f->f_sub_final );
383                 p = strchr( p, '\0' );
384                 strcpy( p, "$" );
385         }
386
387         /* compile the regex */
388         Debug( LDAP_DEBUG_FILTER, "test_substring_filter: regcomp pat: %s\n",
389                 pat, 0, 0 );
390         if ((rc = regcomp(&re, pat, 0))) {
391                 char error[512];
392
393                 regerror(rc, &re, error, sizeof(error));
394                 Debug( LDAP_DEBUG_ANY, "regcomp failed (%s) %s\n",
395                         p, error, 0 );
396                 return( -1 );
397         }
398
399         /* for each value in the attribute see if regex matches */
400         for ( i = 0; a->a_vals[i] != NULL; i++ ) {
401                 val = a->a_vals[i];
402                 tmp = NULL;
403                 if ( val->bv_len < sizeof(buf) ) {
404                         strcpy( buf, val->bv_val );
405                         realval = buf;
406                 } else {
407                         tmp = (char *) ch_malloc( val->bv_len + 1 );
408                         strcpy( tmp, val->bv_val );
409                         realval = tmp;
410                 }
411                 value_normalize( realval, a->a_syntax );
412
413                 rc = !regexec(&re, realval, 0, NULL, 0);
414
415                 if ( tmp != NULL ) {
416                         free( tmp );
417                 }
418                 if ( rc == 1 ) {
419                         regfree(&re);
420                         return( 0 );
421                 }
422         }
423
424         regfree(&re);
425
426         Debug( LDAP_DEBUG_FILTER, "end test_substring_filter 1\n", 0, 0, 0 );
427         return( 1 );
428 }