From 362766b3169e066841eb363689c142b0e57df30e Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Wed, 26 Jan 2005 23:19:48 +0000 Subject: [PATCH] move compare to frontend (disabled by default, only invoked if backend doesn't provide hook); honor ITS#3472 (disclose access check); fix ITS#3521 as well (check access to hasSubordinates); fix a problem in backend_attribute() with operational attrs; add backend_access() helper --- servers/slapd/backend.c | 173 +++++++++++++++++++++++++++++++++++++--- servers/slapd/compare.c | 90 +++++++++++++++++++-- 2 files changed, 244 insertions(+), 19 deletions(-) diff --git a/servers/slapd/backend.c b/servers/slapd/backend.c index b3377d179e..96d51dcf8d 100644 --- a/servers/slapd/backend.c +++ b/servers/slapd/backend.c @@ -555,11 +555,11 @@ backend_db_init( /* assign a default depth limit for alias deref */ be->be_max_deref_depth = SLAPD_DEFAULT_MAXDEREFDEPTH; - if(bi->bi_db_init) { + if ( bi->bi_db_init ) { rc = bi->bi_db_init( be ); } - if(rc != 0) { + if ( rc != 0 ) { fprintf( stderr, "database init failed (%s)\n", type ); nbackends--; return NULL; @@ -583,6 +583,7 @@ be_db_close( void ) if ( frontendDB->bd_info->bi_db_close ) { (*frontendDB->bd_info->bi_db_close)( frontendDB ); } + } Backend * @@ -1479,13 +1480,21 @@ backend_attribute( BER_BVZERO( &anlist[ 1 ].an_name ); rs.sr_attrs = anlist; - rs.sr_attr_flags = slap_attr_flags( rs.sr_attrs ); - - rc = backend_operational( op, &rs ); + /* NOTE: backend_operational() is also called + * when returning results, so it's supposed + * to do no harm to entries */ + rs.sr_entry = e; + rc = backend_operational( op, &rs ); + rs.sr_entry = NULL; + + if ( rc == LDAP_SUCCESS ) { + if ( rs.sr_operational_attrs ) { + freeattr = 1; + a = rs.sr_operational_attrs; - if ( rc == LDAP_SUCCESS && rs.sr_operational_attrs ) { - freeattr = 1; - a = rs.sr_operational_attrs; + } else { + rc = LDAP_NO_SUCH_ATTRIBUTE; + } } } @@ -1567,6 +1576,144 @@ freeit: if ( e != target ) { return rc; } +#ifdef LDAP_SLAPI +static int backend_compute_output_attr_access(computed_attr_context *c, Slapi_Attr *a, Slapi_Entry *e) +{ + struct berval *nval = (struct berval *)c->cac_private; + Operation *op = NULL; + + slapi_pblock_get( c->cac_pb, SLAPI_OPERATION, &op ); + if ( op == NULL ) { + return 1; + } + + return access_allowed( op, e, a->a_desc, nval, ACL_AUTH, NULL ) == 0; +} +#endif /* LDAP_SLAPI */ + +int +backend_access( + Operation *op, + Entry *target, + struct berval *edn, + AttributeDescription *entry_at, + struct berval *nval, + slap_access_t access, + slap_mask_t *mask ) +{ + Entry *e = NULL; + int rc = LDAP_INSUFFICIENT_ACCESS; + Backend *be = op->o_bd; + + /* pedantic */ + assert( op ); + assert( op->o_conn ); + assert( edn ); + assert( access > ACL_NONE ); + + op->o_bd = select_backend( edn, 0, 0 ); + + if ( target && dn_match( &target->e_nname, edn ) ) { + e = target; + + } else { + rc = be_entry_get_rw( op, edn, NULL, entry_at, 0, &e ); + } + + if ( e ) { + Attribute *a = NULL; + int freeattr = 0; + + if ( entry_at == NULL ) { + entry_at = slap_schema.si_ad_entry; + } + + if ( entry_at == slap_schema.si_ad_entry || entry_at == slap_schema.si_ad_children ) + { + if ( access_allowed_mask( op, e, entry_at, + NULL, access, NULL, mask ) == 0 ) + { + rc = LDAP_INSUFFICIENT_ACCESS; + + } else { + rc = LDAP_SUCCESS; + } + + } else { + a = attr_find( e->e_attrs, entry_at ); + if ( a == NULL ) { + SlapReply rs = { 0 }; + AttributeName anlist[ 2 ]; + + anlist[ 0 ].an_name = entry_at->ad_cname; + anlist[ 0 ].an_desc = entry_at; + BER_BVZERO( &anlist[ 1 ].an_name ); + rs.sr_attrs = anlist; + + rs.sr_attr_flags = slap_attr_flags( rs.sr_attrs ); + + /* NOTE: backend_operational() is also called + * when returning results, so it's supposed + * to do no harm to entries */ + rs.sr_entry = e; + rc = backend_operational( op, &rs ); + rs.sr_entry = NULL; + + if ( rc == LDAP_SUCCESS ) { + if ( rs.sr_operational_attrs ) { + freeattr = 1; + a = rs.sr_operational_attrs; + + } else { + rc = LDAP_NO_SUCH_OBJECT; + } + } + } + + if ( a ) { + if ( access_allowed_mask( op, e, entry_at, + nval, access, NULL, mask ) == 0 ) + { + rc = LDAP_INSUFFICIENT_ACCESS; + goto freeit; + } + rc = LDAP_SUCCESS; + } +#ifdef LDAP_SLAPI + else if ( op->o_pb ) { + /* try any computed attributes */ + computed_attr_context ctx; + + slapi_int_pblock_set_operation( op->o_pb, op ); + + ctx.cac_pb = op->o_pb; + ctx.cac_attrs = NULL; + ctx.cac_userattrs = 0; + ctx.cac_opattrs = 0; + ctx.cac_private = (void *)nval; + + rc = compute_evaluator( &ctx, entry_at->ad_cname.bv_val, e, backend_compute_output_attr_access ); + if ( rc == 1 ) { + rc = LDAP_INSUFFICIENT_ACCESS; + + } else { + rc = LDAP_SUCCESS; + } + } +#endif /* LDAP_SLAPI */ + } +freeit: if ( e != target ) { + be_entry_release_r( op, e ); + } + if ( freeattr ) { + attr_free( a ); + } + } + + op->o_bd = be; + return rc; +} + int backend_operational( Operation *op, SlapReply *rs ) @@ -1583,15 +1730,15 @@ int backend_operational( * and the backend supports specific operational attributes, * add them to the attribute list */ - if ( SLAP_OPATTRS( rs->sr_attr_flags ) || ( op->ors_attrs && - ad_inlist( slap_schema.si_ad_entryDN, op->ors_attrs ))) + if ( SLAP_OPATTRS( rs->sr_attr_flags ) || ( rs->sr_attrs && + ad_inlist( slap_schema.si_ad_entryDN, rs->sr_attrs ))) { *ap = slap_operational_entryDN( rs->sr_entry ); ap = &(*ap)->a_next; } - if ( SLAP_OPATTRS( rs->sr_attr_flags ) || ( op->ors_attrs && - ad_inlist( slap_schema.si_ad_subschemaSubentry, op->ors_attrs ))) + if ( SLAP_OPATTRS( rs->sr_attr_flags ) || ( rs->sr_attrs && + ad_inlist( slap_schema.si_ad_subschemaSubentry, rs->sr_attrs ))) { *ap = slap_operational_subschemaSubentry( op->o_bd ); ap = &(*ap)->a_next; @@ -1602,7 +1749,7 @@ int backend_operational( if ( SLAP_ISOVERLAY( be_orig )) op->o_bd = select_backend( be_orig->be_nsuffix, 0, 0 ); - if (( SLAP_OPATTRS( rs->sr_attr_flags ) || op->ors_attrs ) && + if (( SLAP_OPATTRS( rs->sr_attr_flags ) || rs->sr_attrs ) && op->o_bd && op->o_bd->be_operational != NULL ) { Attribute *a; diff --git a/servers/slapd/compare.c b/servers/slapd/compare.c index d6596c2487..9c41d57d41 100644 --- a/servers/slapd/compare.c +++ b/servers/slapd/compare.c @@ -274,6 +274,7 @@ fe_op_compare( Operation *op, SlapReply *rs ) send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, "subschemaSubentry compare not supported" ); +#ifndef SLAP_COMPARE_IN_FRONTEND } else if ( ava.aa_desc == slap_schema.si_ad_hasSubordinates && op->o_bd->be_has_subordinates ) { @@ -281,9 +282,16 @@ fe_op_compare( Operation *op, SlapReply *rs ) rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &entry ); if ( rc == 0 && entry ) { - rc = op->o_bd->be_has_subordinates( op, entry, - &hasSubordinates ); - be_entry_release_r( op, entry ); + if ( ! access_allowed( op, entry, + ava.aa_desc, &ava.aa_value, ACL_COMPARE, NULL ) ) + { + rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS; + + } else { + rc = rs->sr_err = op->o_bd->be_has_subordinates( op, + entry, &hasSubordinates ); + be_entry_release_r( op, entry ); + } } if ( rc == 0 ) { @@ -293,20 +301,90 @@ fe_op_compare( Operation *op, SlapReply *rs ) ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE; if ( hasSubordinates == asserted ) { rs->sr_err = LDAP_COMPARE_TRUE; + } else { rs->sr_err = LDAP_COMPARE_FALSE; } + + } else { +#ifdef SLAP_ACL_HONOR_DISCLOSE + /* return error only if "disclose" + * is granted on the object */ + if ( backend_access( op, NULL, &op->o_req_ndn, + slap_schema.si_ad_entry, + NULL, ACL_DISCLOSE, NULL ) == LDAP_INSUFFICIENT_ACCESS ) + { + rs->sr_err = LDAP_NO_SUCH_OBJECT; + } +#endif /* SLAP_ACL_HONOR_DISCLOSE */ } + send_ldap_result( op, rs ); - if( rc == 0 ) rs->sr_err = LDAP_SUCCESS; + if ( rc == 0 ) { + rs->sr_err = LDAP_SUCCESS; + } } else if ( op->o_bd->be_compare ) { op->o_bd->be_compare( op, rs ); +#endif /* ! SLAP_COMPARE_IN_FRONTEND */ } else { - send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, - "operation not supported within namingContext" ); + /* do our best to compare that AVA + * + * NOTE: this code is used only + * if SLAP_COMPARE_IN_FRONTEND + * is #define'd (it's not by default) + * or if op->o_bd->be_compare is NULL. + * + * FIXME: one potential issue is that + * if SLAP_COMPARE_IN_FRONTEND overlays + * are not executed for compare. */ + BerVarray vals = NULL; + int rc = LDAP_OTHER; + + rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn, + ava.aa_desc, &vals, ACL_COMPARE ); + switch ( rs->sr_err ) { + default: +#ifdef SLAP_ACL_HONOR_DISCLOSE + /* return error only if "disclose" + * is granted on the object */ + if ( backend_access( op, NULL, &op->o_req_ndn, + slap_schema.si_ad_entry, + NULL, ACL_DISCLOSE, NULL ) + == LDAP_INSUFFICIENT_ACCESS ) + { + rs->sr_err = LDAP_NO_SUCH_OBJECT; + } +#endif /* SLAP_ACL_HONOR_DISCLOSE */ + break; + + case LDAP_SUCCESS: + if ( value_find_ex( op->oq_compare.rs_ava->aa_desc, + SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | + SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, + vals, &ava.aa_value, op->o_tmpmemctx ) == 0 ) + { + rs->sr_err = LDAP_COMPARE_TRUE; + break; + + } else { + rs->sr_err = LDAP_COMPARE_FALSE; + } + rc = LDAP_SUCCESS; + break; + } + + send_ldap_result( op, rs ); + + if ( rc == 0 ) { + rs->sr_err = LDAP_SUCCESS; + } + + if ( vals ) { + ber_bvarray_free_x( vals, op->o_tmpmemctx ); + } } #if defined( LDAP_SLAPI ) -- 2.39.5