/* This work is part of OpenLDAP Software . * * 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 * . */ #include #include #include #include #include #include #include #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 ); }