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