xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/allowed/allowed.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: allowed.c,v 1.1.1.3 2014/05/28 09:58:27 tron Exp $	*/
2 
3 /* allowed.c - add allowed attributes based on ACL */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2006-2014 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  * NOTE: part of the schema definition reported below is taken
53  * from Microsoft schema definitions (OID, NAME, SYNTAX);
54  *
55  * EQUALITY is taken from
56  * <http://www.redhat.com/archives/fedora-directory-devel/2006-August/msg00007.html>
57  * (posted by Andrew Bartlett)
58  *
59  * The rest is guessed.  Specifically
60  *
61  * DESC briefly describes the purpose
62  *
63  * NO-USER-MODIFICATION is added to make attributes operational
64  *
65  * USAGE is set to "dSAOperation" as per ITS#7493,
66  * to prevent replication, since this information
67  * is generated (based on ACL and identity of request)
68  * and not stored.
69  */
70 
71 #define AA_SCHEMA_AT "1.2.840.113556.1.4"
72 
73 static AttributeDescription
74 		*ad_allowedChildClasses,
75 		*ad_allowedChildClassesEffective,
76 		*ad_allowedAttributes,
77 		*ad_allowedAttributesEffective;
78 
79 static struct {
80 	char *at;
81 	AttributeDescription **ad;
82 } aa_attrs[] = {
83 	{ "( " AA_SCHEMA_AT ".911 "
84 		"NAME 'allowedChildClasses' "
85 		"EQUALITY objectIdentifierMatch "
86 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
87 		/* added by me :) */
88 		"DESC 'Child classes allowed for a given object' "
89 		"NO-USER-MODIFICATION "
90 		"USAGE dSAOperation )", &ad_allowedChildClasses },
91 	{ "( " AA_SCHEMA_AT ".912 "
92 		"NAME 'allowedChildClassesEffective' "
93 		"EQUALITY objectIdentifierMatch "
94 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
95 		/* added by me :) */
96 		"DESC 'Child classes allowed for a given object according to ACLs' "
97 		"NO-USER-MODIFICATION "
98 		"USAGE dSAOperation )", &ad_allowedChildClassesEffective },
99 	{ "( " AA_SCHEMA_AT ".913 "
100 		"NAME 'allowedAttributes' "
101 		"EQUALITY objectIdentifierMatch "
102 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
103 		/* added by me :) */
104 		"DESC 'Attributes allowed for a given object' "
105 		"NO-USER-MODIFICATION "
106 		"USAGE dSAOperation )", &ad_allowedAttributes },
107 	{ "( " AA_SCHEMA_AT ".914 "
108 		"NAME 'allowedAttributesEffective' "
109 		"EQUALITY objectIdentifierMatch "
110 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
111 		/* added by me :) */
112 		"DESC 'Attributes allowed for a given object according to ACLs' "
113 		"NO-USER-MODIFICATION "
114 		"USAGE dSAOperation )", &ad_allowedAttributesEffective },
115 
116 	/* TODO: add objectClass stuff? */
117 
118 	{ NULL, NULL }
119 };
120 
121 static int
122 aa_add_at( AttributeType *at, AttributeType ***atpp )
123 {
124 	int		i = 0;
125 
126 	if ( *atpp ) {
127 		for ( i = 0; (*atpp)[ i ] != NULL; i++ ) {
128 			if ( (*atpp)[ i ] == at ) {
129 				break;
130 			}
131 		}
132 
133 		if ( (*atpp)[ i ] != NULL ) {
134 			return 0;
135 		}
136 	}
137 
138 	*atpp = ch_realloc( *atpp, sizeof( AttributeType * ) * ( i + 2 ) );
139 	(*atpp)[ i ] = at;
140 	(*atpp)[ i + 1 ] = NULL;
141 
142 	return 0;
143 }
144 
145 static int
146 aa_add_oc( ObjectClass *oc, ObjectClass ***ocpp, AttributeType ***atpp )
147 {
148 	int		i = 0;
149 
150 	if ( *ocpp ) {
151 		for ( ; (*ocpp)[ i ] != NULL; i++ ) {
152 			if ( (*ocpp)[ i ] == oc ) {
153 				break;
154 			}
155 		}
156 
157 		if ( (*ocpp)[ i ] != NULL ) {
158 			return 0;
159 		}
160 	}
161 
162 	*ocpp = ch_realloc( *ocpp, sizeof( ObjectClass * ) * ( i + 2 ) );
163 	(*ocpp)[ i ] = oc;
164 	(*ocpp)[ i + 1 ] = NULL;
165 
166 	if ( oc->soc_required ) {
167 		int		i;
168 
169 		for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) {
170 			aa_add_at( oc->soc_required[ i ], atpp );
171 		}
172 	}
173 
174 	if ( oc->soc_allowed ) {
175 		int		i;
176 
177 		for ( i = 0; oc->soc_allowed[ i ] != NULL; i++ ) {
178 			aa_add_at( oc->soc_allowed[ i ], atpp );
179 		}
180 	}
181 
182 	return 0;
183 }
184 
185 static int
186 aa_operational( Operation *op, SlapReply *rs )
187 {
188 	Attribute		*a, **ap;
189 	AccessControlState	acl_state = ACL_STATE_INIT;
190 	struct berval		*v;
191 	AttributeType		**atp = NULL;
192 	ObjectClass		**ocp = NULL;
193 
194 #define	GOT_NONE	(0x0U)
195 #define	GOT_C		(0x1U)
196 #define	GOT_CE		(0x2U)
197 #define	GOT_A		(0x4U)
198 #define	GOT_AE		(0x8U)
199 #define	GOT_ALL		(GOT_C|GOT_CE|GOT_A|GOT_AE)
200 	int		got = GOT_NONE;
201 
202 	/* only add if requested */
203 	if ( SLAP_OPATTRS( rs->sr_attr_flags ) ) {
204 		got = GOT_ALL;
205 
206 	} else {
207 		if ( ad_inlist( ad_allowedChildClasses, rs->sr_attrs ) ) {
208 			got |= GOT_C;
209 		}
210 
211 		if ( ad_inlist( ad_allowedChildClassesEffective, rs->sr_attrs ) ) {
212 			got |= GOT_CE;
213 		}
214 
215 		if ( ad_inlist( ad_allowedAttributes, rs->sr_attrs ) ) {
216 			got |= GOT_A;
217 		}
218 
219 		if ( ad_inlist( ad_allowedAttributesEffective, rs->sr_attrs ) ) {
220 			got |= GOT_AE;
221 		}
222 	}
223 
224 	if ( got == GOT_NONE ) {
225 		return SLAP_CB_CONTINUE;
226 	}
227 
228 	/* shouldn't be called without an entry; please check */
229 	assert( rs->sr_entry != NULL );
230 
231 	for ( ap = &rs->sr_operational_attrs; *ap != NULL; ap = &(*ap)->a_next )
232 		/* go to last */ ;
233 
234 	/* see caveats; this is not guaranteed for all backends */
235 	a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass );
236 	if ( a == NULL ) {
237 		goto do_oc;
238 	}
239 
240 	/* if client has no access to objectClass attribute; don't compute */
241 	if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_objectClass,
242 				NULL, ACL_READ, &acl_state ) )
243 	{
244 		return SLAP_CB_CONTINUE;
245 	}
246 
247 	for ( v = a->a_nvals; !BER_BVISNULL( v ); v++ ) {
248 		ObjectClass	*oc = oc_bvfind( v );
249 
250 		assert( oc != NULL );
251 
252 		/* if client has no access to specific value, don't compute */
253 		if ( !access_allowed( op, rs->sr_entry,
254 			slap_schema.si_ad_objectClass,
255 			&oc->soc_cname, ACL_READ, &acl_state ) )
256 		{
257 			continue;
258 		}
259 
260 		aa_add_oc( oc, &ocp, &atp );
261 
262 		if ( oc->soc_sups ) {
263 			int i;
264 
265 			for ( i = 0; oc->soc_sups[ i ] != NULL; i++ ) {
266 				aa_add_oc( oc->soc_sups[ i ], &ocp, &atp );
267 			}
268 		}
269 	}
270 
271 	ch_free( ocp );
272 
273 	if ( atp != NULL ) {
274 		BerVarray	bv_allowed = NULL,
275 				bv_effective = NULL;
276 		int		i, ja = 0, je = 0;
277 
278 		for ( i = 0; atp[ i ] != NULL; i++ )
279 			/* just count */ ;
280 
281 		if ( got & GOT_A ) {
282 			bv_allowed = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
283 		}
284 		if ( got & GOT_AE ) {
285 			bv_effective = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
286 		}
287 
288 		for ( i = 0, ja = 0, je = 0; atp[ i ] != NULL; i++ ) {
289 			if ( got & GOT_A ) {
290 				ber_dupbv( &bv_allowed[ ja ], &atp[ i ]->sat_cname );
291 				ja++;
292 			}
293 
294 			if ( got & GOT_AE ) {
295 				AttributeDescription	*ad = NULL;
296 				const char		*text = NULL;
297 
298 				if ( slap_bv2ad( &atp[ i ]->sat_cname, &ad, &text ) ) {
299 					/* log? */
300 					continue;
301 				}
302 
303 				if ( access_allowed( op, rs->sr_entry,
304 					ad, NULL, ACL_WRITE, NULL ) )
305 				{
306 					ber_dupbv( &bv_effective[ je ], &atp[ i ]->sat_cname );
307 					je++;
308 				}
309 			}
310 		}
311 
312 		ch_free( atp );
313 
314 		if ( ( got & GOT_A ) && ja > 0 ) {
315 			BER_BVZERO( &bv_allowed[ ja ] );
316 			*ap = attr_alloc( ad_allowedAttributes );
317 			(*ap)->a_vals = bv_allowed;
318 			(*ap)->a_nvals = bv_allowed;
319 			(*ap)->a_numvals = ja;
320 			ap = &(*ap)->a_next;
321 		}
322 
323 		if ( ( got & GOT_AE ) && je > 0 ) {
324 			BER_BVZERO( &bv_effective[ je ] );
325 			*ap = attr_alloc( ad_allowedAttributesEffective );
326 			(*ap)->a_vals = bv_effective;
327 			(*ap)->a_nvals = bv_effective;
328 			(*ap)->a_numvals = je;
329 			ap = &(*ap)->a_next;
330 		}
331 
332 		*ap = NULL;
333 	}
334 
335 do_oc:;
336 	if ( ( got & GOT_C ) || ( got & GOT_CE ) ) {
337 		BerVarray	bv_allowed = NULL,
338 				bv_effective = NULL;
339 		int		i, ja = 0, je = 0;
340 
341 		ObjectClass	*oc;
342 
343 		for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) {
344 			/* we can only add AUXILIARY objectClasses */
345 			if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
346 				continue;
347 			}
348 
349 			i++;
350 		}
351 
352 		if ( got & GOT_C ) {
353 			bv_allowed = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
354 		}
355 		if ( got & GOT_CE ) {
356 			bv_effective = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
357 		}
358 
359 		for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) {
360 			/* we can only add AUXILIARY objectClasses */
361 			if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
362 				continue;
363 			}
364 
365 			if ( got & GOT_C ) {
366 				ber_dupbv( &bv_allowed[ ja ], &oc->soc_cname );
367 				ja++;
368 			}
369 
370 			if ( got & GOT_CE ) {
371 				if ( !access_allowed( op, rs->sr_entry,
372 					slap_schema.si_ad_objectClass,
373 					&oc->soc_cname, ACL_WRITE, NULL ) )
374 				{
375 					goto done_ce;
376 				}
377 
378 				if ( oc->soc_required ) {
379 					for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) {
380 						AttributeDescription	*ad = NULL;
381 						const char		*text = NULL;
382 
383 						if ( slap_bv2ad( &oc->soc_required[ i ]->sat_cname, &ad, &text ) ) {
384 							/* log? */
385 							continue;
386 						}
387 
388 						if ( !access_allowed( op, rs->sr_entry,
389 							ad, NULL, ACL_WRITE, NULL ) )
390 						{
391 							goto done_ce;
392 						}
393 					}
394 				}
395 
396 				ber_dupbv( &bv_effective[ je ], &oc->soc_cname );
397 				je++;
398 			}
399 done_ce:;
400 		}
401 
402 		if ( ( got & GOT_C ) && ja > 0 ) {
403 			BER_BVZERO( &bv_allowed[ ja ] );
404 			*ap = attr_alloc( ad_allowedChildClasses );
405 			(*ap)->a_vals = bv_allowed;
406 			(*ap)->a_nvals = bv_allowed;
407 			(*ap)->a_numvals = ja;
408 			ap = &(*ap)->a_next;
409 		}
410 
411 		if ( ( got & GOT_CE ) && je > 0 ) {
412 			BER_BVZERO( &bv_effective[ je ] );
413 			*ap = attr_alloc( ad_allowedChildClassesEffective );
414 			(*ap)->a_vals = bv_effective;
415 			(*ap)->a_nvals = bv_effective;
416 			(*ap)->a_numvals = je;
417 			ap = &(*ap)->a_next;
418 		}
419 
420 		*ap = NULL;
421 	}
422 
423 	return SLAP_CB_CONTINUE;
424 }
425 
426 static slap_overinst aa;
427 
428 #if LDAP_VENDOR_VERSION_MINOR != X && LDAP_VENDOR_VERSION_MINOR <= 3
429 /* backport register_at() from HEAD, to allow building with OL <= 2.3 */
430 static int
431 register_at( char *def, AttributeDescription **rad, int dupok )
432 {
433 	LDAPAttributeType *at;
434 	int code, freeit = 0;
435 	const char *err;
436 	AttributeDescription *ad = NULL;
437 
438 	at = ldap_str2attributetype( def, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
439 	if ( !at ) {
440 		Debug( LDAP_DEBUG_ANY,
441 			"register_at: AttributeType \"%s\": %s, %s\n",
442 				def, ldap_scherr2str(code), err );
443 		return code;
444 	}
445 
446 	code = at_add( at, 0, NULL, &err );
447 	if ( code ) {
448 		if ( code == SLAP_SCHERR_ATTR_DUP && dupok ) {
449 			freeit = 1;
450 
451 		} else {
452 			ldap_attributetype_free( at );
453 			Debug( LDAP_DEBUG_ANY,
454 				"register_at: AttributeType \"%s\": %s, %s\n",
455 				def, scherr2str(code), err );
456 			return code;
457 		}
458 	}
459 	code = slap_str2ad( at->at_names[0], &ad, &err );
460 	if ( freeit || code ) {
461 		ldap_attributetype_free( at );
462 	} else {
463 		ldap_memfree( at );
464 	}
465 	if ( code ) {
466 		Debug( LDAP_DEBUG_ANY, "register_at: AttributeType \"%s\": %s\n",
467 			def, err, 0 );
468 	}
469 	if ( rad ) *rad = ad;
470 	return code;
471 }
472 #endif
473 
474 #if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC
475 static
476 #endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */
477 int
478 aa_initialize( void )
479 {
480 	int i;
481 
482 	aa.on_bi.bi_type = "allowed";
483 
484 	aa.on_bi.bi_operational = aa_operational;
485 
486 	/* aa schema integration */
487 	for ( i = 0; aa_attrs[i].at; i++ ) {
488 		int code;
489 
490 		code = register_at( aa_attrs[i].at, aa_attrs[i].ad, 0 );
491 		if ( code ) {
492 			Debug( LDAP_DEBUG_ANY,
493 				"aa_initialize: register_at failed\n", 0, 0, 0 );
494 			return -1;
495 		}
496 	}
497 
498 	return overlay_register( &aa );
499 }
500 
501 #if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC
502 int
503 init_module( int argc, char *argv[] )
504 {
505 	return aa_initialize();
506 }
507 #endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */
508 
509 #endif /* SLAPD_OVER_ALLOWED */
510