Skip to content
Snippets Groups Projects
acl.c 61.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    /* acl.c - routines to parse and check acl's */
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
     *
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
     * Copyright 1998-2012 The OpenLDAP Foundation.
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
     * 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.
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
     */
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    #include "portable.h"
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    #include <stdio.h>
    
    
    #include <ac/regex.h>
    #include <ac/socket.h>
    #include <ac/string.h>
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    #include "slap.h"
    
    #include "lutil.h"
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    #define ACL_BUF_SIZE 	1024	/* use most appropriate size */
    
    
    static const struct berval	acl_bv_ip_eq = BER_BVC( "IP=" );
    
    #ifdef LDAP_PF_INET6
    static const struct berval	acl_bv_ipv6_eq = BER_BVC( "IP=[" );
    #endif /* LDAP_PF_INET6 */
    
    #ifdef LDAP_PF_LOCAL
    
    static const struct berval	acl_bv_path_eq = BER_BVC("PATH=");
    
    #endif /* LDAP_PF_LOCAL */
    
    static AccessControl * slap_acl_get(
    
    	AccessControl *ac, int *count,
    
    	AttributeDescription *desc,
    
    	struct berval *val,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	AclRegexMatches *matches,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	slap_mask_t *mask,
    
    Howard Chu's avatar
    Howard Chu committed
    	AccessControlState *state );
    
    static slap_control_t slap_acl_mask(
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	AccessControl *ac,
    	AccessControl *prev,
    	slap_mask_t *mask,
    
    	AttributeDescription *desc,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	AclRegexMatches *matches,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	AccessControlState *state,
    	slap_access_t access );
    
    static int	regex_matches(
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	struct berval *pat, char *str,
    	struct berval *dn_matches, struct berval *val_matches,
    	AclRegexMatches *matches);
    
    typedef	struct AclSetCookie {
    
    	SetCookie	asc_cookie;
    #define	asc_op		asc_cookie.set_op
    	Entry		*asc_e;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    
    
    SLAP_SET_GATHER acl_set_gather;
    SLAP_SET_GATHER acl_set_gather2;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    /*
    
     * access_allowed - check whether op->o_ndn is allowed the requested access
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
     * to entry e, attribute attr, value val.  if val is null, access to
    
     * the whole attribute is assumed (all values).
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
     *
    
     * This routine loops through all access controls and calls
    
     * slap_acl_mask() on each applicable access control.
    
     * The loop exits when a definitive answer is reached or
     * or no more controls remain.
     *
     * returns:
     *		0	access denied
     *		1	access granted
    
     *
     * Notes:
     * - can be legally called with op == NULL
     * - can be legally called with op->o_bd == NULL
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
     */
    
    
    int
    slap_access_always_allowed(
    	Operation		*op,
    	Entry			*e,
    	AttributeDescription	*desc,
    	struct berval		*val,
    	slap_access_t		access,
    	AccessControlState	*state,
    	slap_mask_t		*maskp )
    {
    
    	/* assign all */
    	ACL_LVL_ASSIGN_MANAGE( *maskp );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    #define MATCHES_DNMAXCOUNT(m) 					\
    	( sizeof ( (m)->dn_data ) / sizeof( *(m)->dn_data ) )
    #define MATCHES_VALMAXCOUNT(m) 					\
    	( sizeof ( (m)->val_data ) / sizeof( *(m)->val_data ) )
    #define MATCHES_MEMSET(m) do {					\
    	memset( (m)->dn_data, '\0', sizeof( (m)->dn_data ) );	\
    	memset( (m)->val_data, '\0', sizeof( (m)->val_data ) );	\
    	(m)->dn_count = MATCHES_DNMAXCOUNT( (m) );		\
    	(m)->val_count = MATCHES_VALMAXCOUNT( (m) );		\
    } while ( 0 /* CONSTCOND */ )
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    int
    
    slap_access_allowed(
    	Operation		*op,
    	Entry			*e,
    	AttributeDescription	*desc,
    	struct berval		*val,
    	slap_access_t		access,
    	AccessControlState	*state,
    	slap_mask_t		*maskp )
    {
    	int				ret = 1;
    	int				count;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	AccessControl			*a, *prev;
    
    
    #ifdef LDAP_DEBUG
    	char				accessmaskbuf[ACCESSMASK_MAXLEN];
    #endif
    	slap_mask_t			mask;
    	slap_control_t			control;
    	slap_access_t			access_level;
    	const char			*attr;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	AclRegexMatches			matches;
    	AccessControlState		acl_state = ACL_STATE_INIT;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	static AccessControlState	state_init = ACL_STATE_INIT;
    
    
    	assert( op != NULL );
    	assert( e != NULL );
    	assert( desc != NULL );
    	assert( maskp != NULL );
    
    	access_level = ACL_LEVEL( access );
    	attr = desc->ad_cname.bv_val;
    
    	assert( attr != NULL );
    
    
    	/* grant database root access */
    	if ( be_isroot( op ) ) {
    		Debug( LDAP_DEBUG_ACL, "<= root access granted\n", 0, 0, 0 );
    		mask = ACL_LVL_MANAGE;
    		goto done;
    	}
    
    	/*
    	 * no-user-modification operational attributes are ignored
    	 * by ACL_WRITE checking as any found here are not provided
    	 * by the user
    
    	 *
    	 * NOTE: but they are not ignored for ACL_MANAGE, because
    	 * if we get here it means a non-root user is trying to 
    	 * manage data, so we need to check its privileges.
    
    	if ( access_level == ACL_WRITE
    		&& is_at_no_user_mod( desc->ad_type )
    
    		&& desc != slap_schema.si_ad_entry
    		&& desc != slap_schema.si_ad_children )
    	{
    		Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
    			" %s access granted\n",
    			attr, 0, 0 );
    		goto done;
    	}
    
    	/* use backend default access if no backend acls */
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	if ( op->o_bd->be_acl == NULL && frontendDB->be_acl == NULL ) {
    
    		int	i;
    
    		Debug( LDAP_DEBUG_ACL,
    			"=> slap_access_allowed: backend default %s "
    			"access %s to \"%s\"\n",
    			access2str( access ),
    			op->o_bd->be_dfltaccess >= access_level ? "granted" : "denied",
    			op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" );
    		ret = op->o_bd->be_dfltaccess >= access_level;
    
    		mask = ACL_PRIV_LEVEL;
    		for ( i = ACL_NONE; i <= op->o_bd->be_dfltaccess; i++ ) {
    			ACL_PRIV_SET( mask, ACL_ACCESS2PRIV( i ) );
    		}
    
    		goto done;
    	}
    
    	ret = 0;
    	control = ACL_BREAK;
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	if ( state == NULL )
    		state = &acl_state;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	if ( state->as_desc == desc &&
    		state->as_access == access &&
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		state->as_vd_acl_present )
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	{
    
    		a = state->as_vd_acl;
    		count = state->as_vd_acl_count;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		if ( state->as_fe_done )
    			state->as_fe_done--;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		ACL_PRIV_ASSIGN( mask, state->as_vd_mask );
    
    	} else {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		*state = state_init;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    
    
    		a = NULL;
    		count = 0;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		ACL_PRIV_ASSIGN( mask, *maskp );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	MATCHES_MEMSET( &matches );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	prev = a;
    
    	while ( ( a = slap_acl_get( a, &count, op, e, desc, val,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		&matches, &mask, state ) ) != NULL )
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		int i; 
    		int dnmaxcount = MATCHES_DNMAXCOUNT( &matches );
    		int valmaxcount = MATCHES_VALMAXCOUNT( &matches );
    		regmatch_t *dn_data = matches.dn_data;
    		regmatch_t *val_data = matches.val_data;
    
    		/* DN matches */
    		for ( i = 0; i < dnmaxcount && dn_data[i].rm_eo > 0; i++ ) {
    			char *data = e->e_ndn;
    
    			Debug( LDAP_DEBUG_ACL, "=> match[dn%d]: %d %d ", i,
    				(int)dn_data[i].rm_so, 
    				(int)dn_data[i].rm_eo );
    			if ( dn_data[i].rm_so <= dn_data[0].rm_eo ) {
    				int n;
    				for ( n = dn_data[i].rm_so; 
    				      n < dn_data[i].rm_eo; n++ ) {
    					Debug( LDAP_DEBUG_ACL, "%c", 
    					       data[n], 0, 0 );
    				}
    			}
    			Debug( LDAP_DEBUG_ACL, "\n", 0, 0, 0 );
    		}
    
    		/* val matches */
    		for ( i = 0; i < valmaxcount && val_data[i].rm_eo > 0; i++ ) {
    			char *data = val->bv_val;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			Debug( LDAP_DEBUG_ACL, "=> match[val%d]: %d %d ", i,
    				(int)val_data[i].rm_so, 
    				(int)val_data[i].rm_eo );
    			if ( val_data[i].rm_so <= val_data[0].rm_eo ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				for ( n = val_data[i].rm_so; 
    				      n < val_data[i].rm_eo; n++ ) {
    					Debug( LDAP_DEBUG_ACL, "%c", 
    					       data[n], 0, 0 );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			Debug( LDAP_DEBUG_ACL, "\n", 0, 0, 0 );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		control = slap_acl_mask( a, prev, &mask, op,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			e, desc, val, &matches, count, state, access );
    
    
    		if ( control != ACL_BREAK ) {
    			break;
    		}
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		MATCHES_MEMSET( &matches );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		prev = a;
    
    	}
    
    	if ( ACL_IS_INVALID( mask ) ) {
    		Debug( LDAP_DEBUG_ACL,
    			"=> slap_access_allowed: \"%s\" (%s) invalid!\n",
    			e->e_dn, attr, 0 );
    
    		ACL_PRIV_ASSIGN( mask, *maskp );
    
    
    	} else if ( control == ACL_BREAK ) {
    		Debug( LDAP_DEBUG_ACL,
    			"=> slap_access_allowed: no more rules\n", 0, 0, 0 );
    
    		goto done;
    	}
    
    
    	ret = ACL_GRANT( mask, access );
    
    
    	Debug( LDAP_DEBUG_ACL,
    		"=> slap_access_allowed: %s access %s by %s\n",
    		access2str( access ), ret ? "granted" : "denied",
    		accessmask2str( mask, accessmaskbuf, 1 ) );
    
    done:
    
    	ACL_PRIV_ASSIGN( *maskp, mask );
    
    int
    fe_access_allowed(
    	Operation		*op,
    	Entry			*e,
    	AttributeDescription	*desc,
    	struct berval		*val,
    	slap_access_t		access,
    	AccessControlState	*state,
    	slap_mask_t		*maskp )
    {
    	BackendDB		*be_orig;
    	int			rc;
    
    
    	/*
    	 * NOTE: control gets here if FIXME
    	 * if an appropriate backend cannot be selected for the operation,
    	 * we assume that the frontend should handle this
    	 * FIXME: should select_backend() take care of this,
    	 * and return frontendDB instead of NULL?  maybe for some value
    	 * of the flags?
    	 */
    
    	if ( op->o_bd == NULL ) {
    
    		op->o_bd = select_backend( &op->o_req_ndn, 0 );
    
    		if ( op->o_bd == NULL )
    			op->o_bd = frontendDB;
    
    	rc = slap_access_allowed( op, e, desc, val, access, state, maskp );
    	op->o_bd = be_orig;
    
    	return rc;
    }
    
    
    int
    access_allowed_mask(
    	Operation		*op,
    	Entry			*e,
    	AttributeDescription	*desc,
    	struct berval		*val,
    	slap_access_t		access,
    	AccessControlState	*state,
    	slap_mask_t		*maskp )
    {
    	int				ret = 1;
    	int				be_null = 0;
    
    #ifdef LDAP_DEBUG
    	char				accessmaskbuf[ACCESSMASK_MAXLEN];
    #endif
    	slap_mask_t			mask;
    	slap_access_t			access_level;
    	const char			*attr;
    
    	assert( e != NULL );
    	assert( desc != NULL );
    
    	access_level = ACL_LEVEL( access );
    
    	assert( access_level > ACL_NONE );
    
    
    	ACL_INIT( mask );
    
    	if ( maskp ) ACL_INVALIDATE( *maskp );
    
    	attr = desc->ad_cname.bv_val;
    
    	assert( attr != NULL );
    
    
    	if ( op ) {
    
    		if ( op->o_acl_priv != ACL_NONE ) {
    			access = op->o_acl_priv;
    
    		} else if ( op->o_is_auth_check &&
    
    			( access_level == ACL_SEARCH || access_level == ACL_READ ) )
    		{
    			access = ACL_AUTH;
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		} else if ( get_relax( op ) && access_level == ACL_WRITE &&
    
    			desc == slap_schema.si_ad_entry )
    		{
    			access = ACL_MANAGE;
    		}
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	if ( state != NULL ) {
    		if ( state->as_desc == desc &&
    			state->as_access == access &&
    			state->as_result != -1 &&
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			!state->as_vd_acl_present )
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			Debug( LDAP_DEBUG_ACL,
    				"=> access_allowed: result was in cache (%s)\n",
    				attr, 0, 0 );
    
    		} else {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			Debug( LDAP_DEBUG_ACL,
    				"=> access_allowed: result not in cache (%s)\n",
    				attr, 0, 0 );
    
    		}
    	}
    
    	Debug( LDAP_DEBUG_ACL,
    		"=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
    		access2str( access ), e->e_dn, attr );
    
    	if ( op == NULL ) {
    		/* no-op call */
    		goto done;
    	}
    
    	if ( op->o_bd == NULL ) {
    
    		op->o_bd = LDAP_STAILQ_FIRST( &backendDB );
    
    		be_null = 1;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		/* FIXME: experimental; use first backend rules
    		 * iff there is no global_acl (ITS#3100)
    		 */
    
    		if ( frontendDB->be_acl != NULL ) {
    			op->o_bd = frontendDB;
    		}
    	}
    	assert( op->o_bd != NULL );
    
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    	/* this is enforced in backend_add() */
    
    	if ( op->o_bd->bd_info->bi_access_allowed ) {
    		/* delegate to backend */
    
    		ret = op->o_bd->bd_info->bi_access_allowed( op, e,
    				desc, val, access, state, &mask );
    
    		/* use default (but pass through frontend
    		 * for global ACL overlays) */
    		ret = frontendDB->bd_info->bi_access_allowed( op, e,
    				desc, val, access, state, &mask );
    
    	if ( !ret ) {
    		if ( ACL_IS_INVALID( mask ) ) {
    			Debug( LDAP_DEBUG_ACL,
    				"=> access_allowed: \"%s\" (%s) invalid!\n",
    				e->e_dn, attr, 0 );
    
    			ACL_INIT( mask );
    
    			Debug( LDAP_DEBUG_ACL,
    				"=> access_allowed: no more rules\n", 0, 0, 0 );
    
    			goto done;
    		}
    	}
    
    	Debug( LDAP_DEBUG_ACL,
    		"=> access_allowed: %s access %s by %s\n",
    		access2str( access ), ret ? "granted" : "denied",
    		accessmask2str( mask, accessmaskbuf, 1 ) );
    
    done:
    	if ( state != NULL ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		state->as_access = access;
    
    			state->as_result = ret;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		state->as_desc = desc;
    
    	}
    	if ( be_null ) op->o_bd = NULL;
    	if ( maskp ) ACL_PRIV_ASSIGN( *maskp, mask );
    	return ret;
    }
    
    
    Howard Chu's avatar
    Howard Chu committed
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    /*
    
     * slap_acl_get - return the acl applicable to entry e, attribute
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
     * attr.  the acl returned is suitable for use in subsequent calls to
     * acl_access_allowed().
     */
    
    
    slap_acl_get(
    
    	AttributeDescription *desc,
    
    	struct berval	*val,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	AclRegexMatches	*matches,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	slap_mask_t *mask,
    
    Howard Chu's avatar
    Howard Chu committed
    	AccessControlState *state )
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    {
    
    	const char *attr;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	ber_len_t dnlen;
    
    	AccessControl *prev;
    
    	assert( e != NULL );
    	assert( count != NULL );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	assert( state != NULL );
    
    	attr = desc->ad_cname.bv_val;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		if( op->o_bd == NULL || op->o_bd->be_acl == NULL ) {
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		}
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		if ( a == frontendDB->be_acl )
    			state->as_fe_done = 1;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	}
    
    
    Howard Chu's avatar
    Howard Chu committed
    	dnlen = e->e_nname.bv_len;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
     retry:
    
    	for ( ; a != NULL; prev = a, a = a->acl_next ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		if ( a != frontendDB->be_acl && state->as_fe_done )
    			state->as_fe_done++;
    
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		if ( a->acl_dn_pat.bv_len || ( a->acl_dn_style != ACL_STYLE_REGEX )) {
    
    			if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
    				Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n", 
    
    					*count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				if ( regexec ( &a->acl_dn_re, 
    					       e->e_ndn, 
    				 	       matches->dn_count, 
    					       matches->dn_data, 0 ) )
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				ber_len_t patlen;
    
    
    				Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n", 
    
    					*count, a->acl_dn_pat.bv_val, 0 );
    
    				if ( dnlen < patlen )
    					continue;
    
    				if ( a->acl_dn_style == ACL_STYLE_BASE ) {
    					/* base dn -- entire object DN must match */
    					if ( dnlen != patlen )
    						continue;
    
    				} else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    					ber_len_t	rdnlen = 0;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    					ber_len_t	sep = 0;
    
    					if ( patlen > 0 ) {
    
    						if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
    
    							continue;
    						sep = 1;
    					}
    
    					rdnlen = dn_rdnlen( NULL, &e->e_nname );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    					if ( rdnlen + patlen + sep != dnlen )
    
    						continue;
    
    				} else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
    
    					if ( dnlen > patlen && !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
    
    						continue;
    
    				} else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
    					if ( dnlen <= patlen )
    						continue;
    
    					if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
    
    				if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 )
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    			}
    
    
    			Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
    				*count, 0, 0 );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		}
    
    		if ( a->acl_attrs && !ad_inlist( desc, a->acl_attrs ) ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			matches->dn_data[0].rm_so = -1;
    			matches->dn_data[0].rm_eo = -1;
    			matches->val_data[0].rm_so = -1;
    			matches->val_data[0].rm_eo = -1;
    
    			continue;
    		}
    
    		/* Is this ACL only for a specific value? */
    
    		if ( a->acl_attrval.bv_val ) {
    
    			if ( val == NULL ) {
    				continue;
    			}
    
    Howard Chu's avatar
    Howard Chu committed
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			if ( !state->as_vd_acl_present ) {
    				state->as_vd_acl_present = 1;
    
    				state->as_vd_acl = prev;
    				state->as_vd_acl_count = *count - 1;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				ACL_PRIV_ASSIGN ( state->as_vd_mask, *mask );
    
    Howard Chu's avatar
    Howard Chu committed
    			}
    
    
    			if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
    				Debug( LDAP_DEBUG_ACL,
    					"acl_get: valpat %s\n",
    					a->acl_attrval.bv_val, 0, 0 );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				if ( regexec ( &a->acl_attrval_re, 
    						    val->bv_val, 
    						    matches->val_count, 
    						    matches->val_data, 0 ) )
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    				{
    
    					continue;
    
    			} else {
    				int match = 0;
    				const char *text;
    				Debug( LDAP_DEBUG_ACL,
    					"acl_get: val %s\n",
    					a->acl_attrval.bv_val, 0, 0 );
    	
    				if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
    					if (value_match( &match, desc,
    
    						val, &a->acl_attrval, &text ) != LDAP_SUCCESS ||
    							match )
    						continue;
    					
    				} else {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    					ber_len_t	patlen, vdnlen;
    
    	
    					patlen = a->acl_attrval.bv_len;
    					vdnlen = val->bv_len;
    	
    					if ( vdnlen < patlen )
    						continue;
    	
    
    Howard Chu's avatar
    Howard Chu committed
    					if ( a->acl_attrval_style == ACL_STYLE_BASE ) {
    
    						if ( vdnlen > patlen )
    							continue;
    	
    
    Howard Chu's avatar
    Howard Chu committed
    					} else if ( a->acl_attrval_style == ACL_STYLE_ONE ) {
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    						ber_len_t	rdnlen = 0;
    
    						if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
    
    							continue;
    	
    						rdnlen = dn_rdnlen( NULL, val );
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    						if ( rdnlen + patlen + 1 != vdnlen )
    
    							continue;
    	
    
    Howard Chu's avatar
    Howard Chu committed
    					} else if ( a->acl_attrval_style == ACL_STYLE_SUBTREE ) {
    
    						if ( vdnlen > patlen && !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
    
    							continue;
    	
    
    Howard Chu's avatar
    Howard Chu committed
    					} else if ( a->acl_attrval_style == ACL_STYLE_CHILDREN ) {
    
    						if ( vdnlen <= patlen )
    							continue;
    	
    
    						if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
    
    					if ( strcmp( a->acl_attrval.bv_val, val->bv_val + vdnlen - patlen ) )
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    		if ( a->acl_filter != NULL ) {
    
    			ber_int_t rc = test_filter( NULL, e, a->acl_filter );
    
    			if ( rc != LDAP_COMPARE_TRUE ) {
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    				continue;
    			}
    		}
    
    		Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] attr %s\n",
    
    Gary Williams's avatar
    Gary Williams committed
    		       *count, attr, 0);
    
    		return a;
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	}
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	if ( !state->as_fe_done ) {
    		state->as_fe_done = 1;
    		a = frontendDB->be_acl;
    		goto retry;
    	}
    
    
    	Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
    
    Kurt Zeilenga's avatar
    Kurt Zeilenga committed
    	return( NULL );
    }
    
    
    /*
     * Record value-dependent access control state
     */
    #define ACL_RECORD_VALUE_STATE do { \
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    		if( state && !state->as_vd_acl_present ) { \
    			state->as_vd_acl_present = 1; \
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			state->as_vd_acl = prev; \
    			state->as_vd_acl_count = count - 1; \
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			ACL_PRIV_ASSIGN( state->as_vd_mask, *mask ); \
    
    	struct berval		*val,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    	AclRegexMatches		*matches,
    
    	struct berval		*opndn )
    {
    	/*
    	 * if access applies to the entry itself, and the
    	 * user is bound as somebody in the same namespace as
    	 * the entry, OR the given dn matches the dn pattern
    	 */
    	/*
    	 * NOTE: styles "anonymous", "users" and "self" 
    	 * have been moved to enum slap_style_t, whose 
    	 * value is set in a_dn_style; however, the string
    
    	 * is maintained in a_dn_pat.
    
    	if ( bdn->a_style == ACL_STYLE_ANONYMOUS ) {
    
    	} else if ( bdn->a_style == ACL_STYLE_USERS ) {
    
    	} else if ( bdn->a_style == ACL_STYLE_SELF ) {
    
    		struct berval	ndn, selfndn;
    		int		level;
    
    		if ( BER_BVISEMPTY( opndn ) || BER_BVISNULL( &e->e_nname ) ) {
    			return 1;
    		}
    
    
    		if ( level < 0 ) {
    			selfndn = *opndn;
    			ndn = e->e_nname;
    			level = -level;
    
    		} else {
    			ndn = *opndn;
    			selfndn = e->e_nname;
    		}
    
    		for ( ; level > 0; level-- ) {
    			if ( BER_BVISEMPTY( &ndn ) ) {
    				break;
    			}
    			dnParent( &ndn, &ndn );
    		}
    			
    		if ( BER_BVISEMPTY( &ndn ) || !dn_match( &ndn, &selfndn ) )
    		{
    			return 1;
    		}
    
    
    	} else if ( bdn->a_style == ACL_STYLE_REGEX ) {
    		if ( !ber_bvccmp( &bdn->a_pat, '*' ) ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			AclRegexMatches	tmp_matches,
    					*tmp_matchesp = &tmp_matches;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			regmatch_t 	*tmp_data;
    
    			MATCHES_MEMSET( &tmp_matches );
    			tmp_data = &tmp_matches.dn_data[0];
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			if ( a->acl_attrval_style == ACL_STYLE_REGEX )
    				tmp_matchesp = matches;
    			else switch ( a->acl_dn_style ) {
    
    			case ACL_STYLE_REGEX:
    				if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    					tmp_matchesp = matches; 
    
    					break;
    				}
    			/* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
    
    			case ACL_STYLE_BASE:
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				tmp_data[0].rm_so = 0;
    				tmp_data[0].rm_eo = e->e_nname.bv_len;
    				tmp_matches.dn_count = 1;
    
    				break;
    
    			case ACL_STYLE_ONE:
    			case ACL_STYLE_SUBTREE:
    			case ACL_STYLE_CHILDREN:
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				tmp_data[0].rm_so = 0;
    				tmp_data[0].rm_eo = e->e_nname.bv_len;
    				tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
    				tmp_data[1].rm_eo = e->e_nname.bv_len;
    				tmp_matches.dn_count = 2;
    
    			if ( !regex_matches( &bdn->a_pat, opndn->bv_val,
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				&e->e_nname, NULL, tmp_matchesp ) )
    
    			{
    				return 1;
    			}
    		}
    
    	} else {
    		struct berval	pat;
    		ber_len_t	patlen, odnlen;
    		int		got_match = 0;
    
    		if ( e->e_dn == NULL )
    			return 1;
    
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			AclRegexMatches	tmp_matches,
    					*tmp_matchesp = &tmp_matches;
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			regmatch_t 	*tmp_data;
    
    			MATCHES_MEMSET( &tmp_matches );
    			tmp_data = &tmp_matches.dn_data[0];
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    			/* Expand value regex */
    			if ( a->acl_attrval_style == ACL_STYLE_REGEX )
    				tmp_matchesp = matches;
    			else switch ( a->acl_dn_style ) {
    
    			case ACL_STYLE_REGEX:
    				if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
    					tmp_matchesp = matches;
    					break;
    				}
    			/* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
    
    			case ACL_STYLE_BASE:
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				tmp_data[0].rm_so = 0;
    				tmp_data[0].rm_eo = e->e_nname.bv_len;
    				tmp_matches.dn_count = 1;
    
    				break;
    
    			case ACL_STYLE_ONE:
    			case ACL_STYLE_SUBTREE:
    			case ACL_STYLE_CHILDREN:
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    				tmp_data[0].rm_so = 0;
    				tmp_data[0].rm_eo = e->e_nname.bv_len;
    				tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
    				tmp_data[1].rm_eo = e->e_nname.bv_len;
    				tmp_matches.dn_count = 2;
    
    			if ( acl_string_expand( &bv, &bdn->a_pat, 
    
    Quanah Gibson-Mount's avatar
    Quanah Gibson-Mount committed
    						&e->e_nname, 
    						val, tmp_matchesp ) )
    
    			{
    				return 1;
    			}
    			
    			if ( dnNormalize(0, NULL, NULL, &bv,
    					&pat, op->o_tmpmemctx )
    					!= LDAP_SUCCESS )
    			{
    				/* did not expand to a valid dn */
    				return 1;
    			}
    
    		} else {
    
    		}
    
    		patlen = pat.bv_len;
    		odnlen = opndn->bv_len;
    		if ( odnlen < patlen ) {
    			goto dn_match_cleanup;
    
    		}
    
    
    		if ( bdn->a_style == ACL_STYLE_BASE ) {
    
    			/* base dn -- entire object DN must match */
    			if ( odnlen != patlen ) {
    				goto dn_match_cleanup;
    			}
    
    
    		} else if ( bdn->a_style == ACL_STYLE_ONE ) {
    
    Pierangelo Masarati's avatar
    Pierangelo Masarati committed
    			ber_len_t	rdnlen = 0;
    
    
    			if ( odnlen <= patlen ) {
    				goto dn_match_cleanup;
    			}
    
    			if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
    				goto dn_match_cleanup;
    			}
    
    			rdnlen = dn_rdnlen( NULL, opndn );
    
    			if ( rdnlen - ( odnlen - patlen - 1 ) != 0 ) {
    
    		} else if ( bdn->a_style == ACL_STYLE_SUBTREE ) {
    
    			if ( odnlen > patlen && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
    				goto dn_match_cleanup;
    			}
    
    
    		} else if ( bdn->a_style == ACL_STYLE_CHILDREN ) {
    
    			if ( odnlen <= patlen ) {
    				goto dn_match_cleanup;
    			}
    
    			if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
    				goto dn_match_cleanup;
    			}
    
    
    		} else if ( bdn->a_style == ACL_STYLE_LEVEL ) {
    			int		level = bdn->a_level;
    
    
    			if ( odnlen <= patlen ) {
    				goto dn_match_cleanup;
    			}
    
    			if ( level > 0 && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) )
    			{
    				goto dn_match_cleanup;
    			}
    			
    			ndn = *opndn;
    			for ( ; level > 0; level-- ) {
    				if ( BER_BVISEMPTY( &ndn ) ) {
    					goto dn_match_cleanup;
    				}
    				dnParent( &ndn, &ndn );
    				if ( ndn.bv_len < patlen ) {
    					goto dn_match_cleanup;
    				}
    			}
    			
    			if ( ndn.bv_len != patlen ) {
    				goto dn_match_cleanup;
    			}
    		}
    
    		got_match = !strcmp( pat.bv_val, &opndn->bv_val[ odnlen - patlen ] );
    
    dn_match_cleanup:;
    
    		if ( pat.bv_val != bdn->a_pat.bv_val ) {