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