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