1/* acl.c - routines to parse and check acl's */ 2/* $OpenLDAP$ */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 1998-2011 The OpenLDAP Foundation. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted only as authorized by the OpenLDAP 10 * Public License. 11 * 12 * A copy of this license is available in the file LICENSE in the 13 * top-level directory of the distribution or, alternatively, at 14 * <http://www.OpenLDAP.org/license.html>. 15 */ 16/* Portions Copyright (c) 1995 Regents of the University of Michigan. 17 * All rights reserved. 18 * 19 * Redistribution and use in source and binary forms are permitted 20 * provided that this notice is preserved and that due credit is given 21 * to the University of Michigan at Ann Arbor. The name of the University 22 * may not be used to endorse or promote products derived from this 23 * software without specific prior written permission. This software 24 * is provided ``as is'' without express or implied warranty. 25 */ 26 27#include "portable.h" 28 29#include <stdio.h> 30 31#include <ac/regex.h> 32#include <ac/socket.h> 33#include <ac/string.h> 34 35#include "slap.h" 36#include "sets.h" 37#include "lber_pvt.h" 38#include "lutil.h" 39 40#define ACL_BUF_SIZE 1024 /* use most appropriate size */ 41 42static const struct berval acl_bv_ip_eq = BER_BVC( "IP=" ); 43#ifdef LDAP_PF_INET6 44static const struct berval acl_bv_ipv6_eq = BER_BVC( "IP=[" ); 45#endif /* LDAP_PF_INET6 */ 46#ifdef LDAP_PF_LOCAL 47static const struct berval acl_bv_path_eq = BER_BVC("PATH="); 48#endif /* LDAP_PF_LOCAL */ 49 50static AccessControl * slap_acl_get( 51 AccessControl *ac, int *count, 52 Operation *op, Entry *e, 53 AttributeDescription *desc, 54 struct berval *val, 55 AclRegexMatches *matches, 56 slap_mask_t *mask, 57 AccessControlState *state ); 58 59static slap_control_t slap_acl_mask( 60 AccessControl *ac, 61 AccessControl *prev, 62 slap_mask_t *mask, 63 Operation *op, Entry *e, 64 AttributeDescription *desc, 65 struct berval *val, 66 AclRegexMatches *matches, 67 int count, 68 AccessControlState *state, 69 slap_access_t access ); 70 71static int regex_matches( 72 struct berval *pat, char *str, 73 struct berval *dn_matches, struct berval *val_matches, 74 AclRegexMatches *matches); 75 76typedef struct AclSetCookie { 77 SetCookie asc_cookie; 78#define asc_op asc_cookie.set_op 79 Entry *asc_e; 80} AclSetCookie; 81 82 83SLAP_SET_GATHER acl_set_gather; 84SLAP_SET_GATHER acl_set_gather2; 85 86/* 87 * access_allowed - check whether op->o_ndn is allowed the requested access 88 * to entry e, attribute attr, value val. if val is null, access to 89 * the whole attribute is assumed (all values). 90 * 91 * This routine loops through all access controls and calls 92 * slap_acl_mask() on each applicable access control. 93 * The loop exits when a definitive answer is reached or 94 * or no more controls remain. 95 * 96 * returns: 97 * 0 access denied 98 * 1 access granted 99 * 100 * Notes: 101 * - can be legally called with op == NULL 102 * - can be legally called with op->o_bd == NULL 103 */ 104 105int 106slap_access_always_allowed( 107 Operation *op, 108 Entry *e, 109 AttributeDescription *desc, 110 struct berval *val, 111 slap_access_t access, 112 AccessControlState *state, 113 slap_mask_t *maskp ) 114{ 115 assert( maskp != NULL ); 116 117 /* assign all */ 118 ACL_LVL_ASSIGN_MANAGE( *maskp ); 119 120 return 1; 121} 122 123#define MATCHES_DNMAXCOUNT(m) \ 124 ( sizeof ( (m)->dn_data ) / sizeof( *(m)->dn_data ) ) 125#define MATCHES_VALMAXCOUNT(m) \ 126 ( sizeof ( (m)->val_data ) / sizeof( *(m)->val_data ) ) 127#define MATCHES_MEMSET(m) do { \ 128 memset( (m)->dn_data, '\0', sizeof( (m)->dn_data ) ); \ 129 memset( (m)->val_data, '\0', sizeof( (m)->val_data ) ); \ 130 (m)->dn_count = MATCHES_DNMAXCOUNT( (m) ); \ 131 (m)->val_count = MATCHES_VALMAXCOUNT( (m) ); \ 132} while ( 0 /* CONSTCOND */ ) 133 134int 135slap_access_allowed( 136 Operation *op, 137 Entry *e, 138 AttributeDescription *desc, 139 struct berval *val, 140 slap_access_t access, 141 AccessControlState *state, 142 slap_mask_t *maskp ) 143{ 144 int ret = 1; 145 int count; 146 AccessControl *a, *prev; 147 148#ifdef LDAP_DEBUG 149 char accessmaskbuf[ACCESSMASK_MAXLEN]; 150#endif 151 slap_mask_t mask; 152 slap_control_t control; 153 slap_access_t access_level; 154 const char *attr; 155 AclRegexMatches matches; 156 AccessControlState acl_state = ACL_STATE_INIT; 157 static AccessControlState state_init = ACL_STATE_INIT; 158 159 assert( op != NULL ); 160 assert( e != NULL ); 161 assert( desc != NULL ); 162 assert( maskp != NULL ); 163 164 access_level = ACL_LEVEL( access ); 165 attr = desc->ad_cname.bv_val; 166 167 assert( attr != NULL ); 168 169 ACL_INIT( mask ); 170 171 /* grant database root access */ 172 if ( be_isroot( op ) ) { 173 Debug( LDAP_DEBUG_ACL, "<= root access granted\n", 0, 0, 0 ); 174 mask = ACL_LVL_MANAGE; 175 goto done; 176 } 177 178 /* 179 * no-user-modification operational attributes are ignored 180 * by ACL_WRITE checking as any found here are not provided 181 * by the user 182 * 183 * NOTE: but they are not ignored for ACL_MANAGE, because 184 * if we get here it means a non-root user is trying to 185 * manage data, so we need to check its privileges. 186 */ 187 if ( access_level == ACL_WRITE 188 && is_at_no_user_mod( desc->ad_type ) 189 && desc != slap_schema.si_ad_entry 190 && desc != slap_schema.si_ad_children ) 191 { 192 Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:" 193 " %s access granted\n", 194 attr, 0, 0 ); 195 goto done; 196 } 197 198 /* use backend default access if no backend acls */ 199 if ( op->o_bd->be_acl == NULL && frontendDB->be_acl == NULL ) { 200 int i; 201 202 Debug( LDAP_DEBUG_ACL, 203 "=> slap_access_allowed: backend default %s " 204 "access %s to \"%s\"\n", 205 access2str( access ), 206 op->o_bd->be_dfltaccess >= access_level ? "granted" : "denied", 207 op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" ); 208 ret = op->o_bd->be_dfltaccess >= access_level; 209 210 mask = ACL_PRIV_LEVEL; 211 for ( i = ACL_NONE; i <= op->o_bd->be_dfltaccess; i++ ) { 212 ACL_PRIV_SET( mask, ACL_ACCESS2PRIV( i ) ); 213 } 214 215 goto done; 216 } 217 218 ret = 0; 219 control = ACL_BREAK; 220 221 if ( state == NULL ) 222 state = &acl_state; 223 if ( state->as_desc == desc && 224 state->as_access == access && 225 state->as_vd_acl_present ) 226 { 227 a = state->as_vd_acl; 228 count = state->as_vd_acl_count; 229 if ( state->as_fe_done ) 230 state->as_fe_done--; 231 ACL_PRIV_ASSIGN( mask, state->as_vd_mask ); 232 } else { 233 *state = state_init; 234 235 a = NULL; 236 count = 0; 237 ACL_PRIV_ASSIGN( mask, *maskp ); 238 } 239 240 MATCHES_MEMSET( &matches ); 241 prev = a; 242 243 while ( ( a = slap_acl_get( a, &count, op, e, desc, val, 244 &matches, &mask, state ) ) != NULL ) 245 { 246 int i; 247 int dnmaxcount = MATCHES_DNMAXCOUNT( &matches ); 248 int valmaxcount = MATCHES_VALMAXCOUNT( &matches ); 249 regmatch_t *dn_data = matches.dn_data; 250 regmatch_t *val_data = matches.val_data; 251 252 /* DN matches */ 253 for ( i = 0; i < dnmaxcount && dn_data[i].rm_eo > 0; i++ ) { 254 char *data = e->e_ndn; 255 256 Debug( LDAP_DEBUG_ACL, "=> match[dn%d]: %d %d ", i, 257 (int)dn_data[i].rm_so, 258 (int)dn_data[i].rm_eo ); 259 if ( dn_data[i].rm_so <= dn_data[0].rm_eo ) { 260 int n; 261 for ( n = dn_data[i].rm_so; 262 n < dn_data[i].rm_eo; n++ ) { 263 Debug( LDAP_DEBUG_ACL, "%c", 264 data[n], 0, 0 ); 265 } 266 } 267 Debug( LDAP_DEBUG_ACL, "\n", 0, 0, 0 ); 268 } 269 270 /* val matches */ 271 for ( i = 0; i < valmaxcount && val_data[i].rm_eo > 0; i++ ) { 272 char *data = val->bv_val; 273 274 Debug( LDAP_DEBUG_ACL, "=> match[val%d]: %d %d ", i, 275 (int)val_data[i].rm_so, 276 (int)val_data[i].rm_eo ); 277 if ( val_data[i].rm_so <= val_data[0].rm_eo ) { 278 int n; 279 for ( n = val_data[i].rm_so; 280 n < val_data[i].rm_eo; n++ ) { 281 Debug( LDAP_DEBUG_ACL, "%c", 282 data[n], 0, 0 ); 283 } 284 } 285 Debug( LDAP_DEBUG_ACL, "\n", 0, 0, 0 ); 286 } 287 288 control = slap_acl_mask( a, prev, &mask, op, 289 e, desc, val, &matches, count, state, access ); 290 291 if ( control != ACL_BREAK ) { 292 break; 293 } 294 295 MATCHES_MEMSET( &matches ); 296 prev = a; 297 } 298 299 if ( ACL_IS_INVALID( mask ) ) { 300 Debug( LDAP_DEBUG_ACL, 301 "=> slap_access_allowed: \"%s\" (%s) invalid!\n", 302 e->e_dn, attr, 0 ); 303 ACL_PRIV_ASSIGN( mask, *maskp ); 304 305 } else if ( control == ACL_BREAK ) { 306 Debug( LDAP_DEBUG_ACL, 307 "=> slap_access_allowed: no more rules\n", 0, 0, 0 ); 308 309 goto done; 310 } 311 312 ret = ACL_GRANT( mask, access ); 313 314 Debug( LDAP_DEBUG_ACL, 315 "=> slap_access_allowed: %s access %s by %s\n", 316 access2str( access ), ret ? "granted" : "denied", 317 accessmask2str( mask, accessmaskbuf, 1 ) ); 318 319done: 320 ACL_PRIV_ASSIGN( *maskp, mask ); 321 return ret; 322} 323 324int 325fe_access_allowed( 326 Operation *op, 327 Entry *e, 328 AttributeDescription *desc, 329 struct berval *val, 330 slap_access_t access, 331 AccessControlState *state, 332 slap_mask_t *maskp ) 333{ 334 BackendDB *be_orig; 335 int rc; 336 337 /* 338 * NOTE: control gets here if FIXME 339 * if an appropriate backend cannot be selected for the operation, 340 * we assume that the frontend should handle this 341 * FIXME: should select_backend() take care of this, 342 * and return frontendDB instead of NULL? maybe for some value 343 * of the flags? 344 */ 345 be_orig = op->o_bd; 346 347 if ( op->o_bd == NULL ) { 348 op->o_bd = select_backend( &op->o_req_ndn, 0 ); 349 if ( op->o_bd == NULL ) 350 op->o_bd = frontendDB; 351 } 352 rc = slap_access_allowed( op, e, desc, val, access, state, maskp ); 353 op->o_bd = be_orig; 354 355 return rc; 356} 357 358int 359access_allowed_mask( 360 Operation *op, 361 Entry *e, 362 AttributeDescription *desc, 363 struct berval *val, 364 slap_access_t access, 365 AccessControlState *state, 366 slap_mask_t *maskp ) 367{ 368 int ret = 1; 369 int be_null = 0; 370 371#ifdef LDAP_DEBUG 372 char accessmaskbuf[ACCESSMASK_MAXLEN]; 373#endif 374 slap_mask_t mask; 375 slap_access_t access_level; 376 const char *attr; 377 378 assert( e != NULL ); 379 assert( desc != NULL ); 380 381 access_level = ACL_LEVEL( access ); 382 383 assert( access_level > ACL_NONE ); 384 385 ACL_INIT( mask ); 386 if ( maskp ) ACL_INVALIDATE( *maskp ); 387 388 attr = desc->ad_cname.bv_val; 389 390 assert( attr != NULL ); 391 392 if ( op ) { 393 if ( op->o_acl_priv != ACL_NONE ) { 394 access = op->o_acl_priv; 395 396 } else if ( op->o_is_auth_check && 397 ( access_level == ACL_SEARCH || access_level == ACL_READ ) ) 398 { 399 access = ACL_AUTH; 400 401 } else if ( get_relax( op ) && access_level == ACL_WRITE && 402 desc == slap_schema.si_ad_entry ) 403 { 404 access = ACL_MANAGE; 405 } 406 } 407 408 if ( state != NULL ) { 409 if ( state->as_desc == desc && 410 state->as_access == access && 411 state->as_result != -1 && 412 !state->as_vd_acl_present ) 413 { 414 Debug( LDAP_DEBUG_ACL, 415 "=> access_allowed: result was in cache (%s)\n", 416 attr, 0, 0 ); 417 return state->as_result; 418 } else { 419 Debug( LDAP_DEBUG_ACL, 420 "=> access_allowed: result not in cache (%s)\n", 421 attr, 0, 0 ); 422 } 423 } 424 425 Debug( LDAP_DEBUG_ACL, 426 "=> access_allowed: %s access to \"%s\" \"%s\" requested\n", 427 access2str( access ), e->e_dn, attr ); 428 429 if ( op == NULL ) { 430 /* no-op call */ 431 goto done; 432 } 433 434 if ( op->o_bd == NULL ) { 435 op->o_bd = LDAP_STAILQ_FIRST( &backendDB ); 436 be_null = 1; 437 438 /* FIXME: experimental; use first backend rules 439 * iff there is no global_acl (ITS#3100) 440 */ 441 if ( frontendDB->be_acl != NULL ) { 442 op->o_bd = frontendDB; 443 } 444 } 445 assert( op->o_bd != NULL ); 446 447 /* this is enforced in backend_add() */ 448 if ( op->o_bd->bd_info->bi_access_allowed ) { 449 /* delegate to backend */ 450 ret = op->o_bd->bd_info->bi_access_allowed( op, e, 451 desc, val, access, state, &mask ); 452 453 } else { 454 /* use default (but pass through frontend 455 * for global ACL overlays) */ 456 ret = frontendDB->bd_info->bi_access_allowed( op, e, 457 desc, val, access, state, &mask ); 458 } 459 460 if ( !ret ) { 461 if ( ACL_IS_INVALID( mask ) ) { 462 Debug( LDAP_DEBUG_ACL, 463 "=> access_allowed: \"%s\" (%s) invalid!\n", 464 e->e_dn, attr, 0 ); 465 ACL_INIT( mask ); 466 467 } else { 468 Debug( LDAP_DEBUG_ACL, 469 "=> access_allowed: no more rules\n", 0, 0, 0 ); 470 471 goto done; 472 } 473 } 474 475 Debug( LDAP_DEBUG_ACL, 476 "=> access_allowed: %s access %s by %s\n", 477 access2str( access ), ret ? "granted" : "denied", 478 accessmask2str( mask, accessmaskbuf, 1 ) ); 479 480done: 481 if ( state != NULL ) { 482 state->as_access = access; 483 state->as_result = ret; 484 state->as_desc = desc; 485 } 486 if ( be_null ) op->o_bd = NULL; 487 if ( maskp ) ACL_PRIV_ASSIGN( *maskp, mask ); 488 return ret; 489} 490 491 492/* 493 * slap_acl_get - return the acl applicable to entry e, attribute 494 * attr. the acl returned is suitable for use in subsequent calls to 495 * acl_access_allowed(). 496 */ 497 498static AccessControl * 499slap_acl_get( 500 AccessControl *a, 501 int *count, 502 Operation *op, 503 Entry *e, 504 AttributeDescription *desc, 505 struct berval *val, 506 AclRegexMatches *matches, 507 slap_mask_t *mask, 508 AccessControlState *state ) 509{ 510 const char *attr; 511 ber_len_t dnlen; 512 AccessControl *prev; 513 514 assert( e != NULL ); 515 assert( count != NULL ); 516 assert( desc != NULL ); 517 assert( state != NULL ); 518 519 attr = desc->ad_cname.bv_val; 520 521 assert( attr != NULL ); 522 523 if( a == NULL ) { 524 if( op->o_bd == NULL || op->o_bd->be_acl == NULL ) { 525 a = frontendDB->be_acl; 526 } else { 527 a = op->o_bd->be_acl; 528 } 529 prev = NULL; 530 531 assert( a != NULL ); 532 if ( a == frontendDB->be_acl ) 533 state->as_fe_done = 1; 534 } else { 535 prev = a; 536 a = a->acl_next; 537 } 538 539 dnlen = e->e_nname.bv_len; 540 541 retry: 542 for ( ; a != NULL; prev = a, a = a->acl_next ) { 543 (*count) ++; 544 545 if ( a != frontendDB->be_acl && state->as_fe_done ) 546 state->as_fe_done++; 547 548 if ( a->acl_dn_pat.bv_len || ( a->acl_dn_style != ACL_STYLE_REGEX )) { 549 if ( a->acl_dn_style == ACL_STYLE_REGEX ) { 550 Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n", 551 *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub ); 552 if ( regexec ( &a->acl_dn_re, 553 e->e_ndn, 554 matches->dn_count, 555 matches->dn_data, 0 ) ) 556 continue; 557 558 } else { 559 ber_len_t patlen; 560 561 Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n", 562 *count, a->acl_dn_pat.bv_val, 0 ); 563 patlen = a->acl_dn_pat.bv_len; 564 if ( dnlen < patlen ) 565 continue; 566 567 if ( a->acl_dn_style == ACL_STYLE_BASE ) { 568 /* base dn -- entire object DN must match */ 569 if ( dnlen != patlen ) 570 continue; 571 572 } else if ( a->acl_dn_style == ACL_STYLE_ONE ) { 573 ber_len_t rdnlen = 0; 574 ber_len_t sep = 0; 575 576 if ( dnlen <= patlen ) 577 continue; 578 579 if ( patlen > 0 ) { 580 if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) ) 581 continue; 582 sep = 1; 583 } 584 585 rdnlen = dn_rdnlen( NULL, &e->e_nname ); 586 if ( rdnlen + patlen + sep != dnlen ) 587 continue; 588 589 } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) { 590 if ( dnlen > patlen && !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) ) 591 continue; 592 593 } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) { 594 if ( dnlen <= patlen ) 595 continue; 596 if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) ) 597 continue; 598 } 599 600 if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 ) 601 continue; 602 } 603 604 Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n", 605 *count, 0, 0 ); 606 } 607 608 if ( a->acl_attrs && !ad_inlist( desc, a->acl_attrs ) ) { 609 matches->dn_data[0].rm_so = -1; 610 matches->dn_data[0].rm_eo = -1; 611 matches->val_data[0].rm_so = -1; 612 matches->val_data[0].rm_eo = -1; 613 continue; 614 } 615 616 /* Is this ACL only for a specific value? */ 617 if ( a->acl_attrval.bv_len ) { 618 if ( val == NULL ) { 619 continue; 620 } 621 622 if ( !state->as_vd_acl_present ) { 623 state->as_vd_acl_present = 1; 624 state->as_vd_acl = prev; 625 state->as_vd_acl_count = *count - 1; 626 ACL_PRIV_ASSIGN ( state->as_vd_mask, *mask ); 627 } 628 629 if ( a->acl_attrval_style == ACL_STYLE_REGEX ) { 630 Debug( LDAP_DEBUG_ACL, 631 "acl_get: valpat %s\n", 632 a->acl_attrval.bv_val, 0, 0 ); 633 if ( regexec ( &a->acl_attrval_re, 634 val->bv_val, 635 matches->val_count, 636 matches->val_data, 0 ) ) 637 { 638 continue; 639 } 640 641 } else { 642 int match = 0; 643 const char *text; 644 Debug( LDAP_DEBUG_ACL, 645 "acl_get: val %s\n", 646 a->acl_attrval.bv_val, 0, 0 ); 647 648 if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) { 649 if (value_match( &match, desc, 650 a->acl_attrval_mr, 0, 651 val, &a->acl_attrval, &text ) != LDAP_SUCCESS || 652 match ) 653 continue; 654 655 } else { 656 ber_len_t patlen, vdnlen; 657 658 patlen = a->acl_attrval.bv_len; 659 vdnlen = val->bv_len; 660 661 if ( vdnlen < patlen ) 662 continue; 663 664 if ( a->acl_attrval_style == ACL_STYLE_BASE ) { 665 if ( vdnlen > patlen ) 666 continue; 667 668 } else if ( a->acl_attrval_style == ACL_STYLE_ONE ) { 669 ber_len_t rdnlen = 0; 670 671 if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) ) 672 continue; 673 674 rdnlen = dn_rdnlen( NULL, val ); 675 if ( rdnlen + patlen + 1 != vdnlen ) 676 continue; 677 678 } else if ( a->acl_attrval_style == ACL_STYLE_SUBTREE ) { 679 if ( vdnlen > patlen && !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) ) 680 continue; 681 682 } else if ( a->acl_attrval_style == ACL_STYLE_CHILDREN ) { 683 if ( vdnlen <= patlen ) 684 continue; 685 686 if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) ) 687 continue; 688 } 689 690 if ( strcmp( a->acl_attrval.bv_val, val->bv_val + vdnlen - patlen ) ) 691 continue; 692 } 693 } 694 } 695 696 if ( a->acl_filter != NULL ) { 697 ber_int_t rc = test_filter( NULL, e, a->acl_filter ); 698 if ( rc != LDAP_COMPARE_TRUE ) { 699 continue; 700 } 701 } 702 703 Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] attr %s\n", 704 *count, attr, 0); 705 return a; 706 } 707 708 if ( !state->as_fe_done ) { 709 state->as_fe_done = 1; 710 a = frontendDB->be_acl; 711 goto retry; 712 } 713 714 Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 ); 715 return( NULL ); 716} 717 718/* 719 * Record value-dependent access control state 720 */ 721#define ACL_RECORD_VALUE_STATE do { \ 722 if( state && !state->as_vd_acl_present ) { \ 723 state->as_vd_acl_present = 1; \ 724 state->as_vd_acl = prev; \ 725 state->as_vd_acl_count = count - 1; \ 726 ACL_PRIV_ASSIGN( state->as_vd_mask, *mask ); \ 727 } \ 728 } while( 0 ) 729 730static int 731acl_mask_dn( 732 Operation *op, 733 Entry *e, 734 struct berval *val, 735 AccessControl *a, 736 AclRegexMatches *matches, 737 slap_dn_access *bdn, 738 struct berval *opndn ) 739{ 740 /* 741 * if access applies to the entry itself, and the 742 * user is bound as somebody in the same namespace as 743 * the entry, OR the given dn matches the dn pattern 744 */ 745 /* 746 * NOTE: styles "anonymous", "users" and "self" 747 * have been moved to enum slap_style_t, whose 748 * value is set in a_dn_style; however, the string 749 * is maintained in a_dn_pat. 750 */ 751 752 if ( bdn->a_style == ACL_STYLE_ANONYMOUS ) { 753 if ( !BER_BVISEMPTY( opndn ) ) { 754 return 1; 755 } 756 757 } else if ( bdn->a_style == ACL_STYLE_USERS ) { 758 if ( BER_BVISEMPTY( opndn ) ) { 759 return 1; 760 } 761 762 } else if ( bdn->a_style == ACL_STYLE_SELF ) { 763 struct berval ndn, selfndn; 764 int level; 765 766 if ( BER_BVISEMPTY( opndn ) || BER_BVISNULL( &e->e_nname ) ) { 767 return 1; 768 } 769 770 level = bdn->a_self_level; 771 if ( level < 0 ) { 772 selfndn = *opndn; 773 ndn = e->e_nname; 774 level = -level; 775 776 } else { 777 ndn = *opndn; 778 selfndn = e->e_nname; 779 } 780 781 for ( ; level > 0; level-- ) { 782 if ( BER_BVISEMPTY( &ndn ) ) { 783 break; 784 } 785 dnParent( &ndn, &ndn ); 786 } 787 788 if ( BER_BVISEMPTY( &ndn ) || !dn_match( &ndn, &selfndn ) ) 789 { 790 return 1; 791 } 792 793 } else if ( bdn->a_style == ACL_STYLE_REGEX ) { 794 if ( !ber_bvccmp( &bdn->a_pat, '*' ) ) { 795 AclRegexMatches tmp_matches, 796 *tmp_matchesp = &tmp_matches; 797 int rc = 0; 798 regmatch_t *tmp_data; 799 800 MATCHES_MEMSET( &tmp_matches ); 801 tmp_data = &tmp_matches.dn_data[0]; 802 803 if ( a->acl_attrval_style == ACL_STYLE_REGEX ) 804 tmp_matchesp = matches; 805 else switch ( a->acl_dn_style ) { 806 case ACL_STYLE_REGEX: 807 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) { 808 tmp_matchesp = matches; 809 break; 810 } 811 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */ 812 813 case ACL_STYLE_BASE: 814 tmp_data[0].rm_so = 0; 815 tmp_data[0].rm_eo = e->e_nname.bv_len; 816 tmp_matches.dn_count = 1; 817 break; 818 819 case ACL_STYLE_ONE: 820 case ACL_STYLE_SUBTREE: 821 case ACL_STYLE_CHILDREN: 822 tmp_data[0].rm_so = 0; 823 tmp_data[0].rm_eo = e->e_nname.bv_len; 824 tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len; 825 tmp_data[1].rm_eo = e->e_nname.bv_len; 826 tmp_matches.dn_count = 2; 827 break; 828 829 default: 830 /* error */ 831 rc = 1; 832 break; 833 } 834 835 if ( rc ) { 836 return 1; 837 } 838 839 if ( !regex_matches( &bdn->a_pat, opndn->bv_val, 840 &e->e_nname, NULL, tmp_matchesp ) ) 841 { 842 return 1; 843 } 844 } 845 846 } else { 847 struct berval pat; 848 ber_len_t patlen, odnlen; 849 int got_match = 0; 850 851 if ( e->e_dn == NULL ) 852 return 1; 853 854 if ( bdn->a_expand ) { 855 struct berval bv; 856 char buf[ACL_BUF_SIZE]; 857 858 AclRegexMatches tmp_matches, 859 *tmp_matchesp = &tmp_matches; 860 int rc = 0; 861 regmatch_t *tmp_data; 862 863 MATCHES_MEMSET( &tmp_matches ); 864 tmp_data = &tmp_matches.dn_data[0]; 865 866 bv.bv_len = sizeof( buf ) - 1; 867 bv.bv_val = buf; 868 869 /* Expand value regex */ 870 if ( a->acl_attrval_style == ACL_STYLE_REGEX ) 871 tmp_matchesp = matches; 872 else switch ( a->acl_dn_style ) { 873 case ACL_STYLE_REGEX: 874 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) { 875 tmp_matchesp = matches; 876 break; 877 } 878 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */ 879 880 case ACL_STYLE_BASE: 881 tmp_data[0].rm_so = 0; 882 tmp_data[0].rm_eo = e->e_nname.bv_len; 883 tmp_matches.dn_count = 1; 884 break; 885 886 case ACL_STYLE_ONE: 887 case ACL_STYLE_SUBTREE: 888 case ACL_STYLE_CHILDREN: 889 tmp_data[0].rm_so = 0; 890 tmp_data[0].rm_eo = e->e_nname.bv_len; 891 tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len; 892 tmp_data[1].rm_eo = e->e_nname.bv_len; 893 tmp_matches.dn_count = 2; 894 break; 895 896 default: 897 /* error */ 898 rc = 1; 899 break; 900 } 901 902 if ( rc ) { 903 return 1; 904 } 905 906 if ( acl_string_expand( &bv, &bdn->a_pat, 907 &e->e_nname, 908 val, tmp_matchesp ) ) 909 { 910 return 1; 911 } 912 913 if ( dnNormalize(0, NULL, NULL, &bv, 914 &pat, op->o_tmpmemctx ) 915 != LDAP_SUCCESS ) 916 { 917 /* did not expand to a valid dn */ 918 return 1; 919 } 920 921 } else { 922 pat = bdn->a_pat; 923 } 924 925 patlen = pat.bv_len; 926 odnlen = opndn->bv_len; 927 if ( odnlen < patlen ) { 928 goto dn_match_cleanup; 929 930 } 931 932 if ( bdn->a_style == ACL_STYLE_BASE ) { 933 /* base dn -- entire object DN must match */ 934 if ( odnlen != patlen ) { 935 goto dn_match_cleanup; 936 } 937 938 } else if ( bdn->a_style == ACL_STYLE_ONE ) { 939 ber_len_t rdnlen = 0; 940 941 if ( odnlen <= patlen ) { 942 goto dn_match_cleanup; 943 } 944 945 if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) { 946 goto dn_match_cleanup; 947 } 948 949 rdnlen = dn_rdnlen( NULL, opndn ); 950 if ( rdnlen - ( odnlen - patlen - 1 ) != 0 ) { 951 goto dn_match_cleanup; 952 } 953 954 } else if ( bdn->a_style == ACL_STYLE_SUBTREE ) { 955 if ( odnlen > patlen && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) { 956 goto dn_match_cleanup; 957 } 958 959 } else if ( bdn->a_style == ACL_STYLE_CHILDREN ) { 960 if ( odnlen <= patlen ) { 961 goto dn_match_cleanup; 962 } 963 964 if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) { 965 goto dn_match_cleanup; 966 } 967 968 } else if ( bdn->a_style == ACL_STYLE_LEVEL ) { 969 int level = bdn->a_level; 970 struct berval ndn; 971 972 if ( odnlen <= patlen ) { 973 goto dn_match_cleanup; 974 } 975 976 if ( level > 0 && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) 977 { 978 goto dn_match_cleanup; 979 } 980 981 ndn = *opndn; 982 for ( ; level > 0; level-- ) { 983 if ( BER_BVISEMPTY( &ndn ) ) { 984 goto dn_match_cleanup; 985 } 986 dnParent( &ndn, &ndn ); 987 if ( ndn.bv_len < patlen ) { 988 goto dn_match_cleanup; 989 } 990 } 991 992 if ( ndn.bv_len != patlen ) { 993 goto dn_match_cleanup; 994 } 995 } 996 997 got_match = !strcmp( pat.bv_val, &opndn->bv_val[ odnlen - patlen ] ); 998 999dn_match_cleanup:; 1000 if ( pat.bv_val != bdn->a_pat.bv_val ) { 1001 slap_sl_free( pat.bv_val, op->o_tmpmemctx ); 1002 } 1003 1004 if ( !got_match ) { 1005 return 1; 1006 } 1007 } 1008 1009 return 0; 1010} 1011 1012static int 1013acl_mask_dnattr( 1014 Operation *op, 1015 Entry *e, 1016 struct berval *val, 1017 AccessControl *a, 1018 int count, 1019 AccessControlState *state, 1020 slap_mask_t *mask, 1021 slap_dn_access *bdn, 1022 struct berval *opndn ) 1023{ 1024 Attribute *at; 1025 struct berval bv; 1026 int rc, match = 0; 1027 const char *text; 1028 const char *attr = bdn->a_at->ad_cname.bv_val; 1029 1030 assert( attr != NULL ); 1031 1032 if ( BER_BVISEMPTY( opndn ) ) { 1033 return 1; 1034 } 1035 1036 Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n", attr, 0, 0 ); 1037 bv = *opndn; 1038 1039 /* see if asker is listed in dnattr */ 1040 for ( at = attrs_find( e->e_attrs, bdn->a_at ); 1041 at != NULL; 1042 at = attrs_find( at->a_next, bdn->a_at ) ) 1043 { 1044 if ( attr_valfind( at, 1045 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | 1046 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, 1047 &bv, NULL, op->o_tmpmemctx ) == 0 ) 1048 { 1049 /* found it */ 1050 match = 1; 1051 break; 1052 } 1053 } 1054 1055 if ( match ) { 1056 /* have a dnattr match. if this is a self clause then 1057 * the target must also match the op dn. 1058 */ 1059 if ( bdn->a_self ) { 1060 /* check if the target is an attribute. */ 1061 if ( val == NULL ) return 1; 1062 1063 /* target is attribute, check if the attribute value 1064 * is the op dn. 1065 */ 1066 rc = value_match( &match, bdn->a_at, 1067 bdn->a_at->ad_type->sat_equality, 0, 1068 val, &bv, &text ); 1069 /* on match error or no match, fail the ACL clause */ 1070 if ( rc != LDAP_SUCCESS || match != 0 ) 1071 return 1; 1072 } 1073 1074 } else { 1075 /* no dnattr match, check if this is a self clause */ 1076 if ( ! bdn->a_self ) 1077 return 1; 1078 1079 /* this is a self clause, check if the target is an 1080 * attribute. 1081 */ 1082 if ( val == NULL ) 1083 return 1; 1084 1085 /* target is attribute, check if the attribute value 1086 * is the op dn. 1087 */ 1088 rc = value_match( &match, bdn->a_at, 1089 bdn->a_at->ad_type->sat_equality, 0, 1090 val, &bv, &text ); 1091 1092 /* on match error or no match, fail the ACL clause */ 1093 if ( rc != LDAP_SUCCESS || match != 0 ) 1094 return 1; 1095 } 1096 1097 return 0; 1098} 1099 1100 1101/* 1102 * slap_acl_mask - modifies mask based upon the given acl and the 1103 * requested access to entry e, attribute attr, value val. if val 1104 * is null, access to the whole attribute is assumed (all values). 1105 * 1106 * returns 0 access NOT allowed 1107 * 1 access allowed 1108 */ 1109 1110static slap_control_t 1111slap_acl_mask( 1112 AccessControl *a, 1113 AccessControl *prev, 1114 slap_mask_t *mask, 1115 Operation *op, 1116 Entry *e, 1117 AttributeDescription *desc, 1118 struct berval *val, 1119 AclRegexMatches *matches, 1120 int count, 1121 AccessControlState *state, 1122 slap_access_t access ) 1123{ 1124 int i; 1125 Access *b; 1126#ifdef LDAP_DEBUG 1127 char accessmaskbuf[ACCESSMASK_MAXLEN]; 1128#endif /* DEBUG */ 1129 const char *attr; 1130#ifdef SLAP_DYNACL 1131 slap_mask_t a2pmask = ACL_ACCESS2PRIV( access ); 1132#endif /* SLAP_DYNACL */ 1133 1134 assert( a != NULL ); 1135 assert( mask != NULL ); 1136 assert( desc != NULL ); 1137 1138 attr = desc->ad_cname.bv_val; 1139 1140 assert( attr != NULL ); 1141 1142 Debug( LDAP_DEBUG_ACL, 1143 "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n", 1144 e->e_dn, attr, 0 ); 1145 1146 Debug( LDAP_DEBUG_ACL, 1147 "=> acl_mask: to %s by \"%s\", (%s) \n", 1148 val ? "value" : "all values", 1149 op->o_ndn.bv_val ? op->o_ndn.bv_val : "", 1150 accessmask2str( *mask, accessmaskbuf, 1 ) ); 1151 1152 1153 b = a->acl_access; 1154 i = 1; 1155 1156 for ( ; b != NULL; b = b->a_next, i++ ) { 1157 slap_mask_t oldmask, modmask; 1158 1159 ACL_INVALIDATE( modmask ); 1160 1161 /* check for the "self" modifier in the <access> field */ 1162 if ( b->a_dn.a_self ) { 1163 const char *dummy; 1164 int rc, match = 0; 1165 1166 ACL_RECORD_VALUE_STATE; 1167 1168 /* must have DN syntax */ 1169 if ( desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName && 1170 !is_at_syntax( desc->ad_type, SLAPD_NAMEUID_SYNTAX )) continue; 1171 1172 /* check if the target is an attribute. */ 1173 if ( val == NULL ) continue; 1174 1175 /* a DN must be present */ 1176 if ( BER_BVISEMPTY( &op->o_ndn ) ) { 1177 continue; 1178 } 1179 1180 /* target is attribute, check if the attribute value 1181 * is the op dn. 1182 */ 1183 rc = value_match( &match, desc, 1184 desc->ad_type->sat_equality, 0, 1185 val, &op->o_ndn, &dummy ); 1186 /* on match error or no match, fail the ACL clause */ 1187 if ( rc != LDAP_SUCCESS || match != 0 ) 1188 continue; 1189 } 1190 1191 /* AND <who> clauses */ 1192 if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) { 1193 Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n", 1194 b->a_dn_pat.bv_val, 0, 0); 1195 /* 1196 * if access applies to the entry itself, and the 1197 * user is bound as somebody in the same namespace as 1198 * the entry, OR the given dn matches the dn pattern 1199 */ 1200 /* 1201 * NOTE: styles "anonymous", "users" and "self" 1202 * have been moved to enum slap_style_t, whose 1203 * value is set in a_dn_style; however, the string 1204 * is maintained in a_dn_pat. 1205 */ 1206 1207 if ( acl_mask_dn( op, e, val, a, matches, 1208 &b->a_dn, &op->o_ndn ) ) 1209 { 1210 continue; 1211 } 1212 } 1213 1214 if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) { 1215 struct berval ndn; 1216 1217 Debug( LDAP_DEBUG_ACL, "<= check a_realdn_pat: %s\n", 1218 b->a_realdn_pat.bv_val, 0, 0); 1219 /* 1220 * if access applies to the entry itself, and the 1221 * user is bound as somebody in the same namespace as 1222 * the entry, OR the given dn matches the dn pattern 1223 */ 1224 /* 1225 * NOTE: styles "anonymous", "users" and "self" 1226 * have been moved to enum slap_style_t, whose 1227 * value is set in a_dn_style; however, the string 1228 * is maintained in a_dn_pat. 1229 */ 1230 1231 if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) ) 1232 { 1233 ndn = op->o_conn->c_ndn; 1234 } else { 1235 ndn = op->o_ndn; 1236 } 1237 1238 if ( acl_mask_dn( op, e, val, a, matches, 1239 &b->a_realdn, &ndn ) ) 1240 { 1241 continue; 1242 } 1243 } 1244 1245 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) { 1246 if ( ! op->o_conn->c_listener ) { 1247 continue; 1248 } 1249 Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n", 1250 b->a_sockurl_pat.bv_val, 0, 0 ); 1251 1252 if ( !ber_bvccmp( &b->a_sockurl_pat, '*' ) ) { 1253 if ( b->a_sockurl_style == ACL_STYLE_REGEX) { 1254 if ( !regex_matches( &b->a_sockurl_pat, op->o_conn->c_listener_url.bv_val, 1255 &e->e_nname, val, matches ) ) 1256 { 1257 continue; 1258 } 1259 1260 } else if ( b->a_sockurl_style == ACL_STYLE_EXPAND ) { 1261 struct berval bv; 1262 char buf[ACL_BUF_SIZE]; 1263 1264 bv.bv_len = sizeof( buf ) - 1; 1265 bv.bv_val = buf; 1266 if ( acl_string_expand( &bv, &b->a_sockurl_pat, &e->e_nname, val, matches ) ) 1267 { 1268 continue; 1269 } 1270 1271 if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_listener_url ) != 0 ) 1272 { 1273 continue; 1274 } 1275 1276 } else { 1277 if ( ber_bvstrcasecmp( &b->a_sockurl_pat, &op->o_conn->c_listener_url ) != 0 ) 1278 { 1279 continue; 1280 } 1281 } 1282 } 1283 } 1284 1285 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) { 1286 if ( !op->o_conn->c_peer_domain.bv_val ) { 1287 continue; 1288 } 1289 Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n", 1290 b->a_domain_pat.bv_val, 0, 0 ); 1291 if ( !ber_bvccmp( &b->a_domain_pat, '*' ) ) { 1292 if ( b->a_domain_style == ACL_STYLE_REGEX) { 1293 if ( !regex_matches( &b->a_domain_pat, op->o_conn->c_peer_domain.bv_val, 1294 &e->e_nname, val, matches ) ) 1295 { 1296 continue; 1297 } 1298 } else { 1299 char buf[ACL_BUF_SIZE]; 1300 1301 struct berval cmp = op->o_conn->c_peer_domain; 1302 struct berval pat = b->a_domain_pat; 1303 1304 if ( b->a_domain_expand ) { 1305 struct berval bv; 1306 1307 bv.bv_len = sizeof(buf) - 1; 1308 bv.bv_val = buf; 1309 1310 if ( acl_string_expand(&bv, &b->a_domain_pat, &e->e_nname, val, matches) ) 1311 { 1312 continue; 1313 } 1314 pat = bv; 1315 } 1316 1317 if ( b->a_domain_style == ACL_STYLE_SUBTREE ) { 1318 int offset = cmp.bv_len - pat.bv_len; 1319 if ( offset < 0 ) { 1320 continue; 1321 } 1322 1323 if ( offset == 1 || ( offset > 1 && cmp.bv_val[ offset - 1 ] != '.' ) ) { 1324 continue; 1325 } 1326 1327 /* trim the domain */ 1328 cmp.bv_val = &cmp.bv_val[ offset ]; 1329 cmp.bv_len -= offset; 1330 } 1331 1332 if ( ber_bvstrcasecmp( &pat, &cmp ) != 0 ) { 1333 continue; 1334 } 1335 } 1336 } 1337 } 1338 1339 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) { 1340 if ( !op->o_conn->c_peer_name.bv_val ) { 1341 continue; 1342 } 1343 Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n", 1344 b->a_peername_pat.bv_val, 0, 0 ); 1345 if ( !ber_bvccmp( &b->a_peername_pat, '*' ) ) { 1346 if ( b->a_peername_style == ACL_STYLE_REGEX ) { 1347 if ( !regex_matches( &b->a_peername_pat, op->o_conn->c_peer_name.bv_val, 1348 &e->e_nname, val, matches ) ) 1349 { 1350 continue; 1351 } 1352 1353 } else { 1354 /* try exact match */ 1355 if ( b->a_peername_style == ACL_STYLE_BASE ) { 1356 if ( ber_bvstrcasecmp( &b->a_peername_pat, &op->o_conn->c_peer_name ) != 0 ) { 1357 continue; 1358 } 1359 1360 } else if ( b->a_peername_style == ACL_STYLE_EXPAND ) { 1361 struct berval bv; 1362 char buf[ACL_BUF_SIZE]; 1363 1364 bv.bv_len = sizeof( buf ) - 1; 1365 bv.bv_val = buf; 1366 if ( acl_string_expand( &bv, &b->a_peername_pat, &e->e_nname, val, matches ) ) 1367 { 1368 continue; 1369 } 1370 1371 if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_peer_name ) != 0 ) { 1372 continue; 1373 } 1374 1375 /* extract IP and try exact match */ 1376 } else if ( b->a_peername_style == ACL_STYLE_IP ) { 1377 char *port; 1378 char buf[STRLENOF("255.255.255.255") + 1]; 1379 struct berval ip; 1380 unsigned long addr; 1381 int port_number = -1; 1382 1383 if ( strncasecmp( op->o_conn->c_peer_name.bv_val, 1384 acl_bv_ip_eq.bv_val, 1385 acl_bv_ip_eq.bv_len ) != 0 ) 1386 continue; 1387 1388 ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ip_eq.bv_len; 1389 ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ip_eq.bv_len; 1390 1391 port = strrchr( ip.bv_val, ':' ); 1392 if ( port ) { 1393 ip.bv_len = port - ip.bv_val; 1394 ++port; 1395 if ( lutil_atoi( &port_number, port ) != 0 ) 1396 continue; 1397 } 1398 1399 /* the port check can be anticipated here */ 1400 if ( b->a_peername_port != -1 && port_number != b->a_peername_port ) 1401 continue; 1402 1403 /* address longer than expected? */ 1404 if ( ip.bv_len >= sizeof(buf) ) 1405 continue; 1406 1407 AC_MEMCPY( buf, ip.bv_val, ip.bv_len ); 1408 buf[ ip.bv_len ] = '\0'; 1409 1410 addr = inet_addr( buf ); 1411 1412 /* unable to convert? */ 1413 if ( addr == (unsigned long)(-1) ) 1414 continue; 1415 1416 if ( (addr & b->a_peername_mask) != b->a_peername_addr ) 1417 continue; 1418 1419#ifdef LDAP_PF_INET6 1420 /* extract IPv6 and try exact match */ 1421 } else if ( b->a_peername_style == ACL_STYLE_IPV6 ) { 1422 char *port; 1423 char buf[STRLENOF("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF") + 1]; 1424 struct berval ip; 1425 struct in6_addr addr; 1426 int port_number = -1; 1427 1428 if ( strncasecmp( op->o_conn->c_peer_name.bv_val, 1429 acl_bv_ipv6_eq.bv_val, 1430 acl_bv_ipv6_eq.bv_len ) != 0 ) 1431 continue; 1432 1433 ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ipv6_eq.bv_len; 1434 ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ipv6_eq.bv_len; 1435 1436 port = strrchr( ip.bv_val, ']' ); 1437 if ( port ) { 1438 ip.bv_len = port - ip.bv_val; 1439 ++port; 1440 if ( port[0] == ':' && lutil_atoi( &port_number, ++port ) != 0 ) 1441 continue; 1442 } 1443 1444 /* the port check can be anticipated here */ 1445 if ( b->a_peername_port != -1 && port_number != b->a_peername_port ) 1446 continue; 1447 1448 /* address longer than expected? */ 1449 if ( ip.bv_len >= sizeof(buf) ) 1450 continue; 1451 1452 AC_MEMCPY( buf, ip.bv_val, ip.bv_len ); 1453 buf[ ip.bv_len ] = '\0'; 1454 1455 if ( inet_pton( AF_INET6, buf, &addr ) != 1 ) 1456 continue; 1457 1458 /* check mask */ 1459 if ( !slap_addr6_mask( &addr, &b->a_peername_mask6, &b->a_peername_addr6 ) ) 1460 continue; 1461#endif /* LDAP_PF_INET6 */ 1462 1463#ifdef LDAP_PF_LOCAL 1464 /* extract path and try exact match */ 1465 } else if ( b->a_peername_style == ACL_STYLE_PATH ) { 1466 struct berval path; 1467 1468 if ( strncmp( op->o_conn->c_peer_name.bv_val, 1469 acl_bv_path_eq.bv_val, 1470 acl_bv_path_eq.bv_len ) != 0 ) 1471 continue; 1472 1473 path.bv_val = op->o_conn->c_peer_name.bv_val 1474 + acl_bv_path_eq.bv_len; 1475 path.bv_len = op->o_conn->c_peer_name.bv_len 1476 - acl_bv_path_eq.bv_len; 1477 1478 if ( ber_bvcmp( &b->a_peername_pat, &path ) != 0 ) 1479 continue; 1480 1481#endif /* LDAP_PF_LOCAL */ 1482 1483 /* exact match (very unlikely...) */ 1484 } else if ( ber_bvcmp( &op->o_conn->c_peer_name, &b->a_peername_pat ) != 0 ) { 1485 continue; 1486 } 1487 } 1488 } 1489 } 1490 1491 if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) { 1492 if ( BER_BVISNULL( &op->o_conn->c_sock_name ) ) { 1493 continue; 1494 } 1495 Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n", 1496 b->a_sockname_pat.bv_val, 0, 0 ); 1497 if ( !ber_bvccmp( &b->a_sockname_pat, '*' ) ) { 1498 if ( b->a_sockname_style == ACL_STYLE_REGEX) { 1499 if ( !regex_matches( &b->a_sockname_pat, op->o_conn->c_sock_name.bv_val, 1500 &e->e_nname, val, matches ) ) 1501 { 1502 continue; 1503 } 1504 1505 } else if ( b->a_sockname_style == ACL_STYLE_EXPAND ) { 1506 struct berval bv; 1507 char buf[ACL_BUF_SIZE]; 1508 1509 bv.bv_len = sizeof( buf ) - 1; 1510 bv.bv_val = buf; 1511 if ( acl_string_expand( &bv, &b->a_sockname_pat, &e->e_nname, val, matches ) ) 1512 { 1513 continue; 1514 } 1515 1516 if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_sock_name ) != 0 ) { 1517 continue; 1518 } 1519 1520 } else { 1521 if ( ber_bvstrcasecmp( &b->a_sockname_pat, &op->o_conn->c_sock_name ) != 0 ) { 1522 continue; 1523 } 1524 } 1525 } 1526 } 1527 1528 if ( b->a_dn_at != NULL ) { 1529 if ( acl_mask_dnattr( op, e, val, a, 1530 count, state, mask, 1531 &b->a_dn, &op->o_ndn ) ) 1532 { 1533 continue; 1534 } 1535 } 1536 1537 if ( b->a_realdn_at != NULL ) { 1538 struct berval ndn; 1539 1540 if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) ) 1541 { 1542 ndn = op->o_conn->c_ndn; 1543 } else { 1544 ndn = op->o_ndn; 1545 } 1546 1547 if ( acl_mask_dnattr( op, e, val, a, 1548 count, state, mask, 1549 &b->a_realdn, &ndn ) ) 1550 { 1551 continue; 1552 } 1553 } 1554 1555 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) { 1556 struct berval bv; 1557 struct berval ndn = BER_BVNULL; 1558 int rc; 1559 1560 if ( op->o_ndn.bv_len == 0 ) { 1561 continue; 1562 } 1563 1564 Debug( LDAP_DEBUG_ACL, "<= check a_group_pat: %s\n", 1565 b->a_group_pat.bv_val, 0, 0 ); 1566 1567 /* b->a_group is an unexpanded entry name, expanded it should be an 1568 * entry with objectclass group* and we test to see if odn is one of 1569 * the values in the attribute group 1570 */ 1571 /* see if asker is listed in dnattr */ 1572 if ( b->a_group_style == ACL_STYLE_EXPAND ) { 1573 char buf[ACL_BUF_SIZE]; 1574 AclRegexMatches tmp_matches, 1575 *tmp_matchesp = &tmp_matches; 1576 regmatch_t *tmp_data; 1577 1578 MATCHES_MEMSET( &tmp_matches ); 1579 tmp_data = &tmp_matches.dn_data[0]; 1580 1581 bv.bv_len = sizeof(buf) - 1; 1582 bv.bv_val = buf; 1583 1584 rc = 0; 1585 1586 if ( a->acl_attrval_style == ACL_STYLE_REGEX ) 1587 tmp_matchesp = matches; 1588 else switch ( a->acl_dn_style ) { 1589 case ACL_STYLE_REGEX: 1590 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) { 1591 tmp_matchesp = matches; 1592 break; 1593 } 1594 1595 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */ 1596 case ACL_STYLE_BASE: 1597 tmp_data[0].rm_so = 0; 1598 tmp_data[0].rm_eo = e->e_nname.bv_len; 1599 tmp_matches.dn_count = 1; 1600 break; 1601 1602 case ACL_STYLE_ONE: 1603 case ACL_STYLE_SUBTREE: 1604 case ACL_STYLE_CHILDREN: 1605 tmp_data[0].rm_so = 0; 1606 tmp_data[0].rm_eo = e->e_nname.bv_len; 1607 1608 tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len; 1609 tmp_data[1].rm_eo = e->e_nname.bv_len; 1610 tmp_matches.dn_count = 2; 1611 break; 1612 1613 default: 1614 /* error */ 1615 rc = 1; 1616 break; 1617 } 1618 1619 if ( rc ) { 1620 continue; 1621 } 1622 1623 if ( acl_string_expand( &bv, &b->a_group_pat, 1624 &e->e_nname, val, 1625 tmp_matchesp ) ) 1626 { 1627 continue; 1628 } 1629 1630 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, 1631 op->o_tmpmemctx ) != LDAP_SUCCESS ) 1632 { 1633 /* did not expand to a valid dn */ 1634 continue; 1635 } 1636 1637 bv = ndn; 1638 1639 } else { 1640 bv = b->a_group_pat; 1641 } 1642 1643 rc = backend_group( op, e, &bv, &op->o_ndn, 1644 b->a_group_oc, b->a_group_at ); 1645 1646 if ( ndn.bv_val ) { 1647 slap_sl_free( ndn.bv_val, op->o_tmpmemctx ); 1648 } 1649 1650 if ( rc != 0 ) { 1651 continue; 1652 } 1653 } 1654 1655 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) { 1656 struct berval bv; 1657 char buf[ACL_BUF_SIZE]; 1658 1659 Debug( LDAP_DEBUG_ACL, "<= check a_set_pat: %s\n", 1660 b->a_set_pat.bv_val, 0, 0 ); 1661 1662 if ( b->a_set_style == ACL_STYLE_EXPAND ) { 1663 AclRegexMatches tmp_matches, 1664 *tmp_matchesp = &tmp_matches; 1665 int rc = 0; 1666 regmatch_t *tmp_data; 1667 1668 MATCHES_MEMSET( &tmp_matches ); 1669 tmp_data = &tmp_matches.dn_data[0]; 1670 1671 bv.bv_len = sizeof( buf ) - 1; 1672 bv.bv_val = buf; 1673 1674 rc = 0; 1675 1676 if ( a->acl_attrval_style == ACL_STYLE_REGEX ) 1677 tmp_matchesp = matches; 1678 else switch ( a->acl_dn_style ) { 1679 case ACL_STYLE_REGEX: 1680 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) { 1681 tmp_matchesp = matches; 1682 break; 1683 } 1684 1685 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */ 1686 case ACL_STYLE_BASE: 1687 tmp_data[0].rm_so = 0; 1688 tmp_data[0].rm_eo = e->e_nname.bv_len; 1689 tmp_matches.dn_count = 1; 1690 break; 1691 1692 case ACL_STYLE_ONE: 1693 case ACL_STYLE_SUBTREE: 1694 case ACL_STYLE_CHILDREN: 1695 tmp_data[0].rm_so = 0; 1696 tmp_data[0].rm_eo = e->e_nname.bv_len; 1697 tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len; 1698 tmp_data[1].rm_eo = e->e_nname.bv_len; tmp_matches.dn_count = 2; 1699 break; 1700 1701 default: 1702 /* error */ 1703 rc = 1; 1704 break; 1705 } 1706 1707 if ( rc ) { 1708 continue; 1709 } 1710 1711 if ( acl_string_expand( &bv, &b->a_set_pat, 1712 &e->e_nname, val, 1713 tmp_matchesp ) ) 1714 { 1715 continue; 1716 } 1717 1718 } else { 1719 bv = b->a_set_pat; 1720 } 1721 1722 if ( acl_match_set( &bv, op, e, NULL ) == 0 ) { 1723 continue; 1724 } 1725 } 1726 1727 if ( b->a_authz.sai_ssf ) { 1728 Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n", 1729 b->a_authz.sai_ssf, op->o_ssf, 0 ); 1730 if ( b->a_authz.sai_ssf > op->o_ssf ) { 1731 continue; 1732 } 1733 } 1734 1735 if ( b->a_authz.sai_transport_ssf ) { 1736 Debug( LDAP_DEBUG_ACL, 1737 "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n", 1738 b->a_authz.sai_transport_ssf, op->o_transport_ssf, 0 ); 1739 if ( b->a_authz.sai_transport_ssf > op->o_transport_ssf ) { 1740 continue; 1741 } 1742 } 1743 1744 if ( b->a_authz.sai_tls_ssf ) { 1745 Debug( LDAP_DEBUG_ACL, 1746 "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n", 1747 b->a_authz.sai_tls_ssf, op->o_tls_ssf, 0 ); 1748 if ( b->a_authz.sai_tls_ssf > op->o_tls_ssf ) { 1749 continue; 1750 } 1751 } 1752 1753 if ( b->a_authz.sai_sasl_ssf ) { 1754 Debug( LDAP_DEBUG_ACL, 1755 "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n", 1756 b->a_authz.sai_sasl_ssf, op->o_sasl_ssf, 0 ); 1757 if ( b->a_authz.sai_sasl_ssf > op->o_sasl_ssf ) { 1758 continue; 1759 } 1760 } 1761 1762#ifdef SLAP_DYNACL 1763 if ( b->a_dynacl ) { 1764 slap_dynacl_t *da; 1765 slap_access_t tgrant, tdeny; 1766 1767 Debug( LDAP_DEBUG_ACL, "<= check a_dynacl\n", 1768 0, 0, 0 ); 1769 1770 /* this case works different from the others above. 1771 * since dynamic ACL's themselves give permissions, we need 1772 * to first check b->a_access_mask, the ACL's access level. 1773 */ 1774 /* first check if the right being requested 1775 * is allowed by the ACL clause. 1776 */ 1777 if ( ! ACL_PRIV_ISSET( b->a_access_mask, a2pmask ) ) { 1778 continue; 1779 } 1780 1781 /* start out with nothing granted, nothing denied */ 1782 ACL_INVALIDATE(tgrant); 1783 ACL_INVALIDATE(tdeny); 1784 1785 for ( da = b->a_dynacl; da; da = da->da_next ) { 1786 slap_access_t grant, 1787 deny; 1788 1789 ACL_INVALIDATE(grant); 1790 ACL_INVALIDATE(deny); 1791 1792 Debug( LDAP_DEBUG_ACL, " <= check a_dynacl: %s\n", 1793 da->da_name, 0, 0 ); 1794 1795 /* 1796 * XXXmanu Only DN matches are supplied 1797 * sending attribute values matches require 1798 * an API update 1799 */ 1800 (void)da->da_mask( da->da_private, op, e, desc, 1801 val, matches->dn_count, matches->dn_data, 1802 &grant, &deny ); 1803 1804 tgrant |= grant; 1805 tdeny |= deny; 1806 } 1807 1808 /* remove anything that the ACL clause does not allow */ 1809 tgrant &= b->a_access_mask & ACL_PRIV_MASK; 1810 tdeny &= ACL_PRIV_MASK; 1811 1812 /* see if we have anything to contribute */ 1813 if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) { 1814 continue; 1815 } 1816 1817 /* this could be improved by changing slap_acl_mask so that it can deal with 1818 * by clauses that return grant/deny pairs. Right now, it does either 1819 * additive or subtractive rights, but not both at the same time. So, 1820 * we need to combine the grant/deny pair into a single rights mask in 1821 * a smart way: if either grant or deny is "empty", then we use the 1822 * opposite as is, otherwise we remove any denied rights from the grant 1823 * rights mask and construct an additive mask. 1824 */ 1825 if (ACL_IS_INVALID(tdeny)) { 1826 modmask = tgrant | ACL_PRIV_ADDITIVE; 1827 1828 } else if (ACL_IS_INVALID(tgrant)) { 1829 modmask = tdeny | ACL_PRIV_SUBSTRACTIVE; 1830 1831 } else { 1832 modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE; 1833 } 1834 1835 } else 1836#endif /* SLAP_DYNACL */ 1837 { 1838 modmask = b->a_access_mask; 1839 } 1840 1841 Debug( LDAP_DEBUG_ACL, 1842 "<= acl_mask: [%d] applying %s (%s)\n", 1843 i, accessmask2str( modmask, accessmaskbuf, 1 ), 1844 b->a_type == ACL_CONTINUE 1845 ? "continue" 1846 : b->a_type == ACL_BREAK 1847 ? "break" 1848 : "stop" ); 1849 /* save old mask */ 1850 oldmask = *mask; 1851 1852 if( ACL_IS_ADDITIVE(modmask) ) { 1853 /* add privs */ 1854 ACL_PRIV_SET( *mask, modmask ); 1855 1856 /* cleanup */ 1857 ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK ); 1858 1859 } else if( ACL_IS_SUBTRACTIVE(modmask) ) { 1860 /* substract privs */ 1861 ACL_PRIV_CLR( *mask, modmask ); 1862 1863 /* cleanup */ 1864 ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK ); 1865 1866 } else { 1867 /* assign privs */ 1868 *mask = modmask; 1869 } 1870 1871 Debug( LDAP_DEBUG_ACL, 1872 "<= acl_mask: [%d] mask: %s\n", 1873 i, accessmask2str(*mask, accessmaskbuf, 1), 0 ); 1874 1875 if( b->a_type == ACL_CONTINUE ) { 1876 continue; 1877 1878 } else if ( b->a_type == ACL_BREAK ) { 1879 return ACL_BREAK; 1880 1881 } else { 1882 return ACL_STOP; 1883 } 1884 } 1885 1886 /* implicit "by * none" clause */ 1887 ACL_INIT(*mask); 1888 1889 Debug( LDAP_DEBUG_ACL, 1890 "<= acl_mask: no more <who> clauses, returning %s (stop)\n", 1891 accessmask2str(*mask, accessmaskbuf, 1), 0, 0 ); 1892 return ACL_STOP; 1893} 1894 1895/* 1896 * acl_check_modlist - check access control on the given entry to see if 1897 * it allows the given modifications by the user associated with op. 1898 * returns 1 if mods allowed ok 1899 * 0 mods not allowed 1900 */ 1901 1902int 1903acl_check_modlist( 1904 Operation *op, 1905 Entry *e, 1906 Modifications *mlist ) 1907{ 1908 struct berval *bv; 1909 AccessControlState state = ACL_STATE_INIT; 1910 Backend *be; 1911 int be_null = 0; 1912 int ret = 1; /* default is access allowed */ 1913 1914 be = op->o_bd; 1915 if ( be == NULL ) { 1916 be = LDAP_STAILQ_FIRST(&backendDB); 1917 be_null = 1; 1918 op->o_bd = be; 1919 } 1920 assert( be != NULL ); 1921 1922 /* If ADD attribute checking is not enabled, just allow it */ 1923 if ( op->o_tag == LDAP_REQ_ADD && !SLAP_DBACL_ADD( be )) 1924 return 1; 1925 1926 /* short circuit root database access */ 1927 if ( be_isroot( op ) ) { 1928 Debug( LDAP_DEBUG_ACL, 1929 "<= acl_access_allowed: granted to database root\n", 1930 0, 0, 0 ); 1931 goto done; 1932 } 1933 1934 /* use backend default access if no backend acls */ 1935 if( op->o_bd != NULL && op->o_bd->be_acl == NULL && frontendDB->be_acl == NULL ) { 1936 Debug( LDAP_DEBUG_ACL, 1937 "=> access_allowed: backend default %s access %s to \"%s\"\n", 1938 access2str( ACL_WRITE ), 1939 op->o_bd->be_dfltaccess >= ACL_WRITE 1940 ? "granted" : "denied", 1941 op->o_dn.bv_val ); 1942 ret = (op->o_bd->be_dfltaccess >= ACL_WRITE); 1943 goto done; 1944 } 1945 1946 for ( ; mlist != NULL; mlist = mlist->sml_next ) { 1947 /* 1948 * Internal mods are ignored by ACL_WRITE checking 1949 */ 1950 if ( mlist->sml_flags & SLAP_MOD_INTERNAL ) { 1951 Debug( LDAP_DEBUG_ACL, "acl: internal mod %s:" 1952 " modify access granted\n", 1953 mlist->sml_desc->ad_cname.bv_val, 0, 0 ); 1954 continue; 1955 } 1956 1957 /* 1958 * no-user-modification operational attributes are ignored 1959 * by ACL_WRITE checking as any found here are not provided 1960 * by the user 1961 */ 1962 if ( is_at_no_user_mod( mlist->sml_desc->ad_type ) 1963 && ! ( mlist->sml_flags & SLAP_MOD_MANAGING ) ) 1964 { 1965 Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:" 1966 " modify access granted\n", 1967 mlist->sml_desc->ad_cname.bv_val, 0, 0 ); 1968 continue; 1969 } 1970 1971 switch ( mlist->sml_op ) { 1972 case LDAP_MOD_REPLACE: 1973 case LDAP_MOD_INCREMENT: 1974 /* 1975 * We must check both permission to delete the whole 1976 * attribute and permission to add the specific attributes. 1977 * This prevents abuse from selfwriters. 1978 */ 1979 if ( ! access_allowed( op, e, 1980 mlist->sml_desc, NULL, 1981 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL, 1982 &state ) ) 1983 { 1984 ret = 0; 1985 goto done; 1986 } 1987 1988 if ( mlist->sml_values == NULL ) break; 1989 1990 /* fall thru to check value to add */ 1991 1992 case LDAP_MOD_ADD: 1993 case SLAP_MOD_ADD_IF_NOT_PRESENT: 1994 assert( mlist->sml_values != NULL ); 1995 1996 if ( mlist->sml_op == SLAP_MOD_ADD_IF_NOT_PRESENT 1997 && attr_find( e->e_attrs, mlist->sml_desc ) ) 1998 { 1999 break; 2000 } 2001 2002 for ( bv = mlist->sml_nvalues 2003 ? mlist->sml_nvalues : mlist->sml_values; 2004 bv->bv_val != NULL; bv++ ) 2005 { 2006 if ( ! access_allowed( op, e, 2007 mlist->sml_desc, bv, 2008 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WADD, 2009 &state ) ) 2010 { 2011 ret = 0; 2012 goto done; 2013 } 2014 } 2015 break; 2016 2017 case LDAP_MOD_DELETE: 2018 case SLAP_MOD_SOFTDEL: 2019 if ( mlist->sml_values == NULL ) { 2020 if ( ! access_allowed( op, e, 2021 mlist->sml_desc, NULL, 2022 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL, 2023 &state ) ) 2024 { 2025 ret = 0; 2026 goto done; 2027 } 2028 break; 2029 } 2030 for ( bv = mlist->sml_nvalues 2031 ? mlist->sml_nvalues : mlist->sml_values; 2032 bv->bv_val != NULL; bv++ ) 2033 { 2034 if ( ! access_allowed( op, e, 2035 mlist->sml_desc, bv, 2036 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL, 2037 &state ) ) 2038 { 2039 ret = 0; 2040 goto done; 2041 } 2042 } 2043 break; 2044 2045 case SLAP_MOD_SOFTADD: 2046 /* allow adding attribute via modrdn thru */ 2047 break; 2048 2049 default: 2050 assert( 0 ); 2051 /* not reached */ 2052 ret = 0; 2053 break; 2054 } 2055 } 2056 2057done: 2058 if (be_null) op->o_bd = NULL; 2059 return( ret ); 2060} 2061 2062int 2063acl_get_part( 2064 struct berval *list, 2065 int ix, 2066 char sep, 2067 struct berval *bv ) 2068{ 2069 int len; 2070 char *p; 2071 2072 if ( bv ) { 2073 BER_BVZERO( bv ); 2074 } 2075 len = list->bv_len; 2076 p = list->bv_val; 2077 while ( len >= 0 && --ix >= 0 ) { 2078 while ( --len >= 0 && *p++ != sep ) 2079 ; 2080 } 2081 while ( len >= 0 && *p == ' ' ) { 2082 len--; 2083 p++; 2084 } 2085 if ( len < 0 ) { 2086 return -1; 2087 } 2088 2089 if ( !bv ) { 2090 return 0; 2091 } 2092 2093 bv->bv_val = p; 2094 while ( --len >= 0 && *p != sep ) { 2095 bv->bv_len++; 2096 p++; 2097 } 2098 while ( bv->bv_len > 0 && *--p == ' ' ) { 2099 bv->bv_len--; 2100 } 2101 2102 return bv->bv_len; 2103} 2104 2105typedef struct acl_set_gather_t { 2106 SetCookie *cookie; 2107 BerVarray bvals; 2108} acl_set_gather_t; 2109 2110static int 2111acl_set_cb_gather( Operation *op, SlapReply *rs ) 2112{ 2113 acl_set_gather_t *p = (acl_set_gather_t *)op->o_callback->sc_private; 2114 2115 if ( rs->sr_type == REP_SEARCH ) { 2116 BerValue bvals[ 2 ]; 2117 BerVarray bvalsp = NULL; 2118 int j; 2119 2120 for ( j = 0; !BER_BVISNULL( &rs->sr_attrs[ j ].an_name ); j++ ) { 2121 AttributeDescription *desc = rs->sr_attrs[ j ].an_desc; 2122 2123 if ( desc == NULL ) { 2124 continue; 2125 } 2126 2127 if ( desc == slap_schema.si_ad_entryDN ) { 2128 bvalsp = bvals; 2129 bvals[ 0 ] = rs->sr_entry->e_nname; 2130 BER_BVZERO( &bvals[ 1 ] ); 2131 2132 } else { 2133 Attribute *a; 2134 2135 a = attr_find( rs->sr_entry->e_attrs, desc ); 2136 if ( a != NULL ) { 2137 bvalsp = a->a_nvals; 2138 } 2139 } 2140 2141 if ( bvalsp ) { 2142 p->bvals = slap_set_join( p->cookie, p->bvals, 2143 ( '|' | SLAP_SET_RREF ), bvalsp ); 2144 } 2145 } 2146 2147 } else { 2148 switch ( rs->sr_type ) { 2149 case REP_SEARCHREF: 2150 case REP_INTERMEDIATE: 2151 /* ignore */ 2152 break; 2153 2154 default: 2155 assert( rs->sr_type == REP_RESULT ); 2156 break; 2157 } 2158 } 2159 2160 return 0; 2161} 2162 2163BerVarray 2164acl_set_gather( SetCookie *cookie, struct berval *name, AttributeDescription *desc ) 2165{ 2166 AclSetCookie *cp = (AclSetCookie *)cookie; 2167 int rc = 0; 2168 LDAPURLDesc *ludp = NULL; 2169 Operation op2 = { 0 }; 2170 SlapReply rs = {REP_RESULT}; 2171 AttributeName anlist[ 2 ], *anlistp = NULL; 2172 int nattrs = 0; 2173 slap_callback cb = { NULL, acl_set_cb_gather, NULL, NULL }; 2174 acl_set_gather_t p = { 0 }; 2175 2176 /* this routine needs to return the bervals instead of 2177 * plain strings, since syntax is not known. It should 2178 * also return the syntax or some "comparison cookie". 2179 */ 2180 if ( strncasecmp( name->bv_val, "ldap:///", STRLENOF( "ldap:///" ) ) != 0 ) { 2181 return acl_set_gather2( cookie, name, desc ); 2182 } 2183 2184 rc = ldap_url_parse( name->bv_val, &ludp ); 2185 if ( rc != LDAP_URL_SUCCESS ) { 2186 Debug( LDAP_DEBUG_TRACE, 2187 "%s acl_set_gather: unable to parse URL=\"%s\"\n", 2188 cp->asc_op->o_log_prefix, name->bv_val, 0 ); 2189 2190 rc = LDAP_PROTOCOL_ERROR; 2191 goto url_done; 2192 } 2193 2194 if ( ( ludp->lud_host && ludp->lud_host[0] ) || ludp->lud_exts ) 2195 { 2196 /* host part must be empty */ 2197 /* extensions parts must be empty */ 2198 Debug( LDAP_DEBUG_TRACE, 2199 "%s acl_set_gather: host/exts must be absent in URL=\"%s\"\n", 2200 cp->asc_op->o_log_prefix, name->bv_val, 0 ); 2201 2202 rc = LDAP_PROTOCOL_ERROR; 2203 goto url_done; 2204 } 2205 2206 /* Grab the searchbase and see if an appropriate database can be found */ 2207 ber_str2bv( ludp->lud_dn, 0, 0, &op2.o_req_dn ); 2208 rc = dnNormalize( 0, NULL, NULL, &op2.o_req_dn, 2209 &op2.o_req_ndn, cp->asc_op->o_tmpmemctx ); 2210 BER_BVZERO( &op2.o_req_dn ); 2211 if ( rc != LDAP_SUCCESS ) { 2212 Debug( LDAP_DEBUG_TRACE, 2213 "%s acl_set_gather: DN=\"%s\" normalize failed\n", 2214 cp->asc_op->o_log_prefix, ludp->lud_dn, 0 ); 2215 2216 goto url_done; 2217 } 2218 2219 op2.o_bd = select_backend( &op2.o_req_ndn, 1 ); 2220 if ( ( op2.o_bd == NULL ) || ( op2.o_bd->be_search == NULL ) ) { 2221 Debug( LDAP_DEBUG_TRACE, 2222 "%s acl_set_gather: no database could be selected for DN=\"%s\"\n", 2223 cp->asc_op->o_log_prefix, op2.o_req_ndn.bv_val, 0 ); 2224 2225 rc = LDAP_NO_SUCH_OBJECT; 2226 goto url_done; 2227 } 2228 2229 /* Grab the filter */ 2230 if ( ludp->lud_filter ) { 2231 ber_str2bv_x( ludp->lud_filter, 0, 0, &op2.ors_filterstr, 2232 cp->asc_op->o_tmpmemctx ); 2233 op2.ors_filter = str2filter_x( cp->asc_op, op2.ors_filterstr.bv_val ); 2234 if ( op2.ors_filter == NULL ) { 2235 Debug( LDAP_DEBUG_TRACE, 2236 "%s acl_set_gather: unable to parse filter=\"%s\"\n", 2237 cp->asc_op->o_log_prefix, op2.ors_filterstr.bv_val, 0 ); 2238 2239 rc = LDAP_PROTOCOL_ERROR; 2240 goto url_done; 2241 } 2242 2243 } else { 2244 op2.ors_filterstr = *slap_filterstr_objectClass_pres; 2245 op2.ors_filter = (Filter *)slap_filter_objectClass_pres; 2246 } 2247 2248 2249 /* Grab the scope */ 2250 op2.ors_scope = ludp->lud_scope; 2251 2252 /* Grap the attributes */ 2253 if ( ludp->lud_attrs ) { 2254 int i; 2255 2256 for ( ; ludp->lud_attrs[ nattrs ]; nattrs++ ) 2257 ; 2258 2259 anlistp = slap_sl_calloc( sizeof( AttributeName ), nattrs + 2, 2260 cp->asc_op->o_tmpmemctx ); 2261 2262 for ( i = 0, nattrs = 0; ludp->lud_attrs[ i ]; i++ ) { 2263 struct berval name; 2264 AttributeDescription *desc = NULL; 2265 const char *text = NULL; 2266 2267 ber_str2bv( ludp->lud_attrs[ i ], 0, 0, &name ); 2268 rc = slap_bv2ad( &name, &desc, &text ); 2269 if ( rc == LDAP_SUCCESS ) { 2270 anlistp[ nattrs ].an_name = name; 2271 anlistp[ nattrs ].an_desc = desc; 2272 nattrs++; 2273 } 2274 } 2275 2276 } else { 2277 anlistp = anlist; 2278 } 2279 2280 anlistp[ nattrs ].an_name = desc->ad_cname; 2281 anlistp[ nattrs ].an_desc = desc; 2282 2283 BER_BVZERO( &anlistp[ nattrs + 1 ].an_name ); 2284 2285 p.cookie = cookie; 2286 2287 op2.o_hdr = cp->asc_op->o_hdr; 2288 op2.o_tag = LDAP_REQ_SEARCH; 2289 op2.o_ndn = op2.o_bd->be_rootndn; 2290 op2.o_callback = &cb; 2291 slap_op_time( &op2.o_time, &op2.o_tincr ); 2292 op2.o_do_not_cache = 1; 2293 op2.o_is_auth_check = 0; 2294 ber_dupbv_x( &op2.o_req_dn, &op2.o_req_ndn, cp->asc_op->o_tmpmemctx ); 2295 op2.ors_slimit = SLAP_NO_LIMIT; 2296 op2.ors_tlimit = SLAP_NO_LIMIT; 2297 op2.ors_attrs = anlistp; 2298 op2.ors_attrsonly = 0; 2299 op2.o_private = cp->asc_op->o_private; 2300 op2.o_extra = cp->asc_op->o_extra; 2301 2302 cb.sc_private = &p; 2303 2304 rc = op2.o_bd->be_search( &op2, &rs ); 2305 if ( rc != 0 ) { 2306 goto url_done; 2307 } 2308 2309url_done:; 2310 if ( op2.ors_filter && op2.ors_filter != slap_filter_objectClass_pres ) { 2311 filter_free_x( cp->asc_op, op2.ors_filter, 1 ); 2312 } 2313 if ( !BER_BVISNULL( &op2.o_req_ndn ) ) { 2314 slap_sl_free( op2.o_req_ndn.bv_val, cp->asc_op->o_tmpmemctx ); 2315 } 2316 if ( !BER_BVISNULL( &op2.o_req_dn ) ) { 2317 slap_sl_free( op2.o_req_dn.bv_val, cp->asc_op->o_tmpmemctx ); 2318 } 2319 if ( ludp ) { 2320 ldap_free_urldesc( ludp ); 2321 } 2322 if ( anlistp && anlistp != anlist ) { 2323 slap_sl_free( anlistp, cp->asc_op->o_tmpmemctx ); 2324 } 2325 2326 return p.bvals; 2327} 2328 2329BerVarray 2330acl_set_gather2( SetCookie *cookie, struct berval *name, AttributeDescription *desc ) 2331{ 2332 AclSetCookie *cp = (AclSetCookie *)cookie; 2333 BerVarray bvals = NULL; 2334 struct berval ndn; 2335 int rc = 0; 2336 2337 /* this routine needs to return the bervals instead of 2338 * plain strings, since syntax is not known. It should 2339 * also return the syntax or some "comparison cookie". 2340 */ 2341 rc = dnNormalize( 0, NULL, NULL, name, &ndn, cp->asc_op->o_tmpmemctx ); 2342 if ( rc == LDAP_SUCCESS ) { 2343 if ( desc == slap_schema.si_ad_entryDN ) { 2344 bvals = (BerVarray)slap_sl_malloc( sizeof( BerValue ) * 2, 2345 cp->asc_op->o_tmpmemctx ); 2346 bvals[ 0 ] = ndn; 2347 BER_BVZERO( &bvals[ 1 ] ); 2348 BER_BVZERO( &ndn ); 2349 2350 } else { 2351 backend_attribute( cp->asc_op, 2352 cp->asc_e, &ndn, desc, &bvals, ACL_NONE ); 2353 } 2354 2355 if ( !BER_BVISNULL( &ndn ) ) { 2356 slap_sl_free( ndn.bv_val, cp->asc_op->o_tmpmemctx ); 2357 } 2358 } 2359 2360 return bvals; 2361} 2362 2363int 2364acl_match_set ( 2365 struct berval *subj, 2366 Operation *op, 2367 Entry *e, 2368 struct berval *default_set_attribute ) 2369{ 2370 struct berval set = BER_BVNULL; 2371 int rc = 0; 2372 AclSetCookie cookie; 2373 2374 if ( default_set_attribute == NULL ) { 2375 set = *subj; 2376 2377 } else { 2378 struct berval subjdn, ndn = BER_BVNULL; 2379 struct berval setat; 2380 BerVarray bvals = NULL; 2381 const char *text; 2382 AttributeDescription *desc = NULL; 2383 2384 /* format of string is "entry/setAttrName" */ 2385 if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) { 2386 return 0; 2387 } 2388 2389 if ( acl_get_part( subj, 1, '/', &setat ) < 0 ) { 2390 setat = *default_set_attribute; 2391 } 2392 2393 /* 2394 * NOTE: dnNormalize honors the ber_len field 2395 * as the length of the dn to be normalized 2396 */ 2397 if ( slap_bv2ad( &setat, &desc, &text ) == LDAP_SUCCESS ) { 2398 if ( dnNormalize( 0, NULL, NULL, &subjdn, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS ) 2399 { 2400 backend_attribute( op, e, &ndn, desc, &bvals, ACL_NONE ); 2401 if ( bvals != NULL && !BER_BVISNULL( &bvals[0] ) ) { 2402 int i; 2403 2404 set = bvals[0]; 2405 BER_BVZERO( &bvals[0] ); 2406 for ( i = 1; !BER_BVISNULL( &bvals[i] ); i++ ) 2407 /* count */ ; 2408 bvals[0].bv_val = bvals[i-1].bv_val; 2409 BER_BVZERO( &bvals[i-1] ); 2410 } 2411 ber_bvarray_free_x( bvals, op->o_tmpmemctx ); 2412 slap_sl_free( ndn.bv_val, op->o_tmpmemctx ); 2413 } 2414 } 2415 } 2416 2417 if ( !BER_BVISNULL( &set ) ) { 2418 cookie.asc_op = op; 2419 cookie.asc_e = e; 2420 rc = ( slap_set_filter( 2421 acl_set_gather, 2422 (SetCookie *)&cookie, &set, 2423 &op->o_ndn, &e->e_nname, NULL ) > 0 ); 2424 if ( set.bv_val != subj->bv_val ) { 2425 slap_sl_free( set.bv_val, op->o_tmpmemctx ); 2426 } 2427 } 2428 2429 return(rc); 2430} 2431 2432#ifdef SLAP_DYNACL 2433 2434/* 2435 * dynamic ACL infrastructure 2436 */ 2437static slap_dynacl_t *da_list = NULL; 2438 2439int 2440slap_dynacl_register( slap_dynacl_t *da ) 2441{ 2442 slap_dynacl_t *tmp; 2443 2444 for ( tmp = da_list; tmp; tmp = tmp->da_next ) { 2445 if ( strcasecmp( da->da_name, tmp->da_name ) == 0 ) { 2446 break; 2447 } 2448 } 2449 2450 if ( tmp != NULL ) { 2451 return -1; 2452 } 2453 2454 if ( da->da_mask == NULL ) { 2455 return -1; 2456 } 2457 2458 da->da_private = NULL; 2459 da->da_next = da_list; 2460 da_list = da; 2461 2462 return 0; 2463} 2464 2465static slap_dynacl_t * 2466slap_dynacl_next( slap_dynacl_t *da ) 2467{ 2468 if ( da ) { 2469 return da->da_next; 2470 } 2471 return da_list; 2472} 2473 2474slap_dynacl_t * 2475slap_dynacl_get( const char *name ) 2476{ 2477 slap_dynacl_t *da; 2478 2479 for ( da = slap_dynacl_next( NULL ); da; da = slap_dynacl_next( da ) ) { 2480 if ( strcasecmp( da->da_name, name ) == 0 ) { 2481 break; 2482 } 2483 } 2484 2485 return da; 2486} 2487#endif /* SLAP_DYNACL */ 2488 2489/* 2490 * statically built-in dynamic ACL initialization 2491 */ 2492 #ifdef SLAP_DYNACL 2493extern int dynacl_idattr_init( void ); 2494 #endif 2495 2496static int (*acl_init_func[])( void ) = { 2497#ifdef SLAP_DYNACL 2498 /* TODO: remove when ACI will only be dynamic */ 2499#if SLAPD_ACI_ENABLED == SLAPD_MOD_STATIC 2500dynacl_aci_init, 2501#endif /* SLAPD_ACI_ENABLED */ 2502dynacl_idattr_init, 2503#endif /* SLAP_DYNACL */ 2504 2505NULL 2506}; 2507 2508int 2509acl_init( void ) 2510{ 2511 int i, rc; 2512 2513 for ( i = 0; acl_init_func[ i ] != NULL; i++ ) { 2514 rc = (*(acl_init_func[ i ]))(); 2515 if ( rc != 0 ) { 2516 return rc; 2517 } 2518 } 2519 2520 return 0; 2521} 2522 2523int 2524acl_string_expand( 2525 struct berval *bv, 2526 struct berval *pat, 2527 struct berval *dn_matches, 2528 struct berval *val_matches, 2529 AclRegexMatches *matches) 2530{ 2531 ber_len_t size; 2532 char *sp; 2533 char *dp; 2534 int flag; 2535 enum { DN_FLAG, VAL_FLAG } tflag; 2536 2537 size = 0; 2538 bv->bv_val[0] = '\0'; 2539 bv->bv_len--; /* leave space for lone $ */ 2540 2541 flag = 0; 2542 tflag = DN_FLAG; 2543 for ( dp = bv->bv_val, sp = pat->bv_val; size < bv->bv_len && 2544 sp < pat->bv_val + pat->bv_len ; sp++ ) 2545 { 2546 /* did we previously see a $ */ 2547 if ( flag ) { 2548 if ( flag == 1 && *sp == '$' ) { 2549 *dp++ = '$'; 2550 size++; 2551 flag = 0; 2552 tflag = DN_FLAG; 2553 2554 } else if ( flag == 2 && *sp == 'v' /*'}'*/) { 2555 tflag = VAL_FLAG; 2556 2557 } else if ( flag == 2 && *sp == 'd' /*'}'*/) { 2558 tflag = DN_FLAG; 2559 2560 } else if ( flag == 1 && *sp == '{' /*'}'*/) { 2561 flag = 2; 2562 2563 } else if ( *sp >= '0' && *sp <= '9' ) { 2564 int nm; 2565 regmatch_t *m; 2566 char *data; 2567 int n; 2568 int i; 2569 int l; 2570 2571 n = *sp - '0'; 2572 2573 if ( flag == 2 ) { 2574 for ( sp++; *sp != '\0' && *sp != /*'{'*/ '}'; sp++ ) { 2575 if ( *sp >= '0' && *sp <= '9' ) { 2576 n = 10*n + ( *sp - '0' ); 2577 } 2578 } 2579 2580 if ( *sp != /*'{'*/ '}' ) { 2581 /* FIXME: error */ 2582 return 1; 2583 } 2584 } 2585 2586 switch (tflag) { 2587 case DN_FLAG: 2588 nm = matches->dn_count; 2589 m = matches->dn_data; 2590 data = dn_matches ? dn_matches->bv_val : NULL; 2591 break; 2592 case VAL_FLAG: 2593 nm = matches->val_count; 2594 m = matches->val_data; 2595 data = val_matches ? val_matches->bv_val : NULL; 2596 break; 2597 default: 2598 assert( 0 ); 2599 } 2600 if ( n >= nm ) { 2601 /* FIXME: error */ 2602 return 1; 2603 } 2604 if ( data == NULL ) { 2605 /* FIXME: error */ 2606 return 1; 2607 } 2608 2609 *dp = '\0'; 2610 i = m[n].rm_so; 2611 l = m[n].rm_eo; 2612 2613 for ( ; size < bv->bv_len && i < l; size++, i++ ) { 2614 *dp++ = data[i]; 2615 } 2616 *dp = '\0'; 2617 2618 flag = 0; 2619 tflag = DN_FLAG; 2620 } 2621 } else { 2622 if (*sp == '$') { 2623 flag = 1; 2624 } else { 2625 *dp++ = *sp; 2626 size++; 2627 } 2628 } 2629 } 2630 2631 if ( flag ) { 2632 /* must have ended with a single $ */ 2633 *dp++ = '$'; 2634 size++; 2635 } 2636 2637 *dp = '\0'; 2638 bv->bv_len = size; 2639 2640 Debug( LDAP_DEBUG_ACL, "=> acl_string_expand: pattern: %.*s\n", (int)pat->bv_len, pat->bv_val, 0 ); 2641 Debug( LDAP_DEBUG_ACL, "=> acl_string_expand: expanded: %s\n", bv->bv_val, 0, 0 ); 2642 2643 return 0; 2644} 2645 2646static int 2647regex_matches( 2648 struct berval *pat, /* pattern to expand and match against */ 2649 char *str, /* string to match against pattern */ 2650 struct berval *dn_matches, /* buffer with $N expansion variables from DN */ 2651 struct berval *val_matches, /* buffer with $N expansion variables from val */ 2652 AclRegexMatches *matches /* offsets in buffer for $N expansion variables */ 2653) 2654{ 2655 regex_t re; 2656 char newbuf[ACL_BUF_SIZE]; 2657 struct berval bv; 2658 int rc; 2659 2660 bv.bv_len = sizeof( newbuf ) - 1; 2661 bv.bv_val = newbuf; 2662 2663 if (str == NULL) { 2664 str = ""; 2665 }; 2666 2667 acl_string_expand( &bv, pat, dn_matches, val_matches, matches ); 2668 rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE ); 2669 if ( rc ) { 2670 char error[ACL_BUF_SIZE]; 2671 regerror( rc, &re, error, sizeof( error ) ); 2672 2673 Debug( LDAP_DEBUG_TRACE, 2674 "compile( \"%s\", \"%s\") failed %s\n", 2675 pat->bv_val, str, error ); 2676 return( 0 ); 2677 } 2678 2679 rc = regexec( &re, str, 0, NULL, 0 ); 2680 regfree( &re ); 2681 2682 Debug( LDAP_DEBUG_TRACE, 2683 "=> regex_matches: string: %s\n", str, 0, 0 ); 2684 Debug( LDAP_DEBUG_TRACE, 2685 "=> regex_matches: rc: %d %s\n", 2686 rc, !rc ? "matches" : "no matches", 0 ); 2687 return( !rc ); 2688} 2689 2690