X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Facl.c;h=9fb688e86947fac5ef52ea1c138fdaef9ff86f87;hb=54fa643a9bd33e59059b84bda010b5f668cd45bb;hp=7404cda5b08a04cb1245e868720339b7b5c9ccac;hpb=4abbf9c610d4fbaeee0dbdceaad1e0f94ed8e8fe;p=openldap diff --git a/servers/slapd/acl.c b/servers/slapd/acl.c index 7404cda5b0..9fb688e869 100644 --- a/servers/slapd/acl.c +++ b/servers/slapd/acl.c @@ -54,7 +54,9 @@ static struct berval aci_bv_br_entry = BER_BVC("[entry]"), aci_bv_br_all = BER_BVC("[all]"), aci_bv_access_id = BER_BVC("access-id"), +#if 0 aci_bv_anonymous = BER_BVC("anonymous"), +#endif aci_bv_public = BER_BVC("public"), aci_bv_users = BER_BVC("users"), aci_bv_self = BER_BVC("self"), @@ -69,7 +71,9 @@ static struct berval aci_bv_ip_eq = BER_BVC("IP="), #ifdef LDAP_PF_LOCAL aci_bv_path_eq = BER_BVC("PATH="), +#if 0 aci_bv_dirsep = BER_BVC(LDAP_DIRSEP), +#endif #endif /* LDAP_PF_LOCAL */ aci_bv_group_class = BER_BVC(SLAPD_GROUP_CLASS), @@ -84,7 +88,7 @@ typedef enum slap_aci_scope_t { SLAP_ACI_SCOPE_SUBTREE = ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN ) } slap_aci_scope_t; -static AccessControl * acl_get( +static AccessControl * slap_acl_get( AccessControl *ac, int *count, Operation *op, Entry *e, AttributeDescription *desc, @@ -92,7 +96,7 @@ static AccessControl * acl_get( int nmatch, regmatch_t *matches, AccessControlState *state ); -static slap_control_t acl_mask( +static slap_control_t slap_acl_mask( AccessControl *ac, slap_mask_t *mask, Operation *op, Entry *e, AttributeDescription *desc, @@ -138,7 +142,7 @@ static int aci_match_set ( struct berval *subj, Operation *op, * the whole attribute is assumed (all values). * * This routine loops through all access controls and calls - * acl_mask() on each applicable access control. + * slap_acl_mask() on each applicable access control. * The loop exits when a definitive answer is reached or * or no more controls remain. * @@ -151,6 +155,340 @@ static int aci_match_set ( struct berval *subj, Operation *op, * - can be legally called with op->o_bd == NULL */ +#ifdef SLAP_OVERLAY_ACCESS +int +slap_access_always_allowed( + Operation *op, + Entry *e, + AttributeDescription *desc, + struct berval *val, + slap_access_t access, + AccessControlState *state, + slap_mask_t *maskp ) +{ + assert( maskp ); + + ACL_PRIV_SET( *maskp, ACL_ACCESS2PRIV( access ) ); + + return 1; +} + +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_PRIV_ASSIGN( mask, *maskp ); + count = 0; + memset( matches, '\0', sizeof( matches ) ); + } + + while ( ( a = slap_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 = slap_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_PRIV_ASSIGN( mask, *maskp ); + + } 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_ASSIGN( *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_INIT( 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 /* LDAP_DEVEL */ + } + assert( op->o_bd != NULL ); + + /* this is enforced in backend_add() */ + if ( op->o_bd->bd_info->bi_access_allowed ) { + /* delegate to backend */ + ret = op->o_bd->bd_info->bi_access_allowed( op, e, desc, val, access, state, &mask ); + + } else { + /* use default */ + ret = slap_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 /* !SLAP_OVERLAY_ACCESS */ + int access_allowed_mask( Operation *op, @@ -190,20 +528,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; @@ -219,7 +558,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 */ @@ -253,10 +592,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; } @@ -280,9 +617,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)" ); @@ -306,7 +644,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 ) { @@ -325,12 +664,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; @@ -341,45 +680,51 @@ 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 = slap_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 ); } } vd_access: - control = acl_mask( a, &mask, op, + control = slap_acl_mask( a, &mask, op, e, desc, val, MAXREMATCHES, matches, count, state ); if ( control != ACL_BREAK ) { break; } - memset(matches, '\0', sizeof(matches)); + memset( matches, '\0', sizeof(matches) ); } if ( ACL_IS_INVALID( mask ) ) { @@ -390,7 +735,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; } @@ -404,28 +749,29 @@ 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 /* SLAP_OVERLAY_ACCESS */ /* - * acl_get - return the acl applicable to entry e, attribute + * slap_acl_get - return the acl applicable to entry e, attribute * attr. the acl returned is suitable for use in subsequent calls to * acl_access_allowed(). */ static AccessControl * -acl_get( +slap_acl_get( AccessControl *a, int *count, Operation *op, @@ -535,7 +881,7 @@ acl_get( if( state && !( state->as_recorded & ACL_STATE_RECORDED_VD )) { state->as_recorded |= ACL_STATE_RECORDED_VD; - state->as_vd_acl = prev; + state->as_vd_acl = a; state->as_vd_acl_count = *count; state->as_vd_access = a->acl_access; state->as_vd_access_count = 1; @@ -574,11 +920,11 @@ acl_get( if ( vdnlen < patlen ) continue; - if ( a->acl_dn_style == ACL_STYLE_BASE ) { + if ( a->acl_attrval_style == ACL_STYLE_BASE ) { if ( vdnlen > patlen ) continue; - } else if ( a->acl_dn_style == ACL_STYLE_ONE ) { + } else if ( a->acl_attrval_style == ACL_STYLE_ONE ) { int rdnlen = -1; if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) ) @@ -588,11 +934,11 @@ acl_get( if ( rdnlen != vdnlen - patlen - 1 ) continue; - } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) { + } else if ( a->acl_attrval_style == ACL_STYLE_SUBTREE ) { if ( vdnlen > patlen && !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) ) continue; - } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) { + } else if ( a->acl_attrval_style == ACL_STYLE_CHILDREN ) { if ( vdnlen <= patlen ) continue; @@ -1007,7 +1353,7 @@ acl_mask_dnattr( /* - * acl_mask - modifies mask based upon the given acl and the + * slap_acl_mask - modifies mask based upon the given acl and the * requested access to entry e, attribute attr, value val. if val * is null, access to the whole attribute is assumed (all values). * @@ -1016,7 +1362,7 @@ acl_mask_dnattr( */ static slap_control_t -acl_mask( +slap_acl_mask( AccessControl *a, slap_mask_t *mask, Operation *op, @@ -1054,7 +1400,7 @@ acl_mask( "=> acl_mask: to %s by \"%s\", (%s) \n", val ? "value" : "all values", op->o_ndn.bv_val ? op->o_ndn.bv_val : "", - accessmask2str( *mask, accessmaskbuf, 1) ); + accessmask2str( *mask, accessmaskbuf, 1 ) ); if( state && ( state->as_recorded & ACL_STATE_RECORDED_VD ) @@ -1632,7 +1978,7 @@ acl_mask( continue; } - /* this could be improved by changing acl_mask so that it can deal with + /* this could be improved by changing slap_acl_mask so that it can deal with * by clauses that return grant/deny pairs. Right now, it does either * additive or subtractive rights, but not both at the same time. So, * we need to combine the grant/deny pair into a single rights mask in @@ -1785,7 +2131,7 @@ acl_mask( continue; } - /* this could be improved by changing acl_mask so that it can deal with + /* this could be improved by changing slap_acl_mask so that it can deal with * by clauses that return grant/deny pairs. Right now, it does either * additive or subtractive rights, but not both at the same time. So, * we need to combine the grant/deny pair into a single rights mask in @@ -1913,6 +2259,16 @@ acl_check_modlist( } for ( ; mlist != NULL; mlist = mlist->sml_next ) { + /* + * Internal mods are ignored by ACL_WRITE checking + */ + if ( mlist->sml_flags & SLAP_MOD_INTERNAL ) { + Debug( LDAP_DEBUG_ACL, "acl: internal mod %s:" + " modify access granted\n", + mlist->sml_desc->ad_cname.bv_val, 0, 0 ); + continue; + } + /* * no-user-modification operational attributes are ignored * by ACL_WRITE checking as any found here are not provided @@ -2817,7 +3173,7 @@ dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *r if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) { fprintf( stderr, "%s: line %d: " "inappropriate style \"%s\" in \"aci\" by clause\n", - fname, lineno, sty ); + fname, lineno, style_strings[sty] ); return -1; }