1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is Mozilla Communicator client code, released
15 * March 31, 1998.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-1999
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37 /*
38 * Copyright (c) 1990 Regents of the University of Michigan.
39 * All rights reserved.
40 */
41 /*
42 * search.c
43 */
44
45 #if 0
46 #ifndef lint
47 static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
48 #endif
49 #endif
50
51 #include "ldap-int.h"
52
53 static int nsldapi_timeval2ldaplimit( struct timeval *timeoutp,
54 int defaultvalue );
55 static int nsldapi_search( LDAP *ld, const char *base, int scope,
56 const char *filter, char **attrs, int attrsonly,
57 LDAPControl **serverctrls, LDAPControl **clientctrls,
58 int timelimit, int sizelimit, int *msgidp );
59 static char *find_right_paren( char *s );
60 static char *put_complex_filter( BerElement *ber, char *str,
61 unsigned long tag, int not );
62 static int put_filter( BerElement *ber, char *str );
63 static int unescape_filterval( char *str );
64 static int hexchar2int( char c );
65 static int is_valid_attr( char *a );
66 static int put_simple_filter( BerElement *ber, char *str );
67 static int put_substring_filter( BerElement *ber, char *type,
68 char *str );
69 static int put_filter_list( BerElement *ber, char *str );
70 static int nsldapi_search_s( LDAP *ld, const char *base, int scope,
71 const char *filter, char **attrs, int attrsonly,
72 LDAPControl **serverctrls, LDAPControl **clientctrls,
73 struct timeval *localtimeoutp, int timelimit, int sizelimit,
74 LDAPMessage **res );
75
76 /*
77 * ldap_search - initiate an ldap search operation. Parameters:
78 *
79 * ld LDAP descriptor
80 * base DN of the base object
81 * scope the search scope - one of LDAP_SCOPE_BASE,
82 * LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
83 * filter a string containing the search filter
84 * (e.g., "(|(cn=bob)(sn=bob))")
85 * attrs list of attribute types to return for matches
86 * attrsonly 1 => attributes only 0 => attributes and values
87 *
88 * Example:
89 * char *attrs[] = { "mail", "title", 0 };
90 * msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
91 * attrs, attrsonly );
92 */
93 int
94 LDAP_CALL
ldap_search(LDAP * ld,const char * base,int scope,const char * filter,char ** attrs,int attrsonly)95 ldap_search(
96 LDAP *ld,
97 const char *base,
98 int scope,
99 const char *filter,
100 char **attrs,
101 int attrsonly
102 )
103 {
104 int msgid;
105
106 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 );
107
108 if ( ldap_search_ext( ld, base, scope, filter, attrs, attrsonly, NULL,
109 NULL, NULL, -1, &msgid ) == LDAP_SUCCESS ) {
110 return( msgid );
111 } else {
112 return( -1 ); /* error is in ld handle */
113 }
114 }
115
116
117 /*
118 * LDAPv3 extended search.
119 * Returns an LDAP error code.
120 */
121 int
122 LDAP_CALL
ldap_search_ext(LDAP * ld,const char * base,int scope,const char * filter,char ** attrs,int attrsonly,LDAPControl ** serverctrls,LDAPControl ** clientctrls,struct timeval * timeoutp,int sizelimit,int * msgidp)123 ldap_search_ext(
124 LDAP *ld,
125 const char *base,
126 int scope,
127 const char *filter,
128 char **attrs,
129 int attrsonly,
130 LDAPControl **serverctrls,
131 LDAPControl **clientctrls,
132 struct timeval *timeoutp, /* NULL means use ld->ld_timelimit */
133 int sizelimit,
134 int *msgidp
135 )
136 {
137 /*
138 * It is an error to pass in a zero'd timeval.
139 */
140 if ( timeoutp != NULL && timeoutp->tv_sec == 0 &&
141 timeoutp->tv_usec == 0 ) {
142 if ( ld != NULL ) {
143 LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
144 }
145 return( LDAP_PARAM_ERROR );
146 }
147
148 return( nsldapi_search( ld, base, scope, filter, attrs, attrsonly,
149 serverctrls, clientctrls,
150 nsldapi_timeval2ldaplimit( timeoutp, -1 ), sizelimit, msgidp ));
151 }
152
153
154 /*
155 * Like ldap_search_ext() except an integer timelimit is passed instead of
156 * using the overloaded struct timeval *timeoutp.
157 */
158 static int
nsldapi_search(LDAP * ld,const char * base,int scope,const char * filter,char ** attrs,int attrsonly,LDAPControl ** serverctrls,LDAPControl ** clientctrls,int timelimit,int sizelimit,int * msgidp)159 nsldapi_search(
160 LDAP *ld,
161 const char *base,
162 int scope,
163 const char *filter,
164 char **attrs,
165 int attrsonly,
166 LDAPControl **serverctrls,
167 LDAPControl **clientctrls,
168 int timelimit, /* -1 means use ld->ld_timelimit */
169 int sizelimit, /* -1 means use ld->ld_sizelimit */
170 int *msgidp
171 )
172 {
173 BerElement *ber;
174 int rc, rc_key;
175 unsigned long key; /* XXXmcs: memcache */
176
177 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_search_ext\n", 0, 0, 0 );
178
179 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
180 return( LDAP_PARAM_ERROR );
181 }
182
183 if ( base == NULL ) {
184 base = "";
185 }
186
187 if ( filter == NULL ) {
188 filter = "(objectclass=*)";
189 }
190
191 if ( msgidp == NULL || ( scope != LDAP_SCOPE_BASE
192 && scope != LDAP_SCOPE_ONELEVEL && scope != LDAP_SCOPE_SUBTREE )
193 || ( sizelimit < -1 )) {
194 LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
195 return( LDAP_PARAM_ERROR );
196 }
197 LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
198 *msgidp = ++ld->ld_msgid;
199 LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );
200
201 /*
202 * XXXmcs: should use cache function pointers to hook in memcache
203 */
204 if ( ld->ld_memcache == NULL ) {
205 rc_key = LDAP_NOT_SUPPORTED;
206 } else if (( rc_key = ldap_memcache_createkey( ld, base, scope, filter,
207 attrs, attrsonly, serverctrls, clientctrls, &key)) == LDAP_SUCCESS
208 && ldap_memcache_result( ld, *msgidp, key ) == LDAP_SUCCESS ) {
209 return LDAP_SUCCESS;
210 }
211
212 /* check the cache */
213 if ( ld->ld_cache_on && ld->ld_cache_search != NULL ) {
214 LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK );
215 if ( (rc = (ld->ld_cache_search)( ld, *msgidp, LDAP_REQ_SEARCH,
216 base, scope, filter, attrs, attrsonly )) != 0 ) {
217 *msgidp = rc;
218 LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
219 return( LDAP_SUCCESS );
220 }
221 LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
222 }
223
224 /* caching off or did not find it in the cache - check the net */
225 if (( rc = nsldapi_build_search_req( ld, base, scope, filter, attrs,
226 attrsonly, serverctrls, clientctrls, timelimit, sizelimit,
227 *msgidp, &ber )) != LDAP_SUCCESS ) {
228 return( rc );
229 }
230
231 /* send the message */
232 rc = nsldapi_send_initial_request( ld, *msgidp, LDAP_REQ_SEARCH,
233 (char *) base, ber );
234
235 /*
236 * XXXmcs: should use cache function pointers to hook in memcache
237 */
238 if ( (rc_key == LDAP_SUCCESS) && (rc >= 0) ) {
239 ldap_memcache_new( ld, rc, key, base );
240 }
241
242 *msgidp = rc;
243 return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS );
244 }
245
246
247 /*
248 * Convert a non-NULL timeoutp to a value in seconds that is appropriate to
249 * send in an LDAP search request. If timeoutp is NULL, return defaultvalue.
250 */
251 static int
nsldapi_timeval2ldaplimit(struct timeval * timeoutp,int defaultvalue)252 nsldapi_timeval2ldaplimit( struct timeval *timeoutp, int defaultvalue )
253 {
254 int timelimit;
255
256 if ( NULL == timeoutp ) {
257 timelimit = defaultvalue;
258 } else if ( timeoutp->tv_sec > 0 ) {
259 timelimit = timeoutp->tv_sec;
260 } else if ( timeoutp->tv_usec > 0 ) {
261 timelimit = 1; /* minimum we can express in LDAP */
262 } else {
263 /*
264 * both tv_sec and tv_usec are less than one (zero?) so
265 * to maintain compatiblity with our "zero means no limit"
266 * convention we pass no limit to the server.
267 */
268 timelimit = 0; /* no limit */
269 }
270
271 return( timelimit );
272 }
273
274
275 /* returns an LDAP error code and also sets it in ld */
276 int
nsldapi_build_search_req(LDAP * ld,const char * base,int scope,const char * filter,char ** attrs,int attrsonly,LDAPControl ** serverctrls,LDAPControl ** clientctrls,int timelimit,int sizelimit,int msgid,BerElement ** berp)277 nsldapi_build_search_req(
278 LDAP *ld,
279 const char *base,
280 int scope,
281 const char *filter,
282 char **attrs,
283 int attrsonly,
284 LDAPControl **serverctrls,
285 LDAPControl **clientctrls, /* not used for anything yet */
286 int timelimit, /* if -1, ld->ld_timelimit is used */
287 int sizelimit, /* if -1, ld->ld_sizelimit is used */
288 int msgid,
289 BerElement **berp
290 )
291 {
292 BerElement *ber;
293 int err;
294 char *fdup;
295
296 /*
297 * Create the search request. It looks like this:
298 * SearchRequest := [APPLICATION 3] SEQUENCE {
299 * baseObject DistinguishedName,
300 * scope ENUMERATED {
301 * baseObject (0),
302 * singleLevel (1),
303 * wholeSubtree (2)
304 * },
305 * derefAliases ENUMERATED {
306 * neverDerefaliases (0),
307 * derefInSearching (1),
308 * derefFindingBaseObj (2),
309 * alwaysDerefAliases (3)
310 * },
311 * sizelimit INTEGER (0 .. 65535),
312 * timelimit INTEGER (0 .. 65535),
313 * attrsOnly BOOLEAN,
314 * filter Filter,
315 * attributes SEQUENCE OF AttributeType
316 * }
317 * wrapped in an ldap message.
318 */
319
320 /* create a message to send */
321 if (( err = nsldapi_alloc_ber_with_options( ld, &ber ))
322 != LDAP_SUCCESS ) {
323 return( err );
324 }
325
326 if ( base == NULL ) {
327 base = "";
328 }
329
330 if ( sizelimit == -1 ) {
331 sizelimit = ld->ld_sizelimit;
332 }
333
334 if ( timelimit == -1 ) {
335 timelimit = ld->ld_timelimit;
336 }
337
338 #ifdef CLDAP
339 if ( ld->ld_sbp->sb_naddr > 0 ) {
340 err = ber_printf( ber, "{ist{seeiib", msgid,
341 ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
342 sizelimit, timelimit, attrsonly );
343 } else {
344 #endif /* CLDAP */
345 err = ber_printf( ber, "{it{seeiib", msgid,
346 LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
347 sizelimit, timelimit, attrsonly );
348 #ifdef CLDAP
349 }
350 #endif /* CLDAP */
351
352 if ( err == -1 ) {
353 LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL );
354 ber_free( ber, 1 );
355 return( LDAP_ENCODING_ERROR );
356 }
357
358 fdup = nsldapi_strdup( filter );
359 err = put_filter( ber, fdup );
360 NSLDAPI_FREE( fdup );
361
362 if ( err == -1 ) {
363 LDAP_SET_LDERRNO( ld, LDAP_FILTER_ERROR, NULL, NULL );
364 ber_free( ber, 1 );
365 return( LDAP_FILTER_ERROR );
366 }
367
368 if ( ber_printf( ber, "{v}}", attrs ) == -1 ) {
369 LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL );
370 ber_free( ber, 1 );
371 return( LDAP_ENCODING_ERROR );
372 }
373
374 if ( (err = nsldapi_put_controls( ld, serverctrls, 1, ber ))
375 != LDAP_SUCCESS ) {
376 ber_free( ber, 1 );
377 return( err );
378 }
379
380 *berp = ber;
381 return( LDAP_SUCCESS );
382 }
383
384 static char *
find_right_paren(char * s)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 char *
put_complex_filter(BerElement * ber,char * str,unsigned long tag,int not)410 put_complex_filter(
411 BerElement *ber,
412 char *str,
413 unsigned long tag,
414 int not
415 )
416 {
417 char *next;
418
419 /*
420 * We have (x(filter)...) with str sitting on
421 * the x. We have to find the paren matching
422 * the one before the x and put the intervening
423 * filters by calling put_filter_list().
424 */
425
426 /* put explicit tag */
427 if ( ber_printf( ber, "t{", tag ) == -1 )
428 return( NULL );
429
430 str++;
431 if ( (next = find_right_paren( str )) == NULL )
432 return( NULL );
433
434 *next = '\0';
435 if ( put_filter_list( ber, str ) == -1 )
436 return( NULL );
437 *next++ = ')';
438
439 /* flush explicit tagged thang */
440 if ( ber_printf( ber, "}" ) == -1 )
441 return( NULL );
442
443 return( next );
444 }
445
446 static int
put_filter(BerElement * ber,char * str)447 put_filter( BerElement *ber, char *str )
448 {
449 char *next;
450 int parens, balance, escape;
451
452 /*
453 * A Filter looks like this:
454 * Filter ::= CHOICE {
455 * and [0] SET OF Filter,
456 * or [1] SET OF Filter,
457 * not [2] Filter,
458 * equalityMatch [3] AttributeValueAssertion,
459 * substrings [4] SubstringFilter,
460 * greaterOrEqual [5] AttributeValueAssertion,
461 * lessOrEqual [6] AttributeValueAssertion,
462 * present [7] AttributeType,,
463 * approxMatch [8] AttributeValueAssertion
464 * }
465 *
466 * SubstringFilter ::= SEQUENCE {
467 * type AttributeType,
468 * SEQUENCE OF CHOICE {
469 * initial [0] IA5String,
470 * any [1] IA5String,
471 * final [2] IA5String
472 * }
473 * }
474 * Note: tags in a choice are always explicit
475 */
476
477 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
478
479 parens = 0;
480 while ( *str ) {
481 switch ( *str ) {
482 case '(':
483 str++;
484 parens++;
485 switch ( *str ) {
486 case '&':
487 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
488 0, 0, 0 );
489
490 if ( (str = put_complex_filter( ber, str,
491 LDAP_FILTER_AND, 0 )) == NULL )
492 return( -1 );
493
494 parens--;
495 break;
496
497 case '|':
498 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
499 0, 0, 0 );
500
501 if ( (str = put_complex_filter( ber, str,
502 LDAP_FILTER_OR, 0 )) == NULL )
503 return( -1 );
504
505 parens--;
506 break;
507
508 case '!':
509 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
510 0, 0, 0 );
511
512 if ( (str = put_complex_filter( ber, str,
513 LDAP_FILTER_NOT, 1 )) == NULL )
514 return( -1 );
515
516 parens--;
517 break;
518
519 default:
520 LDAPDebug( LDAP_DEBUG_TRACE,
521 "put_filter: simple\n", 0, 0, 0 );
522
523 balance = 1;
524 escape = 0;
525 next = str;
526 while ( *next && balance ) {
527 if ( escape == 0 ) {
528 if ( *next == '(' )
529 balance++;
530 else if ( *next == ')' )
531 balance--;
532 }
533 if ( *next == '\\' && ! escape )
534 escape = 1;
535 else
536 escape = 0;
537 if ( balance )
538 next++;
539 }
540 if ( balance != 0 )
541 return( -1 );
542
543 *next = '\0';
544 if ( put_simple_filter( ber, str ) == -1 ) {
545 return( -1 );
546 }
547 *next++ = ')';
548 str = next;
549 parens--;
550 break;
551 }
552 break;
553
554 case ')':
555 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
556 0 );
557 if ( ber_printf( ber, "]" ) == -1 )
558 return( -1 );
559 str++;
560 parens--;
561 break;
562
563 case ' ':
564 str++;
565 break;
566
567 default: /* assume it's a simple type=value filter */
568 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
569 0 );
570 next = strchr( str, '\0' );
571 if ( put_simple_filter( ber, str ) == -1 ) {
572 return( -1 );
573 }
574 str = next;
575 break;
576 }
577 }
578
579 return( parens ? -1 : 0 );
580 }
581
582
583 /*
584 * Put a list of filters like this "(filter1)(filter2)..."
585 */
586
587 static int
put_filter_list(BerElement * ber,char * str)588 put_filter_list( BerElement *ber, char *str )
589 {
590 char *next;
591 char save;
592
593 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
594
595 while ( *str ) {
596 while ( *str && isspace( *str ) )
597 str++;
598 if ( *str == '\0' )
599 break;
600
601 if ( (next = find_right_paren( str + 1 )) == NULL )
602 return( -1 );
603 save = *++next;
604
605 /* now we have "(filter)" with str pointing to it */
606 *next = '\0';
607 if ( put_filter( ber, str ) == -1 )
608 return( -1 );
609 *next = save;
610
611 str = next;
612 }
613
614 return( 0 );
615 }
616
617
618 /*
619 * is_valid_attr - returns 1 if a is a syntactically valid left-hand side
620 * of a filter expression, 0 otherwise. A valid string may contain only
621 * letters, numbers, hyphens, semi-colons, colons and periods. examples:
622 * cn
623 * cn;lang-fr
624 * 1.2.3.4;binary;dynamic
625 * mail;dynamic
626 * cn:dn:1.2.3.4
627 *
628 * For compatibility with older servers, we also allow underscores in
629 * attribute types, even through they are not allowed by the LDAPv3 RFCs.
630 */
631 static int
is_valid_attr(char * a)632 is_valid_attr( char *a )
633 {
634 for ( ; *a; a++ ) {
635 if ( !isascii( *a ) ) {
636 return( 0 );
637 } else if ( !isalnum( *a ) ) {
638 switch ( *a ) {
639 case '-':
640 case '.':
641 case ';':
642 case ':':
643 case '_':
644 break; /* valid */
645 default:
646 return( 0 );
647 }
648 }
649 }
650
651 return( 1 );
652 }
653
654 static char *
find_star(char * s)655 find_star( char *s )
656 {
657 for ( ; *s; ++s ) {
658 switch ( *s ) {
659 case '*': return s;
660 case '\\':
661 ++s;
662 if ( hexchar2int(s[0]) >= 0 && hexchar2int(s[1]) >= 0 ) ++s;
663 default: break;
664 }
665 }
666 return NULL;
667 }
668
669 static int
put_simple_filter(BerElement * ber,char * str)670 put_simple_filter( BerElement *ber, char *str )
671 {
672 char *s, *s2, *s3, filterop;
673 char *value;
674 unsigned long ftype;
675 int rc, len;
676 char *oid; /* for v3 extended filter */
677 int dnattr; /* for v3 extended filter */
678
679 LDAPDebug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
680
681 rc = -1; /* pessimistic */
682
683 if (( str = nsldapi_strdup( str )) == NULL ) {
684 return( rc );
685 }
686
687 if ( (s = strchr( str, '=' )) == NULL ) {
688 goto free_and_return;
689 }
690 value = s + 1;
691 *s-- = '\0';
692 filterop = *s;
693 if ( filterop == '<' || filterop == '>' || filterop == '~' ||
694 filterop == ':' ) {
695 *s = '\0';
696 }
697
698 if ( ! is_valid_attr( str ) ) {
699 goto free_and_return;
700 }
701
702 switch ( filterop ) {
703 case '<':
704 ftype = LDAP_FILTER_LE;
705 break;
706 case '>':
707 ftype = LDAP_FILTER_GE;
708 break;
709 case '~':
710 ftype = LDAP_FILTER_APPROX;
711 break;
712 case ':': /* extended filter - v3 only */
713 /*
714 * extended filter looks like this:
715 *
716 * [type][':dn'][':'oid]':='value
717 *
718 * where one of type or :oid is required.
719 *
720 */
721 ftype = LDAP_FILTER_EXTENDED;
722 s2 = s3 = NULL;
723 if ( (s2 = strrchr( str, ':' )) == NULL ) {
724 goto free_and_return;
725 }
726 if ( strcasecmp( s2, ":dn" ) == 0 ) {
727 oid = NULL;
728 dnattr = 1;
729 *s2 = '\0';
730 } else {
731 oid = s2 + 1;
732 dnattr = 0;
733 *s2 = '\0';
734 if ( (s3 = strrchr( str, ':' )) != NULL ) {
735 if ( strcasecmp( s3, ":dn" ) == 0 ) {
736 dnattr = 1;
737 } else {
738 goto free_and_return;
739 }
740 *s3 = '\0';
741 }
742 }
743 if ( (rc = ber_printf( ber, "t{", ftype )) == -1 ) {
744 goto free_and_return;
745 }
746 if ( oid != NULL ) {
747 if ( (rc = ber_printf( ber, "ts", LDAP_TAG_MRA_OID,
748 oid )) == -1 ) {
749 goto free_and_return;
750 }
751 }
752 if ( *str != '\0' ) {
753 if ( (rc = ber_printf( ber, "ts",
754 LDAP_TAG_MRA_TYPE, str )) == -1 ) {
755 goto free_and_return;
756 }
757 }
758 if (( len = unescape_filterval( value )) < 0 ||
759 ( rc = ber_printf( ber, "totb}", LDAP_TAG_MRA_VALUE,
760 value, len, LDAP_TAG_MRA_DNATTRS, dnattr )) == -1 ) {
761 goto free_and_return;
762 }
763 rc = 0;
764 goto free_and_return;
765 break;
766 default:
767 if ( find_star( value ) == NULL ) {
768 ftype = LDAP_FILTER_EQUALITY;
769 } else if ( strcmp( value, "*" ) == 0 ) {
770 ftype = LDAP_FILTER_PRESENT;
771 } else {
772 rc = put_substring_filter( ber, str, value );
773 goto free_and_return;
774 }
775 break;
776 }
777
778 if ( ftype == LDAP_FILTER_PRESENT ) {
779 rc = ber_printf( ber, "ts", ftype, str );
780 } else if (( len = unescape_filterval( value )) >= 0 ) {
781 rc = ber_printf( ber, "t{so}", ftype, str, value, len );
782 }
783 if ( rc != -1 ) {
784 rc = 0;
785 }
786
787 free_and_return:
788 NSLDAPI_FREE( str );
789 return( rc );
790 }
791
792
793 /*
794 * Undo in place both LDAPv2 (RFC-1960) and LDAPv3 (hexadecimal) escape
795 * sequences within the null-terminated string 'val'. The resulting value
796 * may contain null characters.
797 *
798 * If 'val' contains invalid escape sequences we return -1.
799 * Otherwise the length of the unescaped value is returned.
800 */
801 static int
unescape_filterval(char * val)802 unescape_filterval( char *val )
803 {
804 int escape, firstdigit, ival;
805 char *s, *d;
806
807 escape = firstdigit = 0;
808 for ( s = d = val; *s; s++ ) {
809 if ( escape ) {
810 /*
811 * first try LDAPv3 escape (hexadecimal) sequence
812 */
813 if (( ival = hexchar2int( *s )) < 0 ) {
814 if ( firstdigit ) {
815 /*
816 * LDAPv2 (RFC1960) escape sequence
817 */
818 *d++ = *s;
819 escape = 0;
820 } else {
821 return(-1);
822 }
823 }
824 if ( firstdigit ) {
825 *d = ( ival<<4 );
826 firstdigit = 0;
827 } else {
828 *d++ |= ival;
829 escape = 0;
830 }
831
832 } else if ( *s != '\\' ) {
833 *d++ = *s;
834 escape = 0;
835
836 } else {
837 escape = 1;
838 firstdigit = 1;
839 }
840 }
841
842 return( d - val );
843 }
844
845
846 /*
847 * convert character 'c' that represents a hexadecimal digit to an integer.
848 * if 'c' is not a hexidecimal digit [0-9A-Fa-f], -1 is returned.
849 * otherwise the converted value is returned.
850 */
851 static int
hexchar2int(char c)852 hexchar2int( char c )
853 {
854 if ( c >= '0' && c <= '9' ) {
855 return( c - '0' );
856 }
857 if ( c >= 'A' && c <= 'F' ) {
858 return( c - 'A' + 10 );
859 }
860 if ( c >= 'a' && c <= 'f' ) {
861 return( c - 'a' + 10 );
862 }
863 return( -1 );
864 }
865
866 static int
put_substring_filter(BerElement * ber,char * type,char * val)867 put_substring_filter( BerElement *ber, char *type, char *val )
868 {
869 char *nextstar, gotstar = 0;
870 unsigned long ftype;
871 int len;
872
873 LDAPDebug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
874 val, 0 );
875
876 if ( ber_printf( ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type ) == -1 ) {
877 return( -1 );
878 }
879
880 for ( ; val != NULL; val = nextstar ) {
881 if ( (nextstar = find_star( val )) != NULL ) {
882 *nextstar++ = '\0';
883 }
884
885 if ( gotstar == 0 ) {
886 ftype = LDAP_SUBSTRING_INITIAL;
887 } else if ( nextstar == NULL ) {
888 ftype = LDAP_SUBSTRING_FINAL;
889 } else {
890 ftype = LDAP_SUBSTRING_ANY;
891 }
892 if ( *val != '\0' ) {
893 if (( len = unescape_filterval( val )) < 0 ||
894 ber_printf( ber, "to", ftype, val, len ) == -1 ) {
895 return( -1 );
896 }
897 }
898
899 gotstar = 1;
900 }
901
902 if ( ber_printf( ber, "}}" ) == -1 ) {
903 return( -1 );
904 }
905
906 return( 0 );
907 }
908
909 int
910 LDAP_CALL
ldap_search_st(LDAP * ld,const char * base,int scope,const char * filter,char ** attrs,int attrsonly,struct timeval * timeout,LDAPMessage ** res)911 ldap_search_st(
912 LDAP *ld,
913 const char *base,
914 int scope,
915 const char *filter,
916 char **attrs,
917 int attrsonly,
918 struct timeval *timeout,
919 LDAPMessage **res
920 )
921 {
922 return( nsldapi_search_s( ld, base, scope, filter, attrs, attrsonly,
923 NULL, NULL, timeout, -1, -1, res ));
924 }
925
926 int
927 LDAP_CALL
ldap_search_s(LDAP * ld,const char * base,int scope,const char * filter,char ** attrs,int attrsonly,LDAPMessage ** res)928 ldap_search_s(
929 LDAP *ld,
930 const char *base,
931 int scope,
932 const char *filter,
933 char **attrs,
934 int attrsonly,
935 LDAPMessage **res
936 )
937 {
938 return( nsldapi_search_s( ld, base, scope, filter, attrs, attrsonly,
939 NULL, NULL, NULL, -1, -1, res ));
940 }
941
942 int LDAP_CALL
ldap_search_ext_s(LDAP * ld,const char * base,int scope,const char * filter,char ** attrs,int attrsonly,LDAPControl ** serverctrls,LDAPControl ** clientctrls,struct timeval * timeoutp,int sizelimit,LDAPMessage ** res)943 ldap_search_ext_s(
944 LDAP *ld,
945 const char *base,
946 int scope,
947 const char *filter,
948 char **attrs,
949 int attrsonly,
950 LDAPControl **serverctrls,
951 LDAPControl **clientctrls,
952 struct timeval *timeoutp,
953 int sizelimit,
954 LDAPMessage **res
955 )
956 {
957 return( nsldapi_search_s( ld, base, scope, filter, attrs, attrsonly,
958 serverctrls, clientctrls, timeoutp,
959 nsldapi_timeval2ldaplimit( timeoutp, -1 ), sizelimit, res ));
960 }
961
962
963 static int
nsldapi_search_s(LDAP * ld,const char * base,int scope,const char * filter,char ** attrs,int attrsonly,LDAPControl ** serverctrls,LDAPControl ** clientctrls,struct timeval * localtimeoutp,int timelimit,int sizelimit,LDAPMessage ** res)964 nsldapi_search_s(
965 LDAP *ld,
966 const char *base,
967 int scope,
968 const char *filter,
969 char **attrs,
970 int attrsonly,
971 LDAPControl **serverctrls,
972 LDAPControl **clientctrls,
973 struct timeval *localtimeoutp,
974 int timelimit, /* -1 means use ld->ld_timelimit */
975 int sizelimit, /* -1 means use ld->ld_sizelimit */
976 LDAPMessage **res
977 )
978 {
979 int err, msgid;
980
981 /*
982 * It is an error to pass in a zero'd timeval.
983 */
984 if ( localtimeoutp != NULL && localtimeoutp->tv_sec == 0 &&
985 localtimeoutp->tv_usec == 0 ) {
986 if ( ld != NULL ) {
987 LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
988 }
989 if ( res != NULL ) {
990 *res = NULL;
991 }
992 return( LDAP_PARAM_ERROR );
993 }
994
995 if (( err = nsldapi_search( ld, base, scope, filter, attrs, attrsonly,
996 serverctrls, clientctrls, timelimit, sizelimit, &msgid ))
997 != LDAP_SUCCESS ) {
998 if ( res != NULL ) {
999 *res = NULL;
1000 }
1001 return( err );
1002 }
1003
1004 if ( ldap_result( ld, msgid, 1, localtimeoutp, res ) == -1 ) {
1005 /*
1006 * Error. ldap_result() sets *res to NULL for us.
1007 */
1008 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
1009 }
1010
1011 if ( LDAP_GET_LDERRNO( ld, NULL, NULL ) == LDAP_TIMEOUT ) {
1012 (void) ldap_abandon( ld, msgid );
1013 err = LDAP_TIMEOUT;
1014 LDAP_SET_LDERRNO( ld, err, NULL, NULL );
1015 if ( res != NULL ) {
1016 *res = NULL;
1017 }
1018 return( err );
1019 }
1020
1021 return( ldap_result2error( ld, *res, 0 ) );
1022 }
1023