1/* aci.c - routines to parse and check acl's */ 2/* $OpenLDAP$ */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 1998-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/* Portions Copyright (c) 1995 Regents of the University of Michigan. 17 * All rights reserved. 18 * 19 * Redistribution and use in source and binary forms are permitted 20 * provided that this notice is preserved and that due credit is given 21 * to the University of Michigan at Ann Arbor. The name of the University 22 * may not be used to endorse or promote products derived from this 23 * software without specific prior written permission. This software 24 * is provided ``as is'' without express or implied warranty. 25 */ 26 27#include "portable.h" 28 29#ifdef SLAPD_ACI_ENABLED 30 31#include <stdio.h> 32 33#include <ac/ctype.h> 34#include <ac/regex.h> 35#include <ac/socket.h> 36#include <ac/string.h> 37#include <ac/unistd.h> 38 39#include "slap.h" 40#include "lber_pvt.h" 41#include "lutil.h" 42 43/* use most appropriate size */ 44#define ACI_BUF_SIZE 1024 45 46/* move to "stable" when no longer experimental */ 47#define SLAPD_ACI_SYNTAX "1.3.6.1.4.1.4203.666.2.1" 48 49/* change this to "OpenLDAPset" */ 50#define SLAPD_ACI_SET_ATTR "template" 51 52typedef enum slap_aci_scope_t { 53 SLAP_ACI_SCOPE_ENTRY = 0x1, 54 SLAP_ACI_SCOPE_CHILDREN = 0x2, 55 SLAP_ACI_SCOPE_SUBTREE = ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN ) 56} slap_aci_scope_t; 57 58enum { 59 ACI_BV_ENTRY, 60 ACI_BV_CHILDREN, 61 ACI_BV_ONELEVEL, 62 ACI_BV_SUBTREE, 63 64 ACI_BV_BR_ENTRY, 65 ACI_BV_BR_CHILDREN, 66 ACI_BV_BR_ALL, 67 68 ACI_BV_ACCESS_ID, 69 ACI_BV_PUBLIC, 70 ACI_BV_USERS, 71 ACI_BV_SELF, 72 ACI_BV_DNATTR, 73 ACI_BV_GROUP, 74 ACI_BV_ROLE, 75 ACI_BV_SET, 76 ACI_BV_SET_REF, 77 78 ACI_BV_GRANT, 79 ACI_BV_DENY, 80 81 ACI_BV_GROUP_CLASS, 82 ACI_BV_GROUP_ATTR, 83 ACI_BV_ROLE_CLASS, 84 ACI_BV_ROLE_ATTR, 85 86 ACI_BV_SET_ATTR, 87 88 ACI_BV_LAST 89}; 90 91static const struct berval aci_bv[] = { 92 /* scope */ 93 BER_BVC("entry"), 94 BER_BVC("children"), 95 BER_BVC("onelevel"), 96 BER_BVC("subtree"), 97 98 /* */ 99 BER_BVC("[entry]"), 100 BER_BVC("[children]"), 101 BER_BVC("[all]"), 102 103 /* type */ 104 BER_BVC("access-id"), 105 BER_BVC("public"), 106 BER_BVC("users"), 107 BER_BVC("self"), 108 BER_BVC("dnattr"), 109 BER_BVC("group"), 110 BER_BVC("role"), 111 BER_BVC("set"), 112 BER_BVC("set-ref"), 113 114 /* actions */ 115 BER_BVC("grant"), 116 BER_BVC("deny"), 117 118 /* schema */ 119 BER_BVC(SLAPD_GROUP_CLASS), 120 BER_BVC(SLAPD_GROUP_ATTR), 121 BER_BVC(SLAPD_ROLE_CLASS), 122 BER_BVC(SLAPD_ROLE_ATTR), 123 124 BER_BVC(SLAPD_ACI_SET_ATTR), 125 126 BER_BVNULL 127}; 128 129static AttributeDescription *slap_ad_aci; 130 131static int 132OpenLDAPaciValidate( 133 Syntax *syntax, 134 struct berval *val ); 135 136static int 137OpenLDAPaciPretty( 138 Syntax *syntax, 139 struct berval *val, 140 struct berval *out, 141 void *ctx ); 142 143static int 144OpenLDAPaciNormalize( 145 slap_mask_t use, 146 Syntax *syntax, 147 MatchingRule *mr, 148 struct berval *val, 149 struct berval *out, 150 void *ctx ); 151 152#define OpenLDAPaciMatch octetStringMatch 153 154static int 155aci_list_map_rights( 156 struct berval *list ) 157{ 158 struct berval bv; 159 slap_access_t mask; 160 int i; 161 162 ACL_INIT( mask ); 163 for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) { 164 if ( bv.bv_len <= 0 ) { 165 continue; 166 } 167 168 switch ( *bv.bv_val ) { 169 case 'x': 170 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 171 * define any equivalent to the AUTH right, so I've just used 172 * 'x' for now. 173 */ 174 ACL_PRIV_SET(mask, ACL_PRIV_AUTH); 175 break; 176 case 'd': 177 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines 178 * the right 'd' to mean "delete"; we hijack it to mean 179 * "disclose" for consistency wuith the rest of slapd. 180 */ 181 ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE); 182 break; 183 case 'c': 184 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE); 185 break; 186 case 's': 187 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines 188 * the right 's' to mean "set", but in the examples states 189 * that the right 's' means "search". The latter definition 190 * is used here. 191 */ 192 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH); 193 break; 194 case 'r': 195 ACL_PRIV_SET(mask, ACL_PRIV_READ); 196 break; 197 case 'w': 198 ACL_PRIV_SET(mask, ACL_PRIV_WRITE); 199 break; 200 default: 201 break; 202 } 203 204 } 205 206 return mask; 207} 208 209static int 210aci_list_has_attr( 211 struct berval *list, 212 const struct berval *attr, 213 struct berval *val ) 214{ 215 struct berval bv, left, right; 216 int i; 217 218 for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) { 219 if ( acl_get_part(&bv, 0, '=', &left ) < 0 220 || acl_get_part( &bv, 1, '=', &right ) < 0 ) 221 { 222 if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) { 223 return(1); 224 } 225 226 } else if ( val == NULL ) { 227 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) { 228 return(1); 229 } 230 231 } else { 232 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) { 233 /* FIXME: this is also totally undocumented! */ 234 /* this is experimental code that implements a 235 * simple (prefix) match of the attribute value. 236 * the ACI draft does not provide for aci's that 237 * apply to specific values, but it would be 238 * nice to have. If the <attr> part of an aci's 239 * rights list is of the form <attr>=<value>, 240 * that means the aci applies only to attrs with 241 * the given value. Furthermore, if the attr is 242 * of the form <attr>=<value>*, then <value> is 243 * treated as a prefix, and the aci applies to 244 * any value with that prefix. 245 * 246 * Ideally, this would allow r.e. matches. 247 */ 248 if ( acl_get_part( &right, 0, '*', &left ) < 0 249 || right.bv_len <= left.bv_len ) 250 { 251 if ( ber_bvstrcasecmp( val, &right ) == 0 ) { 252 return 1; 253 } 254 255 } else if ( val->bv_len >= left.bv_len ) { 256 if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) { 257 return(1); 258 } 259 } 260 } 261 } 262 } 263 264 return 0; 265} 266 267static slap_access_t 268aci_list_get_attr_rights( 269 struct berval *list, 270 const struct berval *attr, 271 struct berval *val ) 272{ 273 struct berval bv; 274 slap_access_t mask; 275 int i; 276 277 /* loop through each rights/attr pair, skip first part (action) */ 278 ACL_INIT(mask); 279 for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) { 280 if ( aci_list_has_attr( &bv, attr, val ) == 0 ) { 281 Debug( LDAP_DEBUG_ACL, 282 " <= aci_list_get_attr_rights " 283 "test %s for %s -> failed\n", 284 bv.bv_val, attr->bv_val, 0 ); 285 continue; 286 } 287 288 Debug( LDAP_DEBUG_ACL, 289 " <= aci_list_get_attr_rights " 290 "test %s for %s -> ok\n", 291 bv.bv_val, attr->bv_val, 0 ); 292 293 if ( acl_get_part( list, i, ';', &bv ) < 0 ) { 294 Debug( LDAP_DEBUG_ACL, 295 " <= aci_list_get_attr_rights " 296 "test no rights\n", 297 0, 0, 0 ); 298 continue; 299 } 300 301 mask |= aci_list_map_rights( &bv ); 302 Debug( LDAP_DEBUG_ACL, 303 " <= aci_list_get_attr_rights " 304 "rights %s to mask 0x%x\n", 305 bv.bv_val, mask, 0 ); 306 } 307 308 return mask; 309} 310 311static int 312aci_list_get_rights( 313 struct berval *list, 314 struct berval *attr, 315 struct berval *val, 316 slap_access_t *grant, 317 slap_access_t *deny ) 318{ 319 struct berval perm, actn, baseattr; 320 slap_access_t *mask; 321 int i, found; 322 323 if ( attr == NULL || BER_BVISEMPTY( attr ) ) { 324 attr = (struct berval *)&aci_bv[ ACI_BV_ENTRY ]; 325 326 } else if ( acl_get_part( attr, 0, ';', &baseattr ) > 0 ) { 327 attr = &baseattr; 328 } 329 found = 0; 330 ACL_INIT(*grant); 331 ACL_INIT(*deny); 332 /* loop through each permissions clause */ 333 for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) { 334 if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) { 335 continue; 336 } 337 338 if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) { 339 mask = grant; 340 341 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) { 342 mask = deny; 343 344 } else { 345 continue; 346 } 347 348 *mask |= aci_list_get_attr_rights( &perm, attr, val ); 349 *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL ); 350 351 if ( *mask != ACL_PRIV_NONE ) { 352 found = 1; 353 } 354 } 355 356 return found; 357} 358 359static int 360aci_group_member ( 361 struct berval *subj, 362 const struct berval *defgrpoc, 363 const struct berval *defgrpat, 364 Operation *op, 365 Entry *e, 366 int nmatch, 367 regmatch_t *matches 368) 369{ 370 struct berval subjdn; 371 struct berval grpoc; 372 struct berval grpat; 373 ObjectClass *grp_oc = NULL; 374 AttributeDescription *grp_ad = NULL; 375 const char *text; 376 int rc; 377 378 /* format of string is "{group|role}/objectClassValue/groupAttrName" */ 379 if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) { 380 return 0; 381 } 382 383 if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) { 384 grpoc = *defgrpoc; 385 } 386 387 if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) { 388 grpat = *defgrpat; 389 } 390 391 rc = slap_bv2ad( &grpat, &grp_ad, &text ); 392 if ( rc != LDAP_SUCCESS ) { 393 rc = 0; 394 goto done; 395 } 396 rc = 0; 397 398 grp_oc = oc_bvfind( &grpoc ); 399 400 if ( grp_oc != NULL && grp_ad != NULL ) { 401 char buf[ ACI_BUF_SIZE ]; 402 struct berval bv, ndn; 403 AclRegexMatches amatches = { 0 }; 404 405 amatches.dn_count = nmatch; 406 AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) ); 407 408 bv.bv_len = sizeof( buf ) - 1; 409 bv.bv_val = (char *)&buf; 410 if ( acl_string_expand( &bv, &subjdn, 411 &e->e_nname, NULL, &amatches ) ) 412 { 413 rc = LDAP_OTHER; 414 goto done; 415 } 416 417 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS ) 418 { 419 rc = ( backend_group( op, e, &ndn, &op->o_ndn, 420 grp_oc, grp_ad ) == 0 ); 421 slap_sl_free( ndn.bv_val, op->o_tmpmemctx ); 422 } 423 } 424 425done: 426 return rc; 427} 428 429static int 430aci_mask( 431 Operation *op, 432 Entry *e, 433 AttributeDescription *desc, 434 struct berval *val, 435 struct berval *aci, 436 int nmatch, 437 regmatch_t *matches, 438 slap_access_t *grant, 439 slap_access_t *deny, 440 slap_aci_scope_t asserted_scope ) 441{ 442 struct berval bv, 443 scope, 444 perms, 445 type, 446 opts, 447 sdn; 448 int rc; 449 450 ACL_INIT( *grant ); 451 ACL_INIT( *deny ); 452 453 assert( !BER_BVISNULL( &desc->ad_cname ) ); 454 455 /* parse an aci of the form: 456 oid # scope # action;rights;attr;rights;attr 457 $ action;rights;attr;rights;attr # type # subject 458 459 [NOTE: the following comment is very outdated, 460 as the draft version it refers to (Ando, 2004-11-20)]. 461 462 See draft-ietf-ldapext-aci-model-04.txt section 9.1 for 463 a full description of the format for this attribute. 464 Differences: "this" in the draft is "self" here, and 465 "self" and "public" is in the position of type. 466 467 <scope> = {entry|children|subtree} 468 <type> = {public|users|access-id|subtree|onelevel|children| 469 self|dnattr|group|role|set|set-ref} 470 471 This routine now supports scope={ENTRY,CHILDREN} 472 with the semantics: 473 - ENTRY applies to "entry" and "subtree"; 474 - CHILDREN applies to "children" and "subtree" 475 */ 476 477 /* check that the aci has all 5 components */ 478 if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) { 479 return 0; 480 } 481 482 /* check that the aci family is supported */ 483 /* FIXME: the OID is ignored? */ 484 if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) { 485 return 0; 486 } 487 488 /* check that the scope matches */ 489 if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) { 490 return 0; 491 } 492 493 /* note: scope can be either ENTRY or CHILDREN; 494 * they respectively match "entry" and "children" in bv 495 * both match "subtree" */ 496 switch ( asserted_scope ) { 497 case SLAP_ACI_SCOPE_ENTRY: 498 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0 499 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 ) 500 { 501 return 0; 502 } 503 break; 504 505 case SLAP_ACI_SCOPE_CHILDREN: 506 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0 507 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 ) 508 { 509 return 0; 510 } 511 break; 512 513 case SLAP_ACI_SCOPE_SUBTREE: 514 /* TODO: add assertion? */ 515 return 0; 516 } 517 518 /* get the list of permissions clauses, bail if empty */ 519 if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) { 520 assert( 0 ); 521 return 0; 522 } 523 524 /* check if any permissions allow desired access */ 525 if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) { 526 return 0; 527 } 528 529 /* see if we have a DN match */ 530 if ( acl_get_part( aci, 3, '#', &type ) < 0 ) { 531 assert( 0 ); 532 return 0; 533 } 534 535 /* see if we have a public (i.e. anonymous) access */ 536 if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) { 537 return 1; 538 } 539 540 /* otherwise require an identity */ 541 if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) { 542 return 0; 543 } 544 545 /* see if we have a users access */ 546 if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) { 547 return 1; 548 } 549 550 /* NOTE: this may fail if a DN contains a valid '#' (unescaped); 551 * just grab all the berval up to its end (ITS#3303). 552 * NOTE: the problem could be solved by providing the DN with 553 * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would 554 * become "cn=Foo\23Bar" and be safely used by aci_mask(). */ 555#if 0 556 if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) { 557 return 0; 558 } 559#endif 560 sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" ); 561 sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val ); 562 563 /* get the type options, if any */ 564 if ( acl_get_part( &type, 1, '/', &opts ) > 0 ) { 565 opts.bv_len = type.bv_len - ( opts.bv_val - type.bv_val ); 566 type.bv_len = opts.bv_val - type.bv_val - 1; 567 568 } else { 569 BER_BVZERO( &opts ); 570 } 571 572 if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) { 573 return dn_match( &op->o_ndn, &sdn ); 574 575 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) { 576 return dnIsSuffix( &op->o_ndn, &sdn ); 577 578 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) { 579 struct berval pdn; 580 581 dnParent( &sdn, &pdn ); 582 583 return dn_match( &op->o_ndn, &pdn ); 584 585 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) { 586 return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) ); 587 588 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) { 589 return dn_match( &op->o_ndn, &e->e_nname ); 590 591 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) { 592 Attribute *at; 593 AttributeDescription *ad = NULL; 594 const char *text; 595 596 rc = slap_bv2ad( &sdn, &ad, &text ); 597 assert( rc == LDAP_SUCCESS ); 598 599 rc = 0; 600 for ( at = attrs_find( e->e_attrs, ad ); 601 at != NULL; 602 at = attrs_find( at->a_next, ad ) ) 603 { 604 if ( attr_valfind( at, 605 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | 606 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, 607 &op->o_ndn, NULL, op->o_tmpmemctx ) == 0 ) 608 { 609 rc = 1; 610 break; 611 } 612 } 613 614 return rc; 615 616 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) { 617 struct berval oc, 618 at; 619 620 if ( BER_BVISNULL( &opts ) ) { 621 oc = aci_bv[ ACI_BV_GROUP_CLASS ]; 622 at = aci_bv[ ACI_BV_GROUP_ATTR ]; 623 624 } else { 625 if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) { 626 assert( 0 ); 627 } 628 629 if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) { 630 at = aci_bv[ ACI_BV_GROUP_ATTR ]; 631 } 632 } 633 634 if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) ) 635 { 636 return 1; 637 } 638 639 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) { 640 struct berval oc, 641 at; 642 643 if ( BER_BVISNULL( &opts ) ) { 644 oc = aci_bv[ ACI_BV_ROLE_CLASS ]; 645 at = aci_bv[ ACI_BV_ROLE_ATTR ]; 646 647 } else { 648 if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) { 649 assert( 0 ); 650 } 651 652 if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) { 653 at = aci_bv[ ACI_BV_ROLE_ATTR ]; 654 } 655 } 656 657 if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) ) 658 { 659 return 1; 660 } 661 662 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) { 663 if ( acl_match_set( &sdn, op, e, NULL ) ) { 664 return 1; 665 } 666 667 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) { 668 if ( acl_match_set( &sdn, op, e, (struct berval *)&aci_bv[ ACI_BV_SET_ATTR ] ) ) { 669 return 1; 670 } 671 672 } else { 673 /* it passed normalization! */ 674 assert( 0 ); 675 } 676 677 return 0; 678} 679 680static int 681aci_init( void ) 682{ 683 /* OpenLDAP eXperimental Syntax */ 684 static slap_syntax_defs_rec aci_syntax_def = { 685 "( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )", 686 SLAP_SYNTAX_HIDE, 687 NULL, 688 OpenLDAPaciValidate, 689 OpenLDAPaciPretty 690 }; 691 static slap_mrule_defs_rec aci_mr_def = { 692 "( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' " 693 "SYNTAX 1.3.6.1.4.1.4203.666.2.1 )", 694 SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL, 695 NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch, 696 NULL, NULL, 697 NULL 698 }; 699 static struct { 700 char *name; 701 char *desc; 702 slap_mask_t flags; 703 AttributeDescription **ad; 704 } aci_at = { 705 "OpenLDAPaci", "( 1.3.6.1.4.1.4203.666.1.5 " 706 "NAME 'OpenLDAPaci' " 707 "DESC 'OpenLDAP access control information (experimental)' " 708 "EQUALITY OpenLDAPaciMatch " 709 "SYNTAX 1.3.6.1.4.1.4203.666.2.1 " 710 "USAGE directoryOperation )", 711 SLAP_AT_HIDE, 712 &slap_ad_aci 713 }; 714 715 int rc; 716 717 /* ACI syntax */ 718 rc = register_syntax( &aci_syntax_def ); 719 if ( rc != 0 ) { 720 return rc; 721 } 722 723 /* ACI equality rule */ 724 rc = register_matching_rule( &aci_mr_def ); 725 if ( rc != 0 ) { 726 return rc; 727 } 728 729 /* ACI attribute */ 730 rc = register_at( aci_at.desc, aci_at.ad, 0 ); 731 if ( rc != LDAP_SUCCESS ) { 732 Debug( LDAP_DEBUG_ANY, 733 "aci_init: at_register failed\n", 0, 0, 0 ); 734 return rc; 735 } 736 737 /* install flags */ 738 (*aci_at.ad)->ad_type->sat_flags |= aci_at.flags; 739 740 return rc; 741} 742 743static int 744dynacl_aci_parse( 745 const char *fname, 746 int lineno, 747 const char *opts, 748 slap_style_t sty, 749 const char *right, 750 void **privp ) 751{ 752 AttributeDescription *ad = NULL; 753 const char *text = NULL; 754 755 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) { 756 fprintf( stderr, "%s: line %d: " 757 "inappropriate style \"%s\" in \"aci\" by clause\n", 758 fname, lineno, style_strings[sty] ); 759 return -1; 760 } 761 762 if ( right != NULL && *right != '\0' ) { 763 if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) { 764 fprintf( stderr, 765 "%s: line %d: aci \"%s\": %s\n", 766 fname, lineno, right, text ); 767 return -1; 768 } 769 770 } else { 771 ad = slap_ad_aci; 772 } 773 774 if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) { 775 fprintf( stderr, "%s: line %d: " 776 "aci \"%s\": inappropriate syntax: %s\n", 777 fname, lineno, right, 778 ad->ad_type->sat_syntax_oid ); 779 return -1; 780 } 781 782 *privp = (void *)ad; 783 784 return 0; 785} 786 787static int 788dynacl_aci_unparse( void *priv, struct berval *bv ) 789{ 790 AttributeDescription *ad = ( AttributeDescription * )priv; 791 char *ptr; 792 793 assert( ad != NULL ); 794 795 bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 ); 796 ptr = lutil_strcopy( bv->bv_val, " aci=" ); 797 ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val ); 798 bv->bv_len = ptr - bv->bv_val; 799 800 return 0; 801} 802 803static int 804dynacl_aci_mask( 805 void *priv, 806 Operation *op, 807 Entry *e, 808 AttributeDescription *desc, 809 struct berval *val, 810 int nmatch, 811 regmatch_t *matches, 812 slap_access_t *grantp, 813 slap_access_t *denyp ) 814{ 815 AttributeDescription *ad = ( AttributeDescription * )priv; 816 Attribute *at; 817 slap_access_t tgrant, tdeny, grant, deny; 818#ifdef LDAP_DEBUG 819 char accessmaskbuf[ACCESSMASK_MAXLEN]; 820 char accessmaskbuf1[ACCESSMASK_MAXLEN]; 821#endif /* LDAP_DEBUG */ 822 823 if ( BER_BVISEMPTY( &e->e_nname ) ) { 824 /* no ACIs in the root DSE */ 825 return -1; 826 } 827 828 /* start out with nothing granted, nothing denied */ 829 ACL_INIT(tgrant); 830 ACL_INIT(tdeny); 831 832 /* get the aci attribute */ 833 at = attr_find( e->e_attrs, ad ); 834 if ( at != NULL ) { 835 int i; 836 837 /* the aci is an multi-valued attribute. The 838 * rights are determined by OR'ing the individual 839 * rights given by the acis. 840 */ 841 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) { 842 if ( aci_mask( op, e, desc, val, &at->a_nvals[i], 843 nmatch, matches, &grant, &deny, 844 SLAP_ACI_SCOPE_ENTRY ) != 0 ) 845 { 846 tgrant |= grant; 847 tdeny |= deny; 848 } 849 } 850 851 Debug( LDAP_DEBUG_ACL, " <= aci_mask grant %s deny %s\n", 852 accessmask2str( tgrant, accessmaskbuf, 1 ), 853 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 ); 854 } 855 856 /* If the entry level aci didn't contain anything valid for the 857 * current operation, climb up the tree and evaluate the 858 * acis with scope set to subtree 859 */ 860 if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) { 861 struct berval parent_ndn; 862 863 dnParent( &e->e_nname, &parent_ndn ); 864 while ( !BER_BVISEMPTY( &parent_ndn ) ){ 865 int i; 866 BerVarray bvals = NULL; 867 int ret, stop; 868 869 /* to solve the chicken'n'egg problem of accessing 870 * the OpenLDAPaci attribute, the direct access 871 * to the entry's attribute is unchecked; however, 872 * further accesses to OpenLDAPaci values in the 873 * ancestors occur through backend_attribute(), i.e. 874 * with the identity of the operation, requiring 875 * further access checking. For uniformity, this 876 * makes further requests occur as the rootdn, if 877 * any, i.e. searching for the OpenLDAPaci attribute 878 * is considered an internal search. If this is not 879 * acceptable, then the same check needs be performed 880 * when accessing the entry's attribute. */ 881 struct berval save_o_dn, save_o_ndn; 882 883 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) { 884 save_o_dn = op->o_dn; 885 save_o_ndn = op->o_ndn; 886 887 op->o_dn = op->o_bd->be_rootdn; 888 op->o_ndn = op->o_bd->be_rootndn; 889 } 890 891 Debug( LDAP_DEBUG_ACL, " checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 ); 892 ret = backend_attribute( op, NULL, &parent_ndn, ad, &bvals, ACL_AUTH ); 893 894 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) { 895 op->o_dn = save_o_dn; 896 op->o_ndn = save_o_ndn; 897 } 898 899 switch ( ret ) { 900 case LDAP_SUCCESS : 901 stop = 0; 902 if ( !bvals ) { 903 break; 904 } 905 906 for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) { 907 if ( aci_mask( op, e, desc, val, 908 &bvals[i], 909 nmatch, matches, 910 &grant, &deny, 911 SLAP_ACI_SCOPE_CHILDREN ) != 0 ) 912 { 913 tgrant |= grant; 914 tdeny |= deny; 915 /* evaluation stops as soon as either a "deny" or a 916 * "grant" directive matches. 917 */ 918 if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) { 919 stop = 1; 920 } 921 } 922 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n", 923 accessmask2str( tgrant, accessmaskbuf, 1 ), 924 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 ); 925 } 926 break; 927 928 case LDAP_NO_SUCH_ATTRIBUTE: 929 /* just go on if the aci-Attribute is not present in 930 * the current entry 931 */ 932 Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 ); 933 stop = 0; 934 break; 935 936 case LDAP_NO_SUCH_OBJECT: 937 /* We have reached the base object */ 938 Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 ); 939 stop = 1; 940 break; 941 942 default: 943 stop = 1; 944 break; 945 } 946 947 if ( stop ) { 948 break; 949 } 950 dnParent( &parent_ndn, &parent_ndn ); 951 } 952 } 953 954 *grantp = tgrant; 955 *denyp = tdeny; 956 957 return 0; 958} 959 960/* need to register this at some point */ 961static slap_dynacl_t dynacl_aci = { 962 "aci", 963 dynacl_aci_parse, 964 dynacl_aci_unparse, 965 dynacl_aci_mask, 966 NULL, 967 NULL, 968 NULL 969}; 970 971int 972dynacl_aci_init( void ) 973{ 974 int rc; 975 976 rc = aci_init(); 977 978 if ( rc == 0 ) { 979 rc = slap_dynacl_register( &dynacl_aci ); 980 } 981 982 return rc; 983} 984 985 986/* ACI syntax validation */ 987 988/* 989 * Matches given berval to array of bervals 990 * Returns: 991 * >=0 if one if the array elements equals to this berval 992 * -1 if string was not found in array 993 */ 994static int 995bv_getcaseidx( 996 struct berval *bv, 997 const struct berval *arr[] ) 998{ 999 int i; 1000 1001 if ( BER_BVISEMPTY( bv ) ) { 1002 return -1; 1003 } 1004 1005 for ( i = 0; arr[ i ] != NULL ; i++ ) { 1006 if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) { 1007 return i; 1008 } 1009 } 1010 1011 return -1; 1012} 1013 1014 1015/* Returns what have left in input berval after current sub */ 1016static void 1017bv_get_tail( 1018 struct berval *val, 1019 struct berval *sub, 1020 struct berval *tail ) 1021{ 1022 int head_len; 1023 1024 tail->bv_val = sub->bv_val + sub->bv_len; 1025 head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val; 1026 tail->bv_len = val->bv_len - head_len; 1027} 1028 1029 1030/* 1031 * aci is accepted in following form: 1032 * oid#scope#rights#type#subject 1033 * Where: 1034 * oid := numeric OID (currently ignored) 1035 * scope := entry|children|subtree 1036 * rights := right[[$right]...] 1037 * right := (grant|deny);action 1038 * action := perms;attrs[[;perms;attrs]...] 1039 * perms := perm[[,perm]...] 1040 * perm := c|s|r|w|x 1041 * attrs := attribute[[,attribute]..]|"[all]" 1042 * attribute := attributeType|attributeType=attributeValue|attributeType=attributeValuePrefix* 1043 * type := public|users|self|dnattr|group|role|set|set-ref| 1044 * access_id|subtree|onelevel|children 1045 */ 1046static int 1047OpenLDAPaciValidatePerms( 1048 struct berval *perms ) 1049{ 1050 ber_len_t i; 1051 1052 for ( i = 0; i < perms->bv_len; ) { 1053 switch ( perms->bv_val[ i ] ) { 1054 case 'x': 1055 case 'd': 1056 case 'c': 1057 case 's': 1058 case 'r': 1059 case 'w': 1060 break; 1061 1062 default: 1063 Debug( LDAP_DEBUG_ACL, "aciValidatePerms: perms needs to be one of x,d,c,s,r,w in '%s'\n", perms->bv_val, 0, 0 ); 1064 return LDAP_INVALID_SYNTAX; 1065 } 1066 1067 if ( ++i == perms->bv_len ) { 1068 return LDAP_SUCCESS; 1069 } 1070 1071 while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' ) 1072 i++; 1073 1074 assert( i != perms->bv_len ); 1075 1076 if ( perms->bv_val[ i ] != ',' ) { 1077 Debug( LDAP_DEBUG_ACL, "aciValidatePerms: missing comma in '%s'\n", perms->bv_val, 0, 0 ); 1078 return LDAP_INVALID_SYNTAX; 1079 } 1080 1081 do { 1082 i++; 1083 } while ( perms->bv_val[ i ] == ' ' ); 1084 } 1085 1086 return LDAP_SUCCESS; 1087} 1088 1089static const struct berval *ACIgrantdeny[] = { 1090 &aci_bv[ ACI_BV_GRANT ], 1091 &aci_bv[ ACI_BV_DENY ], 1092 NULL 1093}; 1094 1095static int 1096OpenLDAPaciValidateRight( 1097 struct berval *action ) 1098{ 1099 struct berval bv = BER_BVNULL; 1100 int i; 1101 1102 /* grant|deny */ 1103 if ( acl_get_part( action, 0, ';', &bv ) < 0 || 1104 bv_getcaseidx( &bv, ACIgrantdeny ) == -1 ) 1105 { 1106 Debug( LDAP_DEBUG_ACL, "aciValidateRight: '%s' must be either 'grant' or 'deny'\n", bv.bv_val, 0, 0 ); 1107 return LDAP_INVALID_SYNTAX; 1108 } 1109 1110 for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) { 1111 if ( i & 1 ) { 1112 /* perms */ 1113 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS ) 1114 { 1115 return LDAP_INVALID_SYNTAX; 1116 } 1117 1118 } else { 1119 /* attr */ 1120 AttributeDescription *ad; 1121 const char *text; 1122 struct berval attr, left, right; 1123 int j; 1124 1125 /* could be "[all]" or an attribute description */ 1126 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) { 1127 continue; 1128 } 1129 1130 1131 for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ ) 1132 { 1133 ad = NULL; 1134 text = NULL; 1135 if ( acl_get_part( &attr, 0, '=', &left ) < 0 1136 || acl_get_part( &attr, 1, '=', &right ) < 0 ) 1137 { 1138 if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) 1139 { 1140 Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", attr.bv_val, 0, 0 ); 1141 return LDAP_INVALID_SYNTAX; 1142 } 1143 } else { 1144 if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS ) 1145 { 1146 Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", left.bv_val, 0, 0 ); 1147 return LDAP_INVALID_SYNTAX; 1148 } 1149 } 1150 } 1151 } 1152 } 1153 1154 /* "perms;attr" go in pairs */ 1155 if ( i > 0 && ( i & 1 ) == 0 ) { 1156 return LDAP_SUCCESS; 1157 1158 } else { 1159 Debug( LDAP_DEBUG_ACL, "aciValidateRight: perms:attr need to be pairs in '%s'\n", action->bv_val, 0, 0 ); 1160 return LDAP_INVALID_SYNTAX; 1161 } 1162 1163 return LDAP_SUCCESS; 1164} 1165 1166static int 1167OpenLDAPaciNormalizeRight( 1168 struct berval *action, 1169 struct berval *naction, 1170 void *ctx ) 1171{ 1172 struct berval grantdeny, 1173 perms = BER_BVNULL, 1174 bv = BER_BVNULL; 1175 int idx, 1176 i; 1177 1178 /* grant|deny */ 1179 if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) { 1180 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: missing ';' in '%s'\n", action->bv_val, 0, 0 ); 1181 return LDAP_INVALID_SYNTAX; 1182 } 1183 idx = bv_getcaseidx( &grantdeny, ACIgrantdeny ); 1184 if ( idx == -1 ) { 1185 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: '%s' must be grant or deny\n", grantdeny.bv_val, 0, 0 ); 1186 return LDAP_INVALID_SYNTAX; 1187 } 1188 1189 ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx ); 1190 1191 for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) { 1192 struct berval nattrs = BER_BVNULL; 1193 int freenattrs = 1; 1194 if ( i & 1 ) { 1195 /* perms */ 1196 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS ) 1197 { 1198 return LDAP_INVALID_SYNTAX; 1199 } 1200 perms = bv; 1201 1202 } else { 1203 /* attr */ 1204 char *ptr; 1205 1206 /* could be "[all]" or an attribute description */ 1207 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) { 1208 nattrs = aci_bv[ ACI_BV_BR_ALL ]; 1209 freenattrs = 0; 1210 1211 } else { 1212 AttributeDescription *ad = NULL; 1213 AttributeDescription adstatic= { 0 }; 1214 const char *text = NULL; 1215 struct berval attr, left, right; 1216 int j; 1217 int len; 1218 1219 for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ ) 1220 { 1221 ad = NULL; 1222 text = NULL; 1223 /* openldap 2.1 aci compabitibility [entry] -> entry */ 1224 if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ENTRY ] ) == 0 ) { 1225 ad = &adstatic; 1226 adstatic.ad_cname = aci_bv[ ACI_BV_ENTRY ]; 1227 1228 /* openldap 2.1 aci compabitibility [children] -> children */ 1229 } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_CHILDREN ] ) == 0 ) { 1230 ad = &adstatic; 1231 adstatic.ad_cname = aci_bv[ ACI_BV_CHILDREN ]; 1232 1233 /* openldap 2.1 aci compabitibility [all] -> only [all] */ 1234 } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) { 1235 ber_memfree_x( nattrs.bv_val, ctx ); 1236 nattrs = aci_bv[ ACI_BV_BR_ALL ]; 1237 freenattrs = 0; 1238 break; 1239 1240 } else if ( acl_get_part( &attr, 0, '=', &left ) < 0 1241 || acl_get_part( &attr, 1, '=', &right ) < 0 ) 1242 { 1243 if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) 1244 { 1245 ber_memfree_x( nattrs.bv_val, ctx ); 1246 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", attr.bv_val, 0, 0 ); 1247 return LDAP_INVALID_SYNTAX; 1248 } 1249 1250 } else { 1251 if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS ) 1252 { 1253 ber_memfree_x( nattrs.bv_val, ctx ); 1254 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", left.bv_val, 0, 0 ); 1255 return LDAP_INVALID_SYNTAX; 1256 } 1257 } 1258 1259 1260 len = nattrs.bv_len + ( !BER_BVISEMPTY( &nattrs ) ? STRLENOF( "," ) : 0 ) 1261 + ad->ad_cname.bv_len; 1262 nattrs.bv_val = ber_memrealloc_x( nattrs.bv_val, len + 1, ctx ); 1263 ptr = &nattrs.bv_val[ nattrs.bv_len ]; 1264 if ( !BER_BVISEMPTY( &nattrs ) ) { 1265 *ptr++ = ','; 1266 } 1267 ptr = lutil_strncopy( ptr, ad->ad_cname.bv_val, ad->ad_cname.bv_len ); 1268 ptr[ 0 ] = '\0'; 1269 nattrs.bv_len = len; 1270 } 1271 1272 } 1273 1274 naction->bv_val = ber_memrealloc_x( naction->bv_val, 1275 naction->bv_len + STRLENOF( ";" ) 1276 + perms.bv_len + STRLENOF( ";" ) 1277 + nattrs.bv_len + 1, 1278 ctx ); 1279 1280 ptr = &naction->bv_val[ naction->bv_len ]; 1281 ptr[ 0 ] = ';'; 1282 ptr++; 1283 ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len ); 1284 ptr[ 0 ] = ';'; 1285 ptr++; 1286 ptr = lutil_strncopy( ptr, nattrs.bv_val, nattrs.bv_len ); 1287 ptr[ 0 ] = '\0'; 1288 naction->bv_len += STRLENOF( ";" ) + perms.bv_len 1289 + STRLENOF( ";" ) + nattrs.bv_len; 1290 if ( freenattrs ) { 1291 ber_memfree_x( nattrs.bv_val, ctx ); 1292 } 1293 } 1294 } 1295 1296 /* perms;attr go in pairs */ 1297 if ( i > 1 && ( i & 1 ) ) { 1298 return LDAP_SUCCESS; 1299 1300 } else { 1301 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: perms:attr need to be pairs in '%s'\n", action->bv_val, 0, 0 ); 1302 return LDAP_INVALID_SYNTAX; 1303 } 1304} 1305 1306static int 1307OpenLDAPaciValidateRights( 1308 struct berval *actions ) 1309 1310{ 1311 struct berval bv = BER_BVNULL; 1312 int i; 1313 1314 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) { 1315 if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) { 1316 return LDAP_INVALID_SYNTAX; 1317 } 1318 } 1319 1320 return LDAP_SUCCESS; 1321} 1322 1323static int 1324OpenLDAPaciNormalizeRights( 1325 struct berval *actions, 1326 struct berval *nactions, 1327 void *ctx ) 1328 1329{ 1330 struct berval bv = BER_BVNULL; 1331 int i; 1332 1333 BER_BVZERO( nactions ); 1334 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) { 1335 int rc; 1336 struct berval nbv; 1337 1338 rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx ); 1339 if ( rc != LDAP_SUCCESS ) { 1340 ber_memfree_x( nactions->bv_val, ctx ); 1341 BER_BVZERO( nactions ); 1342 return LDAP_INVALID_SYNTAX; 1343 } 1344 1345 if ( i == 0 ) { 1346 *nactions = nbv; 1347 1348 } else { 1349 nactions->bv_val = ber_memrealloc_x( nactions->bv_val, 1350 nactions->bv_len + STRLENOF( "$" ) 1351 + nbv.bv_len + 1, 1352 ctx ); 1353 nactions->bv_val[ nactions->bv_len ] = '$'; 1354 AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ], 1355 nbv.bv_val, nbv.bv_len + 1 ); 1356 ber_memfree_x( nbv.bv_val, ctx ); 1357 nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len; 1358 } 1359 BER_BVZERO( &nbv ); 1360 } 1361 1362 return LDAP_SUCCESS; 1363} 1364 1365static const struct berval *OpenLDAPaciscopes[] = { 1366 &aci_bv[ ACI_BV_ENTRY ], 1367 &aci_bv[ ACI_BV_CHILDREN ], 1368 &aci_bv[ ACI_BV_SUBTREE ], 1369 1370 NULL 1371}; 1372 1373static const struct berval *OpenLDAPacitypes[] = { 1374 /* DN-valued */ 1375 &aci_bv[ ACI_BV_GROUP ], 1376 &aci_bv[ ACI_BV_ROLE ], 1377 1378/* set to one past the last DN-valued type with options (/) */ 1379#define LAST_OPTIONAL 2 1380 1381 &aci_bv[ ACI_BV_ACCESS_ID ], 1382 &aci_bv[ ACI_BV_SUBTREE ], 1383 &aci_bv[ ACI_BV_ONELEVEL ], 1384 &aci_bv[ ACI_BV_CHILDREN ], 1385 1386/* set to one past the last DN-valued type */ 1387#define LAST_DNVALUED 6 1388 1389 /* non DN-valued */ 1390 &aci_bv[ ACI_BV_DNATTR ], 1391 &aci_bv[ ACI_BV_PUBLIC ], 1392 &aci_bv[ ACI_BV_USERS ], 1393 &aci_bv[ ACI_BV_SELF ], 1394 &aci_bv[ ACI_BV_SET ], 1395 &aci_bv[ ACI_BV_SET_REF ], 1396 1397 NULL 1398}; 1399 1400static int 1401OpenLDAPaciValidate( 1402 Syntax *syntax, 1403 struct berval *val ) 1404{ 1405 struct berval oid = BER_BVNULL, 1406 scope = BER_BVNULL, 1407 rights = BER_BVNULL, 1408 type = BER_BVNULL, 1409 subject = BER_BVNULL; 1410 int idx; 1411 int rc; 1412 1413 if ( BER_BVISEMPTY( val ) ) { 1414 Debug( LDAP_DEBUG_ACL, "aciValidatet: value is empty\n", 0, 0, 0 ); 1415 return LDAP_INVALID_SYNTAX; 1416 } 1417 1418 /* oid */ 1419 if ( acl_get_part( val, 0, '#', &oid ) < 0 || 1420 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS ) 1421 { 1422 /* NOTE: the numericoidValidate() is rather pedantic; 1423 * I'd replace it with X-ORDERED VALUES so that 1424 * it's guaranteed values are maintained and used 1425 * in the desired order */ 1426 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid oid '%s'\n", oid.bv_val, 0, 0 ); 1427 return LDAP_INVALID_SYNTAX; 1428 } 1429 1430 /* scope */ 1431 if ( acl_get_part( val, 1, '#', &scope ) < 0 || 1432 bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 ) 1433 { 1434 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid scope '%s'\n", scope.bv_val, 0, 0 ); 1435 return LDAP_INVALID_SYNTAX; 1436 } 1437 1438 /* rights */ 1439 if ( acl_get_part( val, 2, '#', &rights ) < 0 || 1440 OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS ) 1441 { 1442 return LDAP_INVALID_SYNTAX; 1443 } 1444 1445 /* type */ 1446 if ( acl_get_part( val, 3, '#', &type ) < 0 ) { 1447 Debug( LDAP_DEBUG_ACL, "aciValidate: missing type in '%s'\n", val->bv_val, 0, 0 ); 1448 return LDAP_INVALID_SYNTAX; 1449 } 1450 idx = bv_getcaseidx( &type, OpenLDAPacitypes ); 1451 if ( idx == -1 ) { 1452 struct berval isgr; 1453 1454 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) { 1455 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", type.bv_val, 0, 0 ); 1456 return LDAP_INVALID_SYNTAX; 1457 } 1458 1459 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes ); 1460 if ( idx == -1 || idx >= LAST_OPTIONAL ) { 1461 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", isgr.bv_val, 0, 0 ); 1462 return LDAP_INVALID_SYNTAX; 1463 } 1464 } 1465 1466 /* subject */ 1467 bv_get_tail( val, &type, &subject ); 1468 if ( subject.bv_val[ 0 ] != '#' ) { 1469 Debug( LDAP_DEBUG_ACL, "aciValidate: missing subject in '%s'\n", val->bv_val, 0, 0 ); 1470 return LDAP_INVALID_SYNTAX; 1471 } 1472 1473 if ( idx >= LAST_DNVALUED ) { 1474 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) { 1475 AttributeDescription *ad = NULL; 1476 const char *text = NULL; 1477 1478 rc = slap_bv2ad( &subject, &ad, &text ); 1479 if ( rc != LDAP_SUCCESS ) { 1480 Debug( LDAP_DEBUG_ACL, "aciValidate: unknown dn attribute '%s'\n", subject.bv_val, 0, 0 ); 1481 return LDAP_INVALID_SYNTAX; 1482 } 1483 1484 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) { 1485 /* FIXME: allow nameAndOptionalUID? */ 1486 Debug( LDAP_DEBUG_ACL, "aciValidate: wrong syntax for dn attribute '%s'\n", subject.bv_val, 0, 0 ); 1487 return LDAP_INVALID_SYNTAX; 1488 } 1489 } 1490 1491 /* not a DN */ 1492 return LDAP_SUCCESS; 1493 1494 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ] 1495 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] ) 1496 { 1497 /* do {group|role}/oc/at check */ 1498 struct berval ocbv = BER_BVNULL, 1499 atbv = BER_BVNULL; 1500 1501 ocbv.bv_val = ber_bvchr( &type, '/' ); 1502 if ( ocbv.bv_val != NULL ) { 1503 ocbv.bv_val++; 1504 ocbv.bv_len = type.bv_len 1505 - ( ocbv.bv_val - type.bv_val ); 1506 1507 atbv.bv_val = ber_bvchr( &ocbv, '/' ); 1508 if ( atbv.bv_val != NULL ) { 1509 AttributeDescription *ad = NULL; 1510 const char *text = NULL; 1511 int rc; 1512 1513 atbv.bv_val++; 1514 atbv.bv_len = type.bv_len 1515 - ( atbv.bv_val - type.bv_val ); 1516 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1; 1517 1518 rc = slap_bv2ad( &atbv, &ad, &text ); 1519 if ( rc != LDAP_SUCCESS ) { 1520 Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group attribute '%s'\n", atbv.bv_val, 0, 0 ); 1521 return LDAP_INVALID_SYNTAX; 1522 } 1523 } 1524 1525 if ( oc_bvfind( &ocbv ) == NULL ) { 1526 Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group '%s'\n", ocbv.bv_val, 0, 0 ); 1527 return LDAP_INVALID_SYNTAX; 1528 } 1529 } 1530 } 1531 1532 if ( BER_BVISEMPTY( &subject ) ) { 1533 /* empty DN invalid */ 1534 Debug( LDAP_DEBUG_ACL, "aciValidate: missing dn in '%s'\n", val->bv_val, 0, 0 ); 1535 return LDAP_INVALID_SYNTAX; 1536 } 1537 1538 subject.bv_val++; 1539 subject.bv_len--; 1540 1541 /* FIXME: pass DN syntax? */ 1542 rc = dnValidate( NULL, &subject ); 1543 if ( rc != LDAP_SUCCESS ) { 1544 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid dn '%s'\n", subject.bv_val, 0, 0 ); 1545 } 1546 return rc; 1547} 1548 1549static int 1550OpenLDAPaciPrettyNormal( 1551 struct berval *val, 1552 struct berval *out, 1553 void *ctx, 1554 int normalize ) 1555{ 1556 struct berval oid = BER_BVNULL, 1557 scope = BER_BVNULL, 1558 rights = BER_BVNULL, 1559 nrights = BER_BVNULL, 1560 type = BER_BVNULL, 1561 ntype = BER_BVNULL, 1562 subject = BER_BVNULL, 1563 nsubject = BER_BVNULL; 1564 int idx, 1565 rc = LDAP_SUCCESS, 1566 freesubject = 0, 1567 freetype = 0; 1568 char *ptr; 1569 1570 BER_BVZERO( out ); 1571 1572 if ( BER_BVISEMPTY( val ) ) { 1573 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: value is empty\n", 0, 0, 0 ); 1574 return LDAP_INVALID_SYNTAX; 1575 } 1576 1577 /* oid: if valid, it's already normalized */ 1578 if ( acl_get_part( val, 0, '#', &oid ) < 0 || 1579 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS ) 1580 { 1581 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid oid '%s'\n", oid.bv_val, 0, 0 ); 1582 return LDAP_INVALID_SYNTAX; 1583 } 1584 1585 /* scope: normalize by replacing with OpenLDAPaciscopes */ 1586 if ( acl_get_part( val, 1, '#', &scope ) < 0 ) { 1587 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing scope in '%s'\n", val->bv_val, 0, 0 ); 1588 return LDAP_INVALID_SYNTAX; 1589 } 1590 idx = bv_getcaseidx( &scope, OpenLDAPaciscopes ); 1591 if ( idx == -1 ) { 1592 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid scope '%s'\n", scope.bv_val, 0, 0 ); 1593 return LDAP_INVALID_SYNTAX; 1594 } 1595 scope = *OpenLDAPaciscopes[ idx ]; 1596 1597 /* rights */ 1598 if ( acl_get_part( val, 2, '#', &rights ) < 0 ) { 1599 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing rights in '%s'\n", val->bv_val, 0, 0 ); 1600 return LDAP_INVALID_SYNTAX; 1601 } 1602 if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx ) 1603 != LDAP_SUCCESS ) 1604 { 1605 return LDAP_INVALID_SYNTAX; 1606 } 1607 1608 /* type */ 1609 if ( acl_get_part( val, 3, '#', &type ) < 0 ) { 1610 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing type in '%s'\n", val->bv_val, 0, 0 ); 1611 rc = LDAP_INVALID_SYNTAX; 1612 goto cleanup; 1613 } 1614 idx = bv_getcaseidx( &type, OpenLDAPacitypes ); 1615 if ( idx == -1 ) { 1616 struct berval isgr; 1617 1618 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) { 1619 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", type.bv_val, 0, 0 ); 1620 rc = LDAP_INVALID_SYNTAX; 1621 goto cleanup; 1622 } 1623 1624 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes ); 1625 if ( idx == -1 || idx >= LAST_OPTIONAL ) { 1626 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", isgr.bv_val, 0, 0 ); 1627 rc = LDAP_INVALID_SYNTAX; 1628 goto cleanup; 1629 } 1630 } 1631 ntype = *OpenLDAPacitypes[ idx ]; 1632 1633 /* subject */ 1634 bv_get_tail( val, &type, &subject ); 1635 1636 if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) { 1637 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing subject in '%s'\n", val->bv_val, 0, 0 ); 1638 rc = LDAP_INVALID_SYNTAX; 1639 goto cleanup; 1640 } 1641 1642 subject.bv_val++; 1643 subject.bv_len--; 1644 1645 if ( idx < LAST_DNVALUED ) { 1646 /* FIXME: pass DN syntax? */ 1647 if ( normalize ) { 1648 rc = dnNormalize( 0, NULL, NULL, 1649 &subject, &nsubject, ctx ); 1650 } else { 1651 rc = dnPretty( NULL, &subject, &nsubject, ctx ); 1652 } 1653 1654 if ( rc == LDAP_SUCCESS ) { 1655 freesubject = 1; 1656 1657 } else { 1658 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid subject dn '%s'\n", subject.bv_val, 0, 0 ); 1659 goto cleanup; 1660 } 1661 1662 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ] 1663 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] ) 1664 { 1665 /* do {group|role}/oc/at check */ 1666 struct berval ocbv = BER_BVNULL, 1667 atbv = BER_BVNULL; 1668 1669 ocbv.bv_val = ber_bvchr( &type, '/' ); 1670 if ( ocbv.bv_val != NULL ) { 1671 ObjectClass *oc = NULL; 1672 AttributeDescription *ad = NULL; 1673 const char *text = NULL; 1674 int rc; 1675 struct berval bv; 1676 1677 bv.bv_len = ntype.bv_len; 1678 1679 ocbv.bv_val++; 1680 ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val ); 1681 1682 atbv.bv_val = ber_bvchr( &ocbv, '/' ); 1683 if ( atbv.bv_val != NULL ) { 1684 atbv.bv_val++; 1685 atbv.bv_len = type.bv_len 1686 - ( atbv.bv_val - type.bv_val ); 1687 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1; 1688 1689 rc = slap_bv2ad( &atbv, &ad, &text ); 1690 if ( rc != LDAP_SUCCESS ) { 1691 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown group attribute '%s'\n", atbv.bv_val, 0, 0 ); 1692 rc = LDAP_INVALID_SYNTAX; 1693 goto cleanup; 1694 } 1695 1696 bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len; 1697 } 1698 1699 oc = oc_bvfind( &ocbv ); 1700 if ( oc == NULL ) { 1701 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid group '%s'\n", ocbv.bv_val, 0, 0 ); 1702 rc = LDAP_INVALID_SYNTAX; 1703 goto cleanup; 1704 } 1705 1706 bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len; 1707 bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx ); 1708 1709 ptr = bv.bv_val; 1710 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len ); 1711 ptr[ 0 ] = '/'; 1712 ptr++; 1713 ptr = lutil_strncopy( ptr, 1714 oc->soc_cname.bv_val, 1715 oc->soc_cname.bv_len ); 1716 if ( ad != NULL ) { 1717 ptr[ 0 ] = '/'; 1718 ptr++; 1719 ptr = lutil_strncopy( ptr, 1720 ad->ad_cname.bv_val, 1721 ad->ad_cname.bv_len ); 1722 } 1723 ptr[ 0 ] = '\0'; 1724 1725 ntype = bv; 1726 freetype = 1; 1727 } 1728 } 1729 1730 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) { 1731 AttributeDescription *ad = NULL; 1732 const char *text = NULL; 1733 int rc; 1734 1735 rc = slap_bv2ad( &subject, &ad, &text ); 1736 if ( rc != LDAP_SUCCESS ) { 1737 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown dn attribute '%s'\n", subject.bv_val, 0, 0 ); 1738 rc = LDAP_INVALID_SYNTAX; 1739 goto cleanup; 1740 } 1741 1742 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) { 1743 /* FIXME: allow nameAndOptionalUID? */ 1744 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: wrong syntax for dn attribute '%s'\n", subject.bv_val, 0, 0 ); 1745 rc = LDAP_INVALID_SYNTAX; 1746 goto cleanup; 1747 } 1748 1749 nsubject = ad->ad_cname; 1750 1751 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_SET ] 1752 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_SET_REF ] ) 1753 { 1754 /* NOTE: dunno how to normalize it... */ 1755 nsubject = subject; 1756 } 1757 1758 1759 out->bv_len = 1760 oid.bv_len + STRLENOF( "#" ) 1761 + scope.bv_len + STRLENOF( "#" ) 1762 + nrights.bv_len + STRLENOF( "#" ) 1763 + ntype.bv_len + STRLENOF( "#" ) 1764 + nsubject.bv_len; 1765 1766 out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx ); 1767 ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len ); 1768 ptr[ 0 ] = '#'; 1769 ptr++; 1770 ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len ); 1771 ptr[ 0 ] = '#'; 1772 ptr++; 1773 ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len ); 1774 ptr[ 0 ] = '#'; 1775 ptr++; 1776 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len ); 1777 ptr[ 0 ] = '#'; 1778 ptr++; 1779 if ( !BER_BVISNULL( &nsubject ) ) { 1780 ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len ); 1781 } 1782 ptr[ 0 ] = '\0'; 1783 1784cleanup:; 1785 if ( freesubject ) { 1786 ber_memfree_x( nsubject.bv_val, ctx ); 1787 } 1788 1789 if ( freetype ) { 1790 ber_memfree_x( ntype.bv_val, ctx ); 1791 } 1792 1793 if ( !BER_BVISNULL( &nrights ) ) { 1794 ber_memfree_x( nrights.bv_val, ctx ); 1795 } 1796 1797 return rc; 1798} 1799 1800static int 1801OpenLDAPaciPretty( 1802 Syntax *syntax, 1803 struct berval *val, 1804 struct berval *out, 1805 void *ctx ) 1806{ 1807 return OpenLDAPaciPrettyNormal( val, out, ctx, 0 ); 1808} 1809 1810static int 1811OpenLDAPaciNormalize( 1812 slap_mask_t use, 1813 Syntax *syntax, 1814 MatchingRule *mr, 1815 struct berval *val, 1816 struct berval *out, 1817 void *ctx ) 1818{ 1819 return OpenLDAPaciPrettyNormal( val, out, ctx, 1 ); 1820} 1821 1822#if SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC 1823/* 1824 * FIXME: need config and Makefile.am code to ease building 1825 * as dynamic module 1826 */ 1827int 1828init_module( int argc, char *argv[] ) 1829{ 1830 return dynacl_aci_init(); 1831} 1832#endif /* SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC */ 1833 1834#endif /* SLAPD_ACI_ENABLED */ 1835 1836