X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Facl.c;h=d6e92eb53b42d643afb4004199f5cf986536ae0c;hb=3a9f96ec0d00344c1fb009ca49090a257d8d42a5;hp=6c3b22ee857b04b4f34b74b385891a2a84a66fc9;hpb=42e0d83cb3a1a1c5b25183f1ab74ce7edbe25de7;p=openldap diff --git a/servers/slapd/acl.c b/servers/slapd/acl.c index 6c3b22ee85..d6e92eb53b 100644 --- a/servers/slapd/acl.c +++ b/servers/slapd/acl.c @@ -1,35 +1,22 @@ /* acl.c - routines to parse and check acl's */ +#include "portable.h" + #include -#include -#include -#include -#include -#include -#ifdef sunos5 -#include "regexpr.h" -#else -#include "regex.h" -#endif -#include "slap.h" -extern Attribute *attr_find(); -extern char *re_comp(); -extern struct acl *global_acl; -extern int global_default_access; -extern char *access2str(); -extern char *dn_normalize_case(); +#include +#include +#include -int acl_access_allowed(); -int access_allowed(); -struct acl *acl_get_applicable(); +#include "slap.h" -static int regex_matches(); +static int regex_matches(char *pat, char *str, char *buf, regmatch_t *matches); +static void string_expand(char *newbuf, int bufsiz, char *pattern, + char *match, regmatch_t *matches); -extern pthread_mutex_t regex_mutex; /* - * access_allowed - check whether dn is allowed the requested access + * 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 @@ -47,19 +34,59 @@ access_allowed( Entry *e, char *attr, struct berval *val, - char *dn, int access ) { - int rc; - struct acl *a; + int rc; + struct acl *a; + char *edn; + + regmatch_t matches[MAXREMATCHES]; + int i; + int n; if ( be == NULL ) { return( 0 ); } - a = acl_get_applicable( be, op, e, attr ); - rc = acl_access_allowed( a, be, conn, e, val, op, access ); + edn = e->e_ndn; + + Debug( LDAP_DEBUG_ACL, "\n=> access_allowed: entry (%s) attr (%s)\n", + e->e_dn, attr, 0 ); + + /* the lastmod attributes are ignored by ACL checking */ + if ( strcasecmp( attr, "modifiersname" ) == 0 || + strcasecmp( attr, "modifytimestamp" ) == 0 || + strcasecmp( attr, "creatorsname" ) == 0 || + strcasecmp( attr, "createtimestamp" ) == 0 ) + { + Debug( LDAP_DEBUG_ACL, "LASTMOD attribute: %s access allowed\n", + attr, 0, 0 ); + return(1); + } + + memset(matches, 0, sizeof(matches)); + + a = acl_get_applicable( be, op, e, attr, MAXREMATCHES, matches ); + + if (a) { + for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) { + Debug( LDAP_DEBUG_ARGS, "=> match[%d]: %d %d ", i, + (int)matches[i].rm_so, (int)matches[i].rm_eo ); + + if( matches[i].rm_so <= matches[0].rm_eo ) { + for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) { + Debug( LDAP_DEBUG_ARGS, "%c", edn[n], 0, 0 ); + } + } + Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 ); + } + } + + rc = acl_access_allowed( a, be, conn, e, val, op, access, edn, matches ); + + Debug( LDAP_DEBUG_ACL, "\n=> access_allowed: exit (%s) attr (%s)\n", + e->e_dn, attr, 0); return( rc ); } @@ -75,72 +102,94 @@ acl_get_applicable( Backend *be, Operation *op, Entry *e, - char *attr + char *attr, + int nmatch, + regmatch_t *matches ) { - int i; + int i, j; struct acl *a; - char *edn; + char *edn; - Debug( LDAP_DEBUG_ACL, "=> acl_get: entry (%s) attr (%s)\n", e->e_dn, - attr, 0 ); + Debug( LDAP_DEBUG_ACL, "\n=> acl_get: entry (%s) attr (%s)\n", + e->e_dn, attr, 0 ); - if ( be_isroot( be, op->o_dn ) ) { + 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 ); } + 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_dnpat != NULL ) { - edn = dn_normalize_case( strdup( e->e_dn ) ); - if ( ! regex_matches( a->acl_dnpat, edn ) ) { - free( edn ); + if (a->acl_dnpat != NULL) { + Debug( LDAP_DEBUG_TRACE, "=> dnpat: [%d] %s nsub: %d\n", + i, a->acl_dnpat, (int) a->acl_dnre.re_nsub); + + if (regexec(&a->acl_dnre, edn, nmatch, matches, 0)) continue; - } - free( edn ); + else + Debug( LDAP_DEBUG_TRACE, "=> acl_get:[%d] backend ACL match\n", + i, 0, 0); } + if ( a->acl_filter != NULL ) { - if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) - != 0 ) { + if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) { continue; } } + + Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr %s\n", i, attr, 0); + if ( attr == NULL || a->acl_attrs == NULL || - charray_inlist( a->acl_attrs, attr ) ) { - Debug( LDAP_DEBUG_ACL, "<= acl_get: backend acl #%d\n", - i, e->e_dn, attr ); + 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; } /* check for a global acl that matches the entry */ for ( i = 1, a = global_acl; a != NULL; a = a->acl_next, i++ ) { - if ( a->acl_dnpat != NULL ) { - edn = dn_normalize_case( strdup( e->e_dn ) ); - if ( ! regex_matches( a->acl_dnpat, edn ) ) { - free( edn ); + if (a->acl_dnpat != NULL) { + Debug( LDAP_DEBUG_TRACE, "=> dnpat: [%d] %s nsub: %d\n", + i, a->acl_dnpat, (int) a->acl_dnre.re_nsub); + + if (regexec(&a->acl_dnre, edn, nmatch, matches, 0)) { continue; + } else { + Debug( LDAP_DEBUG_TRACE, "=> acl_get: [%d] global ACL match\n", + i, 0, 0); } - free( edn ); } + if ( a->acl_filter != NULL ) { - if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) - != 0 ) { + if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) { continue; } } - if ( attr == NULL || a->acl_attrs == NULL || charray_inlist( - a->acl_attrs, attr ) ) { - Debug( LDAP_DEBUG_ACL, "<= acl_get: global acl #%d\n", - i, e->e_dn, attr ); + + Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr\n", i, 0, 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 ); } + + 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: no match\n", 0, 0, 0 ); return( NULL ); } @@ -161,81 +210,86 @@ acl_access_allowed( Entry *e, struct berval *val, Operation *op, - int access + int access, + char *edn, + regmatch_t *matches ) { int i; - char *edn, *odn; + char *odn; struct access *b; Attribute *at; struct berval bv; int default_access; - Debug( LDAP_DEBUG_ACL, "=> acl: %s access to value \"%s\" by \"%s\"\n", - access2str( access ), val ? val->bv_val : "any", op->o_dn ? - op->o_dn : "" ); + Debug( LDAP_DEBUG_ACL, + "\n=> acl_access_allowed: %s access to entry \"%s\"\n", + access2str( access ), e->e_dn, 0 ); + + Debug( LDAP_DEBUG_ACL, + "\n=> acl_access_allowed: %s access to value \"%s\" by \"%s\"\n", + access2str( access ), + val ? val->bv_val : "any", + op->o_ndn ? op->o_ndn : "" ); - if ( be_isroot( be, op->o_dn ) ) { - Debug( LDAP_DEBUG_ACL, "<= acl: granted to database root\n", + if ( be_isroot( be, op->o_ndn ) ) { + Debug( LDAP_DEBUG_ACL, + "<= acl_access_allowed: granted to database root\n", 0, 0, 0 ); return( 1 ); } - default_access = be->be_dfltaccess ? be->be_dfltaccess : - global_default_access; + default_access = be->be_dfltaccess ? be->be_dfltaccess : global_default_access; + if ( a == NULL ) { Debug( LDAP_DEBUG_ACL, - "<= acl: %s by default (no matching to)\n", + "<= acl_access_allowed: %s by default (no matching to)\n", default_access >= access ? "granted" : "denied", 0, 0 ); return( default_access >= access ); } - odn = NULL; - if ( op->o_dn != NULL ) { - odn = dn_normalize_case( strdup( op->o_dn ) ); + odn = op->o_ndn; + + if ( odn != NULL ) { bv.bv_val = odn; bv.bv_len = strlen( odn ); } + for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) { if ( b->a_dnpat != NULL ) { + Debug( LDAP_DEBUG_TRACE, "<= check a_dnpat: %s\n", + b->a_dnpat, 0, 0); /* * if access applies to the entry itself, and the * user is bound as somebody in the same namespace as * the entry, OR the given dn matches the dn pattern */ - if ( strcasecmp( b->a_dnpat, "self" ) == 0 && op->o_dn - != NULL && *(op->o_dn) && e->e_dn != NULL ) { - edn = dn_normalize_case( strdup( e->e_dn ) ); - if ( strcasecmp( edn, op->o_dn ) == 0 ) { - free( edn ); - if ( odn ) free( odn ); + if ( strcasecmp( b->a_dnpat, "self" ) == 0 && + op->o_ndn != NULL && *(op->o_ndn) && e->e_dn != NULL ) + { + if ( strcmp( edn, op->o_ndn ) == 0 ) { Debug( LDAP_DEBUG_ACL, - "<= acl: matched by clause #%d access %s\n", + "<= acl_access_allowed: matched by clause #%d access %s\n", i, (b->a_access & ~ACL_SELF) >= access ? "granted" : "denied", 0 ); - return( (b->a_access & ~ACL_SELF) - >= access ); + return( (b->a_access & ~ACL_SELF) >= access ); } - free( edn ); } else { - if ( regex_matches( b->a_dnpat, odn ) ) { - if ( odn ) free( odn ); + if ( regex_matches( b->a_dnpat, odn, edn, matches ) ) { Debug( LDAP_DEBUG_ACL, - "<= acl: matched by clause #%d access %s\n", + "<= acl_access_allowed: matched by clause #%d access %s\n", i, (b->a_access & ~ACL_SELF) >= access ? "granted" : "denied", 0 ); - return( (b->a_access & ~ACL_SELF) - >= access ); + return( (b->a_access & ~ACL_SELF) >= access ); } } } if ( b->a_addrpat != NULL ) { - if ( regex_matches( b->a_addrpat, conn->c_addr ) ) { - if ( odn ) free( odn ); + if ( regex_matches( b->a_addrpat, conn->c_addr, edn, matches ) ) { Debug( LDAP_DEBUG_ACL, - "<= acl: matched by clause #%d access %s\n", + "<= acl_access_allowed: matched by clause #%d access %s\n", i, (b->a_access & ~ACL_SELF) >= access ? "granted" : "denied", 0 ); @@ -243,31 +297,33 @@ acl_access_allowed( } } if ( b->a_domainpat != NULL ) { - if ( regex_matches( b->a_domainpat, conn->c_domain ) ) { - if ( odn ) free( odn ); + Debug( LDAP_DEBUG_ARGS, "<= check a_domainpath: %s\n", + b->a_domainpat, 0, 0 ); + if ( regex_matches( b->a_domainpat, conn->c_domain, edn, matches ) ) + { Debug( LDAP_DEBUG_ACL, - "<= acl: matched by clause #%d access %s\n", + "<= acl_access_allowed: matched by clause #%d access %s\n", i, (b->a_access & ~ACL_SELF) >= access ? "granted" : "denied", 0 ); return( (b->a_access & ~ACL_SELF) >= access ); } } - if ( b->a_dnattr != NULL && op->o_dn != NULL ) { + if ( b->a_dnattr != NULL && op->o_ndn != NULL ) { + Debug( LDAP_DEBUG_ARGS, "<= check a_dnattr: %s\n", + b->a_dnattr, 0, 0); /* see if asker is listed in dnattr */ - if ( (at = attr_find( e->e_attrs, b->a_dnattr )) - != NULL && value_find( at->a_vals, &bv, - at->a_syntax, 3 ) == 0 ) + if ( (at = attr_find( e->e_attrs, b->a_dnattr )) != NULL && + value_find( at->a_vals, &bv, at->a_syntax, 3 ) == 0 ) { - if ( (b->a_access & ACL_SELF) && (val == NULL - || value_cmp( &bv, val, at->a_syntax, - 2 )) ) { + if ( (b->a_access & ACL_SELF) && + (val == NULL || value_cmp( &bv, val, at->a_syntax, 2 )) ) + { continue; } - if ( odn ) free( odn ); Debug( LDAP_DEBUG_ACL, - "<= acl: matched by clause #%d access %s\n", + "<= acl_acces_allowed: matched by clause #%d access %s\n", i, (b->a_access & ~ACL_SELF) >= access ? "granted" : "denied", 0 ); @@ -276,29 +332,51 @@ acl_access_allowed( /* asker not listed in dnattr - check for self access */ if ( ! (b->a_access & ACL_SELF) || val == NULL || - value_cmp( &bv, val, at->a_syntax, 2 ) != 0 ) { + value_cmp( &bv, val, at->a_syntax, 2 ) != 0 ) + { continue; } - if ( odn ) free( odn ); Debug( LDAP_DEBUG_ACL, - "<= acl: matched by clause #%d (self) access %s\n", + "<= acl_access_allowed: matched by clause #%d (self) access %s\n", i, (b->a_access & ~ACL_SELF) >= access ? "granted" : "denied", 0 ); return( (b->a_access & ~ACL_SELF) >= access ); } +#ifdef SLAPD_ACLGROUPS + if ( b->a_group != NULL && op->o_ndn != NULL ) { + char buf[1024]; + + /* b->a_group is an unexpanded entry name, expanded it should be an + * entry with objectclass group* and we test to see if odn is one of + * the values in the attribute group + */ + /* see if asker is listed in dnattr */ + string_expand(buf, sizeof(buf), b->a_group, edn, matches); + (void) dn_normalize_case(buf); + + if (backend_group(be, e, buf, odn, + b->a_objectclassvalue, b->a_groupattrname) == 0) + { + Debug( LDAP_DEBUG_ACL, + "<= acl_access_allowed: matched by clause #%d (group) access granted\n", + i, 0, 0 ); + return( (b->a_access & ~ACL_SELF) >= access ); + } + } +#endif /* SLAPD_ACLGROUPS */ } - if ( odn ) free( odn ); - Debug( LDAP_DEBUG_ACL, "<= acl: %s by default (no matching by)\n", + Debug( LDAP_DEBUG_ACL, + "<= acl_access_allowed: %s by default (no matching by)\n", default_access >= access ? "granted" : "denied", 0, 0 ); return( default_access >= access ); } /* - * acl_check_mods - check access control on the given entry to see if + * 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 @@ -306,50 +384,63 @@ acl_access_allowed( */ int -acl_check_mods( +acl_check_modlist( Backend *be, Connection *conn, Operation *op, Entry *e, - LDAPMod *mods + LDAPModList *mlist ) { int i; struct acl *a; - - for ( ; mods != NULL; mods = mods->mod_next ) { - if ( strcasecmp( mods->mod_type, "modifiersname" ) == 0 || - strcasecmp( mods->mod_type, "modifytimestamp" ) == 0 ) { + char *edn = e->e_ndn; + + for ( ; mlist != NULL; mlist = mlist->ml_next ) { + regmatch_t matches[MAXREMATCHES]; + + /* the lastmod attributes are ignored by ACL checking */ + if ( strcasecmp( mlist->ml_type, "modifiersname" ) == 0 || + strcasecmp( mlist->ml_type, "modifytimestamp" ) == 0 || + strcasecmp( mlist->ml_type, "creatorsname" ) == 0 || + strcasecmp( mlist->ml_type, "createtimestamp" ) == 0 ) + { + Debug( LDAP_DEBUG_ACL, "LASTMOD attribute: %s access allowed\n", + mlist->ml_type, 0, 0 ); continue; } - a = acl_get_applicable( be, op, e, mods->mod_type ); + a = acl_get_applicable( be, op, e, mlist->ml_type, + MAXREMATCHES, matches ); - switch ( mods->mod_op & ~LDAP_MOD_BVALUES ) { + switch ( mlist->ml_op & ~LDAP_MOD_BVALUES ) { case LDAP_MOD_REPLACE: case LDAP_MOD_ADD: - if ( mods->mod_bvalues == NULL ) { + if ( mlist->ml_bvalues == NULL ) { break; } - for ( i = 0; mods->mod_bvalues[i] != NULL; i++ ) { - if ( ! acl_access_allowed( a, be, conn, e, - mods->mod_bvalues[i], op, ACL_WRITE ) ) { + for ( i = 0; mlist->ml_bvalues[i] != NULL; i++ ) { + if ( ! acl_access_allowed( a, be, conn, e, mlist->ml_bvalues[i], + op, ACL_WRITE, edn, matches) ) + { return( LDAP_INSUFFICIENT_ACCESS ); } } break; case LDAP_MOD_DELETE: - if ( mods->mod_bvalues == NULL ) { + if ( mlist->ml_bvalues == NULL ) { if ( ! acl_access_allowed( a, be, conn, e, - NULL, op, ACL_WRITE ) ) { + NULL, op, ACL_WRITE, edn, matches) ) + { return( LDAP_INSUFFICIENT_ACCESS ); } break; } - for ( i = 0; mods->mod_bvalues[i] != NULL; i++ ) { - if ( ! acl_access_allowed( a, be, conn, e, - mods->mod_bvalues[i], op, ACL_WRITE ) ) { + for ( i = 0; mlist->ml_bvalues[i] != NULL; i++ ) { + if ( ! acl_access_allowed( a, be, conn, e, mlist->ml_bvalues[i], + op, ACL_WRITE, edn, matches) ) + { return( LDAP_INSUFFICIENT_ACCESS ); } } @@ -360,45 +451,92 @@ acl_check_mods( return( LDAP_SUCCESS ); } -#ifdef sunos5 - -static int -regex_matches( char *pat, char *str ) +static void +string_expand( + char *newbuf, + int bufsiz, + char *pat, + char *match, + regmatch_t *matches) { - char *e; - int rc; - - if ( (e = compile( pat, NULL, NULL )) == NULL ) { - Debug( LDAP_DEBUG_ANY, - "compile( \"%s\", \"%s\") failed\n", pat, str, 0 ); - return( 0 ); + int size; + char *sp; + char *dp; + int flag; + + size = 0; + newbuf[0] = '\0'; + + flag = 0; + for ( dp = newbuf, sp = pat; size < bufsiz && *sp ; sp++) { + /* did we previously see a $ */ + if (flag) { + if (*sp == '$') { + *dp++ = '$'; + size++; + } else if (*sp >= '0' && *sp <= '9' ) { + int n; + int i; + char *ep; + int l; + + n = *sp - '0'; + *dp = '\0'; + i = matches[n].rm_so; + l = matches[n].rm_eo; + for ( ; size < 512 && i < l; size++, i++ ) { + *dp++ = match[i]; + size++; + } + *dp = '\0'; + } + flag = 0; + } else { + if (*sp == '$') { + flag = 1; + } else { + *dp++ = *sp; + size++; + } + } } - rc = step( str ? str : "", e ); - free( e ); + *dp = '\0'; - return( rc ); + Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern: %s\n", pat, 0, 0 ); + Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", newbuf, 0, 0 ); } -#else /* sunos5 */ - static int -regex_matches( char *pat, char *str ) +regex_matches( + char *pat, /* pattern to expand and match against */ + char *str, /* string to match against pattern */ + char *buf, /* buffer with $N expansion variables */ + regmatch_t *matches /* offsets in buffer for $N expansion variables */ +) { - char *e; + regex_t re; + char newbuf[512]; int rc; - pthread_mutex_lock( ®ex_mutex ); - if ( (e = re_comp( pat )) != NULL ) { - Debug( LDAP_DEBUG_ANY, - "re_comp( \"%s\", \"%s\") failed because (%s)\n", pat, str, - e ); - pthread_mutex_unlock( ®ex_mutex ); + string_expand(newbuf, sizeof(newbuf), pat, buf, matches); + if (( rc = regcomp(&re, newbuf, REG_EXTENDED|REG_ICASE))) { + char error[512]; + regerror(rc, &re, error, sizeof(error)); + + Debug( LDAP_DEBUG_TRACE, + "compile( \"%s\", \"%s\") failed %s\n", + pat, str, error ); return( 0 ); } - rc = re_exec( str ? str : "" ); - pthread_mutex_unlock( ®ex_mutex ); - return( rc == 1 ); + rc = regexec(&re, str, 0, NULL, 0); + regfree( &re ); + + Debug( LDAP_DEBUG_TRACE, + "=> regex_matches: string: %s\n", str, 0, 0 ); + Debug( LDAP_DEBUG_TRACE, + "=> regex_matches: rc: %d %s\n", + rc, !rc ? "matches" : "no matches", 0 ); + return( !rc ); } -#endif /* sunos5 */