]> git.sur5r.net Git - openldap/blob - servers/slapd/filterentry.c
Sync with HEAD
[openldap] / servers / slapd / filterentry.c
1 /* filterentry.c - apply a filter to an entry */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2005 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
17  * All rights reserved.
18  *
19  * Redistribution and use in source and binary forms are permitted
20  * provided that this notice is preserved and that due credit is given
21  * to the University of Michigan at Ann Arbor. The name of the University
22  * may not be used to endorse or promote products derived from this
23  * software without specific prior written permission. This software
24  * is provided ``as is'' without express or implied warranty.
25  */
26
27 #include "portable.h"
28
29 #include <stdio.h>
30
31 #include <ac/socket.h>
32 #include <ac/string.h>
33
34
35 #include "slap.h"
36
37 static int      test_filter_and( Operation *op, Entry *e, Filter *flist );
38 static int      test_filter_or( Operation *op, Entry *e, Filter *flist );
39 static int      test_substrings_filter( Operation *op, Entry *e, Filter *f);
40 static int      test_ava_filter( Operation *op,
41         Entry *e, AttributeAssertion *ava, int type );
42 static int      test_mra_filter( Operation *op,
43         Entry *e, MatchingRuleAssertion *mra );
44 static int      test_presence_filter( Operation *op,
45         Entry *e, AttributeDescription *desc );
46
47
48 /*
49  * test_filter - test a filter against a single entry.
50  * returns:
51  *              LDAP_COMPARE_TRUE               filter matched
52  *              LDAP_COMPARE_FALSE              filter did not match
53  *              SLAPD_COMPARE_UNDEFINED filter is undefined
54  *      or an ldap result code indicating error
55  */
56
57 int
58 test_filter(
59     Operation   *op,
60     Entry       *e,
61     Filter      *f )
62 {
63         int     rc;
64         Debug( LDAP_DEBUG_FILTER, "=> test_filter\n", 0, 0, 0 );
65
66         switch ( f->f_choice ) {
67         case SLAPD_FILTER_COMPUTED:
68                 Debug( LDAP_DEBUG_FILTER, "    COMPUTED %s (%d)\n",
69                         f->f_result == LDAP_COMPARE_FALSE ? "false" :
70                         f->f_result == LDAP_COMPARE_TRUE ? "true" :
71                         f->f_result == SLAPD_COMPARE_UNDEFINED ? "undefined" : "error",
72                         f->f_result, 0 );
73
74                 rc = f->f_result;
75                 break;
76
77         case LDAP_FILTER_EQUALITY:
78                 Debug( LDAP_DEBUG_FILTER, "    EQUALITY\n", 0, 0, 0 );
79                 rc = test_ava_filter( op, e, f->f_ava, LDAP_FILTER_EQUALITY );
80                 break;
81
82         case LDAP_FILTER_SUBSTRINGS:
83                 Debug( LDAP_DEBUG_FILTER, "    SUBSTRINGS\n", 0, 0, 0 );
84                 rc = test_substrings_filter( op, e, f );
85                 break;
86
87         case LDAP_FILTER_GE:
88                 Debug( LDAP_DEBUG_FILTER, "    GE\n", 0, 0, 0 );
89                 rc = test_ava_filter( op, e, f->f_ava, LDAP_FILTER_GE );
90                 break;
91
92         case LDAP_FILTER_LE:
93                 Debug( LDAP_DEBUG_FILTER, "    LE\n", 0, 0, 0 );
94                 rc = test_ava_filter( op, e, f->f_ava, LDAP_FILTER_LE );
95                 break;
96
97         case LDAP_FILTER_PRESENT:
98                 Debug( LDAP_DEBUG_FILTER, "    PRESENT\n", 0, 0, 0 );
99                 rc = test_presence_filter( op, e, f->f_desc );
100                 break;
101
102         case LDAP_FILTER_APPROX:
103                 Debug( LDAP_DEBUG_FILTER, "    APPROX\n", 0, 0, 0 );
104                 rc = test_ava_filter( op, e, f->f_ava, LDAP_FILTER_APPROX );
105                 break;
106
107         case LDAP_FILTER_AND:
108                 Debug( LDAP_DEBUG_FILTER, "    AND\n", 0, 0, 0 );
109                 rc = test_filter_and( op, e, f->f_and );
110                 break;
111
112         case LDAP_FILTER_OR:
113                 Debug( LDAP_DEBUG_FILTER, "    OR\n", 0, 0, 0 );
114                 rc = test_filter_or( op, e, f->f_or );
115                 break;
116
117         case LDAP_FILTER_NOT:
118                 Debug( LDAP_DEBUG_FILTER, "    NOT\n", 0, 0, 0 );
119                 rc = test_filter( op, e, f->f_not );
120
121                 /* Flip true to false and false to true
122                  * but leave Undefined alone.
123                  */
124                 switch( rc ) {
125                 case LDAP_COMPARE_TRUE:
126                         rc = LDAP_COMPARE_FALSE;
127                         break;
128                 case LDAP_COMPARE_FALSE:
129                         rc = LDAP_COMPARE_TRUE;
130                         break;
131                 }
132                 break;
133
134         case LDAP_FILTER_EXT:
135                 Debug( LDAP_DEBUG_FILTER, "    EXT\n", 0, 0, 0 );
136                 rc = test_mra_filter( op, e, f->f_mra );
137                 break;
138
139         default:
140                 Debug( LDAP_DEBUG_ANY, "    unknown filter type %lu\n",
141                     f->f_choice, 0, 0 );
142                 rc = LDAP_PROTOCOL_ERROR;
143         }
144
145         Debug( LDAP_DEBUG_FILTER, "<= test_filter %d\n", rc, 0, 0 );
146         return( rc );
147 }
148
149 static int test_mra_filter(
150         Operation *op,
151         Entry *e,
152         MatchingRuleAssertion *mra )
153 {
154         Attribute       *a;
155         void            *memctx;
156         BER_MEMFREE_FN  *memfree;
157 #ifdef LDAP_COMP_MATCH
158         int i, num_attr_vals;
159 #endif
160
161         if ( op == NULL ) {
162                 memctx = NULL;
163                 memfree = slap_sl_free;
164         } else {
165                 memctx = op->o_tmpmemctx;
166                 memfree = op->o_tmpfree;
167         }
168
169         if ( mra->ma_desc ) {
170                 /*
171                  * if ma_desc is available, then we're filtering for
172                  * one attribute, and SEARCH permissions can be checked
173                  * directly.
174                  */
175                 if( !access_allowed( op, e,
176                         mra->ma_desc, &mra->ma_value, ACL_SEARCH, NULL ) )
177                 {
178                         return LDAP_INSUFFICIENT_ACCESS;
179                 }
180
181                 if ( mra->ma_desc == slap_schema.si_ad_entryDN ) {
182                         int ret, rc;
183                         const char *text;
184
185                         rc = value_match( &ret, slap_schema.si_ad_entryDN, mra->ma_rule,
186                                 0, &e->e_nname, &mra->ma_value, &text );
187         
188         
189                         if( rc != LDAP_SUCCESS ) return rc;
190                         if ( ret == 0 ) return LDAP_COMPARE_TRUE;
191                         return LDAP_COMPARE_FALSE;
192                 }
193
194                 for(a = attrs_find( e->e_attrs, mra->ma_desc );
195                         a != NULL;
196                         a = attrs_find( a->a_next, mra->ma_desc ) )
197                 {
198                         struct berval *bv;
199 #ifdef LDAP_COMP_MATCH
200                         /* Component Matching */
201                         if( mra->ma_cf && mra->ma_rule->smr_usage & SLAP_MR_COMPONENT ) {
202                                 num_attr_vals = 0;
203                                 if ( !a->a_comp_data ) {
204                                         for ( ;
205                                                 a->a_vals[num_attr_vals].bv_val != NULL;
206                                                 num_attr_vals++ )
207                                         {
208                                                 /* empty */;
209                                         }
210                                         if ( num_attr_vals <= 0 ) {
211                                                 /* no attribute value */
212                                                 return LDAP_INAPPROPRIATE_MATCHING;
213                                         }
214                                         num_attr_vals++;
215
216                                         /* following malloced will be freed by comp_tree_free () */
217                                         a->a_comp_data = malloc( sizeof( ComponentData ) +
218                                                 sizeof( ComponentSyntaxInfo* )*num_attr_vals );
219
220                                         if ( !a->a_comp_data ) return LDAP_NO_MEMORY;
221                                         a->a_comp_data->cd_tree = (ComponentSyntaxInfo**)
222                                                 ((char*)a->a_comp_data + sizeof(ComponentData));
223                                         a->a_comp_data->cd_tree[num_attr_vals - 1] =
224                                                 (ComponentSyntaxInfo*) NULL;
225                                         a->a_comp_data->cd_mem_op =
226                                                 nibble_mem_allocator( 1024*16, 1024 );
227                                 }
228                         }
229 #endif
230
231                         /* If ma_rule is not the same as the attribute's
232                          * normal rule, then we can't use the a_nvals.
233                          */
234                         if (mra->ma_rule == a->a_desc->ad_type->sat_equality) {
235                                 bv = a->a_nvals;
236                         } else {
237                                 bv = a->a_vals;
238                         }
239 #ifdef LDAP_COMP_MATCH
240                         i = 0;
241 #endif
242                         for ( ; bv->bv_val != NULL; bv++ ) {
243                                 int ret;
244                                 int rc;
245                                 const char *text;
246         
247 #ifdef LDAP_COMP_MATCH
248                                 if( mra->ma_cf &&
249                                         mra->ma_rule->smr_usage & SLAP_MR_COMPONENT ) {
250                                         /* Check if decoded component trees are already linked */
251                                         if ( num_attr_vals ) {
252                                                 a->a_comp_data->cd_tree[i] = attr_converter(
253                                                         a, a->a_desc->ad_type->sat_syntax, bv );
254                                         }
255                                         /* decoding error */
256                                         if ( !a->a_comp_data->cd_tree[i] ) {
257                                                 return LDAP_OPERATIONS_ERROR;
258                                         }
259                                         rc = value_match( &ret, a->a_desc, mra->ma_rule, 0,
260                                                 (struct berval*)a->a_comp_data->cd_tree[i++],
261                                                 (void*)mra, &text );
262                                 } else 
263 #endif
264                                 {
265                                         rc = value_match( &ret, a->a_desc, mra->ma_rule, 0,
266                                                 bv, &mra->ma_value, &text );
267                                 }
268
269                                 if( rc != LDAP_SUCCESS ) return rc;
270                                 if ( ret == 0 ) return LDAP_COMPARE_TRUE;
271                         }
272                 }
273
274         } else {
275                 /*
276                  * No attribute description: test all
277                  */
278                 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
279                         struct berval   *bv, value;
280                         const char      *text = NULL;
281                         int             rc;
282
283                         /* check if matching is appropriate */
284                         if ( !mr_usable_with_at( mra->ma_rule, a->a_desc->ad_type )) {
285                                 continue;
286                         }
287
288                         /* normalize for equality */
289                         rc = asserted_value_validate_normalize( a->a_desc, mra->ma_rule,
290                                 SLAP_MR_EXT|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
291                                 &mra->ma_value, &value, &text, memctx );
292                         if ( rc != LDAP_SUCCESS ) continue;
293
294                         /* check search access */
295                         if ( !access_allowed( op, e,
296                                 a->a_desc, &value, ACL_SEARCH, NULL ) ) {
297                                 memfree( value.bv_val, memctx );
298                                 continue;
299                         }
300 #ifdef LDAP_COMP_MATCH
301                         /* Component Matching */
302                         if( mra->ma_cf &&
303                                 mra->ma_rule->smr_usage & SLAP_MR_COMPONENT)
304                         {
305                                 int ret;
306
307                                 rc = value_match( &ret, a->a_desc, mra->ma_rule, 0,
308                                         (struct berval*)a, (void*)mra, &text );
309                                 if( rc != LDAP_SUCCESS ) break;
310         
311                                 if ( ret == 0 ) {
312                                         rc = LDAP_COMPARE_TRUE;
313                                         break;
314                                 }
315
316                         }
317 #endif
318
319                         /* check match */
320                         if (mra->ma_rule == a->a_desc->ad_type->sat_equality) {
321                                 bv = a->a_nvals;
322                         } else {
323                                 bv = a->a_vals;
324                         }
325
326                         for ( ; bv->bv_val != NULL; bv++ ) {
327                                 int ret;
328         
329                                 rc = value_match( &ret, a->a_desc, mra->ma_rule, 0,
330                                         bv, &value, &text );
331
332                                 if( rc != LDAP_SUCCESS ) break;
333         
334                                 if ( ret == 0 ) {
335                                         rc = LDAP_COMPARE_TRUE;
336                                         break;
337                                 }
338                         }
339                         memfree( value.bv_val, memctx );
340                         if ( rc != LDAP_SUCCESS ) return rc;
341                 }
342         }
343
344         /* check attrs in DN AVAs if required */
345         if ( mra->ma_dnattrs && !BER_BVISEMPTY( &e->e_nname ) ) {
346                 LDAPDN          dn = NULL;
347                 int             iRDN, iAVA;
348                 int             rc;
349
350                 /* parse and pretty the dn */
351                 rc = dnPrettyDN( NULL, &e->e_name, &dn, memctx );
352                 if ( rc != LDAP_SUCCESS ) {
353                         return LDAP_INVALID_SYNTAX;
354                 }
355
356                 /* for each AVA of each RDN ... */
357                 for ( iRDN = 0; dn[ iRDN ]; iRDN++ ) {
358                         LDAPRDN         rdn = dn[ iRDN ];
359
360                         for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
361                                 LDAPAVA         *ava = rdn[ iAVA ];
362                                 struct berval   *bv = &ava->la_value, value;
363                                 AttributeDescription *ad =
364                                         (AttributeDescription *)ava->la_private;
365                                 int ret;
366                                 const char *text;
367
368                                 assert( ad );
369
370                                 if ( mra->ma_desc ) {
371                                         /* have a mra type? check for subtype */
372                                         if ( !is_ad_subtype( ad, mra->ma_desc ) ) {
373                                                 continue;
374                                         }
375                                         value = mra->ma_value;
376
377                                 } else {
378                                         const char      *text = NULL;
379
380                                         /* check if matching is appropriate */
381                                         if ( !mr_usable_with_at( mra->ma_rule, ad->ad_type )) {
382                                                 continue;
383                                         }
384
385                                         /* normalize for equality */
386                                         rc = asserted_value_validate_normalize( ad,
387                                                 mra->ma_rule,
388                                                 SLAP_MR_EXT|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
389                                                 &mra->ma_value, &value, &text, memctx );
390                                         if ( rc != LDAP_SUCCESS ) continue;
391
392                                         /* check search access */
393                                         if ( !access_allowed( op, e,
394                                                 ad, &value, ACL_SEARCH, NULL ) )
395                                         {
396                                                 memfree( value.bv_val, memctx );
397                                                 continue;
398                                         }
399                                 }
400
401                                 /* check match */
402                                 rc = value_match( &ret, ad, mra->ma_rule, 0,
403                                         bv, &value, &text );
404                                 if ( value.bv_val != mra->ma_value.bv_val ) {
405                                         memfree( value.bv_val, memctx );
406                                 }
407
408                                 if ( rc == LDAP_SUCCESS && ret == 0 ) rc = LDAP_COMPARE_TRUE;
409
410                                 if( rc != LDAP_SUCCESS ) {
411                                         ldap_dnfree_x( dn, memctx );
412                                         return rc;
413                                 }
414                         }
415                 }
416                 ldap_dnfree_x( dn, memctx );
417         }
418
419         return LDAP_COMPARE_FALSE;
420 }
421
422 static int
423 test_ava_filter(
424         Operation       *op,
425         Entry           *e,
426         AttributeAssertion *ava,
427         int             type )
428 {
429         int rc;
430         Attribute       *a;
431
432         if ( !access_allowed( op, e,
433                 ava->aa_desc, &ava->aa_value, ACL_SEARCH, NULL ) )
434         {
435                 return LDAP_INSUFFICIENT_ACCESS;
436         }
437
438         if ( ava->aa_desc == slap_schema.si_ad_hasSubordinates 
439                 && op && op->o_bd && op->o_bd->be_has_subordinates )
440         {
441                 int     hasSubordinates;
442                 struct berval hs;
443
444                 if( type != LDAP_FILTER_EQUALITY &&
445                         type != LDAP_FILTER_APPROX )
446                 {
447                         /* No other match is allowed */
448                         return LDAP_INAPPROPRIATE_MATCHING;
449                 }
450                 
451                 if ( op->o_bd->be_has_subordinates( op, e, &hasSubordinates ) !=
452                         LDAP_SUCCESS )
453                 {
454                         return LDAP_OTHER;
455                 }
456
457                 if ( hasSubordinates == LDAP_COMPARE_TRUE ) {
458                         hs = slap_true_bv;
459
460                 } else if ( hasSubordinates == LDAP_COMPARE_FALSE ) {
461                         hs = slap_false_bv;
462
463                 } else {
464                         return LDAP_OTHER;
465                 }
466
467                 if ( bvmatch( &ava->aa_value, &hs ) ) return LDAP_COMPARE_TRUE;
468                 return LDAP_COMPARE_FALSE;
469         }
470
471         if ( ava->aa_desc == slap_schema.si_ad_entryDN ) {
472                 MatchingRule *mr;
473                 int rc, match;
474                 const char *text;
475
476                 if( type != LDAP_FILTER_EQUALITY &&
477                         type != LDAP_FILTER_APPROX )
478                 {
479                         /* No other match is allowed */
480                         return LDAP_INAPPROPRIATE_MATCHING;
481                 }
482
483                 mr = slap_schema.si_ad_entryDN->ad_type->sat_equality;
484                 assert( mr );
485
486                 rc = value_match( &match, slap_schema.si_ad_entryDN, mr, 0,
487                         &e->e_nname, &ava->aa_value, &text );
488
489                 if( rc != LDAP_SUCCESS ) return rc;
490                 if( match == 0 ) return LDAP_COMPARE_TRUE;
491                 return LDAP_COMPARE_FALSE;
492         }
493
494         rc = LDAP_COMPARE_FALSE;
495
496         for(a = attrs_find( e->e_attrs, ava->aa_desc );
497                 a != NULL;
498                 a = attrs_find( a->a_next, ava->aa_desc ) )
499         {
500                 MatchingRule *mr;
501                 struct berval *bv;
502
503                 if (( ava->aa_desc != a->a_desc ) && !access_allowed( op,
504                         e, a->a_desc, &ava->aa_value, ACL_SEARCH, NULL ))
505                 {
506                         rc = LDAP_INSUFFICIENT_ACCESS;
507                         continue;
508                 }
509
510                 switch ( type ) {
511                 case LDAP_FILTER_APPROX:
512                         mr = a->a_desc->ad_type->sat_approx;
513                         if( mr != NULL ) break;
514
515                         /* use EQUALITY matching rule if no APPROX rule */
516
517                 case LDAP_FILTER_EQUALITY:
518                         mr = a->a_desc->ad_type->sat_equality;
519                         break;
520
521                 case LDAP_FILTER_GE:
522                 case LDAP_FILTER_LE:
523                         mr = a->a_desc->ad_type->sat_ordering;
524                         break;
525
526                 default:
527                         mr = NULL;
528                 }
529
530                 if( mr == NULL ) {
531                         rc = LDAP_INAPPROPRIATE_MATCHING;
532                         continue;
533                 }
534
535                 for ( bv = a->a_nvals; bv->bv_val != NULL; bv++ ) {
536                         int ret, match;
537                         const char *text;
538
539                         ret = value_match( &match, a->a_desc, mr, 0,
540                                 bv, &ava->aa_value, &text );
541
542                         if( ret != LDAP_SUCCESS ) {
543                                 rc = ret;
544                                 break;
545                         }
546
547                         switch ( type ) {
548                         case LDAP_FILTER_EQUALITY:
549                         case LDAP_FILTER_APPROX:
550                                 if ( match == 0 ) return LDAP_COMPARE_TRUE;
551                                 break;
552
553                         case LDAP_FILTER_GE:
554                                 if ( match >= 0 ) return LDAP_COMPARE_TRUE;
555                                 break;
556
557                         case LDAP_FILTER_LE:
558                                 if ( match <= 0 ) return LDAP_COMPARE_TRUE;
559                                 break;
560                         }
561                 }
562         }
563
564         return rc;
565 }
566
567
568 static int
569 test_presence_filter(
570         Operation       *op,
571         Entry           *e,
572         AttributeDescription *desc )
573 {
574         Attribute       *a;
575         int rc;
576
577         if ( !access_allowed( op, e, desc, NULL, ACL_SEARCH, NULL ) ) {
578                 return LDAP_INSUFFICIENT_ACCESS;
579         }
580
581         if ( desc == slap_schema.si_ad_hasSubordinates ) {
582                 /*
583                  * XXX: fairly optimistic: if the function is defined,
584                  * then PRESENCE must succeed, because hasSubordinate
585                  * is boolean-valued; I think we may live with this 
586                  * simplification by now.
587                  */
588                 if ( op && op->o_bd && op->o_bd->be_has_subordinates ) {
589                         return LDAP_COMPARE_TRUE;
590                 }
591
592                 return LDAP_COMPARE_FALSE;
593         }
594
595         if ( desc == slap_schema.si_ad_entryDN ||
596                 desc == slap_schema.si_ad_subschemaSubentry )
597         {
598                 /* entryDN and subschemaSubentry are always present */
599                 return LDAP_COMPARE_TRUE;
600         }
601
602         rc = LDAP_COMPARE_FALSE;
603
604         for(a = attrs_find( e->e_attrs, desc );
605                 a != NULL;
606                 a = attrs_find( a->a_next, desc ) )
607         {
608                 if (( desc != a->a_desc ) && !access_allowed( op,
609                         e, a->a_desc, NULL, ACL_SEARCH, NULL ))
610                 {
611                         rc = LDAP_INSUFFICIENT_ACCESS;
612                         continue;
613                 }
614
615                 rc = LDAP_COMPARE_TRUE;
616                 break;
617         }
618
619         return rc;
620 }
621
622
623 static int
624 test_filter_and(
625         Operation       *op,
626         Entry   *e,
627         Filter  *flist )
628 {
629         Filter  *f;
630         int rtn = LDAP_COMPARE_TRUE; /* True if empty */
631
632         Debug( LDAP_DEBUG_FILTER, "=> test_filter_and\n", 0, 0, 0 );
633
634         for ( f = flist; f != NULL; f = f->f_next ) {
635                 int rc = test_filter( op, e, f );
636
637                 if ( rc == LDAP_COMPARE_FALSE ) {
638                         /* filter is False */
639                         rtn = rc;
640                         break;
641                 }
642
643                 if ( rc != LDAP_COMPARE_TRUE ) {
644                         /* filter is Undefined unless later elements are False */
645                         rtn = rc;
646                 }
647         }
648
649         Debug( LDAP_DEBUG_FILTER, "<= test_filter_and %d\n", rtn, 0, 0 );
650
651         return rtn;
652 }
653
654 static int
655 test_filter_or(
656         Operation       *op,
657         Entry   *e,
658         Filter  *flist )
659 {
660         Filter  *f;
661         int rtn = LDAP_COMPARE_FALSE; /* False if empty */
662
663         Debug( LDAP_DEBUG_FILTER, "=> test_filter_or\n", 0, 0, 0 );
664
665         for ( f = flist; f != NULL; f = f->f_next ) {
666                 int rc = test_filter( op, e, f );
667
668                 if ( rc == LDAP_COMPARE_TRUE ) {
669                         /* filter is True */
670                         rtn = rc;
671                         break;
672                 }
673
674                 if ( rc != LDAP_COMPARE_FALSE ) {
675                         /* filter is Undefined unless later elements are True */
676                         rtn = rc;
677                 }
678         }
679
680         Debug( LDAP_DEBUG_FILTER, "<= test_filter_or %d\n", rtn, 0, 0 );
681         return rtn;
682 }
683
684
685 static int
686 test_substrings_filter(
687         Operation       *op,
688         Entry   *e,
689         Filter  *f )
690 {
691         Attribute       *a;
692         int rc;
693
694         Debug( LDAP_DEBUG_FILTER, "begin test_substrings_filter\n", 0, 0, 0 );
695
696         if ( !access_allowed( op, e,
697                 f->f_sub_desc, NULL, ACL_SEARCH, NULL ) )
698         {
699                 return LDAP_INSUFFICIENT_ACCESS;
700         }
701
702         rc = LDAP_COMPARE_FALSE;
703
704         for(a = attrs_find( e->e_attrs, f->f_sub_desc );
705                 a != NULL;
706                 a = attrs_find( a->a_next, f->f_sub_desc ) )
707         {
708                 MatchingRule *mr;
709                 struct berval *bv;
710
711                 if (( f->f_sub_desc != a->a_desc ) && !access_allowed( op,
712                         e, a->a_desc, NULL, ACL_SEARCH, NULL ))
713                 {
714                         rc = LDAP_INSUFFICIENT_ACCESS;
715                         continue;
716                 }
717
718                 mr = a->a_desc->ad_type->sat_substr;
719                 if( mr == NULL ) {
720                         rc = LDAP_INAPPROPRIATE_MATCHING;
721                         continue;
722                 }
723
724                 for ( bv = a->a_nvals; bv->bv_val != NULL; bv++ ) {
725                         int ret, match;
726                         const char *text;
727
728                         ret = value_match( &match, a->a_desc, mr, 0,
729                                 bv, f->f_sub, &text );
730
731                         if( ret != LDAP_SUCCESS ) {
732                                 rc = ret;
733                                 break;
734                         }
735                         if ( match == 0 ) return LDAP_COMPARE_TRUE;
736                 }
737         }
738
739         Debug( LDAP_DEBUG_FILTER, "end test_substrings_filter %d\n",
740                 rc, 0, 0 );
741         return rc;
742 }