mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-27 03:20:22 +08:00
1433 lines
32 KiB
C
1433 lines
32 KiB
C
/* aci.c - routines to parse and check acl's */
|
|
/* $OpenLDAP$ */
|
|
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
|
*
|
|
* Copyright 1998-2005 The OpenLDAP Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted only as authorized by the OpenLDAP
|
|
* Public License.
|
|
*
|
|
* A copy of this license is available in the file LICENSE in the
|
|
* top-level directory of the distribution or, alternatively, at
|
|
* <http://www.OpenLDAP.org/license.html>.
|
|
*/
|
|
/* Portions Copyright (c) 1995 Regents of the University of Michigan.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms are permitted
|
|
* provided that this notice is preserved and that due credit is given
|
|
* to the University of Michigan at Ann Arbor. The name of the University
|
|
* may not be used to endorse or promote products derived from this
|
|
* software without specific prior written permission. This software
|
|
* is provided ``as is'' without express or implied warranty.
|
|
*/
|
|
|
|
#include "portable.h"
|
|
|
|
#ifdef SLAPD_ACI_ENABLED
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ac/ctype.h>
|
|
#include <ac/regex.h>
|
|
#include <ac/socket.h>
|
|
#include <ac/string.h>
|
|
#include <ac/unistd.h>
|
|
|
|
#include "slap.h"
|
|
#include "lber_pvt.h"
|
|
#include "lutil.h"
|
|
|
|
#define ACI_BUF_SIZE 1024 /* use most appropriate size */
|
|
|
|
static int
|
|
aci_list_map_rights(
|
|
struct berval *list )
|
|
{
|
|
struct berval bv;
|
|
slap_access_t mask;
|
|
int i;
|
|
|
|
ACL_INIT( mask );
|
|
for ( i = 0; acl_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; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
|
|
if ( acl_get_part(&bv, 0, '=', &left ) < 0
|
|
|| acl_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 ) {
|
|
/* FIXME: this is also totally undocumented! */
|
|
/* 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 ( acl_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; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
|
|
if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
if ( acl_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 || BER_BVISEMPTY( attr )
|
|
|| ber_bvstrcasecmp( attr, &aci_bv[ ACI_BV_ENTRY ] ) == 0 )
|
|
{
|
|
attr = &aci_bv[ ACI_BV_BR_ENTRY ];
|
|
}
|
|
|
|
found = 0;
|
|
ACL_INIT(*grant);
|
|
ACL_INIT(*deny);
|
|
/* loop through each permissions clause */
|
|
for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
|
|
if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
|
|
continue;
|
|
}
|
|
|
|
if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
|
|
mask = grant;
|
|
|
|
} else if ( ber_bvstrcasecmp( &aci_bv[ 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[ ACI_BV_BR_ALL ], NULL );
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
static int
|
|
aci_group_member (
|
|
struct berval *subj,
|
|
const struct berval *defgrpoc,
|
|
const 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 ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
|
|
return 0;
|
|
}
|
|
|
|
if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
|
|
grpoc = *defgrpoc;
|
|
}
|
|
|
|
if ( acl_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[ ACI_BUF_SIZE ];
|
|
struct berval bv, ndn;
|
|
|
|
bv.bv_len = sizeof( buf ) - 1;
|
|
bv.bv_val = (char *)&buf;
|
|
if ( acl_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;
|
|
}
|
|
|
|
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 ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
|
|
return 0;
|
|
}
|
|
|
|
/* check that the aci family is supported */
|
|
/* FIXME: the OID is ignored? */
|
|
if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
|
|
return 0;
|
|
}
|
|
|
|
/* check that the scope matches */
|
|
if ( acl_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_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
|
|
&& ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
|
|
{
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case SLAP_ACI_SCOPE_CHILDREN:
|
|
if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
|
|
&& ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
|
|
{
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
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 ) {
|
|
assert( 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 ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
|
|
assert( 0 );
|
|
return 0;
|
|
}
|
|
|
|
/* see if we have a public (i.e. anonymous) access */
|
|
if ( ber_bvcmp( &aci_bv[ 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_bvcmp( &aci_bv[ 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 ( acl_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_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
|
|
return dn_match( &op->o_ndn, &sdn );
|
|
|
|
} else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
|
|
return dnIsSuffix( &op->o_ndn, &sdn );
|
|
|
|
} else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
|
|
struct berval pdn;
|
|
|
|
dnParent( &sdn, &pdn );
|
|
|
|
return dn_match( &op->o_ndn, &pdn );
|
|
|
|
} else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
|
|
return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
|
|
|
|
} else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
|
|
return dn_match( &op->o_ndn, &e->e_nname );
|
|
|
|
} 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 );
|
|
assert( rc == LDAP_SUCCESS );
|
|
|
|
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_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 ) )
|
|
{
|
|
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 ) )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
} else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
|
|
if ( acl_match_set( &sdn, op, e, 0 ) ) {
|
|
return 1;
|
|
}
|
|
|
|
} else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
|
|
if ( acl_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 != NULL );
|
|
|
|
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
|
|
};
|
|
|
|
int
|
|
dynacl_aci_init( void )
|
|
{
|
|
return slap_dynacl_register( &dynacl_aci );
|
|
}
|
|
|
|
#endif /* SLAP_DYNACL */
|
|
|
|
/* ACI syntax validation */
|
|
|
|
/*
|
|
* Matches given berval to array of bervals
|
|
* Returns:
|
|
* >=0 if one if the array elements equals to this berval
|
|
* -1 if string was not found in array
|
|
*/
|
|
static int
|
|
bv_getcaseidx(
|
|
struct berval *bv,
|
|
const struct berval *arr[] )
|
|
{
|
|
int i;
|
|
|
|
if ( BER_BVISEMPTY( bv ) ) {
|
|
return -1;
|
|
}
|
|
|
|
for ( i = 0; arr[ i ] != NULL ; i++ ) {
|
|
if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* Returns what have left in input berval after current sub */
|
|
static void
|
|
bv_get_tail(
|
|
struct berval *val,
|
|
struct berval *sub,
|
|
struct berval *tail )
|
|
{
|
|
int head_len;
|
|
|
|
tail->bv_val = sub->bv_val + sub->bv_len;
|
|
head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
|
|
tail->bv_len = val->bv_len - head_len;
|
|
}
|
|
|
|
|
|
/*
|
|
* aci is accepted in following form:
|
|
* oid#scope#rights#type#subject
|
|
* Where:
|
|
* oid := numeric OID
|
|
* scope := entry|children
|
|
* rights := right[[$right]...]
|
|
* right := (grant|deny);action
|
|
* action := perms;attr[[;perms;attr]...]
|
|
* 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
|
|
*/
|
|
static int
|
|
OpenLDAPaciValidatePerms(
|
|
struct berval *perms )
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0; i < perms->bv_len; ) {
|
|
switch ( perms->bv_val[ i ] ) {
|
|
case 'c':
|
|
case 's':
|
|
case 'r':
|
|
case 'w':
|
|
case 'x':
|
|
break;
|
|
|
|
default:
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
if ( ++i == perms->bv_len ) {
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
|
|
i++;
|
|
|
|
assert( i != perms->bv_len );
|
|
|
|
if ( perms->bv_val[ i ] != ',' ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
do {
|
|
i++;
|
|
} while ( perms->bv_val[ i ] == ' ' );
|
|
}
|
|
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
static const struct berval *ACIgrantdeny[] = {
|
|
&aci_bv[ ACI_BV_GRANT ],
|
|
&aci_bv[ ACI_BV_DENY ],
|
|
NULL
|
|
};
|
|
|
|
static int
|
|
OpenLDAPaciValidateRight(
|
|
struct berval *action )
|
|
{
|
|
struct berval bv = BER_BVNULL;
|
|
int i;
|
|
|
|
/* grant|deny */
|
|
if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
|
|
bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
|
|
{
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
|
|
if ( i & 1 ) {
|
|
/* perms */
|
|
if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
|
|
{
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
} else {
|
|
/* attr */
|
|
AttributeDescription *ad = NULL;
|
|
const char *text = NULL;
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* "perms;attr" go in pairs */
|
|
if ( i > 0 && ( i & 1 ) == 0 ) {
|
|
return LDAP_SUCCESS;
|
|
|
|
} else {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
static int
|
|
OpenLDAPaciNormalizeRight(
|
|
struct berval *action,
|
|
struct berval *naction,
|
|
void *ctx )
|
|
{
|
|
struct berval grantdeny,
|
|
perms = BER_BVNULL,
|
|
bv = BER_BVNULL;
|
|
int idx,
|
|
i;
|
|
|
|
/* grant|deny */
|
|
if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
|
|
if ( idx == -1 ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
|
|
|
|
for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
|
|
if ( i & 1 ) {
|
|
/* perms */
|
|
if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
|
|
{
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
perms = bv;
|
|
|
|
} else {
|
|
/* attr */
|
|
char *ptr;
|
|
|
|
/* 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 ];
|
|
|
|
} else {
|
|
AttributeDescription *ad = NULL;
|
|
const char *text = NULL;
|
|
int rc;
|
|
|
|
rc = slap_bv2ad( &bv, &ad, &text );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
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,
|
|
ctx );
|
|
|
|
ptr = &naction->bv_val[ naction->bv_len ];
|
|
ptr[ 0 ] = ';';
|
|
ptr++;
|
|
ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
|
|
ptr[ 0 ] = ';';
|
|
ptr++;
|
|
ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
|
|
ptr[ 0 ] = '\0';
|
|
naction->bv_len += STRLENOF( ";" ) + perms.bv_len
|
|
+ STRLENOF( ";" ) + bv.bv_len;
|
|
}
|
|
}
|
|
|
|
/* perms;attr go in pairs */
|
|
if ( i > 1 && ( i & 1 ) ) {
|
|
return LDAP_SUCCESS;
|
|
|
|
} else {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
}
|
|
|
|
static int
|
|
OpenLDAPaciValidateRights(
|
|
struct berval *actions )
|
|
|
|
{
|
|
struct berval bv = BER_BVNULL;
|
|
int i;
|
|
|
|
for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
|
|
if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
}
|
|
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
static int
|
|
OpenLDAPaciNormalizeRights(
|
|
struct berval *actions,
|
|
struct berval *nactions,
|
|
void *ctx )
|
|
|
|
{
|
|
struct berval bv = BER_BVNULL;
|
|
int i;
|
|
|
|
BER_BVZERO( nactions );
|
|
for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
|
|
int rc;
|
|
struct berval nbv;
|
|
|
|
rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
ber_memfree_x( nactions->bv_val, ctx );
|
|
BER_BVZERO( nactions );
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
if ( i == 0 ) {
|
|
*nactions = nbv;
|
|
|
|
} else {
|
|
nactions->bv_val = ber_memrealloc_x( nactions->bv_val,
|
|
nactions->bv_len + STRLENOF( "$" )
|
|
+ nbv.bv_len + 1,
|
|
ctx );
|
|
nactions->bv_val[ nactions->bv_len ] = '$';
|
|
AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
|
|
nbv.bv_val, nbv.bv_len + 1 );
|
|
ber_memfree_x( nbv.bv_val, ctx );
|
|
nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
|
|
}
|
|
BER_BVZERO( &nbv );
|
|
}
|
|
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
static const struct berval *OpenLDAPaciscopes[] = {
|
|
&aci_bv[ ACI_BV_ENTRY ],
|
|
&aci_bv[ ACI_BV_CHILDREN ],
|
|
&aci_bv[ ACI_BV_SUBTREE ],
|
|
|
|
NULL
|
|
};
|
|
|
|
static const struct berval *OpenLDAPacitypes[] = {
|
|
/* DN-valued */
|
|
&aci_bv[ ACI_BV_GROUP ],
|
|
&aci_bv[ ACI_BV_ROLE ],
|
|
|
|
/* set to one past the last DN-valued type with options (/) */
|
|
#define LAST_OPTIONAL 2
|
|
|
|
&aci_bv[ ACI_BV_ACCESS_ID ],
|
|
&aci_bv[ ACI_BV_SUBTREE ],
|
|
&aci_bv[ ACI_BV_ONELEVEL ],
|
|
&aci_bv[ ACI_BV_CHILDREN ],
|
|
|
|
/* set to one past the last DN-valued type */
|
|
#define LAST_DNVALUED 6
|
|
|
|
/* non DN-valued */
|
|
&aci_bv[ ACI_BV_DNATTR ],
|
|
&aci_bv[ ACI_BV_PUBLIC ],
|
|
&aci_bv[ ACI_BV_USERS ],
|
|
&aci_bv[ ACI_BV_SELF ],
|
|
&aci_bv[ ACI_BV_SET ],
|
|
&aci_bv[ ACI_BV_SET_REF ],
|
|
|
|
NULL
|
|
};
|
|
|
|
int
|
|
OpenLDAPaciValidate(
|
|
Syntax *syntax,
|
|
struct berval *val )
|
|
{
|
|
struct berval oid = BER_BVNULL,
|
|
scope = BER_BVNULL,
|
|
rights = BER_BVNULL,
|
|
type = BER_BVNULL,
|
|
subject = BER_BVNULL;
|
|
int idx;
|
|
|
|
if ( BER_BVISEMPTY( val ) ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
/* oid */
|
|
if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
|
|
numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
|
|
{
|
|
/* NOTE: the numericoidValidate() is rather pedantic;
|
|
* I'd replace it with X-ORDERED VALUES so that
|
|
* it's guaranteed values are maintained and used
|
|
* in the desired order */
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
/* scope */
|
|
if ( acl_get_part( val, 1, '#', &scope ) < 0 ||
|
|
bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
|
|
{
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
/* rights */
|
|
if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
|
|
OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS )
|
|
{
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
/* type */
|
|
if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
idx = bv_getcaseidx( &type, OpenLDAPacitypes );
|
|
if ( idx == -1 ) {
|
|
struct berval isgr;
|
|
|
|
if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
|
|
if ( idx == -1 || idx >= LAST_OPTIONAL ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
}
|
|
|
|
/* subject */
|
|
bv_get_tail( val, &type, &subject );
|
|
if ( subject.bv_val[ 0 ] != '#' ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
if ( idx >= LAST_DNVALUED ) {
|
|
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 ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
|
|
/* FIXME: allow nameAndOptionalUID? */
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
}
|
|
|
|
/* not a DN */
|
|
return LDAP_SUCCESS;
|
|
|
|
} else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
|
|
|| OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
|
|
{
|
|
/* do {group|role}/oc/at check */
|
|
struct berval ocbv = BER_BVNULL,
|
|
atbv = BER_BVNULL;
|
|
|
|
ocbv.bv_val = strchr( type.bv_val, '/' );
|
|
if ( ocbv.bv_val != NULL ) {
|
|
ocbv.bv_val++;
|
|
|
|
atbv.bv_val = strchr( ocbv.bv_val, '/' );
|
|
if ( atbv.bv_val != NULL ) {
|
|
AttributeDescription *ad = NULL;
|
|
const char *text = NULL;
|
|
int rc;
|
|
|
|
atbv.bv_val++;
|
|
atbv.bv_len = type.bv_len
|
|
- ( atbv.bv_val - type.bv_val );
|
|
ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
|
|
|
|
rc = slap_bv2ad( &atbv, &ad, &text );
|
|
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 ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( BER_BVISEMPTY( &subject ) ) {
|
|
/* empty DN invalid */
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
subject.bv_val++;
|
|
subject.bv_len--;
|
|
|
|
/* FIXME: pass DN syntax? */
|
|
return dnValidate( NULL, &subject );
|
|
}
|
|
|
|
static int
|
|
OpenLDAPaciPrettyNormal(
|
|
struct berval *val,
|
|
struct berval *out,
|
|
void *ctx,
|
|
int normalize )
|
|
{
|
|
struct berval oid = BER_BVNULL,
|
|
scope = BER_BVNULL,
|
|
rights = BER_BVNULL,
|
|
nrights = BER_BVNULL,
|
|
type = BER_BVNULL,
|
|
ntype = BER_BVNULL,
|
|
subject = BER_BVNULL,
|
|
nsubject = BER_BVNULL;
|
|
int idx,
|
|
rc,
|
|
freesubject = 0,
|
|
freetype = 0;
|
|
char *ptr;
|
|
|
|
if ( BER_BVISEMPTY( val ) ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
/* oid: if valid, it's already normalized */
|
|
if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
|
|
numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
|
|
{
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
/* scope: normalize by replacing with OpenLDAPaciscopes */
|
|
if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
|
|
if ( idx == -1 ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
scope = *OpenLDAPaciscopes[ idx ];
|
|
|
|
/* rights */
|
|
if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
|
|
!= LDAP_SUCCESS )
|
|
{
|
|
return LDAP_INVALID_SYNTAX;
|
|
}
|
|
|
|
/* type */
|
|
if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
|
|
rc = LDAP_INVALID_SYNTAX;
|
|
goto cleanup;
|
|
}
|
|
idx = bv_getcaseidx( &type, OpenLDAPacitypes );
|
|
if ( idx == -1 ) {
|
|
struct berval isgr;
|
|
|
|
if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
|
|
rc = LDAP_INVALID_SYNTAX;
|
|
goto cleanup;
|
|
}
|
|
|
|
idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
|
|
if ( idx == -1 || idx >= LAST_OPTIONAL ) {
|
|
rc = LDAP_INVALID_SYNTAX;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
ntype = *OpenLDAPacitypes[ idx ];
|
|
|
|
/* subject */
|
|
bv_get_tail( val, &type, &subject );
|
|
|
|
if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
|
|
rc = LDAP_INVALID_SYNTAX;
|
|
goto cleanup;
|
|
}
|
|
|
|
subject.bv_val++;
|
|
subject.bv_len--;
|
|
|
|
if ( idx < LAST_DNVALUED ) {
|
|
/* FIXME: pass DN syntax? */
|
|
if ( normalize ) {
|
|
rc = dnNormalize( 0, NULL, NULL,
|
|
&subject, &nsubject, ctx );
|
|
} else {
|
|
rc = dnPretty( NULL, &subject, &nsubject, ctx );
|
|
}
|
|
|
|
if ( rc == LDAP_SUCCESS ) {
|
|
freesubject = 1;
|
|
|
|
} else {
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
|
|
|| OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
|
|
{
|
|
/* do {group|role}/oc/at check */
|
|
struct berval ocbv = BER_BVNULL,
|
|
atbv = BER_BVNULL;
|
|
|
|
ocbv.bv_val = strchr( type.bv_val, '/' );
|
|
if ( ocbv.bv_val != NULL ) {
|
|
ObjectClass *oc = NULL;
|
|
AttributeDescription *ad = NULL;
|
|
const char *text = NULL;
|
|
int rc;
|
|
struct berval bv;
|
|
|
|
bv.bv_len = ntype.bv_len;
|
|
|
|
ocbv.bv_val++;
|
|
|
|
atbv.bv_val = strchr( ocbv.bv_val, '/' );
|
|
if ( atbv.bv_val != NULL ) {
|
|
atbv.bv_val++;
|
|
atbv.bv_len = type.bv_len
|
|
- ( atbv.bv_val - type.bv_val );
|
|
ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
|
|
|
|
rc = slap_bv2ad( &atbv, &ad, &text );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
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 ) {
|
|
rc = LDAP_INVALID_SYNTAX;
|
|
goto cleanup;
|
|
}
|
|
|
|
bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
|
|
bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx );
|
|
|
|
ptr = bv.bv_val;
|
|
ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
|
|
ptr[ 0 ] = '/';
|
|
ptr++;
|
|
ptr = lutil_strncopy( ptr,
|
|
oc->soc_cname.bv_val,
|
|
oc->soc_cname.bv_len );
|
|
if ( ad != NULL ) {
|
|
ptr[ 0 ] = '/';
|
|
ptr++;
|
|
ptr = lutil_strncopy( ptr,
|
|
ad->ad_cname.bv_val,
|
|
ad->ad_cname.bv_len );
|
|
}
|
|
ptr[ 0 ] = '\0';
|
|
|
|
ntype = bv;
|
|
freetype = 1;
|
|
}
|
|
}
|
|
|
|
} else 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 ) {
|
|
rc = LDAP_INVALID_SYNTAX;
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
|
|
/* FIXME: allow nameAndOptionalUID? */
|
|
rc = LDAP_INVALID_SYNTAX;
|
|
goto cleanup;
|
|
}
|
|
|
|
nsubject = ad->ad_cname;
|
|
}
|
|
|
|
|
|
out->bv_len =
|
|
oid.bv_len + STRLENOF( "#" )
|
|
+ scope.bv_len + STRLENOF( "#" )
|
|
+ rights.bv_len + STRLENOF( "#" )
|
|
+ ntype.bv_len + STRLENOF( "#" )
|
|
+ nsubject.bv_len;
|
|
|
|
out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx );
|
|
ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
|
|
ptr[ 0 ] = '#';
|
|
ptr++;
|
|
ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
|
|
ptr[ 0 ] = '#';
|
|
ptr++;
|
|
ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
|
|
ptr[ 0 ] = '#';
|
|
ptr++;
|
|
ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
|
|
ptr[ 0 ] = '#';
|
|
ptr++;
|
|
if ( !BER_BVISNULL( &nsubject ) ) {
|
|
ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
|
|
}
|
|
ptr[ 0 ] = '\0';
|
|
|
|
cleanup:;
|
|
if ( freesubject ) {
|
|
ber_memfree_x( nsubject.bv_val, ctx );
|
|
}
|
|
|
|
if ( freetype ) {
|
|
ber_memfree_x( ntype.bv_val, ctx );
|
|
}
|
|
|
|
if ( !BER_BVISNULL( &nrights ) ) {
|
|
ber_memfree_x( nrights.bv_val, ctx );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
OpenLDAPaciPretty(
|
|
Syntax *syntax,
|
|
struct berval *val,
|
|
struct berval *out,
|
|
void *ctx )
|
|
{
|
|
return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
|
|
}
|
|
|
|
int
|
|
OpenLDAPaciNormalize(
|
|
slap_mask_t use,
|
|
Syntax *syntax,
|
|
MatchingRule *mr,
|
|
struct berval *val,
|
|
struct berval *out,
|
|
void *ctx )
|
|
{
|
|
return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
|
|
}
|
|
|
|
#endif /* SLAPD_ACI_ENABLED */
|
|
|