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