--- /dev/null
+/* 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, je;
+
+#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 ) {
+ BER_BVZERO( &bv_allowed[ ja ] );
+ *ap = attr_alloc( ad_allowedAttributes );
+ (*ap)->a_vals = bv_allowed;
+ (*ap)->a_nvals = bv_allowed;
+ ap = &(*ap)->a_next;
+ }
+
+ if ( got & GOT_AE ) {
+ BER_BVZERO( &bv_effective[ je ] );
+ *ap = attr_alloc( ad_allowedAttributesEffective );
+ (*ap)->a_vals = bv_effective;
+ (*ap)->a_nvals = bv_effective;
+ 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 = "aa";
+
+ 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 */