diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:35:32 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:35:32 +0000 |
commit | 5ea77a75dd2d2158401331879f3c8f47940a732c (patch) | |
tree | d89dc06e9f4850a900f161e25f84e922c4f86cc8 /contrib/slapd-modules/acl | |
parent | Initial commit. (diff) | |
download | openldap-upstream.tar.xz openldap-upstream.zip |
Adding upstream version 2.5.13+dfsg.upstream/2.5.13+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | contrib/slapd-modules/acl/Makefile | 50 | ||||
-rw-r--r-- | contrib/slapd-modules/acl/README.gssacl | 32 | ||||
-rw-r--r-- | contrib/slapd-modules/acl/README.posixgroup | 35 | ||||
-rw-r--r-- | contrib/slapd-modules/acl/gssacl.c | 316 | ||||
-rw-r--r-- | contrib/slapd-modules/acl/posixgroup.c | 329 |
5 files changed, 762 insertions, 0 deletions
diff --git a/contrib/slapd-modules/acl/Makefile b/contrib/slapd-modules/acl/Makefile new file mode 100644 index 0000000..090575d --- /dev/null +++ b/contrib/slapd-modules/acl/Makefile @@ -0,0 +1,50 @@ +# $OpenLDAP$ + +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 = +INCS = $(LDAP_INC) +LIBS = $(LDAP_LIB) + +PROGRAMS = posixgroup.la gssacl.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) + +posixgroup.la: posixgroup.lo + $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \ + -rpath $(moduledir) -module -o $@ $? $(LIBS) + +gssacl.la: gssacl.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/acl/README.gssacl b/contrib/slapd-modules/acl/README.gssacl new file mode 100644 index 0000000..368b178 --- /dev/null +++ b/contrib/slapd-modules/acl/README.gssacl @@ -0,0 +1,32 @@ +This directory contains native slapd plugins that implement access rules. + +gssacl.c contains a simple example that implements access control +based on GSS naming extensions attributes. + +To use the acl-gssacl plugin, add: + +moduleload acl-gssacl.so + +to your slapd configuration file. +It is configured using + +access to <what> + by dynacl/gss/<attribute>.[.{base,regex,expand}]=<valpat> {<level>|<priv(s)>} + +The default is "exact"; in case of "expand", "<valpat>" results from +the expansion of submatches in the "<what>" portion. "<level>|<priv(s)>" +describe the level of privilege this rule can assume. + +Use Makefile to compile this plugin or use a command line similar to: + +gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \ + -o acl-gssacl.so gssacl.c + + +--- +Copyright 2011 PADL Software Pty Ltd. 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. + diff --git a/contrib/slapd-modules/acl/README.posixgroup b/contrib/slapd-modules/acl/README.posixgroup new file mode 100644 index 0000000..5e0460d --- /dev/null +++ b/contrib/slapd-modules/acl/README.posixgroup @@ -0,0 +1,35 @@ +This directory contains native slapd plugins that implement access rules. + +posixgroup.c contains a simple example that implements access control +based on posixGroup membership, loosely inspired by ITS#3849. It should +be made clear that this access control policy does not reflect any +standard track model of handling access control, and should be +essentially viewed as an illustration of the use of the dynamic +extension of access control within slapd. + +To use the acl-posixgroup plugin, add: + +moduleload acl-posixgroup.so + +to your slapd configuration file; it requires "nis.schema" to be loaded. +It is configured using + +access to <what> + by dynacl/posixGroup[.{exact,expand}]=<dnpat> {<level>|<priv(s)} + +The default is "exact"; in case of "expand", "<dnpat>" results from +the expansion of submatches in the "<what>" portion. "<level>|<priv(s)>" +describe the level of privilege this rule can assume. + +Use Makefile to compile this plugin or use a command line similar to: + +gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \ + -o acl-posixgroup.so posixgroup.c + +--- +Copyright 2005-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. + diff --git a/contrib/slapd-modules/acl/gssacl.c b/contrib/slapd-modules/acl/gssacl.c new file mode 100644 index 0000000..12d3b9a --- /dev/null +++ b/contrib/slapd-modules/acl/gssacl.c @@ -0,0 +1,316 @@ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2011 PADL Software Pty Ltd. + * 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>. + */ + +#include <portable.h> + +#include <ac/string.h> +#include <slap.h> +#include <lutil.h> + +#include <sasl/sasl.h> +#include <gssapi/gssapi.h> +#include <gssapi/gssapi_ext.h> + +#define ACL_BUF_SIZE 1024 + +typedef struct gssattr_t { + slap_style_t gssattr_style; + struct berval gssattr_name; /* asserted name */ + struct berval gssattr_value; /* asserted value */ +} gssattr_t; + +static int gssattr_dynacl_destroy( void *priv ); + +static int +regex_matches( + struct berval *pat, /* pattern to expand and match against */ + char *str, /* string to match against pattern */ + struct berval *dn_matches, /* buffer with $N expansion variables from DN */ + struct berval *val_matches, /* buffer with $N expansion variables from val */ + AclRegexMatches *matches /* offsets in buffer for $N expansion variables */ +); + +static int +gssattr_dynacl_parse( + const char *fname, + int lineno, + const char *opts, + slap_style_t style, + const char *pattern, + void **privp ) +{ + gssattr_t *gssattr; + + gssattr = (gssattr_t *)ch_calloc( 1, sizeof( gssattr_t ) ); + + if ( opts == NULL || opts[0] == '\0' ) { + fprintf( stderr, "%s line %d: GSS ACL: no attribute specified.\n", + fname, lineno ); + goto cleanup; + } + + if ( pattern == NULL || pattern[0] == '\0' ) { + fprintf( stderr, "%s line %d: GSS ACL: no attribute value specified.\n", + fname, lineno ); + goto cleanup; + } + + gssattr->gssattr_style = style; + + switch ( gssattr->gssattr_style ) { + case ACL_STYLE_BASE: + case ACL_STYLE_REGEX: + case ACL_STYLE_EXPAND: + break; + default: + fprintf( stderr, "%s line %d: GSS ACL: unsupported style \"%s\".\n", + fname, lineno, style_strings[style] ); + goto cleanup; + break; + } + + ber_str2bv( opts, 0, 1, &gssattr->gssattr_name ); + ber_str2bv( pattern, 0, 1, &gssattr->gssattr_value ); + + *privp = (void *)gssattr; + return 0; + +cleanup: + (void)gssattr_dynacl_destroy( (void *)gssattr ); + + return 1; +} + +static int +gssattr_dynacl_unparse( + void *priv, + struct berval *bv ) +{ + gssattr_t *gssattr = (gssattr_t *)priv; + char *ptr; + + bv->bv_len = STRLENOF( " dynacl/gss/.expand=" ) + + gssattr->gssattr_name.bv_len + + gssattr->gssattr_value.bv_len; + bv->bv_val = ch_malloc( bv->bv_len + 1 ); + + ptr = lutil_strcopy( bv->bv_val, " dynacl/gss/" ); + ptr = lutil_strncopy( ptr, gssattr->gssattr_name.bv_val, + gssattr->gssattr_name.bv_len ); + switch ( gssattr->gssattr_style ) { + case ACL_STYLE_BASE: + ptr = lutil_strcopy( ptr, ".exact=" ); + break; + case ACL_STYLE_REGEX: + ptr = lutil_strcopy( ptr, ".regex=" ); + break; + case ACL_STYLE_EXPAND: + ptr = lutil_strcopy( ptr, ".expand=" ); + break; + default: + assert( 0 ); + break; + } + + ptr = lutil_strncopy( ptr, gssattr->gssattr_value.bv_val, + gssattr->gssattr_value.bv_len ); + + ptr[ 0 ] = '\0'; + + bv->bv_len = ptr - bv->bv_val; + + return 0; +} + +static int +gssattr_dynacl_mask( + void *priv, + Operation *op, + Entry *target, + AttributeDescription *desc, + struct berval *val, + int nmatch, + regmatch_t *matches, + slap_access_t *grant, + slap_access_t *deny ) +{ + gssattr_t *gssattr = (gssattr_t *)priv; + sasl_conn_t *sasl_ctx = op->o_conn->c_sasl_authctx; + gss_name_t gss_name = GSS_C_NO_NAME; + OM_uint32 major, minor; + int more = -1; + int authenticated, complete; + gss_buffer_desc attr = GSS_C_EMPTY_BUFFER; + int granted = 0; + + ACL_INVALIDATE( *deny ); + + if ( sasl_ctx == NULL || + sasl_getprop( sasl_ctx, SASL_GSS_PEER_NAME, (const void **)&gss_name) != 0 || + gss_name == GSS_C_NO_NAME ) { + return 0; + } + + attr.length = gssattr->gssattr_name.bv_len; + attr.value = gssattr->gssattr_name.bv_val; + + while ( more != 0 ) { + AclRegexMatches amatches = { 0 }; + gss_buffer_desc gss_value = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_display_value = GSS_C_EMPTY_BUFFER; + struct berval bv_value; + + major = gss_get_name_attribute( &minor, gss_name, &attr, + &authenticated, &complete, + &gss_value, &gss_display_value, &more ); + if ( GSS_ERROR( major ) ) { + break; + } else if ( authenticated == 0 ) { + gss_release_buffer( &minor, &gss_value ); + gss_release_buffer( &minor, &gss_display_value ); + continue; + } + + bv_value.bv_len = gss_value.length; + bv_value.bv_val = (char *)gss_value.value; + + if ( !ber_bvccmp( &gssattr->gssattr_value, '*' ) ) { + if ( gssattr->gssattr_style != ACL_STYLE_BASE ) { + amatches.dn_count = nmatch; + AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) ); + } + + switch ( gssattr->gssattr_style ) { + case ACL_STYLE_REGEX: + /* XXX assumes value NUL terminated */ + granted = regex_matches( &gssattr->gssattr_value, bv_value.bv_val, + &target->e_nname, val, &amatches ); + break; + case ACL_STYLE_EXPAND: { + struct berval bv; + char buf[ACL_BUF_SIZE]; + + bv.bv_len = sizeof( buf ) - 1; + bv.bv_val = buf; + + granted = ( acl_string_expand( &bv, &gssattr->gssattr_value, + &target->e_nname, val, + &amatches ) == 0 ) && + ( ber_bvstrcmp( &bv, &bv_value) == 0 ); + break; + } + case ACL_STYLE_BASE: + granted = ( ber_bvstrcmp( &gssattr->gssattr_value, &bv_value ) == 0 ); + break; + default: + assert(0); + break; + } + } else { + granted = 1; + } + + gss_release_buffer( &minor, &gss_value ); + gss_release_buffer( &minor, &gss_display_value ); + + if ( granted ) { + break; + } + } + + if ( granted ) { + ACL_LVL_ASSIGN_WRITE( *grant ); + } + + return 0; +} + +static int +gssattr_dynacl_destroy( + void *priv ) +{ + gssattr_t *gssattr = (gssattr_t *)priv; + + if ( gssattr != NULL ) { + if ( !BER_BVISNULL( &gssattr->gssattr_name ) ) { + ber_memfree( gssattr->gssattr_name.bv_val ); + } + if ( !BER_BVISNULL( &gssattr->gssattr_value ) ) { + ber_memfree( gssattr->gssattr_value.bv_val ); + } + ch_free( gssattr ); + } + + return 0; +} + +static struct slap_dynacl_t gssattr_dynacl = { + "gss", + gssattr_dynacl_parse, + gssattr_dynacl_unparse, + gssattr_dynacl_mask, + gssattr_dynacl_destroy +}; + +int +init_module( int argc, char *argv[] ) +{ + return slap_dynacl_register( &gssattr_dynacl ); +} + + +static int +regex_matches( + struct berval *pat, /* pattern to expand and match against */ + char *str, /* string to match against pattern */ + struct berval *dn_matches, /* buffer with $N expansion variables from DN */ + struct berval *val_matches, /* buffer with $N expansion variables from val */ + AclRegexMatches *matches /* offsets in buffer for $N expansion variables */ +) +{ + regex_t re; + char newbuf[ACL_BUF_SIZE]; + struct berval bv; + int rc; + + bv.bv_len = sizeof( newbuf ) - 1; + bv.bv_val = newbuf; + + if (str == NULL) { + str = ""; + }; + + acl_string_expand( &bv, pat, dn_matches, val_matches, matches ); + rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE ); + if ( rc ) { + char error[ACL_BUF_SIZE]; + regerror( rc, &re, error, sizeof( error ) ); + + Debug( LDAP_DEBUG_TRACE, + "compile( \"%s\", \"%s\") failed %s\n", + pat->bv_val, str, error ); + return( 0 ); + } + + rc = regexec( &re, str, 0, NULL, 0 ); + regfree( &re ); + + Debug( LDAP_DEBUG_TRACE, + "=> regex_matches: string: %s\n", str ); + Debug( LDAP_DEBUG_TRACE, + "=> regex_matches: rc: %d %s\n", + rc, !rc ? "matches" : "no matches" ); + return( !rc ); +} + diff --git a/contrib/slapd-modules/acl/posixgroup.c b/contrib/slapd-modules/acl/posixgroup.c new file mode 100644 index 0000000..9a9a5a8 --- /dev/null +++ b/contrib/slapd-modules/acl/posixgroup.c @@ -0,0 +1,329 @@ +/* posixgroup.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-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>. + */ + +#include <portable.h> + +#include <ac/string.h> +#include <slap.h> +#include <lutil.h> + +/* Need dynacl... */ + +#ifdef SLAP_DYNACL + +typedef struct pg_t { + slap_style_t pg_style; + struct berval pg_pat; +} pg_t; + +static ObjectClass *pg_posixGroup; +static AttributeDescription *pg_memberUid; +static ObjectClass *pg_posixAccount; +static AttributeDescription *pg_uidNumber; + +static int pg_dynacl_destroy( void *priv ); + +static int +pg_dynacl_parse( + const char *fname, + int lineno, + const char *opts, + slap_style_t style, + const char *pattern, + void **privp ) +{ + pg_t *pg; + int rc; + const char *text = NULL; + struct berval pat; + + ber_str2bv( pattern, 0, 0, &pat ); + + pg = ch_calloc( 1, sizeof( pg_t ) ); + + pg->pg_style = style; + + switch ( pg->pg_style ) { + case ACL_STYLE_BASE: + rc = dnNormalize( 0, NULL, NULL, &pat, &pg->pg_pat, NULL ); + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "%s line %d: posixGroup ACL: " + "unable to normalize DN \"%s\".\n", + fname, lineno, pattern ); + goto cleanup; + } + break; + + case ACL_STYLE_EXPAND: + ber_dupbv( &pg->pg_pat, &pat ); + break; + + default: + fprintf( stderr, "%s line %d: posixGroup ACL: " + "unsupported style \"%s\".\n", + fname, lineno, style_strings[ pg->pg_style ] ); + goto cleanup; + } + + /* TODO: use opts to allow the use of different + * group objects and member attributes */ + if ( pg_posixGroup == NULL ) { + pg_posixGroup = oc_find( "posixGroup" ); + if ( pg_posixGroup == NULL ) { + fprintf( stderr, "%s line %d: posixGroup ACL: " + "unable to lookup \"posixGroup\" " + "objectClass.\n", + fname, lineno ); + goto cleanup; + } + + pg_posixAccount = oc_find( "posixAccount" ); + if ( pg_posixGroup == NULL ) { + fprintf( stderr, "%s line %d: posixGroup ACL: " + "unable to lookup \"posixAccount\" " + "objectClass.\n", + fname, lineno ); + goto cleanup; + } + + rc = slap_str2ad( "memberUid", &pg_memberUid, &text ); + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "%s line %d: posixGroup ACL: " + "unable to lookup \"memberUid\" " + "attributeDescription (%d: %s).\n", + fname, lineno, rc, text ); + goto cleanup; + } + + rc = slap_str2ad( "uidNumber", &pg_uidNumber, &text ); + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "%s line %d: posixGroup ACL: " + "unable to lookup \"uidNumber\" " + "attributeDescription (%d: %s).\n", + fname, lineno, rc, text ); + goto cleanup; + } + } + + *privp = (void *)pg; + return 0; + +cleanup: + (void)pg_dynacl_destroy( (void *)pg ); + + return 1; +} + +static int +pg_dynacl_unparse( + void *priv, + struct berval *bv ) +{ + pg_t *pg = (pg_t *)priv; + char *ptr; + + bv->bv_len = STRLENOF( " dynacl/posixGroup.expand=" ) + pg->pg_pat.bv_len; + bv->bv_val = ch_malloc( bv->bv_len + 1 ); + + ptr = lutil_strcopy( bv->bv_val, " dynacl/posixGroup" ); + + switch ( pg->pg_style ) { + case ACL_STYLE_BASE: + ptr = lutil_strcopy( ptr, ".exact=" ); + break; + + case ACL_STYLE_EXPAND: + ptr = lutil_strcopy( ptr, ".expand=" ); + break; + + default: + assert( 0 ); + } + + ptr = lutil_strncopy( ptr, pg->pg_pat.bv_val, pg->pg_pat.bv_len ); + ptr[ 0 ] = '\0'; + + bv->bv_len = ptr - bv->bv_val; + + return 0; +} + +static int +pg_dynacl_mask( + void *priv, + Operation *op, + Entry *target, + AttributeDescription *desc, + struct berval *val, + int nmatch, + regmatch_t *matches, + slap_access_t *grant, + slap_access_t *deny ) +{ + pg_t *pg = (pg_t *)priv; + Entry *group = NULL, + *user = NULL; + int rc; + Backend *be = op->o_bd, + *group_be = NULL, + *user_be = NULL; + struct berval group_ndn; + + ACL_INVALIDATE( *deny ); + + /* get user */ + if ( target && dn_match( &target->e_nname, &op->o_ndn ) ) { + user = target; + rc = LDAP_SUCCESS; + + } else { + user_be = op->o_bd = select_backend( &op->o_ndn, 0 ); + if ( op->o_bd == NULL ) { + op->o_bd = be; + return 0; + } + rc = be_entry_get_rw( op, &op->o_ndn, pg_posixAccount, pg_uidNumber, 0, &user ); + } + + if ( rc != LDAP_SUCCESS || user == NULL ) { + op->o_bd = be; + return 0; + } + + /* get target */ + if ( pg->pg_style == ACL_STYLE_EXPAND ) { + char buf[ 1024 ]; + struct berval bv; + AclRegexMatches amatches = { 0 }; + + amatches.dn_count = nmatch; + AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) ); + + bv.bv_len = sizeof( buf ) - 1; + bv.bv_val = buf; + + if ( acl_string_expand( &bv, &pg->pg_pat, + &target->e_nname, + NULL, &amatches ) ) + { + goto cleanup; + } + + if ( dnNormalize( 0, NULL, NULL, &bv, &group_ndn, + op->o_tmpmemctx ) != LDAP_SUCCESS ) + { + /* did not expand to a valid dn */ + goto cleanup; + } + + } else { + group_ndn = pg->pg_pat; + } + + if ( target && dn_match( &target->e_nname, &group_ndn ) ) { + group = target; + rc = LDAP_SUCCESS; + + } else { + group_be = op->o_bd = select_backend( &group_ndn, 0 ); + if ( op->o_bd == NULL ) { + goto cleanup; + } + rc = be_entry_get_rw( op, &group_ndn, pg_posixGroup, pg_memberUid, 0, &group ); + } + + if ( group_ndn.bv_val != pg->pg_pat.bv_val ) { + op->o_tmpfree( group_ndn.bv_val, op->o_tmpmemctx ); + } + + if ( rc == LDAP_SUCCESS && group != NULL ) { + Attribute *a_uid, + *a_member; + + a_uid = attr_find( user->e_attrs, pg_uidNumber ); + if ( !a_uid || !BER_BVISNULL( &a_uid->a_nvals[ 1 ] ) ) { + rc = LDAP_NO_SUCH_ATTRIBUTE; + + } else { + a_member = attr_find( group->e_attrs, pg_memberUid ); + if ( !a_member ) { + rc = LDAP_NO_SUCH_ATTRIBUTE; + + } else { + rc = value_find_ex( pg_memberUid, + SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | + SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, + a_member->a_nvals, &a_uid->a_nvals[ 0 ], + op->o_tmpmemctx ); + } + } + + } else { + rc = LDAP_NO_SUCH_OBJECT; + } + + + if ( rc == LDAP_SUCCESS ) { + ACL_LVL_ASSIGN_WRITE( *grant ); + } + +cleanup:; + if ( group != NULL && group != target ) { + op->o_bd = group_be; + be_entry_release_r( op, group ); + op->o_bd = be; + } + + if ( user != NULL && user != target ) { + op->o_bd = user_be; + be_entry_release_r( op, user ); + op->o_bd = be; + } + + return 0; +} + +static int +pg_dynacl_destroy( + void *priv ) +{ + pg_t *pg = (pg_t *)priv; + + if ( pg != NULL ) { + if ( !BER_BVISNULL( &pg->pg_pat ) ) { + ber_memfree( pg->pg_pat.bv_val ); + } + ch_free( pg ); + } + + return 0; +} + +static struct slap_dynacl_t pg_dynacl = { + "posixGroup", + pg_dynacl_parse, + pg_dynacl_unparse, + pg_dynacl_mask, + pg_dynacl_destroy +}; + +int +init_module( int argc, char *argv[] ) +{ + return slap_dynacl_register( &pg_dynacl ); +} + +#endif /* SLAP_DYNACL */ |