1 /* $NetBSD: gssacl.c,v 1.2 2021/08/14 16:14:50 christos Exp $ */ 2 3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 2011 PADL Software Pty Ltd. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted only as authorized by the OpenLDAP 10 * Public License. 11 * 12 * A copy of this license is available in the file LICENSE in the 13 * top-level directory of the distribution or, alternatively, at 14 * <http://www.OpenLDAP.org/license.html>. 15 */ 16 17 #include <portable.h> 18 19 #include <ac/string.h> 20 #include <slap.h> 21 #include <lutil.h> 22 23 #include <sasl/sasl.h> 24 #include <gssapi/gssapi.h> 25 #include <gssapi/gssapi_ext.h> 26 27 #define ACL_BUF_SIZE 1024 28 29 typedef struct gssattr_t { 30 slap_style_t gssattr_style; 31 struct berval gssattr_name; /* asserted name */ 32 struct berval gssattr_value; /* asserted value */ 33 } gssattr_t; 34 35 static int gssattr_dynacl_destroy( void *priv ); 36 37 static int 38 regex_matches( 39 struct berval *pat, /* pattern to expand and match against */ 40 char *str, /* string to match against pattern */ 41 struct berval *dn_matches, /* buffer with $N expansion variables from DN */ 42 struct berval *val_matches, /* buffer with $N expansion variables from val */ 43 AclRegexMatches *matches /* offsets in buffer for $N expansion variables */ 44 ); 45 46 static int 47 gssattr_dynacl_parse( 48 const char *fname, 49 int lineno, 50 const char *opts, 51 slap_style_t style, 52 const char *pattern, 53 void **privp ) 54 { 55 gssattr_t *gssattr; 56 57 gssattr = (gssattr_t *)ch_calloc( 1, sizeof( gssattr_t ) ); 58 59 if ( opts == NULL || opts[0] == '\0' ) { 60 fprintf( stderr, "%s line %d: GSS ACL: no attribute specified.\n", 61 fname, lineno ); 62 goto cleanup; 63 } 64 65 if ( pattern == NULL || pattern[0] == '\0' ) { 66 fprintf( stderr, "%s line %d: GSS ACL: no attribute value specified.\n", 67 fname, lineno ); 68 goto cleanup; 69 } 70 71 gssattr->gssattr_style = style; 72 73 switch ( gssattr->gssattr_style ) { 74 case ACL_STYLE_BASE: 75 case ACL_STYLE_REGEX: 76 case ACL_STYLE_EXPAND: 77 break; 78 default: 79 fprintf( stderr, "%s line %d: GSS ACL: unsupported style \"%s\".\n", 80 fname, lineno, style_strings[style] ); 81 goto cleanup; 82 break; 83 } 84 85 ber_str2bv( opts, 0, 1, &gssattr->gssattr_name ); 86 ber_str2bv( pattern, 0, 1, &gssattr->gssattr_value ); 87 88 *privp = (void *)gssattr; 89 return 0; 90 91 cleanup: 92 (void)gssattr_dynacl_destroy( (void *)gssattr ); 93 94 return 1; 95 } 96 97 static int 98 gssattr_dynacl_unparse( 99 void *priv, 100 struct berval *bv ) 101 { 102 gssattr_t *gssattr = (gssattr_t *)priv; 103 char *ptr; 104 105 bv->bv_len = STRLENOF( " dynacl/gss/.expand=" ) + 106 gssattr->gssattr_name.bv_len + 107 gssattr->gssattr_value.bv_len; 108 bv->bv_val = ch_malloc( bv->bv_len + 1 ); 109 110 ptr = lutil_strcopy( bv->bv_val, " dynacl/gss/" ); 111 ptr = lutil_strncopy( ptr, gssattr->gssattr_name.bv_val, 112 gssattr->gssattr_name.bv_len ); 113 switch ( gssattr->gssattr_style ) { 114 case ACL_STYLE_BASE: 115 ptr = lutil_strcopy( ptr, ".exact=" ); 116 break; 117 case ACL_STYLE_REGEX: 118 ptr = lutil_strcopy( ptr, ".regex=" ); 119 break; 120 case ACL_STYLE_EXPAND: 121 ptr = lutil_strcopy( ptr, ".expand=" ); 122 break; 123 default: 124 assert( 0 ); 125 break; 126 } 127 128 ptr = lutil_strncopy( ptr, gssattr->gssattr_value.bv_val, 129 gssattr->gssattr_value.bv_len ); 130 131 ptr[ 0 ] = '\0'; 132 133 bv->bv_len = ptr - bv->bv_val; 134 135 return 0; 136 } 137 138 static int 139 gssattr_dynacl_mask( 140 void *priv, 141 Operation *op, 142 Entry *target, 143 AttributeDescription *desc, 144 struct berval *val, 145 int nmatch, 146 regmatch_t *matches, 147 slap_access_t *grant, 148 slap_access_t *deny ) 149 { 150 gssattr_t *gssattr = (gssattr_t *)priv; 151 sasl_conn_t *sasl_ctx = op->o_conn->c_sasl_authctx; 152 gss_name_t gss_name = GSS_C_NO_NAME; 153 OM_uint32 major, minor; 154 int more = -1; 155 int authenticated, complete; 156 gss_buffer_desc attr = GSS_C_EMPTY_BUFFER; 157 int granted = 0; 158 159 ACL_INVALIDATE( *deny ); 160 161 if ( sasl_ctx == NULL || 162 sasl_getprop( sasl_ctx, SASL_GSS_PEER_NAME, (const void **)&gss_name) != 0 || 163 gss_name == GSS_C_NO_NAME ) { 164 return 0; 165 } 166 167 attr.length = gssattr->gssattr_name.bv_len; 168 attr.value = gssattr->gssattr_name.bv_val; 169 170 while ( more != 0 ) { 171 AclRegexMatches amatches = { 0 }; 172 gss_buffer_desc gss_value = GSS_C_EMPTY_BUFFER; 173 gss_buffer_desc gss_display_value = GSS_C_EMPTY_BUFFER; 174 struct berval bv_value; 175 176 major = gss_get_name_attribute( &minor, gss_name, &attr, 177 &authenticated, &complete, 178 &gss_value, &gss_display_value, &more ); 179 if ( GSS_ERROR( major ) ) { 180 break; 181 } else if ( authenticated == 0 ) { 182 gss_release_buffer( &minor, &gss_value ); 183 gss_release_buffer( &minor, &gss_display_value ); 184 continue; 185 } 186 187 bv_value.bv_len = gss_value.length; 188 bv_value.bv_val = (char *)gss_value.value; 189 190 if ( !ber_bvccmp( &gssattr->gssattr_value, '*' ) ) { 191 if ( gssattr->gssattr_style != ACL_STYLE_BASE ) { 192 amatches.dn_count = nmatch; 193 AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) ); 194 } 195 196 switch ( gssattr->gssattr_style ) { 197 case ACL_STYLE_REGEX: 198 /* XXX assumes value NUL terminated */ 199 granted = regex_matches( &gssattr->gssattr_value, bv_value.bv_val, 200 &target->e_nname, val, &amatches ); 201 break; 202 case ACL_STYLE_EXPAND: { 203 struct berval bv; 204 char buf[ACL_BUF_SIZE]; 205 206 bv.bv_len = sizeof( buf ) - 1; 207 bv.bv_val = buf; 208 209 granted = ( acl_string_expand( &bv, &gssattr->gssattr_value, 210 &target->e_nname, val, 211 &amatches ) == 0 ) && 212 ( ber_bvstrcmp( &bv, &bv_value) == 0 ); 213 break; 214 } 215 case ACL_STYLE_BASE: 216 granted = ( ber_bvstrcmp( &gssattr->gssattr_value, &bv_value ) == 0 ); 217 break; 218 default: 219 assert(0); 220 break; 221 } 222 } else { 223 granted = 1; 224 } 225 226 gss_release_buffer( &minor, &gss_value ); 227 gss_release_buffer( &minor, &gss_display_value ); 228 229 if ( granted ) { 230 break; 231 } 232 } 233 234 if ( granted ) { 235 ACL_LVL_ASSIGN_WRITE( *grant ); 236 } 237 238 return 0; 239 } 240 241 static int 242 gssattr_dynacl_destroy( 243 void *priv ) 244 { 245 gssattr_t *gssattr = (gssattr_t *)priv; 246 247 if ( gssattr != NULL ) { 248 if ( !BER_BVISNULL( &gssattr->gssattr_name ) ) { 249 ber_memfree( gssattr->gssattr_name.bv_val ); 250 } 251 if ( !BER_BVISNULL( &gssattr->gssattr_value ) ) { 252 ber_memfree( gssattr->gssattr_value.bv_val ); 253 } 254 ch_free( gssattr ); 255 } 256 257 return 0; 258 } 259 260 static struct slap_dynacl_t gssattr_dynacl = { 261 "gss", 262 gssattr_dynacl_parse, 263 gssattr_dynacl_unparse, 264 gssattr_dynacl_mask, 265 gssattr_dynacl_destroy 266 }; 267 268 int 269 init_module( int argc, char *argv[] ) 270 { 271 return slap_dynacl_register( &gssattr_dynacl ); 272 } 273 274 275 static int 276 regex_matches( 277 struct berval *pat, /* pattern to expand and match against */ 278 char *str, /* string to match against pattern */ 279 struct berval *dn_matches, /* buffer with $N expansion variables from DN */ 280 struct berval *val_matches, /* buffer with $N expansion variables from val */ 281 AclRegexMatches *matches /* offsets in buffer for $N expansion variables */ 282 ) 283 { 284 regex_t re; 285 char newbuf[ACL_BUF_SIZE]; 286 struct berval bv; 287 int rc; 288 289 bv.bv_len = sizeof( newbuf ) - 1; 290 bv.bv_val = newbuf; 291 292 if (str == NULL) { 293 str = ""; 294 }; 295 296 acl_string_expand( &bv, pat, dn_matches, val_matches, matches ); 297 rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE ); 298 if ( rc ) { 299 char error[ACL_BUF_SIZE]; 300 regerror( rc, &re, error, sizeof( error ) ); 301 302 Debug( LDAP_DEBUG_TRACE, 303 "compile( \"%s\", \"%s\") failed %s\n", 304 pat->bv_val, str, error ); 305 return( 0 ); 306 } 307 308 rc = regexec( &re, str, 0, NULL, 0 ); 309 regfree( &re ); 310 311 Debug( LDAP_DEBUG_TRACE, 312 "=> regex_matches: string: %s\n", str ); 313 Debug( LDAP_DEBUG_TRACE, 314 "=> regex_matches: rc: %d %s\n", 315 rc, !rc ? "matches" : "no matches" ); 316 return( !rc ); 317 } 318 319