From f6829ee903c2cc6ddb78d7ae4754d3a20a362da6 Mon Sep 17 00:00:00 2001 From: Kurt Zeilenga Date: Thu, 21 Oct 1999 17:53:56 +0000 Subject: [PATCH] Initial commit of new ACL engine. Engine supports descrete access privs, additive/substractive rules, and rule continuation. Existing rules that use 'defaultaccess none' should be 100% compatible. Rules that rely other defaultaccess settings will require addition of explicit clauses granting the access. Needs additional testing and tuning of logs --- doc/man/man5/slapd.conf.5 | 20 +- servers/slapd/acl.c | 472 +++++++++++++++++++------------ servers/slapd/aclparse.c | 364 ++++++++++++++++++++---- servers/slapd/back-bdb2/modify.c | 4 +- servers/slapd/back-ldbm/modify.c | 6 +- servers/slapd/backend.c | 4 + servers/slapd/config.c | 34 ++- servers/slapd/proto-slap.h | 23 +- servers/slapd/result.c | 35 +-- servers/slapd/slap.h | 118 +++++--- servers/slapd/slapd.conf | 4 +- 11 files changed, 737 insertions(+), 347 deletions(-) diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5 index 2ebec753ac..321cce1099 100644 --- a/doc/man/man5/slapd.conf.5 +++ b/doc/man/man5/slapd.conf.5 @@ -67,13 +67,11 @@ overridden in a backend definition. Arguments that should be replaced by actual text are shown in brackets <>. .TP .B -access to [ by ]+ -Grant access (specified by ) to a set of entries and/or +access to [ by ]+ +Grant access (specified by ) to a set of entries and/or attributes (specified by ) by one or more requestors (specified -by ). Refer to "The SLAPD and SLURPD Administrator's Guide" for -information on using the -.B slapd -access-control mechanisms. +by ). +See Developer's FAQ (http://www.openldap.org/faq/) for details. .TP .B attributetype ( [NAME ] [DESC ] [OBSOLETE] \ @@ -136,9 +134,13 @@ distinguished name .RE .TP .B -defaultaccess [self]{ none | compare | search | read | write } -Specify the default access to grant requestors not matched by -any other access line. The default behavior is to grant read access. +defaultaccess { none | auth | compare | search | read | write } +Specify the default access level to grant requestors when +no access directives were provided for the database. +The default behavior is to grant 'read' access. It is +recommended that +.B access +directives be used instead. .TP .B idletimeout Specify the number of seconds to wait before forcibly closing diff --git a/servers/slapd/acl.c b/servers/slapd/acl.c index 1558fc7330..904fcb6a9c 100644 --- a/servers/slapd/acl.c +++ b/servers/slapd/acl.c @@ -15,9 +15,24 @@ #include "slap.h" +static AccessControl * acl_get( + AccessControl *ac, int *count, + Backend *be, Operation *op, + Entry *e, char *attr, + int nmatches, regmatch_t *matches ); + +static slap_control_t acl_mask( + AccessControl *ac, slap_access_mask_t *mask, + Backend *be, Connection *conn, Operation *op, + Entry *e, char *attr, struct berval *val, + regmatch_t *matches ); + #ifdef SLAPD_ACI_ENABLED -int aci_access_allowed (struct berval *aci, char *attr, Backend *be, Entry *e, - Operation *op, int access, char *edn, regmatch_t *matches); +static int aci_access_allowed( + Backend *be, + Operation *op, + Entry *e, char *attr, struct berval *aci, + regmatch_t *matches ); #endif static int regex_matches(char *pat, char *str, char *buf, regmatch_t *matches); @@ -28,12 +43,16 @@ static void string_expand(char *newbuf, int bufsiz, char *pattern, /* * access_allowed - check whether op->o_ndn is allowed the requested access * to entry e, attribute attr, value val. if val is null, access to - * the whole attribute is assumed (all values). this routine finds - * the applicable acl and calls acl_access_allowed() to make the - * decision. + * the whole attribute is assumed (all values). * - * returns 0 access NOT allowed - * 1 access allowed + * This routine loops through all access controls and calls + * acl_mask() on each applicable access control. + * The loop exits when a definitive answer is reached or + * or no more controls remain. + * + * returns: + * 0 access denied + * 1 access granted */ int @@ -44,138 +63,167 @@ access_allowed( Entry *e, char *attr, struct berval *val, - int access + slap_access_t access ) { - int rc; + int count; AccessControl *a; - char *edn; + slap_access_mask_t mask; + slap_control_t control; regmatch_t matches[MAXREMATCHES]; - int i; - int n; - if ( be == NULL ) { - return( 0 ); - } + Debug( LDAP_DEBUG_ACL, + "=> access_allowed: %s access to \"%s\" \"%s\" requested\n", + access2str( access ), + e->e_dn, attr ); - edn = e->e_ndn; + assert( be != NULL ); + assert( e != NULL ); + assert( attr != NULL ); + assert( access > ACL_NONE ); - Debug( LDAP_DEBUG_ACL, "\n=> access_allowed: entry (%s) attr (%s)\n", - e->e_dn, attr, 0 ); + /* grant database root access */ + if ( be != NULL && be_isroot( be, op->o_ndn ) ) { + Debug( LDAP_DEBUG_ACL, + "<= root access granted\n", + 0, 0, 0 ); + return 1; + } - /* the lastmod attributes are ignored by ACL checking */ + /* no user modify operational attributes are ignored by ACL checking */ if ( oc_check_no_usermod_attr( attr ) ) { - Debug( LDAP_DEBUG_ACL, "Operational attribute: %s access allowed\n", + Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:" + " %s access granted\n", attr, 0, 0 ); - return(1); + return 1; } + /* use backend default access if no backend acls */ + if( be != NULL && be->be_acl == NULL ) { + Debug( LDAP_DEBUG_ACL, + "=> access_allowed: backend default %s access %s to \"%s\"\n", + access2str( access ), + be->be_dfltaccess >= access ? "granted" : "denied", op->o_dn ); + + return be->be_dfltaccess >= access; + +#ifdef notdef + /* be is always non-NULL */ + /* use global default access if no global acls */ + } else if ( be == NULL && global_acl == NULL ) { + Debug( LDAP_DEBUG_ACL, + "=> access_allowed: global default %s access %s to \"%s\"\n", + access2str( access ), + global_default_access >= access ? "granted" : "denied", op->o_dn ); + + return global_default_access >= access; +#endif + } + + ACL_INIT(mask); memset(matches, 0, sizeof(matches)); + + control = ACL_BREAK; + a = NULL; + count = 0; - a = acl_get_applicable( be, op, e, attr, MAXREMATCHES, matches ); + while( a = acl_get( a, &count, be, op, e, attr, MAXREMATCHES, matches ) ) + { + int i; - if (a) { for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) { - Debug( LDAP_DEBUG_ARGS, "=> match[%d]: %d %d ", 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_ARGS, "%c", edn[n], 0, 0 ); + Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 ); } } Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 ); } + + control = acl_mask( a, &mask, be, conn, op, + e, attr, val, matches ); + + if ( control != ACL_BREAK ) { + break; + } + + memset(matches, 0, sizeof(matches)); } - rc = acl_access_allowed( a, attr, be, conn, e, val, op, access, edn, matches ); + 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); + ACL_INIT( mask ); + } - Debug( LDAP_DEBUG_ACL, "\n=> access_allowed: exit (%s) attr (%s)\n", - e->e_dn, attr, 0); + Debug( LDAP_DEBUG_ACL, + "=> access_allowed: %s access %s to \"%s\"\n", + access2str( access ), + ACL_GRANT(mask, access) ? "granted" : "denied", op->o_dn ); - return( rc ); + return ACL_GRANT(mask, access); } /* - * acl_get_applicable - return the acl applicable to entry e, attribute + * 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(). */ -AccessControl * -acl_get_applicable( +static AccessControl * +acl_get( + AccessControl *a, + int *count, Backend *be, - Operation *op, + Operation *op, Entry *e, char *attr, int nmatch, regmatch_t *matches ) { - int i; - AccessControl *a; - char *edn; - - Debug( LDAP_DEBUG_ACL, "\n=> acl_get: entry (%s) attr (%s)\n", - e->e_dn, attr, 0 ); - - if ( be_isroot( be, op->o_ndn ) ) { - Debug( LDAP_DEBUG_ACL, - "<= acl_get: no acl applicable to database root\n", 0, 0, - 0 ); - return( NULL ); - } + AccessControl *next; - edn = e->e_ndn; - - Debug( LDAP_DEBUG_ARGS, "=> acl_get: edn %s\n", edn, 0, 0 ); - - /* check for a backend-specific acl that matches the entry */ - for ( i = 1, a = be->be_acl; a != NULL; a = a->acl_next, i++ ) { - if (a->acl_dn_pat != NULL) { - Debug( LDAP_DEBUG_TRACE, "=> dnpat: [%d] %s nsub: %d\n", - i, a->acl_dn_pat, (int) a->acl_dn_re.re_nsub); - - if (regexec(&a->acl_dn_re, edn, nmatch, matches, 0)) { - continue; - - } else { - Debug( LDAP_DEBUG_TRACE, "=> acl_get:[%d] backend ACL match\n", - i, 0, 0); - } - } + assert( e != NULL ); + assert( count != NULL ); - if ( a->acl_filter != NULL ) { - if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) { - continue; - } + if( a == NULL ) { + if( be == NULL ) { + a = global_acl; + } else { + a = be->be_acl; } - Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr %s\n", i, attr, 0); + assert( a != NULL ); - if ( attr == NULL || a->acl_attrs == NULL || - charray_inlist( a->acl_attrs, attr ) ) - { - Debug( LDAP_DEBUG_ACL, "<= acl_get: [%d] backend acl %s attr: %s\n", - i, e->e_dn, attr ); - return( a ); - } - matches[0].rm_so = matches[0].rm_eo = -1; + } else { + a = a->acl_next; } - /* check for a global acl that matches the entry */ - for ( i = 1, a = global_acl; a != NULL; a = a->acl_next, i++ ) { + for ( ; a != NULL; a = a->acl_next ) { + (*count) ++; + if (a->acl_dn_pat != NULL) { - Debug( LDAP_DEBUG_TRACE, "=> dn pat: [%d] %s nsub: %d\n", - i, a->acl_dn_pat, (int) a->acl_dn_re.re_nsub); + Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n", + *count, a->acl_dn_pat, (int) a->acl_dn_re.re_nsub ); - if (regexec(&a->acl_dn_re, edn, nmatch, matches, 0)) { + if (regexec(&a->acl_dn_re, e->e_ndn, nmatch, matches, 0)) { continue; } else { - Debug( LDAP_DEBUG_TRACE, "=> acl_get: [%d] global ACL match\n", - i, 0, 0); + Debug( LDAP_DEBUG_ACL, "=> acl_get: ACL [%d] matched\n", + *count, 0, 0); } } @@ -185,25 +233,27 @@ acl_get_applicable( } } - Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr\n", i, 0, 0); + Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] check attr %s\n", + *count, attr, 0); if ( attr == NULL || a->acl_attrs == NULL || charray_inlist( a->acl_attrs, attr ) ) { - Debug( LDAP_DEBUG_ACL, "<= acl_get: [%d] global acl %s attr: %s\n", - i, e->e_dn, attr ); - return( a ); + Debug( LDAP_DEBUG_ACL, + "<= acl_get: [%d] acl %s attr: %s\n", + *count, e->e_dn, attr ); + return a; } - matches[0].rm_so = matches[0].rm_eo = -1; } - Debug( LDAP_DEBUG_ACL, "<= acl_get: no match\n", 0, 0, 0 ); + Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 ); return( NULL ); } + /* - * acl_access_allowed - check whether the given acl allows dn the + * 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). * @@ -211,54 +261,43 @@ acl_get_applicable( * 1 access allowed */ -int -acl_access_allowed( +static slap_control_t +acl_mask( AccessControl *a, - char *attr, + slap_access_mask_t *mask, Backend *be, - Connection *conn, + Connection *conn, + Operation *op, Entry *e, + char *attr, struct berval *val, - Operation *op, - int access, - char *edn, regmatch_t *matches ) { int i; Access *b; - int default_access; + + assert( a != NULL ); + assert( mask != NULL ); Debug( LDAP_DEBUG_ACL, - "\n=> acl_access_allowed: %s access to entry \"%s\"\n", - access2str( access ), e->e_dn, 0 ); + "=> acl_mask: access to entry \"%s\", attr \"%s\"\n requested\n", + e->e_dn, attr, 0 ); Debug( LDAP_DEBUG_ACL, - "\n=> acl_access_allowed: %s access to value \"%s\" by \"%s\"\n", - access2str( access ), + "=> acl_mask: to value \"%s\" by \"%s\", (%s) \n", val ? val->bv_val : "any", - op->o_ndn ? op->o_ndn : "" ); - - if ( be_isroot( be, op->o_ndn ) ) { - Debug( LDAP_DEBUG_ACL, - "<= acl_access_allowed: granted to database root\n", - 0, 0, 0 ); - return( 1 ); - } + op->o_ndn ? op->o_ndn : "", + accessmask2str( *mask ) ); - default_access = be->be_dfltaccess ? be->be_dfltaccess : global_default_access; + for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) { + slap_access_mask_t oldmask, modmask; - if ( a == NULL ) { - Debug( LDAP_DEBUG_ACL, - "<= acl_access_allowed: %s by default (no matching to)\n", - default_access >= access ? "granted" : "denied", 0, 0 ); - return( default_access >= access ); - } + ACL_INVALIDATE( modmask ); - for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) { /* AND clauses */ if ( b->a_dn_pat != NULL ) { - Debug( LDAP_DEBUG_TRACE, "<= check a_dn_pat: %s\n", + Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n", b->a_dn_pat, 0, 0); /* * if access applies to the entry itself, and the @@ -275,60 +314,60 @@ acl_access_allowed( continue; } - if ( e->e_dn == NULL || strcmp( edn, op->o_ndn ) != 0 ) { + if ( e->e_dn == NULL || strcmp( e->e_ndn, op->o_ndn ) != 0 ) { continue; } } else if ( strcmp( b->a_dn_pat, ".*" ) != 0 && - !regex_matches( b->a_dn_pat, op->o_ndn, edn, matches ) ) + !regex_matches( b->a_dn_pat, op->o_ndn, e->e_ndn, matches ) ) { continue; } } if ( b->a_sockurl_pat != NULL ) { - Debug( LDAP_DEBUG_ARGS, "<= check a_sockurl_pat: %s\n", + Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n", b->a_sockurl_pat, 0, 0 ); if ( strcmp( b->a_sockurl_pat, ".*" ) != 0 && !regex_matches( b->a_sockurl_pat, conn->c_listener_url, - edn, matches ) ) + e->e_ndn, matches ) ) { continue; } } if ( b->a_domain_pat != NULL ) { - Debug( LDAP_DEBUG_ARGS, "<= check a_domain_pat: %s\n", + Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n", b->a_domain_pat, 0, 0 ); if ( strcmp( b->a_domain_pat, ".*" ) != 0 && !regex_matches( b->a_domain_pat, conn->c_peer_domain, - edn, matches ) ) + e->e_ndn, matches ) ) { continue; } } if ( b->a_peername_pat != NULL ) { - Debug( LDAP_DEBUG_ARGS, "<= check a_peername_path: %s\n", + Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n", b->a_peername_pat, 0, 0 ); if ( strcmp( b->a_peername_pat, ".*" ) != 0 && !regex_matches( b->a_peername_pat, conn->c_peer_name, - edn, matches ) ) + e->e_ndn, matches ) ) { continue; } } if ( b->a_sockname_pat != NULL ) { - Debug( LDAP_DEBUG_ARGS, "<= check a_sockname_path: %s\n", + Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n", b->a_sockname_pat, 0, 0 ); if ( strcmp( b->a_sockname_pat, ".*" ) != 0 && !regex_matches( b->a_sockname_pat, conn->c_sock_name, - edn, matches ) ) + e->e_ndn, matches ) ) { continue; } @@ -338,7 +377,7 @@ acl_access_allowed( Attribute *at; struct berval bv; - Debug( LDAP_DEBUG_ARGS, "<= check a_dn_at: %s\n", + Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n", b->a_dn_at, 0, 0); bv.bv_val = op->o_ndn; @@ -348,14 +387,14 @@ acl_access_allowed( if ( (at = attr_find( e->e_attrs, b->a_dn_at )) != NULL && value_find( at->a_vals, &bv, at->a_syntax, 3 ) == 0 ) { - if ( ACL_IS_SELF(b->a_access) && + if ( b->a_dn_self && (val == NULL || value_cmp( &bv, val, at->a_syntax, 2 )) ) { continue; } /* asker not listed in dnattr - check for self access */ - } else if ( ! ACL_IS_SELF(b->a_access) || val == NULL || + } else if ( ! b->a_dn_self || val == NULL || value_cmp( &bv, val, at->a_syntax, 2 ) != 0 ) { continue; @@ -370,7 +409,7 @@ acl_access_allowed( * the values in the attribute group */ /* see if asker is listed in dnattr */ - string_expand(buf, sizeof(buf), b->a_group_pat, edn, matches); + string_expand(buf, sizeof(buf), b->a_group_pat, e->e_ndn, matches); if ( dn_normalize(buf) == NULL ) { /* did not expand to a valid dn */ continue; @@ -384,12 +423,12 @@ acl_access_allowed( } #ifdef SLAPD_ACI_ENABLED - if ( b->a_aci_at != NULL ) { + if ( b->a_aci_at != NULL ) { Attribute *at; /* this case works different from the others above. * since aci's themselves give permissions, we need - * to first check b->a_access, the ACL's access level. + * to first check b->a_mask, the ACL's access level. */ if( op->o_ndn == NULL || op->o_ndn[0] == '\0' ) { @@ -403,7 +442,7 @@ acl_access_allowed( /* first check if the right being requested is * higher than allowed by the ACL clause. */ - if ( ! ACL_GRANT( b->a_access, access ) ) { + if ( ! ACL_GRANT( b->a_mask, access ) ) { continue; } @@ -418,39 +457,71 @@ acl_access_allowed( * rights given by the acis. */ for ( i = 0; at->a_vals[i] != NULL; i++ ) { - if ( aci_access_allowed( at->a_vals[i], attr, be, e, op, access, edn, matches ) ) { + if ( aci_access_allowed( be, op, + e, attr, at->a_vals[i], + matches ) ) + { Debug( LDAP_DEBUG_ACL, - "<= acl_access_allowed: matched by clause #%d access granted\n", + "<= acl_mask: matched by clause #%d access granted\n", i, 0, 0 ); - return(1); + break; } } - continue; - } + + if( ACL_IS_INVALID( modmask ) ) { + continue; + } + + } else #endif + { + modmask = b->a_mask; + } + Debug( LDAP_DEBUG_ACL, - "<= acl_access_allowed: matched by clause #%d access %s\n", - i, - ACL_GRANT(b->a_access, access) ? "granted" : "denied", - 0 ); + "<= acl_mask: matched clause #%d\n", + i, 0, 0 ); - return ACL_GRANT(b->a_access, access ); - } + oldmask = *mask; - Debug( LDAP_DEBUG_ACL, - "<= acl_access_allowed: %s by default (no matching by)\n", - default_access >= access ? "granted" : "denied", 0, 0 ); + if( ACL_IS_ADDITIVE(modmask) ) { + ACL_PRIV_CLR( *mask, ACL_PRIV_LEVEL ); + ACL_PRIV_SET( *mask, modmask ); + + } else if( ACL_IS_SUBTRACTIVE(modmask) ) { + ACL_PRIV_CLR( *mask, ACL_PRIV_LEVEL ); + ACL_PRIV_CLR( *mask, modmask ); + + } else { + ACL_PRIV_ASSIGN( *mask, modmask ); + } + + Debug( LDAP_DEBUG_ACL, + "<= acl_mask: old (%s) mod (%s) new (%s)\n", + accessmask2str(oldmask), + accessmask2str(modmask), + accessmask2str(*mask) ); + + if( b->a_type == ACL_CONTINUE ) { + continue; + + } else if ( b->a_type == ACL_BREAK ) { + return ACL_BREAK; + + } else { + break; + } + } - return( default_access >= access ); + return ACL_STOP; } /* * acl_check_modlist - check access control on the given entry to see if * it allows the given modifications by the user associated with op. - * returns LDAP_SUCCESS mods allowed ok - * anything else mods not allowed - return is an error - * code indicating the problem + * returns 1 if mods allowed ok + * 0 mods not allowed */ int @@ -463,8 +534,38 @@ acl_check_modlist( ) { int i; - AccessControl *a; - char *edn = e->e_ndn; + + assert( be != NULL ); + + /* short circuit root database access */ + if ( be_isroot( be, op->o_ndn ) ) { + Debug( LDAP_DEBUG_ACL, + "<= acl_access_allowed: granted to database root\n", + 0, 0, 0 ); + return 1; + } + + /* use backend default access if no backend acls */ + if( be != NULL && be->be_acl == NULL ) { + Debug( LDAP_DEBUG_ACL, + "=> access_allowed: backend default %s access %s to \"%s\"\n", + access2str( ACL_WRITE ), + be->be_dfltaccess >= ACL_WRITE ? "granted" : "denied", op->o_dn ); + + return be->be_dfltaccess >= ACL_WRITE; + +#ifdef notdef + /* be is always non-NULL */ + /* use global default access if no global acls */ + } else if ( be == NULL && global_acl == NULL ) { + Debug( LDAP_DEBUG_ACL, + "=> access_allowed: global default %s access %s to \"%s\"\n", + access2str( ACL_WRITE ), + global_default_access >= ACL_WRITE ? "granted" : "denied", op->o_dn ); + + return global_default_access >= ACL_WRITE; +#endif + } for ( ; mlist != NULL; mlist = mlist->ml_next ) { regmatch_t matches[MAXREMATCHES]; @@ -476,9 +577,6 @@ acl_check_modlist( continue; } - a = acl_get_applicable( be, op, e, mlist->ml_type, - MAXREMATCHES, matches ); - switch ( mlist->ml_op & ~LDAP_MOD_BVALUES ) { case LDAP_MOD_REPLACE: case LDAP_MOD_ADD: @@ -486,35 +584,38 @@ acl_check_modlist( break; } for ( i = 0; mlist->ml_bvalues[i] != NULL; i++ ) { - if ( ! acl_access_allowed( a, mlist->ml_type, be, conn, e, mlist->ml_bvalues[i], - op, ACL_WRITE, edn, matches) ) + if ( ! access_allowed( be, conn, op, e, + mlist->ml_type, mlist->ml_bvalues[i], + ACL_WRITE ) ) { - return( LDAP_INSUFFICIENT_ACCESS ); + return( 0 ); } } break; case LDAP_MOD_DELETE: if ( mlist->ml_bvalues == NULL ) { - if ( ! acl_access_allowed( a, mlist->ml_type, be, conn, e, - NULL, op, ACL_WRITE, edn, matches) ) + if ( ! access_allowed( be, conn, op, e, + mlist->ml_type, NULL, + ACL_WRITE ) ) { - return( LDAP_INSUFFICIENT_ACCESS ); + return( 0 ); } break; } for ( i = 0; mlist->ml_bvalues[i] != NULL; i++ ) { - if ( ! acl_access_allowed( a, mlist->ml_type, be, conn, e, mlist->ml_bvalues[i], - op, ACL_WRITE, edn, matches) ) + if ( ! access_allowed( be, conn, op, e, + mlist->ml_type, mlist->ml_bvalues[i], + ACL_WRITE ) ) { - return( LDAP_INSUFFICIENT_ACCESS ); + return( 0 ); } } break; } } - return( LDAP_SUCCESS ); + return( 1 ); } #ifdef SLAPD_ACI_ENABLED @@ -583,10 +684,14 @@ aci_get_part (struct berval *list, int ix, char sep, struct berval *bv) } static int -aci_list_has_right (struct berval *list, int access, int action) +aci_list_has_right( + struct berval *list, + slap_access_t access, + int action) { struct berval bv; - int i, right; + int i; + slap_access_t right; for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) { if (bv.bv_len <= 0) @@ -680,7 +785,10 @@ aci_list_has_attr_right (struct berval *list, char *attr, int access, int action } static int -aci_list_has_permission (struct berval *list, char *attr, int access) +aci_list_has_permission( + struct berval *list, + char *attr, + slap_access_t access) { struct berval perm, actn; int i, action, specific, general; @@ -720,7 +828,6 @@ aci_group_member ( Backend *be, Entry *e, Operation *op, - char *edn, regmatch_t *matches ) { @@ -747,7 +854,7 @@ aci_group_member ( grpdn = (char *)ch_malloc(1024); if (grpoc != NULL && grpat != NULL && grpdn != NULL) { - string_expand(grpdn, 1024, subjdn, edn, matches); + string_expand(grpdn, 1024, subjdn, e->e_ndn, matches); if ( dn_normalize(grpdn) != NULL ) { rc = (backend_group(be, e, grpdn, op->o_ndn, grpoc, grpat) == 0); } @@ -761,15 +868,14 @@ aci_group_member ( return(rc); } -int +static int aci_access_allowed ( struct berval *aci, char *attr, Backend *be, Entry *e, Operation *op, - int access, - char *edn, + slap_access_t access, regmatch_t *matches ) { @@ -778,11 +884,11 @@ aci_access_allowed ( int rc; Debug( LDAP_DEBUG_ACL, - "\n=> aci_access_allowed: %s access to entry \"%s\"\n", + "=> aci_access_allowed: %s access to entry \"%s\"\n", access2str( access ), e->e_dn, 0 ); Debug( LDAP_DEBUG_ACL, - "\n=> aci_access_allowed: %s access to attribute \"%s\" by \"%s\"\n", + "=> aci_access_allowed: %s access to attribute \"%s\" by \"%s\"\n", access2str( access ), attr, op->o_ndn ? op->o_ndn : "" ); @@ -833,15 +939,15 @@ aci_access_allowed ( } if (aci_strbvcmp( "self", &bv ) == 0) { - return(strcasecmp(op->o_ndn, edn) == 0); + return(strcasecmp(op->o_ndn, e->e_ndn) == 0); } if (aci_strbvcmp( "group", &bv ) == 0) { - return(aci_group_member(&sdn, "groupOfNames", "member", be, e, op, edn, matches)); + return(aci_group_member(&sdn, "groupOfNames", "member", be, e, op, matches)); } if (aci_strbvcmp( "role", &bv ) == 0) { - return(aci_group_member(&sdn, "organizationalRole", "roleOccupant", be, e, op, edn, matches)); + return(aci_group_member(&sdn, "organizationalRole", "roleOccupant", be, e, op, matches)); } return(0); diff --git a/servers/slapd/aclparse.c b/servers/slapd/aclparse.c index a134506908..5b9363714c 100644 --- a/servers/slapd/aclparse.c +++ b/servers/slapd/aclparse.c @@ -18,11 +18,11 @@ #include "slap.h" static void split(char *line, int splitchar, char **left, char **right); -static void acl_append(AccessControl **l, AccessControl *a); static void access_append(Access **l, Access *a); static void acl_usage(void) LDAP_GCCATTR((noreturn)); + #ifdef LDAP_DEBUG -static void print_acl(AccessControl *a); +static void print_acl(Backend *be, AccessControl *a); static void print_access(Access *b); #endif @@ -179,12 +179,15 @@ parse_acl( fname, lineno ); acl_usage(); } + /* * by clause consists of and */ b = (Access *) ch_calloc( 1, sizeof(Access) ); + ACL_INVALIDATE( b->a_mask ); + if ( ++i == argc ) { fprintf( stderr, "%s: line %d: premature eol: expecting \n", @@ -344,16 +347,92 @@ parse_acl( } #endif - /* get */ - if ( ACL_IS_INVALID(ACL_SET(b->a_access, str2access( left ))) ) { - fprintf( stderr, - "%s: line %d: expecting got \"%s\"\n", - fname, lineno, left ); - acl_usage(); + if( right != NULL ) { + /* unsplit */ + right[-1] = '='; } - access_append( &a->acl_access, b ); break; } + + if( i == argc || ( strcasecmp( left, "stop" ) == 0 )) { + /* out of arguments or plain stop */ + + ACL_PRIV_ASSIGN(b->a_mask, ACL_NONE); + b->a_type = ACL_STOP; + + access_append( &a->acl_access, b ); + continue; + } + + if( strcasecmp( left, "continue" ) == 0 ) { + /* plain continue */ + + ACL_PRIV_ASSIGN(b->a_mask, ACL_NONE); + b->a_type = ACL_CONTINUE; + + access_append( &a->acl_access, b ); + continue; + } + + if( strcasecmp( left, "break" ) == 0 ) { + /* plain continue */ + + ACL_PRIV_ASSIGN(b->a_mask, ACL_NONE); + b->a_type = ACL_BREAK; + + access_append( &a->acl_access, b ); + continue; + } + + if ( strcasecmp( left, "by" ) == 0 ) { + /* we've gone too far */ + --i; + ACL_PRIV_ASSIGN(b->a_mask, ACL_NONE); + b->a_type = ACL_STOP; + + access_append( &a->acl_access, b ); + continue; + } + + /* get */ + if( strncasecmp( left, "self", 4 ) == 0 ) { + b->a_dn_self = 1; + ACL_PRIV_ASSIGN( b->a_mask, str2accessmask( &left[4] ) ); + + } else { + ACL_PRIV_ASSIGN( b->a_mask, str2accessmask( left ) ); + } + + if( ACL_IS_INVALID( b->a_mask ) ) { + fprintf( stderr, + "%s: line %d: expecting got \"%s\"\n", + fname, lineno, left ); + acl_usage(); + } + + b->a_type = ACL_STOP; + + if( ++i == argc ) { + /* out of arguments or plain stop */ + access_append( &a->acl_access, b ); + continue; + } + + if( strcasecmp( argv[i], "continue" ) == 0 ) { + /* plain continue */ + b->a_type = ACL_CONTINUE; + + } else if( strcasecmp( argv[i], "break" ) == 0 ) { + /* plain continue */ + b->a_type = ACL_BREAK; + + } else if ( strcasecmp( argv[i], "stop" ) != 0 ) { + /* gone to far */ + i--; + } + + access_append( &a->acl_access, b ); + } else { fprintf( stderr, "%s: line %d: expecting \"to\" or \"by\" got \"%s\"\n", @@ -372,7 +451,7 @@ parse_acl( #ifdef LDAP_DEBUG if (ldap_debug & LDAP_DEBUG_ACL) - print_acl(a); + print_acl(be, a); #endif if ( a->acl_access == NULL ) { @@ -390,72 +469,165 @@ parse_acl( } char * -access2str( int access ) +accessmask2str( slap_access_mask_t mask ) { - static char buf[12]; + static char buf[sizeof("unknown (+wrsca0)")]; + int none=1; - if ( ACL_IS_SELF( access ) ) { - strcpy( buf, "self" ); - } else { - buf[0] = '\0'; + if ( ACL_IS_INVALID( mask ) ) { + return "invalid"; } - if ( ACL_IS_NONE(access) ) { - strcat( buf, "none" ); - } else if ( ACL_IS_AUTH(access) ) { - strcat( buf, "auth" ); - } else if ( ACL_IS_COMPARE(access) ) { - strcat( buf, "compare" ); - } else if ( ACL_IS_SEARCH(access) ) { - strcat( buf, "search" ); - } else if ( ACL_IS_READ(access) ) { - strcat( buf, "read" ); - } else if ( ACL_IS_WRITE(access) ) { - strcat( buf, "write" ); + buf[0] = '\0'; + + if ( ACL_IS_LEVEL( mask ) ) { + if ( ACL_LVL_IS_NONE(mask) ) { + strcat( buf, "none" ); + + } else if ( ACL_LVL_IS_AUTH(mask) ) { + strcat( buf, "auth" ); + + } else if ( ACL_LVL_IS_COMPARE(mask) ) { + strcat( buf, "compare" ); + + } else if ( ACL_LVL_IS_SEARCH(mask) ) { + strcat( buf, "search" ); + + } else if ( ACL_LVL_IS_READ(mask) ) { + strcat( buf, "read" ); + + } else if ( ACL_LVL_IS_WRITE(mask) ) { + strcat( buf, "write" ); + } else { + strcat( buf, "unknown" ); + } + + strcat(buf, " ("); + } + + if( ACL_IS_ADDITIVE( mask ) ) { + strcat( buf, "+" ); + + } else if( ACL_IS_SUBTRACTIVE( mask ) ) { + strcat( buf, "-" ); } else { - strcat( buf, "unknown" ); + strcat( buf, "=" ); } - return( buf ); + if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) { + none = 0; + strcat( buf, "w" ); + } + + if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) { + none = 0; + strcat( buf, "r" ); + } + + if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) { + none = 0; + strcat( buf, "s" ); + } + + if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) { + none = 0; + strcat( buf, "c" ); + } + + if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) { + none = 0; + strcat( buf, "x" ); + } + + if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) { + strcat( buf, "0" ); + } + + if ( ACL_IS_LEVEL( mask ) ) { + strcat(buf, ")"); + } + return buf; } -int -str2access( char *str ) +slap_access_mask_t +str2accessmask( const char *str ) { - int access; + slap_access_mask_t mask; + + if( !isalpha(str[0]) ) { + int i; + + if ( str[0] == '=' ) { + ACL_INIT(mask); + + } else if( str[0] == '+' ) { + ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE); + + } else if( str[0] == '-' ) { + ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE); - ACL_CLR(access); + } else { + ACL_INVALIDATE(mask); + return mask; + } + + for( i=1; str[i] != '\0'; i++ ) { + if( TOLOWER(str[i]) == 'w' ) { + ACL_PRIV_SET(mask, ACL_PRIV_WRITE); + + } else if( TOLOWER(str[0]) == 'r' ) { + ACL_PRIV_SET(mask, ACL_PRIV_READ); + + } else if( TOLOWER(str[0]) == 's' ) { + ACL_PRIV_SET(mask, ACL_PRIV_SEARCH); + + } else if( TOLOWER(str[0]) == 'c' ) { + ACL_PRIV_SET(mask, ACL_PRIV_COMPARE); + + } else if( TOLOWER(str[0]) == 'x' ) { + ACL_PRIV_SET(mask, ACL_PRIV_AUTH); + + } else { + ACL_INVALIDATE(mask); + return mask; + } + } - if ( strncasecmp( str, "self", 4 ) == 0 ) { - ACL_SET_SELF(access); - str += 4; + return mask; } if ( strcasecmp( str, "none" ) == 0 ) { - ACL_SET_NONE(access); + ACL_LVL_ASSIGN_NONE(mask); + } else if ( strcasecmp( str, "auth" ) == 0 ) { - ACL_SET_AUTH(access); + ACL_LVL_ASSIGN_AUTH(mask); + } else if ( strcasecmp( str, "compare" ) == 0 ) { - ACL_SET_COMPARE(access); + ACL_LVL_ASSIGN_COMPARE(mask); + } else if ( strcasecmp( str, "search" ) == 0 ) { - ACL_SET_SEARCH(access); + ACL_LVL_ASSIGN_SEARCH(mask); + } else if ( strcasecmp( str, "read" ) == 0 ) { - ACL_SET_READ(access); + ACL_LVL_ASSIGN_READ(mask); + } else if ( strcasecmp( str, "write" ) == 0 ) { - ACL_SET_WRITE(access); + ACL_LVL_ASSIGN_WRITE(mask); + } else { - ACL_SET_INVALID(access); + ACL_INVALIDATE( mask ); } - return( access ); + return mask; } static void acl_usage( void ) { fprintf( stderr, "\n" - " ::= access to [ by ]+ \n" + " ::= access to " + "[ by ]+ \n" " ::= * | [dn=] [filter=] [attrs=]\n" " ::= | , \n" " ::= | entry | children\n" @@ -467,7 +639,10 @@ acl_usage( void ) #ifdef SLAPD_ACI_ENABLED "\t[aci=]\n" #endif - " ::= [self]{none|auth|compare|search|read|write}\n" + " ::= [self]{|}\n" + " ::= none | auth | compare | search | read | write\n" + " ::= {=|+|-}{w|r|s|c|x}+\n" + " ::= [ stop | continue | break ]\n" ); exit( EXIT_FAILURE ); } @@ -495,7 +670,7 @@ access_append( Access **l, Access *a ) *l = a; } -static void +void acl_append( AccessControl **l, AccessControl *a ) { for ( ; *l != NULL; l = &(*l)->acl_next ) @@ -542,6 +717,7 @@ print_access( Access *b ) if ( b->a_peername_pat != NULL ) { fprintf( stderr, " peername=%s", b->a_peername_pat ); } + if ( b->a_sockname_pat != NULL ) { fprintf( stderr, " sockname=%s", b->a_sockname_pat ); } @@ -560,31 +736,91 @@ print_access( Access *b ) } #endif + fprintf( stderr, " %s%s", + b->a_dn_self ? "self" : "", + accessmask2str( b->a_mask ) ); + fprintf( stderr, "\n" ); } +char * +access2str( slap_access_t access ) +{ + if ( access == ACL_NONE ) { + return "none"; + + } else if ( access == ACL_AUTH ) { + return "auth"; + + } else if ( access == ACL_COMPARE ) { + return "compare"; + + } else if ( access == ACL_SEARCH ) { + return "search"; + + } else if ( access == ACL_READ ) { + return "read"; + + } else if ( access == ACL_WRITE ) { + return "write"; + } + + return "unknown"; +} + +slap_access_t +str2access( const char *str ) +{ + if ( strcasecmp( str, "none" ) == 0 ) { + return ACL_NONE; + + } else if ( strcasecmp( str, "auth" ) == 0 ) { + return ACL_AUTH; + + } else if ( strcasecmp( str, "compare" ) == 0 ) { + return ACL_COMPARE; + + } else if ( strcasecmp( str, "search" ) == 0 ) { + return ACL_SEARCH; + + } else if ( strcasecmp( str, "read" ) == 0 ) { + return ACL_READ; + + } else if ( strcasecmp( str, "write" ) == 0 ) { + return ACL_WRITE; + } + + return( ACL_INVALID_ACCESS ); +} + + static void -print_acl( AccessControl *a ) +print_acl( Backend *be, AccessControl *a ) { - int i; + int to = 0; Access *b; - if ( a == NULL ) { - fprintf( stderr, "NULL\n" ); + fprintf( stderr, "%s ACL: access to", + be == NULL ? "Global" : "Backend" ); + + if ( a->acl_dn_pat != NULL ) { + to++; + fprintf( stderr, " dn=%s\n", + a->acl_dn_pat ); } - fprintf( stderr, "ACL: access to" ); + if ( a->acl_filter != NULL ) { - fprintf( stderr," filter=" ); + to++; + fprintf( stderr, " filter=" ); filter_print( a->acl_filter ); + fprintf( stderr, "\n" ); } - if ( a->acl_dn_pat != NULL ) { - fprintf( stderr, " dn=" ); - fprintf( stderr, a->acl_dn_pat ); - } + if ( a->acl_attrs != NULL ) { - int first = 1; + int i, first = 1; + to++; - fprintf( stderr, "\n attrs=" ); + fprintf( stderr, " attrs=" ); for ( i = 0; a->acl_attrs[i] != NULL; i++ ) { if ( ! first ) { fprintf( stderr, "," ); @@ -592,11 +828,17 @@ print_acl( AccessControl *a ) fprintf( stderr, a->acl_attrs[i] ); first = 0; } + fprintf( stderr, "\n" ); } - fprintf( stderr, "\n" ); + + if( !to ) { + fprintf( stderr, " *\n" ); + } + for ( b = a->acl_access; b != NULL; b = b->a_next ) { print_access( b ); } + fprintf( stderr, "\n" ); } diff --git a/servers/slapd/back-bdb2/modify.c b/servers/slapd/back-bdb2/modify.c index de632c8df1..badab589eb 100644 --- a/servers/slapd/back-bdb2/modify.c +++ b/servers/slapd/back-bdb2/modify.c @@ -28,8 +28,8 @@ bdb2i_back_modify_internal( Debug(LDAP_DEBUG_ARGS, "bdb2i_back_modify:\n", 0, 0, 0); - if ( (err = acl_check_modlist( be, conn, op, e, modlist )) != LDAP_SUCCESS ) { - send_ldap_result( conn, op, err, + if ( !acl_check_modlist( be, conn, op, e, modlist )) { + send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS, NULL, NULL, NULL, NULL ); goto error_return; } diff --git a/servers/slapd/back-ldbm/modify.c b/servers/slapd/back-ldbm/modify.c index af69d50bc6..fa55df90be 100644 --- a/servers/slapd/back-ldbm/modify.c +++ b/servers/slapd/back-ldbm/modify.c @@ -38,10 +38,8 @@ int ldbm_modify_internal( Attribute *a; Attribute *save_attrs; - if ( (err = acl_check_modlist( be, conn, op, e, modlist )) - != LDAP_SUCCESS ) - { - send_ldap_result( conn, op, err, + if ( !acl_check_modlist( be, conn, op, e, modlist )) { + send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS, NULL, NULL, NULL, NULL ); return -1; } diff --git a/servers/slapd/backend.c b/servers/slapd/backend.c index ad8b827360..7e83933b21 100644 --- a/servers/slapd/backend.c +++ b/servers/slapd/backend.c @@ -222,6 +222,9 @@ int backend_startup(Backend *be) /* open each backend database */ for( i = 0; i < nBackendDB; i++ ) { + /* append global access controls */ + acl_append( &backendDB[i].be_acl, global_acl ); + if ( backendDB[i].bd_info->bi_db_open ) { rc = backendDB[i].bd_info->bi_db_open( &backendDB[i] ); @@ -376,6 +379,7 @@ backend_db_init( be->bd_info = bi; be->be_sizelimit = defsize; be->be_timelimit = deftime; + be->be_dfltaccess = global_default_access; /* assign a default depth limit for alias deref */ be->be_max_deref_depth = SLAPD_DEFAULT_MAXDEREFDEPTH; diff --git a/servers/slapd/config.c b/servers/slapd/config.c index abb5dab945..cee2fe9e3d 100644 --- a/servers/slapd/config.c +++ b/servers/slapd/config.c @@ -27,7 +27,7 @@ int defsize = SLAPD_DEFAULT_SIZELIMIT; int deftime = SLAPD_DEFAULT_TIMELIMIT; AccessControl *global_acl = NULL; -int global_default_access = ACL_AUTH; +slap_access_t global_default_access = ACL_READ; int global_readonly = 0; char *replogfile; int global_lastmod = ON; @@ -478,31 +478,29 @@ read_config( const char *fname ) /* specify default access control info */ } else if ( strcasecmp( cargv[0], "defaultaccess" ) == 0 ) { + slap_access_t access; + if ( cargc < 2 ) { Debug( LDAP_DEBUG_ANY, "%s: line %d: missing limit in \"defaultaccess \" line\n", fname, lineno, 0 ); return( 1 ); } + + access = str2access( cargv[1] ); + + if ( access == ACL_INVALID_ACCESS ) { + Debug( LDAP_DEBUG_ANY, + "%s: line %d: bad access level \"%s\", " + "expecting none|auth|compare|search|read|write\n", + fname, lineno, cargv[1] ); + return( 1 ); + } + if ( be == NULL ) { - if ( ACL_IS_INVALID(ACL_SET(global_default_access, - str2access(cargv[1]))) ) - { - Debug( LDAP_DEBUG_ANY, -"%s: line %d: bad access \"%s\" expecting [self]{none|auth|compare|search|read|write}\n", - fname, lineno, cargv[1] ); - return( 1 ); - } + global_default_access = access; } else { - if ( ACL_IS_INVALID(ACL_SET(be->be_dfltaccess, - str2access(cargv[1]))) ) - { - Debug( LDAP_DEBUG_ANY, - "%s: line %d: bad access \"%s\", " - "expecting [self]{none|auth|compare|search|read|write}\n", - fname, lineno, cargv[1] ); - return( 1 ); - } + be->be_dfltaccess = access; } /* debug level to log things to syslog */ diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index 139a37eb40..f676bc0e16 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -16,16 +16,7 @@ LDAP_BEGIN_DECL int access_allowed LDAP_P(( Backend *be, Connection *conn, Operation *op, Entry *e, - char *attr, struct berval *val, int access )); - -AccessControl * acl_get_applicable LDAP_P(( Backend *be, - Operation *op, Entry *e, - char *attr, int nmatches, regmatch_t *matches )); - -int acl_access_allowed LDAP_P(( - AccessControl *a, char *attr, Backend *be, Connection *conn, Entry *e, - struct berval *val, Operation *op, int access, char *edn, - regmatch_t *matches )); + char *attr, struct berval *val, slap_access_t access )); int acl_check_modlist LDAP_P(( Backend *be, Connection *conn, @@ -33,6 +24,8 @@ int acl_check_modlist LDAP_P(( Backend *be, Entry *e, LDAPModList *ml )); +void acl_append( AccessControl **l, AccessControl *a ); + /* * aclparse.c */ @@ -41,8 +34,12 @@ void parse_acl LDAP_P(( Backend *be, const char *fname, int lineno, int argc, char **argv )); -char * access2str LDAP_P(( int access )); -int str2access LDAP_P(( char *str )); + +char * access2str LDAP_P(( slap_access_t access )); +slap_access_t str2access LDAP_P(( const char *str )); + +char * accessmask2str LDAP_P(( slap_access_mask_t mask )); +slap_access_mask_t str2accessmask LDAP_P(( const char *str )); /* * attr.c @@ -432,7 +429,7 @@ extern int active_threads; extern int defsize; extern int deftime; extern int g_argc; -extern int global_default_access; +extern slap_access_t global_default_access; extern int global_readonly; extern int global_lastmod; extern int global_idletimeout; diff --git a/servers/slapd/result.c b/servers/slapd/result.c index 9e316669bc..3f28d4b654 100644 --- a/servers/slapd/result.c +++ b/servers/slapd/result.c @@ -572,12 +572,11 @@ send_search_entry( } } - acl = acl_get_applicable( be, op, e, a->a_type, - MAXREMATCHES, matches ); - - if ( ! acl_access_allowed( acl, a->a_type, be, conn, e, - NULL, op, ACL_READ, edn, matches ) ) + if ( ! access_allowed( be, conn, op, e, + a->a_type, NULL, ACL_READ ) ) { + Debug( LDAP_DEBUG_ACL, "acl: access to attribute %s not allowed\n", + a->a_type, 0, 0 ); continue; } @@ -591,10 +590,12 @@ send_search_entry( if ( ! attrsonly ) { for ( i = 0; a->a_vals[i] != NULL; i++ ) { - if ( a->a_syntax & SYNTAX_DN && - ! acl_access_allowed( acl, a->a_type, be, conn, e, a->a_vals[i], op, - ACL_READ, edn, matches) ) + if ( ! access_allowed( be, conn, op, e, + a->a_type, a->a_vals[i], ACL_READ ) ) { + Debug( LDAP_DEBUG_ACL, + "acl: access to attribute %s, value %d not allowed\n", + a->a_type, i, 0 ); continue; } @@ -647,12 +648,11 @@ send_search_entry( } } - acl = acl_get_applicable( be, op, e, a->a_type, - MAXREMATCHES, matches ); - - if ( ! acl_access_allowed( acl, a->a_type, be, conn, e, - NULL, op, ACL_READ, edn, matches ) ) + if ( ! access_allowed( be, conn, op, e, + a->a_type, NULL, ACL_READ ) ) { + Debug( LDAP_DEBUG_ACL, "acl: access to attribute %s not allowed\n", + a->a_type, 0, 0 ); continue; } @@ -666,13 +666,16 @@ send_search_entry( if ( ! attrsonly ) { for ( i = 0; a->a_vals[i] != NULL; i++ ) { - if ( a->a_syntax & SYNTAX_DN && - ! acl_access_allowed( acl, a->a_type, be, conn, e, a->a_vals[i], op, - ACL_READ, edn, matches) ) + if ( ! access_allowed( be, conn, op, e, + a->a_type, a->a_vals[i], ACL_READ ) ) { + Debug( LDAP_DEBUG_ACL, + "acl: access to attribute %s, value %d not allowed\n", + a->a_type, i, 0 ); continue; } + if (( rc = ber_printf( ber, "O", a->a_vals[i] )) == -1 ) { Debug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 ); diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index 304a5090d7..1d0339a214 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -226,49 +226,91 @@ typedef struct slap_entry { * represents an access control list */ +typedef enum slap_access_e { + ACL_INVALID_ACCESS = -1, + ACL_NONE = 0, + ACL_AUTH, + ACL_COMPARE, + ACL_SEARCH, + ACL_READ, + ACL_WRITE +} slap_access_t; + +typedef enum slap_control_e { + ACL_INVALID_CONTROL = 0, + ACL_STOP, + ACL_CONTINUE, + ACL_BREAK +} slap_control_t; + +typedef unsigned long slap_access_mask_t; + /* the "by" part */ typedef struct slap_access { + slap_control_t a_type; + +#define ACL_ACCESS2PRIV(access) (0x01U << (access)) + +#define ACL_PRIV_NONE ACL_ACCESS2PRIV( ACL_NONE ) +#define ACL_PRIV_AUTH ACL_ACCESS2PRIV( ACL_AUTH ) +#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_MASK 0x00ffUL + +/* priv flags */ +#define ACL_PRIV_LEVEL 0x1000UL +#define ACL_PRIV_ADDITIVE 0x2000UL +#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_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_INVALIDATE(m) ACL_PRIV_ASSIGN(m, ACL_PRIV_INVALID) + +#define ACL_GRANT(m,a) ACL_PRIV_ISSET((m),ACL_ACCESS2PRIV(a)) + +#define ACL_IS_INVALID(m) ((m) == ACL_PRIV_INVALID) + +#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_LVL_NONE (ACL_PRIV_NONE|ACL_PRIV_LEVEL) +#define ACL_LVL_AUTH (ACL_PRIV_AUTH|ACL_LVL_NONE) +#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_WRITE (ACL_PRIV_WRITE|ACL_LVL_READ) + +#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_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_READ(m) ACL_LVL((m),ACL_LVL_READ) +#define ACL_LVL_IS_WRITE(m) ACL_LVL((m),ACL_LVL_WRITE) + +#define ACL_LVL_ASSIGN_NONE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_NONE) +#define ACL_LVL_ASSIGN_AUTH(m) ACL_PRIV_ASSIGN((m),ACL_LVL_AUTH) +#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_WRITE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_WRITE) -#define ACL_NONE 0x0001 -#define ACL_AUTH 0x0004 -#define ACL_COMPARE 0x0008 -#define ACL_SEARCH 0x0010 -#define ACL_READ 0x0020 -#define ACL_WRITE 0x0040 -#define ACL_PRIV_MASK 0x00ff - -#define ACL_SELF 0x4000 -#define ACL_INVALID (-1) - -#define ACL_IS(a,lvl) (((a) & (lvl)) == (lvl)) - -#define ACL_IS_NONE(a) ACL_IS((a),ACL_SELF) -#define ACL_IS_AUTH(a) ACL_IS((a),ACL_AUTH) -#define ACL_IS_COMPARE(a) ACL_IS((a),ACL_COMPARE) -#define ACL_IS_SEARCH(a) ACL_IS((a),ACL_SEARCH) -#define ACL_IS_READ(a) ACL_IS((a),ACL_READ) -#define ACL_IS_WRITE(a) ACL_IS((a),ACL_WRITE) -#define ACL_IS_SELF(a) ACL_IS((a),ACL_SELF) -#define ACL_IS_INVALID(a) ((a) == ACL_INVALID) - -#define ACL_CLR(a) ((a) = 0) -#define ACL_SET(a,lvl) ((a) |= (lvl)) -#define ACL_SET_NONE(a) ACL_SET((a),ACL_SELF) -#define ACL_SET_AUTH(a) ACL_SET((a),ACL_AUTH) -#define ACL_SET_COMPARE(a) ACL_SET((a),ACL_COMPARE) -#define ACL_SET_SEARCH(a) ACL_SET((a),ACL_SEARCH) -#define ACL_SET_READ(a) ACL_SET((a),ACL_READ) -#define ACL_SET_WRITE(a) ACL_SET((a),ACL_WRITE) -#define ACL_SET_SELF(a) ACL_SET((a),ACL_SELF) -#define ACL_SET_INVALID(a) ((a) = ACL_INVALID) - -#define ACL_PRIV(a) ((a) & ACL_PRIV_MASK) -#define ACL_GRANT(a,lvl) (ACL_PRIV(a) >= (lvl)) - - int a_access; + slap_access_mask_t a_mask; char *a_dn_pat; char *a_dn_at; + int a_dn_self; char *a_peername_pat; char *a_sockname_pat; @@ -469,7 +511,7 @@ struct slap_backend_db { int be_sizelimit; /* size limit for this backend */ int be_timelimit; /* time limit for this backend */ AccessControl *be_acl; /* access control list for this backend */ - int be_dfltaccess; /* access given if no acl matches */ + slap_access_t be_dfltaccess; /* access given if no acl matches */ char **be_replica; /* replicas of this backend (in master) */ char *be_replogfile; /* replication log file (in master) */ char *be_update_ndn; /* allowed to make changes (in replicas) */ diff --git a/servers/slapd/slapd.conf b/servers/slapd/slapd.conf index e20c277be2..7f13a04257 100644 --- a/servers/slapd/slapd.conf +++ b/servers/slapd/slapd.conf @@ -6,9 +6,7 @@ include %SYSCONFDIR%/slapd.at.conf include %SYSCONFDIR%/slapd.oc.conf -# Using ACLs to control access is wise. When ACLs are used, -# "defaultaccess none" is recommended (default is 'auth'). -defaultaccess read +# Define global ACLs to disable default read access. # Do not enable referrals until AFTER you have a working directory # service AND an understanding of referrals. -- 2.39.5