-#ifdef SLAPD_ACI_ENABLED
-static int
-aci_list_map_rights(
- struct berval *list )
-{
- struct berval bv;
- slap_access_t mask;
- int i;
-
- ACL_INIT(mask);
- for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
- if (bv.bv_len <= 0)
- continue;
- switch (*bv.bv_val) {
- case 'c':
- ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
- break;
- case 's':
- /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
- * the right 's' to mean "set", but in the examples states
- * that the right 's' means "search". The latter definition
- * is used here.
- */
- ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
- break;
- case 'r':
- ACL_PRIV_SET(mask, ACL_PRIV_READ);
- 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;
- }
-
- }
- return(mask);
-}
-
-static int
-aci_list_has_attr(
- struct berval *list,
- const struct berval *attr,
- struct berval *val )
-{
- struct berval bv, left, right;
- int i;
-
- for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
- if (aci_get_part(&bv, 0, '=', &left) < 0
- || aci_get_part(&bv, 1, '=', &right) < 0)
- {
- if (ber_bvstrcasecmp(attr, &bv) == 0)
- return(1);
- } else if (val == NULL) {
- if (ber_bvstrcasecmp(attr, &left) == 0)
- return(1);
- } else {
- if (ber_bvstrcasecmp(attr, &left) == 0) {
- /* this is experimental code that implements a
- * simple (prefix) match of the attribute value.
- * the ACI draft does not provide for aci's that
- * apply to specific values, but it would be
- * nice to have. If the <attr> part of an aci's
- * rights list is of the form <attr>=<value>,
- * that means the aci applies only to attrs with
- * the given value. Furthermore, if the attr is
- * of the form <attr>=<value>*, then <value> is
- * treated as a prefix, and the aci applies to
- * any value with that prefix.
- *
- * Ideally, this would allow r.e. matches.
- */
- if (aci_get_part(&right, 0, '*', &left) < 0
- || right.bv_len <= left.bv_len)
- {
- if (ber_bvstrcasecmp(val, &right) == 0)
- return(1);
- } else if (val->bv_len >= left.bv_len) {
- if (strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0)
- return(1);
- }
- }
- }
- }
- return(0);
-}
-
-static slap_access_t
-aci_list_get_attr_rights(
- struct berval *list,
- const struct berval *attr,
- struct berval *val )
-{
- struct berval bv;
- slap_access_t mask;
- int i;
-
- /* loop through each rights/attr pair, skip first part (action) */
- ACL_INIT(mask);
- for (i = 1; aci_get_part(list, i + 1, ';', &bv) >= 0; i += 2) {
- if (aci_list_has_attr(&bv, attr, val) == 0)
- continue;
- if (aci_get_part(list, i, ';', &bv) < 0)
- continue;
- mask |= aci_list_map_rights(&bv);
- }
- return(mask);
-}
-
-static int
-aci_list_get_rights(
- struct berval *list,
- const struct berval *attr,
- struct berval *val,
- slap_access_t *grant,
- slap_access_t *deny )
-{
- struct berval perm, actn;
- slap_access_t *mask;
- int i, found;
-
- if (attr == NULL || attr->bv_len == 0
- || ber_bvstrcasecmp( attr, &aci_bv_entry ) == 0) {
- attr = &aci_bv_br_entry;
- }
-
- found = 0;
- ACL_INIT(*grant);
- ACL_INIT(*deny);
- /* loop through each permissions clause */
- for (i = 0; aci_get_part(list, i, '$', &perm) >= 0; i++) {
- if (aci_get_part(&perm, 0, ';', &actn) < 0)
- continue;
- if (ber_bvstrcasecmp( &aci_bv_grant, &actn ) == 0) {
- mask = grant;
- } else if (ber_bvstrcasecmp( &aci_bv_deny, &actn ) == 0) {
- mask = deny;
- } else {
- continue;
- }
-
- found = 1;
- *mask |= aci_list_get_attr_rights(&perm, attr, val);
- *mask |= aci_list_get_attr_rights(&perm, &aci_bv_br_all, NULL);
- }
- return(found);
-}
-
-static int
-aci_group_member (
- struct berval *subj,
- struct berval *defgrpoc,
- struct berval *defgrpat,
- Operation *op,
- Entry *e,
- int nmatch,
- regmatch_t *matches
-)
-{
- struct berval subjdn;
- struct berval grpoc;
- struct berval grpat;
- ObjectClass *grp_oc = NULL;
- AttributeDescription *grp_ad = NULL;
- const char *text;
- int rc;
-
- /* format of string is "group/objectClassValue/groupAttrName" */
- if (aci_get_part(subj, 0, '/', &subjdn) < 0) {
- return(0);
- }
-
- if (aci_get_part(subj, 1, '/', &grpoc) < 0) {
- grpoc = *defgrpoc;
- }
-
- if (aci_get_part(subj, 2, '/', &grpat) < 0) {
- grpat = *defgrpat;
- }
-
- rc = slap_bv2ad( &grpat, &grp_ad, &text );
- if( rc != LDAP_SUCCESS ) {
- rc = 0;
- goto done;
- }
- rc = 0;
-
- grp_oc = oc_bvfind( &grpoc );
-
- if (grp_oc != NULL && grp_ad != NULL ) {
- char buf[ACL_BUF_SIZE];
- struct berval bv, ndn;
- bv.bv_len = sizeof( buf ) - 1;
- bv.bv_val = (char *)&buf;
- if ( string_expand(&bv, &subjdn,
- e->e_ndn, nmatch, matches) )
- {
- rc = LDAP_OTHER;
- goto done;
- }
- if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
- rc = ( backend_group( op, e, &ndn, &op->o_ndn,
- grp_oc, grp_ad ) == 0 );
- slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
- }
- }
-
-done:
- return(rc);
-}
-
-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
-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, 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_print( void *priv )
-{
- AttributeDescription *ad = ( AttributeDescription * )priv;
-
- assert( ad );
-
- fprintf( stderr, " aci=%s", ad->ad_cname.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 ),
- accessmask2str( tdeny, accessmaskbuf1 ), 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;
- struct berval old_parent_ndn = BER_BVNULL;
-
-#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 ( parent_ndn.bv_val != old_parent_ndn.bv_val ){
- int i;
- BerVarray bvals = NULL;
- int ret, stop;
-
- old_parent_ndn = parent_ndn;
- 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 ),
- accessmask2str( tdeny, accessmaskbuf1 ), 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( &old_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_print,
- dynacl_aci_mask,
- NULL,
- NULL,
- NULL
-};
-
-#endif /* SLAP_DYNACL */
-
-#endif /* SLAPD_ACI_ENABLED */
-