1/* allowed.c - add allowed attributes based on ACL */ 2/* $OpenLDAP$ */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 2006-2011 The OpenLDAP Foundation. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted only as authorized by the OpenLDAP 10 * Public License. 11 * 12 * A copy of this license is available in the file LICENSE in the 13 * top-level directory of the distribution or, alternatively, at 14 * <http://www.OpenLDAP.org/license.html>. 15 */ 16/* ACKNOWLEDGEMENTS: 17 * This work was initially developed by Pierangelo Masarati for inclusion in 18 * OpenLDAP Software. 19 */ 20 21/* 22 * Rationale: return in allowedAttributes the attributes required/allowed 23 * by the objectClasses that are currently present in an object; return 24 * in allowedAttributesEffective the subset of the above that can be written 25 * by the identity that performs the search. 26 * 27 * Caveats: 28 * - right now, the overlay assumes that all values of the objectClass 29 * attribute will be returned in rs->sr_entry; this may not be true 30 * in general, but it usually is for back-bdb/back-hdb. To generalize, 31 * the search request should be analyzed, and if allowedAttributes or 32 * allowedAttributesEffective are requested, add objectClass to the 33 * requested attributes 34 * - it assumes that there is no difference between write-add and 35 * write-delete 36 * - it assumes that access rules do not depend on the values of the 37 * attributes or on the contents of the entry (attr/val, filter, ...) 38 * allowedAttributes and allowedAttributesEffective cannot be used 39 * in filters or in compare 40 */ 41 42#include "portable.h" 43 44/* define SLAPD_OVER_ALLOWED=2 to build as run-time loadable module */ 45#ifdef SLAPD_OVER_ALLOWED 46 47#include "slap.h" 48 49/* 50 * Schema from 51 * 52 * <http://www.redhat.com/archives/fedora-directory-devel/2006-August/msg00007.html> 53 * 54 * posted by Andrew Bartlett 55 */ 56 57#define AA_SCHEMA_AT "1.2.840.113556.1.4" 58 59static AttributeDescription 60 *ad_allowedChildClasses, 61 *ad_allowedChildClassesEffective, 62 *ad_allowedAttributes, 63 *ad_allowedAttributesEffective; 64 65static struct { 66 char *at; 67 AttributeDescription **ad; 68} aa_attrs[] = { 69 { "( " AA_SCHEMA_AT ".911 " 70 "NAME 'allowedChildClasses' " 71 "EQUALITY objectIdentifierMatch " 72 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 " 73 /* added by me :) */ 74 "DESC 'Child classes allowed for a given object' " 75 "NO-USER-MODIFICATION " 76 "USAGE directoryOperation )", &ad_allowedChildClasses }, 77 { "( " AA_SCHEMA_AT ".912 " 78 "NAME 'allowedChildClassesEffective' " 79 "EQUALITY objectIdentifierMatch " 80 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 " 81 /* added by me :) */ 82 "DESC 'Child classes allowed for a given object according to ACLs' " 83 "NO-USER-MODIFICATION " 84 "USAGE directoryOperation )", &ad_allowedChildClassesEffective }, 85 { "( " AA_SCHEMA_AT ".913 " 86 "NAME 'allowedAttributes' " 87 "EQUALITY objectIdentifierMatch " 88 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 " 89 /* added by me :) */ 90 "DESC 'Attributes allowed for a given object' " 91 "NO-USER-MODIFICATION " 92 "USAGE directoryOperation )", &ad_allowedAttributes }, 93 { "( " AA_SCHEMA_AT ".914 " 94 "NAME 'allowedAttributesEffective' " 95 "EQUALITY objectIdentifierMatch " 96 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 " 97 /* added by me :) */ 98 "DESC 'Attributes allowed for a given object according to ACLs' " 99 "NO-USER-MODIFICATION " 100 "USAGE directoryOperation )", &ad_allowedAttributesEffective }, 101 102 /* TODO: add objectClass stuff? */ 103 104 { NULL, NULL } 105}; 106 107static int 108aa_add_at( AttributeType *at, AttributeType ***atpp ) 109{ 110 int i = 0; 111 112 if ( *atpp ) { 113 for ( i = 0; (*atpp)[ i ] != NULL; i++ ) { 114 if ( (*atpp)[ i ] == at ) { 115 break; 116 } 117 } 118 119 if ( (*atpp)[ i ] != NULL ) { 120 return 0; 121 } 122 } 123 124 *atpp = ch_realloc( *atpp, sizeof( AttributeType * ) * ( i + 2 ) ); 125 (*atpp)[ i ] = at; 126 (*atpp)[ i + 1 ] = NULL; 127 128 return 0; 129} 130 131static int 132aa_add_oc( ObjectClass *oc, ObjectClass ***ocpp, AttributeType ***atpp ) 133{ 134 int i = 0; 135 136 if ( *ocpp ) { 137 for ( ; (*ocpp)[ i ] != NULL; i++ ) { 138 if ( (*ocpp)[ i ] == oc ) { 139 break; 140 } 141 } 142 143 if ( (*ocpp)[ i ] != NULL ) { 144 return 0; 145 } 146 } 147 148 *ocpp = ch_realloc( *ocpp, sizeof( ObjectClass * ) * ( i + 2 ) ); 149 (*ocpp)[ i ] = oc; 150 (*ocpp)[ i + 1 ] = NULL; 151 152 if ( oc->soc_required ) { 153 int i; 154 155 for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) { 156 aa_add_at( oc->soc_required[ i ], atpp ); 157 } 158 } 159 160 if ( oc->soc_allowed ) { 161 int i; 162 163 for ( i = 0; oc->soc_allowed[ i ] != NULL; i++ ) { 164 aa_add_at( oc->soc_allowed[ i ], atpp ); 165 } 166 } 167 168 return 0; 169} 170 171static int 172aa_operational( Operation *op, SlapReply *rs ) 173{ 174 Attribute *a, **ap; 175 AccessControlState acl_state = ACL_STATE_INIT; 176 struct berval *v; 177 AttributeType **atp = NULL; 178 ObjectClass **ocp = NULL; 179 180#define GOT_NONE (0x0U) 181#define GOT_C (0x1U) 182#define GOT_CE (0x2U) 183#define GOT_A (0x4U) 184#define GOT_AE (0x8U) 185#define GOT_ALL (GOT_C|GOT_CE|GOT_A|GOT_AE) 186 int got = GOT_NONE; 187 188 /* only add if requested */ 189 if ( SLAP_OPATTRS( rs->sr_attr_flags ) ) { 190 got = GOT_ALL; 191 192 } else { 193 if ( ad_inlist( ad_allowedChildClasses, rs->sr_attrs ) ) { 194 got |= GOT_C; 195 } 196 197 if ( ad_inlist( ad_allowedChildClassesEffective, rs->sr_attrs ) ) { 198 got |= GOT_CE; 199 } 200 201 if ( ad_inlist( ad_allowedAttributes, rs->sr_attrs ) ) { 202 got |= GOT_A; 203 } 204 205 if ( ad_inlist( ad_allowedAttributesEffective, rs->sr_attrs ) ) { 206 got |= GOT_AE; 207 } 208 } 209 210 if ( got == GOT_NONE ) { 211 return SLAP_CB_CONTINUE; 212 } 213 214 /* shouldn't be called without an entry; please check */ 215 assert( rs->sr_entry != NULL ); 216 217 for ( ap = &rs->sr_operational_attrs; *ap != NULL; ap = &(*ap)->a_next ) 218 /* go to last */ ; 219 220 /* see caveats; this is not guaranteed for all backends */ 221 a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass ); 222 if ( a == NULL ) { 223 goto do_oc; 224 } 225 226 /* if client has no access to objectClass attribute; don't compute */ 227 if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_objectClass, 228 NULL, ACL_READ, &acl_state ) ) 229 { 230 return SLAP_CB_CONTINUE; 231 } 232 233 for ( v = a->a_nvals; !BER_BVISNULL( v ); v++ ) { 234 ObjectClass *oc = oc_bvfind( v ); 235 236 assert( oc != NULL ); 237 238 /* if client has no access to specific value, don't compute */ 239 if ( !access_allowed( op, rs->sr_entry, 240 slap_schema.si_ad_objectClass, 241 &oc->soc_cname, ACL_READ, &acl_state ) ) 242 { 243 continue; 244 } 245 246 aa_add_oc( oc, &ocp, &atp ); 247 248 if ( oc->soc_sups ) { 249 int i; 250 251 for ( i = 0; oc->soc_sups[ i ] != NULL; i++ ) { 252 aa_add_oc( oc->soc_sups[ i ], &ocp, &atp ); 253 } 254 } 255 } 256 257 ch_free( ocp ); 258 259 if ( atp != NULL ) { 260 BerVarray bv_allowed = NULL, 261 bv_effective = NULL; 262 int i, ja = 0, je = 0; 263 264 for ( i = 0; atp[ i ] != NULL; i++ ) 265 /* just count */ ; 266 267 if ( got & GOT_A ) { 268 bv_allowed = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) ); 269 } 270 if ( got & GOT_AE ) { 271 bv_effective = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) ); 272 } 273 274 for ( i = 0, ja = 0, je = 0; atp[ i ] != NULL; i++ ) { 275 if ( got & GOT_A ) { 276 ber_dupbv( &bv_allowed[ ja ], &atp[ i ]->sat_cname ); 277 ja++; 278 } 279 280 if ( got & GOT_AE ) { 281 AttributeDescription *ad = NULL; 282 const char *text = NULL; 283 284 if ( slap_bv2ad( &atp[ i ]->sat_cname, &ad, &text ) ) { 285 /* log? */ 286 continue; 287 } 288 289 if ( access_allowed( op, rs->sr_entry, 290 ad, NULL, ACL_WRITE, NULL ) ) 291 { 292 ber_dupbv( &bv_effective[ je ], &atp[ i ]->sat_cname ); 293 je++; 294 } 295 } 296 } 297 298 ch_free( atp ); 299 300 if ( ( got & GOT_A ) && ja > 0 ) { 301 BER_BVZERO( &bv_allowed[ ja ] ); 302 *ap = attr_alloc( ad_allowedAttributes ); 303 (*ap)->a_vals = bv_allowed; 304 (*ap)->a_nvals = bv_allowed; 305 (*ap)->a_numvals = ja; 306 ap = &(*ap)->a_next; 307 } 308 309 if ( ( got & GOT_AE ) && je > 0 ) { 310 BER_BVZERO( &bv_effective[ je ] ); 311 *ap = attr_alloc( ad_allowedAttributesEffective ); 312 (*ap)->a_vals = bv_effective; 313 (*ap)->a_nvals = bv_effective; 314 (*ap)->a_numvals = je; 315 ap = &(*ap)->a_next; 316 } 317 318 *ap = NULL; 319 } 320 321do_oc:; 322 if ( ( got & GOT_C ) || ( got & GOT_CE ) ) { 323 BerVarray bv_allowed = NULL, 324 bv_effective = NULL; 325 int i, ja = 0, je = 0; 326 327 ObjectClass *oc; 328 329 for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) { 330 /* we can only add AUXILIARY objectClasses */ 331 if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) { 332 continue; 333 } 334 335 i++; 336 } 337 338 if ( got & GOT_C ) { 339 bv_allowed = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) ); 340 } 341 if ( got & GOT_CE ) { 342 bv_effective = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) ); 343 } 344 345 for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) { 346 /* we can only add AUXILIARY objectClasses */ 347 if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) { 348 continue; 349 } 350 351 if ( got & GOT_C ) { 352 ber_dupbv( &bv_allowed[ ja ], &oc->soc_cname ); 353 ja++; 354 } 355 356 if ( got & GOT_CE ) { 357 if ( !access_allowed( op, rs->sr_entry, 358 slap_schema.si_ad_objectClass, 359 &oc->soc_cname, ACL_WRITE, NULL ) ) 360 { 361 goto done_ce; 362 } 363 364 if ( oc->soc_required ) { 365 for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) { 366 AttributeDescription *ad = NULL; 367 const char *text = NULL; 368 369 if ( slap_bv2ad( &oc->soc_required[ i ]->sat_cname, &ad, &text ) ) { 370 /* log? */ 371 continue; 372 } 373 374 if ( !access_allowed( op, rs->sr_entry, 375 ad, NULL, ACL_WRITE, NULL ) ) 376 { 377 goto done_ce; 378 } 379 } 380 } 381 382 ber_dupbv( &bv_effective[ je ], &oc->soc_cname ); 383 je++; 384 } 385done_ce:; 386 } 387 388 if ( ( got & GOT_C ) && ja > 0 ) { 389 BER_BVZERO( &bv_allowed[ ja ] ); 390 *ap = attr_alloc( ad_allowedChildClasses ); 391 (*ap)->a_vals = bv_allowed; 392 (*ap)->a_nvals = bv_allowed; 393 (*ap)->a_numvals = ja; 394 ap = &(*ap)->a_next; 395 } 396 397 if ( ( got & GOT_CE ) && je > 0 ) { 398 BER_BVZERO( &bv_effective[ je ] ); 399 *ap = attr_alloc( ad_allowedChildClassesEffective ); 400 (*ap)->a_vals = bv_effective; 401 (*ap)->a_nvals = bv_effective; 402 (*ap)->a_numvals = je; 403 ap = &(*ap)->a_next; 404 } 405 406 *ap = NULL; 407 } 408 409 return SLAP_CB_CONTINUE; 410} 411 412static slap_overinst aa; 413 414#if LDAP_VENDOR_VERSION_MINOR != X && LDAP_VENDOR_VERSION_MINOR <= 3 415/* backport register_at() from HEAD, to allow building with OL <= 2.3 */ 416static int 417register_at( char *def, AttributeDescription **rad, int dupok ) 418{ 419 LDAPAttributeType *at; 420 int code, freeit = 0; 421 const char *err; 422 AttributeDescription *ad = NULL; 423 424 at = ldap_str2attributetype( def, &code, &err, LDAP_SCHEMA_ALLOW_ALL ); 425 if ( !at ) { 426 Debug( LDAP_DEBUG_ANY, 427 "register_at: AttributeType \"%s\": %s, %s\n", 428 def, ldap_scherr2str(code), err ); 429 return code; 430 } 431 432 code = at_add( at, 0, NULL, &err ); 433 if ( code ) { 434 if ( code == SLAP_SCHERR_ATTR_DUP && dupok ) { 435 freeit = 1; 436 437 } else { 438 ldap_attributetype_free( at ); 439 Debug( LDAP_DEBUG_ANY, 440 "register_at: AttributeType \"%s\": %s, %s\n", 441 def, scherr2str(code), err ); 442 return code; 443 } 444 } 445 code = slap_str2ad( at->at_names[0], &ad, &err ); 446 if ( freeit || code ) { 447 ldap_attributetype_free( at ); 448 } else { 449 ldap_memfree( at ); 450 } 451 if ( code ) { 452 Debug( LDAP_DEBUG_ANY, "register_at: AttributeType \"%s\": %s\n", 453 def, err, 0 ); 454 } 455 if ( rad ) *rad = ad; 456 return code; 457} 458#endif 459 460#if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC 461static 462#endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */ 463int 464aa_initialize( void ) 465{ 466 int i; 467 468 aa.on_bi.bi_type = "allowed"; 469 470 aa.on_bi.bi_operational = aa_operational; 471 472 /* aa schema integration */ 473 for ( i = 0; aa_attrs[i].at; i++ ) { 474 int code; 475 476 code = register_at( aa_attrs[i].at, aa_attrs[i].ad, 0 ); 477 if ( code ) { 478 Debug( LDAP_DEBUG_ANY, 479 "aa_initialize: register_at failed\n", 0, 0, 0 ); 480 return -1; 481 } 482 } 483 484 return overlay_register( &aa ); 485} 486 487#if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC 488int 489init_module( int argc, char *argv[] ) 490{ 491 return aa_initialize(); 492} 493#endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */ 494 495#endif /* SLAPD_OVER_ALLOWED */ 496