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
gssattr_dynacl_parse(const char * fname,int lineno,const char * opts,slap_style_t style,const char * pattern,void ** privp)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
gssattr_dynacl_unparse(void * priv,struct berval * bv)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
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)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
gssattr_dynacl_destroy(void * priv)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
init_module(int argc,char * argv[])269 init_module( int argc, char *argv[] )
270 {
271 return slap_dynacl_register( &gssattr_dynacl );
272 }
273
274
275 static int
regex_matches(struct berval * pat,char * str,struct berval * dn_matches,struct berval * val_matches,AclRegexMatches * matches)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