xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/acl/gssacl.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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