/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
- * Copyright 1998-2005 The OpenLDAP Foundation.
+ * Copyright 1998-2006 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
#include "lber_pvt.h"
#include "lutil.h"
-#define ACI_BUF_SIZE 1024 /* use most appropriate size */
+/* use most appropriate size */
+#define ACI_BUF_SIZE 1024
+
+/* move to "stable" when no longer experimental */
+#define SLAPD_ACI_SYNTAX "1.3.6.1.4.1.4203.666.2.1"
+
+/* change this to "OpenLDAPset" */
+#define SLAPD_ACI_SET_ATTR "template"
+
+typedef enum slap_aci_scope_t {
+ SLAP_ACI_SCOPE_ENTRY = 0x1,
+ SLAP_ACI_SCOPE_CHILDREN = 0x2,
+ SLAP_ACI_SCOPE_SUBTREE = ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN )
+} slap_aci_scope_t;
+
+enum {
+ ACI_BV_ENTRY,
+ ACI_BV_CHILDREN,
+ ACI_BV_ONELEVEL,
+ ACI_BV_SUBTREE,
+
+ ACI_BV_BR_ENTRY,
+ ACI_BV_BR_ALL,
+
+ ACI_BV_ACCESS_ID,
+ ACI_BV_PUBLIC,
+ ACI_BV_USERS,
+ ACI_BV_SELF,
+ ACI_BV_DNATTR,
+ ACI_BV_GROUP,
+ ACI_BV_ROLE,
+ ACI_BV_SET,
+ ACI_BV_SET_REF,
+
+ ACI_BV_GRANT,
+ ACI_BV_DENY,
+
+ ACI_BV_GROUP_CLASS,
+ ACI_BV_GROUP_ATTR,
+ ACI_BV_ROLE_CLASS,
+ ACI_BV_ROLE_ATTR,
+
+ ACI_BV_SET_ATTR,
+
+ ACI_BV_LAST
+};
+
+static const struct berval aci_bv[] = {
+ /* scope */
+ BER_BVC("entry"),
+ BER_BVC("children"),
+ BER_BVC("onelevel"),
+ BER_BVC("subtree"),
+
+ /* */
+ BER_BVC("[entry]"),
+ BER_BVC("[all]"),
+
+ /* type */
+ BER_BVC("access-id"),
+ BER_BVC("public"),
+ BER_BVC("users"),
+ BER_BVC("self"),
+ BER_BVC("dnattr"),
+ BER_BVC("group"),
+ BER_BVC("role"),
+ BER_BVC("set"),
+ BER_BVC("set-ref"),
+
+ /* actions */
+ BER_BVC("grant"),
+ BER_BVC("deny"),
+
+ /* schema */
+ BER_BVC(SLAPD_GROUP_CLASS),
+ BER_BVC(SLAPD_GROUP_ATTR),
+ BER_BVC(SLAPD_ROLE_CLASS),
+ BER_BVC(SLAPD_ROLE_ATTR),
+
+ BER_BVC(SLAPD_ACI_SET_ATTR),
+
+ BER_BVNULL
+};
+
+static AttributeDescription *slap_ad_aci;
+
+static int
+OpenLDAPaciValidate(
+ Syntax *syntax,
+ struct berval *val );
+
+static int
+OpenLDAPaciPretty(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *out,
+ void *ctx );
+
+static int
+OpenLDAPaciNormalize(
+ slap_mask_t use,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *out,
+ void *ctx );
+
+#define OpenLDAPaciMatch octetStringMatch
static int
aci_list_map_rights(
}
switch ( *bv.bv_val ) {
+ case 'x':
+ /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not
+ * define any equivalent to the AUTH right, so I've just used
+ * 'x' for now.
+ */
+ ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
+ break;
+ case 'd':
+ /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
+ * the right 'd' to mean "delete"; we hijack it to mean
+ * "disclose" for consistency wuith the rest of slapd.
+ */
+ ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
+ break;
case 'c':
ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
break;
case 'w':
ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
break;
- case 'x':
- /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not
- * define any equivalent to the AUTH right, so I've just used
- * 'x' for now.
- */
- ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
- break;
default:
break;
}
const char *text;
int rc;
- /* format of string is "group/objectClassValue/groupAttrName" */
+ /* format of string is "{group|role}/objectClassValue/groupAttrName" */
if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
return 0;
}
return rc;
}
-int
+static int
aci_mask(
Operation *op,
Entry *e,
slap_access_t *deny,
slap_aci_scope_t asserted_scope )
{
- struct berval bv, scope, perms, type, sdn;
+ struct berval bv,
+ scope,
+ perms,
+ type,
+ opts,
+ sdn;
int rc;
sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
+ /* get the type options, if any */
+ if ( acl_get_part( &type, 1, '/', &opts ) > 0 ) {
+ opts.bv_len = type.bv_len - ( opts.bv_val - type.bv_val );
+ type.bv_len = opts.bv_val - type.bv_val - 1;
+
+ } else {
+ BER_BVZERO( &opts );
+ }
+
if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
return dn_match( &op->o_ndn, &sdn );
return rc;
} else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
- if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_GROUP_CLASS ],
- &aci_bv[ ACI_BV_GROUP_ATTR ], op, e, nmatch, matches ) )
+ struct berval oc,
+ at;
+
+ if ( BER_BVISNULL( &opts ) ) {
+ oc = aci_bv[ ACI_BV_GROUP_CLASS ];
+ at = aci_bv[ ACI_BV_GROUP_ATTR ];
+
+ } else {
+ if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
+ assert( 0 );
+ }
+
+ if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
+ at = aci_bv[ ACI_BV_GROUP_ATTR ];
+ }
+ }
+
+ if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
{
return 1;
}
} else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
- if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_ROLE_CLASS ],
- &aci_bv[ ACI_BV_ROLE_ATTR ], op, e, nmatch, matches ) )
+ struct berval oc,
+ at;
+
+ if ( BER_BVISNULL( &opts ) ) {
+ oc = aci_bv[ ACI_BV_ROLE_CLASS ];
+ at = aci_bv[ ACI_BV_ROLE_ATTR ];
+
+ } else {
+ if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
+ assert( 0 );
+ }
+
+ if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
+ at = aci_bv[ ACI_BV_ROLE_ATTR ];
+ }
+ }
+
+ if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
{
return 1;
}
} else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
- if ( acl_match_set( &sdn, op, e, 0 ) ) {
+ if ( acl_match_set( &sdn, op, e, NULL ) ) {
return 1;
}
} else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
- if ( acl_match_set( &sdn, op, e, 1 ) ) {
+ if ( acl_match_set( &sdn, op, e, (struct berval *)&aci_bv[ ACI_BV_SET_ATTR ] ) ) {
return 1;
}
+
+ } else {
+ /* it passed normalization! */
+ assert( 0 );
}
return 0;
}
-#ifdef SLAP_DYNACL
-/*
- * FIXME: there is a silly dependence that makes it difficult
- * to move ACIs in a run-time loadable module under the "dynacl"
- * umbrella, because sets share some helpers with ACIs.
- */
static int
-dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *right, void **privp )
+aci_init( void )
+{
+ /* OpenLDAP eXperimental Syntax */
+ static slap_syntax_defs_rec aci_syntax_def = {
+ "( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
+ SLAP_SYNTAX_HIDE,
+ OpenLDAPaciValidate,
+ OpenLDAPaciPretty
+ };
+ static slap_mrule_defs_rec aci_mr_def = {
+ "( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' "
+ "SYNTAX 1.3.6.1.4.1.4203.666.2.1 )",
+ SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
+ NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch,
+ NULL, NULL,
+ NULL
+ };
+ static struct {
+ char *name;
+ char *desc;
+ slap_mask_t flags;
+ AttributeDescription **ad;
+ } aci_at = {
+ "OpenLDAPaci", "( 1.3.6.1.4.1.4203.666.1.5 "
+ "NAME 'OpenLDAPaci' "
+ "DESC 'OpenLDAP access control information (experimental)' "
+ "EQUALITY OpenLDAPaciMatch "
+ "SYNTAX 1.3.6.1.4.1.4203.666.2.1 "
+ "USAGE directoryOperation )",
+ SLAP_AT_HIDE,
+ &slap_ad_aci
+ };
+
+ LDAPAttributeType *at;
+ AttributeType *sat;
+ int rc;
+ const char *text;
+
+ /* ACI syntax */
+ rc = register_syntax( &aci_syntax_def );
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ /* ACI equality rule */
+ rc = register_matching_rule( &aci_mr_def );
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ /* ACI attribute */
+ at = ldap_str2attributetype( aci_at.desc,
+ &rc, &text, LDAP_SCHEMA_ALLOW_ALL );
+ if ( !at ) {
+ Debug( LDAP_DEBUG_ANY,
+ "aci_init: AttributeType \"%s\" parse failed: %s %s\n",
+ aci_at.name, ldap_scherr2str( rc ), text );
+ return rc;
+ }
+
+ rc = at_add( at, 0, &sat, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_attributetype_free( at );
+ Debug( LDAP_DEBUG_ANY,
+ "aci_init: AttributeType \"%s\" load failed: %s %s\n",
+ aci_at.name, scherr2str( rc ), text );
+ return rc;
+ }
+ ldap_memfree( at );
+
+ rc = slap_str2ad( aci_at.name,
+ aci_at.ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "aci_init: unable to find AttributeDescription "
+ "\"%s\": %d (%s)\n",
+ aci_at.name, rc, text );
+ return 1;
+ }
+
+ /* install flags */
+ sat->sat_flags |= aci_at.flags;
+
+ return rc;
+}
+
+static int
+dynacl_aci_parse(
+ const char *fname,
+ int lineno,
+ const char *opts,
+ slap_style_t sty,
+ const char *right,
+ void **privp )
{
AttributeDescription *ad = NULL;
const char *text = NULL;
}
} else {
- ad = slap_schema.si_ad_aci;
+ ad = slap_ad_aci;
}
if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
char accessmaskbuf1[ACCESSMASK_MAXLEN];
#endif /* LDAP_DEBUG */
+ if ( BER_BVISEMPTY( &e->e_nname ) ) {
+ /* no ACIs in the root DSE */
+ return -1;
+ }
+
/* start out with nothing granted, nothing denied */
ACL_INIT(tgrant);
ACL_INIT(tdeny);
}
}
- Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
+ Debug( LDAP_DEBUG_ACL, " <= aci_mask grant %s deny %s\n",
accessmask2str( tgrant, accessmaskbuf, 1 ),
accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
}
if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
struct berval parent_ndn;
-#if 1
- /* to solve the chicken'n'egg problem of accessing
- * the OpenLDAPaci attribute, the direct access
- * to the entry's attribute is unchecked; however,
- * further accesses to OpenLDAPaci values in the
- * ancestors occur through backend_attribute(), i.e.
- * with the identity of the operation, requiring
- * further access checking. For uniformity, this
- * makes further requests occur as the rootdn, if
- * any, i.e. searching for the OpenLDAPaci attribute
- * is considered an internal search. If this is not
- * acceptable, then the same check needs be performed
- * when accessing the entry's attribute. */
- Operation op2 = *op;
-
- if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
- op2.o_dn = op->o_bd->be_rootdn;
- op2.o_ndn = op->o_bd->be_rootndn;
- }
-#endif
-
dnParent( &e->e_nname, &parent_ndn );
while ( !BER_BVISEMPTY( &parent_ndn ) ){
int i;
BerVarray bvals = NULL;
int ret, stop;
- Debug( LDAP_DEBUG_ACL, "checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
- ret = backend_attribute( &op2, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
+ /* to solve the chicken'n'egg problem of accessing
+ * the OpenLDAPaci attribute, the direct access
+ * to the entry's attribute is unchecked; however,
+ * further accesses to OpenLDAPaci values in the
+ * ancestors occur through backend_attribute(), i.e.
+ * with the identity of the operation, requiring
+ * further access checking. For uniformity, this
+ * makes further requests occur as the rootdn, if
+ * any, i.e. searching for the OpenLDAPaci attribute
+ * is considered an internal search. If this is not
+ * acceptable, then the same check needs be performed
+ * when accessing the entry's attribute. */
+ struct berval save_o_dn, save_o_ndn;
+
+ if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
+ save_o_dn = op->o_dn;
+ save_o_ndn = op->o_ndn;
+
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ }
+
+ Debug( LDAP_DEBUG_ACL, " checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
+ ret = backend_attribute( op, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
+
+ if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
+ op->o_dn = save_o_dn;
+ op->o_ndn = save_o_ndn;
+ }
switch ( ret ) {
case LDAP_SUCCESS :
int
dynacl_aci_init( void )
{
- return slap_dynacl_register( &dynacl_aci );
+ int rc;
+
+ rc = aci_init();
+
+ if ( rc == 0 ) {
+ rc = slap_dynacl_register( &dynacl_aci );
+ }
+
+ return rc;
}
-#endif /* SLAP_DYNACL */
/* ACI syntax validation */
for ( i = 0; i < perms->bv_len; ) {
switch ( perms->bv_val[ i ] ) {
+ case 'x':
+ case 'd':
case 'c':
case 's':
case 'r':
case 'w':
- case 'x':
break;
default:
NULL
};
-int
+static int
OpenLDAPaciValidate(
Syntax *syntax,
struct berval *val )
struct berval ocbv = BER_BVNULL,
atbv = BER_BVNULL;
- ocbv.bv_val = strchr( type.bv_val, '/' );
+ ocbv.bv_val = ber_bvchr( &type, '/' );
if ( ocbv.bv_val != NULL ) {
ocbv.bv_val++;
+ ocbv.bv_len = type.bv_len
+ - ( ocbv.bv_val - type.bv_val );
- atbv.bv_val = strchr( ocbv.bv_val, '/' );
+ atbv.bv_val = ber_bvchr( &ocbv, '/' );
if ( atbv.bv_val != NULL ) {
AttributeDescription *ad = NULL;
const char *text = NULL;
if ( rc != LDAP_SUCCESS ) {
return LDAP_INVALID_SYNTAX;
}
-
- } else {
- ocbv.bv_len = type.bv_len
- - ( ocbv.bv_val - type.bv_val );
}
if ( oc_bvfind( &ocbv ) == NULL ) {
subject = BER_BVNULL,
nsubject = BER_BVNULL;
int idx,
- rc,
+ rc = LDAP_SUCCESS,
freesubject = 0,
freetype = 0;
char *ptr;
struct berval ocbv = BER_BVNULL,
atbv = BER_BVNULL;
- ocbv.bv_val = strchr( type.bv_val, '/' );
+ ocbv.bv_val = ber_bvchr( &type, '/' );
if ( ocbv.bv_val != NULL ) {
ObjectClass *oc = NULL;
AttributeDescription *ad = NULL;
bv.bv_len = ntype.bv_len;
ocbv.bv_val++;
+ ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val );
- atbv.bv_val = strchr( ocbv.bv_val, '/' );
+ atbv.bv_val = ber_bvchr( &ocbv, '/' );
if ( atbv.bv_val != NULL ) {
atbv.bv_val++;
atbv.bv_len = type.bv_len
}
bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
-
- } else {
- ocbv.bv_len = type.bv_len
- - ( ocbv.bv_val - type.bv_val );
}
- if ( oc_bvfind( &ocbv ) == NULL ) {
+ oc = oc_bvfind( &ocbv );
+ if ( oc == NULL ) {
rc = LDAP_INVALID_SYNTAX;
goto cleanup;
}
return rc;
}
-int
+static int
OpenLDAPaciPretty(
Syntax *syntax,
struct berval *val,
return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
}
-int
+static int
OpenLDAPaciNormalize(
slap_mask_t use,
Syntax *syntax,
return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
}
+#if SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC
+/*
+ * FIXME: need config and Makefile.am code to ease building
+ * as dynamic module
+ */
+int
+init_module( int argc, char *argv[] )
+{
+ return dynacl_aci_init();
+}
+#endif /* SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC */
+
#endif /* SLAPD_ACI_ENABLED */