xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/allowed/allowed.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: allowed.c,v 1.1.1.2 2010/12/12 15:18:55 adam Exp $	*/
2 
3 /* allowed.c - add allowed attributes based on ACL */
4 /* OpenLDAP: pkg/ldap/contrib/slapd-modules/allowed/allowed.c,v 1.3.2.4 2010/04/15 20:35:22 quanah Exp */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2006-2010 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by Pierangelo Masarati for inclusion in
20  * OpenLDAP Software.
21  */
22 
23 /*
24  * Rationale: return in allowedAttributes the attributes required/allowed
25  * by the objectClasses that are currently present in an object; return
26  * in allowedAttributesEffective the subset of the above that can be written
27  * by the identity that performs the search.
28  *
29  * Caveats:
30  * - right now, the overlay assumes that all values of the objectClass
31  *   attribute will be returned in rs->sr_entry; this may not be true
32  *   in general, but it usually is for back-bdb/back-hdb.  To generalize,
33  *   the search request should be analyzed, and if allowedAttributes or
34  *   allowedAttributesEffective are requested, add objectClass to the
35  *   requested attributes
36  * - it assumes that there is no difference between write-add and
37  *   write-delete
38  * - it assumes that access rules do not depend on the values of the
39  *   attributes or on the contents of the entry (attr/val, filter, ...)
40  *   allowedAttributes and allowedAttributesEffective cannot be used
41  *   in filters or in compare
42  */
43 
44 #include "portable.h"
45 
46 /* define SLAPD_OVER_ALLOWED=2 to build as run-time loadable module */
47 #ifdef SLAPD_OVER_ALLOWED
48 
49 #include "slap.h"
50 
51 /*
52  * Schema from
53  *
54  * <http://www.redhat.com/archives/fedora-directory-devel/2006-August/msg00007.html>
55  *
56  * posted by Andrew Bartlett
57  */
58 
59 #define AA_SCHEMA_AT "1.2.840.113556.1.4"
60 
61 static AttributeDescription
62 		*ad_allowedChildClasses,
63 		*ad_allowedChildClassesEffective,
64 		*ad_allowedAttributes,
65 		*ad_allowedAttributesEffective;
66 
67 static struct {
68 	char *at;
69 	AttributeDescription **ad;
70 } aa_attrs[] = {
71 	{ "( " AA_SCHEMA_AT ".911 "
72 		"NAME 'allowedChildClasses' "
73 		"EQUALITY objectIdentifierMatch "
74 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
75 		/* added by me :) */
76 		"DESC 'Child classes allowed for a given object' "
77 		"NO-USER-MODIFICATION "
78 		"USAGE directoryOperation )", &ad_allowedChildClasses },
79 	{ "( " AA_SCHEMA_AT ".912 "
80 		"NAME 'allowedChildClassesEffective' "
81 		"EQUALITY objectIdentifierMatch "
82 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
83 		/* added by me :) */
84 		"DESC 'Child classes allowed for a given object according to ACLs' "
85 		"NO-USER-MODIFICATION "
86 		"USAGE directoryOperation )", &ad_allowedChildClassesEffective },
87 	{ "( " AA_SCHEMA_AT ".913 "
88 		"NAME 'allowedAttributes' "
89 		"EQUALITY objectIdentifierMatch "
90 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
91 		/* added by me :) */
92 		"DESC 'Attributes allowed for a given object' "
93 		"NO-USER-MODIFICATION "
94 		"USAGE directoryOperation )", &ad_allowedAttributes },
95 	{ "( " AA_SCHEMA_AT ".914 "
96 		"NAME 'allowedAttributesEffective' "
97 		"EQUALITY objectIdentifierMatch "
98 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
99 		/* added by me :) */
100 		"DESC 'Attributes allowed for a given object according to ACLs' "
101 		"NO-USER-MODIFICATION "
102 		"USAGE directoryOperation )", &ad_allowedAttributesEffective },
103 
104 	/* TODO: add objectClass stuff? */
105 
106 	{ NULL, NULL }
107 };
108 
109 static int
110 aa_add_at( AttributeType *at, AttributeType ***atpp )
111 {
112 	int		i = 0;
113 
114 	if ( *atpp ) {
115 		for ( i = 0; (*atpp)[ i ] != NULL; i++ ) {
116 			if ( (*atpp)[ i ] == at ) {
117 				break;
118 			}
119 		}
120 
121 		if ( (*atpp)[ i ] != NULL ) {
122 			return 0;
123 		}
124 	}
125 
126 	*atpp = ch_realloc( *atpp, sizeof( AttributeType * ) * ( i + 2 ) );
127 	(*atpp)[ i ] = at;
128 	(*atpp)[ i + 1 ] = NULL;
129 
130 	return 0;
131 }
132 
133 static int
134 aa_add_oc( ObjectClass *oc, ObjectClass ***ocpp, AttributeType ***atpp )
135 {
136 	int		i = 0;
137 
138 	if ( *ocpp ) {
139 		for ( ; (*ocpp)[ i ] != NULL; i++ ) {
140 			if ( (*ocpp)[ i ] == oc ) {
141 				break;
142 			}
143 		}
144 
145 		if ( (*ocpp)[ i ] != NULL ) {
146 			return 0;
147 		}
148 	}
149 
150 	*ocpp = ch_realloc( *ocpp, sizeof( ObjectClass * ) * ( i + 2 ) );
151 	(*ocpp)[ i ] = oc;
152 	(*ocpp)[ i + 1 ] = NULL;
153 
154 	if ( oc->soc_required ) {
155 		int		i;
156 
157 		for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) {
158 			aa_add_at( oc->soc_required[ i ], atpp );
159 		}
160 	}
161 
162 	if ( oc->soc_allowed ) {
163 		int		i;
164 
165 		for ( i = 0; oc->soc_allowed[ i ] != NULL; i++ ) {
166 			aa_add_at( oc->soc_allowed[ i ], atpp );
167 		}
168 	}
169 
170 	return 0;
171 }
172 
173 static int
174 aa_operational( Operation *op, SlapReply *rs )
175 {
176 	Attribute		*a, **ap;
177 	AccessControlState	acl_state = ACL_STATE_INIT;
178 	struct berval		*v;
179 	AttributeType		**atp = NULL;
180 	ObjectClass		**ocp = NULL;
181 
182 #define	GOT_NONE	(0x0U)
183 #define	GOT_C		(0x1U)
184 #define	GOT_CE		(0x2U)
185 #define	GOT_A		(0x4U)
186 #define	GOT_AE		(0x8U)
187 #define	GOT_ALL		(GOT_C|GOT_CE|GOT_A|GOT_AE)
188 	int		got = GOT_NONE;
189 
190 	/* only add if requested */
191 	if ( SLAP_OPATTRS( rs->sr_attr_flags ) ) {
192 		got = GOT_ALL;
193 
194 	} else {
195 		if ( ad_inlist( ad_allowedChildClasses, rs->sr_attrs ) ) {
196 			got |= GOT_C;
197 		}
198 
199 		if ( ad_inlist( ad_allowedChildClassesEffective, rs->sr_attrs ) ) {
200 			got |= GOT_CE;
201 		}
202 
203 		if ( ad_inlist( ad_allowedAttributes, rs->sr_attrs ) ) {
204 			got |= GOT_A;
205 		}
206 
207 		if ( ad_inlist( ad_allowedAttributesEffective, rs->sr_attrs ) ) {
208 			got |= GOT_AE;
209 		}
210 	}
211 
212 	if ( got == GOT_NONE ) {
213 		return SLAP_CB_CONTINUE;
214 	}
215 
216 	/* shouldn't be called without an entry; please check */
217 	assert( rs->sr_entry != NULL );
218 
219 	for ( ap = &rs->sr_operational_attrs; *ap != NULL; ap = &(*ap)->a_next )
220 		/* go to last */ ;
221 
222 	/* see caveats; this is not guaranteed for all backends */
223 	a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass );
224 	if ( a == NULL ) {
225 		goto do_oc;
226 	}
227 
228 	/* if client has no access to objectClass attribute; don't compute */
229 	if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_objectClass,
230 				NULL, ACL_READ, &acl_state ) )
231 	{
232 		return SLAP_CB_CONTINUE;
233 	}
234 
235 	for ( v = a->a_nvals; !BER_BVISNULL( v ); v++ ) {
236 		ObjectClass	*oc = oc_bvfind( v );
237 
238 		assert( oc != NULL );
239 
240 		/* if client has no access to specific value, don't compute */
241 		if ( !access_allowed( op, rs->sr_entry,
242 			slap_schema.si_ad_objectClass,
243 			&oc->soc_cname, ACL_READ, &acl_state ) )
244 		{
245 			continue;
246 		}
247 
248 		aa_add_oc( oc, &ocp, &atp );
249 
250 		if ( oc->soc_sups ) {
251 			int i;
252 
253 			for ( i = 0; oc->soc_sups[ i ] != NULL; i++ ) {
254 				aa_add_oc( oc->soc_sups[ i ], &ocp, &atp );
255 			}
256 		}
257 	}
258 
259 	ch_free( ocp );
260 
261 	if ( atp != NULL ) {
262 		BerVarray	bv_allowed = NULL,
263 				bv_effective = NULL;
264 		int		i, ja = 0, je = 0;
265 
266 		for ( i = 0; atp[ i ] != NULL; i++ )
267 			/* just count */ ;
268 
269 		if ( got & GOT_A ) {
270 			bv_allowed = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
271 		}
272 		if ( got & GOT_AE ) {
273 			bv_effective = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
274 		}
275 
276 		for ( i = 0, ja = 0, je = 0; atp[ i ] != NULL; i++ ) {
277 			if ( got & GOT_A ) {
278 				ber_dupbv( &bv_allowed[ ja ], &atp[ i ]->sat_cname );
279 				ja++;
280 			}
281 
282 			if ( got & GOT_AE ) {
283 				AttributeDescription	*ad = NULL;
284 				const char		*text = NULL;
285 
286 				if ( slap_bv2ad( &atp[ i ]->sat_cname, &ad, &text ) ) {
287 					/* log? */
288 					continue;
289 				}
290 
291 				if ( access_allowed( op, rs->sr_entry,
292 					ad, NULL, ACL_WRITE, NULL ) )
293 				{
294 					ber_dupbv( &bv_effective[ je ], &atp[ i ]->sat_cname );
295 					je++;
296 				}
297 			}
298 		}
299 
300 		ch_free( atp );
301 
302 		if ( ( got & GOT_A ) && ja > 0 ) {
303 			BER_BVZERO( &bv_allowed[ ja ] );
304 			*ap = attr_alloc( ad_allowedAttributes );
305 			(*ap)->a_vals = bv_allowed;
306 			(*ap)->a_nvals = bv_allowed;
307 			(*ap)->a_numvals = ja;
308 			ap = &(*ap)->a_next;
309 		}
310 
311 		if ( ( got & GOT_AE ) && je > 0 ) {
312 			BER_BVZERO( &bv_effective[ je ] );
313 			*ap = attr_alloc( ad_allowedAttributesEffective );
314 			(*ap)->a_vals = bv_effective;
315 			(*ap)->a_nvals = bv_effective;
316 			(*ap)->a_numvals = je;
317 			ap = &(*ap)->a_next;
318 		}
319 
320 		*ap = NULL;
321 	}
322 
323 do_oc:;
324 	if ( ( got & GOT_C ) || ( got & GOT_CE ) ) {
325 		BerVarray	bv_allowed = NULL,
326 				bv_effective = NULL;
327 		int		i, ja = 0, je = 0;
328 
329 		ObjectClass	*oc;
330 
331 		for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) {
332 			/* we can only add AUXILIARY objectClasses */
333 			if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
334 				continue;
335 			}
336 
337 			i++;
338 		}
339 
340 		if ( got & GOT_C ) {
341 			bv_allowed = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
342 		}
343 		if ( got & GOT_CE ) {
344 			bv_effective = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
345 		}
346 
347 		for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) {
348 			/* we can only add AUXILIARY objectClasses */
349 			if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
350 				continue;
351 			}
352 
353 			if ( got & GOT_C ) {
354 				ber_dupbv( &bv_allowed[ ja ], &oc->soc_cname );
355 				ja++;
356 			}
357 
358 			if ( got & GOT_CE ) {
359 				if ( !access_allowed( op, rs->sr_entry,
360 					slap_schema.si_ad_objectClass,
361 					&oc->soc_cname, ACL_WRITE, NULL ) )
362 				{
363 					goto done_ce;
364 				}
365 
366 				if ( oc->soc_required ) {
367 					for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) {
368 						AttributeDescription	*ad = NULL;
369 						const char		*text = NULL;
370 
371 						if ( slap_bv2ad( &oc->soc_required[ i ]->sat_cname, &ad, &text ) ) {
372 							/* log? */
373 							continue;
374 						}
375 
376 						if ( !access_allowed( op, rs->sr_entry,
377 							ad, NULL, ACL_WRITE, NULL ) )
378 						{
379 							goto done_ce;
380 						}
381 					}
382 				}
383 
384 				ber_dupbv( &bv_effective[ je ], &oc->soc_cname );
385 				je++;
386 			}
387 done_ce:;
388 		}
389 
390 		if ( ( got & GOT_C ) && ja > 0 ) {
391 			BER_BVZERO( &bv_allowed[ ja ] );
392 			*ap = attr_alloc( ad_allowedChildClasses );
393 			(*ap)->a_vals = bv_allowed;
394 			(*ap)->a_nvals = bv_allowed;
395 			(*ap)->a_numvals = ja;
396 			ap = &(*ap)->a_next;
397 		}
398 
399 		if ( ( got & GOT_CE ) && je > 0 ) {
400 			BER_BVZERO( &bv_effective[ je ] );
401 			*ap = attr_alloc( ad_allowedChildClassesEffective );
402 			(*ap)->a_vals = bv_effective;
403 			(*ap)->a_nvals = bv_effective;
404 			(*ap)->a_numvals = je;
405 			ap = &(*ap)->a_next;
406 		}
407 
408 		*ap = NULL;
409 	}
410 
411 	return SLAP_CB_CONTINUE;
412 }
413 
414 static slap_overinst aa;
415 
416 #if LDAP_VENDOR_VERSION_MINOR != X && LDAP_VENDOR_VERSION_MINOR <= 3
417 /* backport register_at() from HEAD, to allow building with OL <= 2.3 */
418 static int
419 register_at( char *def, AttributeDescription **rad, int dupok )
420 {
421 	LDAPAttributeType *at;
422 	int code, freeit = 0;
423 	const char *err;
424 	AttributeDescription *ad = NULL;
425 
426 	at = ldap_str2attributetype( def, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
427 	if ( !at ) {
428 		Debug( LDAP_DEBUG_ANY,
429 			"register_at: AttributeType \"%s\": %s, %s\n",
430 				def, ldap_scherr2str(code), err );
431 		return code;
432 	}
433 
434 	code = at_add( at, 0, NULL, &err );
435 	if ( code ) {
436 		if ( code == SLAP_SCHERR_ATTR_DUP && dupok ) {
437 			freeit = 1;
438 
439 		} else {
440 			ldap_attributetype_free( at );
441 			Debug( LDAP_DEBUG_ANY,
442 				"register_at: AttributeType \"%s\": %s, %s\n",
443 				def, scherr2str(code), err );
444 			return code;
445 		}
446 	}
447 	code = slap_str2ad( at->at_names[0], &ad, &err );
448 	if ( freeit || code ) {
449 		ldap_attributetype_free( at );
450 	} else {
451 		ldap_memfree( at );
452 	}
453 	if ( code ) {
454 		Debug( LDAP_DEBUG_ANY, "register_at: AttributeType \"%s\": %s\n",
455 			def, err, 0 );
456 	}
457 	if ( rad ) *rad = ad;
458 	return code;
459 }
460 #endif
461 
462 #if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC
463 static
464 #endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */
465 int
466 aa_initialize( void )
467 {
468 	int i;
469 
470 	aa.on_bi.bi_type = "allowed";
471 
472 	aa.on_bi.bi_operational = aa_operational;
473 
474 	/* aa schema integration */
475 	for ( i = 0; aa_attrs[i].at; i++ ) {
476 		int code;
477 
478 		code = register_at( aa_attrs[i].at, aa_attrs[i].ad, 0 );
479 		if ( code ) {
480 			Debug( LDAP_DEBUG_ANY,
481 				"aa_initialize: register_at failed\n", 0, 0, 0 );
482 			return -1;
483 		}
484 	}
485 
486 	return overlay_register( &aa );
487 }
488 
489 #if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC
490 int
491 init_module( int argc, char *argv[] )
492 {
493 	return aa_initialize();
494 }
495 #endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */
496 
497 #endif /* SLAPD_OVER_ALLOWED */
498