*
anonymous
users
- self
+ self[.<selfstyle>]
dn[.<dnstyle>[,<modifier>]]=<DN>
dnattr=<attrname>
.LP
.nf
<style>={exact|regex|expand}
+ <selfstyle>={level{<n>}}
<dnstyle>={{exact|base(object)}|regex
- |one(level)|sub(tree)|children}
+ |one(level)|sub(tree)|children|level{<n>}}
<groupstyle>={exact|expand}
<peernamestyle>={<style>|ip|path}
<domainstyle>={exact|regex|sub(tree)}
.B self
means access to an entry is allowed to the entry itself (e.g. the entry
being accessed and the requesting entry must be the same).
+It allows the
+.B level{<n>}
+style, where \fI<n>\fP indicates what ancestor of the DN
+is to be used in matches.
+A positive value indicates that the <n>-th ancestor of the user's DN
+is to be considered; a negative value indicates that the <n>-th ancestor
+of the target is to be considered.
+For example, a "\fIby self.level{1} ...\fP" clause would match
+when the object "\fIdc=example,dc=com\fP" is accessed
+by "\fIcn=User,dc=example,dc=com\fP".
+A "\fIby self.level{-1} ...\fP" clause would match when the same user
+accesses the object "\fIou=Address Book,cn=User,dc=example,dc=com\fP".
.LP
The statement
.B dn=<DN>
the
.BR one(level) ,
and the
-.B children
+.BR children
forms provide
.B $0
as the match of the entire string.
the
.BR one(level) ,
and the
-.B children
+.BR children
forms also provide
.B $1
as the match of the rightmost part of the DN as defined in the
.B <by>
clause is allowed.
.LP
+The
+.BR level{<n>}
+form is an extension and a generalization of the
+.BR onelevel
+form, which matches all DNs whose <n>-th ancestor is the pattern.
+So, \fIlevel{1}\fP is equivalent to \fIonelevel\fP,
+and \fIlevel{0}\fP is equivalent to \fIbase\fP.
+.LP
It is perfectly useless to give any access privileges to a DN
that exactly matches the
.B rootdn
.LP
The
.B search
-operation, for each entry, requires
+operation, requires
+.B search (=s)
+privileges on the
+.B entry
+pseudo-attribute of the searchBase (NOTE: this was introduced with 2.3).
+Then, for each entry, it requires
.B search (=s)
privileges on the attributes that are defined in the filter.
-Then, the resulting entries are tested for
+The resulting entries are finally tested for
.B read (=r)
privileges on the pseudo-attribute
.B entry
}
} else if ( b->a_dn_style == ACL_STYLE_SELF ) {
- if ( BER_BVISEMPTY( &op->o_ndn ) ) {
+ struct berval ndn, selfndn;
+ int level;
+
+ if ( BER_BVISEMPTY( &op->o_ndn ) || BER_BVISNULL( &e->e_nname ) ) {
continue;
}
+
+ level = b->a_dn_self_level;
+ if ( level < 0 ) {
+ selfndn = op->o_ndn;
+ ndn = e->e_nname;
+ level = -level;
+
+ } else {
+ ndn = op->o_ndn;
+ selfndn = e->e_nname;
+ }
+
+ for ( ; level > 0; level-- ) {
+ if ( BER_BVISEMPTY( &ndn ) ) {
+ break;
+ }
+ dnParent( &ndn, &ndn );
+ }
- if ( e->e_dn == NULL || !dn_match( &e->e_nname, &op->o_ndn ) ) {
+ if ( BER_BVISEMPTY( &ndn ) || !dn_match( &ndn, &selfndn ) )
+ {
continue;
}
if ( !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) ) {
goto dn_match_cleanup;
}
+
+ } else if ( b->a_dn_style == ACL_STYLE_LEVEL ) {
+ int level;
+ struct berval ndn;
+
+ if ( odnlen <= patlen ) {
+ goto dn_match_cleanup;
+ }
+
+ if ( level > 0 && !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) )
+ {
+ goto dn_match_cleanup;
+ }
+
+ level = b->a_dn_level;
+ ndn = op->o_ndn;
+ for ( ; level > 0; level-- ) {
+ if ( BER_BVISEMPTY( &ndn ) ) {
+ goto dn_match_cleanup;
+ }
+ dnParent( &ndn, &ndn );
+ if ( ndn.bv_len < patlen ) {
+ goto dn_match_cleanup;
+ }
+ }
+
+ if ( ndn.bv_len != patlen ) {
+ goto dn_match_cleanup;
+ }
}
- got_match = !strcmp( pat.bv_val, op->o_ndn.bv_val + odnlen - patlen );
+ got_match = !strcmp( pat.bv_val, &op->o_ndn.bv_val[ odnlen - patlen ] );
dn_match_cleanup:;
if ( pat.bv_val != b->a_dn_pat.bv_val ) {
"one",
"subtree",
"children",
+ "level",
"attrof",
+ "anonymous",
+ "users",
+ "self",
"ip",
"path",
NULL
for ( ; i < argc; i++ ) {
slap_style_t sty = ACL_STYLE_REGEX;
char *style_modifier = NULL;
+ char *style_level = NULL;
+ int level = 0;
int expand = 0;
split( argv[i], '=', &left, &right );
split( left, '.', &left, &style );
if ( style ) {
- split( style, ',', &style, &style_modifier);
+ split( style, ',', &style, &style_modifier );
+
+ if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) {
+ split( style, '{', &style, &style_level );
+ if ( style_level != NULL ) {
+ char *p = strchr( style_level, '}' );
+ if ( p == NULL ) {
+ fprintf( stderr,
+ "%s: line %d: premature eol: "
+ "expecting closing '}' in \"level{n}\"\n",
+ fname, lineno );
+ acl_usage();
+ } else if ( p == style_level ) {
+ fprintf( stderr,
+ "%s: line %d: empty level "
+ "in \"level{n}\"\n",
+ fname, lineno );
+ acl_usage();
+ }
+ p[0] = '\0';
+ }
+ }
}
if ( style == NULL || *style == '\0' ||
} else if ( strcasecmp( style, "children" ) == 0 ) {
sty = ACL_STYLE_CHILDREN;
+ } else if ( strcasecmp( style, "level" ) == 0 )
+ {
+ char *next;
+
+ level = strtol( style_level, &next, 10 );
+ if ( next[0] != '\0' ) {
+ fprintf( stderr,
+ "%s: line %d: unable to parse level "
+ "in \"level{n}\"\n",
+ fname, lineno );
+ acl_usage();
+ }
+
+ sty = ACL_STYLE_LEVEL;
+
} else if ( strcasecmp( style, "regex" ) == 0 ) {
sty = ACL_STYLE_REGEX;
}
b->a_dn_style = sty;
b->a_dn_expand = expand;
+ if ( sty == ACL_STYLE_SELF ) {
+ b->a_dn_self_level = level;
+
+ } else {
+ if ( level < 0 ) {
+ fprintf( stderr,
+ "%s: line %d: bad negative level \"%d\" "
+ "in by DN clause\n",
+ fname, lineno, level );
+ acl_usage();
+ } else if ( level == 1 ) {
+ fprintf( stderr,
+ "%s: line %d: \"onelevel\" should be used "
+ "instead of \"level{1}\" in by DN clause\n",
+ fname, lineno, 0 );
+ } else if ( level == 0 && sty == ACL_STYLE_LEVEL ) {
+ fprintf( stderr,
+ "%s: line %d: \"base\" should be used "
+ "instead of \"level{0}\" in by DN clause\n",
+ fname, lineno, 0 );
+ }
+
+ b->a_dn_level = level;
+ }
continue;
}
b->a_dn_style == ACL_STYLE_SELF )
{
ptr = lutil_strcopy( ptr, b->a_dn_pat.bv_val );
+ if ( b->a_dn_style == ACL_STYLE_SELF && b->a_dn_self_level != 0 ) {
+ int n = sprintf( ptr, ".level{%d}", b->a_dn_self_level );
+ if ( n > 0 ) {
+ ptr += n;
+ } /* else ? */
+ }
} else {
ptr = lutil_strcopy( ptr, "dn." );
ptr = lutil_strcopy( ptr, style_strings[b->a_dn_style] );
+ if ( b->a_dn_style == ACL_STYLE_LEVEL ) {
+ int n = sprintf( ptr, "{%d}", b->a_dn_level );
+ if ( n > 0 ) {
+ ptr += n;
+ } /* else ? */
+ }
+ if ( b->a_dn_expand ) {
+ ptr = lutil_strcopy( ptr, ",expand" );
+ }
*ptr++ = '=';
*ptr++ = '"';
ptr = lutil_strcopy( ptr, b->a_dn_pat.bv_val );
ACL_STYLE_ONE,
ACL_STYLE_SUBTREE,
ACL_STYLE_CHILDREN,
+ ACL_STYLE_LEVEL,
ACL_STYLE_ATTROF,
ACL_STYLE_ANONYMOUS,
ACL_STYLE_USERS,
#define a_dn_pat a_authz.sai_dn
slap_style_t a_dn_style;
+ int a_dn_level;
+ int a_dn_self_level;
AttributeDescription *a_dn_at;
int a_dn_self;
int a_dn_expand;