X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=libraries%2Flibldap%2Ffilter.c;h=2d05d81c25dc78187d08596ecfd9b5867ff6ef84;hb=62295c0928c071685dbbfb98644cadaeb37333b8;hp=1cf825822862b9baef8360db0f7c7eb378d1f0aa;hpb=6fc8c3ff4e6c35cb8b0135a3b2dfb555b307b84c;p=openldap diff --git a/libraries/libldap/filter.c b/libraries/libldap/filter.c index 1cf8258228..2d05d81c25 100644 --- a/libraries/libldap/filter.c +++ b/libraries/libldap/filter.c @@ -1,13 +1,20 @@ +/* search.c */ /* $OpenLDAP$ */ -/* - * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved. - * COPYING RESTRICTIONS APPLY, see COPYRIGHT file - */ -/* Portions - * Copyright (c) 1990 Regents of the University of Michigan. - * All rights reserved. +/* This work is part of OpenLDAP Software . + * + * Copyright 1998-2009 The OpenLDAP Foundation. + * All rights reserved. * - * search.c + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. */ #include "portable.h" @@ -22,6 +29,14 @@ #include "ldap-int.h" +static int put_simple_vrFilter LDAP_P(( + BerElement *ber, + char *str )); + +static int put_vrFilter_list LDAP_P(( + BerElement *ber, + char *str )); + static char *put_complex_filter LDAP_P(( BerElement *ber, char *str, @@ -35,7 +50,8 @@ static int put_simple_filter LDAP_P(( static int put_substring_filter LDAP_P(( BerElement *ber, char *type, - char *str )); + char *str, + char *nextstar )); static int put_filter_list LDAP_P(( BerElement *ber, @@ -153,7 +169,7 @@ find_right_paren( char *s ) if ( balance ) s++; } - return( *s ? s : NULL ); + return *s ? s : NULL; } static int hex2value( int c ) @@ -181,6 +197,10 @@ ldap_pvt_find_wildcard( const char *s ) case '*': /* found wildcard */ return (char *) s; + case '(': + case ')': + return NULL; + case '\\': if( s[1] == '\0' ) return NULL; @@ -192,10 +212,10 @@ ldap_pvt_find_wildcard( const char *s ) return NULL; /* allow RFC 1960 escapes */ - case '\\': case '*': case '(': case ')': + case '\\': s++; } } @@ -215,6 +235,11 @@ ldap_pvt_filter_value_unescape( char *fval ) for( r=v=0; fval[v] != '\0'; v++ ) { switch( fval[v] ) { + case '(': + case ')': + case '*': + return -1; + case '\\': /* escape */ v++; @@ -272,67 +297,78 @@ put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not ) */ /* put explicit tag */ - if ( ber_printf( ber, "t{" /*"}"*/, tag ) == -1 ) - return( NULL ); + if ( ber_printf( ber, "t{" /*"}"*/, tag ) == -1 ) { + return NULL; + } str++; - if ( (next = find_right_paren( str )) == NULL ) - return( NULL ); + if ( (next = find_right_paren( str )) == NULL ) { + return NULL; + } *next = '\0'; - if ( put_filter_list( ber, str, tag ) == -1 ) - return( NULL ); + if ( put_filter_list( ber, str, tag ) == -1 ) { + return NULL; + } /* close the '(' */ *next++ = ')'; /* flush explicit tagged thang */ - if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) - return( NULL ); + if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) { + return NULL; + } - return( next ); + return next; } int -ldap_int_put_filter( BerElement *ber, char *str ) +ldap_pvt_put_filter( BerElement *ber, const char *str_in ) { + int rc; + char *freeme; + char *str; char *next; int parens, balance, escape; /* - * A Filter looks like this: - * Filter ::= CHOICE { - * and [0] SET OF Filter, - * or [1] SET OF Filter, - * not [2] Filter, - * equalityMatch [3] AttributeValueAssertion, - * substrings [4] SubstringFilter, - * greaterOrEqual [5] AttributeValueAssertion, - * lessOrEqual [6] AttributeValueAssertion, - * present [7] AttributeType, - * approxMatch [8] AttributeValueAssertion, - * extensibleMatch [9] MatchingRuleAssertion -- LDAPv3 - * } + * A Filter looks like this (RFC 4511 as extended by RFC 4526): + * Filter ::= CHOICE { + * and [0] SET SIZE (0..MAX) OF filter Filter, + * or [1] SET SIZE (0..MAX) OF filter Filter, + * not [2] Filter, + * equalityMatch [3] AttributeValueAssertion, + * substrings [4] SubstringFilter, + * greaterOrEqual [5] AttributeValueAssertion, + * lessOrEqual [6] AttributeValueAssertion, + * present [7] AttributeDescription, + * approxMatch [8] AttributeValueAssertion, + * extensibleMatch [9] MatchingRuleAssertion, + * ... } * - * SubstringFilter ::= SEQUENCE { - * type AttributeType, - * SEQUENCE OF CHOICE { - * initial [0] IA5String, - * any [1] IA5String, - * final [2] IA5String - * } - * } + * SubstringFilter ::= SEQUENCE { + * type AttributeDescription, + * substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { + * initial [0] AssertionValue, -- only once + * any [1] AssertionValue, + * final [2] AssertionValue -- only once + * } + * } * - * MatchingRuleAssertion ::= SEQUENCE { -- LDAPv3 - * matchingRule [1] MatchingRuleId OPTIONAL, - * type [2] AttributeDescription OPTIONAL, - * matchValue [3] AssertionValue, - * dnAttributes [4] BOOLEAN DEFAULT FALSE } + * MatchingRuleAssertion ::= SEQUENCE { + * matchingRule [1] MatchingRuleId OPTIONAL, + * type [2] AttributeDescription OPTIONAL, + * matchValue [3] AssertionValue, + * dnAttributes [4] BOOLEAN DEFAULT FALSE } * - * Note: tags in a choice are always explicit + * Note: tags in a CHOICE are always explicit */ - Debug( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str, 0, 0 ); + Debug( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in, 0, 0 ); + + freeme = LDAP_STRDUP( str_in ); + if( freeme == NULL ) return LDAP_NO_MEMORY; + str = freeme; parens = 0; while ( *str ) { @@ -351,7 +387,10 @@ ldap_int_put_filter( BerElement *ber, char *str ) str = put_complex_filter( ber, str, LDAP_FILTER_AND, 0 ); - if( str == NULL ) return( -1 ); + if( str == NULL ) { + rc = -1; + goto done; + } parens--; break; @@ -362,7 +401,10 @@ ldap_int_put_filter( BerElement *ber, char *str ) str = put_complex_filter( ber, str, LDAP_FILTER_OR, 0 ); - if( str == NULL ) return( -1 ); + if( str == NULL ) { + rc = -1; + goto done; + } parens--; break; @@ -373,11 +415,18 @@ ldap_int_put_filter( BerElement *ber, char *str ) str = put_complex_filter( ber, str, LDAP_FILTER_NOT, 0 ); - if( str == NULL ) return( -1 ); + if( str == NULL ) { + rc = -1; + goto done; + } parens--; break; + case '(': + rc = -1; + goto done; + default: Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n", 0, 0, 0 ); @@ -385,28 +434,39 @@ ldap_int_put_filter( BerElement *ber, char *str ) balance = 1; escape = 0; next = str; + while ( *next && balance ) { if ( escape == 0 ) { - if ( *next == '(' ) + if ( *next == '(' ) { balance++; - else if ( *next == ')' ) + } else if ( *next == ')' ) { balance--; + } } - if ( *next == '\\' && ! escape ) + + if ( *next == '\\' && ! escape ) { escape = 1; - else + } else { escape = 0; - if ( balance ) - next++; + } + + if ( balance ) next++; + } + + if ( balance != 0 ) { + rc = -1; + goto done; } - if ( balance != 0 ) - return( -1 ); *next = '\0'; + if ( put_simple_filter( ber, str ) == -1 ) { - return( -1 ); + rc = -1; + goto done; } - *next++ = ')'; + + *next++ = /*'('*/ ')'; + str = next; parens--; break; @@ -416,8 +476,10 @@ ldap_int_put_filter( BerElement *ber, char *str ) case /*'('*/ ')': Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0, 0 ); - if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) - return( -1 ); + if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) { + rc = -1; + goto done; + } str++; parens--; break; @@ -431,14 +493,21 @@ ldap_int_put_filter( BerElement *ber, char *str ) 0, 0, 0 ); next = strchr( str, '\0' ); if ( put_simple_filter( ber, str ) == -1 ) { - return( -1 ); + rc = -1; + goto done; } str = next; break; } + if ( !parens ) + break; } - return( parens ? -1 : 0 ); + rc = ( parens || *str ) ? -1 : 0; + +done: + LDAP_FREE( freeme ); + return rc; } /* @@ -461,13 +530,13 @@ put_filter_list( BerElement *ber, char *str, ber_tag_t tag ) if ( *str == '\0' ) break; if ( (next = find_right_paren( str + 1 )) == NULL ) { - return( -1 ); + return -1; } save = *++next; /* now we have "(filter)" with str pointing to it */ *next = '\0'; - if ( ldap_int_put_filter( ber, str ) == -1 ) return -1; + if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1; *next = save; str = next; @@ -521,7 +590,7 @@ put_simple_filter( break; case ':': - /* RFC2254 extensible filters are off the form: + /* RFC 4515 extensible filters are off the form: * type [:dn] [:rule] := value * or [:dn]:rule := value */ @@ -538,7 +607,7 @@ put_simple_filter( if( rule == NULL ) { /* one colon */ - if ( strcmp(dn, "dn") == 0 ) { + if ( strcasecmp(dn, "dn") == 0 ) { /* must have attribute */ if( !ldap_is_desc( str ) ) { goto done; @@ -555,7 +624,7 @@ put_simple_filter( /* two colons */ *rule++ = '\0'; - if ( strcmp(dn, "dn") != 0 ) { + if ( strcasecmp(dn, "dn") != 0 ) { /* must have "dn" */ goto done; } @@ -581,9 +650,11 @@ put_simple_filter( if( rc != -1 && rule && *rule != '\0' ) { rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule ); } + if( rc != -1 && *str != '\0' ) { rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str ); } + if( rc != -1 ) { ber_slen_t len = ldap_pvt_filter_value_unescape( value ); @@ -594,10 +665,12 @@ put_simple_filter( rc = -1; } } + if( rc != -1 && dn ) { rc = ber_printf( ber, "tb", LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 ); } + if( rc != -1 ) { rc = ber_printf( ber, /*"{"*/ "N}" ); } @@ -605,16 +678,23 @@ put_simple_filter( goto done; default: - { + if( !ldap_is_desc( str ) ) { + goto done; + + } else { char *nextstar = ldap_pvt_find_wildcard( value ); + if ( nextstar == NULL ) { goto done; + } else if ( *nextstar == '\0' ) { ftype = LDAP_FILTER_EQUALITY; + } else if ( strcmp( value, "*" ) == 0 ) { ftype = LDAP_FILTER_PRESENT; + } else { - rc = put_substring_filter( ber, str, value ); + rc = put_substring_filter( ber, str, value, nextstar ); goto done; } } break; @@ -641,39 +721,41 @@ done: } static int -put_substring_filter( BerElement *ber, char *type, char *val ) +put_substring_filter( BerElement *ber, char *type, char *val, char *nextstar ) { - char *nextstar; int gotstar = 0; ber_tag_t ftype = LDAP_FILTER_SUBSTRINGS; Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type, val, 0 ); - if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) - return( -1 ); + if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) { + return -1; + } for( ; *val; val=nextstar ) { - nextstar = ldap_pvt_find_wildcard( val ); + if ( gotstar ) + nextstar = ldap_pvt_find_wildcard( val ); if ( nextstar == NULL ) { return -1; } - + if ( *nextstar == '\0' ) { ftype = LDAP_SUBSTRING_FINAL; - } else if ( gotstar++ == 0 ) { - ftype = LDAP_SUBSTRING_INITIAL; } else { - ftype = LDAP_SUBSTRING_ANY; + *nextstar++ = '\0'; + if ( gotstar++ == 0 ) { + ftype = LDAP_SUBSTRING_INITIAL; + } else { + ftype = LDAP_SUBSTRING_ANY; + } } - *nextstar++ = '\0'; - if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) { ber_slen_t len = ldap_pvt_filter_value_unescape( val ); - if ( len < 0 ) { + if ( len <= 0 ) { return -1; } @@ -683,8 +765,362 @@ put_substring_filter( BerElement *ber, char *type, char *val ) } } - if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) + if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) { return -1; + } return 0; } + +static int +put_vrFilter( BerElement *ber, const char *str_in ) +{ + int rc; + char *freeme; + char *str; + char *next; + int parens, balance, escape; + + /* + * A ValuesReturnFilter looks like this: + * + * ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem + * SimpleFilterItem ::= CHOICE { + * equalityMatch [3] AttributeValueAssertion, + * substrings [4] SubstringFilter, + * greaterOrEqual [5] AttributeValueAssertion, + * lessOrEqual [6] AttributeValueAssertion, + * present [7] AttributeType, + * approxMatch [8] AttributeValueAssertion, + * extensibleMatch [9] SimpleMatchingAssertion -- LDAPv3 + * } + * + * SubstringFilter ::= SEQUENCE { + * type AttributeType, + * SEQUENCE OF CHOICE { + * initial [0] IA5String, + * any [1] IA5String, + * final [2] IA5String + * } + * } + * + * SimpleMatchingAssertion ::= SEQUENCE { -- LDAPv3 + * matchingRule [1] MatchingRuleId OPTIONAL, + * type [2] AttributeDescription OPTIONAL, + * matchValue [3] AssertionValue } + * + * (Source: RFC 3876) + */ + + Debug( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in, 0, 0 ); + + freeme = LDAP_STRDUP( str_in ); + if( freeme == NULL ) return LDAP_NO_MEMORY; + str = freeme; + + parens = 0; + while ( *str ) { + switch ( *str ) { + case '(': /*')'*/ + str++; + parens++; + + /* skip spaces */ + while( LDAP_SPACE( *str ) ) str++; + + switch ( *str ) { + case '(': + if ( (next = find_right_paren( str )) == NULL ) { + rc = -1; + goto done; + } + + *next = '\0'; + + if ( put_vrFilter_list( ber, str ) == -1 ) { + rc = -1; + goto done; + } + + /* close the '(' */ + *next++ = ')'; + + str = next; + + parens--; + break; + + + default: + Debug( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n", + 0, 0, 0 ); + + balance = 1; + escape = 0; + next = str; + + while ( *next && balance ) { + if ( escape == 0 ) { + if ( *next == '(' ) { + balance++; + } else if ( *next == ')' ) { + balance--; + } + } + + if ( *next == '\\' && ! escape ) { + escape = 1; + } else { + escape = 0; + } + + if ( balance ) next++; + } + + if ( balance != 0 ) { + rc = -1; + goto done; + } + + *next = '\0'; + + if ( put_simple_vrFilter( ber, str ) == -1 ) { + rc = -1; + goto done; + } + + *next++ = /*'('*/ ')'; + + str = next; + parens--; + break; + } + break; + + case /*'('*/ ')': + Debug( LDAP_DEBUG_TRACE, "put_vrFilter: end\n", + 0, 0, 0 ); + if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) { + rc = -1; + goto done; + } + str++; + parens--; + break; + + case ' ': + str++; + break; + + default: /* assume it's a simple type=value filter */ + Debug( LDAP_DEBUG_TRACE, "put_vrFilter: default\n", + 0, 0, 0 ); + next = strchr( str, '\0' ); + if ( put_simple_vrFilter( ber, str ) == -1 ) { + rc = -1; + goto done; + } + str = next; + break; + } + } + + rc = parens ? -1 : 0; + +done: + LDAP_FREE( freeme ); + return rc; +} + +int +ldap_put_vrFilter( BerElement *ber, const char *str_in ) +{ + int rc =0; + + if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) { + rc = -1; + } + + rc = put_vrFilter( ber, str_in ); + + if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) { + rc = -1; + } + + return rc; +} + +static int +put_vrFilter_list( BerElement *ber, char *str ) +{ + char *next = NULL; + char save; + + Debug( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n", + str, 0, 0 ); + + while ( *str ) { + while ( *str && LDAP_SPACE( (unsigned char) *str ) ) { + str++; + } + if ( *str == '\0' ) break; + + if ( (next = find_right_paren( str + 1 )) == NULL ) { + return -1; + } + save = *++next; + + /* now we have "(filter)" with str pointing to it */ + *next = '\0'; + if ( put_vrFilter( ber, str ) == -1 ) return -1; + *next = save; + str = next; + } + + return 0; +} + +static int +put_simple_vrFilter( + BerElement *ber, + char *str ) +{ + char *s; + char *value; + ber_tag_t ftype; + int rc = -1; + + Debug( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n", + str, 0, 0 ); + + str = LDAP_STRDUP( str ); + if( str == NULL ) return -1; + + if ( (s = strchr( str, '=' )) == NULL ) { + goto done; + } + + value = s + 1; + *s-- = '\0'; + + switch ( *s ) { + case '<': + ftype = LDAP_FILTER_LE; + *s = '\0'; + break; + + case '>': + ftype = LDAP_FILTER_GE; + *s = '\0'; + break; + + case '~': + ftype = LDAP_FILTER_APPROX; + *s = '\0'; + break; + + case ':': + /* According to ValuesReturnFilter control definition + * extensible filters are off the form: + * type [:rule] := value + * or :rule := value + */ + ftype = LDAP_FILTER_EXT; + *s = '\0'; + + { + char *rule = strchr( str, ':' ); + + if( rule == NULL ) { + /* must have attribute */ + if( !ldap_is_desc( str ) ) { + goto done; + } + rule = ""; + } else { + *rule++ = '\0'; + } + + if ( *str == '\0' && ( !rule || *rule == '\0' ) ) { + /* must have either type or rule */ + goto done; + } + + if ( *str != '\0' && !ldap_is_desc( str ) ) { + goto done; + } + + if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) { + goto done; + } + + rc = ber_printf( ber, "t{" /*"}"*/, ftype ); + + if( rc != -1 && rule && *rule != '\0' ) { + rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule ); + } + + if( rc != -1 && *str != '\0' ) { + rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str ); + } + + if( rc != -1 ) { + ber_slen_t len = ldap_pvt_filter_value_unescape( value ); + + if( len >= 0 ) { + rc = ber_printf( ber, "to", + LDAP_FILTER_EXT_VALUE, value, len ); + } else { + rc = -1; + } + } + + if( rc != -1 ) { + rc = ber_printf( ber, /*"{"*/ "N}" ); + } + } + goto done; + + default: + if( !ldap_is_desc( str ) ) { + goto done; + + } else { + char *nextstar = ldap_pvt_find_wildcard( value ); + + if ( nextstar == NULL ) { + goto done; + + } else if ( *nextstar == '\0' ) { + ftype = LDAP_FILTER_EQUALITY; + + } else if ( strcmp( value, "*" ) == 0 ) { + ftype = LDAP_FILTER_PRESENT; + + } else { + rc = put_substring_filter( ber, str, value, nextstar ); + goto done; + } + } break; + } + + if( !ldap_is_desc( str ) ) goto done; + + if ( ftype == LDAP_FILTER_PRESENT ) { + rc = ber_printf( ber, "ts", ftype, str ); + + } else { + ber_slen_t len = ldap_pvt_filter_value_unescape( value ); + + if( len >= 0 ) { + rc = ber_printf( ber, "t{soN}", + ftype, str, value, len ); + } + } + +done: + if( rc != -1 ) rc = 0; + LDAP_FREE( str ); + return rc; +} +