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