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