]> git.sur5r.net Git - openldap/commitdiff
Add GSS naming extensions ACL plugin
authorLuke Howard <lukeh@padl.com>
Mon, 9 May 2011 08:24:42 +0000 (10:24 +0200)
committerLuke Howard <lukeh@padl.com>
Mon, 9 May 2011 11:55:40 +0000 (13:55 +0200)
contrib/slapd-modules/acl/README.gssacl [new file with mode: 0644]
contrib/slapd-modules/acl/gssacl.c [new file with mode: 0644]

diff --git a/contrib/slapd-modules/acl/README.gssacl b/contrib/slapd-modules/acl/README.gssacl
new file mode 100644 (file)
index 0000000..5f0ded6
--- /dev/null
@@ -0,0 +1,33 @@
+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.
+
+No Makefile is provided. Use a command line similar to:
+
+gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \
+       -o acl-gssacl.so gssacl.c
+
+to compile the gssacl ACL plugin.
+
+---
+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/gssacl.c b/contrib/slapd-modules/acl/gssacl.c
new file mode 100644 (file)
index 0000000..2fba789
--- /dev/null
@@ -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, 0, 0 );
+       Debug( LDAP_DEBUG_TRACE,
+           "=> regex_matches: rc: %d %s\n",
+               rc, !rc ? "matches" : "no matches", 0 );
+       return( !rc );
+}
+