From 15016154b6c8b986a55faed0adeba65071f17a86 Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Tue, 12 Apr 2005 00:24:04 +0000 Subject: [PATCH] add ACL hook to overlays --- servers/slapd/acl.c | 385 +++++++++++++++++++++++++++++++++++---- servers/slapd/backend.c | 8 +- servers/slapd/backover.c | 55 ++++++ servers/slapd/slap.h | 4 + 4 files changed, 416 insertions(+), 36 deletions(-) diff --git a/servers/slapd/acl.c b/servers/slapd/acl.c index 3bcd3065c6..2cbef06023 100644 --- a/servers/slapd/acl.c +++ b/servers/slapd/acl.c @@ -155,6 +155,319 @@ static int aci_match_set ( struct berval *subj, Operation *op, * - can be legally called with op->o_bd == NULL */ +#ifdef LDAP_DEVEL + +static int +slap_access_allowed( + Operation *op, + Entry *e, + AttributeDescription *desc, + struct berval *val, + slap_access_t access, + AccessControlState *state, + slap_mask_t *maskp ) +{ + int ret = 1; + int count; + AccessControl *a = NULL; + +#ifdef LDAP_DEBUG + char accessmaskbuf[ACCESSMASK_MAXLEN]; +#endif + slap_mask_t mask; + slap_control_t control; + slap_access_t access_level; + const char *attr; + regmatch_t matches[MAXREMATCHES]; + int st_same_attr = 0; + + assert( op != NULL ); + assert( e != NULL ); + assert( desc != NULL ); + assert( maskp != NULL ); + + access_level = ACL_LEVEL( access ); + attr = desc->ad_cname.bv_val; + + assert( attr != NULL ); + +#ifdef LDAP_SLAPI + if ( op->o_pb != NULL ) { + ret = slapi_int_access_allowed( op, e, desc, val, access, state ); + if ( ret == 0 ) { + /* ACL plugin denied access */ + goto done; + } + } +#endif /* LDAP_SLAPI */ + + /* grant database root access */ + if ( be_isroot( op ) ) { + Debug( LDAP_DEBUG_ACL, "<= root access granted\n", 0, 0, 0 ); + mask = ACL_LVL_MANAGE; + goto done; + } + + /* + * no-user-modification operational attributes are ignored + * by ACL_WRITE checking as any found here are not provided + * by the user + */ + if ( access_level >= ACL_WRITE && is_at_no_user_mod( desc->ad_type ) + && desc != slap_schema.si_ad_entry + && desc != slap_schema.si_ad_children ) + { + Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:" + " %s access granted\n", + attr, 0, 0 ); + goto done; + } + + /* use backend default access if no backend acls */ + if ( op->o_bd->be_acl == NULL ) { + int i; + + Debug( LDAP_DEBUG_ACL, + "=> slap_access_allowed: backend default %s " + "access %s to \"%s\"\n", + access2str( access ), + op->o_bd->be_dfltaccess >= access_level ? "granted" : "denied", + op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" ); + ret = op->o_bd->be_dfltaccess >= access_level; + + mask = ACL_PRIV_LEVEL; + for ( i = ACL_NONE; i <= op->o_bd->be_dfltaccess; i++ ) { + ACL_PRIV_SET( mask, ACL_ACCESS2PRIV( i ) ); + } + + goto done; + } + + ret = 0; + control = ACL_BREAK; + + if ( st_same_attr ) { + assert( state->as_vd_acl != NULL ); + + a = state->as_vd_acl; + count = state->as_vd_acl_count; + if ( !ACL_IS_INVALID( state->as_vd_acl_mask ) ) { + mask = state->as_vd_acl_mask; + AC_MEMCPY( matches, state->as_vd_acl_matches, sizeof(matches) ); + goto vd_access; + } + + } else { + if ( state ) state->as_vi_acl = NULL; + a = NULL; + ACL_INIT( mask ); + count = 0; + memset( matches, '\0', sizeof( matches ) ); + } + + while ( ( a = acl_get( a, &count, op, e, desc, val, + MAXREMATCHES, matches, state ) ) != NULL ) + { + int i; + + for ( i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++ ) { + Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i, + (int)matches[i].rm_so, (int)matches[i].rm_eo ); + if ( matches[i].rm_so <= matches[0].rm_eo ) { + int n; + for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++ ) { + Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 ); + } + } + Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 ); + } + + if ( state ) { + if ( state->as_vi_acl == a && + ( state->as_recorded & ACL_STATE_RECORDED_NV ) ) + { + Debug( LDAP_DEBUG_ACL, + "slap_access_allowed: result from state (%s)\n", + attr, 0, 0 ); + ret = state->as_result; + goto done; + } else { + Debug( LDAP_DEBUG_ACL, + "slap_access_allowed: no res from state (%s)\n", + attr, 0, 0 ); + } + } + +vd_access: + control = acl_mask( a, &mask, op, + e, desc, val, MAXREMATCHES, matches, count, state ); + + if ( control != ACL_BREAK ) { + break; + } + + memset( matches, '\0', sizeof( matches ) ); + } + + if ( ACL_IS_INVALID( mask ) ) { + Debug( LDAP_DEBUG_ACL, + "=> slap_access_allowed: \"%s\" (%s) invalid!\n", + e->e_dn, attr, 0 ); + ACL_INIT( mask ); + + } else if ( control == ACL_BREAK ) { + Debug( LDAP_DEBUG_ACL, + "=> slap_access_allowed: no more rules\n", 0, 0, 0 ); + + goto done; + } + + ret = ACL_GRANT(mask, access); + + Debug( LDAP_DEBUG_ACL, + "=> slap_access_allowed: %s access %s by %s\n", + access2str( access ), ret ? "granted" : "denied", + accessmask2str( mask, accessmaskbuf, 1 ) ); + +done: + ACL_PRIV_SET( *maskp, mask ); + return ret; +} + +int +access_allowed_mask( + Operation *op, + Entry *e, + AttributeDescription *desc, + struct berval *val, + slap_access_t access, + AccessControlState *state, + slap_mask_t *maskp ) +{ + int ret = 1; + AccessControl *a = NULL; + int be_null = 0; + +#ifdef LDAP_DEBUG + char accessmaskbuf[ACCESSMASK_MAXLEN]; +#endif + slap_mask_t mask; + slap_control_t control; + slap_access_t access_level; + const char *attr; + int st_same_attr = 0; + static AccessControlState state_init = ACL_STATE_INIT; + BI_access_allowed *bi_access_allowed = NULL; + + assert( e != NULL ); + assert( desc != NULL ); + + access_level = ACL_LEVEL( access ); + + assert( access_level > ACL_NONE ); + + ACL_INVALIDATE( mask ); + if ( maskp ) ACL_INVALIDATE( *maskp ); + + attr = desc->ad_cname.bv_val; + + assert( attr != NULL ); + + if ( op && op->o_is_auth_check && + ( access_level == ACL_SEARCH || access_level == ACL_READ ) ) + { + access = ACL_AUTH; + } + + if ( state ) { + if ( state->as_vd_ad == desc ) { + if ( state->as_recorded ) { + if ( ( state->as_recorded & ACL_STATE_RECORDED_NV ) && + val == NULL ) + { + return state->as_result; + + } else if ( ( state->as_recorded & ACL_STATE_RECORDED_VD ) && + val != NULL && state->as_vd_acl == NULL ) + { + return state->as_result; + } + } + st_same_attr = 1; + } else { + *state = state_init; + } + + state->as_vd_ad = desc; + } + + Debug( LDAP_DEBUG_ACL, + "=> access_allowed: %s access to \"%s\" \"%s\" requested\n", + access2str( access ), e->e_dn, attr ); + + if ( op == NULL ) { + /* no-op call */ + goto done; + } + + if ( op->o_bd == NULL ) { + op->o_bd = LDAP_STAILQ_FIRST(&backendDB); + be_null = 1; +#ifdef LDAP_DEVEL + /* + * FIXME: experimental; use first backend rules + * iff there is no global_acl (ITS#3100) */ + if ( frontendDB->be_acl != NULL ) { + op->o_bd = frontendDB; + } +#endif + } + assert( op->o_bd != NULL ); + + /* delegate to backend */ + if ( op->o_bd->bd_info->bi_access_allowed != NULL ) { + bi_access_allowed = op->o_bd->bd_info->bi_access_allowed; + } else { + bi_access_allowed = slap_access_allowed; + } + ret = bi_access_allowed( op, e, desc, val, access, state, &mask ); + if ( !ret ) { + if ( ACL_IS_INVALID( mask ) ) { + Debug( LDAP_DEBUG_ACL, + "=> access_allowed: \"%s\" (%s) invalid!\n", + e->e_dn, attr, 0 ); + ACL_INIT(mask); + + } else if ( control == ACL_BREAK ) { + Debug( LDAP_DEBUG_ACL, + "=> access_allowed: no more rules\n", 0, 0, 0 ); + + goto done; + } + + ret = ACL_GRANT( mask, access ); + } + + Debug( LDAP_DEBUG_ACL, + "=> access_allowed: %s access %s by %s\n", + access2str( access ), ret ? "granted" : "denied", + accessmask2str( mask, accessmaskbuf, 1 ) ); + +done: + if ( state != NULL ) { + /* If not value-dependent, save ACL in case of more attrs */ + if ( !( state->as_recorded & ACL_STATE_RECORDED_VD ) ) { + state->as_vi_acl = a; + state->as_result = ret; + } + state->as_recorded |= ACL_STATE_RECORDED; + } + if ( be_null ) op->o_bd = NULL; + if ( maskp ) ACL_PRIV_ASSIGN( *maskp, mask ); + return ret; +} + +#else /* !LDAP_DEVEL */ int access_allowed_mask( Operation *op, @@ -194,20 +507,21 @@ access_allowed_mask( assert( attr != NULL ); - if( op && op->o_is_auth_check && - ( access_level == ACL_SEARCH || access_level == ACL_READ )) + if ( op && op->o_is_auth_check && + ( access_level == ACL_SEARCH || access_level == ACL_READ ) ) { access = ACL_AUTH; } - if( state ) { - if ( state->as_vd_ad==desc) { + if ( state ) { + if ( state->as_vd_ad == desc ) { if ( state->as_recorded ) { - if( state->as_recorded & ACL_STATE_RECORDED_NV && + if ( ( state->as_recorded & ACL_STATE_RECORDED_NV ) && val == NULL ) { return state->as_result; - } else if ( state->as_recorded & ACL_STATE_RECORDED_VD && + + } else if ( ( state->as_recorded & ACL_STATE_RECORDED_VD ) && val != NULL && state->as_vd_acl == NULL ) { return state->as_result; @@ -223,7 +537,7 @@ access_allowed_mask( Debug( LDAP_DEBUG_ACL, "=> access_allowed: %s access to \"%s\" \"%s\" requested\n", - access2str( access ), e->e_dn, attr ); + access2str( access ), e->e_dn, attr ); if ( op == NULL ) { /* no-op call */ @@ -257,10 +571,8 @@ access_allowed_mask( #endif /* LDAP_SLAPI */ /* grant database root access */ - if ( /* be != NULL && */ be_isroot( op ) ) { - Debug( LDAP_DEBUG_ACL, - "<= root access granted\n", - 0, 0, 0 ); + if ( be_isroot( op ) ) { + Debug( LDAP_DEBUG_ACL, "<= root access granted\n", 0, 0, 0 ); if ( maskp ) { mask = ACL_LVL_MANAGE; } @@ -284,9 +596,10 @@ access_allowed_mask( } /* use backend default access if no backend acls */ - if ( /* be != NULL && */ be->be_acl == NULL ) { + if ( be->be_acl == NULL ) { Debug( LDAP_DEBUG_ACL, - "=> access_allowed: backend default %s access %s to \"%s\"\n", + "=> access_allowed: backend default %s " + "access %s to \"%s\"\n", access2str( access ), be->be_dfltaccess >= access_level ? "granted" : "denied", op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" ); @@ -310,7 +623,8 @@ access_allowed_mask( Debug( LDAP_DEBUG_ACL, "=> access_allowed: global default %s access %s to \"%s\"\n", access2str( access ), - frontendDB->be_dfltaccess >= access_level ? "granted" : "denied", op->o_dn.bv_val ); + frontendDB->be_dfltaccess >= access_level ? + "granted" : "denied", op->o_dn.bv_val ); ret = frontendDB->be_dfltaccess >= access_level; if ( maskp ) { @@ -329,12 +643,12 @@ access_allowed_mask( ret = 0; control = ACL_BREAK; - if( st_same_attr ) { + if ( st_same_attr ) { assert( state->as_vd_acl != NULL ); a = state->as_vd_acl; count = state->as_vd_acl_count; - if ( !ACL_IS_INVALID( state->as_vd_acl_mask )) { + if ( !ACL_IS_INVALID( state->as_vd_acl_mask ) ) { mask = state->as_vd_acl_mask; AC_MEMCPY( matches, state->as_vd_acl_matches, sizeof(matches) ); goto vd_access; @@ -345,33 +659,39 @@ access_allowed_mask( a = NULL; ACL_INIT(mask); count = 0; - memset(matches, '\0', sizeof(matches)); + memset( matches, '\0', sizeof(matches) ); } - while((a = acl_get( a, &count, op, e, desc, val, - MAXREMATCHES, matches, state )) != NULL) + while ( ( a = acl_get( a, &count, op, e, desc, val, + MAXREMATCHES, matches, state ) ) != NULL ) { int i; - for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) { + for ( i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++ ) { Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i, - (int)matches[i].rm_so, (int)matches[i].rm_eo ); - if( matches[i].rm_so <= matches[0].rm_eo ) { + (int)matches[i].rm_so, (int)matches[i].rm_eo ); + if ( matches[i].rm_so <= matches[0].rm_eo ) { int n; - for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) { + for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++ ) { Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 ); } } Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 ); } - if (state) { - if (state->as_vi_acl == a && (state->as_recorded & ACL_STATE_RECORDED_NV)) { - Debug( LDAP_DEBUG_ACL, "access_allowed: result from state (%s)\n", attr, 0, 0 ); + if ( state ) { + if ( state->as_vi_acl == a && + ( state->as_recorded & ACL_STATE_RECORDED_NV ) ) + { + Debug( LDAP_DEBUG_ACL, + "access_allowed: result from state (%s)\n", + attr, 0, 0 ); ret = state->as_result; goto done; } else { - Debug( LDAP_DEBUG_ACL, "access_allowed: no res from state (%s)\n", attr, 0, 0); + Debug( LDAP_DEBUG_ACL, + "access_allowed: no res from state (%s)\n", + attr, 0, 0 ); } } @@ -383,7 +703,7 @@ vd_access: break; } - memset(matches, '\0', sizeof(matches)); + memset( matches, '\0', sizeof(matches) ); } if ( ACL_IS_INVALID( mask ) ) { @@ -394,7 +714,7 @@ vd_access: } else if ( control == ACL_BREAK ) { Debug( LDAP_DEBUG_ACL, - "=> access_allowed: no more rules\n", 0, 0, 0); + "=> access_allowed: no more rules\n", 0, 0, 0 ); goto done; } @@ -408,19 +728,20 @@ vd_access: ret = ACL_GRANT(mask, access); done: - if( state != NULL ) { + if ( state != NULL ) { /* If not value-dependent, save ACL in case of more attrs */ - if ( !(state->as_recorded & ACL_STATE_RECORDED_VD) ) { + if ( !( state->as_recorded & ACL_STATE_RECORDED_VD ) ) { state->as_vi_acl = a; state->as_result = ret; } state->as_recorded |= ACL_STATE_RECORDED; } - if (be_null) op->o_bd = NULL; + if ( be_null ) op->o_bd = NULL; if ( maskp ) *maskp = mask; return ret; } +#endif /* LDAP_DEVEL */ /* * acl_get - return the acl applicable to entry e, attribute diff --git a/servers/slapd/backend.c b/servers/slapd/backend.c index e69513647c..1430f012bc 100644 --- a/servers/slapd/backend.c +++ b/servers/slapd/backend.c @@ -1752,14 +1752,14 @@ int backend_operational( * add them to the attribute list */ if ( SLAP_OPATTRS( rs->sr_attr_flags ) || ( rs->sr_attrs && - ad_inlist( slap_schema.si_ad_entryDN, 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 ) || ( rs->sr_attrs && - ad_inlist( slap_schema.si_ad_subschemaSubentry, rs->sr_attrs ))) + ad_inlist( slap_schema.si_ad_subschemaSubentry, rs->sr_attrs ) ) ) { *ap = slap_operational_subschemaSubentry( op->o_bd ); ap = &(*ap)->a_next; @@ -1767,10 +1767,10 @@ int backend_operational( /* Let the overlays have a chance at this */ be_orig = op->o_bd; - if ( SLAP_ISOVERLAY( be_orig )) + if ( SLAP_ISOVERLAY( be_orig ) ) op->o_bd = select_backend( be_orig->be_nsuffix, 0, 0 ); - if (( SLAP_OPATTRS( rs->sr_attr_flags ) || rs->sr_attrs ) && + if ( ( SLAP_OPATTRS( rs->sr_attr_flags ) || rs->sr_attrs ) && op->o_bd && op->o_bd->be_operational != NULL ) { rc = op->o_bd->be_operational( op, rs ); diff --git a/servers/slapd/backover.c b/servers/slapd/backover.c index f4b82dc0be..11fec23fa7 100644 --- a/servers/slapd/backover.c +++ b/servers/slapd/backover.c @@ -248,6 +248,58 @@ static int op_rc[] = { SLAP_CB_CONTINUE /* aux_chk_controls; pass to frontend */ }; +static int +over_access_allowed( + Operation *op, + Entry *e, + AttributeDescription *desc, + struct berval *val, + slap_access_t access, + AccessControlState *state, + slap_mask_t *maskp ) +{ + slap_overinfo *oi; + slap_overinst *on; + BackendDB *be = op->o_bd, db; + int rc = SLAP_CB_CONTINUE; + + /* FIXME: used to happen for instance during abandon + * when global overlays are used... */ + assert( op->o_bd != NULL ); + + oi = op->o_bd->bd_info->bi_private; + on = oi->oi_list; + + if ( !SLAP_ISOVERLAY( op->o_bd ) ) { + db = *op->o_bd; + db.be_flags |= SLAP_DBFLAG_OVERLAY; + op->o_bd = &db; + } + + for ( ; on; on = on->on_next ) { + if ( on->on_bi.bi_access_allowed ) { + op->o_bd->bd_info = (BackendInfo *)on; + rc = on->on_bi.bi_access_allowed( op, e, + desc, val, access, state, maskp ); + if ( rc != SLAP_CB_CONTINUE ) break; + } + } + + if ( rc == SLAP_CB_CONTINUE && oi->oi_orig->bi_access_allowed ) { + op->o_bd->bd_info = oi->oi_orig; + rc = oi->oi_orig->bi_access_allowed( op, e, + desc, val, access, state, maskp ); + } + /* should not fall thru this far without anything happening... */ + if ( rc == SLAP_CB_CONTINUE ) { + /* access not allowed */ + rc = 0; + } + + op->o_bd = be; + return rc; +} + static int over_op_func( Operation *op, @@ -631,6 +683,9 @@ overlay_config( BackendDB *be, const char *ov ) bi->bi_operational = over_aux_operational; bi->bi_chk_referrals = over_aux_chk_referrals; bi->bi_chk_controls = over_aux_chk_controls; + + /* this has a specific arglist */ + bi->bi_access_allowed = over_access_allowed; bi->bi_connection_destroy = over_connection_destroy; diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index 18ab4df30a..73d76a272c 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -1986,6 +1986,9 @@ typedef int (BI_entry_get_rw) LDAP_P(( struct slap_op *op, struct berval *ndn, typedef int (BI_operational) LDAP_P(( struct slap_op *op, struct slap_rep *rs )); typedef int (BI_has_subordinates) LDAP_P(( struct slap_op *op, Entry *e, int *hasSubs )); +typedef int (BI_access_allowed) LDAP_P(( struct slap_op *op, Entry *e, + AttributeDescription *desc, struct berval *val, slap_access_t access, + AccessControlState *state, slap_mask_t *maskp )); typedef int (BI_connection_init) LDAP_P(( BackendDB *bd, struct slap_conn *c )); @@ -2087,6 +2090,7 @@ struct slap_backend_info { BI_entry_release_rw *bi_entry_release_rw; BI_has_subordinates *bi_has_subordinates; + BI_access_allowed *bi_access_allowed; BI_connection_init *bi_connection_init; BI_connection_destroy *bi_connection_destroy; -- 2.39.5