From 4abbf9c610d4fbaeee0dbdceaad1e0f94ed8e8fe Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Fri, 8 Apr 2005 00:18:24 +0000 Subject: [PATCH] implement add/delete granularity in write access (ITS#3631) --- servers/slapd/acl.c | 44 +++++++------- servers/slapd/aclparse.c | 46 ++++++++++++++- servers/slapd/back-bdb/add.c | 4 +- servers/slapd/back-bdb/delete.c | 6 +- servers/slapd/back-bdb/modrdn.c | 14 +++-- servers/slapd/back-ldbm/add.c | 4 +- servers/slapd/back-ldbm/delete.c | 7 +-- servers/slapd/back-ldbm/modrdn.c | 18 +++--- servers/slapd/back-shell/add.c | 2 +- servers/slapd/back-shell/delete.c | 2 +- servers/slapd/back-shell/modrdn.c | 7 ++- servers/slapd/back-sql/add.c | 4 +- servers/slapd/back-sql/delete.c | 4 +- servers/slapd/back-sql/modrdn.c | 7 ++- servers/slapd/modrdn.c | 6 +- servers/slapd/slap.h | 49 ++++++++++++---- servers/slapd/slapi/slapi_utils.c | 2 + tests/data/acl.out.master | 16 +++++ tests/data/slapd-acl.conf | 6 ++ tests/scripts/test006-acls | 97 +++++++++++++++++++++++++++++++ 20 files changed, 273 insertions(+), 72 deletions(-) diff --git a/servers/slapd/acl.c b/servers/slapd/acl.c index b26162e052..7404cda5b0 100644 --- a/servers/slapd/acl.c +++ b/servers/slapd/acl.c @@ -164,22 +164,26 @@ access_allowed_mask( int ret = 1; int count; AccessControl *a = NULL; - Backend *be; - int be_null = 0; + Backend *be; + int be_null = 0; #ifdef LDAP_DEBUG - char accessmaskbuf[ACCESSMASK_MAXLEN]; + char accessmaskbuf[ACCESSMASK_MAXLEN]; #endif - slap_mask_t mask; - slap_control_t control; - const char *attr; - regmatch_t matches[MAXREMATCHES]; - int st_same_attr = 0; - static AccessControlState state_init = ACL_STATE_INIT; + 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; + static AccessControlState state_init = ACL_STATE_INIT; assert( e != NULL ); assert( desc != NULL ); - assert( access > ACL_NONE ); + + access_level = ACL_LEVEL( access ); + + assert( access_level > ACL_NONE ); if ( maskp ) ACL_INVALIDATE( *maskp ); attr = desc->ad_cname.bv_val; @@ -187,7 +191,7 @@ access_allowed_mask( assert( attr != NULL ); if( op && op->o_is_auth_check && - ( access == ACL_SEARCH || access == ACL_READ )) + ( access_level == ACL_SEARCH || access_level == ACL_READ )) { access = ACL_AUTH; } @@ -265,7 +269,7 @@ access_allowed_mask( * by ACL_WRITE checking as any found here are not provided * by the user */ - if ( access >= ACL_WRITE && is_at_no_user_mod( desc->ad_type ) + 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 ) { @@ -280,9 +284,9 @@ access_allowed_mask( Debug( LDAP_DEBUG_ACL, "=> access_allowed: backend default %s access %s to \"%s\"\n", access2str( access ), - be->be_dfltaccess >= access ? "granted" : "denied", + be->be_dfltaccess >= access_level ? "granted" : "denied", op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" ); - ret = be->be_dfltaccess >= access; + ret = be->be_dfltaccess >= access_level; if ( maskp ) { int i; @@ -302,8 +306,8 @@ access_allowed_mask( Debug( LDAP_DEBUG_ACL, "=> access_allowed: global default %s access %s to \"%s\"\n", access2str( access ), - frontendDB->be_dfltaccess >= access ? "granted" : "denied", op->o_dn.bv_val ); - ret = frontendDB->be_dfltaccess >= access; + frontendDB->be_dfltaccess >= access_level ? "granted" : "denied", op->o_dn.bv_val ); + ret = frontendDB->be_dfltaccess >= access_level; if ( maskp ) { int i; @@ -1929,7 +1933,7 @@ acl_check_modlist( * This prevents abuse from selfwriters. */ if ( ! access_allowed( op, e, - mlist->sml_desc, NULL, ACL_WRITE, &state ) ) + mlist->sml_desc, NULL, ACL_WDEL, &state ) ) { ret = 0; goto done; @@ -1947,7 +1951,7 @@ acl_check_modlist( bv->bv_val != NULL; bv++ ) { if ( ! access_allowed( op, e, - mlist->sml_desc, bv, ACL_WRITE, &state ) ) + mlist->sml_desc, bv, ACL_WADD, &state ) ) { ret = 0; goto done; @@ -1958,7 +1962,7 @@ acl_check_modlist( case LDAP_MOD_DELETE: if ( mlist->sml_values == NULL ) { if ( ! access_allowed( op, e, - mlist->sml_desc, NULL, ACL_WRITE, NULL ) ) + mlist->sml_desc, NULL, ACL_WDEL, NULL ) ) { ret = 0; goto done; @@ -1970,7 +1974,7 @@ acl_check_modlist( bv->bv_val != NULL; bv++ ) { if ( ! access_allowed( op, e, - mlist->sml_desc, bv, ACL_WRITE, &state ) ) + mlist->sml_desc, bv, ACL_WDEL, &state ) ) { ret = 0; goto done; diff --git a/servers/slapd/aclparse.c b/servers/slapd/aclparse.c index fddae6b13a..43f93f827f 100644 --- a/servers/slapd/aclparse.c +++ b/servers/slapd/aclparse.c @@ -1816,6 +1816,12 @@ accessmask2str( slap_mask_t mask, char *buf, int debug ) } else if ( ACL_LVL_IS_WRITE(mask) ) { ptr = lutil_strcopy( ptr, "write" ); + } else if ( ACL_LVL_IS_WADD(mask) ) { + ptr = lutil_strcopy( ptr, "add" ); + + } else if ( ACL_LVL_IS_WDEL(mask) ) { + ptr = lutil_strcopy( ptr, "delete" ); + } else if ( ACL_LVL_IS_MANAGE(mask) ) { ptr = lutil_strcopy( ptr, "manage" ); @@ -1850,6 +1856,16 @@ accessmask2str( slap_mask_t mask, char *buf, int debug ) *ptr++ = 'w'; } + if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WADD) ) { + none = 0; + *ptr++ = 'a'; + } + + if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WDEL) ) { + none = 0; + *ptr++ = 'z'; + } + if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) { none = 0; *ptr++ = 'r'; @@ -1877,7 +1893,7 @@ accessmask2str( slap_mask_t mask, char *buf, int debug ) if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) { none = 0; - *ptr++ = 'n'; + *ptr++ = '0'; } if ( none ) { @@ -1922,6 +1938,12 @@ str2accessmask( const char *str ) } else if( TOLOWER((unsigned char) str[i]) == 'w' ) { ACL_PRIV_SET(mask, ACL_PRIV_WRITE); + } else if( TOLOWER((unsigned char) str[i]) == 'a' ) { + ACL_PRIV_SET(mask, ACL_PRIV_WADD); + + } else if( TOLOWER((unsigned char) str[i]) == 'z' ) { + ACL_PRIV_SET(mask, ACL_PRIV_WDEL); + } else if( TOLOWER((unsigned char) str[i]) == 'r' ) { ACL_PRIV_SET(mask, ACL_PRIV_READ); @@ -1964,6 +1986,12 @@ str2accessmask( const char *str ) } else if ( strcasecmp( str, "read" ) == 0 ) { ACL_LVL_ASSIGN_READ(mask); + } else if ( strcasecmp( str, "add" ) == 0 ) { + ACL_LVL_ASSIGN_WADD(mask); + + } else if ( strcasecmp( str, "delete" ) == 0 ) { + ACL_LVL_ASSIGN_WDEL(mask); + } else if ( strcasecmp( str, "write" ) == 0 ) { ACL_LVL_ASSIGN_WRITE(mask); @@ -2008,8 +2036,8 @@ acl_usage( void ) " ::= exact | regex | ip | path\n" " ::= exact | regex | base(Object) | sub(tree)\n" " ::= [[real]self]{|}\n" - " ::= none|disclose|auth|compare|search|read|write|manage\n" - " ::= {=|+|-}{0|d|x|c|s|r|w|m}+\n" + " ::= none|disclose|auth|compare|search|read|{write|add|delete}|manage\n" + " ::= {=|+|-}{0|d|x|c|s|r|{w|a|z}|m}+\n" " ::= [ stop | continue | break ]\n" ); exit( EXIT_FAILURE ); @@ -2192,6 +2220,12 @@ access2str( slap_access_t access ) } else if ( access == ACL_WRITE ) { return "write"; + } else if ( access == ACL_WADD ) { + return "add"; + + } else if ( access == ACL_WDEL ) { + return "delete"; + } else if ( access == ACL_MANAGE ) { return "manage"; @@ -2224,6 +2258,12 @@ str2access( const char *str ) } else if ( strcasecmp( str, "write" ) == 0 ) { return ACL_WRITE; + } else if ( strcasecmp( str, "add" ) == 0 ) { + return ACL_WADD; + + } else if ( strcasecmp( str, "delete" ) == 0 ) { + return ACL_WDEL; + } else if ( strcasecmp( str, "manage" ) == 0 ) { return ACL_MANAGE; } diff --git a/servers/slapd/back-bdb/add.c b/servers/slapd/back-bdb/add.c index f94448412c..b8554453d9 100644 --- a/servers/slapd/back-bdb/add.c +++ b/servers/slapd/back-bdb/add.c @@ -177,7 +177,7 @@ retry: /* transaction retry */ } rs->sr_err = access_allowed( op, p, - children, NULL, ACL_WRITE, NULL ); + children, NULL, ACL_WADD, NULL ); if ( ! rs->sr_err ) { switch( opinfo.boi_err ) { @@ -272,7 +272,7 @@ retry: /* transaction retry */ } rs->sr_err = access_allowed( op, op->oq_add.rs_e, - entry, NULL, ACL_WRITE, NULL ); + entry, NULL, ACL_WADD, NULL ); if ( ! rs->sr_err ) { switch( opinfo.boi_err ) { diff --git a/servers/slapd/back-bdb/delete.c b/servers/slapd/back-bdb/delete.c index f5910ef9a3..e2a8561572 100644 --- a/servers/slapd/back-bdb/delete.c +++ b/servers/slapd/back-bdb/delete.c @@ -202,7 +202,7 @@ retry: /* transaction retry */ /* check parent for "children" acl */ rs->sr_err = access_allowed( op, p, - children, NULL, ACL_WRITE, NULL ); + children, NULL, ACL_WDEL, NULL ); if ( !rs->sr_err ) { switch( opinfo.boi_err ) { @@ -228,7 +228,7 @@ retry: /* transaction retry */ /* check parent for "children" acl */ rs->sr_err = access_allowed( op, p, - children, NULL, ACL_WRITE, NULL ); + children, NULL, ACL_WDEL, NULL ); p = NULL; @@ -266,7 +266,7 @@ retry: /* transaction retry */ } rs->sr_err = access_allowed( op, e, - entry, NULL, ACL_WRITE, NULL ); + entry, NULL, ACL_WDEL, NULL ); if ( !rs->sr_err ) { switch( opinfo.boi_err ) { diff --git a/servers/slapd/back-bdb/modrdn.c b/servers/slapd/back-bdb/modrdn.c index 4385e8e92c..3bbf08d54b 100644 --- a/servers/slapd/back-bdb/modrdn.c +++ b/servers/slapd/back-bdb/modrdn.c @@ -289,7 +289,10 @@ retry: /* transaction retry */ /* check parent for "children" acl */ rs->sr_err = access_allowed( op, p, - children, NULL, ACL_WRITE, NULL ); + children, NULL, + op->oq_modrdn.rs_newSup == NULL ? + ACL_WRITE : ACL_WDEL, + NULL ); if ( ! rs->sr_err ) { switch( opinfo.boi_err ) { @@ -330,7 +333,10 @@ retry: /* transaction retry */ /* check parent for "children" acl */ rs->sr_err = access_allowed( op, p, - children, NULL, ACL_WRITE, NULL ); + children, NULL, + op->oq_modrdn.rs_newSup == NULL ? + ACL_WRITE : ACL_WDEL, + NULL ); p = NULL; @@ -437,7 +443,7 @@ retry: /* transaction retry */ /* check newSuperior for "children" acl */ rs->sr_err = access_allowed( op, np, children, - NULL, ACL_WRITE, NULL ); + NULL, ACL_WADD, NULL ); if( ! rs->sr_err ) { switch( opinfo.boi_err ) { @@ -492,7 +498,7 @@ retry: /* transaction retry */ /* check parent for "children" acl */ rs->sr_err = access_allowed( op, np, - children, NULL, ACL_WRITE, NULL ); + children, NULL, ACL_WADD, NULL ); np = NULL; diff --git a/servers/slapd/back-ldbm/add.c b/servers/slapd/back-ldbm/add.c index 6bc32c1f22..25cb07cc0b 100644 --- a/servers/slapd/back-ldbm/add.c +++ b/servers/slapd/back-ldbm/add.c @@ -62,7 +62,7 @@ ldbm_back_add( #endif if ( !access_allowed( op, op->oq_add.rs_e, - entry, NULL, ACL_WRITE, NULL ) ) + entry, NULL, ACL_WADD, NULL ) ) { Debug( LDAP_DEBUG_TRACE, "no write access to entry\n", 0, 0, 0 ); @@ -131,7 +131,7 @@ ldbm_back_add( return rs->sr_err; } - if ( ! access_allowed( op, p, children, NULL, ACL_WRITE, NULL ) ) { + if ( ! access_allowed( op, p, children, NULL, ACL_WADD, NULL ) ) { /* free parent and writer lock */ cache_return_entry_w( &li->li_cache, p ); ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock); diff --git a/servers/slapd/back-ldbm/delete.c b/servers/slapd/back-ldbm/delete.c index dc6256ac22..6db0d31dbf 100644 --- a/servers/slapd/back-ldbm/delete.c +++ b/servers/slapd/back-ldbm/delete.c @@ -77,8 +77,7 @@ ldbm_back_delete( } /* check entry for "entry" acl */ - if ( ! access_allowed( op, e, - entry, NULL, ACL_WRITE, NULL ) ) + if ( ! access_allowed( op, e, entry, NULL, ACL_WDEL, NULL ) ) { Debug( LDAP_DEBUG_TRACE, "<=- ldbm_back_delete: no write access to entry\n", 0, @@ -134,7 +133,7 @@ ldbm_back_delete( /* check parent for "children" acl */ if ( ! access_allowed( op, p, - children, NULL, ACL_WRITE, NULL ) ) + children, NULL, ACL_WDEL, NULL ) ) { Debug( LDAP_DEBUG_TRACE, "<=- ldbm_back_delete: no access to parent\n", 0, @@ -153,7 +152,7 @@ ldbm_back_delete( p = (Entry *)&slap_entry_root; rc = access_allowed( op, p, - children, NULL, ACL_WRITE, NULL ); + children, NULL, ACL_WDEL, NULL ); p = NULL; /* check parent for "children" acl */ diff --git a/servers/slapd/back-ldbm/modrdn.c b/servers/slapd/back-ldbm/modrdn.c index 4df7478e0c..c999b27b64 100644 --- a/servers/slapd/back-ldbm/modrdn.c +++ b/servers/slapd/back-ldbm/modrdn.c @@ -101,8 +101,7 @@ ldbm_back_modrdn( } /* check entry for "entry" acl */ - if ( ! access_allowed( op, e, - entry, NULL, ACL_WRITE, NULL ) ) + if ( ! access_allowed( op, e, entry, NULL, ACL_WRITE, NULL ) ) { Debug( LDAP_DEBUG_TRACE, "<=- ldbm_back_modrdn: no write access to entry\n", 0, @@ -163,8 +162,10 @@ ldbm_back_modrdn( } /* check parent for "children" acl */ - if ( ! access_allowed( op, p, - children, NULL, ACL_WRITE, NULL ) ) + if ( ! access_allowed( op, p, children, NULL, + op->oq_modrdn.rs_newSup != NULL ? + ACL_WDEL : ACL_WRITE, + NULL ) ) { Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0, 0, 0 ); @@ -197,7 +198,10 @@ ldbm_back_modrdn( p = (Entry *)&slap_entry_root; can_access = access_allowed( op, p, - children, NULL, ACL_WRITE, NULL ); + children, NULL, + op->oq_modrdn.rs_newSup ? + ACL_WDEL : ACL_WRITE, + NULL ); p = NULL; /* check parent for "children" acl */ @@ -270,7 +274,7 @@ ldbm_back_modrdn( /* check newSuperior for "children" acl */ if ( !access_allowed( op, np, children, NULL, - ACL_WRITE, NULL ) ) + ACL_WADD, NULL ) ) { Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: no wr to newSup children\n", @@ -316,7 +320,7 @@ ldbm_back_modrdn( np = (Entry *)&slap_entry_root; can_access = access_allowed( op, np, - children, NULL, ACL_WRITE, NULL ); + children, NULL, ACL_WADD, NULL ); np = NULL; /* check parent for "children" acl */ diff --git a/servers/slapd/back-shell/add.c b/servers/slapd/back-shell/add.c index 120ed0e9ce..3ae45cec70 100644 --- a/servers/slapd/back-shell/add.c +++ b/servers/slapd/back-shell/add.c @@ -55,7 +55,7 @@ shell_back_add( } if ( ! access_allowed( op, op->oq_add.rs_e, - entry, NULL, ACL_WRITE, NULL ) ) + entry, NULL, ACL_WADD, NULL ) ) { send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, NULL ); return -1; diff --git a/servers/slapd/back-shell/delete.c b/servers/slapd/back-shell/delete.c index 8721efb082..df81625688 100644 --- a/servers/slapd/back-shell/delete.c +++ b/servers/slapd/back-shell/delete.c @@ -64,7 +64,7 @@ shell_back_delete( e.e_private = NULL; if ( ! access_allowed( op, &e, - entry, NULL, ACL_WRITE, NULL ) ) + entry, NULL, ACL_WDEL, NULL ) ) { send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, NULL ); return -1; diff --git a/servers/slapd/back-shell/modrdn.c b/servers/slapd/back-shell/modrdn.c index 6388f17f6d..60484def5b 100644 --- a/servers/slapd/back-shell/modrdn.c +++ b/servers/slapd/back-shell/modrdn.c @@ -63,8 +63,9 @@ shell_back_modrdn( e.e_bv.bv_val = NULL; e.e_private = NULL; - if ( ! access_allowed( op, &e, - entry, NULL, ACL_WRITE, NULL ) ) + if ( ! access_allowed( op, &e, entry, NULL, + op->oq_modrdn.rs_newSup ? ACL_WDEL : ACL_WRITE, + NULL ) ) { send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, NULL ); return -1; @@ -83,7 +84,7 @@ shell_back_modrdn( fprintf( wfp, "dn: %s\n", op->o_req_dn.bv_val ); fprintf( wfp, "newrdn: %s\n", op->oq_modrdn.rs_newrdn.bv_val ); fprintf( wfp, "deleteoldrdn: %d\n", op->oq_modrdn.rs_deleteoldrdn ? 1 : 0 ); - if (op->oq_modrdn.rs_newSup != NULL) { + if ( op->oq_modrdn.rs_newSup != NULL ) { fprintf( wfp, "newSuperior: %s\n", op->oq_modrdn.rs_newSup->bv_val ); } fclose( wfp ); diff --git a/servers/slapd/back-sql/add.c b/servers/slapd/back-sql/add.c index 1e46866dd8..40aac5a3e4 100644 --- a/servers/slapd/back-sql/add.c +++ b/servers/slapd/back-sql/add.c @@ -1152,7 +1152,7 @@ backsql_add( Operation *op, SlapReply *rs ) /* check "children" pseudo-attribute access to parent */ if ( !access_allowed( op, &p, slap_schema.si_ad_children, - NULL, ACL_WRITE, NULL ) ) + NULL, ACL_WADD, NULL ) ) { rs->sr_err = LDAP_INSUFFICIENT_ACCESS; e = &p; @@ -1170,7 +1170,7 @@ backsql_add( Operation *op, SlapReply *rs ) if ( !access_allowed_mask( op, op->ora_e, slap_schema.si_ad_entry, - NULL, ACL_WRITE, NULL, &mask ) ) + NULL, ACL_WADD, NULL, &mask ) ) { rs->sr_err = LDAP_INSUFFICIENT_ACCESS; e = op->ora_e; diff --git a/servers/slapd/back-sql/delete.c b/servers/slapd/back-sql/delete.c index 9d85cce33a..5659541268 100644 --- a/servers/slapd/back-sql/delete.c +++ b/servers/slapd/back-sql/delete.c @@ -162,7 +162,7 @@ backsql_delete( Operation *op, SlapReply *rs ) } if ( !access_allowed( op, &d, slap_schema.si_ad_entry, - NULL, ACL_WRITE, NULL ) ) + NULL, ACL_WDEL, NULL ) ) { Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " "no write access to entry\n", @@ -238,7 +238,7 @@ backsql_delete( Operation *op, SlapReply *rs ) /* check parent for "children" acl */ if ( !access_allowed( op, &p, slap_schema.si_ad_children, - NULL, ACL_WRITE, NULL ) ) + NULL, ACL_WDEL, NULL ) ) { Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " "no write access to parent\n", diff --git a/servers/slapd/back-sql/modrdn.c b/servers/slapd/back-sql/modrdn.c index a1578e565d..55acd62d2d 100644 --- a/servers/slapd/back-sql/modrdn.c +++ b/servers/slapd/back-sql/modrdn.c @@ -194,8 +194,9 @@ backsql_modrdn( Operation *op, SlapReply *rs ) goto done; } - if ( !access_allowed( op, &p, slap_schema.si_ad_children, - NULL, ACL_WRITE, NULL ) ) { + if ( !access_allowed( op, &p, slap_schema.si_ad_children, NULL, + newSuperior ? ACL_WDEL : ACL_WRITE, NULL ) ) + { Debug( LDAP_DEBUG_TRACE, " no access to parent\n", 0, 0, 0 ); rs->sr_err = LDAP_INSUFFICIENT_ACCESS; goto done; @@ -250,7 +251,7 @@ backsql_modrdn( Operation *op, SlapReply *rs ) #endif /* ! BACKSQL_ARBITRARY_KEY */ if ( !access_allowed( op, &n, slap_schema.si_ad_children, - NULL, ACL_WRITE, NULL ) ) { + NULL, ACL_WADD, NULL ) ) { Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): " "no access to new parent \"%s\"\n", new_pdn->bv_val, 0, 0 ); diff --git a/servers/slapd/modrdn.c b/servers/slapd/modrdn.c index c9171d709b..b0f8d3e028 100644 --- a/servers/slapd/modrdn.c +++ b/servers/slapd/modrdn.c @@ -450,7 +450,7 @@ slap_modrdn2mods( /* ACL check of newly added attrs */ if ( op->o_bd && !access_allowed( op, e, desc, - &new_rdn[a_cnt]->la_value, ACL_WRITE, NULL ) ) { + &new_rdn[a_cnt]->la_value, ACL_WADD, NULL ) ) { Debug( LDAP_DEBUG_TRACE, "slap_modrdn2modlist: access to attr \"%s\" " "(new) not allowed\n", @@ -500,9 +500,9 @@ slap_modrdn2mods( goto done; } - /* ACL check of newly added attrs */ + /* ACL check of old rdn attrs removal */ if ( op->o_bd && !access_allowed( op, e, desc, - &old_rdn[d_cnt]->la_value, ACL_WRITE, + &old_rdn[d_cnt]->la_value, ACL_WDEL, NULL ) ) { Debug( LDAP_DEBUG_TRACE, "slap_modrdn2modlist: access " diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index 2f855c9ef6..fc020433c2 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -1156,7 +1156,19 @@ typedef enum slap_access_e { ACL_SEARCH, ACL_READ, ACL_WRITE, - ACL_MANAGE + ACL_MANAGE, + + /* ACL level mask and modifiers */ + ACL_LEVEL_MASK = 0x000f, + ACL_QUALIFIER1 = 0x0100, + ACL_QUALIFIER2 = 0x0200, + ACL_QUALIFIER3 = 0x0400, + ACL_QUALIFIER4 = 0x0800, + ACL_QUALIFIER_MASK = 0x0f00, + + /* write granularity */ + ACL_WADD = ACL_WRITE|ACL_QUALIFIER1, + ACL_WDEL = ACL_WRITE|ACL_QUALIFIER2 } slap_access_t; typedef enum slap_control_e { @@ -1246,7 +1258,11 @@ typedef struct slap_dn_access { typedef struct slap_access { slap_control_t a_type; -#define ACL_ACCESS2PRIV(access) (0x01U << (access)) +/* strip qualifiers */ +#define ACL_LEVEL(p) ((p) & ACL_LEVEL_MASK) +#define ACL_QUALIFIERS(p) ((p) & ~ACL_LEVEL_MASK) + +#define ACL_ACCESS2PRIV(access) ((0x01U << ACL_LEVEL((access))) | ACL_QUALIFIERS((access))) #define ACL_PRIV_NONE ACL_ACCESS2PRIV( ACL_NONE ) #define ACL_PRIV_DISCLOSE ACL_ACCESS2PRIV( ACL_DISCLOSE ) @@ -1254,25 +1270,28 @@ typedef struct slap_access { #define ACL_PRIV_COMPARE ACL_ACCESS2PRIV( ACL_COMPARE ) #define ACL_PRIV_SEARCH ACL_ACCESS2PRIV( ACL_SEARCH ) #define ACL_PRIV_READ ACL_ACCESS2PRIV( ACL_READ ) -#define ACL_PRIV_WRITE ACL_ACCESS2PRIV( ACL_WRITE ) +#define ACL_PRIV_WADD ACL_ACCESS2PRIV( ACL_WADD ) +#define ACL_PRIV_WDEL ACL_ACCESS2PRIV( ACL_WDEL ) +#define ACL_PRIV_WRITE ( ACL_PRIV_WADD | ACL_PRIV_WDEL ) #define ACL_PRIV_MANAGE ACL_ACCESS2PRIV( ACL_MANAGE ) -#define ACL_PRIV_MASK 0x00ffUL +/* NOTE: always use the highest level; current: 0x00ffUL */ +#define ACL_PRIV_MASK ((ACL_PRIV_MANAGE - 1) | ACL_QUALIFIER_MASK) /* priv flags */ #define ACL_PRIV_LEVEL 0x1000UL #define ACL_PRIV_ADDITIVE 0x2000UL -#define ACL_PRIV_SUBSTRACTIVE 0x4000UL +#define ACL_PRIV_SUBSTRACTIVE 0x4000UL /* invalid privs */ #define ACL_PRIV_INVALID 0x0UL #define ACL_PRIV_ISSET(m,p) (((m) & (p)) == (p)) -#define ACL_PRIV_ASSIGN(m,p) do { (m) = (p); } while(0) +#define ACL_PRIV_ASSIGN(m,p) do { (m) = (p); } while(0) #define ACL_PRIV_SET(m,p) do { (m) |= (p); } while(0) #define ACL_PRIV_CLR(m,p) do { (m) &= ~(p); } while(0) -#define ACL_INIT(m) ACL_PRIV_ASSIGN(m, ACL_PRIV_NONE) +#define ACL_INIT(m) ACL_PRIV_ASSIGN(m, ACL_PRIV_NONE) #define ACL_INVALIDATE(m) ACL_PRIV_ASSIGN(m, ACL_PRIV_INVALID) #define ACL_GRANT(m,a) ACL_PRIV_ISSET((m),ACL_ACCESS2PRIV(a)) @@ -1281,7 +1300,7 @@ typedef struct slap_access { #define ACL_IS_LEVEL(m) ACL_PRIV_ISSET((m),ACL_PRIV_LEVEL) #define ACL_IS_ADDITIVE(m) ACL_PRIV_ISSET((m),ACL_PRIV_ADDITIVE) -#define ACL_IS_SUBTRACTIVE(m) ACL_PRIV_ISSET((m),ACL_PRIV_SUBSTRACTIVE) +#define ACL_IS_SUBTRACTIVE(m) ACL_PRIV_ISSET((m),ACL_PRIV_SUBSTRACTIVE) #define ACL_LVL_NONE (ACL_PRIV_NONE|ACL_PRIV_LEVEL) #define ACL_LVL_DISCLOSE (ACL_PRIV_DISCLOSE|ACL_LVL_NONE) @@ -1289,18 +1308,22 @@ typedef struct slap_access { #define ACL_LVL_COMPARE (ACL_PRIV_COMPARE|ACL_LVL_AUTH) #define ACL_LVL_SEARCH (ACL_PRIV_SEARCH|ACL_LVL_COMPARE) #define ACL_LVL_READ (ACL_PRIV_READ|ACL_LVL_SEARCH) +#define ACL_LVL_WADD (ACL_PRIV_WADD|ACL_LVL_READ) +#define ACL_LVL_WDEL (ACL_PRIV_WDEL|ACL_LVL_READ) #define ACL_LVL_WRITE (ACL_PRIV_WRITE|ACL_LVL_READ) #define ACL_LVL_MANAGE (ACL_PRIV_MANAGE|ACL_LVL_WRITE) #define ACL_LVL(m,l) (((m)&ACL_PRIV_MASK) == ((l)&ACL_PRIV_MASK)) #define ACL_LVL_IS_NONE(m) ACL_LVL((m),ACL_LVL_NONE) -#define ACL_LVL_IS_DISCLOSE(m) ACL_LVL((m),ACL_LVL_DISCLOSE) +#define ACL_LVL_IS_DISCLOSE(m) ACL_LVL((m),ACL_LVL_DISCLOSE) #define ACL_LVL_IS_AUTH(m) ACL_LVL((m),ACL_LVL_AUTH) -#define ACL_LVL_IS_COMPARE(m) ACL_LVL((m),ACL_LVL_COMPARE) -#define ACL_LVL_IS_SEARCH(m) ACL_LVL((m),ACL_LVL_SEARCH) +#define ACL_LVL_IS_COMPARE(m) ACL_LVL((m),ACL_LVL_COMPARE) +#define ACL_LVL_IS_SEARCH(m) ACL_LVL((m),ACL_LVL_SEARCH) #define ACL_LVL_IS_READ(m) ACL_LVL((m),ACL_LVL_READ) +#define ACL_LVL_IS_WADD(m) ACL_LVL((m),ACL_LVL_WADD) +#define ACL_LVL_IS_WDEL(m) ACL_LVL((m),ACL_LVL_WDEL) #define ACL_LVL_IS_WRITE(m) ACL_LVL((m),ACL_LVL_WRITE) -#define ACL_LVL_IS_MANAGE(m) ACL_LVL((m),ACL_LVL_MANAGE) +#define ACL_LVL_IS_MANAGE(m) ACL_LVL((m),ACL_LVL_MANAGE) #define ACL_LVL_ASSIGN_NONE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_NONE) #define ACL_LVL_ASSIGN_DISCLOSE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_DISCLOSE) @@ -1308,6 +1331,8 @@ typedef struct slap_access { #define ACL_LVL_ASSIGN_COMPARE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_COMPARE) #define ACL_LVL_ASSIGN_SEARCH(m) ACL_PRIV_ASSIGN((m),ACL_LVL_SEARCH) #define ACL_LVL_ASSIGN_READ(m) ACL_PRIV_ASSIGN((m),ACL_LVL_READ) +#define ACL_LVL_ASSIGN_WADD(m) ACL_PRIV_ASSIGN((m),ACL_LVL_WADD) +#define ACL_LVL_ASSIGN_WDEL(m) ACL_PRIV_ASSIGN((m),ACL_LVL_WDEL) #define ACL_LVL_ASSIGN_WRITE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_WRITE) #define ACL_LVL_ASSIGN_MANAGE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_MANAGE) diff --git a/servers/slapd/slapi/slapi_utils.c b/servers/slapd/slapi/slapi_utils.c index 244ec53701..7590a93912 100644 --- a/servers/slapd/slapi/slapi_utils.c +++ b/servers/slapd/slapi/slapi_utils.c @@ -3364,6 +3364,7 @@ int slapi_access_allowed( Slapi_PBlock *pb, Slapi_Entry *e, char *attr, case SLAPI_ACL_DELETE: case SLAPI_ACL_ADD: case SLAPI_ACL_SELF: + /* FIXME: handle ACL_WADD/ACL_WDEL */ slap_access = ACL_WRITE; break; default: @@ -4000,6 +4001,7 @@ int slapi_int_access_allowed( Operation *op, switch ( access ) { case ACL_WRITE: + /* FIXME: handle ACL_WADD/ACL_WDEL */ slap_access |= SLAPI_ACL_ADD | SLAPI_ACL_DELETE | SLAPI_ACL_WRITE; break; case ACL_READ: diff --git a/tests/data/acl.out.master b/tests/data/acl.out.master index ea2abb2317..8b0134895e 100644 --- a/tests/data/acl.out.master +++ b/tests/data/acl.out.master @@ -21,6 +21,22 @@ facsimileTelephoneNumber: +1 313 555 4332 telephoneNumber: +1 313 555 0895 # Using ldapsearch to retrieve all the entries... +dn: ou=Add/Delete,dc=example,dc=com +objectClass: organizationalUnit +ou: Add/Delete + +dn: cn=Added by Bjorn (must succeed),ou=Add/Delete,dc=example,dc=com +objectClass: inetOrgPerson +cn: Added by Bjorn (must succeed) +sn: None +description: this attribute value has been added __after__entry creation +description: Bjorn will try to delete this attribute value (should fail) + +dn: cn=Added by Bjorn (renamed by Jaj),ou=Add/Delete,dc=example,dc=com +objectClass: inetOrgPerson +sn: None +cn: Added by Bjorn (renamed by Jaj) + dn: cn=All Staff,ou=Groups,dc=example,dc=com member: cn=Manager,dc=example,dc=com member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam diff --git a/tests/data/slapd-acl.conf b/tests/data/slapd-acl.conf index f01b747f8f..c56586bff1 100644 --- a/tests/data/slapd-acl.conf +++ b/tests/data/slapd-acl.conf @@ -95,6 +95,12 @@ access to dn.exact="cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com" access to filter="(name=X*Y*Z)" by * continue +access to dn.subtree="ou=Add/Delete,dc=example,dc=com" + by dn.exact="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" add + by dn.exact="cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" delete + by dn.exact="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com" write + by * read + # fall into global ACLs #monitor#database monitor diff --git a/tests/scripts/test006-acls b/tests/scripts/test006-acls index 338b145fc9..20a367f6ac 100755 --- a/tests/scripts/test006-acls +++ b/tests/scripts/test006-acls @@ -172,6 +172,103 @@ description: added by bjensen (should fail) - EOMODS6 +$LDAPMODIFY -D "$MANAGERDN" -h $LOCALHOST -p $PORT1 -w $PASSWD >> \ + $TESTOUT 2>&1 << EOMODS7 +dn: ou=Add/Delete,dc=example,dc=com +changetype: add +objectClass: organizationalUnit +ou: Add/Delete +EOMODS7 + +$LDAPMODIFY -D "$BABSDN" -h $LOCALHOST -p $PORT1 -w bjensen >> \ + $TESTOUT 2>&1 << EOMODS8 +dn: cn=Added by Babs (must fail),ou=Add/Delete,dc=example,dc=com +changetype: add +objectClass: inetOrgPerson +cn: Added by Babs (must fail) +sn: None +EOMODS8 + +$LDAPMODIFY -D "$BJORNSDN" -h $LOCALHOST -p $PORT1 -w bjorn >> \ + $TESTOUT 2>&1 << EOMODS9 +dn: cn=Added by Bjorn (must succeed),ou=Add/Delete,dc=example,dc=com +changetype: add +objectClass: inetOrgPerson +cn: Added by Bjorn (must succeed) +sn: None + +dn: cn=Added by Bjorn (will be deleted),ou=Add/Delete,dc=example,dc=com +changetype: add +objectClass: inetOrgPerson +cn: Added by Bjorn (will be deleted) +sn: None + +dn: cn=Added by Bjorn (will be renamed),ou=Add/Delete,dc=example,dc=com +changetype: add +objectClass: inetOrgPerson +cn: Added by Bjorn (will be renamed) +sn: None + +dn: cn=Added by Bjorn (must succeed),ou=Add/Delete,dc=example,dc=com +changetype: modify +add: description +description: this attribute value has been added __after__entry creation +description: this attribute value will be deleted by Babs (must succeed) +description: Bjorn will try to delete this attribute value (should fail) +- +EOMODS9 + +$LDAPMODIFY -D "$BJORNSDN" -h $LOCALHOST -p $PORT1 -w bjorn >> \ + $TESTOUT 2>&1 << EOMODS10 +dn: cn=Added by Bjorn (will be deleted),ou=Add/Delete,dc=example,dc=com +changetype: delete +EOMODS10 + +$LDAPMODIFY -D "$BJORNSDN" -h $LOCALHOST -p $PORT1 -w bjorn >> \ + $TESTOUT 2>&1 << EOMODS11 +dn: cn=Added by Bjorn (will be renamed),ou=Add/Delete,dc=example,dc=com +changetype: modrdn +newrdn: cn=Added by Bjorn (renamed by Bjorn) +deleteoldrdn: 1 +EOMODS11 + +$LDAPMODIFY -D "$BABSDN" -h $LOCALHOST -p $PORT1 -w bjensen >> \ + $TESTOUT 2>&1 << EOMODS12 +dn: cn=Added by Bjorn (will be renamed),ou=Add/Delete,dc=example,dc=com +changetype: modrdn +newrdn: cn=Added by Bjorn (renamed by Babs) +deleteoldrdn: 1 +EOMODS12 + +$LDAPMODIFY -D "$JAJDN" -h $LOCALHOST -p $PORT1 -w jaj >> \ + $TESTOUT 2>&1 << EOMODS13 +dn: cn=Added by Bjorn (will be renamed),ou=Add/Delete,dc=example,dc=com +changetype: modrdn +newrdn: cn=Added by Bjorn (renamed by Jaj) +deleteoldrdn: 1 +EOMODS13 + +$LDAPMODIFY -D "$BJORNSDN" -h $LOCALHOST -p $PORT1 -w bjorn >> \ + $TESTOUT 2>&1 << EOMODS14 +dn: cn=Added by Bjorn (must succeed),ou=Add/Delete,dc=example,dc=com +changetype: modify +delete: description +description: Bjorn will try to delete this attribute value (should fail) +- +EOMODS14 + +$LDAPMODIFY -D "$BABSDN" -h $LOCALHOST -p $PORT1 -w bjensen >> \ + $TESTOUT 2>&1 << EOMODS15 +dn: cn=Added by Bjorn (will be deleted),ou=Add/Delete,dc=example,dc=com +changetype: delete + +dn: cn=Added by Bjorn (must succeed),ou=Add/Delete,dc=example,dc=com +changetype: modify +delete: description +description: this attribute value will be deleted by Babs (must succeed) +- +EOMODS15 + echo "Using ldapsearch to retrieve all the entries..." echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT $LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT1 \ -- 2.39.5