]> git.sur5r.net Git - openldap/blob - libraries/libldap/search.c
Fix NULL pointer deref bugs
[openldap] / libraries / libldap / search.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*  Portions
7  *  Copyright (c) 1990 Regents of the University of Michigan.
8  *  All rights reserved.
9  *
10  *  search.c
11  */
12
13 #include "portable.h"
14
15 #include <stdio.h>
16
17 #include <ac/stdlib.h>
18
19 #include <ac/ctype.h>
20 #include <ac/socket.h>
21 #include <ac/string.h>
22 #include <ac/time.h>
23
24 #include "ldap-int.h"
25
26 static int ldap_is_attr_oid LDAP_P((
27         const char *attr ));
28
29 static int ldap_is_attr_desc LDAP_P((
30         const char *attr ));
31
32 static int hex2value LDAP_P((
33         int c ));
34
35 static char *find_right_paren LDAP_P((
36         char *s ));
37
38 static char *put_complex_filter LDAP_P((
39         BerElement *ber,
40         char *str,
41         ber_tag_t tag,
42         int not ));
43
44 static int put_filter LDAP_P((
45         BerElement *ber,
46         char *str ));
47
48 static int put_simple_filter LDAP_P((
49         BerElement *ber,
50         char *str ));
51
52 static int put_substring_filter LDAP_P((
53         BerElement *ber,
54         char *type,
55         char *str ));
56
57 static int put_filter_list LDAP_P((
58         BerElement *ber,
59         char *str ));
60
61 /*
62  * ldap_search_ext - initiate an ldap search operation.
63  *
64  * Parameters:
65  *
66  *      ld              LDAP descriptor
67  *      base            DN of the base object
68  *      scope           the search scope - one of LDAP_SCOPE_BASE,
69  *                          LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
70  *      filter          a string containing the search filter
71  *                      (e.g., "(|(cn=bob)(sn=bob))")
72  *      attrs           list of attribute types to return for matches
73  *      attrsonly       1 => attributes only 0 => attributes and values
74  *
75  * Example:
76  *      char    *attrs[] = { "mail", "title", 0 };
77  *      ldap_search_ext( ld, "c=us,o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
78  *          attrs, attrsonly, sctrls, ctrls, timeout, sizelimit,
79  *              &msgid );
80  */
81 int
82 ldap_search_ext(
83         LDAP *ld,
84         LDAP_CONST char *base,
85         int scope,
86         LDAP_CONST char *filter,
87         char **attrs,
88         int attrsonly,
89         LDAPControl **sctrls,
90         LDAPControl **cctrls,
91         struct timeval *timeout,
92         int sizelimit,
93         int *msgidp )
94 {
95         BerElement      *ber;
96         int timelimit;
97
98         Debug( LDAP_DEBUG_TRACE, "ldap_search_ext\n", 0, 0, 0 );
99
100         /*
101          * if timeout is provided, both tv_sec and tv_usec must
102          * be non-zero
103          */
104         if( timeout != NULL ) {
105                 if( timeout->tv_sec == 0 && timeout->tv_usec == 0 ) {
106                         return LDAP_PARAM_ERROR;
107                 }
108
109                 /* timelimit must be non-zero if timeout is provided */
110                 timelimit = timeout->tv_sec != 0 ? timeout->tv_sec : 1;
111
112         } else {
113                 /* no timeout, no timelimit */
114                 timelimit = -1;
115         }
116
117         ber = ldap_build_search_req( ld, base, scope, filter, attrs,
118             attrsonly, sctrls, cctrls, timelimit, sizelimit ); 
119
120         if ( ber == NULL ) {
121                 return ld->ld_errno;
122         }
123
124 #ifndef LDAP_NOCACHE
125         if ( ld->ld_cache != NULL ) {
126                 if ( ldap_check_cache( ld, LDAP_REQ_SEARCH, ber ) == 0 ) {
127                         ber_free( ber, 1 );
128                         ld->ld_errno = LDAP_SUCCESS;
129                         *msgidp = ld->ld_msgid;
130                         return ld->ld_errno;
131                 }
132                 ldap_add_request_to_cache( ld, LDAP_REQ_SEARCH, ber );
133         }
134 #endif /* LDAP_NOCACHE */
135
136         /* send the message */
137         *msgidp = ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber );
138
139         if( *msgidp < 0 )
140                 return ld->ld_errno;
141
142         return LDAP_SUCCESS;
143 }
144
145 int
146 ldap_search_ext_s(
147         LDAP *ld,
148         LDAP_CONST char *base,
149         int scope,
150         LDAP_CONST char *filter,
151         char **attrs,
152         int attrsonly,
153         LDAPControl **sctrls,
154         LDAPControl **cctrls,
155         struct timeval *timeout,
156         int sizelimit,
157         LDAPMessage **res )
158 {
159         int rc;
160         int     msgid;
161
162         rc = ldap_search_ext( ld, base, scope, filter, attrs, attrsonly,
163                 sctrls, cctrls, timeout, sizelimit, &msgid );
164
165         if ( rc != LDAP_SUCCESS ) {
166                 return( rc );
167         }
168
169         rc = ldap_result( ld, msgid, 1, timeout, res );
170
171         if( rc <= 0 ) {
172                 /* error(-1) or timeout(0) */
173                 return( ld->ld_errno );
174         }
175
176         return( ldap_result2error( ld, *res, 0 ) );
177 }
178
179 /*
180  * ldap_search - initiate an ldap search operation.
181  *
182  * Parameters:
183  *
184  *      ld              LDAP descriptor
185  *      base            DN of the base object
186  *      scope           the search scope - one of LDAP_SCOPE_BASE,
187  *                          LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
188  *      filter          a string containing the search filter
189  *                      (e.g., "(|(cn=bob)(sn=bob))")
190  *      attrs           list of attribute types to return for matches
191  *      attrsonly       1 => attributes only 0 => attributes and values
192  *
193  * Example:
194  *      char    *attrs[] = { "mail", "title", 0 };
195  *      msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
196  *          attrs, attrsonly );
197  */
198 int
199 ldap_search(
200         LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter,
201         char **attrs, int attrsonly )
202 {
203         BerElement      *ber;
204
205         Debug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 );
206
207         ber = ldap_build_search_req( ld, base, scope, filter, attrs,
208             attrsonly, NULL, NULL, -1, -1 ); 
209
210         if ( ber == NULL ) {
211                 return( -1 );
212         }
213
214 #ifndef LDAP_NOCACHE
215         if ( ld->ld_cache != NULL ) {
216                 if ( ldap_check_cache( ld, LDAP_REQ_SEARCH, ber ) == 0 ) {
217                         ber_free( ber, 1 );
218                         ld->ld_errno = LDAP_SUCCESS;
219                         return( ld->ld_msgid );
220                 }
221                 ldap_add_request_to_cache( ld, LDAP_REQ_SEARCH, ber );
222         }
223 #endif /* LDAP_NOCACHE */
224
225         /* send the message */
226         return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber ));
227 }
228
229
230 BerElement *
231 ldap_build_search_req(
232         LDAP *ld,
233         LDAP_CONST char *base,
234         ber_int_t scope,
235         LDAP_CONST char *filter_in,
236         char **attrs,
237         ber_int_t attrsonly,
238         LDAPControl **sctrls,
239         LDAPControl **cctrls,
240         ber_int_t timelimit,
241         ber_int_t sizelimit )
242 {
243         BerElement      *ber;
244         int             err;
245         char    *filter;
246
247         /*
248          * Create the search request.  It looks like this:
249          *      SearchRequest := [APPLICATION 3] SEQUENCE {
250          *              baseObject      DistinguishedName,
251          *              scope           ENUMERATED {
252          *                      baseObject      (0),
253          *                      singleLevel     (1),
254          *                      wholeSubtree    (2)
255          *              },
256          *              derefAliases    ENUMERATED {
257          *                      neverDerefaliases       (0),
258          *                      derefInSearching        (1),
259          *                      derefFindingBaseObj     (2),
260          *                      alwaysDerefAliases      (3)
261          *              },
262          *              sizelimit       INTEGER (0 .. 65535),
263          *              timelimit       INTEGER (0 .. 65535),
264          *              attrsOnly       BOOLEAN,
265          *              filter          Filter,
266          *              attributes      SEQUENCE OF AttributeType
267          *      }
268          * wrapped in an ldap message.
269          */
270
271         /* create a message to send */
272         if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
273                 return( NULL );
274         }
275
276         if ( base == NULL ) {
277                 /* no base provided, use session default base */
278                 base = ld->ld_options.ldo_defbase;
279
280                 if ( base == NULL ) {
281                         /* no session default base, use top */
282                         base = "";
283                 }
284         }
285
286 #ifdef LDAP_CONNECTIONLESS
287         if ( ld->ld_cldapnaddr > 0 ) {
288             err = ber_printf( ber, "{ist{seeiib", ++ld->ld_msgid,
289                         ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
290                         (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
291                         (timelimit < 0) ? ld->ld_timelimit : timelimit,
292                         attrsonly );
293         } else {
294 #endif /* LDAP_CONNECTIONLESS */
295                 err = ber_printf( ber, "{it{seeiib", ++ld->ld_msgid,
296                     LDAP_REQ_SEARCH, base, (ber_int_t) scope, ld->ld_deref,
297                         (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
298                         (timelimit < 0) ? ld->ld_timelimit : timelimit,
299                     attrsonly );
300 #ifdef LDAP_CONNECTIONLESS
301         }
302 #endif /* LDAP_CONNECTIONLESS */
303
304         if ( err == -1 ) {
305                 ld->ld_errno = LDAP_ENCODING_ERROR;
306                 ber_free( ber, 1 );
307                 return( NULL );
308         }
309
310         filter = LDAP_STRDUP( filter_in );
311         err = put_filter( ber, filter );
312         LDAP_FREE( filter );
313
314         if ( err  == -1 ) {
315                 ld->ld_errno = LDAP_FILTER_ERROR;
316                 ber_free( ber, 1 );
317                 return( NULL );
318         }
319
320         if ( ber_printf( ber, /*{*/ "{v}}", attrs ) == -1 ) {
321                 ld->ld_errno = LDAP_ENCODING_ERROR;
322                 ber_free( ber, 1 );
323                 return( NULL );
324         }
325
326         /* Put Server Controls */
327         if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
328                 ber_free( ber, 1 );
329                 return( NULL );
330         }
331
332         if ( ber_printf( ber, /*{*/ "}", attrs ) == -1 ) {
333                 ld->ld_errno = LDAP_ENCODING_ERROR;
334                 ber_free( ber, 1 );
335                 return( NULL );
336         }
337
338         return( ber );
339 }
340
341 static int ldap_is_attr_oid ( const char *attr )
342 {
343         int i, c, digit=0;
344
345         for( i = 0; (c = attr[i]) != 0; i++ ) {
346                 if( c >= '0' && c <= '9' ) {
347                         digit=1;
348
349                 } else if ( c != '.' ) {
350                         /* not digit nor '.' */
351                         return 0;
352
353                 } else if ( !digit ) {
354                         /* '.' but prev not digit */
355                         return 0;
356
357                 } else {
358                         /* '.' */
359                         digit = 0;
360                 }
361         }
362
363         return digit;
364 }
365
366 static int ldap_is_attr_desc ( const char *attr )
367 {
368         /* cheap attribute description check */
369         int i, c;
370
371         for( i = 0; (c = attr[i]) != 0; i++ ) {
372                 if (( c >= '0' && c <= '9' )
373                         || ( c >= 'A' && c <= 'Z' )
374                         || ( c >= 'a' && c <= 'z' )
375                         || ( c == '.' || c == '-' )
376                         || ( c == ';' )) continue;
377
378                 return 0;
379         }
380
381         return i > 0;
382 }
383
384 static char *
385 find_right_paren( char *s )
386 {
387         int     balance, escape;
388
389         balance = 1;
390         escape = 0;
391         while ( *s && balance ) {
392                 if ( escape == 0 ) {
393                         if ( *s == '(' )
394                                 balance++;
395                         else if ( *s == ')' )
396                                 balance--;
397                 }
398                 if ( *s == '\\' && ! escape )
399                         escape = 1;
400                 else
401                         escape = 0;
402                 if ( balance )
403                         s++;
404         }
405
406         return( *s ? s : NULL );
407 }
408
409 static int hex2value( int c )
410 {
411         if( c >= '0' && c <= '9' ) {
412                 return c - '0';
413         }
414
415         if( c >= 'A' && c <= 'F' ) {
416                 return c + (10 - (int) 'A');
417         }
418
419         if( c >= 'a' && c <= 'f' ) {
420                 return c + (10 - (int) 'a');
421         }
422
423         return -1;
424 }
425
426 char *
427 ldap_pvt_find_wildcard( const char *s )
428 {
429         for( ; *s != '\0' ; s++ ) {
430                 switch( *s ) {
431                 case '*':       /* found wildcard */
432                         return (char *) s;
433
434                 case '\\':
435                         s++; /* skip over escape */
436                         if ( *s == '\0' )
437                                 return NULL;    /* escape at end of string */
438                 }
439         }
440
441         return NULL;
442 }
443
444 /* unescape filter value */
445 /* support both LDAP v2 and v3 escapes */
446 /* output can include nul characters */
447 ber_slen_t
448 ldap_pvt_filter_value_unescape( char *fval )
449 {
450         ber_slen_t r, v;
451         int v1, v2;
452
453         for( r=v=0; fval[v] != '\0'; v++ ) {
454                 switch( fval[v] ) {
455                 case '\\':
456                         /* escape */
457                         v++;
458
459                         if ( fval[v] == '\0' ) {
460                                 /* escape at end of string */
461                                 return -1;
462
463                         }
464
465                         if (( v1 = hex2value( fval[v] )) >= 0 ) {
466                                 /* LDAPv3 escape */
467
468                                 if (( v2 = hex2value( fval[v+1] )) < 0 ) {
469                                         /* must be two digit code */
470                                         return -1;
471                                 }
472
473                                 fval[r++] = v1 * 16 + v2;
474                                 v++;
475
476                         } else {
477                                 /* LDAPv2 escape */
478                                 fval[r++] = fval[v];
479                         }
480
481                         break;
482
483                 default:
484                         fval[r++] = fval[v];
485                 }
486         }
487
488         fval[r] = '\0';
489         return r;
490 }
491
492 static char *
493 put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not )
494 {
495         char    *next;
496
497         /*
498          * We have (x(filter)...) with str sitting on
499          * the x.  We have to find the paren matching
500          * the one before the x and put the intervening
501          * filters by calling put_filter_list().
502          */
503
504         /* put explicit tag */
505         if ( ber_printf( ber, "t{" /*}*/, tag ) == -1 )
506                 return( NULL );
507
508         str++;
509         if ( (next = find_right_paren( str )) == NULL )
510                 return( NULL );
511
512         *next = '\0';
513         if ( put_filter_list( ber, str ) == -1 )
514                 return( NULL );
515         *next++ = ')';
516
517         /* flush explicit tagged thang */
518         if ( ber_printf( ber, /*{*/ "}" ) == -1 )
519                 return( NULL );
520
521         return( next );
522 }
523
524 static int
525 put_filter( BerElement *ber, char *str )
526 {
527         char    *next;
528         int     parens, balance, escape;
529
530         /*
531          * A Filter looks like this:
532          *      Filter ::= CHOICE {
533          *              and             [0]     SET OF Filter,
534          *              or              [1]     SET OF Filter,
535          *              not             [2]     Filter,
536          *              equalityMatch   [3]     AttributeValueAssertion,
537          *              substrings      [4]     SubstringFilter,
538          *              greaterOrEqual  [5]     AttributeValueAssertion,
539          *              lessOrEqual     [6]     AttributeValueAssertion,
540          *              present         [7]     AttributeType,
541          *              approxMatch     [8]     AttributeValueAssertion,
542          *                              extensibleMatch [9]             MatchingRuleAssertion -- LDAPv3
543          *      }
544          *
545          *      SubstringFilter ::= SEQUENCE {
546          *              type               AttributeType,
547          *              SEQUENCE OF CHOICE {
548          *                      initial          [0] IA5String,
549          *                      any              [1] IA5String,
550          *                      final            [2] IA5String
551          *              }
552          *      }
553          *
554          *              MatchingRuleAssertion ::= SEQUENCE {    -- LDAPv3
555          *                      matchingRule    [1] MatchingRuleId OPTIONAL,
556          *                      type            [2] AttributeDescription OPTIONAL,
557          *                      matchValue      [3] AssertionValue,
558          *                      dnAttributes    [4] BOOLEAN DEFAULT FALSE }
559          *
560          * Note: tags in a choice are always explicit
561          */
562
563         Debug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
564
565         parens = 0;
566         while ( *str ) {
567                 switch ( *str ) {
568                 case '(':
569                         str++;
570                         parens++;
571
572                         /* skip spaces */
573                         while( isspace( *str ) ) str++;
574
575                         switch ( *str ) {
576                         case '&':
577                                 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
578                                     0, 0, 0 );
579
580                                 if ( (str = put_complex_filter( ber, str,
581                                     LDAP_FILTER_AND, 0 )) == NULL )
582                                         return( -1 );
583
584                                 parens--;
585                                 break;
586
587                         case '|':
588                                 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
589                                     0, 0, 0 );
590
591                                 if ( (str = put_complex_filter( ber, str,
592                                     LDAP_FILTER_OR, 0 )) == NULL )
593                                         return( -1 );
594
595                                 parens--;
596                                 break;
597
598                         case '!':
599                                 Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
600                                     0, 0, 0 );
601
602                                 if ( (str = put_complex_filter( ber, str,
603                                     LDAP_FILTER_NOT, 1 )) == NULL )
604                                         return( -1 );
605
606                                 parens--;
607                                 break;
608
609                         default:
610                                 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
611                                     0, 0, 0 );
612
613                                 balance = 1;
614                                 escape = 0;
615                                 next = str;
616                                 while ( *next && balance ) {
617                                         if ( escape == 0 ) {
618                                                 if ( *next == '(' )
619                                                         balance++;
620                                                 else if ( *next == ')' )
621                                                         balance--;
622                                         }
623                                         if ( *next == '\\' && ! escape )
624                                                 escape = 1;
625                                         else
626                                                 escape = 0;
627                                         if ( balance )
628                                                 next++;
629                                 }
630                                 if ( balance != 0 )
631                                         return( -1 );
632
633                                 *next = '\0';
634                                 if ( put_simple_filter( ber, str ) == -1 ) {
635                                         return( -1 );
636                                 }
637                                 *next++ = ')';
638                                 str = next;
639                                 parens--;
640                                 break;
641                         }
642                         break;
643
644                 case ')':
645                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
646                             0 );
647                         if ( ber_printf( ber, /*[*/ "]" ) == -1 )
648                                 return( -1 );
649                         str++;
650                         parens--;
651                         break;
652
653                 case ' ':
654                         str++;
655                         break;
656
657                 default:        /* assume it's a simple type=value filter */
658                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
659                             0 );
660                         next = strchr( str, '\0' );
661                         if ( put_simple_filter( ber, str ) == -1 ) {
662                                 return( -1 );
663                         }
664                         str = next;
665                         break;
666                 }
667         }
668
669         return( parens ? -1 : 0 );
670 }
671
672 /*
673  * Put a list of filters like this "(filter1)(filter2)..."
674  */
675
676 static int
677 put_filter_list( BerElement *ber, char *str )
678 {
679         char    *next;
680         char    save;
681
682         Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
683
684         while ( *str ) {
685                 while ( *str && isspace( (unsigned char) *str ) )
686                         str++;
687                 if ( *str == '\0' )
688                         break;
689
690                 if ( (next = find_right_paren( str + 1 )) == NULL )
691                         return( -1 );
692                 save = *++next;
693
694                 /* now we have "(filter)" with str pointing to it */
695                 *next = '\0';
696                 if ( put_filter( ber, str ) == -1 )
697                         return( -1 );
698                 *next = save;
699
700                 str = next;
701         }
702
703         return( 0 );
704 }
705
706 static int
707 put_simple_filter(
708         BerElement *ber,
709         char *str )
710 {
711         char            *s;
712         char            *value;
713         ber_tag_t       ftype;
714         int             rc = -1;
715
716         Debug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
717
718         str = LDAP_STRDUP( str );
719         if( str == NULL ) return -1;
720
721         if ( (s = strchr( str, '=' )) == NULL ) {
722                 goto done;
723         }
724
725         value = s + 1;
726         *s-- = '\0';
727
728         switch ( *s ) {
729         case '<':
730                 ftype = LDAP_FILTER_LE;
731                 *s = '\0';
732                 if(! ldap_is_attr_desc( str ) ) goto done;
733                 break;
734
735         case '>':
736                 ftype = LDAP_FILTER_GE;
737                 *s = '\0';
738                 if(! ldap_is_attr_desc( str ) ) goto done;
739                 break;
740
741         case '~':
742                 ftype = LDAP_FILTER_APPROX;
743                 *s = '\0';
744                 if(! ldap_is_attr_desc( str ) ) goto done;
745                 break;
746
747         case ':':
748                 /* RFC2254 extensible filters are off the form:
749                  *              type [:dn] [:rule] := value
750                  * or   [:dn]:rule := value             
751                  */
752                 ftype = LDAP_FILTER_EXT;
753                 *s = '\0';
754
755                 {
756                         char *dn = strchr( str, ':' );
757                         char *rule = NULL;
758
759                         if( dn == NULL ) {
760                                 if(! ldap_is_attr_desc( str ) ) goto done;
761                                 break;
762                         }
763
764                         *dn++ = '\0';
765                         rule = strchr( dn, ':' );
766
767                         if( rule == NULL ) {
768                                 /* one colon */
769                                 if ( strcmp(dn, "dn") == 0 ) {
770                                         /* must have attribute */
771                                         if( !ldap_is_attr_desc( str ) ) {
772                                                 goto done;
773                                         }
774
775                                         rule = "";
776
777                                 } else {
778                                         rule = dn;
779                                         dn = NULL;
780                                 }
781                                 
782                         } else {
783                                 /* two colons */
784                                 *rule++ = '\0';
785
786                                 if ( strcmp(dn, "dn") != 0 ) {
787                                         /* must have "dn" */
788                                         goto done;
789                                 }
790                         }
791
792                         if ( *str == '\0' && *rule == '\0' ) {
793                                 /* must have either type or rule */
794                                 goto done;
795                         }
796
797                         if ( *str != '\0' && !ldap_is_attr_desc( str ) ) {
798                                 goto done;
799                         }
800
801                         if ( *rule != '\0' && !ldap_is_attr_oid( rule ) ) {
802                                 goto done;
803                         }
804
805                         rc = ber_printf( ber, "t{" /*}*/, ftype );
806
807                         if( rc != -1 && *rule != '\0' ) {
808                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
809                         }
810                         if( rc != -1 && *str != '\0' ) {
811                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
812                         }
813
814                         if( rc != -1 ) {
815                                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
816
817                                 if( len >= 0 ) {
818                                         rc = ber_printf( ber, "totb}",
819                                                 LDAP_FILTER_EXT_VALUE, value, len,
820                                                 LDAP_FILTER_EXT_DNATTRS, dn != NULL);
821                                 } else {
822                                         rc = -1;
823                                 }
824                         }
825                 }
826                 break;
827
828         default:
829                 if ( ldap_pvt_find_wildcard( value ) == NULL ) {
830                         ftype = LDAP_FILTER_EQUALITY;
831                 } else if ( strcmp( value, "*" ) == 0 ) {
832                         ftype = LDAP_FILTER_PRESENT;
833                 } else {
834                         rc = put_substring_filter( ber, str, value );
835                         goto done;
836                 }
837                 break;
838         }
839
840         if ( ftype == LDAP_FILTER_PRESENT ) {
841                 rc = ber_printf( ber, "ts", ftype, str );
842
843         } else {
844                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
845
846                 if( len >= 0 ) {
847                         rc = ber_printf( ber, "t{so}",
848                                 ftype, str, value, len );
849                 }
850         }
851
852         if( rc != -1 ) rc = 0;
853
854 done:
855         LDAP_FREE( str );
856         return rc;
857 }
858
859 static int
860 put_substring_filter( BerElement *ber, char *type, char *val )
861 {
862         char            *nextstar, gotstar = 0;
863         ber_tag_t       ftype = LDAP_FILTER_SUBSTRINGS;
864
865         Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
866             val, 0 );
867
868         if ( ber_printf( ber, "t{s{", ftype, type ) == -1 )
869                 return( -1 );
870
871         for( ; val != NULL; val=nextstar ) {
872                 if ( (nextstar = ldap_pvt_find_wildcard( val )) != NULL )
873                         *nextstar++ = '\0';
874
875                 if ( gotstar == 0 ) {
876                         ftype = LDAP_SUBSTRING_INITIAL;
877                 } else if ( nextstar == NULL ) {
878                         ftype = LDAP_SUBSTRING_FINAL;
879                 } else {
880                         ftype = LDAP_SUBSTRING_ANY;
881                 }
882
883                 if ( *val != '\0' ) {
884                         ber_slen_t len = ldap_pvt_filter_value_unescape( val );
885
886                         if ( len < 0  ) {
887                                 return -1;
888                         }
889
890                         if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) {
891                                 return( -1 );
892                         }
893                 }
894
895                 gotstar = 1;
896         }
897
898         if ( ber_printf( ber, /* {{ */ "}}" ) == -1 )
899                 return( -1 );
900
901         return( 0 );
902 }
903
904 int
905 ldap_search_st(
906         LDAP *ld, LDAP_CONST char *base, int scope,
907         LDAP_CONST char *filter, char **attrs,
908         int attrsonly, struct timeval *timeout, LDAPMessage **res )
909 {
910         int     msgid;
911
912         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
913             == -1 )
914                 return( ld->ld_errno );
915
916         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
917                 return( ld->ld_errno );
918
919         if ( ld->ld_errno == LDAP_TIMEOUT ) {
920                 (void) ldap_abandon( ld, msgid );
921                 ld->ld_errno = LDAP_TIMEOUT;
922                 return( ld->ld_errno );
923         }
924
925         return( ldap_result2error( ld, *res, 0 ) );
926 }
927
928 int
929 ldap_search_s(
930         LDAP *ld,
931         LDAP_CONST char *base,
932         int scope,
933         LDAP_CONST char *filter,
934         char **attrs,
935         int attrsonly,
936         LDAPMessage **res )
937 {
938         int     msgid;
939
940         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
941             == -1 )
942                 return( ld->ld_errno );
943
944         if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, res ) == -1 )
945                 return( ld->ld_errno );
946
947         return( ldap_result2error( ld, *res, 0 ) );
948 }
949