diff --git a/servers/slapd/back-bdb/search.c b/servers/slapd/back-bdb/search.c index 462d8ec027..7220226830 100644 --- a/servers/slapd/back-bdb/search.c +++ b/servers/slapd/back-bdb/search.c @@ -59,6 +59,10 @@ bdb_search( struct slap_limits_set *limit = NULL; int isroot = 0; +#ifdef SLAP_X_FILTER_HASSUBORDINATES + int filter_hasSubordinates = 0; +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ + u_int32_t locker; DB_LOCK lock; @@ -322,11 +326,22 @@ dn2entry_retry: } } +#ifdef SLAP_X_FILTER_HASSUBORDINATES + /* + * is hasSubordinates used in the filter ? + * FIXME: we may compute this directly when parsing the filter + */ + filter_hasSubordinates = filter_has_subordinates( filter ); +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ + for ( id = bdb_idl_first( candidates, &cursor ); id != NOID; id = bdb_idl_next( candidates, &cursor ) ) { int scopeok = 0; +#ifdef SLAP_X_FILTER_HASSUBORDINATES + Attribute *hasSubordinates = NULL; +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ /* check for abandon */ if ( op->o_abandon ) { @@ -490,8 +505,47 @@ id2entry_retry: goto loop_continue; } +#ifdef SLAP_X_FILTER_HASSUBORDINATES + /* + * if hasSubordinates is used in the filter, + * append it to the entry's attributes + */ + if ( filter_hasSubordinates ) { + int hs; + + rc = bdb_hasSubordinates( be, conn, op, e, &hs); + if ( rc != LDAP_SUCCESS ) { + goto loop_continue; + } + + hasSubordinates = slap_operational_hasSubordinate( hs == LDAP_COMPARE_TRUE ); + if ( hasSubordinates == NULL ) { + goto loop_continue; + } + + hasSubordinates->a_next = e->e_attrs; + e->e_attrs = hasSubordinates; + } +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ + /* if it matches the filter and scope, send it */ rc = test_filter( be, conn, op, e, filter ); + +#ifdef SLAP_X_FILTER_HASSUBORDINATES + if ( hasSubordinates ) { + /* + * FIXME: this is fairly inefficient, because + * if hasSubordinates is among the required + * attrs, it will be added again later; + * maybe we should leave it and check + * check later if it's already present, + * if required + */ + e->e_attrs = e->e_attrs->a_next; + attr_free( hasSubordinates ); + } +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ + if ( rc == LDAP_COMPARE_TRUE ) { struct berval dn; @@ -557,8 +611,8 @@ id2entry_retry: } } else { #ifdef NEW_LOGGING - LDAP_LOG ( OPERATION, RESULTS, - "bdb_search: %ld does match filter\n", (long) id, 0, 0); + LDAP_LOG ( OPERATION, RESULTS, + "bdb_search: %ld does match filter\n", (long) id, 0, 0); #else Debug( LDAP_DEBUG_TRACE, "bdb_search: %ld does match filter\n", @@ -774,3 +828,4 @@ static int search_candidates( return rc; } + diff --git a/servers/slapd/back-ldbm/search.c b/servers/slapd/back-ldbm/search.c index 87f4b9278b..eac5f831e9 100644 --- a/servers/slapd/back-ldbm/search.c +++ b/servers/slapd/back-ldbm/search.c @@ -57,6 +57,10 @@ ldbm_back_search( struct slap_limits_set *limit = NULL; int isroot = 0; +#ifdef SLAP_X_FILTER_HASSUBORDINATES + int filter_hasSubordinates = 0; +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ + #ifdef NEW_LOGGING LDAP_LOG( BACK_LDBM, ENTRY, "ldbm_back_search: enter\n", 0, 0, 0 ); #else @@ -288,10 +292,22 @@ searchit: /* compute it anyway; root does not use it */ stoptime = op->o_time + tlimit; +#ifdef SLAP_X_FILTER_HASSUBORDINATES + /* + * is hasSubordinates used in the filter ? + * FIXME: we may compute this directly when parsing the filter + */ + filter_hasSubordinates = filter_has_subordinates( filter ); +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ + for ( id = idl_firstid( candidates, &cursor ); id != NOID; id = idl_nextid( candidates, &cursor ) ) { int scopeok = 0; + int result = 0; +#ifdef SLAP_X_FILTER_HASSUBORDINATES + Attribute *hasSubordinates = NULL; +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ /* check for abandon */ if ( op->o_abandon ) { @@ -420,8 +436,44 @@ searchit: goto loop_continue; } +#ifdef SLAP_X_FILTER_HASSUBORDINATES + /* + * if hasSubordinates is used in the filter, + * append it to the entry's attributes + */ + if ( filter_hasSubordinates ) { + int hs; + + hs = has_children( be, e ); + hasSubordinates = slap_operational_hasSubordinate( hs ); + if ( hasSubordinates == NULL ) { + goto loop_continue; + } + + hasSubordinates->a_next = e->e_attrs; + e->e_attrs = hasSubordinates; + } +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ + /* if it matches the filter and scope, send it */ - if ( test_filter( be, conn, op, e, filter ) == LDAP_COMPARE_TRUE ) { + result = test_filter( be, conn, op, e, filter ); + +#ifdef SLAP_X_FILTER_HASSUBORDINATES + if ( hasSubordinates ) { + /* + * FIXME: this is fairly inefficient, because + * if hasSubordinates is among the required + * attrs, it will be added again later; + * maybe we should leave it and check + * check later if it's already present, + * if required + */ + e->e_attrs = e->e_attrs->a_next; + attr_free( hasSubordinates ); + } +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ + + if ( result == LDAP_COMPARE_TRUE ) { struct berval dn; /* check scope */ @@ -452,7 +504,7 @@ searchit: } if (e) { - int result = send_search_entry(be, conn, op, + result = send_search_entry(be, conn, op, e, attrs, attrsonly, NULL); switch (result) { diff --git a/servers/slapd/back-sql/back-sql.h b/servers/slapd/back-sql/back-sql.h index a456351479..2fb5508670 100644 --- a/servers/slapd/back-sql/back-sql.h +++ b/servers/slapd/back-sql/back-sql.h @@ -49,6 +49,10 @@ * statements (needed by PostgreSQL) * - upper_needs_cast cast the argument of upper when required * (basically when building dn substring queries) + * - added noop control + * - added values return filter control + * - hasSubordinate can be used in search filters (with limitations) + * - eliminated oc->name; use oc->oc->soc_cname instead * * Todo: * - add security checks for SQL statements that can be injected (?) @@ -80,7 +84,6 @@ */ #undef BACKSQL_TRACE - typedef struct { char *dbhost; int dbport; diff --git a/servers/slapd/back-sql/entry-id.c b/servers/slapd/back-sql/entry-id.c index 90564463ac..e1a45c7f1e 100644 --- a/servers/slapd/back-sql/entry-id.c +++ b/servers/slapd/back-sql/entry-id.c @@ -14,6 +14,7 @@ #include #include #include "ac/string.h" +#include "lber_pvt.h" #include "ldap_pvt.h" #include "slap.h" #include "back-sql.h" @@ -255,8 +256,7 @@ backsql_get_attr_vals( backsql_at_map_rec *at, backsql_srch_info *bsi ) Debug( LDAP_DEBUG_TRACE, "==>backsql_get_attr_vals(): " "oc='%s' attr='%s' keyval=%ld\n", - // bsi->oc->name.bv_val, at->name.bv_val, - bsi->oc->oc->soc_names[0], at->ad->ad_cname.bv_val, + BACKSQL_OC_NAME( bsi->oc ), at->ad->ad_cname.bv_val, bsi->c_eid->keyval ); rc = backsql_Prepare( bsi->dbh, &sth, at->query, 0 ); @@ -363,7 +363,7 @@ backsql_id2entry( backsql_srch_info *bsi, Entry *e, backsql_entryID *eid ) #if 0 backsql_entry_addattr( bsi->e, &bv_n_objectclass, - &bsi->oc->name ); + BACKSQL_OC_NAME( bsi->oc ) ); #endif continue; } @@ -376,7 +376,7 @@ backsql_id2entry( backsql_srch_info *bsi, Entry *e, backsql_entryID *eid ) "attribute '%s' is not defined " "for objectlass '%s'\n", attr->an_name.bv_val, - bsi->oc->name.bv_val, 0 ); + BACKSQL_OC_NAME( bsi->oc ), 0 ); } } @@ -387,7 +387,7 @@ backsql_id2entry( backsql_srch_info *bsi, Entry *e, backsql_entryID *eid ) bsi, 0, AVL_INORDER ); } - if ( attr_merge_one( bsi->e, ad_oc, &bsi->oc->name ) ) { + if ( attr_merge_one( bsi->e, ad_oc, &bsi->oc->oc->soc_cname ) ) { entry_free( e ); return NULL; } @@ -396,7 +396,7 @@ backsql_id2entry( backsql_srch_info *bsi, Entry *e, backsql_entryID *eid ) const char *text = NULL; char textbuf[ 1024 ]; size_t textlen = sizeof( textbuf ); - struct berval bv[ 2 ] = { bsi->oc->name, { 0, NULL } }; + struct berval bv[ 2 ] = { bsi->oc->oc->soc_cname, BER_BVNULL }; struct berval soc; AttributeDescription *ad_soc = slap_schema.si_ad_structuralObjectClass; @@ -408,7 +408,7 @@ backsql_id2entry( backsql_srch_info *bsi, Entry *e, backsql_entryID *eid ) return NULL; } - if ( bsi->attr_flags | BSQL_SF_ALL_OPER + if ( bsi->bsi_flags | BSQL_SF_ALL_OPER || an_find( bsi->attrs, &AllOper ) ) { if ( attr_merge_one( bsi->e, ad_soc, &soc ) ) { entry_free( e ); diff --git a/servers/slapd/back-sql/init.c b/servers/slapd/back-sql/init.c index 3d4e30242f..301646ccda 100644 --- a/servers/slapd/back-sql/init.c +++ b/servers/slapd/back-sql/init.c @@ -43,6 +43,18 @@ int sql_back_initialize( BackendInfo *bi ) { + static char *controls[] = { +#ifdef LDAP_CONTROL_NOOP + LDAP_CONTROL_NOOP, +#endif +#ifdef LDAP_CONTROL_VALUESRETURNFILTER + LDAP_CONTROL_VALUESRETURNFILTER, +#endif + NULL + }; + + bi->bi_controls = controls; + Debug( LDAP_DEBUG_TRACE,"==>backsql_initialize()\n", 0, 0, 0 ); bi->bi_open = 0; diff --git a/servers/slapd/back-sql/modify.c b/servers/slapd/back-sql/modify.c index 6966c2c6fd..bff2f9f8ed 100644 --- a/servers/slapd/back-sql/modify.c +++ b/servers/slapd/back-sql/modify.c @@ -549,9 +549,10 @@ backsql_modify( * or if a single operation on an attribute fails * for any reason */ - SQLTransact( SQL_NULL_HENV, dbh, SQL_COMMIT ); + SQLTransact( SQL_NULL_HENV, dbh, + op->o_noop ? SQL_ROLLBACK : SQL_COMMIT ); } - send_ldap_result( conn, op, res, "", text, NULL, NULL ); + send_ldap_result( conn, op, res, NULL, text, NULL, NULL ); Debug( LDAP_DEBUG_TRACE, "<==backsql_modify()\n", 0, 0, 0 ); return 0; @@ -865,7 +866,8 @@ backsql_modrdn( * or if a single operation on an attribute fails for any * reason */ - SQLTransact( SQL_NULL_HENV, dbh, SQL_COMMIT ); + SQLTransact( SQL_NULL_HENV, dbh, + op->o_noop ? SQL_ROLLBACK : SQL_COMMIT ); } modrdn_return: @@ -1239,7 +1241,7 @@ backsql_add( "attribute '%s' is not registered " "in objectclass '%s'\n", at->a_desc->ad_cname.bv_val, - oc->name.bv_val, 0 ); + BACKSQL_OC_NAME( oc ), 0 ); if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { send_ldap_result( conn, op, @@ -1312,7 +1314,7 @@ backsql_add( * to build the entry */ if ( at->a_desc == slap_schema.si_ad_objectClass ) { - if ( ber_bvcmp( at_val, &oc->name ) == 0 ) { + if ( bvmatch( at_val, &oc->oc->soc_cname ) ) { continue; } } @@ -1411,7 +1413,8 @@ backsql_add( * or if a single operation on an attribute fails * for any reason */ - SQLTransact( SQL_NULL_HENV, dbh, SQL_COMMIT ); + SQLTransact( SQL_NULL_HENV, dbh, + op->o_noop ? SQL_ROLLBACK : SQL_COMMIT ); send_ldap_result( conn, op, LDAP_SUCCESS, "", NULL, NULL, NULL ); @@ -1570,7 +1573,8 @@ backsql_delete( * or if a single operation on an attribute fails * for any reason */ - SQLTransact( SQL_NULL_HENV, dbh, SQL_COMMIT ); + SQLTransact( SQL_NULL_HENV, dbh, + op->o_noop ? SQL_ROLLBACK : SQL_COMMIT ); send_ldap_result( conn, op, LDAP_SUCCESS, "", NULL, NULL, NULL ); Debug( LDAP_DEBUG_TRACE, "<==backsql_delete()\n", 0, 0, 0 ); diff --git a/servers/slapd/back-sql/other.c b/servers/slapd/back-sql/other.c index 1629a086e3..b2638960f4 100644 --- a/servers/slapd/back-sql/other.c +++ b/servers/slapd/back-sql/other.c @@ -63,13 +63,18 @@ backsql_operational( backsql_info *bi = (backsql_info*)be->be_private; SQLHDBC dbh = SQL_NULL_HDBC; Attribute **aa = a; - int rc; + int rc = 0; Debug( LDAP_DEBUG_TRACE, "==>backsql_operational(): entry '%s'\n", e->e_nname.bv_val, 0, 0 ); - if ( opattrs || ad_inlist( slap_schema.si_ad_hasSubordinates, attrs ) ) { + if ( ( opattrs || ad_inlist( slap_schema.si_ad_hasSubordinates, attrs ) ) +#ifdef SLAP_X_FILTER_HASSUBORDINATES + && attr_find( e->e_attrs, slap_schema.si_ad_hasSubordinates ) == NULL +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ + ) { + rc = backsql_get_db_conn( be, conn, &dbh ); if ( rc != LDAP_SUCCESS ) { goto no_connection; diff --git a/servers/slapd/back-sql/schema-map.c b/servers/slapd/back-sql/schema-map.c index eaa63ab692..d3a7956dcd 100644 --- a/servers/slapd/back-sql/schema-map.c +++ b/servers/slapd/back-sql/schema-map.c @@ -15,23 +15,13 @@ #include #include #include "slap.h" +#include "lber_pvt.h" #include "ldap_pvt.h" #include "back-sql.h" #include "sql-wrap.h" #include "schema-map.h" #include "util.h" -/* - * Deprecated - */ -#if 0 -static int -backsql_cmp_oc_name( backsql_oc_map_rec *m1, backsql_oc_map_rec *m2 ) -{ - return BACKSQL_NCMP( &m1->name, &m2->name ); -} -#endif - /* * Uses the pointer to the ObjectClass structure */ @@ -47,17 +37,6 @@ backsql_cmp_oc_id( backsql_oc_map_rec *m1, backsql_oc_map_rec *m2 ) return ( m1->id < m2->id ? -1 : ( m1->id > m2->id ? 1 : 0 ) ); } -/* - * Deprecated - */ -#if 0 -static int -backsql_cmp_attr_name( backsql_at_map_rec *m1, backsql_at_map_rec *m2 ) -{ - return BACKSQL_NCMP( &m1->name, &m2->name ); -} -#endif - /* * Uses the pointer to the AttributeDescription structure */ @@ -72,7 +51,7 @@ backsql_make_attr_query( backsql_oc_map_rec *oc_map, backsql_at_map_rec *at_map ) { - struct berval tmps = { 0, NULL }; + struct berval tmps = BER_BVNULL; ber_len_t tmpslen = 0; backsql_strfcat( &tmps, &tmpslen, "lblblblbcbl", @@ -250,12 +229,11 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh ) oc_map->id = strtol( oc_row.cols[ 0 ], NULL, 0 ); - ber_str2bv( oc_row.cols[ 1 ], 0, 1, &oc_map->name ); - oc_map->oc = oc_bvfind( &oc_map->name ); + oc_map->oc = oc_find( oc_row.cols[ 1 ] ); if ( oc_map->oc == NULL ) { Debug( LDAP_DEBUG_TRACE, "load_schema_map(): " "objectClass '%s' is not defined in schema\n", - oc_map->name.bv_val, 0, 0 ); + oc_row.cols[ 1 ], 0, 0 ); return LDAP_OTHER; /* undefined objectClass ? */ } @@ -288,7 +266,7 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh ) oc_id = oc_map->id; Debug( LDAP_DEBUG_TRACE, "load_schema_map(): " "objectClass '%s': keytbl='%s' keycol='%s'\n", - oc_map->name.bv_val, + BACKSQL_OC_NAME( oc_map ), oc_map->keytbl.bv_val, oc_map->keycol.bv_val ); if ( oc_map->create_proc ) { Debug( LDAP_DEBUG_TRACE, "create_proc='%s'\n", @@ -349,7 +327,7 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh ) "attribute '%s' for objectClass '%s' " "is not defined in schema: %s\n", at_map->ad->ad_cname.bv_val, - oc_map->name.bv_val, text ); + BACKSQL_OC_NAME( oc_map ), text ); return LDAP_CONSTRAINT_VIOLATION; } @@ -415,7 +393,8 @@ backsql_oc2oc( backsql_info *si, ObjectClass *oc ) #ifdef BACKSQL_TRACE if ( res != NULL ) { Debug( LDAP_DEBUG_TRACE, "<==backsql_oc2oc(): " - "found name='%s', id=%d\n", res->name, res->id, 0 ); + "found name='%s', id=%d\n", + BACKSQL_OC_NAME( res ), res->id, 0 ); } else { Debug( LDAP_DEBUG_TRACE, "<==backsql_oc2oc(): " "not found\n", 0, 0, 0 ); @@ -425,9 +404,6 @@ backsql_oc2oc( backsql_info *si, ObjectClass *oc ) return res; } -/* - * Deprecated - */ backsql_oc_map_rec * backsql_name2oc( backsql_info *si, struct berval *oc_name ) { @@ -449,7 +425,8 @@ backsql_name2oc( backsql_info *si, struct berval *oc_name ) #ifdef BACKSQL_TRACE if ( res != NULL ) { Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): " - "found name='%s', id=%d\n", res->name, res->id, 0 ); + "found name='%s', id=%d\n", + BACKSQL_OC_NAME( res ), res->id, 0 ); } else { Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): " "not found\n", 0, 0, 0 ); @@ -476,7 +453,8 @@ backsql_id2oc( backsql_info *si, unsigned long id ) #ifdef BACKSQL_TRACE if ( res != NULL ) { Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): " - "found name='%s', id=%d\n", res->name, res->id, 0 ); + "found name='%s', id=%d\n", + BACKSQL_OC_NAME( res ), res->id, 0 ); } else { Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): " "not found\n", 0, 0, 0 ); @@ -494,7 +472,7 @@ backsql_ad2at( backsql_oc_map_rec* objclass, AttributeDescription *ad ) #ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "==>backsql_ad2at(): " "searching for attribute '%s' for objectclass '%s'\n", - attr, objclass->name, 0 ); + attr, BACKSQL_OC_NAME( objclass ), 0 ); #endif /* BACKSQL_TRACE */ tmp.ad = ad; @@ -505,7 +483,7 @@ backsql_ad2at( backsql_oc_map_rec* objclass, AttributeDescription *ad ) if ( res != NULL ) { Debug( LDAP_DEBUG_TRACE, "<==backsql_ad2at(): " "found name='%s', sel_expr='%s'\n", - res->name, res->sel_expr.bv_val, 0 ); + res->ad->ad_cname.bv_val, res->sel_expr.bv_val, 0 ); } else { Debug( LDAP_DEBUG_TRACE, "<==backsql_ad2at(): " "not found\n", 0, 0, 0 ); @@ -527,7 +505,7 @@ backsql_name2at( backsql_oc_map_rec* objclass, struct berval *attr ) #ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "==>backsql_name2at(): " "searching for attribute '%s' for objectclass '%s'\n", - attr, objclass->name, 0 ); + attr, BACKSQL_OC_NAME( objclass ), 0 ); #endif /* BACKSQL_TRACE */ if ( slap_bv2ad( attr, &tmp.ad, &text ) != LDAP_SUCCESS ) { @@ -587,9 +565,8 @@ static void backsql_free_oc( backsql_oc_map_rec *oc ) { Debug( LDAP_DEBUG_TRACE, "==>free_oc(): '%s'\n", - oc->name.bv_val, 0, 0 ); + BACKSQL_OC_NAME( oc ), 0, 0 ); avl_free( oc->attrs, (AVL_FREE)backsql_free_attr ); - ch_free( oc->name.bv_val ); ch_free( oc->keytbl.bv_val ); ch_free( oc->keycol.bv_val ); if ( oc->create_proc != NULL ) { diff --git a/servers/slapd/back-sql/schema-map.h b/servers/slapd/back-sql/schema-map.h index 7c7f0cf39e..5467523fad 100644 --- a/servers/slapd/back-sql/schema-map.h +++ b/servers/slapd/back-sql/schema-map.h @@ -11,15 +11,12 @@ */ typedef struct { - /* - * FIXME: we explicitly keep the objectClass name because - * the ObjectClass structure does not use bervals (yet?) - */ - struct berval name; /* * Structure of corresponding LDAP objectClass definition */ ObjectClass *oc; +#define BACKSQL_OC_NAME(ocmap) ((ocmap)->oc->soc_cname.bv_val) + struct berval keytbl; struct berval keycol; /* expected to return keyval of newly created entry */ diff --git a/servers/slapd/back-sql/search.c b/servers/slapd/back-sql/search.c index ffd1856fd8..ed48753a08 100644 --- a/servers/slapd/back-sql/search.c +++ b/servers/slapd/back-sql/search.c @@ -23,6 +23,8 @@ #include "entry-id.h" #include "util.h" +static int backsql_process_filter( backsql_srch_info *bsi, Filter *f ); + static int backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad ) { @@ -33,6 +35,15 @@ backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad ) return 1; } + /* + * clear the list (retrieve all attrs) + */ + if ( ad == NULL ) { + ch_free( bsi->attrs ); + bsi->attrs = NULL; + return 1; + } + for ( ; bsi->attrs[ n_attrs ].an_name.bv_val; n_attrs++ ) { an = &bsi->attrs[ n_attrs ]; @@ -94,12 +105,8 @@ backsql_init_search( bsi->be = be; bsi->conn = conn; bsi->op = op; - bsi->attr_flags = 0; + bsi->bsi_flags = 0; - /* - * FIXME: need to discover how to deal with 1.1 (NoAttrs) - */ - /* * handle "*" */ @@ -114,13 +121,13 @@ backsql_init_search( for ( p = attrs; p->an_name.bv_val; p++ ) { /* - * ignore "+" + * ignore "1.1"; handle "+" */ if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) { + bsi->bsi_flags |= BSQL_SF_ALL_OPER; continue; } else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) { - bsi->attr_flags |= BSQL_SF_ALL_OPER; continue; } @@ -149,7 +156,7 @@ backsql_init_search( bsi->status = LDAP_SUCCESS; } -int +static int backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op ) { int res; @@ -195,7 +202,7 @@ backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op ) return 1; } -int +static int backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f ) { int i; @@ -301,13 +308,13 @@ backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f ) return 1; } -int +static int backsql_process_filter( backsql_srch_info *bsi, Filter *f ) { backsql_at_map_rec *at; backsql_at_map_rec oc_attr = { slap_schema.si_ad_objectClass, BER_BVC(""), BER_BVC(""), - { 0, NULL }, NULL, NULL, NULL }; + BER_BVNULL, NULL, NULL, NULL }; AttributeDescription *ad = NULL; int done = 0; ber_len_t len = 0; @@ -328,7 +335,7 @@ backsql_process_filter( backsql_srch_info *bsi, Filter *f ) case LDAP_FILTER_AND: rc = backsql_process_filter_list( bsi, f->f_and, - LDAP_FILTER_AND); + LDAP_FILTER_AND ); done = 1; break; @@ -346,6 +353,10 @@ backsql_process_filter( backsql_srch_info *bsi, Filter *f ) ad = f->f_desc; break; + case LDAP_FILTER_EXT: + ad = f->f_mra->ma_desc; + break; + default: ad = f->f_av_desc; break; @@ -360,23 +371,58 @@ backsql_process_filter( backsql_srch_info *bsi, Filter *f ) goto done; } - if ( ad != slap_schema.si_ad_objectClass ) { - at = backsql_ad2at( bsi->oc, ad ); - - } else { + /* + * Turn structuralObjectClass into objectClass + */ + if ( ad == slap_schema.si_ad_objectClass + || ad == slap_schema.si_ad_structuralObjectClass ) { at = &oc_attr; backsql_strfcat( &at->sel_expr, &len, "cbc", '\'', - &bsi->oc->name, + &bsi->oc->oc->soc_cname, '\'' ); + +#if defined(SLAP_X_FILTER_HASSUBORDINATES) || defined(SLAP_X_MRA_MATCH_DNATTRS) + } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) { + /* + * FIXME: this is not robust; e.g. a filter + * '(!(hasSubordinates=TRUE))' fails because + * in SQL it would read 'NOT (1=1)' instead + * of no condition. + * Note however that hasSubordinates is boolean, + * so a more appropriate filter would be + * '(hasSubordinates=FALSE)' + */ + backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l", + (ber_len_t)sizeof( "1=1" ) - 1, "1=1" ); + if ( ad != NULL ) { + /* + * We use this flag since we need to parse + * the filter anyway; we should have used + * the frontend API function + * filter_has_subordinates() + */ + bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE; + } else { + /* + * clear attributes to fetch, to require ALL + * and try extended match on all attributes + */ + backsql_attrlist_add( bsi, NULL ); + } + goto done; +#endif /* SLAP_X_FILTER_HASSUBORDINATES || SLAP_X_MRA_MATCH_DNATTRS */ + + } else { + at = backsql_ad2at( bsi->oc, ad ); } if ( at == NULL ) { Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): " "attribute '%s' is not defined for objectclass '%s'\n", - ad->ad_cname.bv_val, bsi->oc->name.bv_val, 0 ); + ad->ad_cname.bv_val, BACKSQL_OC_NAME( bsi->oc ), 0 ); backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l", - (ber_len_t)sizeof( " 1=0 " ) - 1, " 1=0 " ); + (ber_len_t)sizeof( "1=0" ) - 1, "1=0" ); goto impossible; } @@ -461,6 +507,9 @@ backsql_process_filter( backsql_srch_info *bsi, Filter *f ) break; case LDAP_FILTER_GE: + /* + * FIXME: should we uppercase the operands? + */ backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "cblbc", '(' /* ) */ , &at->sel_expr, @@ -470,6 +519,9 @@ backsql_process_filter( backsql_srch_info *bsi, Filter *f ) break; case LDAP_FILTER_LE: + /* + * FIXME: should we uppercase the operands? + */ backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "cblbc", '(' /* ) */ , &at->sel_expr, @@ -558,13 +610,13 @@ backsql_srch_query( backsql_srch_info *bsi, struct berval *query ) &bi->strcast_func, (ber_len_t)sizeof( "('" /* ') */ ) - 1, "('" /* ') */ , - &bsi->oc->name, + &bsi->oc->oc->soc_cname, (ber_len_t)sizeof( /* (' */ "')" ) - 1, /* (' */ "')" ); } else { backsql_strfcat( &bsi->sel, &bsi->sel_len, "cbc", '\'', - &bsi->oc->name, + &bsi->oc->oc->soc_cname, '\'' ); } backsql_strfcat( &bsi->sel, &bsi->sel_len, "l", @@ -669,7 +721,7 @@ backsql_oc_get_candidates( backsql_oc_map_rec *oc, backsql_srch_info *bsi ) int j; Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc='%s'\n", - oc->name.bv_val, 0, 0 ); + BACKSQL_OC_NAME( oc ), 0, 0 ); if ( bsi->n_candidates == -1 ) { Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " @@ -998,6 +1050,10 @@ backsql_search( */ for ( eid = srch_info.id_list; eid != NULL; eid = backsql_free_entryID( eid, 1 ) ) { +#ifdef SLAP_X_FILTER_HASSUBORDINATES + Attribute *hasSubordinate = NULL, + *a = NULL; +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ /* check for abandon */ if ( op->o_abandon ) { @@ -1035,10 +1091,68 @@ backsql_search( continue; } +#ifdef SLAP_X_FILTER_HASSUBORDINATES + /* + * We use this flag since we need to parse the filter + * anyway; we should have used the frontend API function + * filter_has_subordinates() + */ + if ( srch_info.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) { + int rc; + + rc = backsql_has_children( bi, dbh, &entry->e_nname ); + + switch( rc ) { + case LDAP_COMPARE_TRUE: + case LDAP_COMPARE_FALSE: + hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE ); + if ( hasSubordinate != NULL ) { + for ( a = entry->e_attrs; + a && a->a_next; + a = a->a_next ); + + a->a_next = hasSubordinate; + } + rc = 0; + break; + + default: + Debug(LDAP_DEBUG_TRACE, + "backsql_search(): " + "has_children failed( %d)\n", + rc, 0, 0 ); + rc = 1; + break; + } + + if ( rc ) { + continue; + } + } +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ + if ( test_filter( be, conn, op, entry, filter ) == LDAP_COMPARE_TRUE ) { - sres = send_search_entry( be, conn, op, entry, - attrs, attrsonly, NULL ); +#ifdef SLAP_X_FILTER_HASSUBORDINATES + if ( hasSubordinate && !( srch_info.bsi_flags & BSQL_SF_ALL_OPER ) + && !ad_inlist( slap_schema.si_ad_hasSubordinates, attrs ) ) { + a->a_next = NULL; + attr_free( hasSubordinate ); + hasSubordinate = NULL; + } +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ + +#if 0 /* noop is masked SLAP_CTRL_UPDATE */ + if ( op->o_noop ) { + sres = 0; + } else { +#endif + sres = send_search_entry( be, conn, op, entry, + attrs, attrsonly, NULL ); +#if 0 + } +#endif + switch ( sres ) { case 0: nentries++; diff --git a/servers/slapd/back-sql/util.c b/servers/slapd/back-sql/util.c index ffd10efc95..402d36fbab 100644 --- a/servers/slapd/back-sql/util.c +++ b/servers/slapd/back-sql/util.c @@ -273,7 +273,7 @@ char * backsql_get_table_spec( char **p ) { char *s, *q; - struct berval res = { 0, NULL }; + struct berval res = BER_BVNULL; ber_len_t res_len = 0; assert( p ); diff --git a/servers/slapd/back-sql/util.h b/servers/slapd/back-sql/util.h index a9f1042d03..06e38d5824 100644 --- a/servers/slapd/back-sql/util.h +++ b/servers/slapd/back-sql/util.h @@ -42,14 +42,14 @@ typedef struct backsql_srch_info { Connection *conn; Operation *op; AttributeName *attrs; - int attr_flags; -#define BSQL_SF_ALL_OPER 0x0001 + int bsi_flags; +#define BSQL_SF_ALL_OPER 0x0001 +#define BSQL_SF_FILTER_HASSUBORDINATE 0x0002 Entry *e; /* 1 if the db is TimesTen; 0 if it's not */ int use_reverse_dn; } backsql_srch_info; -int backsql_process_filter( backsql_srch_info *bsi, Filter *f ); void backsql_init_search( backsql_srch_info *bsi, backsql_info *bi, struct berval *nbase, int scope, int slimit, int tlimit, time_t stoptime, Filter *filter, SQLHDBC dbh, diff --git a/servers/slapd/dn.c b/servers/slapd/dn.c index 9194a9cb53..0084a7c80a 100644 --- a/servers/slapd/dn.c +++ b/servers/slapd/dn.c @@ -22,10 +22,6 @@ const struct berval slap_empty_bv = { 0, "" }; -#define SLAP_LDAPDN_PRETTY 0x1 - -#define SLAP_LDAPDN_MAXLEN 8192 - /* * The DN syntax-related functions take advantage of the dn representation * handling functions ldap_str2dn/ldap_dn2str. The latter are not schema- @@ -493,6 +489,60 @@ dnPretty2( return LDAP_SUCCESS; } +int +dnPrettyNormalDN( + Syntax *syntax, + struct berval *val, + LDAPDN **dn, + int flags ) +{ + assert( val ); + assert( dn ); + +#ifdef NEW_LOGGING + LDAP_LOG( OPERATION, ARGS, ">>> dn%sDN: <%s>\n", + flags == SLAP_LDAPDN_PRETTY ? "Pretty" : "Normal", + val->bv_val, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, ">>> dn%sDN: <%s>\n", + flags == SLAP_LDAPDN_PRETTY ? "Pretty" : "Normal", + val->bv_val, 0 ); +#endif + + if ( val->bv_len == 0 ) { + return LDAP_SUCCESS; + + } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) { + return LDAP_INVALID_SYNTAX; + + } else { + int rc; + + /* FIXME: should be liberal in what we accept */ + rc = ldap_bv2dn( val, dn, LDAP_DN_FORMAT_LDAP ); + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + + assert( strlen( val->bv_val ) == val->bv_len ); + + /* + * Schema-aware rewrite + */ + if ( LDAPDN_rewrite( *dn, flags ) != LDAP_SUCCESS ) { + ldap_dnfree( *dn ); + *dn = NULL; + return LDAP_INVALID_SYNTAX; + } + } + + Debug( LDAP_DEBUG_TRACE, "<<< dn%sDN\n", + flags == SLAP_LDAPDN_PRETTY ? "Pretty" : "Normal", + 0, 0 ); + + return LDAP_SUCCESS; +} + /* * Combination of both dnPretty and dnNormalize */ diff --git a/servers/slapd/filter.c b/servers/slapd/filter.c index 69a26bf34b..608b732c84 100644 --- a/servers/slapd/filter.c +++ b/servers/slapd/filter.c @@ -744,7 +744,7 @@ filter2bv( Filter *f, struct berval *fstr ) case LDAP_FILTER_EXT: filter_escape_value( &f->f_mr_value, &tmp ); - +#ifndef SLAP_X_MRA_MATCH_DNATTRS 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 ) + @@ -757,6 +757,31 @@ filter2bv( Filter *f, struct berval *fstr ) f->f_mr_rule_text.bv_len ? ":" : "", f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_val : "", tmp.bv_val ); +#else /* SLAP_X_MRA_MATCH_DNATTRS */ + { + struct berval ad; + + if ( f->f_mr_desc ) { + ad = f->f_mr_desc->ad_cname; + } else { + ad.bv_len = 0; + ad.bv_val = ""; + } + + fstr->bv_len = ad.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)", + ad.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 ); + } +#endif /* SLAP_X_MRA_MATCH_DNATTRS */ ber_memfree( tmp.bv_val ); break; @@ -1286,6 +1311,7 @@ simple_vrFilter2bv( ValuesReturnFilter *f, struct berval *fstr ) case LDAP_FILTER_EXT: filter_escape_value( &f->f_mr_value, &tmp ); +#ifndef SLAP_X_MRA_MATCH_DNATTRS 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 ) + @@ -1298,6 +1324,32 @@ simple_vrFilter2bv( ValuesReturnFilter *f, struct berval *fstr ) f->f_mr_rule_text.bv_len ? ":" : "", f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_val : "", tmp.bv_val ); +#else /* SLAP_X_MRA_MATCH_DNATTRS */ + { + struct berval ad; + + if ( f->f_mr_desc ) { + ad = f->f_mr_desc->ad_cname; + } else { + ad.bv_len = 0; + ad.bv_val = ""; + } + + fstr->bv_len = ad.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)", + ad.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 ); + } +#endif /* SLAP_X_MRA_MATCH_DNATTRS */ + ber_memfree( tmp.bv_val ); break; @@ -1514,3 +1566,92 @@ return_error: return( LDAP_SUCCESS ); } +#ifdef SLAP_X_FILTER_HASSUBORDINATES +static int filter_has_subordinates_list( + Filter *filter ); + +/* + * FIXME: we could detect the need to filter + * for hasSubordinates when parsing the filter ... + */ + +static int +filter_has_subordinates_list( + Filter *fl ) +{ + Filter *f; + + for ( f = fl; f != NULL; f = f->f_next ) { + int rc; + + rc = filter_has_subordinates( f ); + + if ( rc ) { + return rc; + } + } + + return 0; +} + +int +filter_has_subordinates( + Filter *f ) +{ + AttributeDescription *ad = NULL; + + switch ( f->f_choice ) { + case LDAP_FILTER_PRESENT: + ad = f->f_desc; + break; + + case LDAP_FILTER_EQUALITY: + case LDAP_FILTER_APPROX: + case LDAP_FILTER_GE: + case LDAP_FILTER_LE: + ad = f->f_ava->aa_desc; + break; + + case LDAP_FILTER_SUBSTRINGS: + ad = f->f_sub_desc; + break; + + case LDAP_FILTER_EXT: + /* could be null; however here it is harmless */ + ad = f->f_mra->ma_desc; + break; + + case LDAP_FILTER_NOT: + return filter_has_subordinates( f->f_not ); + + case LDAP_FILTER_AND: + return filter_has_subordinates_list( f->f_and ); + + case LDAP_FILTER_OR: + return filter_has_subordinates_list( f->f_or ); + + case SLAPD_FILTER_COMPUTED: + /* + * something wrong? + */ + return 0; + + default: + /* + * this means a new type of filter has been implemented, + * which is not handled yet in this function; we should + * issue a developer's warning, e.g. an assertion + */ + assert( 0 ); + return -1; + } + + if ( ad == slap_schema.si_ad_hasSubordinates ) { + return 1; + } + + return 0; +} + +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ + diff --git a/servers/slapd/filterentry.c b/servers/slapd/filterentry.c index 672915cb8c..111e258e61 100644 --- a/servers/slapd/filterentry.c +++ b/servers/slapd/filterentry.c @@ -215,37 +215,175 @@ static int test_mra_filter( { Attribute *a; +#ifndef SLAP_X_MRA_MATCH_DNATTRS if( !access_allowed( be, conn, op, e, mra->ma_desc, &mra->ma_value, ACL_SEARCH, NULL ) ) { return LDAP_INSUFFICIENT_ACCESS; } +#else /* SLAP_X_MRA_MATCH_DNATTRS */ + if ( mra->ma_desc ) { + /* + * if ma_desc is available, then we're filtering for + * one attribute, and SEARCH permissions can be checked + * directly. + */ + if( !access_allowed( be, conn, op, e, + mra->ma_desc, &mra->ma_value, ACL_SEARCH, NULL ) ) + { + return LDAP_INSUFFICIENT_ACCESS; + } +#endif /* SLAP_X_MRA_MATCH_DNATTRS */ - for(a = attrs_find( e->e_attrs, mra->ma_desc ); - a != NULL; - a = attrs_find( a->a_next, mra->ma_desc ) ) - { - struct berval *bv; - for ( bv = a->a_vals; bv->bv_val != NULL; bv++ ) { - int ret; - int rc; - const char *text; + for(a = attrs_find( e->e_attrs, mra->ma_desc ); + a != NULL; + a = attrs_find( a->a_next, mra->ma_desc ) ) + { + struct berval *bv; + for ( bv = a->a_vals; bv->bv_val != NULL; bv++ ) { + 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 == 0 ) { + return LDAP_COMPARE_TRUE; + } + } + } +#ifdef SLAP_X_MRA_MATCH_DNATTRS + } else { - rc = value_match( &ret, a->a_desc, mra->ma_rule, - SLAP_MR_ASSERTION_SYNTAX_MATCH, - bv, &mra->ma_value, - &text ); + /* + * No attribute description: test all + */ + for ( a = e->e_attrs; a != NULL; a = a->a_next ) { + struct berval *bv, value; + const char *text = NULL; + int rc; - if( rc != LDAP_SUCCESS ) { - return rc; + /* check if matching is appropriate */ + if ( strcmp( mra->ma_rule->smr_syntax->ssyn_oid, + a->a_desc->ad_type->sat_syntax->ssyn_oid ) != 0 ) { + continue; } - if ( ret == 0 ) { - return LDAP_COMPARE_TRUE; + /* normalize for equality */ + rc = value_validate_normalize( a->a_desc, + SLAP_MR_EQUALITY, + &mra->ma_value, &value, &text ); + if ( rc != LDAP_SUCCESS ) { + continue; + } + + /* check search access */ + if ( !access_allowed( be, conn, op, e, + a->a_desc, &value, ACL_SEARCH, NULL ) ) { + continue; + } + + /* check match */ + for ( bv = a->a_vals; bv->bv_val != NULL; bv++ ) { + int ret; + int rc; + + rc = value_match( &ret, a->a_desc, mra->ma_rule, + SLAP_MR_ASSERTION_SYNTAX_MATCH, + bv, &value, &text ); + + if( rc != LDAP_SUCCESS ) { + return rc; + } + + if ( ret == 0 ) { + return LDAP_COMPARE_TRUE; + } } } } + /* check attrs in DN AVAs if required */ + if ( mra->ma_dnattrs ) { + LDAPDN *dn = NULL; + int iRDN, iAVA; + int rc; + + /* parse and pretty the dn */ + rc = dnPrettyDN( NULL, &e->e_name, &dn ); + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + + /* for each AVA of each RDN ... */ + for ( iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) { + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + + for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ 0 ][ iAVA ]; + struct berval *bv = &ava->la_value, value; + AttributeDescription *ad = (AttributeDescription *)ava->la_private; + int ret; + int rc; + const char *text; + + assert( ad ); + + if ( mra->ma_desc ) { + /* have a mra type? check for subtype */ + if ( !is_ad_subtype( ad, mra->ma_desc ) ) { + continue; + } + value = mra->ma_value; + + } else { + const char *text = NULL; + + /* check if matching is appropriate */ + if ( strcmp( mra->ma_rule->smr_syntax->ssyn_oid, + ad->ad_type->sat_syntax->ssyn_oid ) != 0 ) { + continue; + } + + /* normalize for equality */ + rc = value_validate_normalize( ad, SLAP_MR_EQUALITY, + &mra->ma_value, &value, &text ); + if ( rc != LDAP_SUCCESS ) { + continue; + } + + /* check search access */ + if ( !access_allowed( be, conn, op, e, + ad, &value, ACL_SEARCH, NULL ) ) { + continue; + } + } + + /* check match */ + rc = value_match( &ret, ad, mra->ma_rule, + SLAP_MR_ASSERTION_SYNTAX_MATCH, + bv, &value, &text ); + + if( rc != LDAP_SUCCESS ) { + ldap_dnfree( dn ); + return rc; + } + + if ( ret == 0 ) { + ldap_dnfree( dn ); + return LDAP_COMPARE_TRUE; + } + } + } + } +#endif /* SLAP_X_MRA_MATCH_DNATTRS */ + return LDAP_COMPARE_FALSE; } diff --git a/servers/slapd/matchedValues.c b/servers/slapd/matchedValues.c index 170977d0ee..51c223e97d 100644 --- a/servers/slapd/matchedValues.c +++ b/servers/slapd/matchedValues.c @@ -19,7 +19,8 @@ #include "../../libraries/liblber/lber-int.h" -static int test_mra_vrFilter( +static int +test_mra_vrFilter( Backend *be, Connection *conn, Operation *op, @@ -342,7 +343,8 @@ test_substrings_vrFilter( return LDAP_SUCCESS; } -static int test_mra_vrFilter( +static int +test_mra_vrFilter( Backend *be, Connection *conn, Operation *op, @@ -354,11 +356,40 @@ static int test_mra_vrFilter( int i, j; for ( i=0; a != NULL; a = a->a_next, i++ ) { - struct berval *bv; - + struct berval *bv, value; + +#ifndef SLAP_X_MRA_MATCH_DNATTRS if ( !is_ad_subtype( a->a_desc, mra->ma_desc ) ) { return( LDAP_SUCCESS ); } + value = mra->ma_value; + +#else /* SLAP_X_MRA_MATCH_DNATTRS */ + if ( mra->ma_desc ) { + if ( !is_ad_subtype( a->a_desc, mra->ma_desc ) ) { + return( LDAP_SUCCESS ); + } + value = mra->ma_value; + + } else { + const char *text = NULL; + + /* check if matching is appropriate */ + if ( strcmp( mra->ma_rule->smr_syntax->ssyn_oid, + a->a_desc->ad_type->sat_syntax->ssyn_oid ) != 0 ) { + continue; + } + + /* normalize for equality */ + if ( value_validate_normalize( a->a_desc, + SLAP_MR_EQUALITY, + &mra->ma_value, &value, + &text ) != LDAP_SUCCESS ) { + continue; + } + + } +#endif /* SLAP_X_MRA_MATCH_DNATTRS */ for ( bv = a->a_vals, j = 0; bv->bv_val != NULL; bv++, j++ ) { int ret; @@ -367,7 +398,7 @@ static int test_mra_vrFilter( rc = value_match( &ret, a->a_desc, mra->ma_rule, SLAP_MR_ASSERTION_SYNTAX_MATCH, - bv, &mra->ma_value, + bv, &value, &text ); if( rc != LDAP_SUCCESS ) { diff --git a/servers/slapd/mra.c b/servers/slapd/mra.c index eca939b026..d4e331f194 100644 --- a/servers/slapd/mra.c +++ b/servers/slapd/mra.c @@ -170,10 +170,15 @@ get_mra( return SLAPD_DISCONNECT; } +#ifndef SLAP_X_MRA_MATCH_DNATTRS + /* + * Let's try to implement it + */ if( ma->ma_dnattrs ) { *text = "matching with \":dn\" not supported"; return LDAP_INAPPROPRIATE_MATCHING; } +#endif /* !SLAP_X_MRA_MATCH_DNATTRS */ if( type.bv_val != NULL ) { rc = slap_bv2ad( &type, &ma->ma_desc, text ); @@ -182,9 +187,11 @@ get_mra( return rc; } +#ifndef SLAP_X_MRA_MATCH_DNATTRS } else { *text = "matching without attribute description rule not supported"; return LDAP_INAPPROPRIATE_MATCHING; +#endif /* !SLAP_X_MRA_MATCH_DNATTRS */ } if( ma->ma_rule_text.bv_val != NULL ) { @@ -196,6 +203,40 @@ get_mra( } } + /* + * FIXME: is it correct that ma->ma_rule_text, if present, + * is looked-up, checked, and then replaced by the sat_equality + * of the given attribute? I'd rather do smtg like use + * the attribute's equality rule only if no matching rule + * was given, otherwise I don't see any extension ... + */ + +#if 1 + if ( ma->ma_rule == NULL ) { +#ifdef SLAP_X_MRA_MATCH_DNATTRS + /* + * Need either type or rule ... + */ + if ( ma->ma_desc == NULL ) { + mra_free( ma, 1 ); + *text = "matching rule not recognized"; + return LDAP_INAPPROPRIATE_MATCHING; + } +#endif /* !SLAP_X_MRA_MATCH_DNATTRS */ + + if ( ma->ma_desc->ad_type->sat_equality != NULL && + ma->ma_desc->ad_type->sat_equality->smr_usage & SLAP_MR_EXT ) + { + /* no matching rule was provided, use the attribute's + equality rule if it supports extensible matching. */ + ma->ma_rule = ma->ma_desc->ad_type->sat_equality; + + } else { + mra_free( ma, 1 ); + return LDAP_INAPPROPRIATE_MATCHING; + } + } +#else if( ma->ma_desc != NULL && ma->ma_desc->ad_type->sat_equality != NULL && ma->ma_desc->ad_type->sat_equality->smr_usage & SLAP_MR_EXT ) @@ -208,24 +249,38 @@ get_mra( mra_free( ma, 1 ); return LDAP_INAPPROPRIATE_MATCHING; } +#endif + +#ifdef SLAP_X_MRA_MATCH_DNATTRS + if ( ma->ma_desc != NULL ) { +#endif /* SLAP_X_MRA_MATCH_DNATTRS */ + /* check to see if the matching rule is appropriate for + the syntax of the attribute. This check will need + to be extended to support other kinds of extensible + matching rules */ + if( strcmp( ma->ma_rule->smr_syntax->ssyn_oid, + ma->ma_desc->ad_type->sat_syntax->ssyn_oid ) != 0 ) + { + mra_free( ma, 1 ); + return LDAP_INAPPROPRIATE_MATCHING; + } + + /* + * OK, if no matching rule, normalize for equality, otherwise + * normalize for the matching rule. + */ + rc = value_validate_normalize( ma->ma_desc, SLAP_MR_EQUALITY, + &value, &ma->ma_value, text ); +#ifdef SLAP_X_MRA_MATCH_DNATTRS + } else { + /* + * Need to normalize, but how? + */ + ma->ma_value = value; + rc = value_validate( ma->ma_rule, &ma->ma_value, text ); - /* check to see if the matching rule is appropriate for - the syntax of the attribute. This check will need - to be extended to support other kinds of extensible - matching rules */ - if( strcmp( ma->ma_rule->smr_syntax->ssyn_oid, - ma->ma_desc->ad_type->sat_syntax->ssyn_oid ) != 0 ) - { - mra_free( ma, 1 ); - return LDAP_INAPPROPRIATE_MATCHING; } - - /* - * OK, if no matching rule, normalize for equality, otherwise - * normalize for the matching rule. - */ - rc = value_validate_normalize( ma->ma_desc, SLAP_MR_EQUALITY, - &value, &ma->ma_value, text ); +#endif /* SLAP_X_MRA_MATCH_DNATTRS */ if( rc != LDAP_SUCCESS ) { mra_free( ma, 1 ); diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index 95720738d0..a3bde2cedc 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -395,6 +395,13 @@ LDAP_SLAPD_F (int) dnX509normalize LDAP_P(( void *x509_name, struct berval *out LDAP_SLAPD_F (int) dnX509peerNormalize LDAP_P(( void *ssl, struct berval *dn )); +LDAP_SLAPD_F (int) dnPrettyNormalDN LDAP_P(( Syntax *syntax, struct berval *val, LDAPDN **dn, int flags )); +#define dnPrettyDN(syntax, val, dn) \ + dnPrettyNormalDN((syntax),(val),(dn), SLAP_LDAPDN_PRETTY) +#define dnNormalDN(syntax, val, dn) \ + dnPrettyNormalDN((syntax),(val),(dn), 0) + + /* * entry.c */ @@ -451,17 +458,31 @@ 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, +LDAP_SLAPD_F (int) get_vrFilter LDAP_P(( Connection *conn, BerElement *ber, ValuesReturnFilter **f, - const char **text ); + const char **text )); -LDAP_SLAPD_F (void) vrFilter_free( ValuesReturnFilter *f ); -LDAP_SLAPD_F (void) vrFilter2bv( ValuesReturnFilter *f, struct berval *fstr ); +LDAP_SLAPD_F (void) vrFilter_free LDAP_P(( ValuesReturnFilter *f )); +LDAP_SLAPD_F (void) vrFilter2bv LDAP_P(( ValuesReturnFilter *f, struct berval *fstr )); +/* + * define to honor hasSubordinates operational attribute in search filters + */ +#define SLAP_X_FILTER_HASSUBORDINATES + +#ifdef SLAP_X_FILTER_HASSUBORDINATES +LDAP_SLAPD_F (int) filter_has_subordinates LDAP_P(( Filter *filter )); +#endif /* SLAP_X_FILTER_HASSUBORDINATES */ /* * filterentry.c */ + +/* + * define to enable dn components match in extended filter matching + */ +#define SLAP_X_MRA_MATCH_DNATTRS + LDAP_SLAPD_F (int) test_filter LDAP_P(( Backend *be, Connection *conn, Operation *op, Entry *e, Filter *f )); diff --git a/servers/slapd/result.c b/servers/slapd/result.c index bdba964126..074b1c6b6d 100644 --- a/servers/slapd/result.c +++ b/servers/slapd/result.c @@ -770,6 +770,7 @@ send_search_entry( } e_flags = ch_calloc ( 1, i * sizeof(char *) + k ); a_flags = (char *)(e_flags + i); + memset( a_flags, 0, k ); 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++ ); e_flags[i] = a_flags; @@ -909,25 +910,45 @@ send_search_entry( } } - /* free e_flags */ - if ( e_flags ) { - free( e_flags ); - e_flags = NULL; - } - /* eventually will loop through generated operational attributes */ /* only have subschemaSubentry implemented */ aa = backend_operational( be, conn, op, e, attrs, opattrs ); if ( aa != NULL && op->vrFilter != NULL ) { int k = 0; - char *a_flags; + char *a_flags, **tmp; for ( a = aa, i=0; a != NULL; a = a->a_next, i++ ) { for ( j = 0; a->a_vals[j].bv_val != NULL; j++ ) k++; } - e_flags = ch_calloc ( 1, i * sizeof(char *) + k ); + /* + * Reuse previous memory - we likely need less space + * for operational attributes + */ + tmp = ch_realloc ( e_flags, i * sizeof(char *) + k ); + if ( tmp == NULL ) { +#ifdef NEW_LOGGING + LDAP_LOG( OPERATION, ERR, + "send_search_entry: conn %lu " + "not enough memory " + "for matched values filtering\n", + conn ? conn->c_connid : 0, 0, 0); +#else + Debug( LDAP_DEBUG_ANY, + "send_search_entry: conn %lu " + "not enough memory " + "for matched values filtering\n", + conn ? conn->c_connid : 0, 0, 0 ); +#endif + ber_free( ber, 1 ); + + send_ldap_result( conn, op, LDAP_NO_MEMORY, + NULL, NULL, NULL, NULL ); + goto error_return; + } + e_flags = tmp; a_flags = (char *)(e_flags + i); + memset( a_flags, 0, k ); for ( a = aa, i=0; a != NULL; a = a->a_next, i++ ) { for ( j = 0; a->a_vals[j].bv_val != NULL; j++ ); e_flags[i] = a_flags; @@ -942,7 +963,7 @@ send_search_entry( "matched values filtering failed\n", conn ? conn->c_connid : 0, 0, 0); #else - Debug( LDAP_DEBUG_ANY, + Debug( LDAP_DEBUG_ANY, "matched values filtering failed\n", 0, 0, 0 ); #endif ber_free( ber, 1 ); diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index 92ac6fdd8f..f23101680e 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -1683,6 +1683,12 @@ enum { }; #endif /* SLAPD_MONITOR */ +/* + * Better know these all around slapd + */ +#define SLAP_LDAPDN_PRETTY 0x1 +#define SLAP_LDAPDN_MAXLEN 8192 + LDAP_END_DECL #include "proto-slap.h"