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