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 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 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 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 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 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 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