diff --git a/CHANGES b/CHANGES
index 9b4a93214ee77909009acb5b8ff2b00a5cd02a9e..c34611375af02ea29738fd868112f4480c3013e1 100644
--- a/CHANGES
+++ b/CHANGES
@@ -23,13 +23,14 @@ OpenLDAP 2.4.18 Engineering
 	Fixed slapd-relay response/cleanup callback mismatch (ITS#6154)
 	Fixed slapd-sql with baseObject query (ITS#6172)
 	Fixed slapd-sql with empty attribute (ITS#6163)
-	Added slapo-pcache olcProxyCacheOffline (ITS#6152)
+	Fixed slapo-pcache multiple enhancements (ITS#6152)
 	Fixed slapo-translucent attribute return (ITS#6254)
 	Fixed slapo-translucent filter matching (ITS#6255)
 	Fixed slapo-translucent to honor sizelimit (ITS#6253)
 	Fixed slapo-unique filter matching (ITS#6077)
 	Fixed tools off by one error (ITS#6233)
 	Fixed tools resource leaks (ITS#6145)
+	Added contrib/allowed (ITS#4730)
 	Fixed contrib/autogroup with RE24 (ITS#6227)
 	Fixed contrib/nss symbols (ITS#6273)
 	Build Environment
diff --git a/contrib/slapd-modules/allowed/README b/contrib/slapd-modules/allowed/README
new file mode 100644
index 0000000000000000000000000000000000000000..32abdf5f64e0955987e9da198ea55a6a1a392e08
--- /dev/null
+++ b/contrib/slapd-modules/allowed/README
@@ -0,0 +1,83 @@
+This directory contains a slapd overlay, "allowed".
+
+    --- o --- o --- o ---
+
+It adds to entries returned by search operations the value of attributes
+
+"allowedAttributes"
+	<http://msdn.microsoft.com/en-us/library/ms675217(VS.85).aspx>
+
+"allowedAttributesEffective"
+	<http://msdn.microsoft.com/en-us/library/ms675218(VS.85).aspx>
+
+No other use is made of those attributes: they cannot be compared,
+they cannot be used in search filters, they cannot be used in ACLs, ...
+
+    --- o --- o --- o ---
+
+Other attributes like
+
+"allowedChildClasses"
+	<http://msdn.microsoft.com/en-us/library/ms675219(VS.85).aspx>
+"allowedChildClassesEffective"
+	<http://msdn.microsoft.com/en-us/library/ms675220(VS.85).aspx>
+
+make little sense within OpenLDAP's slapd right now, since any AUXILIARY
+objectClass can be added to an entry, while no STRUCTURAL objectClass can.
+This may change when DIT structure rules are implemented, while ACLs may
+restrict what AUXILIARY objectClasses can be added to an entry.
+
+    --- o --- o --- o ---
+
+Usage: add to slapd.conf(5)
+
+
+moduleload	path/to/allowed.so
+overlay		allowed
+
+or add
+
+dn: olcOverlay={0}allowed,olcDatabase={1}bdb,cn=config
+objectClass: olcOverlayConfig
+olcOverlay: {0}allowed
+
+as a child of the database that's intended to support this feature
+(replace "olcDatabase={1}bdb,cn=config" with the appropriate parent);
+or use
+
+dn: olcOverlay={0}allowed,olcDatabase={-1}frontend,cn=config
+objectClass: olcOverlayConfig
+olcOverlay: {0}allowed
+
+if it's supposed to be global.
+
+    --- o --- o --- o ---
+
+No Makefile is provided. Use a command line similar to:
+
+gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \
+	-o allowed.so allowed.c
+
+to compile this overlay, or even better use OpenLDAP's libtool as appropriate.
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2006-2009 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.
+
+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>.
+
+ACKNOWLEDGEMENTS:
+This work was initially developed by Pierangelo Masarati for inclusion in
+OpenLDAP Software.
+
diff --git a/contrib/slapd-modules/allowed/allowed.c b/contrib/slapd-modules/allowed/allowed.c
new file mode 100644
index 0000000000000000000000000000000000000000..415aeefa9bed4281268f56493d040d87cbf18189
--- /dev/null
+++ b/contrib/slapd-modules/allowed/allowed.c
@@ -0,0 +1,403 @@
+/* allowed.c - add allowed attributes based on ACL */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2009 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+/*
+ * Rationale: return in allowedAttributes the attributes required/allowed
+ * by the objectClasses that are currently present in an object; return
+ * in allowedAttributesEffective the subset of the above that can be written
+ * by the identity that performs the search.
+ *
+ * Caveats:
+ * - right now, the overlay assumes that all values of the objectClass
+ *   attribute will be returned in rs->sr_entry; this may not be true
+ *   in general, but it usually is for back-bdb/back-hdb.  To generalize,
+ *   the search request should be analyzed, and if allowedAttributes or
+ *   allowedAttributesEffective are requested, add objectClass to the
+ *   requested attributes
+ * - it assumes that there is no difference between write-add and 
+ *   write-delete
+ * - it assumes that access rules do not depend on the values of the 
+ *   attributes or on the contents of the entry (attr/val, filter, ...)
+ *   allowedAttributes and allowedAttributesEffective cannot be used
+ *   in filters or in compare
+ */
+
+#include "portable.h"
+
+/* define SLAPD_OVER_ALLOWED=2 to build as run-time loadable module */
+#ifdef SLAPD_OVER_ALLOWED
+
+#include "slap.h"
+
+/*
+ * Schema from
+ *
+ * <http://www.redhat.com/archives/fedora-directory-devel/2006-August/msg00007.html>
+ *
+ * posted by Andrew Bartlett
+ */
+
+#define AA_SCHEMA_AT "1.2.840.113556.1.4"
+
+static AttributeDescription
+		*ad_allowedChildClasses,
+		*ad_allowedChildClassesEffective,
+		*ad_allowedAttributes,
+		*ad_allowedAttributesEffective;
+
+static struct {
+	char *at;
+	AttributeDescription **ad;
+} aa_attrs[] = {
+	{ "( " AA_SCHEMA_AT ".911 "
+		"NAME 'allowedChildClasses' "
+		"EQUALITY objectIdentifierMatch "
+		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+		/* added by me :) */
+		"DESC 'Child classes allowed for a given object' "
+		"NO-USER-MODIFICATION "
+		"USAGE directoryOperation )", &ad_allowedChildClasses },
+	{ "( " AA_SCHEMA_AT ".912 "
+		"NAME 'allowedChildClassesEffective' "
+		"EQUALITY objectIdentifierMatch "
+		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+		/* added by me :) */
+		"DESC 'Child classes allowed for a given object according to ACLs' "
+		"NO-USER-MODIFICATION "
+		"USAGE directoryOperation )", &ad_allowedChildClassesEffective },
+	{ "( " AA_SCHEMA_AT ".913 "
+		"NAME 'allowedAttributes' "
+		"EQUALITY objectIdentifierMatch "
+		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+		/* added by me :) */
+		"DESC 'Attributes allowed for a given object' "
+		"NO-USER-MODIFICATION "
+		"USAGE directoryOperation )", &ad_allowedAttributes },
+	{ "( " AA_SCHEMA_AT ".914 "
+		"NAME 'allowedAttributesEffective' "
+		"EQUALITY objectIdentifierMatch "
+		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+		/* added by me :) */
+		"DESC 'Attributes allowed for a given object according to ACLs' "
+		"NO-USER-MODIFICATION "
+		"USAGE directoryOperation )", &ad_allowedAttributesEffective },
+
+	/* TODO: add objectClass stuff? */
+
+	{ NULL, NULL }
+};
+
+static int
+aa_add_at( AttributeType *at, AttributeType ***atpp )
+{
+	int		i = 0;
+
+	if ( *atpp ) {
+		for ( i = 0; (*atpp)[ i ] != NULL; i++ ) {
+			if ( (*atpp)[ i ] == at ) {
+				break;
+			}
+		}
+	
+		if ( (*atpp)[ i ] != NULL ) {
+			return 0;
+		}
+	}
+
+	*atpp = ch_realloc( *atpp, sizeof( AttributeType * ) * ( i + 2 ) );
+	(*atpp)[ i ] = at;
+	(*atpp)[ i + 1 ] = NULL;
+
+	return 0;
+}
+
+static int
+aa_add_oc( ObjectClass *oc, ObjectClass ***ocpp, AttributeType ***atpp )
+{
+	int		i = 0;
+
+	if ( *ocpp ) {
+		for ( ; (*ocpp)[ i ] != NULL; i++ ) {
+			if ( (*ocpp)[ i ] == oc ) {
+				break;
+			}
+		}
+
+		if ( (*ocpp)[ i ] != NULL ) {
+			return 0;
+		}
+	}
+
+	*ocpp = ch_realloc( *ocpp, sizeof( ObjectClass * ) * ( i + 2 ) );
+	(*ocpp)[ i ] = oc;
+	(*ocpp)[ i + 1 ] = NULL;
+
+	if ( oc->soc_required ) {
+		int		i;
+
+		for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) {
+			aa_add_at( oc->soc_required[ i ], atpp );
+		}
+	}
+
+	if ( oc->soc_allowed ) {
+		int		i;
+
+		for ( i = 0; oc->soc_allowed[ i ] != NULL; i++ ) {
+			aa_add_at( oc->soc_allowed[ i ], atpp );
+		}
+	}
+
+	return 0;
+}
+
+static int
+aa_operational( Operation *op, SlapReply *rs )
+{
+	Attribute		*a, **ap;
+	AccessControlState	acl_state = ACL_STATE_INIT;
+	struct berval		*v;
+	AttributeType		**atp = NULL;
+	ObjectClass		**ocp = NULL;
+	BerVarray		bv_allowed = NULL,
+				bv_effective = NULL;
+	int			i, ja = 0, je = 0;
+
+#define	GOT_NONE	(0x0U)
+#define	GOT_C		(0x1U)
+#define	GOT_CE		(0x2U)
+#define	GOT_A		(0x4U)
+#define	GOT_AE		(0x8U)
+#define	GOT_ALL		(GOT_C|GOT_CE|GOT_A|GOT_AE)
+	int		got = GOT_NONE;
+
+	/* only add if requested */
+	if ( SLAP_OPATTRS( rs->sr_attr_flags ) ) {
+		got = GOT_ALL;
+
+	} else {
+		if ( ad_inlist( ad_allowedChildClasses, rs->sr_attrs ) ) {
+			got |= GOT_C;
+		}
+
+		if ( ad_inlist( ad_allowedChildClassesEffective, rs->sr_attrs ) ) {
+			got |= GOT_CE;
+		}
+
+		if ( ad_inlist( ad_allowedAttributes, rs->sr_attrs ) ) {
+			got |= GOT_A;
+		}
+
+		if ( ad_inlist( ad_allowedAttributesEffective, rs->sr_attrs ) ) {
+			got |= GOT_AE;
+		}
+	}
+
+	if ( got == GOT_NONE ) {
+		return SLAP_CB_CONTINUE;
+	}
+
+	/* shouldn't be called without an entry; please check */
+	assert( rs->sr_entry != NULL );
+
+	/* see caveats; this is not guaranteed for all backends */
+	a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass );
+	if ( a == NULL ) {
+		return SLAP_CB_CONTINUE;
+	}
+
+	/* if client has no access to objectClass attribute; don't compute */
+	if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_objectClass,
+				NULL, ACL_READ, &acl_state ) )
+	{
+		return SLAP_CB_CONTINUE;
+	}
+
+	for ( v = a->a_nvals; !BER_BVISNULL( v ); v++ ) {
+		ObjectClass	*oc = oc_bvfind( v );
+
+		assert( oc != NULL );
+
+		/* if client has no access to specific value, don't compute */
+		if ( !access_allowed( op, rs->sr_entry,
+			slap_schema.si_ad_objectClass,
+			&oc->soc_cname, ACL_READ, &acl_state ) )
+		{
+			continue;
+		}
+
+		aa_add_oc( oc, &ocp, &atp );
+
+		if ( oc->soc_sups ) {
+			for ( i = 0; oc->soc_sups[ i ] != NULL; i++ ) {
+				aa_add_oc( oc->soc_sups[ i ], &ocp, &atp );
+			}
+		}
+	}
+
+	if ( atp != NULL ) {
+		for ( i = 0; atp[ i ] != NULL; i++ )
+			/* just count */ ;
+	
+		if ( got & GOT_A ) {
+			bv_allowed = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
+		}
+		if ( got & GOT_AE ) {
+			bv_effective = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
+		}
+
+		for ( i = 0, ja = 0, je = 0; atp[ i ] != NULL; i++ ) {
+			if ( got & GOT_A ) {
+				ber_dupbv( &bv_allowed[ ja ], &atp[ i ]->sat_cname );
+				ja++;
+			}
+
+			if ( got & GOT_AE ) {
+				AttributeDescription	*ad = NULL;
+				const char		*text = NULL;
+	
+				if ( slap_bv2ad( &atp[ i ]->sat_cname, &ad, &text ) ) {
+					/* log? */
+					continue;
+				}
+
+				if ( access_allowed( op, rs->sr_entry,
+					ad, NULL, ACL_WRITE, NULL ) )
+				{
+					ber_dupbv( &bv_effective[ je ], &atp[ i ]->sat_cname );
+					je++;
+				}
+			}
+		}
+
+		for ( ap = &rs->sr_operational_attrs; *ap != NULL; ap = &(*ap)->a_next )
+			/* go to last */ ;
+
+		if ( ( got & GOT_A ) && ja > 0 ) {
+			BER_BVZERO( &bv_allowed[ ja ] );
+			*ap = attr_alloc( ad_allowedAttributes );
+			(*ap)->a_vals = bv_allowed;
+			(*ap)->a_nvals = bv_allowed;
+			(*ap)->a_numvals = ja;
+			ap = &(*ap)->a_next;
+		}
+	
+		if ( ( got & GOT_AE ) && je > 0 ) {
+			BER_BVZERO( &bv_effective[ je ] );
+			*ap = attr_alloc( ad_allowedAttributesEffective );
+			(*ap)->a_vals = bv_effective;
+			(*ap)->a_nvals = bv_effective;
+			(*ap)->a_numvals = je;
+			ap = &(*ap)->a_next;
+		}
+	
+		*ap = NULL;
+	}
+
+	ch_free( atp );
+	ch_free( ocp );
+
+	return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst aa;
+
+#if LDAP_VENDOR_VERSION_MINOR != X && LDAP_VENDOR_VERSION_MINOR <= 3
+/* backport register_at() from HEAD, to allow building with OL <= 2.3 */
+static int
+register_at( char *def, AttributeDescription **rad, int dupok )
+{
+	LDAPAttributeType *at;
+	int code, freeit = 0;
+	const char *err;
+	AttributeDescription *ad = NULL;
+
+	at = ldap_str2attributetype( def, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+	if ( !at ) {
+		Debug( LDAP_DEBUG_ANY,
+			"register_at: AttributeType \"%s\": %s, %s\n",
+				def, ldap_scherr2str(code), err );
+		return code;
+	}
+
+	code = at_add( at, 0, NULL, &err );
+	if ( code ) {
+		if ( code == SLAP_SCHERR_ATTR_DUP && dupok ) {
+			freeit = 1;
+
+		} else {
+			ldap_attributetype_free( at );
+			Debug( LDAP_DEBUG_ANY,
+				"register_at: AttributeType \"%s\": %s, %s\n",
+				def, scherr2str(code), err );
+			return code;
+		}
+	}
+	code = slap_str2ad( at->at_names[0], &ad, &err );
+	if ( freeit || code ) {
+		ldap_attributetype_free( at );
+	} else {
+		ldap_memfree( at );
+	}
+	if ( code ) {
+		Debug( LDAP_DEBUG_ANY, "register_at: AttributeType \"%s\": %s\n",
+			def, err, 0 );
+	}
+	if ( rad ) *rad = ad;
+	return code;
+}
+#endif
+
+#if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */
+int
+aa_initialize( void )
+{
+	int i;
+
+	aa.on_bi.bi_type = "allowed";
+
+	aa.on_bi.bi_operational = aa_operational;
+
+	/* aa schema integration */
+	for ( i = 0; aa_attrs[i].at; i++ ) {
+		int code;
+
+		code = register_at( aa_attrs[i].at, aa_attrs[i].ad, 0 );
+		if ( code ) {
+			Debug( LDAP_DEBUG_ANY,
+				"aa_initialize: register_at failed\n", 0, 0, 0 );
+			return -1;
+		}
+	}
+
+	return overlay_register( &aa );
+}
+
+#if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+	return aa_initialize();
+}
+#endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_ALLOWED */