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