1 /* $NetBSD: allowed.c,v 1.1.1.5 2018/02/06 01:53:06 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-2017 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 <sys/cdefs.h> 45 __RCSID("$NetBSD: allowed.c,v 1.1.1.5 2018/02/06 01:53:06 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 = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) ); 286 } 287 if ( got & GOT_AE ) { 288 bv_effective = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) ); 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 BER_BVZERO( &bv_allowed[ ja ] ); 319 *ap = attr_alloc( ad_allowedAttributes ); 320 (*ap)->a_vals = bv_allowed; 321 (*ap)->a_nvals = bv_allowed; 322 (*ap)->a_numvals = ja; 323 ap = &(*ap)->a_next; 324 } 325 326 if ( ( got & GOT_AE ) && je > 0 ) { 327 BER_BVZERO( &bv_effective[ je ] ); 328 *ap = attr_alloc( ad_allowedAttributesEffective ); 329 (*ap)->a_vals = bv_effective; 330 (*ap)->a_nvals = bv_effective; 331 (*ap)->a_numvals = je; 332 ap = &(*ap)->a_next; 333 } 334 335 *ap = NULL; 336 } 337 338 do_oc:; 339 if ( ( got & GOT_C ) || ( got & GOT_CE ) ) { 340 BerVarray bv_allowed = NULL, 341 bv_effective = NULL; 342 int i, ja = 0, je = 0; 343 344 ObjectClass *oc; 345 346 for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) { 347 /* we can only add AUXILIARY objectClasses */ 348 if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) { 349 continue; 350 } 351 352 i++; 353 } 354 355 if ( got & GOT_C ) { 356 bv_allowed = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) ); 357 } 358 if ( got & GOT_CE ) { 359 bv_effective = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) ); 360 } 361 362 for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) { 363 /* we can only add AUXILIARY objectClasses */ 364 if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) { 365 continue; 366 } 367 368 if ( got & GOT_C ) { 369 ber_dupbv( &bv_allowed[ ja ], &oc->soc_cname ); 370 ja++; 371 } 372 373 if ( got & GOT_CE ) { 374 if ( !access_allowed( op, rs->sr_entry, 375 slap_schema.si_ad_objectClass, 376 &oc->soc_cname, ACL_WRITE, NULL ) ) 377 { 378 goto done_ce; 379 } 380 381 if ( oc->soc_required ) { 382 for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) { 383 AttributeDescription *ad = NULL; 384 const char *text = NULL; 385 386 if ( slap_bv2ad( &oc->soc_required[ i ]->sat_cname, &ad, &text ) ) { 387 /* log? */ 388 continue; 389 } 390 391 if ( !access_allowed( op, rs->sr_entry, 392 ad, NULL, ACL_WRITE, NULL ) ) 393 { 394 goto done_ce; 395 } 396 } 397 } 398 399 ber_dupbv( &bv_effective[ je ], &oc->soc_cname ); 400 je++; 401 } 402 done_ce:; 403 } 404 405 if ( ( got & GOT_C ) && ja > 0 ) { 406 BER_BVZERO( &bv_allowed[ ja ] ); 407 *ap = attr_alloc( ad_allowedChildClasses ); 408 (*ap)->a_vals = bv_allowed; 409 (*ap)->a_nvals = bv_allowed; 410 (*ap)->a_numvals = ja; 411 ap = &(*ap)->a_next; 412 } 413 414 if ( ( got & GOT_CE ) && je > 0 ) { 415 BER_BVZERO( &bv_effective[ je ] ); 416 *ap = attr_alloc( ad_allowedChildClassesEffective ); 417 (*ap)->a_vals = bv_effective; 418 (*ap)->a_nvals = bv_effective; 419 (*ap)->a_numvals = je; 420 ap = &(*ap)->a_next; 421 } 422 423 *ap = NULL; 424 } 425 426 return SLAP_CB_CONTINUE; 427 } 428 429 static slap_overinst aa; 430 431 #if LDAP_VENDOR_VERSION_MINOR != X && LDAP_VENDOR_VERSION_MINOR <= 3 432 /* backport register_at() from HEAD, to allow building with OL <= 2.3 */ 433 static int 434 register_at( char *def, AttributeDescription **rad, int dupok ) 435 { 436 LDAPAttributeType *at; 437 int code, freeit = 0; 438 const char *err; 439 AttributeDescription *ad = NULL; 440 441 at = ldap_str2attributetype( def, &code, &err, LDAP_SCHEMA_ALLOW_ALL ); 442 if ( !at ) { 443 Debug( LDAP_DEBUG_ANY, 444 "register_at: AttributeType \"%s\": %s, %s\n", 445 def, ldap_scherr2str(code), err ); 446 return code; 447 } 448 449 code = at_add( at, 0, NULL, &err ); 450 if ( code ) { 451 if ( code == SLAP_SCHERR_ATTR_DUP && dupok ) { 452 freeit = 1; 453 454 } else { 455 ldap_attributetype_free( at ); 456 Debug( LDAP_DEBUG_ANY, 457 "register_at: AttributeType \"%s\": %s, %s\n", 458 def, scherr2str(code), err ); 459 return code; 460 } 461 } 462 code = slap_str2ad( at->at_names[0], &ad, &err ); 463 if ( freeit || code ) { 464 ldap_attributetype_free( at ); 465 } else { 466 ldap_memfree( at ); 467 } 468 if ( code ) { 469 Debug( LDAP_DEBUG_ANY, "register_at: AttributeType \"%s\": %s\n", 470 def, err, 0 ); 471 } 472 if ( rad ) *rad = ad; 473 return code; 474 } 475 #endif 476 477 #if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC 478 static 479 #endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */ 480 int 481 aa_initialize( void ) 482 { 483 int i; 484 485 aa.on_bi.bi_type = "allowed"; 486 487 aa.on_bi.bi_operational = aa_operational; 488 489 /* aa schema integration */ 490 for ( i = 0; aa_attrs[i].at; i++ ) { 491 int code; 492 493 code = register_at( aa_attrs[i].at, aa_attrs[i].ad, 0 ); 494 if ( code ) { 495 Debug( LDAP_DEBUG_ANY, 496 "aa_initialize: register_at failed\n", 0, 0, 0 ); 497 return -1; 498 } 499 } 500 501 return overlay_register( &aa ); 502 } 503 504 #if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC 505 int 506 init_module( int argc, char *argv[] ) 507 { 508 return aa_initialize(); 509 } 510 #endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */ 511 512 #endif /* SLAPD_OVER_ALLOWED */ 513