]> git.sur5r.net Git - openldap/blob - libraries/libldap/search.c
Add LDAP_CONST to kerberos bind routines
[openldap] / libraries / libldap / search.c
1 /*
2  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
3  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
4  */
5 /*  Portions
6  *  Copyright (c) 1990 Regents of the University of Michigan.
7  *  All rights reserved.
8  *
9  *  search.c
10  */
11
12 #include "portable.h"
13
14 #include <stdio.h>
15 #include <stdlib.h>
16
17 #include <ac/ctype.h>
18 #include <ac/socket.h>
19 #include <ac/string.h>
20 #include <ac/time.h>
21
22 #include "ldap-int.h"
23
24 static char *find_right_paren LDAP_P(( char *s ));
25 static char *put_complex_filter LDAP_P(( BerElement *ber, char *str,
26         unsigned long tag, int not ));
27 static int put_filter LDAP_P(( BerElement *ber, char *str ));
28 static int put_simple_filter LDAP_P(( BerElement *ber, char *str ));
29 static int put_substring_filter LDAP_P(( BerElement *ber, char *type, char *str ));
30 static int put_filter_list LDAP_P(( BerElement *ber, char *str ));
31
32 /*
33  * ldap_search - initiate an ldap search operation.
34  *
35  * Parameters:
36  *
37  *      ld              LDAP descriptor
38  *      base            DN of the base object
39  *      scope           the search scope - one of LDAP_SCOPE_BASE,
40  *                          LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
41  *      filter          a string containing the search filter
42  *                      (e.g., "(|(cn=bob)(sn=bob))")
43  *      attrs           list of attribute types to return for matches
44  *      attrsonly       1 => attributes only 0 => attributes and values
45  *
46  * Example:
47  *      char    *attrs[] = { "mail", "title", 0 };
48  *      msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
49  *          attrs, attrsonly );
50  */
51 int
52 ldap_search( LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter,
53         char **attrs, int attrsonly )
54 {
55         BerElement      *ber;
56
57         Debug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 );
58
59         if (( ber = ldap_build_search_req( ld, base, scope, filter, attrs,
60             attrsonly, NULL, NULL )) == NULLBER ) {
61                 return( -1 );
62         }
63
64 #ifndef LDAP_NOCACHE
65         if ( ld->ld_cache != NULL ) {
66                 if ( ldap_check_cache( ld, LDAP_REQ_SEARCH, ber ) == 0 ) {
67                         ber_free( ber, 1 );
68                         ld->ld_errno = LDAP_SUCCESS;
69                         return( ld->ld_msgid );
70                 }
71                 ldap_add_request_to_cache( ld, LDAP_REQ_SEARCH, ber );
72         }
73 #endif /* LDAP_NOCACHE */
74
75         /* send the message */
76         return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber ));
77 }
78
79
80 BerElement *
81 ldap_build_search_req(
82         LDAP *ld,
83         LDAP_CONST char *base_in,
84         int scope,
85         LDAP_CONST char *filter_in,
86         char **attrs,
87         int attrsonly,
88         LDAPControl **sctrls,
89         LDAPControl **cctrls )
90 {
91         BerElement      *ber;
92         int             err;
93         char    *base;
94         char    *filter;
95
96         /*
97          * Create the search request.  It looks like this:
98          *      SearchRequest := [APPLICATION 3] SEQUENCE {
99          *              baseObject      DistinguishedName,
100          *              scope           ENUMERATED {
101          *                      baseObject      (0),
102          *                      singleLevel     (1),
103          *                      wholeSubtree    (2)
104          *              },
105          *              derefAliases    ENUMERATED {
106          *                      neverDerefaliases       (0),
107          *                      derefInSearching        (1),
108          *                      derefFindingBaseObj     (2),
109          *                      alwaysDerefAliases      (3)
110          *              },
111          *              sizelimit       INTEGER (0 .. 65535),
112          *              timelimit       INTEGER (0 .. 65535),
113          *              attrsOnly       BOOLEAN,
114          *              filter          Filter,
115          *              attributes      SEQUENCE OF AttributeType
116          *      }
117          * wrapped in an ldap message.
118          */
119
120         /* create a message to send */
121         if ( (ber = ldap_alloc_ber_with_options( ld )) == NULLBER ) {
122                 return( NULLBER );
123         }
124
125         if ( base_in == NULL ) {
126                 /* no base provided, use session default base */
127                 base = ld->ld_options.ldo_defbase;
128         } else {
129                 base = (char *) base_in;
130         }
131
132         if ( base == NULL ) {
133                 /* no session default base, use top */
134             base = "";
135         }
136
137 #ifdef LDAP_CONNECTIONLESS
138         if ( ld->ld_cldapnaddr > 0 ) {
139             err = ber_printf( ber, "{ist{seeiib", ++ld->ld_msgid,
140                 ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
141                 ld->ld_sizelimit, ld->ld_timelimit, attrsonly );
142         } else {
143 #endif /* LDAP_CONNECTIONLESS */
144                 err = ber_printf( ber, "{it{seeiib", ++ld->ld_msgid,
145                     LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
146                     ld->ld_sizelimit, ld->ld_timelimit, attrsonly );
147 #ifdef LDAP_CONNECTIONLESS
148         }
149 #endif /* LDAP_CONNECTIONLESS */
150
151         if ( err == -1 ) {
152                 ld->ld_errno = LDAP_ENCODING_ERROR;
153                 ber_free( ber, 1 );
154                 return( NULLBER );
155         }
156
157         filter = strdup( filter_in );
158         err = put_filter( ber, filter );
159         free( filter );
160
161         if ( err  == -1 ) {
162                 ld->ld_errno = LDAP_FILTER_ERROR;
163                 ber_free( ber, 1 );
164                 return( NULLBER );
165         }
166
167         if ( ber_printf( ber, "{v}}", attrs ) == -1 ) {
168                 ld->ld_errno = LDAP_ENCODING_ERROR;
169                 ber_free( ber, 1 );
170                 return( NULLBER );
171         }
172
173         /* Put Server Controls */
174         if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
175                 ber_free( ber, 1 );
176                 return( NULLBER );
177         }
178
179         if ( ber_printf( ber, "}", attrs ) == -1 ) {
180                 ld->ld_errno = LDAP_ENCODING_ERROR;
181                 ber_free( ber, 1 );
182                 return( NULLBER );
183         }
184
185         return( ber );
186 }
187
188 static char *
189 find_right_paren( char *s )
190 {
191         int     balance, escape;
192
193         balance = 1;
194         escape = 0;
195         while ( *s && balance ) {
196                 if ( escape == 0 ) {
197                         if ( *s == '(' )
198                                 balance++;
199                         else if ( *s == ')' )
200                                 balance--;
201                 }
202                 if ( *s == '\\' && ! escape )
203                         escape = 1;
204                 else
205                         escape = 0;
206                 if ( balance )
207                         s++;
208         }
209
210         return( *s ? s : NULL );
211 }
212
213 static char *
214 put_complex_filter( BerElement *ber, char *str, unsigned long tag, int not )
215 {
216         char    *next;
217
218         /*
219          * We have (x(filter)...) with str sitting on
220          * the x.  We have to find the paren matching
221          * the one before the x and put the intervening
222          * filters by calling put_filter_list().
223          */
224
225         /* put explicit tag */
226         if ( ber_printf( ber, "t{", tag ) == -1 )
227                 return( NULL );
228 /*
229         if ( !not && ber_printf( ber, "{" ) == -1 )
230                 return( NULL );
231 */
232
233         str++;
234         if ( (next = find_right_paren( str )) == NULL )
235                 return( NULL );
236
237         *next = '\0';
238         if ( put_filter_list( ber, str ) == -1 )
239                 return( NULL );
240         *next++ = ')';
241
242         /* flush explicit tagged thang */
243         if ( ber_printf( ber, "}" ) == -1 )
244                 return( NULL );
245 /*
246         if ( !not && ber_printf( ber, "}" ) == -1 )
247                 return( NULL );
248 */
249
250         return( next );
251 }
252
253 static int
254 put_filter( BerElement *ber, char *str )
255 {
256         char    *next, *tmp, *s, *d;
257         int     parens, balance, escape, gotescape;
258
259         /*
260          * A Filter looks like this:
261          *      Filter ::= CHOICE {
262          *              and             [0]     SET OF Filter,
263          *              or              [1]     SET OF Filter,
264          *              not             [2]     Filter,
265          *              equalityMatch   [3]     AttributeValueAssertion,
266          *              substrings      [4]     SubstringFilter,
267          *              greaterOrEqual  [5]     AttributeValueAssertion,
268          *              lessOrEqual     [6]     AttributeValueAssertion,
269          *              present         [7]     AttributeType,,
270          *              approxMatch     [8]     AttributeValueAssertion
271          *      }
272          *
273          *      SubstringFilter ::= SEQUENCE {
274          *              type               AttributeType,
275          *              SEQUENCE OF CHOICE {
276          *                      initial          [0] IA5String,
277          *                      any              [1] IA5String,
278          *                      final            [2] IA5String
279          *              }
280          *      }
281          * Note: tags in a choice are always explicit
282          */
283
284         Debug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
285
286         gotescape = parens = 0;
287         while ( *str ) {
288                 switch ( *str ) {
289                 case '(':
290                         str++;
291                         parens++;
292                         switch ( *str ) {
293                         case '&':
294                                 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
295                                     0, 0, 0 );
296
297                                 if ( (str = put_complex_filter( ber, str,
298                                     LDAP_FILTER_AND, 0 )) == NULL )
299                                         return( -1 );
300
301                                 parens--;
302                                 break;
303
304                         case '|':
305                                 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
306                                     0, 0, 0 );
307
308                                 if ( (str = put_complex_filter( ber, str,
309                                     LDAP_FILTER_OR, 0 )) == NULL )
310                                         return( -1 );
311
312                                 parens--;
313                                 break;
314
315                         case '!':
316                                 Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
317                                     0, 0, 0 );
318
319                                 if ( (str = put_complex_filter( ber, str,
320                                     LDAP_FILTER_NOT, 1 )) == NULL )
321                                         return( -1 );
322
323                                 parens--;
324                                 break;
325
326                         default:
327                                 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
328                                     0, 0, 0 );
329
330                                 balance = 1;
331                                 escape = 0;
332                                 next = str;
333                                 while ( *next && balance ) {
334                                         if ( escape == 0 ) {
335                                                 if ( *next == '(' )
336                                                         balance++;
337                                                 else if ( *next == ')' )
338                                                         balance--;
339                                         }
340                                         if ( *next == '\\' && ! escape )
341                                                 gotescape = escape = 1;
342                                         else
343                                                 escape = 0;
344                                         if ( balance )
345                                                 next++;
346                                 }
347                                 if ( balance != 0 )
348                                         return( -1 );
349
350                                 *next = '\0';
351                                 tmp = strdup( str );
352                                 if ( gotescape ) {
353                                         escape = 0;
354                                         for ( s = d = tmp; *s; s++ ) {
355                                                 if ( *s != '\\' || escape ) {
356                                                         *d++ = *s;
357                                                         escape = 0;
358                                                 } else {
359                                                         escape = 1;
360                                                 }
361                                         }
362                                         *d = '\0';
363                                 }
364                                 if ( put_simple_filter( ber, tmp ) == -1 ) {
365                                         free( tmp );
366                                         return( -1 );
367                                 }
368                                 free( tmp );
369                                 *next++ = ')';
370                                 str = next;
371                                 parens--;
372                                 break;
373                         }
374                         break;
375
376                 case ')':
377                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
378                             0 );
379                         if ( ber_printf( ber, "]" ) == -1 )
380                                 return( -1 );
381                         str++;
382                         parens--;
383                         break;
384
385                 case ' ':
386                         str++;
387                         break;
388
389                 default:        /* assume it's a simple type=value filter */
390                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
391                             0 );
392                         next = strchr( str, '\0' );
393                         tmp = strdup( str );
394                         if ( strchr( tmp, '\\' ) != NULL ) {
395                                 escape = 0;
396                                 for ( s = d = tmp; *s; s++ ) {
397                                         if ( *s != '\\' || escape ) {
398                                                 *d++ = *s;
399                                                 escape = 0;
400                                         } else {
401                                                 escape = 1;
402                                         }
403                                 }
404                                 *d = '\0';
405                         }
406                         if ( put_simple_filter( ber, tmp ) == -1 ) {
407                                 free( tmp );
408                                 return( -1 );
409                         }
410                         free( tmp );
411                         str = next;
412                         break;
413                 }
414         }
415
416         return( parens ? -1 : 0 );
417 }
418
419 /*
420  * Put a list of filters like this "(filter1)(filter2)..."
421  */
422
423 static int
424 put_filter_list( BerElement *ber, char *str )
425 {
426         char    *next;
427         char    save;
428
429         Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
430
431         while ( *str ) {
432                 while ( *str && isspace( (unsigned char) *str ) )
433                         str++;
434                 if ( *str == '\0' )
435                         break;
436
437                 if ( (next = find_right_paren( str + 1 )) == NULL )
438                         return( -1 );
439                 save = *++next;
440
441                 /* now we have "(filter)" with str pointing to it */
442                 *next = '\0';
443                 if ( put_filter( ber, str ) == -1 )
444                         return( -1 );
445                 *next = save;
446
447                 str = next;
448         }
449
450         return( 0 );
451 }
452
453 static int
454 put_simple_filter( BerElement *ber, char *str )
455 {
456         char            *s;
457         char            *value, savechar;
458         unsigned long   ftype;
459         int             rc;
460
461         Debug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
462
463         if ( (s = strchr( str, '=' )) == NULL )
464                 return( -1 );
465         value = s + 1;
466         *s-- = '\0';
467         savechar = *s;
468
469         switch ( *s ) {
470         case '<':
471                 ftype = LDAP_FILTER_LE;
472                 *s = '\0';
473                 break;
474         case '>':
475                 ftype = LDAP_FILTER_GE;
476                 *s = '\0';
477                 break;
478         case '~':
479                 ftype = LDAP_FILTER_APPROX;
480                 *s = '\0';
481                 break;
482         default:
483                 if ( strchr( value, '*' ) == NULL ) {
484                         ftype = LDAP_FILTER_EQUALITY;
485                 } else if ( strcmp( value, "*" ) == 0 ) {
486                         ftype = LDAP_FILTER_PRESENT;
487                 } else {
488                         rc = put_substring_filter( ber, str, value );
489                         *(value-1) = '=';
490                         return( rc );
491                 }
492                 break;
493         }
494
495         if ( ftype == LDAP_FILTER_PRESENT ) {
496                 rc = ber_printf( ber, "ts", ftype, str );
497         } else {
498                 rc = ber_printf( ber, "t{ss}", ftype, str, value );
499         }
500
501         *s = savechar;
502         *(value-1) = '=';
503         return( rc == -1 ? rc : 0 );
504 }
505
506 static int
507 put_substring_filter( BerElement *ber, char *type, char *val )
508 {
509         char            *nextstar, gotstar = 0;
510         unsigned long   ftype;
511
512         Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
513             val, 0 );
514
515         if ( ber_printf( ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type ) == -1 )
516                 return( -1 );
517
518         while ( val != NULL ) {
519                 if ( (nextstar = strchr( val, '*' )) != NULL )
520                         *nextstar++ = '\0';
521
522                 if ( gotstar == 0 ) {
523                         ftype = LDAP_SUBSTRING_INITIAL;
524                 } else if ( nextstar == NULL ) {
525                         ftype = LDAP_SUBSTRING_FINAL;
526                 } else {
527                         ftype = LDAP_SUBSTRING_ANY;
528                 }
529                 if ( *val != '\0' ) {
530                         if ( ber_printf( ber, "ts", ftype, val ) == -1 )
531                                 return( -1 );
532                 }
533
534                 gotstar = 1;
535                 if ( nextstar != NULL )
536                         *(nextstar-1) = '*';
537                 val = nextstar;
538         }
539
540         if ( ber_printf( ber, "}}" ) == -1 )
541                 return( -1 );
542
543         return( 0 );
544 }
545
546 int
547 ldap_search_st(
548         LDAP *ld, LDAP_CONST char *base, int scope,
549         LDAP_CONST char *filter, char **attrs,
550         int attrsonly, struct timeval *timeout, LDAPMessage **res )
551 {
552         int     msgid;
553
554         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
555             == -1 )
556                 return( ld->ld_errno );
557
558         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
559                 return( ld->ld_errno );
560
561         if ( ld->ld_errno == LDAP_TIMEOUT ) {
562                 (void) ldap_abandon( ld, msgid );
563                 ld->ld_errno = LDAP_TIMEOUT;
564                 return( ld->ld_errno );
565         }
566
567         return( ldap_result2error( ld, *res, 0 ) );
568 }
569
570 int
571 ldap_search_s(
572         LDAP *ld,
573         LDAP_CONST char *base,
574         int scope,
575         LDAP_CONST char *filter,
576         char **attrs,
577         int attrsonly,
578         LDAPMessage **res )
579 {
580         int     msgid;
581
582         if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
583             == -1 )
584                 return( ld->ld_errno );
585
586         if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, res ) == -1 )
587                 return( ld->ld_errno );
588
589         return( ldap_result2error( ld, *res, 0 ) );
590 }
591