From 8c152396b9ff4fa0f2618435441550c1f12df461 Mon Sep 17 00:00:00 2001 From: Kurt Zeilenga Date: Thu, 2 May 2002 18:56:56 +0000 Subject: [PATCH] Matched Values implementation (ITS#1776) based upon submission form Mikhail Sahalaev . Further work needed: add testxxx-matchedvalues rework ldapsearch(1) portion of patch to generalize use of options --- Copyright 2001, Mikhail Sahalaev, All rights reserved. This software is not subject to any license of University Of Salford. Redistribution and use in source and binary forms are permitted without restriction or fee of any kind as long as this notice is preserved. --- include/ldap.h | 2 + libraries/libldap/filter.c | 392 ++++++++++++++++ servers/slapd/Makefile.in | 4 +- servers/slapd/back-bdb/init.c | 3 + servers/slapd/back-dnssrv/init.c | 1 + servers/slapd/back-ldbm/init.c | 1 + servers/slapd/back-monitor/init.c | 1 + servers/slapd/connection.c | 2 + servers/slapd/controls.c | 64 +++ servers/slapd/filter.c | 727 ++++++++++++++++++++++++++++++ servers/slapd/matchedValues.c | 423 +++++++++++++++++ servers/slapd/proto-slap.h | 18 + servers/slapd/result.c | 80 +++- servers/slapd/slap.h | 28 ++ 14 files changed, 1742 insertions(+), 4 deletions(-) create mode 100644 servers/slapd/matchedValues.c 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) -- 2.39.5