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