summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules/allowed
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--contrib/slapd-modules/allowed/Makefile58
-rw-r--r--contrib/slapd-modules/allowed/README73
-rw-r--r--contrib/slapd-modules/allowed/allowed.c504
3 files changed, 635 insertions, 0 deletions
diff --git a/contrib/slapd-modules/allowed/Makefile b/contrib/slapd-modules/allowed/Makefile
new file mode 100644
index 0000000..32cb432
--- /dev/null
+++ b/contrib/slapd-modules/allowed/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_ALLOWED=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = allowed.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+allowed.la: allowed.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/allowed/README b/contrib/slapd-modules/allowed/README
new file mode 100644
index 0000000..a1267cf
--- /dev/null
+++ b/contrib/slapd-modules/allowed/README
@@ -0,0 +1,73 @@
+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>
+
+"allowedChildClasses"
+ <http://msdn.microsoft.com/en-us/library/ms675219(VS.85).aspx>
+
+"allowedChildClassesEffective"
+ <http://msdn.microsoft.com/en-us/library/ms675220(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 ---
+
+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 ---
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \
+ -o allowed.so allowed.c
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2006-2022 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 0000000..26e3106
--- /dev/null
+++ b/contrib/slapd-modules/allowed/allowed.c
@@ -0,0 +1,504 @@
+/* allowed.c - add allowed attributes based on ACL */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2022 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-mdb. 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"
+
+/*
+ * NOTE: part of the schema definition reported below is taken
+ * from Microsoft schema definitions (OID, NAME, SYNTAX);
+ *
+ * EQUALITY is taken from
+ * <http://www.redhat.com/archives/fedora-directory-devel/2006-August/msg00007.html>
+ * (posted by Andrew Bartlett)
+ *
+ * The rest is guessed. Specifically
+ *
+ * DESC briefly describes the purpose
+ *
+ * NO-USER-MODIFICATION is added to make attributes operational
+ *
+ * USAGE is set to "dSAOperation" as per ITS#7493,
+ * to prevent replication, since this information
+ * is generated (based on ACL and identity of request)
+ * and not stored.
+ */
+
+#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 dSAOperation )", &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 dSAOperation )", &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 dSAOperation )", &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 dSAOperation )", &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;
+
+#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 );
+
+ for ( ap = &rs->sr_operational_attrs; *ap != NULL; ap = &(*ap)->a_next )
+ /* go to last */ ;
+
+ /* 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 ) {
+ goto do_oc;
+ }
+
+ /* 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 ) {
+ int i;
+
+ for ( i = 0; oc->soc_sups[ i ] != NULL; i++ ) {
+ aa_add_oc( oc->soc_sups[ i ], &ocp, &atp );
+ }
+ }
+ }
+
+ ch_free( ocp );
+
+ if ( atp != NULL ) {
+ BerVarray bv_allowed = NULL,
+ bv_effective = NULL;
+ int i, ja = 0, je = 0;
+
+ for ( i = 0; atp[ i ] != NULL; i++ )
+ /* just count */ ;
+
+ if ( got & GOT_A ) {
+ bv_allowed = ch_calloc( i + 1, sizeof( struct berval ) );
+ }
+ if ( got & GOT_AE ) {
+ bv_effective = ch_calloc( i + 1, sizeof( struct berval ) );
+ }
+
+ 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++;
+ }
+ }
+ }
+
+ ch_free( atp );
+
+ if ( ( got & GOT_A ) && ja > 0 ) {
+ *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 ) {
+ *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;
+ }
+
+do_oc:;
+ if ( ( got & GOT_C ) || ( got & GOT_CE ) ) {
+ BerVarray bv_allowed = NULL,
+ bv_effective = NULL;
+ int i, ja = 0, je = 0;
+
+ ObjectClass *oc;
+
+ for ( i = 0, oc_start( &oc ); oc != NULL; oc_next( &oc ) ) {
+ /* we can only add AUXILIARY objectClasses */
+ if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
+ continue;
+ }
+
+ i++;
+ }
+
+ if ( got & GOT_C ) {
+ bv_allowed = ch_calloc( i + 1, sizeof( struct berval ) );
+ }
+ if ( got & GOT_CE ) {
+ bv_effective = ch_calloc( i + 1, sizeof( struct berval ) );
+ }
+
+ for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) {
+ /* we can only add AUXILIARY objectClasses */
+ if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
+ continue;
+ }
+
+ if ( got & GOT_C ) {
+ ber_dupbv( &bv_allowed[ ja ], &oc->soc_cname );
+ ja++;
+ }
+
+ if ( got & GOT_CE ) {
+ if ( !access_allowed( op, rs->sr_entry,
+ slap_schema.si_ad_objectClass,
+ &oc->soc_cname, ACL_WRITE, NULL ) )
+ {
+ goto done_ce;
+ }
+
+ if ( oc->soc_required ) {
+ for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ if ( slap_bv2ad( &oc->soc_required[ i ]->sat_cname, &ad, &text ) ) {
+ /* log? */
+ continue;
+ }
+
+ if ( !access_allowed( op, rs->sr_entry,
+ ad, NULL, ACL_WRITE, NULL ) )
+ {
+ goto done_ce;
+ }
+ }
+ }
+
+ ber_dupbv( &bv_effective[ je ], &oc->soc_cname );
+ je++;
+ }
+done_ce:;
+ }
+
+ if ( ( got & GOT_C ) && ja > 0 ) {
+ *ap = attr_alloc( ad_allowedChildClasses );
+ (*ap)->a_vals = bv_allowed;
+ (*ap)->a_nvals = bv_allowed;
+ (*ap)->a_numvals = ja;
+ ap = &(*ap)->a_next;
+ }
+
+ if ( ( got & GOT_CE ) && je > 0 ) {
+ *ap = attr_alloc( ad_allowedChildClassesEffective );
+ (*ap)->a_vals = bv_effective;
+ (*ap)->a_nvals = bv_effective;
+ (*ap)->a_numvals = je;
+ ap = &(*ap)->a_next;
+ }
+
+ *ap = NULL;
+ }
+
+ 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 );
+ }
+ 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_flags = SLAPO_BFLAG_SINGLE;
+ 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" );
+ 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 */