1147072Sbrooks/* $NetBSD: aclparse.c,v 1.3 2021/08/14 16:14:58 christos Exp $ */ 2147072Sbrooks 3147072Sbrooks/* aclparse.c - routines to parse and check acl's */ 4147072Sbrooks/* $OpenLDAP$ */ 5147072Sbrooks/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6147072Sbrooks * 7147072Sbrooks * Copyright 1998-2021 The OpenLDAP Foundation. 8147072Sbrooks * All rights reserved. 9147072Sbrooks * 10147072Sbrooks * Redistribution and use in source and binary forms, with or without 11147072Sbrooks * modification, are permitted only as authorized by the OpenLDAP 12147072Sbrooks * Public License. 13147072Sbrooks * 14147072Sbrooks * A copy of this license is available in the file LICENSE in the 15147072Sbrooks * top-level directory of the distribution or, alternatively, at 16147072Sbrooks * <http://www.OpenLDAP.org/license.html>. 17147072Sbrooks */ 18147072Sbrooks/* Portions Copyright (c) 1995 Regents of the University of Michigan. 19147072Sbrooks * All rights reserved. 20147072Sbrooks * 21147072Sbrooks * Redistribution and use in source and binary forms are permitted 22147072Sbrooks * provided that this notice is preserved and that due credit is given 23147072Sbrooks * to the University of Michigan at Ann Arbor. The name of the University 24147072Sbrooks * may not be used to endorse or promote products derived from this 25147072Sbrooks * software without specific prior written permission. This software 26147072Sbrooks * is provided ``as is'' without express or implied warranty. 27147072Sbrooks */ 28147072Sbrooks 29147072Sbrooks#include <sys/cdefs.h> 30147072Sbrooks__RCSID("$NetBSD: aclparse.c,v 1.3 2021/08/14 16:14:58 christos Exp $"); 31147072Sbrooks 32147072Sbrooks#include "portable.h" 33147072Sbrooks 34147072Sbrooks#include <stdio.h> 35147072Sbrooks 36147072Sbrooks#include <ac/ctype.h> 37147072Sbrooks#include <ac/regex.h> 38147072Sbrooks#include <ac/socket.h> 39147072Sbrooks#include <ac/string.h> 40147072Sbrooks#include <ac/unistd.h> 41147072Sbrooks 42147072Sbrooks#include "slap.h" 43147072Sbrooks#include "lber_pvt.h" 44147072Sbrooks#include "lutil.h" 45147072Sbrooks 46147072Sbrooksstatic const char style_base[] = "base"; 47147072Sbrooksconst char *style_strings[] = { 48147072Sbrooks "regex", 49147072Sbrooks "expand", 50147072Sbrooks "exact", 51147072Sbrooks "one", 52147072Sbrooks "subtree", 53147072Sbrooks "children", 54147072Sbrooks "level", 55147072Sbrooks "attrof", 56147072Sbrooks "anonymous", 57 "users", 58 "self", 59 "ip", 60 "ipv6", 61 "path", 62 NULL 63}; 64 65#define ACLBUF_CHUNKSIZE 8192 66static struct berval aclbuf; 67 68static void split(char *line, int splitchar, char **left, char **right); 69static void access_append(Access **l, Access *a); 70static void access_free( Access *a ); 71static int acl_usage(void); 72 73static void acl_regex_normalized_dn(const char *src, struct berval *pat); 74 75#ifdef LDAP_DEBUG 76static void print_acl(Backend *be, AccessControl *a); 77#endif 78 79static int check_scope( BackendDB *be, AccessControl *a ); 80 81#ifdef SLAP_DYNACL 82static int 83slap_dynacl_config( 84 const char *fname, 85 int lineno, 86 Access *b, 87 const char *name, 88 const char *opts, 89 slap_style_t sty, 90 const char *right ) 91{ 92 slap_dynacl_t *da, *tmp; 93 int rc = 0; 94 95 for ( da = b->a_dynacl; da; da = da->da_next ) { 96 if ( strcasecmp( da->da_name, name ) == 0 ) { 97 Debug( LDAP_DEBUG_ANY, 98 "%s: line %d: dynacl \"%s\" already specified.\n", 99 fname, lineno, name ); 100 return acl_usage(); 101 } 102 } 103 104 da = slap_dynacl_get( name ); 105 if ( da == NULL ) { 106 return -1; 107 } 108 109 tmp = ch_malloc( sizeof( slap_dynacl_t ) ); 110 *tmp = *da; 111 112 if ( tmp->da_parse ) { 113 rc = ( *tmp->da_parse )( fname, lineno, opts, sty, right, &tmp->da_private ); 114 if ( rc ) { 115 ch_free( tmp ); 116 return rc; 117 } 118 } 119 120 tmp->da_next = b->a_dynacl; 121 b->a_dynacl = tmp; 122 123 return 0; 124} 125#endif /* SLAP_DYNACL */ 126 127static void 128regtest(const char *fname, int lineno, char *pat) { 129 int e; 130 regex_t re; 131 132 char buf[ SLAP_TEXT_BUFLEN ]; 133 unsigned size; 134 135 char *sp; 136 char *dp; 137 int flag; 138 139 sp = pat; 140 dp = buf; 141 size = 0; 142 buf[0] = '\0'; 143 144 for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) { 145 if (flag) { 146 if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) { 147 *dp++ = *sp; 148 size++; 149 } 150 flag = 0; 151 152 } else { 153 if (*sp == '$') { 154 flag = 1; 155 } else { 156 *dp++ = *sp; 157 size++; 158 } 159 } 160 } 161 162 *dp = '\0'; 163 if ( size >= (sizeof(buf) - 1) ) { 164 Debug( LDAP_DEBUG_ANY, 165 "%s: line %d: regular expression \"%s\" too large\n", 166 fname, lineno, pat ); 167 (void)acl_usage(); 168 exit( EXIT_FAILURE ); 169 } 170 171 if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) { 172 char error[ SLAP_TEXT_BUFLEN ]; 173 174 regerror(e, &re, error, sizeof(error)); 175 176 Debug(LDAP_DEBUG_ANY, 177 "%s: line %d: regular expression \"%s\" bad because of %s\n", 178 fname, lineno, pat, error ); 179 acl_usage(); 180 exit( EXIT_FAILURE ); 181 } 182 regfree(&re); 183} 184 185/* 186 * Experimental 187 * 188 * Check if the pattern of an ACL, if any, matches the scope 189 * of the backend it is defined within. 190 */ 191#define ACL_SCOPE_UNKNOWN (-2) 192#define ACL_SCOPE_ERR (-1) 193#define ACL_SCOPE_OK (0) 194#define ACL_SCOPE_PARTIAL (1) 195#define ACL_SCOPE_WARN (2) 196 197static int 198check_scope( BackendDB *be, AccessControl *a ) 199{ 200 ber_len_t patlen; 201 struct berval dn; 202 203 dn = be->be_nsuffix[0]; 204 205 if ( BER_BVISEMPTY( &dn ) ) { 206 return ACL_SCOPE_OK; 207 } 208 209 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) || 210 a->acl_dn_style != ACL_STYLE_REGEX ) 211 { 212 slap_style_t style = a->acl_dn_style; 213 214 if ( style == ACL_STYLE_REGEX ) { 215 char dnbuf[SLAP_LDAPDN_MAXLEN + 2]; 216 char rebuf[SLAP_LDAPDN_MAXLEN + 1]; 217 ber_len_t rebuflen; 218 regex_t re; 219 int rc; 220 221 /* add trailing '$' to database suffix to form 222 * a simple trial regex pattern "<suffix>$" */ 223 AC_MEMCPY( dnbuf, be->be_nsuffix[0].bv_val, 224 be->be_nsuffix[0].bv_len ); 225 dnbuf[be->be_nsuffix[0].bv_len] = '$'; 226 dnbuf[be->be_nsuffix[0].bv_len + 1] = '\0'; 227 228 if ( regcomp( &re, dnbuf, REG_EXTENDED|REG_ICASE ) ) { 229 return ACL_SCOPE_WARN; 230 } 231 232 /* remove trailing ')$', if any, from original 233 * regex pattern */ 234 rebuflen = a->acl_dn_pat.bv_len; 235 AC_MEMCPY( rebuf, a->acl_dn_pat.bv_val, rebuflen + 1 ); 236 if ( rebuf[rebuflen - 1] == '$' ) { 237 rebuf[--rebuflen] = '\0'; 238 } 239 while ( rebuflen > be->be_nsuffix[0].bv_len && rebuf[rebuflen - 1] == ')' ) { 240 rebuf[--rebuflen] = '\0'; 241 } 242 if ( rebuflen == be->be_nsuffix[0].bv_len ) { 243 rc = ACL_SCOPE_WARN; 244 goto regex_done; 245 } 246 247 /* not a clear indication of scoping error, though */ 248 rc = regexec( &re, rebuf, 0, NULL, 0 ) 249 ? ACL_SCOPE_WARN : ACL_SCOPE_OK; 250 251regex_done:; 252 regfree( &re ); 253 return rc; 254 } 255 256 patlen = a->acl_dn_pat.bv_len; 257 /* If backend suffix is longer than pattern, 258 * it is a potential mismatch (in the sense 259 * that a superior naming context could 260 * match */ 261 if ( dn.bv_len > patlen ) { 262 /* base is blatantly wrong */ 263 if ( style == ACL_STYLE_BASE ) return ACL_SCOPE_ERR; 264 265 /* a style of one can be wrong if there is 266 * more than one level between the suffix 267 * and the pattern */ 268 if ( style == ACL_STYLE_ONE ) { 269 ber_len_t rdnlen = 0; 270 int sep = 0; 271 272 if ( patlen > 0 ) { 273 if ( !DN_SEPARATOR( dn.bv_val[dn.bv_len - patlen - 1] )) { 274 return ACL_SCOPE_ERR; 275 } 276 sep = 1; 277 } 278 279 rdnlen = dn_rdnlen( NULL, &dn ); 280 if ( rdnlen != dn.bv_len - patlen - sep ) 281 return ACL_SCOPE_ERR; 282 } 283 284 /* if the trailing part doesn't match, 285 * then it's an error */ 286 if ( strcmp( a->acl_dn_pat.bv_val, 287 &dn.bv_val[dn.bv_len - patlen] ) != 0 ) 288 { 289 return ACL_SCOPE_ERR; 290 } 291 292 return ACL_SCOPE_PARTIAL; 293 } 294 295 switch ( style ) { 296 case ACL_STYLE_BASE: 297 case ACL_STYLE_ONE: 298 case ACL_STYLE_CHILDREN: 299 case ACL_STYLE_SUBTREE: 300 break; 301 302 default: 303 assert( 0 ); 304 break; 305 } 306 307 if ( dn.bv_len < patlen && 308 !DN_SEPARATOR( a->acl_dn_pat.bv_val[patlen - dn.bv_len - 1] )) 309 { 310 return ACL_SCOPE_ERR; 311 } 312 313 if ( strcmp( &a->acl_dn_pat.bv_val[patlen - dn.bv_len], dn.bv_val ) 314 != 0 ) 315 { 316 return ACL_SCOPE_ERR; 317 } 318 319 return ACL_SCOPE_OK; 320 } 321 322 return ACL_SCOPE_UNKNOWN; 323} 324 325int 326parse_acl( 327 Backend *be, 328 const char *fname, 329 int lineno, 330 int argc, 331 char **argv, 332 int pos ) 333{ 334 int i; 335 char *left, *right, *style; 336 struct berval bv; 337 AccessControl *a = NULL; 338 Access *b = NULL; 339 int rc; 340 const char *text; 341 342 for ( i = 1; i < argc; i++ ) { 343 /* to clause - select which entries are protected */ 344 if ( strcasecmp( argv[i], "to" ) == 0 ) { 345 if ( a != NULL ) { 346 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 347 "only one to clause allowed in access line\n", 348 fname, lineno ); 349 goto fail; 350 } 351 a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) ); 352 a->acl_attrval_style = ACL_STYLE_NONE; 353 for ( ++i; i < argc; i++ ) { 354 if ( strcasecmp( argv[i], "by" ) == 0 ) { 355 i--; 356 break; 357 } 358 359 if ( strcasecmp( argv[i], "*" ) == 0 ) { 360 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) || 361 a->acl_dn_style != ACL_STYLE_REGEX ) 362 { 363 Debug( LDAP_DEBUG_ANY, 364 "%s: line %d: dn pattern" 365 " already specified in to clause.\n", 366 fname, lineno ); 367 goto fail; 368 } 369 370 ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat ); 371 continue; 372 } 373 374 split( argv[i], '=', &left, &right ); 375 split( left, '.', &left, &style ); 376 377 if ( right == NULL ) { 378 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 379 "missing \"=\" in \"%s\" in to clause\n", 380 fname, lineno, left ); 381 goto fail; 382 } 383 384 if ( strcasecmp( left, "dn" ) == 0 ) { 385 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) || 386 a->acl_dn_style != ACL_STYLE_REGEX ) 387 { 388 Debug( LDAP_DEBUG_ANY, 389 "%s: line %d: dn pattern" 390 " already specified in to clause.\n", 391 fname, lineno ); 392 goto fail; 393 } 394 395 if ( style == NULL || *style == '\0' || 396 strcasecmp( style, "baseObject" ) == 0 || 397 strcasecmp( style, "base" ) == 0 || 398 strcasecmp( style, "exact" ) == 0 ) 399 { 400 a->acl_dn_style = ACL_STYLE_BASE; 401 ber_str2bv( right, 0, 1, &a->acl_dn_pat ); 402 403 } else if ( strcasecmp( style, "oneLevel" ) == 0 || 404 strcasecmp( style, "one" ) == 0 ) 405 { 406 a->acl_dn_style = ACL_STYLE_ONE; 407 ber_str2bv( right, 0, 1, &a->acl_dn_pat ); 408 409 } else if ( strcasecmp( style, "subtree" ) == 0 || 410 strcasecmp( style, "sub" ) == 0 ) 411 { 412 if( *right == '\0' ) { 413 ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat ); 414 415 } else { 416 a->acl_dn_style = ACL_STYLE_SUBTREE; 417 ber_str2bv( right, 0, 1, &a->acl_dn_pat ); 418 } 419 420 } else if ( strcasecmp( style, "children" ) == 0 ) { 421 a->acl_dn_style = ACL_STYLE_CHILDREN; 422 ber_str2bv( right, 0, 1, &a->acl_dn_pat ); 423 424 } else if ( strcasecmp( style, "regex" ) == 0 ) { 425 a->acl_dn_style = ACL_STYLE_REGEX; 426 427 if ( *right == '\0' ) { 428 /* empty regex should match empty DN */ 429 a->acl_dn_style = ACL_STYLE_BASE; 430 ber_str2bv( right, 0, 1, &a->acl_dn_pat ); 431 432 } else if ( strcmp(right, "*") == 0 433 || strcmp(right, ".*") == 0 434 || strcmp(right, ".*$") == 0 435 || strcmp(right, "^.*") == 0 436 || strcmp(right, "^.*$") == 0 437 || strcmp(right, ".*$$") == 0 438 || strcmp(right, "^.*$$") == 0 ) 439 { 440 ber_str2bv( "*", STRLENOF("*"), 1, &a->acl_dn_pat ); 441 442 } else { 443 acl_regex_normalized_dn( right, &a->acl_dn_pat ); 444 } 445 446 } else { 447 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 448 "unknown dn style \"%s\" in to clause\n", 449 fname, lineno, style ); 450 goto fail; 451 } 452 453 continue; 454 } 455 456 if ( strcasecmp( left, "filter" ) == 0 ) { 457 if ( (a->acl_filter = str2filter( right )) == NULL ) { 458 Debug( LDAP_DEBUG_ANY, 459 "%s: line %d: bad filter \"%s\" in to clause\n", 460 fname, lineno, right ); 461 goto fail; 462 } 463 464 } else if ( strcasecmp( left, "attr" ) == 0 /* TOLERATED */ 465 || strcasecmp( left, "attrs" ) == 0 ) /* DOCUMENTED */ 466 { 467 if ( strcasecmp( left, "attr" ) == 0 ) { 468 Debug( LDAP_DEBUG_ANY, 469 "%s: line %d: \"attr\" " 470 "is deprecated (and undocumented); " 471 "use \"attrs\" instead.\n", 472 fname, lineno ); 473 } 474 475 a->acl_attrs = str2anlist( a->acl_attrs, 476 right, "," ); 477 if ( a->acl_attrs == NULL ) { 478 Debug( LDAP_DEBUG_ANY, 479 "%s: line %d: unknown attr \"%s\" in to clause\n", 480 fname, lineno, right ); 481 goto fail; 482 } 483 484 } else if ( strncasecmp( left, "val", 3 ) == 0 ) { 485 struct berval bv; 486 char *mr; 487 488 if ( !BER_BVISEMPTY( &a->acl_attrval ) ) { 489 Debug( LDAP_DEBUG_ANY, 490 "%s: line %d: attr val already specified in to clause.\n", 491 fname, lineno ); 492 goto fail; 493 } 494 if ( a->acl_attrs == NULL || !BER_BVISEMPTY( &a->acl_attrs[1].an_name ) ) 495 { 496 Debug( LDAP_DEBUG_ANY, 497 "%s: line %d: attr val requires a single attribute.\n", 498 fname, lineno ); 499 goto fail; 500 } 501 502 ber_str2bv( right, 0, 0, &bv ); 503 a->acl_attrval_style = ACL_STYLE_BASE; 504 505 mr = strchr( left, '/' ); 506 if ( mr != NULL ) { 507 mr[ 0 ] = '\0'; 508 mr++; 509 510 a->acl_attrval_mr = mr_find( mr ); 511 if ( a->acl_attrval_mr == NULL ) { 512 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 513 "invalid matching rule \"%s\".\n", 514 fname, lineno, mr ); 515 goto fail; 516 } 517 518 if( !mr_usable_with_at( a->acl_attrval_mr, a->acl_attrs[ 0 ].an_desc->ad_type ) ) 519 { 520 Debug(LDAP_DEBUG_ANY, 521 "%s: line %d: matching rule \"%s\" use " "with attr \"%s\" not appropriate.\n", 522 fname, lineno, 523 mr, 524 a->acl_attrs[0].an_name.bv_val ); 525 goto fail; 526 } 527 } 528 529 if ( style != NULL ) { 530 if ( strcasecmp( style, "regex" ) == 0 ) { 531 int e = regcomp( &a->acl_attrval_re, bv.bv_val, 532 REG_EXTENDED | REG_ICASE ); 533 if ( e ) { 534 char err[SLAP_TEXT_BUFLEN]; 535 536 regerror( e, &a->acl_attrval_re, err, sizeof( err ) ); 537 Debug(LDAP_DEBUG_ANY, 538 "%s: line %d: regular expression \"%s\" bad because of %s\n", 539 fname, lineno, right, err ); 540 goto fail; 541 } 542 a->acl_attrval_style = ACL_STYLE_REGEX; 543 544 } else { 545 /* FIXME: if the attribute has DN syntax, we might 546 * allow one, subtree and children styles as well */ 547 if ( !strcasecmp( style, "base" ) || 548 !strcasecmp( style, "exact" ) ) { 549 a->acl_attrval_style = ACL_STYLE_BASE; 550 551 } else if ( a->acl_attrs[0].an_desc->ad_type-> 552 sat_syntax == slap_schema.si_syn_distinguishedName ) 553 { 554 if ( !strcasecmp( style, "baseObject" ) || 555 !strcasecmp( style, "base" ) ) 556 { 557 a->acl_attrval_style = ACL_STYLE_BASE; 558 } else if ( !strcasecmp( style, "onelevel" ) || 559 !strcasecmp( style, "one" ) ) 560 { 561 a->acl_attrval_style = ACL_STYLE_ONE; 562 } else if ( !strcasecmp( style, "subtree" ) || 563 !strcasecmp( style, "sub" ) ) 564 { 565 a->acl_attrval_style = ACL_STYLE_SUBTREE; 566 } else if ( !strcasecmp( style, "children" ) ) { 567 a->acl_attrval_style = ACL_STYLE_CHILDREN; 568 } else { 569 Debug(LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, 570 "%s: line %d: unknown val.<style> \"%s\" for attributeType \"%s\" " "with DN syntax.\n", 571 fname, 572 lineno, 573 style, 574 a->acl_attrs[0].an_desc->ad_cname.bv_val ); 575 goto fail; 576 } 577 578 rc = dnNormalize( 0, NULL, NULL, &bv, &a->acl_attrval, NULL ); 579 if ( rc != LDAP_SUCCESS ) { 580 Debug(LDAP_DEBUG_ANY, 581 "%s: line %d: unable to normalize DN \"%s\" " "for attributeType \"%s\" (%d).\n", 582 fname, 583 lineno, 584 bv.bv_val, 585 a->acl_attrs[0].an_desc->ad_cname.bv_val, 586 rc ); 587 goto fail; 588 } 589 590 } else { 591 Debug(LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, 592 "%s: line %d: unknown val.<style> \"%s\" for attributeType \"%s\".\n", 593 fname, 594 lineno, 595 style, 596 a->acl_attrs[0].an_desc->ad_cname.bv_val ); 597 goto fail; 598 } 599 } 600 } 601 602 /* Check for appropriate matching rule */ 603 if ( a->acl_attrval_style == ACL_STYLE_REGEX ) { 604 ber_dupbv( &a->acl_attrval, &bv ); 605 606 } else if ( BER_BVISNULL( &a->acl_attrval ) ) { 607 int rc; 608 const char *text; 609 610 if ( a->acl_attrval_mr == NULL ) { 611 a->acl_attrval_mr = a->acl_attrs[ 0 ].an_desc->ad_type->sat_equality; 612 } 613 614 if ( a->acl_attrval_mr == NULL ) { 615 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 616 "attr \"%s\" does not have an EQUALITY matching rule.\n", 617 fname, lineno, a->acl_attrs[ 0 ].an_name.bv_val ); 618 goto fail; 619 } 620 621 rc = asserted_value_validate_normalize( 622 a->acl_attrs[ 0 ].an_desc, 623 a->acl_attrval_mr, 624 SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, 625 &bv, 626 &a->acl_attrval, 627 &text, 628 NULL ); 629 if ( rc != LDAP_SUCCESS ) { 630 Debug(LDAP_DEBUG_ANY, 631 "%s: line %d: %s: line %d: " " attr \"%s\" normalization failed (%d: %s).\n", 632 fname, lineno, 633 fname, lineno, 634 a->acl_attrs[0].an_name.bv_val, 635 rc, text ); 636 goto fail; 637 } 638 } 639 640 } else { 641 Debug( LDAP_DEBUG_ANY, 642 "%s: line %d: expecting <what> got \"%s\"\n", 643 fname, lineno, left ); 644 goto fail; 645 } 646 } 647 648 if ( !BER_BVISNULL( &a->acl_dn_pat ) && 649 ber_bvccmp( &a->acl_dn_pat, '*' ) ) 650 { 651 free( a->acl_dn_pat.bv_val ); 652 BER_BVZERO( &a->acl_dn_pat ); 653 a->acl_dn_style = ACL_STYLE_REGEX; 654 } 655 656 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) || 657 a->acl_dn_style != ACL_STYLE_REGEX ) 658 { 659 if ( a->acl_dn_style != ACL_STYLE_REGEX ) { 660 struct berval bv; 661 rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL); 662 if ( rc != LDAP_SUCCESS ) { 663 Debug( LDAP_DEBUG_ANY, 664 "%s: line %d: bad DN \"%s\" in to DN clause\n", 665 fname, lineno, a->acl_dn_pat.bv_val ); 666 goto fail; 667 } 668 free( a->acl_dn_pat.bv_val ); 669 a->acl_dn_pat = bv; 670 671 } else { 672 int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val, 673 REG_EXTENDED | REG_ICASE ); 674 if ( e ) { 675 char err[ SLAP_TEXT_BUFLEN ]; 676 677 regerror( e, &a->acl_dn_re, err, sizeof( err ) ); 678 Debug(LDAP_DEBUG_ANY, 679 "%s: line %d: regular expression \"%s\" bad because of %s\n", 680 fname, lineno, right, err ); 681 goto fail; 682 } 683 } 684 } 685 686 /* by clause - select who has what access to entries */ 687 } else if ( strcasecmp( argv[i], "by" ) == 0 ) { 688 if ( a == NULL ) { 689 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 690 "to clause required before by clause in access line\n", 691 fname, lineno ); 692 goto fail; 693 } 694 695 /* 696 * by clause consists of <who> and <access> 697 */ 698 699 if ( ++i == argc ) { 700 Debug( LDAP_DEBUG_ANY, 701 "%s: line %d: premature EOL: expecting <who>\n", 702 fname, lineno ); 703 goto fail; 704 } 705 706 b = (Access *) ch_calloc( 1, sizeof(Access) ); 707 708 ACL_INVALIDATE( b->a_access_mask ); 709 710 /* get <who> */ 711 for ( ; i < argc; i++ ) { 712 slap_style_t sty = ACL_STYLE_REGEX; 713 char *style_modifier = NULL; 714 char *style_level = NULL; 715 int level = 0; 716 int expand = 0; 717 slap_dn_access *bdn = &b->a_dn; 718 int is_realdn = 0; 719 720 split( argv[i], '=', &left, &right ); 721 split( left, '.', &left, &style ); 722 if ( style ) { 723 split( style, ',', &style, &style_modifier ); 724 725 if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) { 726 split( style, '{', &style, &style_level ); 727 if ( style_level != NULL ) { 728 char *p = strchr( style_level, '}' ); 729 if ( p == NULL ) { 730 Debug( LDAP_DEBUG_ANY, 731 "%s: line %d: premature eol: " 732 "expecting closing '}' in \"level{n}\"\n", 733 fname, lineno ); 734 goto fail; 735 } else if ( p == style_level ) { 736 Debug( LDAP_DEBUG_ANY, 737 "%s: line %d: empty level " 738 "in \"level{n}\"\n", 739 fname, lineno ); 740 goto fail; 741 } 742 p[0] = '\0'; 743 } 744 } 745 } 746 747 if ( style == NULL || *style == '\0' || 748 strcasecmp( style, "exact" ) == 0 || 749 strcasecmp( style, "baseObject" ) == 0 || 750 strcasecmp( style, "base" ) == 0 ) 751 { 752 sty = ACL_STYLE_BASE; 753 754 } else if ( strcasecmp( style, "onelevel" ) == 0 || 755 strcasecmp( style, "one" ) == 0 ) 756 { 757 sty = ACL_STYLE_ONE; 758 759 } else if ( strcasecmp( style, "subtree" ) == 0 || 760 strcasecmp( style, "sub" ) == 0 ) 761 { 762 sty = ACL_STYLE_SUBTREE; 763 764 } else if ( strcasecmp( style, "children" ) == 0 ) { 765 sty = ACL_STYLE_CHILDREN; 766 767 } else if ( strcasecmp( style, "level" ) == 0 ) 768 { 769 if ( lutil_atoi( &level, style_level ) != 0 ) { 770 Debug( LDAP_DEBUG_ANY, 771 "%s: line %d: unable to parse level " 772 "in \"level{n}\"\n", 773 fname, lineno ); 774 goto fail; 775 } 776 777 sty = ACL_STYLE_LEVEL; 778 779 } else if ( strcasecmp( style, "regex" ) == 0 ) { 780 sty = ACL_STYLE_REGEX; 781 782 } else if ( strcasecmp( style, "expand" ) == 0 ) { 783 sty = ACL_STYLE_EXPAND; 784 785 } else if ( strcasecmp( style, "ip" ) == 0 ) { 786 sty = ACL_STYLE_IP; 787 788 } else if ( strcasecmp( style, "ipv6" ) == 0 ) { 789#ifndef LDAP_PF_INET6 790 Debug( LDAP_DEBUG_ANY, 791 "%s: line %d: IPv6 not supported\n", 792 fname, lineno ); 793#endif /* ! LDAP_PF_INET6 */ 794 sty = ACL_STYLE_IPV6; 795 796 } else if ( strcasecmp( style, "path" ) == 0 ) { 797 sty = ACL_STYLE_PATH; 798#ifndef LDAP_PF_LOCAL 799 Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, 800 "%s: line %d: " 801 "\"path\" style modifier is useless without local.\n", 802 fname, lineno ); 803 goto fail; 804#endif /* LDAP_PF_LOCAL */ 805 806 } else { 807 Debug( LDAP_DEBUG_ANY, 808 "%s: line %d: unknown style \"%s\" in by clause\n", 809 fname, lineno, style ); 810 goto fail; 811 } 812 813 if ( style_modifier && 814 strcasecmp( style_modifier, "expand" ) == 0 ) 815 { 816 switch ( sty ) { 817 case ACL_STYLE_REGEX: 818 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 819 "\"regex\" style implies \"expand\" modifier.\n", 820 fname, lineno ); 821 goto fail; 822 break; 823 824 case ACL_STYLE_EXPAND: 825 break; 826 827 default: 828 /* we'll see later if it's pertinent */ 829 expand = 1; 830 break; 831 } 832 } 833 834 if ( strncasecmp( left, "real", STRLENOF( "real" ) ) == 0 ) { 835 is_realdn = 1; 836 bdn = &b->a_realdn; 837 left += STRLENOF( "real" ); 838 } 839 840 if ( strcasecmp( left, "*" ) == 0 ) { 841 if ( is_realdn ) { 842 goto fail; 843 } 844 845 ber_str2bv( "*", STRLENOF( "*" ), 1, &bv ); 846 sty = ACL_STYLE_REGEX; 847 848 } else if ( strcasecmp( left, "anonymous" ) == 0 ) { 849 ber_str2bv("anonymous", STRLENOF( "anonymous" ), 1, &bv); 850 sty = ACL_STYLE_ANONYMOUS; 851 852 } else if ( strcasecmp( left, "users" ) == 0 ) { 853 ber_str2bv("users", STRLENOF( "users" ), 1, &bv); 854 sty = ACL_STYLE_USERS; 855 856 } else if ( strcasecmp( left, "self" ) == 0 ) { 857 ber_str2bv("self", STRLENOF( "self" ), 1, &bv); 858 sty = ACL_STYLE_SELF; 859 860 } else if ( strcasecmp( left, "dn" ) == 0 ) { 861 if ( sty == ACL_STYLE_REGEX ) { 862 bdn->a_style = ACL_STYLE_REGEX; 863 if ( right == NULL ) { 864 /* no '=' */ 865 ber_str2bv("users", 866 STRLENOF( "users" ), 867 1, &bv); 868 bdn->a_style = ACL_STYLE_USERS; 869 870 } else if (*right == '\0' ) { 871 /* dn="" */ 872 ber_str2bv("anonymous", 873 STRLENOF( "anonymous" ), 874 1, &bv); 875 bdn->a_style = ACL_STYLE_ANONYMOUS; 876 877 } else if ( strcmp( right, "*" ) == 0 ) { 878 /* dn=* */ 879 /* any or users? users for now */ 880 ber_str2bv("users", 881 STRLENOF( "users" ), 882 1, &bv); 883 bdn->a_style = ACL_STYLE_USERS; 884 885 } else if ( strcmp( right, ".+" ) == 0 886 || strcmp( right, "^.+" ) == 0 887 || strcmp( right, ".+$" ) == 0 888 || strcmp( right, "^.+$" ) == 0 889 || strcmp( right, ".+$$" ) == 0 890 || strcmp( right, "^.+$$" ) == 0 ) 891 { 892 ber_str2bv("users", 893 STRLENOF( "users" ), 894 1, &bv); 895 bdn->a_style = ACL_STYLE_USERS; 896 897 } else if ( strcmp( right, ".*" ) == 0 898 || strcmp( right, "^.*" ) == 0 899 || strcmp( right, ".*$" ) == 0 900 || strcmp( right, "^.*$" ) == 0 901 || strcmp( right, ".*$$" ) == 0 902 || strcmp( right, "^.*$$" ) == 0 ) 903 { 904 ber_str2bv("*", 905 STRLENOF( "*" ), 906 1, &bv); 907 908 } else { 909 acl_regex_normalized_dn( right, &bv ); 910 if ( !ber_bvccmp( &bv, '*' ) ) { 911 regtest( fname, lineno, bv.bv_val ); 912 } 913 } 914 915 } else if ( right == NULL || *right == '\0' ) { 916 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 917 "missing \"=\" in (or value after) \"%s\" " 918 "in by clause\n", 919 fname, lineno, left ); 920 goto fail; 921 922 } else { 923 ber_str2bv( right, 0, 1, &bv ); 924 } 925 926 } else { 927 BER_BVZERO( &bv ); 928 } 929 930 if ( !BER_BVISNULL( &bv ) ) { 931 if ( !BER_BVISEMPTY( &bdn->a_pat ) ) { 932 Debug( LDAP_DEBUG_ANY, 933 "%s: line %d: dn pattern already specified.\n", 934 fname, lineno ); 935 goto fail; 936 } 937 938 if ( sty != ACL_STYLE_REGEX && 939 sty != ACL_STYLE_ANONYMOUS && 940 sty != ACL_STYLE_USERS && 941 sty != ACL_STYLE_SELF && 942 expand == 0 ) 943 { 944 rc = dnNormalize(0, NULL, NULL, 945 &bv, &bdn->a_pat, NULL); 946 if ( rc != LDAP_SUCCESS ) { 947 Debug( LDAP_DEBUG_ANY, 948 "%s: line %d: bad DN \"%s\" in by DN clause\n", 949 fname, lineno, bv.bv_val ); 950 goto fail; 951 } 952 free( bv.bv_val ); 953 if ( sty == ACL_STYLE_BASE 954 && be != NULL 955 && !BER_BVISNULL( &be->be_rootndn ) 956 && dn_match( &bdn->a_pat, &be->be_rootndn ) ) 957 { 958 Debug( LDAP_DEBUG_ANY, 959 "%s: line %d: rootdn is always granted " 960 "unlimited privileges.\n", 961 fname, lineno ); 962 } 963 964 } else { 965 bdn->a_pat = bv; 966 } 967 bdn->a_style = sty; 968 if ( expand ) { 969 char *exp; 970 int gotit = 0; 971 972 for ( exp = strchr( bdn->a_pat.bv_val, '$' ); 973 exp && (ber_len_t)(exp - bdn->a_pat.bv_val) 974 < bdn->a_pat.bv_len; 975 exp = strchr( exp, '$' ) ) 976 { 977 if ( ( isdigit( (unsigned char) exp[ 1 ] ) || 978 exp[ 1 ] == '{' ) ) { 979 gotit = 1; 980 break; 981 } 982 } 983 984 if ( gotit == 1 ) { 985 bdn->a_expand = expand; 986 987 } else { 988 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 989 "\"expand\" used with no expansions in \"pattern\".\n", 990 fname, lineno ); 991 goto fail; 992 } 993 } 994 if ( sty == ACL_STYLE_SELF ) { 995 bdn->a_self_level = level; 996 997 } else { 998 if ( level < 0 ) { 999 Debug( LDAP_DEBUG_ANY, 1000 "%s: line %d: bad negative level \"%d\" " 1001 "in by DN clause\n", 1002 fname, lineno, level ); 1003 goto fail; 1004 } else if ( level == 1 ) { 1005 Debug( LDAP_DEBUG_ANY, 1006 "%s: line %d: \"onelevel\" should be used " 1007 "instead of \"level{1}\" in by DN clause\n", 1008 fname, lineno ); 1009 } else if ( level == 0 && sty == ACL_STYLE_LEVEL ) { 1010 Debug( LDAP_DEBUG_ANY, 1011 "%s: line %d: \"base\" should be used " 1012 "instead of \"level{0}\" in by DN clause\n", 1013 fname, lineno ); 1014 } 1015 1016 bdn->a_level = level; 1017 } 1018 continue; 1019 } 1020 1021 if ( strcasecmp( left, "dnattr" ) == 0 ) { 1022 if ( right == NULL || right[0] == '\0' ) { 1023 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1024 "missing \"=\" in (or value after) \"%s\" " 1025 "in by clause\n", 1026 fname, lineno, left ); 1027 goto fail; 1028 } 1029 1030 if( bdn->a_at != NULL ) { 1031 Debug( LDAP_DEBUG_ANY, 1032 "%s: line %d: dnattr already specified.\n", 1033 fname, lineno ); 1034 goto fail; 1035 } 1036 1037 rc = slap_str2ad( right, &bdn->a_at, &text ); 1038 1039 if( rc != LDAP_SUCCESS ) { 1040 Debug(LDAP_DEBUG_ANY, 1041 "%s: line %d: dnattr \"%s\": %s\n", 1042 fname, lineno, right, 1043 text ); 1044 goto fail; 1045 } 1046 1047 1048 if( !is_at_syntax( bdn->a_at->ad_type, 1049 SLAPD_DN_SYNTAX ) && 1050 !is_at_syntax( bdn->a_at->ad_type, 1051 SLAPD_NAMEUID_SYNTAX )) 1052 { 1053 Debug(LDAP_DEBUG_ANY, 1054 "%s: line %d: dnattr \"%s\": " "inappropriate syntax: %s\n\n", 1055 fname, lineno, right, 1056 bdn->a_at->ad_type->sat_syntax_oid ); 1057 goto fail; 1058 } 1059 1060 if( bdn->a_at->ad_type->sat_equality == NULL ) { 1061 Debug( LDAP_DEBUG_ANY, 1062 "%s: line %d: dnattr \"%s\": " 1063 "inappropriate matching (no EQUALITY)\n", 1064 fname, lineno, right ); 1065 goto fail; 1066 } 1067 1068 continue; 1069 } 1070 1071 if ( strncasecmp( left, "group", STRLENOF( "group" ) ) == 0 ) { 1072 char *name = NULL; 1073 char *value = NULL; 1074 char *attr_name = SLAPD_GROUP_ATTR; 1075 1076 switch ( sty ) { 1077 case ACL_STYLE_REGEX: 1078 /* legacy, tolerated */ 1079 Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, 1080 "%s: line %d: " 1081 "deprecated group style \"regex\"; " 1082 "use \"expand\" instead.\n", 1083 fname, lineno ); 1084 sty = ACL_STYLE_EXPAND; 1085 break; 1086 1087 case ACL_STYLE_BASE: 1088 /* legal, traditional */ 1089 case ACL_STYLE_EXPAND: 1090 /* legal, substring expansion; supersedes regex */ 1091 break; 1092 1093 default: 1094 /* unknown */ 1095 Debug( LDAP_DEBUG_ANY, 1096 "%s: line %d: " 1097 "inappropriate style \"%s\" in by clause.\n", 1098 fname, lineno, style ); 1099 goto fail; 1100 } 1101 1102 if ( right == NULL || right[0] == '\0' ) { 1103 Debug( LDAP_DEBUG_ANY, 1104 "%s: line %d: " 1105 "missing \"=\" in (or value after) \"%s\" " 1106 "in by clause.\n", 1107 fname, lineno, left ); 1108 goto fail; 1109 } 1110 1111 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) { 1112 Debug( LDAP_DEBUG_ANY, 1113 "%s: line %d: group pattern already specified.\n", 1114 fname, lineno ); 1115 goto fail; 1116 } 1117 1118 /* format of string is 1119 "group/objectClassValue/groupAttrName" */ 1120 if ( ( value = strchr(left, '/') ) != NULL ) { 1121 *value++ = '\0'; 1122 if ( *value && ( name = strchr( value, '/' ) ) != NULL ) { 1123 *name++ = '\0'; 1124 } 1125 } 1126 1127 b->a_group_style = sty; 1128 if ( sty == ACL_STYLE_EXPAND ) { 1129 acl_regex_normalized_dn( right, &bv ); 1130 if ( !ber_bvccmp( &bv, '*' ) ) { 1131 regtest( fname, lineno, bv.bv_val ); 1132 } 1133 b->a_group_pat = bv; 1134 1135 } else { 1136 ber_str2bv( right, 0, 0, &bv ); 1137 rc = dnNormalize( 0, NULL, NULL, &bv, 1138 &b->a_group_pat, NULL ); 1139 if ( rc != LDAP_SUCCESS ) { 1140 Debug( LDAP_DEBUG_ANY, 1141 "%s: line %d: bad DN \"%s\".\n", 1142 fname, lineno, right ); 1143 goto fail; 1144 } 1145 } 1146 1147 if ( value && *value ) { 1148 b->a_group_oc = oc_find( value ); 1149 *--value = '/'; 1150 1151 if ( b->a_group_oc == NULL ) { 1152 Debug( LDAP_DEBUG_ANY, 1153 "%s: line %d: group objectclass " 1154 "\"%s\" unknown.\n", 1155 fname, lineno, value ); 1156 goto fail; 1157 } 1158 1159 } else { 1160 b->a_group_oc = oc_find( SLAPD_GROUP_CLASS ); 1161 1162 if( b->a_group_oc == NULL ) { 1163 Debug( LDAP_DEBUG_ANY, 1164 "%s: line %d: group default objectclass " 1165 "\"%s\" unknown.\n", 1166 fname, lineno, SLAPD_GROUP_CLASS ); 1167 goto fail; 1168 } 1169 } 1170 1171 if ( is_object_subclass( slap_schema.si_oc_referral, 1172 b->a_group_oc ) ) 1173 { 1174 Debug( LDAP_DEBUG_ANY, 1175 "%s: line %d: group objectclass \"%s\" " 1176 "is subclass of referral.\n", 1177 fname, lineno, value ); 1178 goto fail; 1179 } 1180 1181 if ( is_object_subclass( slap_schema.si_oc_alias, 1182 b->a_group_oc ) ) 1183 { 1184 Debug( LDAP_DEBUG_ANY, 1185 "%s: line %d: group objectclass \"%s\" " 1186 "is subclass of alias.\n", 1187 fname, lineno, value ); 1188 goto fail; 1189 } 1190 1191 if ( name && *name ) { 1192 attr_name = name; 1193 *--name = '/'; 1194 1195 } 1196 1197 rc = slap_str2ad( attr_name, &b->a_group_at, &text ); 1198 if ( rc != LDAP_SUCCESS ) { 1199 Debug(LDAP_DEBUG_ANY, 1200 "%s: line %d: group \"%s\": %s.\n", 1201 fname, lineno, right, 1202 text ); 1203 goto fail; 1204 } 1205 1206 if ( !is_at_syntax( b->a_group_at->ad_type, 1207 SLAPD_DN_SYNTAX ) /* e.g. "member" */ 1208 && !is_at_syntax( b->a_group_at->ad_type, 1209 SLAPD_NAMEUID_SYNTAX ) /* e.g. memberUID */ 1210 && !is_at_subtype( b->a_group_at->ad_type, 1211 slap_schema.si_ad_labeledURI->ad_type ) /* e.g. memberURL */ ) 1212 { 1213 Debug(LDAP_DEBUG_ANY, 1214 "%s: line %d: group \"%s\" attr \"%s\": inappropriate syntax: %s; " "must be " SLAPD_DN_SYNTAX " (DN), " SLAPD_NAMEUID_SYNTAX " (NameUID) " "or a subtype of labeledURI.\n", 1215 fname, lineno, right, 1216 attr_name, 1217 at_syntax(b->a_group_at->ad_type) ); 1218 goto fail; 1219 } 1220 1221 1222 { 1223 int rc; 1224 ObjectClass *ocs[2]; 1225 1226 ocs[0] = b->a_group_oc; 1227 ocs[1] = NULL; 1228 1229 rc = oc_check_allowed( b->a_group_at->ad_type, 1230 ocs, NULL ); 1231 1232 if( rc != 0 ) { 1233 Debug(LDAP_DEBUG_ANY, 1234 "%s: line %d: group: \"%s\" not allowed by \"%s\".\n", 1235 fname, lineno, 1236 b->a_group_at->ad_cname.bv_val, 1237 b->a_group_oc->soc_oid ); 1238 goto fail; 1239 } 1240 } 1241 continue; 1242 } 1243 1244 if ( strcasecmp( left, "peername" ) == 0 ) { 1245 switch ( sty ) { 1246 case ACL_STYLE_REGEX: 1247 case ACL_STYLE_BASE: 1248 /* legal, traditional */ 1249 case ACL_STYLE_EXPAND: 1250 /* cheap replacement to regex for simple expansion */ 1251 case ACL_STYLE_IP: 1252 case ACL_STYLE_IPV6: 1253 case ACL_STYLE_PATH: 1254 /* legal, peername specific */ 1255 break; 1256 1257 default: 1258 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1259 "inappropriate style \"%s\" in by clause.\n", 1260 fname, lineno, style ); 1261 goto fail; 1262 } 1263 1264 if ( right == NULL || right[0] == '\0' ) { 1265 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1266 "missing \"=\" in (or value after) \"%s\" " 1267 "in by clause.\n", 1268 fname, lineno, left ); 1269 goto fail; 1270 } 1271 1272 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) { 1273 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1274 "peername pattern already specified.\n", 1275 fname, lineno ); 1276 goto fail; 1277 } 1278 1279 b->a_peername_style = sty; 1280 if ( sty == ACL_STYLE_REGEX ) { 1281 acl_regex_normalized_dn( right, &bv ); 1282 if ( !ber_bvccmp( &bv, '*' ) ) { 1283 regtest( fname, lineno, bv.bv_val ); 1284 } 1285 b->a_peername_pat = bv; 1286 1287 } else { 1288 ber_str2bv( right, 0, 1, &b->a_peername_pat ); 1289 1290 if ( sty == ACL_STYLE_IP ) { 1291 char *addr = NULL, 1292 *mask = NULL, 1293 *port = NULL; 1294 1295 split( right, '{', &addr, &port ); 1296 split( addr, '%', &addr, &mask ); 1297 1298 b->a_peername_addr = inet_addr( addr ); 1299 if ( b->a_peername_addr == (unsigned long)(-1) ) { 1300 /* illegal address */ 1301 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1302 "illegal peername address \"%s\".\n", 1303 fname, lineno, addr ); 1304 goto fail; 1305 } 1306 1307 b->a_peername_mask = (unsigned long)(-1); 1308 if ( mask != NULL ) { 1309 b->a_peername_mask = inet_addr( mask ); 1310 if ( b->a_peername_mask == 1311 (unsigned long)(-1) ) 1312 { 1313 /* illegal mask */ 1314 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1315 "illegal peername address mask " 1316 "\"%s\".\n", 1317 fname, lineno, mask ); 1318 goto fail; 1319 } 1320 } 1321 1322 b->a_peername_port = -1; 1323 if ( port ) { 1324 char *end = NULL; 1325 1326 b->a_peername_port = strtol( port, &end, 10 ); 1327 if ( end == port || end[0] != '}' ) { 1328 /* illegal port */ 1329 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1330 "illegal peername port specification " 1331 "\"{%s}\".\n", 1332 fname, lineno, port ); 1333 goto fail; 1334 } 1335 } 1336 1337#ifdef LDAP_PF_INET6 1338 } else if ( sty == ACL_STYLE_IPV6 ) { 1339 char *addr = NULL, 1340 *mask = NULL, 1341 *port = NULL; 1342 1343 split( right, '{', &addr, &port ); 1344 split( addr, '%', &addr, &mask ); 1345 1346 if ( inet_pton( AF_INET6, addr, &b->a_peername_addr6 ) != 1 ) { 1347 /* illegal address */ 1348 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1349 "illegal peername address \"%s\".\n", 1350 fname, lineno, addr ); 1351 goto fail; 1352 } 1353 1354 if ( mask == NULL ) { 1355 mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"; 1356 } 1357 1358 if ( inet_pton( AF_INET6, mask, &b->a_peername_mask6 ) != 1 ) { 1359 /* illegal mask */ 1360 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1361 "illegal peername address mask " 1362 "\"%s\".\n", 1363 fname, lineno, mask ); 1364 goto fail; 1365 } 1366 1367 b->a_peername_port = -1; 1368 if ( port ) { 1369 char *end = NULL; 1370 1371 b->a_peername_port = strtol( port, &end, 10 ); 1372 if ( end == port || end[0] != '}' ) { 1373 /* illegal port */ 1374 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1375 "illegal peername port specification " 1376 "\"{%s}\".\n", 1377 fname, lineno, port ); 1378 goto fail; 1379 } 1380 } 1381#endif /* LDAP_PF_INET6 */ 1382 } 1383 } 1384 continue; 1385 } 1386 1387 if ( strcasecmp( left, "sockname" ) == 0 ) { 1388 switch ( sty ) { 1389 case ACL_STYLE_REGEX: 1390 case ACL_STYLE_BASE: 1391 /* legal, traditional */ 1392 case ACL_STYLE_EXPAND: 1393 /* cheap replacement to regex for simple expansion */ 1394 break; 1395 1396 default: 1397 /* unknown */ 1398 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1399 "inappropriate style \"%s\" in by clause\n", 1400 fname, lineno, style ); 1401 goto fail; 1402 } 1403 1404 if ( right == NULL || right[0] == '\0' ) { 1405 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1406 "missing \"=\" in (or value after) \"%s\" " 1407 "in by clause\n", 1408 fname, lineno, left ); 1409 goto fail; 1410 } 1411 1412 if ( !BER_BVISNULL( &b->a_sockname_pat ) ) { 1413 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1414 "sockname pattern already specified.\n", 1415 fname, lineno ); 1416 goto fail; 1417 } 1418 1419 b->a_sockname_style = sty; 1420 if ( sty == ACL_STYLE_REGEX ) { 1421 acl_regex_normalized_dn( right, &bv ); 1422 if ( !ber_bvccmp( &bv, '*' ) ) { 1423 regtest( fname, lineno, bv.bv_val ); 1424 } 1425 b->a_sockname_pat = bv; 1426 1427 } else { 1428 ber_str2bv( right, 0, 1, &b->a_sockname_pat ); 1429 } 1430 continue; 1431 } 1432 1433 if ( strcasecmp( left, "domain" ) == 0 ) { 1434 switch ( sty ) { 1435 case ACL_STYLE_REGEX: 1436 case ACL_STYLE_BASE: 1437 case ACL_STYLE_SUBTREE: 1438 /* legal, traditional */ 1439 break; 1440 1441 case ACL_STYLE_EXPAND: 1442 /* tolerated: means exact,expand */ 1443 if ( expand ) { 1444 Debug( LDAP_DEBUG_ANY, 1445 "%s: line %d: " 1446 "\"expand\" modifier " 1447 "with \"expand\" style.\n", 1448 fname, lineno ); 1449 } 1450 sty = ACL_STYLE_BASE; 1451 expand = 1; 1452 break; 1453 1454 default: 1455 /* unknown */ 1456 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1457 "inappropriate style \"%s\" in by clause.\n", 1458 fname, lineno, style ); 1459 goto fail; 1460 } 1461 1462 if ( right == NULL || right[0] == '\0' ) { 1463 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1464 "missing \"=\" in (or value after) \"%s\" " 1465 "in by clause.\n", 1466 fname, lineno, left ); 1467 goto fail; 1468 } 1469 1470 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) { 1471 Debug( LDAP_DEBUG_ANY, 1472 "%s: line %d: domain pattern already specified.\n", 1473 fname, lineno ); 1474 goto fail; 1475 } 1476 1477 b->a_domain_style = sty; 1478 b->a_domain_expand = expand; 1479 if ( sty == ACL_STYLE_REGEX ) { 1480 acl_regex_normalized_dn( right, &bv ); 1481 if ( !ber_bvccmp( &bv, '*' ) ) { 1482 regtest( fname, lineno, bv.bv_val ); 1483 } 1484 b->a_domain_pat = bv; 1485 1486 } else { 1487 ber_str2bv( right, 0, 1, &b->a_domain_pat ); 1488 } 1489 continue; 1490 } 1491 1492 if ( strcasecmp( left, "sockurl" ) == 0 ) { 1493 switch ( sty ) { 1494 case ACL_STYLE_REGEX: 1495 case ACL_STYLE_BASE: 1496 /* legal, traditional */ 1497 case ACL_STYLE_EXPAND: 1498 /* cheap replacement to regex for simple expansion */ 1499 break; 1500 1501 default: 1502 /* unknown */ 1503 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1504 "inappropriate style \"%s\" in by clause.\n", 1505 fname, lineno, style ); 1506 goto fail; 1507 } 1508 1509 if ( right == NULL || right[0] == '\0' ) { 1510 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1511 "missing \"=\" in (or value after) \"%s\" " 1512 "in by clause.\n", 1513 fname, lineno, left ); 1514 goto fail; 1515 } 1516 1517 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) { 1518 Debug( LDAP_DEBUG_ANY, 1519 "%s: line %d: sockurl pattern already specified.\n", 1520 fname, lineno ); 1521 goto fail; 1522 } 1523 1524 b->a_sockurl_style = sty; 1525 if ( sty == ACL_STYLE_REGEX ) { 1526 acl_regex_normalized_dn( right, &bv ); 1527 if ( !ber_bvccmp( &bv, '*' ) ) { 1528 regtest( fname, lineno, bv.bv_val ); 1529 } 1530 b->a_sockurl_pat = bv; 1531 1532 } else { 1533 ber_str2bv( right, 0, 1, &b->a_sockurl_pat ); 1534 } 1535 continue; 1536 } 1537 1538 if ( strcasecmp( left, "set" ) == 0 ) { 1539 switch ( sty ) { 1540 /* deprecated */ 1541 case ACL_STYLE_REGEX: 1542 Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, 1543 "%s: line %d: " 1544 "deprecated set style " 1545 "\"regex\" in <by> clause; " 1546 "use \"expand\" instead.\n", 1547 fname, lineno ); 1548 sty = ACL_STYLE_EXPAND; 1549 /* FALLTHRU */ 1550 1551 case ACL_STYLE_BASE: 1552 case ACL_STYLE_EXPAND: 1553 break; 1554 1555 default: 1556 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1557 "inappropriate style \"%s\" in by clause.\n", 1558 fname, lineno, style ); 1559 goto fail; 1560 } 1561 1562 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) { 1563 Debug( LDAP_DEBUG_ANY, 1564 "%s: line %d: set attribute already specified.\n", 1565 fname, lineno ); 1566 goto fail; 1567 } 1568 1569 if ( right == NULL || *right == '\0' ) { 1570 Debug( LDAP_DEBUG_ANY, 1571 "%s: line %d: no set is defined.\n", 1572 fname, lineno ); 1573 goto fail; 1574 } 1575 1576 b->a_set_style = sty; 1577 ber_str2bv( right, 0, 1, &b->a_set_pat ); 1578 1579 continue; 1580 } 1581 1582#ifdef SLAP_DYNACL 1583 { 1584 char *name = NULL, 1585 *opts = NULL; 1586 1587#if 1 /* tolerate legacy "aci" <who> */ 1588 if ( strcasecmp( left, "aci" ) == 0 ) { 1589 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1590 "undocumented deprecated \"aci\" directive " 1591 "is superseded by \"dynacl/aci\".\n", 1592 fname, lineno ); 1593 name = "aci"; 1594 1595 } else 1596#endif /* tolerate legacy "aci" <who> */ 1597 if ( strncasecmp( left, "dynacl/", STRLENOF( "dynacl/" ) ) == 0 ) { 1598 name = &left[ STRLENOF( "dynacl/" ) ]; 1599 opts = strchr( name, '/' ); 1600 if ( opts ) { 1601 opts[ 0 ] = '\0'; 1602 opts++; 1603 } 1604 } 1605 1606 if ( name ) { 1607 if ( slap_dynacl_config( fname, lineno, b, name, opts, sty, right ) ) { 1608 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1609 "unable to configure dynacl \"%s\".\n", 1610 fname, lineno, name ); 1611 goto fail; 1612 } 1613 1614 continue; 1615 } 1616 } 1617#endif /* SLAP_DYNACL */ 1618 1619 if ( strcasecmp( left, "ssf" ) == 0 ) { 1620 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) { 1621 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1622 "inappropriate style \"%s\" in by clause.\n", 1623 fname, lineno, style ); 1624 goto fail; 1625 } 1626 1627 if ( b->a_authz.sai_ssf ) { 1628 Debug( LDAP_DEBUG_ANY, 1629 "%s: line %d: ssf attribute already specified.\n", 1630 fname, lineno ); 1631 goto fail; 1632 } 1633 1634 if ( right == NULL || *right == '\0' ) { 1635 Debug( LDAP_DEBUG_ANY, 1636 "%s: line %d: no ssf is defined.\n", 1637 fname, lineno ); 1638 goto fail; 1639 } 1640 1641 if ( lutil_atou( &b->a_authz.sai_ssf, right ) != 0 ) { 1642 Debug( LDAP_DEBUG_ANY, 1643 "%s: line %d: unable to parse ssf value (%s).\n", 1644 fname, lineno, right ); 1645 goto fail; 1646 } 1647 1648 if ( !b->a_authz.sai_ssf ) { 1649 Debug( LDAP_DEBUG_ANY, 1650 "%s: line %d: invalid ssf value (%s).\n", 1651 fname, lineno, right ); 1652 goto fail; 1653 } 1654 continue; 1655 } 1656 1657 if ( strcasecmp( left, "transport_ssf" ) == 0 ) { 1658 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) { 1659 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1660 "inappropriate style \"%s\" in by clause.\n", 1661 fname, lineno, style ); 1662 goto fail; 1663 } 1664 1665 if ( b->a_authz.sai_transport_ssf ) { 1666 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1667 "transport_ssf attribute already specified.\n", 1668 fname, lineno ); 1669 goto fail; 1670 } 1671 1672 if ( right == NULL || *right == '\0' ) { 1673 Debug( LDAP_DEBUG_ANY, 1674 "%s: line %d: no transport_ssf is defined.\n", 1675 fname, lineno ); 1676 goto fail; 1677 } 1678 1679 if ( lutil_atou( &b->a_authz.sai_transport_ssf, right ) != 0 ) { 1680 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1681 "unable to parse transport_ssf value (%s).\n", 1682 fname, lineno, right ); 1683 goto fail; 1684 } 1685 1686 if ( !b->a_authz.sai_transport_ssf ) { 1687 Debug( LDAP_DEBUG_ANY, 1688 "%s: line %d: invalid transport_ssf value (%s).\n", 1689 fname, lineno, right ); 1690 goto fail; 1691 } 1692 continue; 1693 } 1694 1695 if ( strcasecmp( left, "tls_ssf" ) == 0 ) { 1696 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) { 1697 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1698 "inappropriate style \"%s\" in by clause.\n", 1699 fname, lineno, style ); 1700 goto fail; 1701 } 1702 1703 if ( b->a_authz.sai_tls_ssf ) { 1704 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1705 "tls_ssf attribute already specified.\n", 1706 fname, lineno ); 1707 goto fail; 1708 } 1709 1710 if ( right == NULL || *right == '\0' ) { 1711 Debug( LDAP_DEBUG_ANY, 1712 "%s: line %d: no tls_ssf is defined\n", 1713 fname, lineno ); 1714 goto fail; 1715 } 1716 1717 if ( lutil_atou( &b->a_authz.sai_tls_ssf, right ) != 0 ) { 1718 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1719 "unable to parse tls_ssf value (%s).\n", 1720 fname, lineno, right ); 1721 goto fail; 1722 } 1723 1724 if ( !b->a_authz.sai_tls_ssf ) { 1725 Debug( LDAP_DEBUG_ANY, 1726 "%s: line %d: invalid tls_ssf value (%s).\n", 1727 fname, lineno, right ); 1728 goto fail; 1729 } 1730 continue; 1731 } 1732 1733 if ( strcasecmp( left, "sasl_ssf" ) == 0 ) { 1734 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) { 1735 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1736 "inappropriate style \"%s\" in by clause.\n", 1737 fname, lineno, style ); 1738 goto fail; 1739 } 1740 1741 if ( b->a_authz.sai_sasl_ssf ) { 1742 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1743 "sasl_ssf attribute already specified.\n", 1744 fname, lineno ); 1745 goto fail; 1746 } 1747 1748 if ( right == NULL || *right == '\0' ) { 1749 Debug( LDAP_DEBUG_ANY, 1750 "%s: line %d: no sasl_ssf is defined.\n", 1751 fname, lineno ); 1752 goto fail; 1753 } 1754 1755 if ( lutil_atou( &b->a_authz.sai_sasl_ssf, right ) != 0 ) { 1756 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1757 "unable to parse sasl_ssf value (%s).\n", 1758 fname, lineno, right ); 1759 goto fail; 1760 } 1761 1762 if ( !b->a_authz.sai_sasl_ssf ) { 1763 Debug( LDAP_DEBUG_ANY, 1764 "%s: line %d: invalid sasl_ssf value (%s).\n", 1765 fname, lineno, right ); 1766 goto fail; 1767 } 1768 continue; 1769 } 1770 1771 if ( right != NULL ) { 1772 /* unsplit */ 1773 right[-1] = '='; 1774 } 1775 break; 1776 } 1777 1778 if ( i == argc || ( strcasecmp( left, "stop" ) == 0 ) ) { 1779 /* out of arguments or plain stop */ 1780 1781 ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE ); 1782 ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE); 1783 b->a_type = ACL_STOP; 1784 1785 access_append( &a->acl_access, b ); 1786 continue; 1787 } 1788 1789 if ( strcasecmp( left, "continue" ) == 0 ) { 1790 /* plain continue */ 1791 1792 ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE ); 1793 ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE); 1794 b->a_type = ACL_CONTINUE; 1795 1796 access_append( &a->acl_access, b ); 1797 continue; 1798 } 1799 1800 if ( strcasecmp( left, "break" ) == 0 ) { 1801 /* plain continue */ 1802 1803 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE); 1804 ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE); 1805 b->a_type = ACL_BREAK; 1806 1807 access_append( &a->acl_access, b ); 1808 continue; 1809 } 1810 1811 if ( strcasecmp( left, "by" ) == 0 ) { 1812 /* we've gone too far */ 1813 --i; 1814 ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE ); 1815 ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE); 1816 b->a_type = ACL_STOP; 1817 1818 access_append( &a->acl_access, b ); 1819 continue; 1820 } 1821 1822 /* get <access> */ 1823 { 1824 char *lleft = left; 1825 1826 if ( strncasecmp( left, "self", STRLENOF( "self" ) ) == 0 ) { 1827 b->a_dn_self = 1; 1828 lleft = &left[ STRLENOF( "self" ) ]; 1829 1830 } else if ( strncasecmp( left, "realself", STRLENOF( "realself" ) ) == 0 ) { 1831 b->a_realdn_self = 1; 1832 lleft = &left[ STRLENOF( "realself" ) ]; 1833 } 1834 1835 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( lleft ) ); 1836 } 1837 1838 if ( ACL_IS_INVALID( b->a_access_mask ) ) { 1839 Debug( LDAP_DEBUG_ANY, 1840 "%s: line %d: expecting <access> got \"%s\".\n", 1841 fname, lineno, left ); 1842 goto fail; 1843 } 1844 1845 b->a_type = ACL_STOP; 1846 1847 if ( ++i == argc ) { 1848 /* out of arguments or plain stop */ 1849 access_append( &a->acl_access, b ); 1850 continue; 1851 } 1852 1853 if ( strcasecmp( argv[i], "continue" ) == 0 ) { 1854 /* plain continue */ 1855 b->a_type = ACL_CONTINUE; 1856 1857 } else if ( strcasecmp( argv[i], "break" ) == 0 ) { 1858 /* plain continue */ 1859 b->a_type = ACL_BREAK; 1860 1861 } else if ( strcasecmp( argv[i], "stop" ) != 0 ) { 1862 /* gone to far */ 1863 i--; 1864 } 1865 1866 access_append( &a->acl_access, b ); 1867 b = NULL; 1868 1869 } else { 1870 Debug( LDAP_DEBUG_ANY, 1871 "%s: line %d: expecting \"to\" " 1872 "or \"by\" got \"%s\"\n", 1873 fname, lineno, argv[i] ); 1874 goto fail; 1875 } 1876 } 1877 1878 /* if we have no real access clause, complain and do nothing */ 1879 if ( a == NULL ) { 1880 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1881 "warning: no access clause(s) specified in access line.\n", 1882 fname, lineno ); 1883 goto fail; 1884 1885 } else { 1886#ifdef LDAP_DEBUG 1887 if ( slap_debug & LDAP_DEBUG_ACL ) { 1888 print_acl( be, a ); 1889 } 1890#endif 1891 1892 if ( a->acl_access == NULL ) { 1893 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1894 "warning: no by clause(s) specified in access line.\n", 1895 fname, lineno ); 1896 goto fail; 1897 } 1898 1899 if ( be != NULL ) { 1900 if ( be->be_nsuffix == NULL ) { 1901 Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: " 1902 "scope checking needs suffix before ACLs.\n", 1903 fname, lineno ); 1904 /* go ahead, since checking is not authoritative */ 1905 } else if ( !BER_BVISNULL( &be->be_nsuffix[ 1 ] ) ) { 1906 Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: " 1907 "scope checking only applies to single-valued " 1908 "suffix databases\n", 1909 fname, lineno ); 1910 /* go ahead, since checking is not authoritative */ 1911 } else { 1912 switch ( check_scope( be, a ) ) { 1913 case ACL_SCOPE_UNKNOWN: 1914 Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: " 1915 "cannot assess the validity of the ACL scope within " 1916 "backend naming context\n", 1917 fname, lineno ); 1918 break; 1919 1920 case ACL_SCOPE_WARN: 1921 Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: " 1922 "ACL could be out of scope within backend naming context\n", 1923 fname, lineno ); 1924 break; 1925 1926 case ACL_SCOPE_PARTIAL: 1927 Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: " 1928 "ACL appears to be partially out of scope within " 1929 "backend naming context\n", 1930 fname, lineno ); 1931 break; 1932 1933 case ACL_SCOPE_ERR: 1934 Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: " 1935 "ACL appears to be out of scope within " 1936 "backend naming context\n", 1937 fname, lineno ); 1938 break; 1939 1940 default: 1941 break; 1942 } 1943 } 1944 acl_append( &be->be_acl, a, pos ); 1945 1946 } else { 1947 acl_append( &frontendDB->be_acl, a, pos ); 1948 } 1949 } 1950 1951 return 0; 1952 1953fail: 1954 if ( b ) access_free( b ); 1955 if ( a ) acl_free( a ); 1956 return acl_usage(); 1957} 1958 1959char * 1960accessmask2str( slap_mask_t mask, char *buf, int debug ) 1961{ 1962 int none = 1; 1963 char *ptr = buf; 1964 1965 assert( buf != NULL ); 1966 1967 if ( ACL_IS_INVALID( mask ) ) { 1968 return "invalid"; 1969 } 1970 1971 buf[0] = '\0'; 1972 1973 if ( ACL_IS_LEVEL( mask ) ) { 1974 if ( ACL_LVL_IS_NONE(mask) ) { 1975 ptr = lutil_strcopy( ptr, "none" ); 1976 1977 } else if ( ACL_LVL_IS_DISCLOSE(mask) ) { 1978 ptr = lutil_strcopy( ptr, "disclose" ); 1979 1980 } else if ( ACL_LVL_IS_AUTH(mask) ) { 1981 ptr = lutil_strcopy( ptr, "auth" ); 1982 1983 } else if ( ACL_LVL_IS_COMPARE(mask) ) { 1984 ptr = lutil_strcopy( ptr, "compare" ); 1985 1986 } else if ( ACL_LVL_IS_SEARCH(mask) ) { 1987 ptr = lutil_strcopy( ptr, "search" ); 1988 1989 } else if ( ACL_LVL_IS_READ(mask) ) { 1990 ptr = lutil_strcopy( ptr, "read" ); 1991 1992 } else if ( ACL_LVL_IS_WRITE(mask) ) { 1993 ptr = lutil_strcopy( ptr, "write" ); 1994 1995 } else if ( ACL_LVL_IS_WADD(mask) ) { 1996 ptr = lutil_strcopy( ptr, "add" ); 1997 1998 } else if ( ACL_LVL_IS_WDEL(mask) ) { 1999 ptr = lutil_strcopy( ptr, "delete" ); 2000 2001 } else if ( ACL_LVL_IS_MANAGE(mask) ) { 2002 ptr = lutil_strcopy( ptr, "manage" ); 2003 2004 } else { 2005 ptr = lutil_strcopy( ptr, "unknown" ); 2006 } 2007 2008 if ( !debug ) { 2009 *ptr = '\0'; 2010 return buf; 2011 } 2012 *ptr++ = '('; 2013 } 2014 2015 if( ACL_IS_ADDITIVE( mask ) ) { 2016 *ptr++ = '+'; 2017 2018 } else if( ACL_IS_SUBTRACTIVE( mask ) ) { 2019 *ptr++ = '-'; 2020 2021 } else { 2022 *ptr++ = '='; 2023 } 2024 2025 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_MANAGE) ) { 2026 none = 0; 2027 *ptr++ = 'm'; 2028 } 2029 2030 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) { 2031 none = 0; 2032 *ptr++ = 'w'; 2033 2034 } else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WADD) ) { 2035 none = 0; 2036 *ptr++ = 'a'; 2037 2038 } else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WDEL) ) { 2039 none = 0; 2040 *ptr++ = 'z'; 2041 } 2042 2043 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) { 2044 none = 0; 2045 *ptr++ = 'r'; 2046 } 2047 2048 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) { 2049 none = 0; 2050 *ptr++ = 's'; 2051 } 2052 2053 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) { 2054 none = 0; 2055 *ptr++ = 'c'; 2056 } 2057 2058 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) { 2059 none = 0; 2060 *ptr++ = 'x'; 2061 } 2062 2063 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_DISCLOSE) ) { 2064 none = 0; 2065 *ptr++ = 'd'; 2066 } 2067 2068 if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) { 2069 none = 0; 2070 *ptr++ = '0'; 2071 } 2072 2073 if ( none ) { 2074 ptr = buf; 2075 } 2076 2077 if ( ACL_IS_LEVEL( mask ) ) { 2078 *ptr++ = ')'; 2079 } 2080 2081 *ptr = '\0'; 2082 2083 return buf; 2084} 2085 2086slap_mask_t 2087str2accessmask( const char *str ) 2088{ 2089 slap_mask_t mask; 2090 2091 if( !ASCII_ALPHA(str[0]) ) { 2092 int i; 2093 2094 if ( str[0] == '=' ) { 2095 ACL_INIT(mask); 2096 2097 } else if( str[0] == '+' ) { 2098 ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE); 2099 2100 } else if( str[0] == '-' ) { 2101 ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE); 2102 2103 } else { 2104 ACL_INVALIDATE(mask); 2105 return mask; 2106 } 2107 2108 for( i=1; str[i] != '\0'; i++ ) { 2109 if( TOLOWER((unsigned char) str[i]) == 'm' ) { 2110 ACL_PRIV_SET(mask, ACL_PRIV_MANAGE); 2111 2112 } else if( TOLOWER((unsigned char) str[i]) == 'w' ) { 2113 ACL_PRIV_SET(mask, ACL_PRIV_WRITE); 2114 2115 } else if( TOLOWER((unsigned char) str[i]) == 'a' ) { 2116 ACL_PRIV_SET(mask, ACL_PRIV_WADD); 2117 2118 } else if( TOLOWER((unsigned char) str[i]) == 'z' ) { 2119 ACL_PRIV_SET(mask, ACL_PRIV_WDEL); 2120 2121 } else if( TOLOWER((unsigned char) str[i]) == 'r' ) { 2122 ACL_PRIV_SET(mask, ACL_PRIV_READ); 2123 2124 } else if( TOLOWER((unsigned char) str[i]) == 's' ) { 2125 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH); 2126 2127 } else if( TOLOWER((unsigned char) str[i]) == 'c' ) { 2128 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE); 2129 2130 } else if( TOLOWER((unsigned char) str[i]) == 'x' ) { 2131 ACL_PRIV_SET(mask, ACL_PRIV_AUTH); 2132 2133 } else if( TOLOWER((unsigned char) str[i]) == 'd' ) { 2134 ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE); 2135 2136 } else if( str[i] == '0' ) { 2137 ACL_PRIV_SET(mask, ACL_PRIV_NONE); 2138 2139 } else { 2140 ACL_INVALIDATE(mask); 2141 return mask; 2142 } 2143 } 2144 2145 return mask; 2146 } 2147 2148 if ( strcasecmp( str, "none" ) == 0 ) { 2149 ACL_LVL_ASSIGN_NONE(mask); 2150 2151 } else if ( strcasecmp( str, "disclose" ) == 0 ) { 2152 ACL_LVL_ASSIGN_DISCLOSE(mask); 2153 2154 } else if ( strcasecmp( str, "auth" ) == 0 ) { 2155 ACL_LVL_ASSIGN_AUTH(mask); 2156 2157 } else if ( strcasecmp( str, "compare" ) == 0 ) { 2158 ACL_LVL_ASSIGN_COMPARE(mask); 2159 2160 } else if ( strcasecmp( str, "search" ) == 0 ) { 2161 ACL_LVL_ASSIGN_SEARCH(mask); 2162 2163 } else if ( strcasecmp( str, "read" ) == 0 ) { 2164 ACL_LVL_ASSIGN_READ(mask); 2165 2166 } else if ( strcasecmp( str, "add" ) == 0 ) { 2167 ACL_LVL_ASSIGN_WADD(mask); 2168 2169 } else if ( strcasecmp( str, "delete" ) == 0 ) { 2170 ACL_LVL_ASSIGN_WDEL(mask); 2171 2172 } else if ( strcasecmp( str, "write" ) == 0 ) { 2173 ACL_LVL_ASSIGN_WRITE(mask); 2174 2175 } else if ( strcasecmp( str, "manage" ) == 0 ) { 2176 ACL_LVL_ASSIGN_MANAGE(mask); 2177 2178 } else { 2179 ACL_INVALIDATE( mask ); 2180 } 2181 2182 return mask; 2183} 2184 2185static int 2186acl_usage( void ) 2187{ 2188 char *access = 2189 "<access clause> ::= access to <what> " 2190 "[ by <who> [ <access> ] [ <control> ] ]+ \n"; 2191 char *what = 2192 "<what> ::= * | dn[.<dnstyle>=<DN>] [filter=<filter>] [attrs=<attrspec>]\n" 2193 "<attrspec> ::= <attrname> [val[/<matchingRule>][.<attrstyle>]=<value>] | <attrlist>\n" 2194 "<attrlist> ::= <attr> [ , <attrlist> ]\n" 2195 "<attr> ::= <attrname> | @<objectClass> | !<objectClass> | entry | children\n"; 2196 2197 char *who = 2198 "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n" 2199 "\t[ realanonymous | realusers | realself | realdn[.<dnstyle>]=<DN> ]\n" 2200 "\t[dnattr=<attrname>]\n" 2201 "\t[realdnattr=<attrname>]\n" 2202 "\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n" 2203 "\t[peername[.<peernamestyle>]=<peer>] [sockname[.<style>]=<name>]\n" 2204 "\t[domain[.<domainstyle>]=<domain>] [sockurl[.<style>]=<url>]\n" 2205#ifdef SLAP_DYNACL 2206 "\t[dynacl/<name>[/<options>][.<dynstyle>][=<pattern>]]\n" 2207#endif /* SLAP_DYNACL */ 2208 "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n" 2209 "<style> ::= exact | regex | base(Object)\n" 2210 "<dnstyle> ::= base(Object) | one(level) | sub(tree) | children | " 2211 "exact | regex\n" 2212 "<attrstyle> ::= exact | regex | base(Object) | one(level) | " 2213 "sub(tree) | children\n" 2214 "<peernamestyle> ::= exact | regex | ip | ipv6 | path\n" 2215 "<domainstyle> ::= exact | regex | base(Object) | sub(tree)\n" 2216 "<access> ::= [[real]self]{<level>|<priv>}\n" 2217 "<level> ::= none|disclose|auth|compare|search|read|{write|add|delete}|manage\n" 2218 "<priv> ::= {=|+|-}{0|d|x|c|s|r|{w|a|z}|m}+\n" 2219 "<control> ::= [ stop | continue | break ]\n" 2220#ifdef SLAP_DYNACL 2221#ifdef SLAPD_ACI_ENABLED 2222 "dynacl:\n" 2223 "\t<name>=ACI\t<pattern>=<attrname>\n" 2224#endif /* SLAPD_ACI_ENABLED */ 2225#endif /* ! SLAP_DYNACL */ 2226 ""; 2227 2228 Debug( LDAP_DEBUG_ANY, "%s%s%s\n", access, what, who ); 2229 2230 return 1; 2231} 2232 2233/* 2234 * Set pattern to a "normalized" DN from src. 2235 * At present it simply eats the (optional) space after 2236 * a RDN separator (,) 2237 * Eventually will evolve in a more complete normalization 2238 */ 2239static void 2240acl_regex_normalized_dn( 2241 const char *src, 2242 struct berval *pattern ) 2243{ 2244 char *str, *p; 2245 ber_len_t len; 2246 2247 str = ch_strdup( src ); 2248 len = strlen( src ); 2249 2250 for ( p = str; p && p[0]; p++ ) { 2251 /* escape */ 2252 if ( p[0] == '\\' && p[1] ) { 2253 /* 2254 * if escaping a hex pair we should 2255 * increment p twice; however, in that 2256 * case the second hex number does 2257 * no harm 2258 */ 2259 p++; 2260 } 2261 2262 if ( p[0] == ',' && p[1] == ' ' ) { 2263 char *q; 2264 2265 /* 2266 * too much space should be an error if we are pedantic 2267 */ 2268 for ( q = &p[2]; q[0] == ' '; q++ ) { 2269 /* DO NOTHING */ ; 2270 } 2271 AC_MEMCPY( p+1, q, len-(q-str)+1); 2272 } 2273 } 2274 pattern->bv_val = str; 2275 pattern->bv_len = p - str; 2276 2277 return; 2278} 2279 2280static void 2281split( 2282 char *line, 2283 int splitchar, 2284 char **left, 2285 char **right ) 2286{ 2287 *left = line; 2288 if ( (*right = strchr( line, splitchar )) != NULL ) { 2289 *((*right)++) = '\0'; 2290 } 2291} 2292 2293static void 2294access_append( Access **l, Access *a ) 2295{ 2296 for ( ; *l != NULL; l = &(*l)->a_next ) { 2297 ; /* Empty */ 2298 } 2299 2300 *l = a; 2301} 2302 2303void 2304acl_append( AccessControl **l, AccessControl *a, int pos ) 2305{ 2306 int i; 2307 2308 for (i=0 ; i != pos && *l != NULL; l = &(*l)->acl_next, i++ ) { 2309 ; /* Empty */ 2310 } 2311 if ( *l && a ) 2312 a->acl_next = *l; 2313 *l = a; 2314} 2315 2316static void 2317access_free( Access *a ) 2318{ 2319 if ( !BER_BVISNULL( &a->a_dn_pat ) ) { 2320 free( a->a_dn_pat.bv_val ); 2321 } 2322 if ( !BER_BVISNULL( &a->a_realdn_pat ) ) { 2323 free( a->a_realdn_pat.bv_val ); 2324 } 2325 if ( !BER_BVISNULL( &a->a_peername_pat ) ) { 2326 free( a->a_peername_pat.bv_val ); 2327 } 2328 if ( !BER_BVISNULL( &a->a_sockname_pat ) ) { 2329 free( a->a_sockname_pat.bv_val ); 2330 } 2331 if ( !BER_BVISNULL( &a->a_domain_pat ) ) { 2332 free( a->a_domain_pat.bv_val ); 2333 } 2334 if ( !BER_BVISNULL( &a->a_sockurl_pat ) ) { 2335 free( a->a_sockurl_pat.bv_val ); 2336 } 2337 if ( !BER_BVISNULL( &a->a_set_pat ) ) { 2338 free( a->a_set_pat.bv_val ); 2339 } 2340 if ( !BER_BVISNULL( &a->a_group_pat ) ) { 2341 free( a->a_group_pat.bv_val ); 2342 } 2343#ifdef SLAP_DYNACL 2344 if ( a->a_dynacl != NULL ) { 2345 slap_dynacl_t *da; 2346 for ( da = a->a_dynacl; da; ) { 2347 slap_dynacl_t *tmp = da; 2348 2349 da = da->da_next; 2350 2351 if ( tmp->da_destroy ) { 2352 tmp->da_destroy( tmp->da_private ); 2353 } 2354 2355 ch_free( tmp ); 2356 } 2357 } 2358#endif /* SLAP_DYNACL */ 2359 free( a ); 2360} 2361 2362void 2363acl_free( AccessControl *a ) 2364{ 2365 Access *n; 2366 AttributeName *an; 2367 2368 if ( a->acl_filter ) { 2369 filter_free( a->acl_filter ); 2370 } 2371 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) { 2372 if ( a->acl_dn_style == ACL_STYLE_REGEX ) { 2373 regfree( &a->acl_dn_re ); 2374 } 2375 free ( a->acl_dn_pat.bv_val ); 2376 } 2377 if ( a->acl_attrs ) { 2378 for ( an = a->acl_attrs; !BER_BVISNULL( &an->an_name ); an++ ) { 2379 free( an->an_name.bv_val ); 2380 } 2381 free( a->acl_attrs ); 2382 2383 if ( a->acl_attrval_style == ACL_STYLE_REGEX ) { 2384 regfree( &a->acl_attrval_re ); 2385 } 2386 2387 if ( !BER_BVISNULL( &a->acl_attrval ) ) { 2388 ber_memfree( a->acl_attrval.bv_val ); 2389 } 2390 } 2391 for ( ; a->acl_access; a->acl_access = n ) { 2392 n = a->acl_access->a_next; 2393 access_free( a->acl_access ); 2394 } 2395 free( a ); 2396} 2397 2398void 2399acl_destroy( AccessControl *a ) 2400{ 2401 AccessControl *n; 2402 2403 for ( ; a; a = n ) { 2404 n = a->acl_next; 2405 acl_free( a ); 2406 } 2407 2408 if ( !BER_BVISNULL( &aclbuf ) ) { 2409 ch_free( aclbuf.bv_val ); 2410 BER_BVZERO( &aclbuf ); 2411 } 2412} 2413 2414char * 2415access2str( slap_access_t access ) 2416{ 2417 if ( access == ACL_NONE ) { 2418 return "none"; 2419 2420 } else if ( access == ACL_DISCLOSE ) { 2421 return "disclose"; 2422 2423 } else if ( access == ACL_AUTH ) { 2424 return "auth"; 2425 2426 } else if ( access == ACL_COMPARE ) { 2427 return "compare"; 2428 2429 } else if ( access == ACL_SEARCH ) { 2430 return "search"; 2431 2432 } else if ( access == ACL_READ ) { 2433 return "read"; 2434 2435 } else if ( access == ACL_WRITE ) { 2436 return "write"; 2437 2438 } else if ( access == ACL_WADD ) { 2439 return "add"; 2440 2441 } else if ( access == ACL_WDEL ) { 2442 return "delete"; 2443 2444 } else if ( access == ACL_MANAGE ) { 2445 return "manage"; 2446 2447 } 2448 2449 return "unknown"; 2450} 2451 2452slap_access_t 2453str2access( const char *str ) 2454{ 2455 if ( strcasecmp( str, "none" ) == 0 ) { 2456 return ACL_NONE; 2457 2458 } else if ( strcasecmp( str, "disclose" ) == 0 ) { 2459 return ACL_DISCLOSE; 2460 2461 } else if ( strcasecmp( str, "auth" ) == 0 ) { 2462 return ACL_AUTH; 2463 2464 } else if ( strcasecmp( str, "compare" ) == 0 ) { 2465 return ACL_COMPARE; 2466 2467 } else if ( strcasecmp( str, "search" ) == 0 ) { 2468 return ACL_SEARCH; 2469 2470 } else if ( strcasecmp( str, "read" ) == 0 ) { 2471 return ACL_READ; 2472 2473 } else if ( strcasecmp( str, "write" ) == 0 ) { 2474 return ACL_WRITE; 2475 2476 } else if ( strcasecmp( str, "add" ) == 0 ) { 2477 return ACL_WADD; 2478 2479 } else if ( strcasecmp( str, "delete" ) == 0 ) { 2480 return ACL_WDEL; 2481 2482 } else if ( strcasecmp( str, "manage" ) == 0 ) { 2483 return ACL_MANAGE; 2484 } 2485 2486 return( ACL_INVALID_ACCESS ); 2487} 2488 2489static char * 2490safe_strncopy( char *ptr, const char *src, size_t n, struct berval *buf ) 2491{ 2492 while ( ptr + n >= buf->bv_val + buf->bv_len ) { 2493 char *tmp = ch_realloc( buf->bv_val, 2*buf->bv_len ); 2494 if ( tmp == NULL ) { 2495 return NULL; 2496 } 2497 ptr = tmp + (ptr - buf->bv_val); 2498 buf->bv_val = tmp; 2499 buf->bv_len *= 2; 2500 } 2501 2502 return lutil_strncopy( ptr, src, n ); 2503} 2504 2505static char * 2506safe_strcopy( char *ptr, const char *s, struct berval *buf ) 2507{ 2508 size_t n = strlen( s ); 2509 2510 return safe_strncopy( ptr, s, n, buf ); 2511} 2512 2513static char * 2514safe_strbvcopy( char *ptr, const struct berval *bv, struct berval *buf ) 2515{ 2516 return safe_strncopy( ptr, bv->bv_val, bv->bv_len, buf ); 2517} 2518 2519#define acl_safe_strcopy( ptr, s ) safe_strcopy( (ptr), (s), &aclbuf ) 2520#define acl_safe_strncopy( ptr, s, n ) safe_strncopy( (ptr), (s), (n), &aclbuf ) 2521#define acl_safe_strbvcopy( ptr, bv ) safe_strbvcopy( (ptr), (bv), &aclbuf ) 2522 2523static char * 2524dnaccess2text( slap_dn_access *bdn, char *ptr, int is_realdn ) 2525{ 2526 *ptr++ = ' '; 2527 2528 if ( is_realdn ) { 2529 ptr = acl_safe_strcopy( ptr, "real" ); 2530 } 2531 2532 if ( ber_bvccmp( &bdn->a_pat, '*' ) || 2533 bdn->a_style == ACL_STYLE_ANONYMOUS || 2534 bdn->a_style == ACL_STYLE_USERS || 2535 bdn->a_style == ACL_STYLE_SELF ) 2536 { 2537 if ( is_realdn ) { 2538 assert( ! ber_bvccmp( &bdn->a_pat, '*' ) ); 2539 } 2540 2541 ptr = acl_safe_strbvcopy( ptr, &bdn->a_pat ); 2542 if ( bdn->a_style == ACL_STYLE_SELF && bdn->a_self_level != 0 ) { 2543 char buf[SLAP_TEXT_BUFLEN]; 2544 int n = snprintf( buf, sizeof(buf), ".level{%d}", bdn->a_self_level ); 2545 if ( n > 0 ) { 2546 ptr = acl_safe_strncopy( ptr, buf, n ); 2547 } /* else ? */ 2548 } 2549 2550 } else { 2551 ptr = acl_safe_strcopy( ptr, "dn." ); 2552 if ( bdn->a_style == ACL_STYLE_BASE ) 2553 ptr = acl_safe_strcopy( ptr, style_base ); 2554 else 2555 ptr = acl_safe_strcopy( ptr, style_strings[bdn->a_style] ); 2556 if ( bdn->a_style == ACL_STYLE_LEVEL ) { 2557 char buf[SLAP_TEXT_BUFLEN]; 2558 int n = snprintf( buf, sizeof(buf), "{%d}", bdn->a_level ); 2559 if ( n > 0 ) { 2560 ptr = acl_safe_strncopy( ptr, buf, n ); 2561 } /* else ? */ 2562 } 2563 if ( bdn->a_expand ) { 2564 ptr = acl_safe_strcopy( ptr, ",expand" ); 2565 } 2566 ptr = acl_safe_strcopy( ptr, "=\"" ); 2567 ptr = acl_safe_strbvcopy( ptr, &bdn->a_pat ); 2568 ptr = acl_safe_strcopy( ptr, "\"" ); 2569 } 2570 return ptr; 2571} 2572 2573static char * 2574access2text( Access *b, char *ptr ) 2575{ 2576 char maskbuf[ACCESSMASK_MAXLEN]; 2577 2578 ptr = acl_safe_strcopy( ptr, "\tby" ); 2579 2580 if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) { 2581 ptr = dnaccess2text( &b->a_dn, ptr, 0 ); 2582 } 2583 if ( b->a_dn_at ) { 2584 ptr = acl_safe_strcopy( ptr, " dnattr=" ); 2585 ptr = acl_safe_strbvcopy( ptr, &b->a_dn_at->ad_cname ); 2586 } 2587 2588 if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) { 2589 ptr = dnaccess2text( &b->a_realdn, ptr, 1 ); 2590 } 2591 if ( b->a_realdn_at ) { 2592 ptr = acl_safe_strcopy( ptr, " realdnattr=" ); 2593 ptr = acl_safe_strbvcopy( ptr, &b->a_realdn_at->ad_cname ); 2594 } 2595 2596 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) { 2597 ptr = acl_safe_strcopy( ptr, " group/" ); 2598 ptr = acl_safe_strcopy( ptr, b->a_group_oc ? 2599 b->a_group_oc->soc_cname.bv_val : SLAPD_GROUP_CLASS ); 2600 ptr = acl_safe_strcopy( ptr, "/" ); 2601 ptr = acl_safe_strcopy( ptr, b->a_group_at ? 2602 b->a_group_at->ad_cname.bv_val : SLAPD_GROUP_ATTR ); 2603 ptr = acl_safe_strcopy( ptr, "." ); 2604 ptr = acl_safe_strcopy( ptr, style_strings[b->a_group_style] ); 2605 ptr = acl_safe_strcopy( ptr, "=\"" ); 2606 ptr = acl_safe_strbvcopy( ptr, &b->a_group_pat ); 2607 ptr = acl_safe_strcopy( ptr, "\"" ); 2608 } 2609 2610 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) { 2611 ptr = acl_safe_strcopy( ptr, " peername" ); 2612 ptr = acl_safe_strcopy( ptr, "." ); 2613 ptr = acl_safe_strcopy( ptr, style_strings[b->a_peername_style] ); 2614 ptr = acl_safe_strcopy( ptr, "=\"" ); 2615 ptr = acl_safe_strbvcopy( ptr, &b->a_peername_pat ); 2616 ptr = acl_safe_strcopy( ptr, "\"" ); 2617 } 2618 2619 if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) { 2620 ptr = acl_safe_strcopy( ptr, " sockname" ); 2621 ptr = acl_safe_strcopy( ptr, "." ); 2622 ptr = acl_safe_strcopy( ptr, style_strings[b->a_sockname_style] ); 2623 ptr = acl_safe_strcopy( ptr, "=\"" ); 2624 ptr = acl_safe_strbvcopy( ptr, &b->a_sockname_pat ); 2625 ptr = acl_safe_strcopy( ptr, "\"" ); 2626 } 2627 2628 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) { 2629 ptr = acl_safe_strcopy( ptr, " domain" ); 2630 ptr = acl_safe_strcopy( ptr, "." ); 2631 ptr = acl_safe_strcopy( ptr, style_strings[b->a_domain_style] ); 2632 if ( b->a_domain_expand ) { 2633 ptr = acl_safe_strcopy( ptr, ",expand" ); 2634 } 2635 ptr = acl_safe_strcopy( ptr, "=" ); 2636 ptr = acl_safe_strbvcopy( ptr, &b->a_domain_pat ); 2637 } 2638 2639 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) { 2640 ptr = acl_safe_strcopy( ptr, " sockurl" ); 2641 ptr = acl_safe_strcopy( ptr, "." ); 2642 ptr = acl_safe_strcopy( ptr, style_strings[b->a_sockurl_style] ); 2643 ptr = acl_safe_strcopy( ptr, "=\"" ); 2644 ptr = acl_safe_strbvcopy( ptr, &b->a_sockurl_pat ); 2645 ptr = acl_safe_strcopy( ptr, "\"" ); 2646 } 2647 2648 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) { 2649 ptr = acl_safe_strcopy( ptr, " set" ); 2650 ptr = acl_safe_strcopy( ptr, "." ); 2651 ptr = acl_safe_strcopy( ptr, style_strings[b->a_set_style] ); 2652 ptr = acl_safe_strcopy( ptr, "=\"" ); 2653 ptr = acl_safe_strbvcopy( ptr, &b->a_set_pat ); 2654 ptr = acl_safe_strcopy( ptr, "\"" ); 2655 } 2656 2657#ifdef SLAP_DYNACL 2658 if ( b->a_dynacl ) { 2659 slap_dynacl_t *da; 2660 2661 for ( da = b->a_dynacl; da; da = da->da_next ) { 2662 if ( da->da_unparse ) { 2663 struct berval bv = BER_BVNULL; 2664 (void)( *da->da_unparse )( da->da_private, &bv ); 2665 assert( !BER_BVISNULL( &bv ) ); 2666 ptr = acl_safe_strbvcopy( ptr, &bv ); 2667 ch_free( bv.bv_val ); 2668 } 2669 } 2670 } 2671#endif /* SLAP_DYNACL */ 2672 2673 /* Security Strength Factors */ 2674 if ( b->a_authz.sai_ssf ) { 2675 char buf[SLAP_TEXT_BUFLEN]; 2676 int n = snprintf( buf, sizeof(buf), " ssf=%u", 2677 b->a_authz.sai_ssf ); 2678 ptr = acl_safe_strncopy( ptr, buf, n ); 2679 } 2680 if ( b->a_authz.sai_transport_ssf ) { 2681 char buf[SLAP_TEXT_BUFLEN]; 2682 int n = snprintf( buf, sizeof(buf), " transport_ssf=%u", 2683 b->a_authz.sai_transport_ssf ); 2684 ptr = acl_safe_strncopy( ptr, buf, n ); 2685 } 2686 if ( b->a_authz.sai_tls_ssf ) { 2687 char buf[SLAP_TEXT_BUFLEN]; 2688 int n = snprintf( buf, sizeof(buf), " tls_ssf=%u", 2689 b->a_authz.sai_tls_ssf ); 2690 ptr = acl_safe_strncopy( ptr, buf, n ); 2691 } 2692 if ( b->a_authz.sai_sasl_ssf ) { 2693 char buf[SLAP_TEXT_BUFLEN]; 2694 int n = snprintf( buf, sizeof(buf), " sasl_ssf=%u", 2695 b->a_authz.sai_sasl_ssf ); 2696 ptr = acl_safe_strncopy( ptr, buf, n ); 2697 } 2698 2699 ptr = acl_safe_strcopy( ptr, " " ); 2700 if ( b->a_dn_self ) { 2701 ptr = acl_safe_strcopy( ptr, "self" ); 2702 } else if ( b->a_realdn_self ) { 2703 ptr = acl_safe_strcopy( ptr, "realself" ); 2704 } 2705 ptr = acl_safe_strcopy( ptr, accessmask2str( b->a_access_mask, maskbuf, 0 )); 2706 if ( !maskbuf[0] ) ptr--; 2707 2708 if( b->a_type == ACL_BREAK ) { 2709 ptr = acl_safe_strcopy( ptr, " break" ); 2710 2711 } else if( b->a_type == ACL_CONTINUE ) { 2712 ptr = acl_safe_strcopy( ptr, " continue" ); 2713 2714 } else if( b->a_type != ACL_STOP ) { 2715 ptr = acl_safe_strcopy( ptr, " unknown-control" ); 2716 } else { 2717 if ( !maskbuf[0] ) ptr = acl_safe_strcopy( ptr, " stop" ); 2718 } 2719 ptr = acl_safe_strcopy( ptr, "\n" ); 2720 2721 return ptr; 2722} 2723 2724void 2725acl_unparse( AccessControl *a, struct berval *bv ) 2726{ 2727 Access *b; 2728 char *ptr; 2729 int to = 0; 2730 2731 if ( BER_BVISNULL( &aclbuf ) ) { 2732 aclbuf.bv_val = ch_malloc( ACLBUF_CHUNKSIZE ); 2733 aclbuf.bv_len = ACLBUF_CHUNKSIZE; 2734 } 2735 2736 bv->bv_len = 0; 2737 2738 ptr = aclbuf.bv_val; 2739 2740 ptr = acl_safe_strcopy( ptr, "to" ); 2741 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) { 2742 to++; 2743 ptr = acl_safe_strcopy( ptr, " dn." ); 2744 if ( a->acl_dn_style == ACL_STYLE_BASE ) 2745 ptr = acl_safe_strcopy( ptr, style_base ); 2746 else 2747 ptr = acl_safe_strcopy( ptr, style_strings[a->acl_dn_style] ); 2748 ptr = acl_safe_strcopy( ptr, "=\"" ); 2749 ptr = acl_safe_strbvcopy( ptr, &a->acl_dn_pat ); 2750 ptr = acl_safe_strcopy( ptr, "\"\n" ); 2751 } 2752 2753 if ( a->acl_filter != NULL ) { 2754 struct berval fbv = BER_BVNULL; 2755 2756 to++; 2757 filter2bv( a->acl_filter, &fbv ); 2758 ptr = acl_safe_strcopy( ptr, " filter=\"" ); 2759 ptr = acl_safe_strbvcopy( ptr, &fbv ); 2760 ptr = acl_safe_strcopy( ptr, "\"\n" ); 2761 ch_free( fbv.bv_val ); 2762 } 2763 2764 if ( a->acl_attrs != NULL ) { 2765 int first = 1; 2766 AttributeName *an; 2767 to++; 2768 2769 ptr = acl_safe_strcopy( ptr, " attrs=" ); 2770 for ( an = a->acl_attrs; an && !BER_BVISNULL( &an->an_name ); an++ ) { 2771 if ( ! first ) ptr = acl_safe_strcopy( ptr, ","); 2772 if (an->an_oc) { 2773 ptr = acl_safe_strcopy( ptr, ( an->an_flags & SLAP_AN_OCEXCLUDE ) ? "!" : "@" ); 2774 ptr = acl_safe_strbvcopy( ptr, &an->an_oc->soc_cname ); 2775 2776 } else { 2777 ptr = acl_safe_strbvcopy( ptr, &an->an_name ); 2778 } 2779 first = 0; 2780 } 2781 ptr = acl_safe_strcopy( ptr, "\n" ); 2782 } 2783 2784 if ( !BER_BVISNULL( &a->acl_attrval ) ) { 2785 to++; 2786 ptr = acl_safe_strcopy( ptr, " val." ); 2787 if ( a->acl_attrval_style == ACL_STYLE_BASE && 2788 a->acl_attrs[0].an_desc->ad_type->sat_syntax == 2789 slap_schema.si_syn_distinguishedName ) 2790 ptr = acl_safe_strcopy( ptr, style_base ); 2791 else 2792 ptr = acl_safe_strcopy( ptr, style_strings[a->acl_attrval_style] ); 2793 ptr = acl_safe_strcopy( ptr, "=\"" ); 2794 ptr = acl_safe_strbvcopy( ptr, &a->acl_attrval ); 2795 ptr = acl_safe_strcopy( ptr, "\"\n" ); 2796 } 2797 2798 if ( !to ) { 2799 ptr = acl_safe_strcopy( ptr, " *\n" ); 2800 } 2801 2802 for ( b = a->acl_access; b != NULL; b = b->a_next ) { 2803 ptr = access2text( b, ptr ); 2804 } 2805 *ptr = '\0'; 2806 bv->bv_val = aclbuf.bv_val; 2807 bv->bv_len = ptr - bv->bv_val; 2808} 2809 2810#ifdef LDAP_DEBUG 2811static void 2812print_acl( Backend *be, AccessControl *a ) 2813{ 2814 struct berval bv; 2815 2816 acl_unparse( a, &bv ); 2817 fprintf( stderr, "%s ACL: access %s\n", 2818 be == NULL ? "Global" : "Backend", bv.bv_val ); 2819} 2820#endif /* LDAP_DEBUG */ 2821