diff --git a/include/ldap.h b/include/ldap.h index 0332f88597..e60b453d5d 100644 --- a/include/ldap.h +++ b/include/ldap.h @@ -202,6 +202,8 @@ typedef struct ldapcontrol { #define LDAP_CONTROL_VLVREQUEST "2.16.840.1.113730.3.4.9" #define LDAP_CONTROL_VLVRESPONSE "2.16.840.1.113730.3.4.10" +#define LDAP_CONTROL_VALUESRETURNFILTER "1.2.826.0.1.334810.2.3" + /* LDAP Unsolicited Notifications */ #define LDAP_NOTICE_OF_DISCONNECTION "1.3.6.1.4.1.1466.20036" #define LDAP_NOTICE_DISCONNECT LDAP_NOTICE_OF_DISCONNECTION diff --git a/libraries/libldap/filter.c b/libraries/libldap/filter.c index fe0fab327b..7d40d1ceb4 100644 --- a/libraries/libldap/filter.c +++ b/libraries/libldap/filter.c @@ -22,6 +22,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, @@ -799,3 +807,387 @@ put_substring_filter( BerElement *ber, char *type, char *val ) return 0; } + +int +ldap_pvt_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 } + */ + +#ifdef NEW_LOGGING + LDAP_LOG (( "filter", LDAP_LEVEL_ARGS, "ldap_pvt_put_vrFilter: \"%s\"\n", + str_in )); +#else + Debug( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in, 0, 0 ); +#endif + + 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: +#ifdef NEW_LOGGING + LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, + "ldap_pvt_put_vrFilter: simple\n" )); +#else + Debug( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n", + 0, 0, 0 ); +#endif + + 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 /*'('*/ ')': +#ifdef NEW_LOGGING + LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, + "ldap_pvt_put_filter: end\n" )); +#else + Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", + 0, 0, 0 ); +#endif + if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) { + rc = -1; + goto done; + } + str++; + parens--; + break; + + case ' ': + str++; + break; + + default: /* assume it's a simple type=value filter */ +#ifdef NEW_LOGGING + LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, + "ldap_pvt_put_filter: default\n" )); +#else + Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", + 0, 0, 0 ); +#endif + next = strchr( str, '\0' ); + if ( put_simple_filter( ber, str ) == -1 ) { + rc = -1; + goto done; + } + str = next; + break; + } + } + + rc = parens ? -1 : 0; + +done: + LDAP_FREE( freeme ); + return rc; +} + +int +put_vrFilter( BerElement *ber, const char *str_in ) +{ + int rc =0; + + if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) { + rc = -1; + } + + rc = ldap_pvt_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; + +#ifdef NEW_LOGGING + LDAP_LOG (( "filter", LDAP_LEVEL_ARGS, + "put_vrFilter_list \"%s\"\n", str )); +#else + Debug( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n", + str, 0, 0 ); +#endif + + 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 ( ldap_pvt_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; + +#ifdef NEW_LOGGING + LDAP_LOG (( "filter", LDAP_LEVEL_ARGS, + "put_simple_vrFilter: \"%s\"\n", str )); +#else + Debug( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n", + str, 0, 0 ); +#endif + + 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, ':' ); + *rule++ = '\0'; + + 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 ); + 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; +} + diff --git a/servers/slapd/Makefile.in b/servers/slapd/Makefile.in index 2d77b635ee..936ee9d106 100644 --- a/servers/slapd/Makefile.in +++ b/servers/slapd/Makefile.in @@ -19,7 +19,7 @@ SRCS = main.c daemon.c connection.c search.c filter.c add.c charray.c \ schemaparse.c ad.c at.c mr.c syntax.c oc.c saslauthz.c \ oidm.c starttls.c index.c sets.c referral.c \ root_dse.c sasl.c module.c suffixalias.c mra.c mods.c \ - limits.c backglue.c operational.c \ + limits.c backglue.c operational.c matchedValues.c \ $(@PLAT@_SRCS) OBJS = main.o daemon.o connection.o search.o filter.o add.o charray.o \ @@ -32,7 +32,7 @@ OBJS = main.o daemon.o connection.o search.o filter.o add.o charray.o \ schemaparse.o ad.o at.o mr.o syntax.o oc.o saslauthz.o \ oidm.o starttls.o index.o sets.o referral.o \ root_dse.o sasl.o module.o suffixalias.o mra.o mods.o \ - limits.o backglue.o operational.o \ + limits.o backglue.o operational.o matchedValues.o \ $(@PLAT@_OBJS) LDAP_INCDIR= ../../include diff --git a/servers/slapd/back-bdb/init.c b/servers/slapd/back-bdb/init.c index a1bb111a7b..0a366fd2b7 100644 --- a/servers/slapd/back-bdb/init.c +++ b/servers/slapd/back-bdb/init.c @@ -479,6 +479,9 @@ bdb_initialize( #endif #ifdef LDAP_CONTROL_NOOP LDAP_CONTROL_NOOP, +#endif +#ifdef LDAP_CONTROL_VALUESRETURNFILTER + LDAP_CONTROL_VALUESRETURNFILTER, #endif NULL }; diff --git a/servers/slapd/back-dnssrv/init.c b/servers/slapd/back-dnssrv/init.c index 9b4059f827..3363644477 100644 --- a/servers/slapd/back-dnssrv/init.c +++ b/servers/slapd/back-dnssrv/init.c @@ -36,6 +36,7 @@ dnssrv_back_initialize( { static char *controls[] = { LDAP_CONTROL_MANAGEDSAIT, + LDAP_CONTROL_VALUESRETURNFILTER, NULL }; diff --git a/servers/slapd/back-ldbm/init.c b/servers/slapd/back-ldbm/init.c index ada60432c2..d3e16e31c9 100644 --- a/servers/slapd/back-ldbm/init.c +++ b/servers/slapd/back-ldbm/init.c @@ -37,6 +37,7 @@ ldbm_back_initialize( { static char *controls[] = { LDAP_CONTROL_MANAGEDSAIT, + LDAP_CONTROL_VALUESRETURNFILTER, NULL }; diff --git a/servers/slapd/back-monitor/init.c b/servers/slapd/back-monitor/init.c index 44d81d0529..b8637dd0d8 100644 --- a/servers/slapd/back-monitor/init.c +++ b/servers/slapd/back-monitor/init.c @@ -163,6 +163,7 @@ monitor_back_initialize( { static char *controls[] = { LDAP_CONTROL_MANAGEDSAIT, + LDAP_CONTROL_VALUESRETURNFILTER, NULL }; diff --git a/servers/slapd/connection.c b/servers/slapd/connection.c index 43844eb97e..6f47c9d1be 100644 --- a/servers/slapd/connection.c +++ b/servers/slapd/connection.c @@ -1423,6 +1423,8 @@ connection_input( op = slap_op_alloc( ber, msgid, tag, conn->c_n_ops_received++ ); + op->vrFilter = NULL; + op->o_pagedresults_state = conn->c_pagedresults_state; #ifdef LDAP_CONNECTIONLESS diff --git a/servers/slapd/controls.c b/servers/slapd/controls.c index b475dd1af4..16ea8cb6cc 100644 --- a/servers/slapd/controls.c +++ b/servers/slapd/controls.c @@ -47,6 +47,7 @@ static SLAP_CTRL_PARSE_FN parseManageDSAit; static SLAP_CTRL_PARSE_FN parseSubentries; static SLAP_CTRL_PARSE_FN parseNoOp; static SLAP_CTRL_PARSE_FN parsePagedResults; +static SLAP_CTRL_PARSE_FN parseValuesReturnFilter; static struct slap_control { char *sc_oid; @@ -72,6 +73,11 @@ static struct slap_control { { LDAP_CONTROL_PAGEDRESULTS_REQUEST, SLAP_CTRL_SEARCH, NULL, parsePagedResults }, +#endif +#ifdef LDAP_CONTROL_VALUESRETURNFILTER + { LDAP_CONTROL_VALUESRETURNFILTER, + SLAP_CTRL_SEARCH, NULL, + parseValuesReturnFilter }, #endif { NULL } }; @@ -527,3 +533,61 @@ static int parsePagedResults ( return LDAP_SUCCESS; } #endif + +#ifdef LDAP_CONTROL_VALUESRETURNFILTER +int parseValuesReturnFilter ( + Connection *conn, + Operation *op, + LDAPControl *ctrl, + const char **text ) +{ + int rc; + BerElement *ber; + struct berval fstr = { 0, NULL }; + const char *err_msg = ""; + + if ( op->o_valuesreturnfilter != SLAP_NO_CONTROL ) { + *text = "valuesreturnfilter control specified multiple times"; + return LDAP_PROTOCOL_ERROR; + } + + ber = ber_init( &(ctrl->ldctl_value) ); + if (ber == NULL) { + *text = "internal error"; + return LDAP_OTHER; + } + + rc = get_vrFilter( conn, ber, &(op->vrFilter), &err_msg); + + if( rc != LDAP_SUCCESS ) { + text = &err_msg; + if( rc == SLAPD_DISCONNECT ) { + send_ldap_disconnect( conn, op, + LDAP_PROTOCOL_ERROR, *text ); + } else { + send_ldap_result( conn, op, rc, + NULL, *text, NULL, NULL ); + } + if( fstr.bv_val != NULL) free( fstr.bv_val ); + if( op->vrFilter != NULL) vrFilter_free( op->vrFilter ); + + } else { + vrFilter2bv( op->vrFilter, &fstr ); + } + +#ifdef NEW_LOGGING + LDAP_LOG(( "operation", LDAP_LEVEL_ARGS, + "parseValuesReturnFilter: conn %d vrFilter: %s\n", conn->c_connid, + fstr.bv_len ? fstr.bv_val : "empty" )); +#else + Debug( LDAP_DEBUG_ARGS, " vrFilter: %s\n", + fstr.bv_len ? fstr.bv_val : "empty", 0, 0 ); +#endif + + op->o_valuesreturnfilter = ctrl->ldctl_iscritical + ? SLAP_CRITICAL_CONTROL + : SLAP_NONCRITICAL_CONTROL; + + return LDAP_SUCCESS; +} +#endif diff --git a/servers/slapd/filter.c b/servers/slapd/filter.c index 16124c0c31..105a3fb68c 100644 --- a/servers/slapd/filter.c +++ b/servers/slapd/filter.c @@ -30,6 +30,17 @@ static int filter_escape_value( struct berval *in, struct berval *out ); +static void simple_vrFilter2bv( + ValuesReturnFilter *f, + struct berval *fstr ); + +static int get_simple_vrFilter( + Connection *conn, + BerElement *ber, + ValuesReturnFilter **f, + const char **text ); + + int get_filter( Connection *conn, @@ -796,3 +807,719 @@ static int filter_escape_value( out->bv_val[out->bv_len] = '\0'; return LDAP_SUCCESS; } + +static int +get_simple_vrFilter( + Connection *conn, + BerElement *ber, + ValuesReturnFilter **filt, + const char **text ) +{ + ber_tag_t tag; + ber_len_t len; + int err; + ValuesReturnFilter *f; + +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY, "get_simple_vrFilter: conn %d\n", + conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "begin get_simple_vrFilter\n", 0, 0, 0 ); +#endif + + tag = ber_peek_tag( ber, &len ); + + if( tag == LBER_ERROR ) { + *text = "error decoding filter"; + return SLAPD_DISCONNECT; + } + + f = (ValuesReturnFilter *) ch_malloc( sizeof(ValuesReturnFilter) ); + f->f_next = NULL; + + err = LDAP_SUCCESS; + f->f_choice = tag; + + switch ( f->f_choice ) { + case LDAP_FILTER_EQUALITY: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL2, + "get_simple_vrFilter: conn %d EQUALITY\n", conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "EQUALITY\n", 0, 0, 0 ); +#endif + err = get_ava( ber, &f->f_ava, SLAP_MR_EQUALITY, text ); + if ( err != LDAP_SUCCESS ) { + break; + } + + assert( f->f_ava != NULL ); + break; + + case LDAP_FILTER_SUBSTRINGS: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "get_simple_vrFilter: conn %d SUBSTRINGS\n", conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "SUBSTRINGS\n", 0, 0, 0 ); +#endif + err = get_substring_filter( conn, ber, (Filter *)f, text ); + break; + + case LDAP_FILTER_GE: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "get_simple_vrFilter: conn %d GE\n", conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "GE\n", 0, 0, 0 ); +#endif + err = get_ava( ber, &f->f_ava, SLAP_MR_ORDERING, text ); + if ( err != LDAP_SUCCESS ) { + break; + } + break; + + case LDAP_FILTER_LE: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "get_simple_vrFilter: conn %d LE\n", conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "LE\n", 0, 0, 0 ); +#endif + err = get_ava( ber, &f->f_ava, SLAP_MR_ORDERING, text ); + if ( err != LDAP_SUCCESS ) { + break; + } + break; + + case LDAP_FILTER_PRESENT: { + struct berval type; + +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "get_simple_vrFilter: conn %d PRESENT\n", conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "PRESENT\n", 0, 0, 0 ); +#endif + if ( ber_scanf( ber, "m", &type ) == LBER_ERROR ) { + err = SLAPD_DISCONNECT; + *text = "error decoding filter"; + break; + } + + f->f_desc = NULL; + err = slap_bv2ad( &type, &f->f_desc, text ); + + if( err != LDAP_SUCCESS ) { + /* unrecognized attribute description or other error */ + f->f_choice = SLAPD_FILTER_COMPUTED; + f->f_result = LDAP_COMPARE_FALSE; + err = LDAP_SUCCESS; + break; + } + } break; + + case LDAP_FILTER_APPROX: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "get_simple_vrFilter: conn %d APPROX\n", conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "APPROX\n", 0, 0, 0 ); +#endif + err = get_ava( ber, &f->f_ava, SLAP_MR_EQUALITY_APPROX, text ); + if ( err != LDAP_SUCCESS ) { + break; + } + break; + + case LDAP_FILTER_EXT: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "get_simple_vrFilter: conn %d EXTENSIBLE\n", conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "EXTENSIBLE\n", 0, 0, 0 ); +#endif + + err = get_mra( ber, &f->f_mra, text ); + if ( err != LDAP_SUCCESS ) { + break; + } + + assert( f->f_mra != NULL ); + break; + + default: + (void) ber_scanf( ber, "x" ); /* skip the element */ +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_ERR, + "get_simple_vrFilter: conn %d unknown filter type=%lu\n", + conn->c_connid, f->f_choice )); +#else + Debug( LDAP_DEBUG_ANY, "get_simple_vrFilter: unknown filter type=%lu\n", + f->f_choice, 0, 0 ); +#endif + f->f_choice = SLAPD_FILTER_COMPUTED; + f->f_result = SLAPD_COMPARE_UNDEFINED; + break; + } + + if ( err != LDAP_SUCCESS ) { + if( err != SLAPD_DISCONNECT ) { + /* ignore error */ + f->f_choice = SLAPD_FILTER_COMPUTED; + f->f_result = SLAPD_COMPARE_UNDEFINED; + err = LDAP_SUCCESS; + *filt = f; + + } else { + free(f); + } + + } else { + *filt = f; + } + +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL2, + "get_simple_vrFilter: conn %d exit\n", conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "end get_simple_vrFilter %d\n", err, 0, 0 ); +#endif + return( err ); +} + +int +get_vrFilter( Connection *conn, BerElement *ber, + ValuesReturnFilter **f, + const char **text ) +{ + /* + * 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 } + */ + + ValuesReturnFilter **new; + int err; + ber_tag_t tag; + ber_len_t len; + char *last; + +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY, + "get_vrFilter: conn %d start\n", conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "begin get_vrFilter\n", 0, 0, 0 ); +#endif + + tag = ber_peek_tag( ber, &len ); + + if( tag == LBER_ERROR ) { + *text = "error decoding vrFilter"; + return SLAPD_DISCONNECT; + } + + if( tag != LBER_SEQUENCE ) { + *text = "error decoding vrFilter, expect SEQUENCE tag"; + return SLAPD_DISCONNECT; + } + + new = f; + for ( tag = ber_first_element( ber, &len, &last ); tag != LBER_DEFAULT; + tag = ber_next_element( ber, &len, last ) ) + { + err = get_simple_vrFilter( conn, ber, new, text ); + if ( err != LDAP_SUCCESS ) + return( err ); + new = &(*new)->f_next; + } + *new = NULL; + +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY, + "get_vrFilter: conn %d exit\n", conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "end get_vrFilter %d\n", err, 0, 0 ); +#endif + return( LDAP_SUCCESS ); +} + +void +vrFilter_free( ValuesReturnFilter *f ) +{ + ValuesReturnFilter *p, *next; + + if ( f == NULL ) { + return; + } + + for ( p = f; p != NULL; p = next ) { + next = p->f_next; + + switch ( f->f_choice ) { + case LDAP_FILTER_PRESENT: + break; + + case LDAP_FILTER_EQUALITY: + case LDAP_FILTER_GE: + case LDAP_FILTER_LE: + case LDAP_FILTER_APPROX: + ava_free( f->f_ava, 1 ); + break; + + case LDAP_FILTER_SUBSTRINGS: + if ( f->f_sub_initial.bv_val != NULL ) { + free( f->f_sub_initial.bv_val ); + } + ber_bvarray_free( f->f_sub_any ); + if ( f->f_sub_final.bv_val != NULL ) { + free( f->f_sub_final.bv_val ); + } + ch_free( f->f_sub ); + break; + + case LDAP_FILTER_EXT: + mra_free( f->f_mra, 1 ); + break; + + case SLAPD_FILTER_COMPUTED: + break; + + default: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_ERR, + "filter_free: unknown filter type %lu\n", f->f_choice )); +#else + Debug( LDAP_DEBUG_ANY, "filter_free: unknown filter type=%lu\n", + f->f_choice, 0, 0 ); +#endif + break; + } + + free( f ); + } +} + + +void +vrFilter2bv( ValuesReturnFilter *f, struct berval *fstr ) +{ + int i; + ValuesReturnFilter *p; + struct berval tmp; + ber_len_t len; + + if ( f == NULL ) { + ber_str2bv( "No filter!", sizeof("No filter!")-1, 1, fstr ); + return; + } + + fstr->bv_len = sizeof("()") - 1; + fstr->bv_val = malloc( fstr->bv_len + 128 ); + + snprintf( fstr->bv_val, fstr->bv_len + 1, "()"); + + for ( p = f; p != NULL; p = p->f_next ) { + len = fstr->bv_len; + + simple_vrFilter2bv( p, &tmp ); + + fstr->bv_len += tmp.bv_len; + fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 ); + + snprintf( &fstr->bv_val[len-1], tmp.bv_len + 2, + /*"("*/ "%s)", tmp.bv_val ); + + ch_free( tmp.bv_val ); + } +} + +static void +simple_vrFilter2bv( ValuesReturnFilter *f, struct berval *fstr ) +{ + int i; + ValuesReturnFilter *p; + struct berval tmp; + ber_len_t len; + + if ( f == NULL ) { + ber_str2bv( "No filter!", sizeof("No filter!")-1, 1, fstr ); + return; + } + + switch ( f->f_choice ) { + case LDAP_FILTER_EQUALITY: + filter_escape_value( &f->f_av_value, &tmp ); + + fstr->bv_len = f->f_av_desc->ad_cname.bv_len + + tmp.bv_len + ( sizeof("(=)") - 1 ); + fstr->bv_val = malloc( fstr->bv_len + 1 ); + + snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)", + f->f_av_desc->ad_cname.bv_val, + tmp.bv_val ); + + ber_memfree( tmp.bv_val ); + break; + + case LDAP_FILTER_GE: + filter_escape_value( &f->f_av_value, &tmp ); + + fstr->bv_len = f->f_av_desc->ad_cname.bv_len + + tmp.bv_len + ( sizeof("(>=)") - 1 ); + fstr->bv_val = malloc( fstr->bv_len + 1 ); + + snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)", + f->f_av_desc->ad_cname.bv_val, + tmp.bv_val ); + + ber_memfree( tmp.bv_val ); + break; + + case LDAP_FILTER_LE: + filter_escape_value( &f->f_av_value, &tmp ); + + fstr->bv_len = f->f_av_desc->ad_cname.bv_len + + tmp.bv_len + ( sizeof("(<=)") - 1 ); + fstr->bv_val = malloc( fstr->bv_len + 1 ); + + snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)", + f->f_av_desc->ad_cname.bv_val, + tmp.bv_val ); + + ber_memfree( tmp.bv_val ); + break; + + case LDAP_FILTER_APPROX: + filter_escape_value( &f->f_av_value, &tmp ); + + fstr->bv_len = f->f_av_desc->ad_cname.bv_len + + tmp.bv_len + ( sizeof("(~=)") - 1 ); + fstr->bv_val = malloc( fstr->bv_len + 1 ); + + snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)", + f->f_av_desc->ad_cname.bv_val, + tmp.bv_val ); + ber_memfree( tmp.bv_val ); + break; + + case LDAP_FILTER_SUBSTRINGS: + fstr->bv_len = f->f_sub_desc->ad_cname.bv_len + + ( sizeof("(=*)") - 1 ); + fstr->bv_val = malloc( fstr->bv_len + 128 ); + + snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)", + f->f_sub_desc->ad_cname.bv_val ); + + if ( f->f_sub_initial.bv_val != NULL ) { + len = fstr->bv_len; + + filter_escape_value( &f->f_sub_initial, &tmp ); + + fstr->bv_len += tmp.bv_len; + fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 ); + + snprintf( &fstr->bv_val[len-2], tmp.bv_len+3, + /* "(attr=" */ "%s*)", + tmp.bv_val ); + + ber_memfree( tmp.bv_val ); + } + + if ( f->f_sub_any != NULL ) { + for ( i = 0; f->f_sub_any[i].bv_val != NULL; i++ ) { + len = fstr->bv_len; + filter_escape_value( &f->f_sub_any[i], &tmp ); + + fstr->bv_len += tmp.bv_len + 1; + fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 ); + + snprintf( &fstr->bv_val[len-1], tmp.bv_len+3, + /* "(attr=[init]*[any*]" */ "%s*)", + tmp.bv_val ); + ber_memfree( tmp.bv_val ); + } + } + + if ( f->f_sub_final.bv_val != NULL ) { + len = fstr->bv_len; + + filter_escape_value( &f->f_sub_final, &tmp ); + + fstr->bv_len += tmp.bv_len; + fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 ); + + snprintf( &fstr->bv_val[len-1], tmp.bv_len+3, + /* "(attr=[init*][any*]" */ "%s)", + tmp.bv_val ); + + ber_memfree( tmp.bv_val ); + } + + break; + + case LDAP_FILTER_PRESENT: + fstr->bv_len = f->f_desc->ad_cname.bv_len + + ( sizeof("(=*)") - 1 ); + fstr->bv_val = malloc( fstr->bv_len + 1 ); + + snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)", + f->f_desc->ad_cname.bv_val ); + break; + + case LDAP_FILTER_EXT: + filter_escape_value( &f->f_mr_value, &tmp ); + + fstr->bv_len = f->f_mr_desc->ad_cname.bv_len + + ( f->f_mr_dnattrs ? sizeof(":dn")-1 : 0 ) + + ( f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_len+1 : 0 ) + + tmp.bv_len + ( sizeof("(:=)") - 1 ); + fstr->bv_val = malloc( fstr->bv_len + 1 ); + + snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)", + f->f_mr_desc->ad_cname.bv_val, + f->f_mr_dnattrs ? ":dn" : "", + f->f_mr_rule_text.bv_len ? ":" : "", + f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_val : "", + tmp.bv_val ); + ber_memfree( tmp.bv_val ); + break; + + case SLAPD_FILTER_COMPUTED: + ber_str2bv( + f->f_result == LDAP_COMPARE_FALSE ? "(?=false)" : + f->f_result == LDAP_COMPARE_TRUE ? "(?=true)" : + f->f_result == SLAPD_COMPARE_UNDEFINED ? "(?=undefined)" : + "(?=error)", + f->f_result == LDAP_COMPARE_FALSE ? sizeof("(?=false)")-1 : + f->f_result == LDAP_COMPARE_TRUE ? sizeof("(?=true)")-1 : + f->f_result == SLAPD_COMPARE_UNDEFINED ? sizeof("(?=undefined)")-1 : + sizeof("(?=error)")-1, + 1, fstr ); + break; + + default: + ber_str2bv( "(?=unknown)", sizeof("(?=unknown)")-1, 1, fstr ); + break; + } +} + +static int +get_substring_vrFilter( + Connection *conn, + BerElement *ber, + ValuesReturnFilter *f, + const char **text ) +{ + ber_tag_t tag; + ber_len_t len; + ber_tag_t rc; + struct berval value; + char *last; + struct berval bv; + *text = "error decoding filter"; + +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY, + "get_substring_filter: conn %d begin\n", conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "begin get_substring_filter\n", 0, 0, 0 ); +#endif + if ( ber_scanf( ber, "{m" /*}*/, &bv ) == LBER_ERROR ) { + return SLAPD_DISCONNECT; + } + + f->f_sub = ch_calloc( 1, sizeof(SubstringsAssertion) ); + f->f_sub_desc = NULL; + rc = slap_bv2ad( &bv, &f->f_sub_desc, text ); + + if( rc != LDAP_SUCCESS ) { + text = NULL; + ch_free( f->f_sub ); + f->f_choice = SLAPD_FILTER_COMPUTED; + f->f_result = SLAPD_COMPARE_UNDEFINED; + return LDAP_SUCCESS; + } + + f->f_sub_initial.bv_val = NULL; + f->f_sub_any = NULL; + f->f_sub_final.bv_val = NULL; + + for ( tag = ber_first_element( ber, &len, &last ); tag != LBER_DEFAULT; + tag = ber_next_element( ber, &len, last ) ) + { + unsigned usage; + + rc = ber_scanf( ber, "m", &value ); + if ( rc == LBER_ERROR ) { + rc = SLAPD_DISCONNECT; + goto return_error; + } + + if ( value.bv_val == NULL || value.bv_len == 0 ) { + rc = LDAP_INVALID_SYNTAX; + goto return_error; + } + + switch ( tag ) { + case LDAP_SUBSTRING_INITIAL: + usage = SLAP_MR_SUBSTR_INITIAL; + break; + + case LDAP_SUBSTRING_ANY: + usage = SLAP_MR_SUBSTR_ANY; + break; + + case LDAP_SUBSTRING_FINAL: + usage = SLAP_MR_SUBSTR_FINAL; + break; + + default: + rc = LDAP_PROTOCOL_ERROR; + +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_ERR, + "get_filter_substring: conn %d unknown substring choice=%ld\n", + conn->c_connid, (long)tag )); +#else + Debug( LDAP_DEBUG_FILTER, + " unknown substring choice=%ld\n", + (long) tag, 0, 0 ); +#endif + goto return_error; + } + + /* valiate using equality matching rule validator! */ + rc = value_validate( f->f_sub_desc->ad_type->sat_equality, + &value, text ); + if( rc != LDAP_SUCCESS ) { + goto return_error; + } + + rc = value_normalize( f->f_sub_desc, usage, + &value, &bv, text ); + if( rc != LDAP_SUCCESS ) { + goto return_error; + } + + value = bv; + + rc = LDAP_PROTOCOL_ERROR; + + switch ( tag ) { + case LDAP_SUBSTRING_INITIAL: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "get_substring_filter: conn %d INITIAL\n", + conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, " INITIAL\n", 0, 0, 0 ); +#endif + + if ( f->f_sub_initial.bv_val != NULL + || f->f_sub_any != NULL + || f->f_sub_final.bv_val != NULL ) + { + free( value.bv_val ); + goto return_error; + } + + f->f_sub_initial = value; + break; + + case LDAP_SUBSTRING_ANY: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "get_substring_filter: conn %d ANY\n", + conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, " ANY\n", 0, 0, 0 ); +#endif + + if ( f->f_sub_final.bv_val != NULL ) { + free( value.bv_val ); + goto return_error; + } + + ber_bvarray_add( &f->f_sub_any, &value ); + break; + + case LDAP_SUBSTRING_FINAL: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "get_substring_filter: conn %d FINAL\n", + conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, " FINAL\n", 0, 0, 0 ); +#endif + + if ( f->f_sub_final.bv_val != NULL ) { + free( value.bv_val ); + goto return_error; + } + + f->f_sub_final = value; + break; + + default: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_INFO, + "get_substring_filter: conn %d unknown substring type %ld\n", + conn->c_connid, (long)tag )); +#else + Debug( LDAP_DEBUG_FILTER, + " unknown substring type=%ld\n", + (long) tag, 0, 0 ); +#endif + + free( value.bv_val ); + +return_error: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_INFO, + "get_substring_filter: conn %d error %ld\n", + conn->c_connid, (long)rc )); +#else + Debug( LDAP_DEBUG_FILTER, " error=%ld\n", + (long) rc, 0, 0 ); +#endif + free( f->f_sub_initial.bv_val ); + ber_bvarray_free( f->f_sub_any ); + free( f->f_sub_final.bv_val ); + ch_free( f->f_sub ); + return rc; + } + } + +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY, + "get_substring_filter: conn %d exit\n", conn->c_connid )); +#else + Debug( LDAP_DEBUG_FILTER, "end get_substring_filter\n", 0, 0, 0 ); +#endif + return( LDAP_SUCCESS ); +} + diff --git a/servers/slapd/matchedValues.c b/servers/slapd/matchedValues.c new file mode 100644 index 0000000000..7a9b2d2ab2 --- /dev/null +++ b/servers/slapd/matchedValues.c @@ -0,0 +1,423 @@ +/* $OpenLDAP$ */ +/* + * Copyright 1999-2002 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted only + * as authorized by the OpenLDAP Public License. A copy of this + * license is available at http://www.OpenLDAP.org/license.html or + * in file LICENSE in the top-level directory of the distribution. + */ +#include "portable.h" + +#include + +#include +#include + +#include "slap.h" + +#include "../../libraries/liblber/lber-int.h" + +static int test_mra_vrFilter( + Backend *be, + Connection *conn, + Operation *op, + Entry *e, + MatchingRuleAssertion *mra, + char ***e_flags +); + +static int +test_substrings_vrFilter( + Backend *be, + Connection *conn, + Operation *op, + Entry *e, + ValuesReturnFilter *f, + char ***e_flags +); + +static int +test_presence_vrFilter( + Backend *be, + Connection *conn, + Operation *op, + Entry *e, + AttributeDescription *desc, + char ***e_flags +); + +static int +test_ava_vrFilter( + Backend *be, + Connection *conn, + Operation *op, + Entry *e, + AttributeAssertion *ava, + int type, + char ***e_flags +); + + +int +filter_matched_values( + Backend *be, + Connection *conn, + Operation *op, + Entry *e, + char ***e_flags +) +{ + ValuesReturnFilter *f; + Attribute *a; + struct berval *bv; + char filter_found; + int i, j; + int rc = LDAP_SUCCESS; + +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY, + "filter_matched_values: begin\n" )); +#else + Debug( LDAP_DEBUG_FILTER, "=> filter_matched_values\n", 0, 0, 0 ); +#endif + + for ( f = op->vrFilter; f != NULL; f = f->f_next ) { + switch ( f->f_choice ) { + case SLAPD_FILTER_COMPUTED: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "test_vrFilter: COMPUTED %s (%d)\n", + f->f_result == LDAP_COMPARE_FALSE ? "false" : + f->f_result == LDAP_COMPARE_TRUE ? "true" : + f->f_result == SLAPD_COMPARE_UNDEFINED ? "undefined" : + "error", + f->f_result )); +#else + Debug( LDAP_DEBUG_FILTER, " COMPUTED %s (%d)\n", + f->f_result == LDAP_COMPARE_FALSE ? "false" : + f->f_result == LDAP_COMPARE_TRUE ? "true" : + f->f_result == SLAPD_COMPARE_UNDEFINED ? "undefined" : "error", + f->f_result, 0 ); +#endif + /*This type of filter does not affect the result */ + rc = LDAP_SUCCESS; + break; + + case LDAP_FILTER_EQUALITY: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "test_vrFilter: EQUALITY\n" )); +#else + Debug( LDAP_DEBUG_FILTER, " EQUALITY\n", 0, 0, 0 ); +#endif + rc = test_ava_vrFilter( be, conn, op, e, f->f_ava, + LDAP_FILTER_EQUALITY, e_flags ); + if( rc == -1 ) { + return rc; + } + break; + + case LDAP_FILTER_SUBSTRINGS: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "test_vrFilter SUBSTRINGS\n" )); +#else + Debug( LDAP_DEBUG_FILTER, " SUBSTRINGS\n", 0, 0, 0 ); +#endif + + rc = test_substrings_vrFilter( be, conn, op, e, + f, e_flags ); + if( rc == -1 ) { + return rc; + } + break; + + case LDAP_FILTER_PRESENT: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "test_vrFilter: PRESENT\n" )); +#else + Debug( LDAP_DEBUG_FILTER, " PRESENT\n", 0, 0, 0 ); +#endif + rc = test_presence_vrFilter( be, conn, op, e, + f->f_desc, e_flags ); + if( rc == -1 ) { + return rc; + } + break; + + case LDAP_FILTER_GE: + rc = test_ava_vrFilter( be, conn, op, e, f->f_ava, + LDAP_FILTER_GE, e_flags ); + if( rc == -1 ) { + return rc; + } + break; + + case LDAP_FILTER_LE: + rc = test_ava_vrFilter( be, conn, op, e, f->f_ava, + LDAP_FILTER_LE, e_flags ); + if( rc == -1 ) { + return rc; + } + break; + + case LDAP_FILTER_EXT: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_DETAIL1, + "test_vrFilter: EXT\n" )); +#else + Debug( LDAP_DEBUG_FILTER, " EXT\n", 0, 0, 0 ); +#endif + rc = test_mra_vrFilter( be, conn, op, e, + f->f_mra, e_flags ); + if( rc == -1 ) { + return rc; + } + break; + + default: +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_INFO, + "test_vrFilter: unknown filter type %lu\n", + f->f_choice )); +#else + Debug( LDAP_DEBUG_ANY, " unknown filter type %lu\n", + f->f_choice, 0, 0 ); +#endif + rc = LDAP_PROTOCOL_ERROR; + } + } + +#ifdef NEW_LOGGING + LDAP_LOG(( "filter", LDAP_LEVEL_ENTRY, + "filter_matched_values: return=%d\n", rc )); +#else + Debug( LDAP_DEBUG_FILTER, "<= filter_matched_values %d\n", rc, 0, 0 ); +#endif + return( rc ); +} + +static int +test_ava_vrFilter( + Backend *be, + Connection *conn, + Operation *op, + Entry *e, + AttributeAssertion *ava, + int type, + char ***e_flags +) +{ + int rc; + int i, j; + Attribute *a; + + if ( !access_allowed( be, conn, op, e, + ava->aa_desc, &ava->aa_value, ACL_SEARCH, NULL ) ) + { + return LDAP_INSUFFICIENT_ACCESS; + } + + for (a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) { + + MatchingRule *mr; + struct berval *bv; + + if ( !is_ad_subtype( a->a_desc, ava->aa_desc ) ) { + continue; + } + + switch ( type ) { + case LDAP_FILTER_APPROX: + mr = a->a_desc->ad_type->sat_approx; + if( mr != NULL ) break; + + /* use EQUALITY matching rule if no APPROX rule */ + case LDAP_FILTER_EQUALITY: + mr = a->a_desc->ad_type->sat_equality; + break; + + case LDAP_FILTER_GE: + case LDAP_FILTER_LE: + mr = a->a_desc->ad_type->sat_ordering; + break; + + default: + mr = NULL; + } + + if( mr == NULL ) { + continue; + + } + + for ( bv = a->a_vals, j=0; bv->bv_val != NULL; bv++, j++ ) { + int ret; + int rc; + const char *text; + + rc = value_match( &ret, a->a_desc, mr, + SLAP_MR_ASSERTION_SYNTAX_MATCH, bv, &ava->aa_value, &text ); + if( rc != LDAP_SUCCESS ) { + return rc; + } + + switch ( type ) { + case LDAP_FILTER_EQUALITY: + case LDAP_FILTER_APPROX: + if ( ret == 0 ) { + (*e_flags)[i][j] = 1; + } + break; + + case LDAP_FILTER_GE: + if ( ret >= 0 ) { + (*e_flags)[i][j] = 1; + } + break; + + case LDAP_FILTER_LE: + if ( ret <= 0 ) { + (*e_flags)[i][j] = 1; + } + break; + } + } + } + return( LDAP_SUCCESS ); +} + +static int +test_presence_vrFilter( + Backend *be, + Connection *conn, + Operation *op, + Entry *e, + AttributeDescription *desc, + char ***e_flags +) +{ + int i, j; + Attribute *a; + + if ( !access_allowed( be, conn, op, e, desc, NULL, ACL_SEARCH, NULL ) ) { + return LDAP_INSUFFICIENT_ACCESS; + } + + for (a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) { + struct berval *bv; + + if ( !is_ad_subtype( a->a_desc, desc ) ) { + continue; + } + + for ( bv = a->a_vals, j=0; bv->bv_val != NULL; bv++, j++ ); + memset( (*e_flags)[i], 1, j); + } + + return( LDAP_SUCCESS ); +} + +static int +test_substrings_vrFilter( + Backend *be, + Connection *conn, + Operation *op, + Entry *e, + ValuesReturnFilter *f, + char ***e_flags +) +{ + int i, j; + Attribute *a; + + if ( !access_allowed( be, conn, op, e, + f->f_sub_desc, NULL, ACL_SEARCH, NULL ) ) + { + return LDAP_INSUFFICIENT_ACCESS; + } + + for (a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) { + MatchingRule *mr = a->a_desc->ad_type->sat_substr; + struct berval *bv; + + if ( !is_ad_subtype( a->a_desc, f->f_sub_desc ) ) { + continue; + } + + if( mr == NULL ) { + continue; + } + + for ( bv = a->a_vals, j = 0; bv->bv_val != NULL; bv++, j++ ) { + int ret; + int rc; + const char *text; + + rc = value_match( &ret, a->a_desc, mr, + SLAP_MR_ASSERTION_SYNTAX_MATCH, + bv, f->f_sub, &text ); + + if( rc != LDAP_SUCCESS ) { + return rc; + } + + if ( ret == 0 ) { + (*e_flags)[i][j] = 1; + } + } + } + + return LDAP_SUCCESS; +} + +static int test_mra_vrFilter( + Backend *be, + Connection *conn, + Operation *op, + Entry *e, + MatchingRuleAssertion *mra, + char ***e_flags +) +{ + int i, j; + Attribute *a; + + if( !access_allowed( be, conn, op, e, + mra->ma_desc, &mra->ma_value, ACL_SEARCH, NULL ) ) + { + return LDAP_INSUFFICIENT_ACCESS; + } + + for (a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) { + struct berval *bv; + + if ( !is_ad_subtype( a->a_desc, mra->ma_desc ) ) { + return( LDAP_SUCCESS ); + } + + for ( bv = a->a_vals, j = 0; bv->bv_val != NULL; bv++, j++ ) { + int ret; + int rc; + const char *text; + + rc = value_match( &ret, a->a_desc, mra->ma_rule, + SLAP_MR_ASSERTION_SYNTAX_MATCH, + bv, &mra->ma_value, + &text ); + + if( rc != LDAP_SUCCESS ) { + return rc; + } + + if ( ret ) { + (*e_flags)[i][j] = 1; + } + } + } + + return LDAP_SUCCESS; +} diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index c3e04319f5..5f469ac84a 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -464,6 +464,14 @@ LDAP_SLAPD_F (int) get_filter LDAP_P(( LDAP_SLAPD_F (void) filter_free LDAP_P(( Filter *f )); LDAP_SLAPD_F (void) filter2bv LDAP_P(( Filter *f, struct berval *bv )); +LDAP_SLAPD_F (int) get_vrFilter( Connection *conn, BerElement *ber, + ValuesReturnFilter **f, + const char **text ); + +LDAP_SLAPD_F (void) vrFilter_free( ValuesReturnFilter *f ); +LDAP_SLAPD_F (void) vrFilter2bv( ValuesReturnFilter *f, struct berval *fstr ); + + /* * filterentry.c */ @@ -511,6 +519,16 @@ LDAP_SLAPD_F (FILE *) lock_fopen LDAP_P(( const char *fname, const char *type, FILE **lfp )); LDAP_SLAPD_F (int) lock_fclose LDAP_P(( FILE *fp, FILE *lfp )); +/* + * matchedValues.c + */ +LDAP_SLAPD_F (int) filter_matched_values( + Backend *be, + Connection *conn, + Operation *op, + Entry *e, + char ***e_flags ); + /* * modify.c */ diff --git a/servers/slapd/result.c b/servers/slapd/result.c index 4343f90794..1f88150e9a 100644 --- a/servers/slapd/result.c +++ b/servers/slapd/result.c @@ -628,7 +628,7 @@ send_search_entry( char berbuf[256]; BerElement *ber = (BerElement *)berbuf; Attribute *a, *aa; - int i, rc=-1, bytes; + int i, j, rc=-1, bytes; char *edn; int userattrs; int opattrs; @@ -637,6 +637,12 @@ send_search_entry( AttributeDescription *ad_entry = slap_schema.si_ad_entry; + /* a_flags: array of flags telling if the i-th element will be + * returned or filtered out + * e_flags: array of a_flags + */ + char *a_flags, **e_flags; + if (op->o_callback && op->o_callback->sc_sendentry) { return op->o_callback->sc_sendentry( be, conn, op, e, attrs, attrsonly, ctrls ); @@ -721,7 +727,52 @@ send_search_entry( opattrs = ( attrs == NULL ) ? 0 : an_find( attrs, &AllOper ); - for ( a = e->e_attrs; a != NULL; a = a->a_next ) { + /* create an array of arrays of flags. Each flag corresponds + * to particular value of attribute an equals 1 if value matches + * to ValuesReturnFilter or 0 if not + */ + for ( a = e->e_attrs; a != NULL; a = a->a_next ) i++; + e_flags = ch_malloc ( i * sizeof(a_flags) ); + + for ( a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) { + for ( j = 0; a->a_vals[j].bv_val != NULL; j++ ); + + a_flags = ch_calloc ( j, sizeof(char) ); + /* If no ValuesReturnFilter control return everything */ + if ( op->vrFilter == NULL ){ + memset(a_flags, 1, j); + } + e_flags[i] = a_flags; + } + + if ( op->vrFilter != NULL ){ + + rc = filter_matched_values(be, conn, op, e, &e_flags) ; + + if ( rc == -1 ) { +#ifdef NEW_LOGGING + LDAP_LOG(( "operation", LDAP_LEVEL_ERR, + "send_search_entry: conn %d matched values fitering failed\n", + conn ? conn->c_connid : 0 )); +#else + Debug( LDAP_DEBUG_ANY, + "matched values fitering failed\n", 0, 0, 0 ); +#endif + ber_free( ber, 1 ); + + /* free e_flags */ + for ( a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) { + free( e_flags[i] ); + } + free( e_flags ); + + send_ldap_result( conn, op, LDAP_OTHER, + NULL, "matched values fitering error", NULL, NULL ); + goto error_return; + } + } + + for ( a = e->e_attrs, j = 0; a != NULL; a = a->a_next, j++ ) { AttributeDescription *desc = a->a_desc; if ( attrs == NULL ) { @@ -771,6 +822,11 @@ send_search_entry( #endif ber_free_buf( ber ); + /* free e_flags */ + for ( a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) { + free( e_flags[i] ); + } + free( e_flags ); send_ldap_result( conn, op, LDAP_OTHER, NULL, "encoding description error", NULL, NULL ); goto error_return; @@ -795,6 +851,10 @@ send_search_entry( continue; } + if ( e_flags[j][i] == 0 ){ + continue; + } + if (( rc = ber_printf( ber, "O", &a->a_vals[i] )) == -1 ) { #ifdef NEW_LOGGING LDAP_LOG(( "operation", LDAP_LEVEL_ERR, @@ -806,6 +866,11 @@ send_search_entry( #endif ber_free_buf( ber ); + /* free e_flags */ + for ( a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) { + free( e_flags[i] ); + } + free( e_flags ); send_ldap_result( conn, op, LDAP_OTHER, NULL, "encoding values error", NULL, NULL ); goto error_return; @@ -823,12 +888,23 @@ send_search_entry( #endif ber_free_buf( ber ); + /* free e_flags */ + for ( a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) { + free( e_flags[i] ); + } + free( e_flags ); send_ldap_result( conn, op, LDAP_OTHER, NULL, "encode end error", NULL, NULL ); goto error_return; } } + /* free e_flags */ + for ( a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) { + free( e_flags[i] ); + } + free( e_flags ); + /* eventually will loop through generated operational attributes */ /* only have subschemaSubentry implemented */ aa = backend_operational( be, conn, op, e, attrs, opattrs ); diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index ddd7c9b048..6042e3e808 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -723,6 +723,32 @@ typedef struct slap_filter { /* compare routines can return undefined */ #define SLAPD_COMPARE_UNDEFINED ((ber_int_t) -1) +typedef struct slap_valuesreturnfilter { + ber_tag_t f_choice; + + union vrf_un_u { + /* precomputed result */ + ber_int_t f_un_result; + + /* DN */ + char *f_un_dn; + + /* present */ + AttributeDescription *f_un_desc; + + /* simple value assertion */ + AttributeAssertion *f_un_ava; + + /* substring assertion */ + SubstringsAssertion *f_un_ssa; + + /* matching rule assertion */ + MatchingRuleAssertion *f_un_mra; + } f_un; + + struct slap_valuesreturnfilter *f_next; +} ValuesReturnFilter; + /* * represents an attribute (description + values) */ @@ -1453,6 +1479,7 @@ typedef struct slap_op { char o_noop; char o_subentries; char o_subentries_visibility; + char o_valuesreturnfilter; char o_pagedresults; ber_int_t o_pagedresults_size; @@ -1470,6 +1497,7 @@ typedef struct slap_op { void *o_private; /* anything the backend needs */ LDAP_STAILQ_ENTRY(slap_op) o_next; /* next operation in list */ + ValuesReturnFilter *vrFilter; /* Structure represents ValuesReturnFilter */ } Operation; #define get_manageDSAit(op) ((int)(op)->o_managedsait)