X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Faci.c;h=97276cfc0b625b26c83aa1e33c093b1ada4a2482;hb=58886b6a9c7c6b8e9e829fe24fe057e5a77b6117;hp=3d587cd9e661bb7d69ae376b534fb657fbcb8c86;hpb=ec49990d51658542eff1461366806cbfb58eb63f;p=openldap diff --git a/servers/slapd/aci.c b/servers/slapd/aci.c index 3d587cd9e6..97276cfc0b 100644 --- a/servers/slapd/aci.c +++ b/servers/slapd/aci.c @@ -2,7 +2,7 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * 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 @@ -40,7 +40,116 @@ #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_CHILDREN, + 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("[children]"), + 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( @@ -57,6 +166,20 @@ 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; @@ -74,13 +197,6 @@ aci_list_map_rights( 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; } @@ -162,14 +278,18 @@ aci_list_get_attr_rights( ACL_INIT(mask); for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) { if ( aci_list_has_attr( &bv, attr, val ) == 0 ) { + Debug( LDAP_DEBUG_ACL, " <= aci_list_get_attr_rights test %s for %s -> failed\n", bv.bv_val, attr->bv_val, 0 ); continue; } + Debug( LDAP_DEBUG_ACL, " <= aci_list_get_attr_rights test %s for %s -> ok\n", bv.bv_val, attr->bv_val, 0 ); if ( acl_get_part( list, i, ';', &bv ) < 0 ) { + Debug( LDAP_DEBUG_ACL, " <= aci_list_get_attr_rights test no rightsk\n", 0, 0, 0 ); continue; } mask |= aci_list_map_rights( &bv ); + Debug( LDAP_DEBUG_ACL, " <= aci_list_get_attr_rights rights %s to mask 0x%x\n", bv.bv_val, mask, 0 ); } return mask; @@ -177,22 +297,22 @@ aci_list_get_attr_rights( 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 *list, + struct berval *attr, + struct berval *val, + slap_access_t *grant, + slap_access_t *deny ) { - struct berval perm, actn; + struct berval perm, actn, baseattr; slap_access_t *mask; int i, found; - if ( attr == NULL || BER_BVISEMPTY( attr ) - || ber_bvstrcasecmp( attr, &aci_bv[ ACI_BV_ENTRY ] ) == 0 ) - { - attr = &aci_bv[ ACI_BV_BR_ENTRY ]; - } + if ( attr == NULL || BER_BVISEMPTY( attr ) ) { + attr = (struct berval *)&aci_bv[ ACI_BV_ENTRY ]; + } else if ( acl_get_part( attr, 0, ';', &baseattr ) > 0 ) { + attr = &baseattr; + } found = 0; ACL_INIT(*grant); ACL_INIT(*deny); @@ -239,7 +359,7 @@ aci_group_member ( 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; } @@ -286,7 +406,7 @@ done: return rc; } -int +static int aci_mask( Operation *op, Entry *e, @@ -299,7 +419,12 @@ aci_mask( 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; @@ -348,8 +473,7 @@ aci_mask( * both match "subtree" */ switch ( asserted_scope ) { case SLAP_ACI_SCOPE_ENTRY: - /* TODO: use ber_bvcmp */ - if ( ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0 + if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 ) { return 0; @@ -357,22 +481,21 @@ aci_mask( break; case SLAP_ACI_SCOPE_CHILDREN: - /* TODO: use ber_bvcmp */ - if ( ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0 + if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 ) { return 0; } break; - default: - /* TODO: add assertion */ + case SLAP_ACI_SCOPE_SUBTREE: + /* TODO: add assertion? */ return 0; } /* get the list of permissions clauses, bail if empty */ if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) { - /* TODO: add assertion */ + assert( 0 ); return 0; } @@ -383,13 +506,12 @@ aci_mask( /* see if we have a DN match */ if ( acl_get_part( aci, 3, '#', &type ) < 0 ) { - /* TODO: add assertion */ + assert( 0 ); return 0; } /* see if we have a public (i.e. anonymous) access */ - /* TODO: use ber_bvcmp */ - if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) { + if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) { return 1; } @@ -399,8 +521,7 @@ aci_mask( } /* see if we have a users access */ - /* TODO: use ber_bvcmp */ - if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) { + if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) { return 1; } @@ -417,95 +538,41 @@ aci_mask( sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" ); sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val ); - /* TODO: use ber_bvcmp */ - if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) { - struct berval ndn; - - /* TODO: don't normalize */ - 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; - - /* TODO: use ber_bvcmp */ - } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) { - struct berval ndn; - - /* TODO: don't normalize */ - 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; + /* 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; - /* TODO: use ber_bvcmp */ - } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) { - struct berval ndn, pndn; - - /* TODO: don't normalize */ - rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx ); - if ( rc != LDAP_SUCCESS ) { - return 0; - } - - dnParent( &ndn, &pndn ); + } else { + BER_BVZERO( &opts ); + } - if ( dn_match( &op->o_ndn, &pndn ) ) { - rc = 1; - } - slap_sl_free( ndn.bv_val, op->o_tmpmemctx ); + 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_SUBTREE ], &type ) == 0 ) { + return dnIsSuffix( &op->o_ndn, &sdn ); - /* TODO: use ber_bvcmp */ - } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) { - struct berval ndn; + } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) { + struct berval pdn; - /* TODO: don't normalize */ - rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx ); - if ( rc != LDAP_SUCCESS ) { - return 0; - } + dnParent( &sdn, &pdn ); - if ( !dn_match( &op->o_ndn, &ndn ) - && dnIsSuffix( &op->o_ndn, &ndn ) ) - { - rc = 1; - } - slap_sl_free( ndn.bv_val, op->o_tmpmemctx ); + return dn_match( &op->o_ndn, &pdn ); - return rc; + } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) { + return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) ); - /* TODO: use ber_bvcmp */ - } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) { - if ( dn_match( &op->o_ndn, &e->e_nname ) ) { - return 1; - } + } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) { + return dn_match( &op->o_ndn, &e->e_nname ); - /* TODO: use ber_bvcmp */ - } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) { + } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) { Attribute *at; AttributeDescription *ad = NULL; const char *text; rc = slap_bv2ad( &sdn, &ad, &text ); - if ( rc != LDAP_SUCCESS ) { - /* TODO: add assertion */ - return 0; - } + assert( rc == LDAP_SUCCESS ); rc = 0; for ( at = attrs_find( e->e_attrs, ad ); @@ -525,46 +592,140 @@ aci_mask( return rc; - /* TODO: use ber_bvcmp */ - } else if ( ber_bvstrcasecmp( &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 ) ) + } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) { + 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; } - /* TODO: use ber_bvcmp */ - } else if ( ber_bvstrcasecmp( &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 ) ) + } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) { + 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; } - /* TODO: use ber_bvcmp */ - } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) { - if ( acl_match_set( &sdn, op, e, 0 ) ) { + } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) { + if ( acl_match_set( &sdn, op, e, NULL ) ) { return 1; } - /* TODO: use ber_bvcmp */ - } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) { - if ( acl_match_set( &sdn, op, e, 1 ) ) { + } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) { + 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 + }; + + int rc; + + /* 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 */ + rc = register_at( aci_at.desc, aci_at.ad, 0 ); + if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ANY, + "aci_init: at_register failed\n", 0, 0, 0 ); + return rc; + } + + /* install flags */ + (*aci_at.ad)->ad_type->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; @@ -585,7 +746,7 @@ dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *r } } else { - ad = slap_schema.si_ad_aci; + ad = slap_ad_aci; } if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) { @@ -637,6 +798,11 @@ dynacl_aci_mask( 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); @@ -660,7 +826,7 @@ dynacl_aci_mask( } } - 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 ); } @@ -672,35 +838,41 @@ dynacl_aci_mask( 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 : @@ -777,10 +949,17 @@ static slap_dynacl_t dynacl_aci = { 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 */ @@ -830,16 +1009,17 @@ bv_get_tail( * aci is accepted in following form: * oid#scope#rights#type#subject * Where: - * oid := numeric OID - * scope := entry|children + * oid := numeric OID (currently ignored) + * scope := entry|children|subtree * rights := right[[$right]...] * right := (grant|deny);action - * action := perms;attr[[;perms;attr]...] + * action := perms;attrs[[;perms;attrs]...] * perms := perm[[,perm]...] * perm := c|s|r|w|x - * attr := attributeType|[all] - * type := public|users|self|dnattr|group|role|set|set-ref| - * access_id|subtree|onelevel|children + * attrs := attribute[[,attribute]..]|[all] + * attribute := attributeType|attributeType=attributeValue|attributeType=attributeValuePrefix* + * type := public|users|self|dnattr|group|role|set|set-ref| + * access_id|subtree|onelevel|children */ static int OpenLDAPaciValidatePerms( @@ -849,14 +1029,16 @@ OpenLDAPaciValidatePerms( 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: + Debug( LDAP_DEBUG_ACL, "aciValidatePerms: perms needs to be one of x,d,c,s,r,w in '%s'\n", perms->bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } @@ -870,6 +1052,7 @@ OpenLDAPaciValidatePerms( assert( i != perms->bv_len ); if ( perms->bv_val[ i ] != ',' ) { + Debug( LDAP_DEBUG_ACL, "aciValidatePerms: missing comma in '%s'\n", perms->bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } @@ -898,6 +1081,7 @@ OpenLDAPaciValidateRight( if ( acl_get_part( action, 0, ';', &bv ) < 0 || bv_getcaseidx( &bv, ACIgrantdeny ) == -1 ) { + Debug( LDAP_DEBUG_ACL, "aciValidateRight: '%s' must be either 'grant' or 'deny'\n", bv.bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } @@ -911,16 +1095,36 @@ OpenLDAPaciValidateRight( } else { /* attr */ - AttributeDescription *ad = NULL; - const char *text = NULL; + AttributeDescription *ad; + const char *text; + struct berval attr, left, right; + int j; /* could be "[all]" or an attribute description */ if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) { continue; } - if ( slap_bv2ad( &bv, &ad, &text ) != LDAP_SUCCESS ) { - return LDAP_INVALID_SYNTAX; + + for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ ) + { + ad = NULL; + text = NULL; + if ( acl_get_part( &attr, 0, '=', &left ) < 0 + || acl_get_part( &attr, 1, '=', &right ) < 0 ) + { + if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) + { + Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", attr.bv_val, 0, 0 ); + return LDAP_INVALID_SYNTAX; + } + } else { + if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS ) + { + Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", left.bv_val, 0, 0 ); + return LDAP_INVALID_SYNTAX; + } + } } } } @@ -930,6 +1134,7 @@ OpenLDAPaciValidateRight( return LDAP_SUCCESS; } else { + Debug( LDAP_DEBUG_ACL, "aciValidateRight: perms:attr need to be pairs in '%s'\n", action->bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } @@ -950,16 +1155,20 @@ OpenLDAPaciNormalizeRight( /* grant|deny */ if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) { + Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: missing ';' in '%s'\n", action->bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } idx = bv_getcaseidx( &grantdeny, ACIgrantdeny ); if ( idx == -1 ) { + Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: '%s' must be grant or deny\n", grantdeny.bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx ); for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) { + struct berval nattrs = BER_BVNULL; + int freenattrs = 1; if ( i & 1 ) { /* perms */ if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS ) @@ -974,25 +1183,76 @@ OpenLDAPaciNormalizeRight( /* could be "[all]" or an attribute description */ if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) { - bv = aci_bv[ ACI_BV_BR_ALL ]; + nattrs = aci_bv[ ACI_BV_BR_ALL ]; + freenattrs = 0; } else { AttributeDescription *ad = NULL; + AttributeDescription adstatic= { 0 }; const char *text = NULL; - int rc; + struct berval attr, left, right; + int j; + int len; - rc = slap_bv2ad( &bv, &ad, &text ); - if ( rc != LDAP_SUCCESS ) { - return LDAP_INVALID_SYNTAX; + for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ ) + { + ad = NULL; + text = NULL; + /* openldap 2.1 aci compabitibility [entry] -> entry */ + if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ENTRY ] ) == 0 ) { + ad = &adstatic; + adstatic.ad_cname = aci_bv[ ACI_BV_ENTRY ]; + + /* openldap 2.1 aci compabitibility [children] -> children */ + } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_CHILDREN ] ) == 0 ) { + ad = &adstatic; + adstatic.ad_cname = aci_bv[ ACI_BV_CHILDREN ]; + + /* openldap 2.1 aci compabitibility [all] -> only [all] */ + } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) { + ber_memfree_x( nattrs.bv_val, ctx ); + nattrs = aci_bv[ ACI_BV_BR_ALL ]; + freenattrs = 0; + break; + + } else if ( acl_get_part( &attr, 0, '=', &left ) < 0 + || acl_get_part( &attr, 1, '=', &right ) < 0 ) + { + if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) + { + ber_memfree_x( nattrs.bv_val, ctx ); + Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", attr.bv_val, 0, 0 ); + return LDAP_INVALID_SYNTAX; + } + + } else { + if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS ) + { + ber_memfree_x( nattrs.bv_val, ctx ); + Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", left.bv_val, 0, 0 ); + return LDAP_INVALID_SYNTAX; + } + } + + + len = nattrs.bv_len + ( !BER_BVISEMPTY( &nattrs ) ? STRLENOF( "," ) : 0 ) + + ad->ad_cname.bv_len; + nattrs.bv_val = ber_memrealloc_x( nattrs.bv_val, len + 1, ctx ); + ptr = &nattrs.bv_val[ nattrs.bv_len ]; + if ( !BER_BVISEMPTY( &nattrs ) ) { + *ptr++ = ','; + } + ptr = lutil_strncopy( ptr, ad->ad_cname.bv_val, ad->ad_cname.bv_len ); + ptr[ 0 ] = '\0'; + nattrs.bv_len = len; } - bv = ad->ad_cname; } naction->bv_val = ber_memrealloc_x( naction->bv_val, naction->bv_len + STRLENOF( ";" ) + perms.bv_len + STRLENOF( ";" ) - + bv.bv_len + 1, + + nattrs.bv_len + 1, ctx ); ptr = &naction->bv_val[ naction->bv_len ]; @@ -1001,10 +1261,13 @@ OpenLDAPaciNormalizeRight( ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len ); ptr[ 0 ] = ';'; ptr++; - ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len ); + ptr = lutil_strncopy( ptr, nattrs.bv_val, nattrs.bv_len ); ptr[ 0 ] = '\0'; naction->bv_len += STRLENOF( ";" ) + perms.bv_len - + STRLENOF( ";" ) + bv.bv_len; + + STRLENOF( ";" ) + nattrs.bv_len; + if ( freenattrs ) { + ber_memfree_x( nattrs.bv_val, ctx ); + } } } @@ -1013,6 +1276,7 @@ OpenLDAPaciNormalizeRight( return LDAP_SUCCESS; } else { + Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: perms:attr need to be pairs in '%s'\n", action->bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } } @@ -1079,6 +1343,7 @@ OpenLDAPaciNormalizeRights( static const struct berval *OpenLDAPaciscopes[] = { &aci_bv[ ACI_BV_ENTRY ], &aci_bv[ ACI_BV_CHILDREN ], + &aci_bv[ ACI_BV_SUBTREE ], NULL }; @@ -1110,7 +1375,7 @@ static const struct berval *OpenLDAPacitypes[] = { NULL }; -int +static int OpenLDAPaciValidate( Syntax *syntax, struct berval *val ) @@ -1121,8 +1386,10 @@ OpenLDAPaciValidate( type = BER_BVNULL, subject = BER_BVNULL; int idx; - + int rc; + if ( BER_BVISEMPTY( val ) ) { + Debug( LDAP_DEBUG_ACL, "aciValidatet: value is empty\n", 0, 0, 0 ); return LDAP_INVALID_SYNTAX; } @@ -1134,6 +1401,7 @@ OpenLDAPaciValidate( * I'd replace it with X-ORDERED VALUES so that * it's guaranteed values are maintained and used * in the desired order */ + Debug( LDAP_DEBUG_ACL, "aciValidate: invalid oid '%s'\n", oid.bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } @@ -1141,6 +1409,7 @@ OpenLDAPaciValidate( if ( acl_get_part( val, 1, '#', &scope ) < 0 || bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 ) { + Debug( LDAP_DEBUG_ACL, "aciValidate: invalid scope '%s'\n", scope.bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } @@ -1153,6 +1422,7 @@ OpenLDAPaciValidate( /* type */ if ( acl_get_part( val, 3, '#', &type ) < 0 ) { + Debug( LDAP_DEBUG_ACL, "aciValidate: missing type in '%s'\n", val->bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } idx = bv_getcaseidx( &type, OpenLDAPacitypes ); @@ -1160,11 +1430,13 @@ OpenLDAPaciValidate( struct berval isgr; if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) { + Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", type.bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } idx = bv_getcaseidx( &isgr, OpenLDAPacitypes ); if ( idx == -1 || idx >= LAST_OPTIONAL ) { + Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", isgr.bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } } @@ -1172,6 +1444,7 @@ OpenLDAPaciValidate( /* subject */ bv_get_tail( val, &type, &subject ); if ( subject.bv_val[ 0 ] != '#' ) { + Debug( LDAP_DEBUG_ACL, "aciValidate: missing subject in '%s'\n", val->bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } @@ -1179,15 +1452,16 @@ OpenLDAPaciValidate( if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) { AttributeDescription *ad = NULL; const char *text = NULL; - int rc; rc = slap_bv2ad( &subject, &ad, &text ); if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ACL, "aciValidate: unknown dn attribute '%s'\n", subject.bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) { /* FIXME: allow nameAndOptionalUID? */ + Debug( LDAP_DEBUG_ACL, "aciValidate: wrong syntax for dn attribute '%s'\n", subject.bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } } @@ -1202,11 +1476,13 @@ OpenLDAPaciValidate( 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; @@ -1219,15 +1495,13 @@ OpenLDAPaciValidate( rc = slap_bv2ad( &atbv, &ad, &text ); if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group attribute '%s'\n", atbv.bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } - - } else { - ocbv.bv_len = type.bv_len - - ( ocbv.bv_val - type.bv_val ); } if ( oc_bvfind( &ocbv ) == NULL ) { + Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group '%s'\n", ocbv.bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } } @@ -1235,6 +1509,7 @@ OpenLDAPaciValidate( if ( BER_BVISEMPTY( &subject ) ) { /* empty DN invalid */ + Debug( LDAP_DEBUG_ACL, "aciValidate: missing dn in '%s'\n", val->bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } @@ -1242,7 +1517,11 @@ OpenLDAPaciValidate( subject.bv_len--; /* FIXME: pass DN syntax? */ - return dnValidate( NULL, &subject ); + rc = dnValidate( NULL, &subject ); + if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ACL, "aciValidate: invalid dn '%s'\n", subject.bv_val, 0, 0 ); + } + return rc; } static int @@ -1261,12 +1540,13 @@ OpenLDAPaciPrettyNormal( subject = BER_BVNULL, nsubject = BER_BVNULL; int idx, - rc, + rc = LDAP_SUCCESS, freesubject = 0, freetype = 0; char *ptr; if ( BER_BVISEMPTY( val ) ) { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: value is empty\n", 0, 0, 0 ); return LDAP_INVALID_SYNTAX; } @@ -1274,21 +1554,25 @@ OpenLDAPaciPrettyNormal( if ( acl_get_part( val, 0, '#', &oid ) < 0 || numericoidValidate( NULL, &oid ) != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid oid '%s'\n", oid.bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } /* scope: normalize by replacing with OpenLDAPaciscopes */ if ( acl_get_part( val, 1, '#', &scope ) < 0 ) { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing scope in '%s'\n", val->bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } idx = bv_getcaseidx( &scope, OpenLDAPaciscopes ); if ( idx == -1 ) { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid scope '%s'\n", scope.bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } scope = *OpenLDAPaciscopes[ idx ]; /* rights */ if ( acl_get_part( val, 2, '#', &rights ) < 0 ) { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing rights in '%s'\n", val->bv_val, 0, 0 ); return LDAP_INVALID_SYNTAX; } if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx ) @@ -1299,6 +1583,7 @@ OpenLDAPaciPrettyNormal( /* type */ if ( acl_get_part( val, 3, '#', &type ) < 0 ) { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing type in '%s'\n", val->bv_val, 0, 0 ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } @@ -1307,12 +1592,14 @@ OpenLDAPaciPrettyNormal( struct berval isgr; if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", type.bv_val, 0, 0 ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } idx = bv_getcaseidx( &isgr, OpenLDAPacitypes ); if ( idx == -1 || idx >= LAST_OPTIONAL ) { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", isgr.bv_val, 0, 0 ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } @@ -1323,6 +1610,7 @@ OpenLDAPaciPrettyNormal( bv_get_tail( val, &type, &subject ); if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing subject in '%s'\n", val->bv_val, 0, 0 ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } @@ -1343,6 +1631,7 @@ OpenLDAPaciPrettyNormal( freesubject = 1; } else { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid subject dn '%s'\n", subject.bv_val, 0, 0 ); goto cleanup; } @@ -1353,7 +1642,7 @@ OpenLDAPaciPrettyNormal( 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; @@ -1364,8 +1653,9 @@ OpenLDAPaciPrettyNormal( 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 @@ -1374,18 +1664,17 @@ OpenLDAPaciPrettyNormal( rc = slap_bv2ad( &atbv, &ad, &text ); if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown group attribute '%s'\n", atbv.bv_val, 0, 0 ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } 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 ) { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid group '%s'\n", ocbv.bv_val, 0, 0 ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } @@ -1421,12 +1710,14 @@ OpenLDAPaciPrettyNormal( rc = slap_bv2ad( &subject, &ad, &text ); if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown dn attribute '%s'\n", subject.bv_val, 0, 0 ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) { /* FIXME: allow nameAndOptionalUID? */ + Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: wrong syntax for dn attribute '%s'\n", subject.bv_val, 0, 0 ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } @@ -1438,7 +1729,7 @@ OpenLDAPaciPrettyNormal( out->bv_len = oid.bv_len + STRLENOF( "#" ) + scope.bv_len + STRLENOF( "#" ) - + rights.bv_len + STRLENOF( "#" ) + + nrights.bv_len + STRLENOF( "#" ) + ntype.bv_len + STRLENOF( "#" ) + nsubject.bv_len; @@ -1476,7 +1767,7 @@ cleanup:; return rc; } -int +static int OpenLDAPaciPretty( Syntax *syntax, struct berval *val, @@ -1486,7 +1777,7 @@ OpenLDAPaciPretty( return OpenLDAPaciPrettyNormal( val, out, ctx, 0 ); } -int +static int OpenLDAPaciNormalize( slap_mask_t use, Syntax *syntax, @@ -1498,5 +1789,17 @@ OpenLDAPaciNormalize( 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 */