-static int
-aci_mask(
- Operation *op,
- Entry *e,
- AttributeDescription *desc,
- struct berval *val,
- struct berval *aci,
- int nmatch,
- regmatch_t *matches,
- slap_access_t *grant,
- slap_access_t *deny,
- slap_aci_scope_t asserted_scope
-)
-{
- struct berval bv, scope, perms, type, sdn;
- int rc;
-
-
- assert( !BER_BVISNULL( &desc->ad_cname ) );
-
- /* parse an aci of the form:
- oid # scope # action;rights;attr;rights;attr
- $ action;rights;attr;rights;attr # type # subject
-
- [NOTE: the following comment is very outdated,
- as the draft version it refers to (Ando, 2004-11-20)].
-
- See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
- a full description of the format for this attribute.
- Differences: "this" in the draft is "self" here, and
- "self" and "public" is in the position of type.
-
- <scope> = {entry|children|subtree}
- <type> = {public|users|access-id|subtree|onelevel|children|
- self|dnattr|group|role|set|set-ref}
-
- This routine now supports scope={ENTRY,CHILDREN}
- with the semantics:
- - ENTRY applies to "entry" and "subtree";
- - CHILDREN aplies to "children" and "subtree"
- */
-
- /* check that the aci has all 5 components */
- if ( aci_get_part( aci, 4, '#', NULL ) < 0 ) {
- return 0;
- }
-
- /* check that the aci family is supported */
- if ( aci_get_part( aci, 0, '#', &bv ) < 0 ) {
- return 0;
- }
-
- /* check that the scope matches */
- if ( aci_get_part( aci, 1, '#', &scope ) < 0 ) {
- return 0;
- }
-
- /* note: scope can be either ENTRY or CHILDREN;
- * they respectively match "entry" and "children" in bv
- * both match "subtree" */
- switch ( asserted_scope ) {
- case SLAP_ACI_SCOPE_ENTRY:
- if ( ber_bvstrcasecmp( &scope, &aci_bv_entry ) != 0
- && ber_bvstrcasecmp( &scope, &aci_bv_subtree ) != 0 )
- {
- return 0;
- }
- break;
-
- case SLAP_ACI_SCOPE_CHILDREN:
- if ( ber_bvstrcasecmp( &scope, &aci_bv_children ) != 0
- && ber_bvstrcasecmp( &scope, &aci_bv_subtree ) != 0 )
- {
- return 0;
- }
- break;
-
- default:
- return 0;
- }
-
- /* get the list of permissions clauses, bail if empty */
- if ( aci_get_part( aci, 2, '#', &perms ) <= 0 ) {
- return 0;
- }
-
- /* check if any permissions allow desired access */
- if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
- return 0;
- }
-
- /* see if we have a DN match */
- if ( aci_get_part( aci, 3, '#', &type ) < 0 ) {
- return 0;
- }
-
- /* see if we have a public (i.e. anonymous) access */
- if ( ber_bvstrcasecmp( &aci_bv_public, &type ) == 0 ) {
- return 1;
- }
-
- /* otherwise require an identity */
- if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
- return 0;
- }
-
- /* see if we have a users access */
- if ( ber_bvstrcasecmp( &aci_bv_users, &type ) == 0 ) {
- return 1;
- }
-
- /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
- * just grab all the berval up to its end (ITS#3303).
- * NOTE: the problem could be solved by providing the DN with
- * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would
- * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
-#if 0
- if ( aci_get_part( aci, 4, '#', &sdn ) < 0 ) {
- return 0;
- }
-#endif
- sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
- sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
-
- if ( ber_bvstrcasecmp( &aci_bv_access_id, &type ) == 0 ) {
- struct berval ndn;
-
- rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
- if ( rc != LDAP_SUCCESS ) {
- return 0;
- }
-
- if ( dn_match( &op->o_ndn, &ndn ) ) {
- rc = 1;
- }
- slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
-
- return rc;
-
- } else if ( ber_bvstrcasecmp( &aci_bv_subtree, &type ) == 0 ) {
- struct berval ndn;
-
- rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
- if ( rc != LDAP_SUCCESS ) {
- return 0;
- }
-
- if ( dnIsSuffix( &op->o_ndn, &ndn ) ) {
- rc = 1;
- }
- slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
-
- return rc;
-
- } else if ( ber_bvstrcasecmp( &aci_bv_onelevel, &type ) == 0 ) {
- struct berval ndn, pndn;
-
- rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
- if ( rc != LDAP_SUCCESS ) {
- return 0;
- }
-
- dnParent( &ndn, &pndn );
-
- if ( dn_match( &op->o_ndn, &pndn ) ) {
- rc = 1;
- }
- slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
-
- return rc;
-
- } else if ( ber_bvstrcasecmp( &aci_bv_children, &type ) == 0 ) {
- struct berval ndn;
-
- rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
- if ( rc != LDAP_SUCCESS ) {
- return 0;
- }
-
- if ( !dn_match( &op->o_ndn, &ndn )
- && dnIsSuffix( &op->o_ndn, &ndn ) )
- {
- rc = 1;
- }
- slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
-
- return rc;
-
- } else if ( ber_bvstrcasecmp( &aci_bv_self, &type ) == 0 ) {
- if ( dn_match( &op->o_ndn, &e->e_nname ) ) {
- return 1;
- }
-
- } else if ( ber_bvstrcasecmp( &aci_bv_dnattr, &type ) == 0 ) {
- Attribute *at;
- AttributeDescription *ad = NULL;
- const char *text;
-
- rc = slap_bv2ad( &sdn, &ad, &text );
-
- if( rc != LDAP_SUCCESS ) {
- return 0;
- }
-
- rc = 0;
-
- for ( at = attrs_find( e->e_attrs, ad );
- at != NULL;
- at = attrs_find( at->a_next, ad ) )
- {
- if ( value_find_ex( ad,
- SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
- SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
- at->a_nvals,
- &op->o_ndn, op->o_tmpmemctx ) == 0 )
- {
- rc = 1;
- break;
- }
- }
-
- return rc;
-
- } else if ( ber_bvstrcasecmp( &aci_bv_group, &type ) == 0 ) {
- if ( aci_group_member( &sdn, &aci_bv_group_class,
- &aci_bv_group_attr, op, e, nmatch, matches ) )
- {
- return 1;
- }
-
- } else if ( ber_bvstrcasecmp( &aci_bv_role, &type ) == 0 ) {
- if ( aci_group_member( &sdn, &aci_bv_role_class,
- &aci_bv_role_attr, op, e, nmatch, matches ) )
- {
- return 1;
- }
-
- } else if ( ber_bvstrcasecmp( &aci_bv_set, &type ) == 0 ) {
- if ( aci_match_set( &sdn, op, e, 0 ) ) {
- return 1;
- }
-
- } else if ( ber_bvstrcasecmp( &aci_bv_set_ref, &type ) == 0 ) {
- if ( aci_match_set( &sdn, op, e, 1 ) ) {
- return 1;
- }
- }
-
- 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 )
-{
- AttributeDescription *ad = NULL;
- const char *text = NULL;
-
- if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
- fprintf( stderr, "%s: line %d: "
- "inappropriate style \"%s\" in \"aci\" by clause\n",
- fname, lineno, style_strings[sty] );
- return -1;
- }
-
- if ( right != NULL && *right != '\0' ) {
- if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
- fprintf( stderr,
- "%s: line %d: aci \"%s\": %s\n",
- fname, lineno, right, text );
- return -1;
- }
-
- } else {
- ad = slap_schema.si_ad_aci;
- }
-
- if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
- fprintf( stderr, "%s: line %d: "
- "aci \"%s\": inappropriate syntax: %s\n",
- fname, lineno, right,
- ad->ad_type->sat_syntax_oid );
- return -1;
- }
-
- *privp = (void *)ad;
-
- return 0;
-}
-
-static int
-dynacl_aci_unparse( void *priv, struct berval *bv )
-{
- AttributeDescription *ad = ( AttributeDescription * )priv;
- char *ptr;
-
- assert( ad );
-
- bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
- ptr = lutil_strcopy( bv->bv_val, " aci=" );
- ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
- bv->bv_len = ptr - bv->bv_val;
-
- return 0;
-}
-
-
-static int
-dynacl_aci_mask(
- void *priv,
- Operation *op,
- Entry *e,
- AttributeDescription *desc,
- struct berval *val,
- int nmatch,
- regmatch_t *matches,
- slap_access_t *grantp,
- slap_access_t *denyp )
-{
- AttributeDescription *ad = ( AttributeDescription * )priv;
- Attribute *at;
- slap_access_t tgrant, tdeny, grant, deny;
-#ifdef LDAP_DEBUG
- char accessmaskbuf[ACCESSMASK_MAXLEN];
- char accessmaskbuf1[ACCESSMASK_MAXLEN];
-#endif /* LDAP_DEBUG */
-
- /* start out with nothing granted, nothing denied */
- ACL_INIT(tgrant);
- ACL_INIT(tdeny);
-
- /* get the aci attribute */
- at = attr_find( e->e_attrs, ad );
- if ( at != NULL ) {
- int i;
-
- /* the aci is an multi-valued attribute. The
- * rights are determined by OR'ing the individual
- * rights given by the acis.
- */
- for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
- if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
- nmatch, matches, &grant, &deny,
- SLAP_ACI_SCOPE_ENTRY ) != 0 )
- {
- tgrant |= grant;
- tdeny |= deny;
- }
- }
-
- Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
- accessmask2str( tgrant, accessmaskbuf, 1 ),
- accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
- }
-
- /* If the entry level aci didn't contain anything valid for the
- * current operation, climb up the tree and evaluate the
- * acis with scope set to subtree
- */
- 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 );
-
- switch ( ret ) {
- case LDAP_SUCCESS :
- stop = 0;
- if ( !bvals ) {
- break;
- }
-
- for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++) {
- if ( aci_mask( op, e, desc, val,
- &bvals[i],
- nmatch, matches,
- &grant, &deny,
- SLAP_ACI_SCOPE_CHILDREN ) != 0 )
- {
- tgrant |= grant;
- tdeny |= deny;
- /* evaluation stops as soon as either a "deny" or a
- * "grant" directive matches.
- */
- if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
- stop = 1;
- }
- }
- Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
- accessmask2str( tgrant, accessmaskbuf, 1 ),
- accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
- }
- break;
-
- case LDAP_NO_SUCH_ATTRIBUTE:
- /* just go on if the aci-Attribute is not present in
- * the current entry
- */
- Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
- stop = 0;
- break;
-
- case LDAP_NO_SUCH_OBJECT:
- /* We have reached the base object */
- Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
- stop = 1;
- break;
-
- default:
- stop = 1;
- break;
- }
-
- if ( stop ) {
- break;
- }
- dnParent( &parent_ndn, &parent_ndn );
- }
- }
-
- *grantp = tgrant;
- *denyp = tdeny;
-
- return 0;
-}
-
-/* need to register this at some point */
-static slap_dynacl_t dynacl_aci = {
- "aci",
- dynacl_aci_parse,
- dynacl_aci_unparse,
- dynacl_aci_mask,
- NULL,
- NULL,
- NULL
-};
-
-#endif /* SLAP_DYNACL */
-
-#endif /* SLAPD_ACI_ENABLED */
-