1 /* search.c */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2021 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* Portions Copyright (c) 1990 Regents of the University of Michigan.
17  * All rights reserved.
18  */
19 
20 #include "portable.h"
21 
22 #include <stdio.h>
23 
24 #include <ac/stdlib.h>
25 
26 #include <ac/socket.h>
27 #include <ac/string.h>
28 #include <ac/time.h>
29 
30 #include "ldap-int.h"
31 
32 static int put_simple_vrFilter LDAP_P((
33 	BerElement *ber,
34 	char *str ));
35 
36 static int put_vrFilter_list LDAP_P((
37 	BerElement *ber,
38 	char *str ));
39 
40 static char *put_complex_filter LDAP_P((
41 	BerElement *ber,
42 	char *str,
43 	ber_tag_t tag,
44 	int not ));
45 
46 static int put_simple_filter LDAP_P((
47 	BerElement *ber,
48 	char *str ));
49 
50 static int put_substring_filter LDAP_P((
51 	BerElement *ber,
52 	char *type,
53 	char *str,
54 	char *nextstar ));
55 
56 static int put_filter_list LDAP_P((
57 	BerElement *ber,
58 	char *str,
59 	ber_tag_t tag ));
60 
ldap_is_oid(const char * str)61 static int ldap_is_oid ( const char *str )
62 {
63 	int i;
64 
65 	if( LDAP_ALPHA( str[0] )) {
66 		for( i=1; str[i]; i++ ) {
67 			if( !LDAP_LDH( str[i] )) {
68 				return 0;
69 			}
70 		}
71 		return 1;
72 
73 	} else if LDAP_DIGIT( str[0] ) {
74 		int dot=0;
75 		for( i=1; str[i]; i++ ) {
76 			if( LDAP_DIGIT( str[i] )) {
77 				dot=0;
78 
79 			} else if ( str[i] == '.' ) {
80 				if( ++dot > 1 ) return 0;
81 
82 			} else {
83 				return 0;
84 			}
85 		}
86 		return !dot;
87 	}
88 
89 	return 0;
90 }
91 
ldap_is_desc(const char * str)92 static int ldap_is_desc ( const char *str )
93 {
94 	int i;
95 
96 	if( LDAP_ALPHA( str[0] )) {
97 		for( i=1; str[i]; i++ ) {
98 			if( str[i] == ';' ) {
99 				str = &str[i+1];
100 				goto options;
101 			}
102 
103 			if( !LDAP_LDH( str[i] )) {
104 				return 0;
105 			}
106 		}
107 		return 1;
108 
109 	} else if LDAP_DIGIT( str[0] ) {
110 		int dot=0;
111 		for( i=1; str[i]; i++ ) {
112 			if( str[i] == ';' ) {
113 				if( dot ) return 0;
114 				str = &str[i+1];
115 				goto options;
116 			}
117 
118 			if( LDAP_DIGIT( str[i] )) {
119 				dot=0;
120 
121 			} else if ( str[i] == '.' ) {
122 				if( ++dot > 1 ) return 0;
123 
124 			} else {
125 				return 0;
126 			}
127 		}
128 		return !dot;
129 	}
130 
131 	return 0;
132 
133 options:
134 	if( !LDAP_LDH( str[0] )) {
135 		return 0;
136 	}
137 	for( i=1; str[i]; i++ ) {
138 		if( str[i] == ';' ) {
139 			str = &str[i+1];
140 			goto options;
141 		}
142 		if( !LDAP_LDH( str[i] )) {
143 			return 0;
144 		}
145 	}
146 	return 1;
147 }
148 
149 static char *
find_right_paren(char * s)150 find_right_paren( char *s )
151 {
152 	int	balance, escape;
153 
154 	balance = 1;
155 	escape = 0;
156 	while ( *s && balance ) {
157 		if ( !escape ) {
158 			if ( *s == '(' ) {
159 				balance++;
160 			} else if ( *s == ')' ) {
161 				balance--;
162 			}
163 		}
164 
165 		escape = ( *s == '\\' && !escape );
166 
167 		if ( balance ) s++;
168 	}
169 
170 	return *s ? s : NULL;
171 }
172 
hex2value(int c)173 static int hex2value( int c )
174 {
175 	if( c >= '0' && c <= '9' ) {
176 		return c - '0';
177 	}
178 
179 	if( c >= 'A' && c <= 'F' ) {
180 		return c + (10 - (int) 'A');
181 	}
182 
183 	if( c >= 'a' && c <= 'f' ) {
184 		return c + (10 - (int) 'a');
185 	}
186 
187 	return -1;
188 }
189 
190 char *
ldap_pvt_find_wildcard(const char * s)191 ldap_pvt_find_wildcard( const char *s )
192 {
193 	for( ; *s; s++ ) {
194 		switch( *s ) {
195 		case '*':	/* found wildcard */
196 			return (char *) s;
197 
198 		case '(':
199 		case ')':
200 			return NULL;
201 
202 		case '\\':
203 			if( s[1] == '\0' ) return NULL;
204 
205 			if( LDAP_HEX( s[1] ) && LDAP_HEX( s[2] ) ) {
206 				s+=2;
207 
208 			} else switch( s[1] ) {
209 			default:
210 				return NULL;
211 
212 			/* allow RFC 1960 escapes */
213 			case '*':
214 			case '(':
215 			case ')':
216 			case '\\':
217 				s++;
218 			}
219 		}
220 	}
221 
222 	return (char *) s;
223 }
224 
225 /* unescape filter value */
226 /* support both LDAP v2 and v3 escapes */
227 /* output can include nul characters! */
228 ber_slen_t
ldap_pvt_filter_value_unescape(char * fval)229 ldap_pvt_filter_value_unescape( char *fval )
230 {
231 	ber_slen_t r, v;
232 	int v1, v2;
233 
234 	for( r=v=0; fval[v] != '\0'; v++ ) {
235 		switch( fval[v] ) {
236 		case '(':
237 		case ')':
238 		case '*':
239 			return -1;
240 
241 		case '\\':
242 			/* escape */
243 			v++;
244 
245 			if ( fval[v] == '\0' ) {
246 				/* escape at end of string */
247 				return -1;
248 			}
249 
250 			if (( v1 = hex2value( fval[v] )) >= 0 ) {
251 				/* LDAPv3 escape */
252 				if (( v2 = hex2value( fval[v+1] )) < 0 ) {
253 					/* must be two digit code */
254 					return -1;
255 				}
256 
257 				fval[r++] = v1 * 16 + v2;
258 				v++;
259 
260 			} else {
261 				/* LDAPv2 escape */
262 				switch( fval[v] ) {
263 				case '(':
264 				case ')':
265 				case '*':
266 				case '\\':
267 					fval[r++] = fval[v];
268 					break;
269 				default:
270 					/* illegal escape */
271 					return -1;
272 				}
273 			}
274 			break;
275 
276 		default:
277 			fval[r++] = fval[v];
278 		}
279 	}
280 
281 	fval[r] = '\0';
282 	return r;
283 }
284 
285 static char *
put_complex_filter(BerElement * ber,char * str,ber_tag_t tag,int not)286 put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not )
287 {
288 	char	*next;
289 
290 	/*
291 	 * We have (x(filter)...) with str sitting on
292 	 * the x.  We have to find the paren matching
293 	 * the one before the x and put the intervening
294 	 * filters by calling put_filter_list().
295 	 */
296 
297 	/* put explicit tag */
298 	if ( ber_printf( ber, "t{" /*"}"*/, tag ) == -1 ) {
299 		return NULL;
300 	}
301 
302 	str++;
303 	if ( (next = find_right_paren( str )) == NULL ) {
304 		return NULL;
305 	}
306 
307 	*next = '\0';
308 	if ( put_filter_list( ber, str, tag ) == -1 ) {
309 		return NULL;
310 	}
311 
312 	/* close the '(' */
313 	*next++ = ')';
314 
315 	/* flush explicit tagged thang */
316 	if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
317 		return NULL;
318 	}
319 
320 	return next;
321 }
322 
323 int
ldap_pvt_put_filter(BerElement * ber,const char * str_in)324 ldap_pvt_put_filter( BerElement *ber, const char *str_in )
325 {
326 	int rc;
327 	char	*freeme;
328 	char	*str;
329 	char	*next;
330 	int	parens, balance, escape;
331 
332 	/*
333 	 * A Filter looks like this (RFC 4511 as extended by RFC 4526):
334 	 *     Filter ::= CHOICE {
335 	 *         and             [0]     SET SIZE (0..MAX) OF filter Filter,
336 	 *         or              [1]     SET SIZE (0..MAX) OF filter Filter,
337 	 *         not             [2]     Filter,
338 	 *         equalityMatch   [3]     AttributeValueAssertion,
339 	 *         substrings      [4]     SubstringFilter,
340 	 *         greaterOrEqual  [5]     AttributeValueAssertion,
341 	 *         lessOrEqual     [6]     AttributeValueAssertion,
342 	 *         present         [7]     AttributeDescription,
343 	 *         approxMatch     [8]     AttributeValueAssertion,
344 	 *         extensibleMatch [9]     MatchingRuleAssertion,
345 	 *         ... }
346 	 *
347 	 *     SubstringFilter ::= SEQUENCE {
348 	 *         type         AttributeDescription,
349 	 *         substrings   SEQUENCE SIZE (1..MAX) OF substring CHOICE {
350 	 *             initial          [0] AssertionValue, -- only once
351 	 *             any              [1] AssertionValue,
352 	 *             final            [2] AssertionValue  -- only once
353 	 *             }
354 	 *         }
355 	 *
356 	 *	   MatchingRuleAssertion ::= SEQUENCE {
357 	 *         matchingRule    [1] MatchingRuleId OPTIONAL,
358 	 *         type            [2] AttributeDescription OPTIONAL,
359 	 *         matchValue      [3] AssertionValue,
360 	 *         dnAttributes    [4] BOOLEAN DEFAULT FALSE }
361 	 *
362 	 * Note: tags in a CHOICE are always explicit
363 	 */
364 
365 	Debug1( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in );
366 
367 	freeme = LDAP_STRDUP( str_in );
368 	if( freeme == NULL ) return LDAP_NO_MEMORY;
369 	str = freeme;
370 
371 	parens = 0;
372 	while ( *str ) {
373 		switch ( *str ) {
374 		case '(': /*')'*/
375 			str++;
376 			parens++;
377 
378 			/* skip spaces */
379 			while( LDAP_SPACE( *str ) ) str++;
380 
381 			switch ( *str ) {
382 			case '&':
383 				Debug0( LDAP_DEBUG_TRACE, "put_filter: AND\n" );
384 
385 				str = put_complex_filter( ber, str,
386 				    LDAP_FILTER_AND, 0 );
387 				if( str == NULL ) {
388 					rc = -1;
389 					goto done;
390 				}
391 
392 				parens--;
393 				break;
394 
395 			case '|':
396 				Debug0( LDAP_DEBUG_TRACE, "put_filter: OR\n" );
397 
398 				str = put_complex_filter( ber, str,
399 				    LDAP_FILTER_OR, 0 );
400 				if( str == NULL ) {
401 					rc = -1;
402 					goto done;
403 				}
404 
405 				parens--;
406 				break;
407 
408 			case '!':
409 				Debug0( LDAP_DEBUG_TRACE, "put_filter: NOT\n" );
410 
411 				str = put_complex_filter( ber, str,
412 				    LDAP_FILTER_NOT, 0 );
413 				if( str == NULL ) {
414 					rc = -1;
415 					goto done;
416 				}
417 
418 				parens--;
419 				break;
420 
421 			case '(':
422 				rc = -1;
423 				goto done;
424 
425 			default:
426 				Debug0( LDAP_DEBUG_TRACE, "put_filter: simple\n" );
427 
428 				balance = 1;
429 				escape = 0;
430 				next = str;
431 
432 				while ( *next && balance ) {
433 					if ( escape == 0 ) {
434 						if ( *next == '(' ) {
435 							balance++;
436 						} else if ( *next == ')' ) {
437 							balance--;
438 						}
439 					}
440 
441 					if ( *next == '\\' && ! escape ) {
442 						escape = 1;
443 					} else {
444 						escape = 0;
445 					}
446 
447 					if ( balance ) next++;
448 				}
449 
450 				if ( balance != 0 ) {
451 					rc = -1;
452 					goto done;
453 				}
454 
455 				*next = '\0';
456 
457 				if ( put_simple_filter( ber, str ) == -1 ) {
458 					rc = -1;
459 					goto done;
460 				}
461 
462 				*next++ = /*'('*/ ')';
463 
464 				str = next;
465 				parens--;
466 				break;
467 			}
468 			break;
469 
470 		case /*'('*/ ')':
471 			Debug0( LDAP_DEBUG_TRACE, "put_filter: end\n" );
472 			if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
473 				rc = -1;
474 				goto done;
475 			}
476 			str++;
477 			parens--;
478 			break;
479 
480 		case ' ':
481 			str++;
482 			break;
483 
484 		default:	/* assume it's a simple type=value filter */
485 			Debug0( LDAP_DEBUG_TRACE, "put_filter: default\n" );
486 			next = strchr( str, '\0' );
487 			if ( put_simple_filter( ber, str ) == -1 ) {
488 				rc = -1;
489 				goto done;
490 			}
491 			str = next;
492 			break;
493 		}
494 		if ( !parens )
495 			break;
496 	}
497 
498 	rc = ( parens || *str ) ? -1 : 0;
499 
500 done:
501 	LDAP_FREE( freeme );
502 	return rc;
503 }
504 
505 /*
506  * Put a list of filters like this "(filter1)(filter2)..."
507  */
508 
509 static int
put_filter_list(BerElement * ber,char * str,ber_tag_t tag)510 put_filter_list( BerElement *ber, char *str, ber_tag_t tag )
511 {
512 	char	*next = NULL;
513 	char	save;
514 
515 	Debug1( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n",
516 		str );
517 
518 	while ( *str ) {
519 		while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
520 			str++;
521 		}
522 		if ( *str == '\0' ) break;
523 
524 		if ( (next = find_right_paren( str + 1 )) == NULL ) {
525 			return -1;
526 		}
527 		save = *++next;
528 
529 		/* now we have "(filter)" with str pointing to it */
530 		*next = '\0';
531 		if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1;
532 		*next = save;
533 		str = next;
534 
535 		if( tag == LDAP_FILTER_NOT ) break;
536 	}
537 
538 	if( tag == LDAP_FILTER_NOT && ( next == NULL || *str )) {
539 		return -1;
540 	}
541 
542 	return 0;
543 }
544 
545 static int
put_simple_filter(BerElement * ber,char * str)546 put_simple_filter(
547 	BerElement *ber,
548 	char *str )
549 {
550 	char		*s;
551 	char		*value;
552 	ber_tag_t	ftype;
553 	int		rc = -1;
554 
555 	Debug1( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n",
556 		str );
557 
558 	str = LDAP_STRDUP( str );
559 	if( str == NULL ) return -1;
560 
561 	if ( (s = strchr( str, '=' )) == NULL ) {
562 		goto done;
563 	}
564 
565 	value = s + 1;
566 	*s-- = '\0';
567 
568 	switch ( *s ) {
569 	case '<':
570 		ftype = LDAP_FILTER_LE;
571 		*s = '\0';
572 		break;
573 
574 	case '>':
575 		ftype = LDAP_FILTER_GE;
576 		*s = '\0';
577 		break;
578 
579 	case '~':
580 		ftype = LDAP_FILTER_APPROX;
581 		*s = '\0';
582 		break;
583 
584 	case ':':
585 		/* RFC 4515 extensible filters are off the form:
586 		 *		type [:dn] [:rule] := value
587 		 * or	[:dn]:rule := value
588 		 */
589 		ftype = LDAP_FILTER_EXT;
590 		*s = '\0';
591 
592 		{
593 			char *dn = strchr( str, ':' );
594 			char *rule = NULL;
595 
596 			if( dn != NULL ) {
597 				*dn++ = '\0';
598 				rule = strchr( dn, ':' );
599 
600 				if( rule == NULL ) {
601 					/* one colon */
602 					if ( strcasecmp(dn, "dn") == 0 ) {
603 						/* must have attribute */
604 						if( !ldap_is_desc( str ) ) {
605 							goto done;
606 						}
607 
608 						rule = "";
609 
610 					} else {
611 					  rule = dn;
612 					  dn = NULL;
613 					}
614 
615 				} else {
616 					/* two colons */
617 					*rule++ = '\0';
618 
619 					if ( strcasecmp(dn, "dn") != 0 ) {
620 						/* must have "dn" */
621 						goto done;
622 					}
623 				}
624 
625 			}
626 
627 			if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
628 				/* must have either type or rule */
629 				goto done;
630 			}
631 
632 			if ( *str != '\0' && !ldap_is_desc( str ) ) {
633 				goto done;
634 			}
635 
636 			if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
637 				goto done;
638 			}
639 
640 			rc = ber_printf( ber, "t{" /*"}"*/, ftype );
641 
642 			if( rc != -1 && rule && *rule != '\0' ) {
643 				rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
644 			}
645 
646 			if( rc != -1 && *str != '\0' ) {
647 				rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
648 			}
649 
650 			if( rc != -1 ) {
651 				ber_slen_t len = ldap_pvt_filter_value_unescape( value );
652 
653 				if( len >= 0 ) {
654 					rc = ber_printf( ber, "to",
655 						LDAP_FILTER_EXT_VALUE, value, len );
656 				} else {
657 					rc = -1;
658 				}
659 			}
660 
661 			if( rc != -1 && dn ) {
662 				rc = ber_printf( ber, "tb",
663 					LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 );
664 			}
665 
666 			if( rc != -1 ) {
667 				rc = ber_printf( ber, /*"{"*/ "N}" );
668 			}
669 		}
670 		goto done;
671 
672 	default:
673 		if( !ldap_is_desc( str ) ) {
674 			goto done;
675 
676 		} else {
677 			char *nextstar = ldap_pvt_find_wildcard( value );
678 
679 			if ( nextstar == NULL ) {
680 				goto done;
681 
682 			} else if ( *nextstar == '\0' ) {
683 				ftype = LDAP_FILTER_EQUALITY;
684 
685 			} else if ( strcmp( value, "*" ) == 0 ) {
686 				ftype = LDAP_FILTER_PRESENT;
687 
688 			} else {
689 				rc = put_substring_filter( ber, str, value, nextstar );
690 				goto done;
691 			}
692 		} break;
693 	}
694 
695 	if( !ldap_is_desc( str ) ) goto done;
696 
697 	if ( ftype == LDAP_FILTER_PRESENT ) {
698 		rc = ber_printf( ber, "ts", ftype, str );
699 
700 	} else {
701 		ber_slen_t len = ldap_pvt_filter_value_unescape( value );
702 
703 		if( len >= 0 ) {
704 			rc = ber_printf( ber, "t{soN}",
705 				ftype, str, value, len );
706 		}
707 	}
708 
709 done:
710 	if( rc != -1 ) rc = 0;
711 	LDAP_FREE( str );
712 	return rc;
713 }
714 
715 static int
put_substring_filter(BerElement * ber,char * type,char * val,char * nextstar)716 put_substring_filter( BerElement *ber, char *type, char *val, char *nextstar )
717 {
718 	int gotstar = 0;
719 	ber_tag_t	ftype = LDAP_FILTER_SUBSTRINGS;
720 
721 	Debug2( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n",
722 		type, val );
723 
724 	if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) {
725 		return -1;
726 	}
727 
728 	for( ; *val; val=nextstar ) {
729 		if ( gotstar )
730 			nextstar = ldap_pvt_find_wildcard( val );
731 
732 		if ( nextstar == NULL ) {
733 			return -1;
734 		}
735 
736 		if ( *nextstar == '\0' ) {
737 			ftype = LDAP_SUBSTRING_FINAL;
738 		} else {
739 			*nextstar++ = '\0';
740 			if ( gotstar++ == 0 ) {
741 				ftype = LDAP_SUBSTRING_INITIAL;
742 			} else {
743 				ftype = LDAP_SUBSTRING_ANY;
744 			}
745 		}
746 
747 		if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) {
748 			ber_slen_t len = ldap_pvt_filter_value_unescape( val );
749 
750 			if ( len <= 0  ) {
751 				return -1;
752 			}
753 
754 			if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) {
755 				return -1;
756 			}
757 		}
758 	}
759 
760 	if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) {
761 		return -1;
762 	}
763 
764 	return 0;
765 }
766 
767 static int
put_vrFilter(BerElement * ber,const char * str_in)768 put_vrFilter( BerElement *ber, const char *str_in )
769 {
770 	int rc;
771 	char	*freeme;
772 	char	*str;
773 	char	*next;
774 	int	parens, balance, escape;
775 
776 	/*
777 	 * A ValuesReturnFilter looks like this:
778 	 *
779 	 *	ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem
780 	 *      SimpleFilterItem ::= CHOICE {
781 	 *              equalityMatch   [3]     AttributeValueAssertion,
782 	 *              substrings      [4]     SubstringFilter,
783 	 *              greaterOrEqual  [5]     AttributeValueAssertion,
784 	 *              lessOrEqual     [6]     AttributeValueAssertion,
785 	 *              present         [7]     AttributeType,
786 	 *              approxMatch     [8]     AttributeValueAssertion,
787 	 *		extensibleMatch [9]	SimpleMatchingAssertion -- LDAPv3
788 	 *      }
789 	 *
790 	 *      SubstringFilter ::= SEQUENCE {
791 	 *              type               AttributeType,
792 	 *              SEQUENCE OF CHOICE {
793 	 *                      initial          [0] IA5String,
794 	 *                      any              [1] IA5String,
795 	 *                      final            [2] IA5String
796 	 *              }
797 	 *      }
798 	 *
799 	 *	SimpleMatchingAssertion ::= SEQUENCE {	-- LDAPv3
800 	 *		matchingRule    [1] MatchingRuleId OPTIONAL,
801 	 *		type            [2] AttributeDescription OPTIONAL,
802 	 *		matchValue      [3] AssertionValue }
803 	 *
804 	 * (Source: RFC 3876)
805 	 */
806 
807 	Debug1( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in );
808 
809 	freeme = LDAP_STRDUP( str_in );
810 	if( freeme == NULL ) return LDAP_NO_MEMORY;
811 	str = freeme;
812 
813 	parens = 0;
814 	while ( *str ) {
815 		switch ( *str ) {
816 		case '(': /*')'*/
817 			str++;
818 			parens++;
819 
820 			/* skip spaces */
821 			while( LDAP_SPACE( *str ) ) str++;
822 
823 			switch ( *str ) {
824 			case '(':
825 				if ( (next = find_right_paren( str )) == NULL ) {
826 					rc = -1;
827 					goto done;
828 				}
829 
830 				*next = '\0';
831 
832 				if ( put_vrFilter_list( ber, str ) == -1 ) {
833 					rc = -1;
834 					goto done;
835 				}
836 
837 				/* close the '(' */
838 				*next++ = ')';
839 
840 				str = next;
841 
842 				parens--;
843 				break;
844 
845 
846 			default:
847 				Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n" );
848 
849 				balance = 1;
850 				escape = 0;
851 				next = str;
852 
853 				while ( *next && balance ) {
854 					if ( escape == 0 ) {
855 						if ( *next == '(' ) {
856 							balance++;
857 						} else if ( *next == ')' ) {
858 							balance--;
859 						}
860 					}
861 
862 					if ( *next == '\\' && ! escape ) {
863 						escape = 1;
864 					} else {
865 						escape = 0;
866 					}
867 
868 					if ( balance ) next++;
869 				}
870 
871 				if ( balance != 0 ) {
872 					rc = -1;
873 					goto done;
874 				}
875 
876 				*next = '\0';
877 
878 				if ( put_simple_vrFilter( ber, str ) == -1 ) {
879 					rc = -1;
880 					goto done;
881 				}
882 
883 				*next++ = /*'('*/ ')';
884 
885 				str = next;
886 				parens--;
887 				break;
888 			}
889 			break;
890 
891 		case /*'('*/ ')':
892 			Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: end\n" );
893 			if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
894 				rc = -1;
895 				goto done;
896 			}
897 			str++;
898 			parens--;
899 			break;
900 
901 		case ' ':
902 			str++;
903 			break;
904 
905 		default:	/* assume it's a simple type=value filter */
906 			Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: default\n" );
907 			next = strchr( str, '\0' );
908 			if ( put_simple_vrFilter( ber, str ) == -1 ) {
909 				rc = -1;
910 				goto done;
911 			}
912 			str = next;
913 			break;
914 		}
915 	}
916 
917 	rc = parens ? -1 : 0;
918 
919 done:
920 	LDAP_FREE( freeme );
921 	return rc;
922 }
923 
924 int
ldap_put_vrFilter(BerElement * ber,const char * str_in)925 ldap_put_vrFilter( BerElement *ber, const char *str_in )
926 {
927 	int rc =0;
928 
929 	if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) {
930 		return -1;
931 	}
932 
933 	rc = put_vrFilter( ber, str_in );
934 
935 	if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
936 		rc = -1;
937 	}
938 
939 	return rc;
940 }
941 
942 static int
put_vrFilter_list(BerElement * ber,char * str)943 put_vrFilter_list( BerElement *ber, char *str )
944 {
945 	char	*next = NULL;
946 	char	save;
947 
948 	Debug1( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n",
949 		str );
950 
951 	while ( *str ) {
952 		while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
953 			str++;
954 		}
955 		if ( *str == '\0' ) break;
956 
957 		if ( (next = find_right_paren( str + 1 )) == NULL ) {
958 			return -1;
959 		}
960 		save = *++next;
961 
962 		/* now we have "(filter)" with str pointing to it */
963 		*next = '\0';
964 		if ( put_vrFilter( ber, str ) == -1 ) return -1;
965 		*next = save;
966 		str = next;
967 	}
968 
969 	return 0;
970 }
971 
972 static int
put_simple_vrFilter(BerElement * ber,char * str)973 put_simple_vrFilter(
974 	BerElement *ber,
975 	char *str )
976 {
977 	char		*s;
978 	char		*value;
979 	ber_tag_t	ftype;
980 	int		rc = -1;
981 
982 	Debug1( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n",
983 		str );
984 
985 	str = LDAP_STRDUP( str );
986 	if( str == NULL ) return -1;
987 
988 	if ( (s = strchr( str, '=' )) == NULL ) {
989 		goto done;
990 	}
991 
992 	value = s + 1;
993 	*s-- = '\0';
994 
995 	switch ( *s ) {
996 	case '<':
997 		ftype = LDAP_FILTER_LE;
998 		*s = '\0';
999 		break;
1000 
1001 	case '>':
1002 		ftype = LDAP_FILTER_GE;
1003 		*s = '\0';
1004 		break;
1005 
1006 	case '~':
1007 		ftype = LDAP_FILTER_APPROX;
1008 		*s = '\0';
1009 		break;
1010 
1011 	case ':':
1012 		/* According to ValuesReturnFilter control definition
1013 		 * extensible filters are off the form:
1014 		 *		type [:rule] := value
1015 		 * or	:rule := value
1016 		 */
1017 		ftype = LDAP_FILTER_EXT;
1018 		*s = '\0';
1019 
1020 		{
1021 			char *rule = strchr( str, ':' );
1022 
1023 			if( rule == NULL ) {
1024 				/* must have attribute */
1025 				if( !ldap_is_desc( str ) ) {
1026 					goto done;
1027 				}
1028 				rule = "";
1029 			} else {
1030 				*rule++ = '\0';
1031 			}
1032 
1033 			if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
1034 				/* must have either type or rule */
1035 				goto done;
1036 			}
1037 
1038 			if ( *str != '\0' && !ldap_is_desc( str ) ) {
1039 				goto done;
1040 			}
1041 
1042 			if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
1043 				goto done;
1044 			}
1045 
1046 			rc = ber_printf( ber, "t{" /*"}"*/, ftype );
1047 
1048 			if( rc != -1 && rule && *rule != '\0' ) {
1049 				rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
1050 			}
1051 
1052 			if( rc != -1 && *str != '\0' ) {
1053 				rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
1054 			}
1055 
1056 			if( rc != -1 ) {
1057 				ber_slen_t len = ldap_pvt_filter_value_unescape( value );
1058 
1059 				if( len >= 0 ) {
1060 					rc = ber_printf( ber, "to",
1061 						LDAP_FILTER_EXT_VALUE, value, len );
1062 				} else {
1063 					rc = -1;
1064 				}
1065 			}
1066 
1067 			if( rc != -1 ) {
1068 				rc = ber_printf( ber, /*"{"*/ "N}" );
1069 			}
1070 		}
1071 		goto done;
1072 
1073 	default:
1074 		if( !ldap_is_desc( str ) ) {
1075 			goto done;
1076 
1077 		} else {
1078 			char *nextstar = ldap_pvt_find_wildcard( value );
1079 
1080 			if ( nextstar == NULL ) {
1081 				goto done;
1082 
1083 			} else if ( *nextstar == '\0' ) {
1084 				ftype = LDAP_FILTER_EQUALITY;
1085 
1086 			} else if ( strcmp( value, "*" ) == 0 ) {
1087 				ftype = LDAP_FILTER_PRESENT;
1088 
1089 			} else {
1090 				rc = put_substring_filter( ber, str, value, nextstar );
1091 				goto done;
1092 			}
1093 		} break;
1094 	}
1095 
1096 	if( !ldap_is_desc( str ) ) goto done;
1097 
1098 	if ( ftype == LDAP_FILTER_PRESENT ) {
1099 		rc = ber_printf( ber, "ts", ftype, str );
1100 
1101 	} else {
1102 		ber_slen_t len = ldap_pvt_filter_value_unescape( value );
1103 
1104 		if( len >= 0 ) {
1105 			rc = ber_printf( ber, "t{soN}",
1106 				ftype, str, value, len );
1107 		}
1108 	}
1109 
1110 done:
1111 	if( rc != -1 ) rc = 0;
1112 	LDAP_FREE( str );
1113 	return rc;
1114 }
1115 
1116