1/* $NetBSD$ */ 2 3/* limits.c - routines to handle regex-based size and time limits */ 4/* OpenLDAP: pkg/ldap/servers/slapd/limits.c,v 1.73.2.12 2010/04/13 20:23:16 kurt Exp */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 1998-2010 The OpenLDAP Foundation. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 19#include "portable.h" 20 21#include <stdio.h> 22 23#include <ac/ctype.h> 24#include <ac/regex.h> 25#include <ac/string.h> 26 27#include "slap.h" 28#include "lutil.h" 29 30/* define to get an error if requesting limit higher than hard */ 31#undef ABOVE_HARD_LIMIT_IS_ERROR 32 33static const struct berval lmpats[] = { 34 BER_BVC( "base" ), 35 BER_BVC( "base" ), 36 BER_BVC( "onelevel" ), 37 BER_BVC( "subtree" ), 38 BER_BVC( "children" ), 39 BER_BVC( "regex" ), 40 BER_BVC( "anonymous" ), 41 BER_BVC( "users" ), 42 BER_BVC( "*" ) 43}; 44 45#ifdef LDAP_DEBUG 46static const char *const dn_source[2] = { "DN", "DN.THIS" }; 47static const char *const lmpats_out[] = { 48 "UNDEFINED", 49 "EXACT", 50 "ONELEVEL", 51 "SUBTREE", 52 "CHILDREN", 53 "REGEX", 54 "ANONYMOUS", 55 "USERS", 56 "ANY" 57}; 58 59static const char * 60limits2str( unsigned i ) 61{ 62 return i < (sizeof( lmpats_out ) / sizeof( lmpats_out[0] )) 63 ? lmpats_out[i] : "UNKNOWN"; 64} 65#endif /* LDAP_DEBUG */ 66 67static int 68limits_get( 69 Operation *op, 70 struct slap_limits_set **limit 71) 72{ 73 static struct berval empty_dn = BER_BVC( "" ); 74 struct slap_limits **lm; 75 struct berval *ndns[2]; 76 77 assert( op != NULL ); 78 assert( limit != NULL ); 79 80 ndns[0] = &op->o_ndn; 81 ndns[1] = &op->o_req_ndn; 82 83 Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s self=\"%s\" this=\"%s\"\n", 84 op->o_log_prefix, 85 BER_BVISNULL( ndns[0] ) ? "[anonymous]" : ndns[0]->bv_val, 86 BER_BVISNULL( ndns[1] ) ? "" : ndns[1]->bv_val ); 87 /* 88 * default values 89 */ 90 *limit = &op->o_bd->be_def_limit; 91 92 if ( op->o_bd->be_limits == NULL ) { 93 return( 0 ); 94 } 95 96 for ( lm = op->o_bd->be_limits; lm[0] != NULL; lm++ ) { 97 unsigned style = lm[0]->lm_flags & SLAP_LIMITS_MASK; 98 unsigned type = lm[0]->lm_flags & SLAP_LIMITS_TYPE_MASK; 99 unsigned isthis = type == SLAP_LIMITS_TYPE_THIS; 100 struct berval *ndn = ndns[isthis]; 101 102 if ( style == SLAP_LIMITS_ANY ) 103 goto found_any; 104 105 if ( BER_BVISEMPTY( ndn ) ) { 106 if ( style == SLAP_LIMITS_ANONYMOUS ) 107 goto found_nodn; 108 if ( !isthis ) 109 continue; 110 ndn = &empty_dn; 111 } 112 113 switch ( style ) { 114 case SLAP_LIMITS_EXACT: 115 if ( type == SLAP_LIMITS_TYPE_GROUP ) { 116 int rc = backend_group( op, NULL, 117 &lm[0]->lm_pat, ndn, 118 lm[0]->lm_group_oc, 119 lm[0]->lm_group_ad ); 120 if ( rc == 0 ) { 121 goto found_group; 122 } 123 } else { 124 if ( dn_match( &lm[0]->lm_pat, ndn ) ) { 125 goto found_dn; 126 } 127 } 128 break; 129 130 case SLAP_LIMITS_ONE: 131 case SLAP_LIMITS_SUBTREE: 132 case SLAP_LIMITS_CHILDREN: { 133 ber_len_t d; 134 135 /* ndn shorter than lm_pat */ 136 if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) { 137 break; 138 } 139 d = ndn->bv_len - lm[0]->lm_pat.bv_len; 140 141 if ( d == 0 ) { 142 /* allow exact match for SUBTREE only */ 143 if ( style != SLAP_LIMITS_SUBTREE ) { 144 break; 145 } 146 } else { 147 /* check for unescaped rdn separator */ 148 if ( !DN_SEPARATOR( ndn->bv_val[d - 1] ) ) { 149 break; 150 } 151 } 152 153 /* check that ndn ends with lm_pat */ 154 if ( strcmp( lm[0]->lm_pat.bv_val, &ndn->bv_val[d] ) != 0 ) { 155 break; 156 } 157 158 /* in case of ONE, require exactly one rdn below lm_pat */ 159 if ( style == SLAP_LIMITS_ONE ) { 160 if ( dn_rdnlen( NULL, ndn ) != d - 1 ) { 161 break; 162 } 163 } 164 165 goto found_dn; 166 } 167 168 case SLAP_LIMITS_REGEX: 169 if ( regexec( &lm[0]->lm_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) { 170 goto found_dn; 171 } 172 break; 173 174 case SLAP_LIMITS_ANONYMOUS: 175 break; 176 177 case SLAP_LIMITS_USERS: 178 found_nodn: 179 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n", 180 dn_source[isthis], limits2str( style ), 0 ); 181 found_any: 182 *limit = &lm[0]->lm_limits; 183 return( 0 ); 184 185 found_dn: 186 Debug( LDAP_DEBUG_TRACE, 187 "<== limits_get: type=%s match=%s dn=\"%s\"\n", 188 dn_source[isthis], limits2str( style ), lm[0]->lm_pat.bv_val ); 189 *limit = &lm[0]->lm_limits; 190 return( 0 ); 191 192 found_group: 193 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=GROUP match=EXACT " 194 "dn=\"%s\" oc=\"%s\" ad=\"%s\"\n", 195 lm[0]->lm_pat.bv_val, 196 lm[0]->lm_group_oc->soc_cname.bv_val, 197 lm[0]->lm_group_ad->ad_cname.bv_val ); 198 *limit = &lm[0]->lm_limits; 199 return( 0 ); 200 201 default: 202 assert( 0 ); /* unreachable */ 203 return( -1 ); 204 } 205 } 206 207 return( 0 ); 208} 209 210static int 211limits_add( 212 Backend *be, 213 unsigned flags, 214 const char *pattern, 215 ObjectClass *group_oc, 216 AttributeDescription *group_ad, 217 struct slap_limits_set *limit 218) 219{ 220 int i; 221 struct slap_limits *lm; 222 unsigned type, style; 223 224 assert( be != NULL ); 225 assert( limit != NULL ); 226 227 type = flags & SLAP_LIMITS_TYPE_MASK; 228 style = flags & SLAP_LIMITS_MASK; 229 230 switch ( style ) { 231 case SLAP_LIMITS_ANONYMOUS: 232 case SLAP_LIMITS_USERS: 233 case SLAP_LIMITS_ANY: 234 /* For these styles, type == 0 (SLAP_LIMITS_TYPE_SELF). */ 235 for ( i = 0; be->be_limits && be->be_limits[ i ]; i++ ) { 236 if ( be->be_limits[ i ]->lm_flags == style ) { 237 return( -1 ); 238 } 239 } 240 break; 241 } 242 243 244 lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 ); 245 246 switch ( style ) { 247 case SLAP_LIMITS_UNDEFINED: 248 style = SLAP_LIMITS_EXACT; 249 /* continue to next cases */ 250 case SLAP_LIMITS_EXACT: 251 case SLAP_LIMITS_ONE: 252 case SLAP_LIMITS_SUBTREE: 253 case SLAP_LIMITS_CHILDREN: 254 { 255 int rc; 256 struct berval bv; 257 258 ber_str2bv( pattern, 0, 0, &bv ); 259 260 rc = dnNormalize( 0, NULL, NULL, &bv, &lm->lm_pat, NULL ); 261 if ( rc != LDAP_SUCCESS ) { 262 ch_free( lm ); 263 return( -1 ); 264 } 265 } 266 break; 267 268 case SLAP_LIMITS_REGEX: 269 ber_str2bv( pattern, 0, 1, &lm->lm_pat ); 270 if ( regcomp( &lm->lm_regex, lm->lm_pat.bv_val, 271 REG_EXTENDED | REG_ICASE ) ) { 272 free( lm->lm_pat.bv_val ); 273 ch_free( lm ); 274 return( -1 ); 275 } 276 break; 277 278 case SLAP_LIMITS_ANONYMOUS: 279 case SLAP_LIMITS_USERS: 280 case SLAP_LIMITS_ANY: 281 BER_BVZERO( &lm->lm_pat ); 282 break; 283 } 284 285 switch ( type ) { 286 case SLAP_LIMITS_TYPE_GROUP: 287 assert( group_oc != NULL ); 288 assert( group_ad != NULL ); 289 lm->lm_group_oc = group_oc; 290 lm->lm_group_ad = group_ad; 291 break; 292 } 293 294 lm->lm_flags = style | type; 295 lm->lm_limits = *limit; 296 297 i = 0; 298 if ( be->be_limits != NULL ) { 299 for ( ; be->be_limits[i]; i++ ); 300 } 301 302 be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits, 303 sizeof( struct slap_limits * ) * ( i + 2 ) ); 304 be->be_limits[i] = lm; 305 be->be_limits[i+1] = NULL; 306 307 return( 0 ); 308} 309 310#define STRSTART( s, m ) (strncasecmp( s, m, STRLENOF( "" m "" )) == 0) 311 312int 313limits_parse( 314 Backend *be, 315 const char *fname, 316 int lineno, 317 int argc, 318 char **argv 319) 320{ 321 int flags = SLAP_LIMITS_UNDEFINED; 322 char *pattern; 323 struct slap_limits_set limit; 324 int i, rc = 0; 325 ObjectClass *group_oc = NULL; 326 AttributeDescription *group_ad = NULL; 327 328 assert( be != NULL ); 329 330 if ( argc < 3 ) { 331 Debug( LDAP_DEBUG_ANY, 332 "%s : line %d: missing arg(s) in " 333 "\"limits <pattern> <limits>\" line.\n%s", 334 fname, lineno, "" ); 335 return( -1 ); 336 } 337 338 limit = be->be_def_limit; 339 340 /* 341 * syntax: 342 * 343 * "limits" <pattern> <limit> [ ... ] 344 * 345 * 346 * <pattern>: 347 * 348 * "anonymous" 349 * "users" 350 * [ "dn" [ "." { "this" | "self" } ] [ "." { "exact" | "base" | 351 * "onelevel" | "subtree" | "children" | "regex" | "anonymous" } ] 352 * "=" ] <dn pattern> 353 * 354 * Note: 355 * "this" is the baseobject, "self" (the default) is the bound DN 356 * "exact" and "base" are the same (exact match); 357 * "onelevel" means exactly one rdn below, NOT including pattern 358 * "subtree" means any rdn below, including pattern 359 * "children" means any rdn below, NOT including pattern 360 * 361 * "anonymous" may be deprecated in favour 362 * of the pattern = "anonymous" form 363 * 364 * "group[/objectClass[/attributeType]]" "=" "<dn pattern>" 365 * 366 * <limit>: 367 * 368 * "time" [ "." { "soft" | "hard" } ] "=" <integer> 369 * 370 * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer> 371 */ 372 373 pattern = argv[1]; 374 if ( strcmp( pattern, "*" ) == 0) { 375 flags = SLAP_LIMITS_ANY; 376 377 } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) { 378 flags = SLAP_LIMITS_ANONYMOUS; 379 380 } else if ( strcasecmp( pattern, "users" ) == 0 ) { 381 flags = SLAP_LIMITS_USERS; 382 383 } else if ( STRSTART( pattern, "dn" ) ) { 384 pattern += STRLENOF( "dn" ); 385 flags = SLAP_LIMITS_TYPE_SELF; 386 if ( pattern[0] == '.' ) { 387 pattern++; 388 if ( STRSTART( pattern, "this" ) ) { 389 flags = SLAP_LIMITS_TYPE_THIS; 390 pattern += STRLENOF( "this" ); 391 } else if ( STRSTART( pattern, "self" ) ) { 392 pattern += STRLENOF( "self" ); 393 } else { 394 goto got_dn_dot; 395 } 396 } 397 if ( pattern[0] == '.' ) { 398 pattern++; 399 got_dn_dot: 400 if ( STRSTART( pattern, "exact" ) ) { 401 flags |= SLAP_LIMITS_EXACT; 402 pattern += STRLENOF( "exact" ); 403 404 } else if ( STRSTART( pattern, "base" ) ) { 405 flags |= SLAP_LIMITS_BASE; 406 pattern += STRLENOF( "base" ); 407 408 } else if ( STRSTART( pattern, "one" ) ) { 409 flags |= SLAP_LIMITS_ONE; 410 pattern += STRLENOF( "one" ); 411 if ( STRSTART( pattern, "level" ) ) { 412 pattern += STRLENOF( "level" ); 413 414 } else { 415 Debug( LDAP_DEBUG_ANY, 416 "%s : line %d: deprecated \"one\" style " 417 "\"limits <pattern> <limits>\" line; " 418 "use \"onelevel\" instead.\n", fname, lineno, 0 ); 419 } 420 421 } else if ( STRSTART( pattern, "sub" ) ) { 422 flags |= SLAP_LIMITS_SUBTREE; 423 pattern += STRLENOF( "sub" ); 424 if ( STRSTART( pattern, "tree" ) ) { 425 pattern += STRLENOF( "tree" ); 426 427 } else { 428 Debug( LDAP_DEBUG_ANY, 429 "%s : line %d: deprecated \"sub\" style " 430 "\"limits <pattern> <limits>\" line; " 431 "use \"subtree\" instead.\n", fname, lineno, 0 ); 432 } 433 434 } else if ( STRSTART( pattern, "children" ) ) { 435 flags |= SLAP_LIMITS_CHILDREN; 436 pattern += STRLENOF( "children" ); 437 438 } else if ( STRSTART( pattern, "regex" ) ) { 439 flags |= SLAP_LIMITS_REGEX; 440 pattern += STRLENOF( "regex" ); 441 442 /* 443 * this could be deprecated in favour 444 * of the pattern = "anonymous" form 445 */ 446 } else if ( STRSTART( pattern, "anonymous" ) 447 && flags == SLAP_LIMITS_TYPE_SELF ) 448 { 449 flags = SLAP_LIMITS_ANONYMOUS; 450 pattern = NULL; 451 452 } else { 453 /* force error below */ 454 if ( *pattern == '=' ) 455 --pattern; 456 } 457 } 458 459 /* pre-check the data */ 460 if ( pattern != NULL ) { 461 if ( pattern[0] != '=' ) { 462 Debug( LDAP_DEBUG_ANY, 463 "%s : line %d: %s in " 464 "\"dn[.{this|self}][.{exact|base" 465 "|onelevel|subtree|children|regex" 466 "|anonymous}]=<pattern>\" in " 467 "\"limits <pattern> <limits>\" line.\n", 468 fname, lineno, 469 isalnum( (unsigned char)pattern[0] ) 470 ? "unknown DN modifier" : "missing '='" ); 471 return( -1 ); 472 } 473 474 /* skip '=' (required) */ 475 pattern++; 476 477 /* trim obvious cases */ 478 if ( strcmp( pattern, "*" ) == 0 ) { 479 flags = SLAP_LIMITS_ANY; 480 pattern = NULL; 481 482 } else if ( (flags & SLAP_LIMITS_MASK) == SLAP_LIMITS_REGEX 483 && strcmp( pattern, ".*" ) == 0 ) { 484 flags = SLAP_LIMITS_ANY; 485 pattern = NULL; 486 } 487 } 488 489 } else if (STRSTART( pattern, "group" ) ) { 490 pattern += STRLENOF( "group" ); 491 492 if ( pattern[0] == '/' ) { 493 struct berval oc, ad; 494 495 oc.bv_val = pattern + 1; 496 pattern = strchr( pattern, '=' ); 497 if ( pattern == NULL ) { 498 return -1; 499 } 500 501 ad.bv_val = strchr( oc.bv_val, '/' ); 502 if ( ad.bv_val != NULL ) { 503 const char *text = NULL; 504 505 oc.bv_len = ad.bv_val - oc.bv_val; 506 507 ad.bv_val++; 508 ad.bv_len = pattern - ad.bv_val; 509 rc = slap_bv2ad( &ad, &group_ad, &text ); 510 if ( rc != LDAP_SUCCESS ) { 511 goto no_ad; 512 } 513 514 } else { 515 oc.bv_len = pattern - oc.bv_val; 516 } 517 518 group_oc = oc_bvfind( &oc ); 519 if ( group_oc == NULL ) { 520 goto no_oc; 521 } 522 } 523 524 if ( group_oc == NULL ) { 525 group_oc = oc_find( SLAPD_GROUP_CLASS ); 526 if ( group_oc == NULL ) { 527no_oc:; 528 return( -1 ); 529 } 530 } 531 532 if ( group_ad == NULL ) { 533 const char *text = NULL; 534 535 rc = slap_str2ad( SLAPD_GROUP_ATTR, &group_ad, &text ); 536 537 if ( rc != LDAP_SUCCESS ) { 538no_ad:; 539 return( -1 ); 540 } 541 } 542 543 flags = SLAP_LIMITS_TYPE_GROUP | SLAP_LIMITS_EXACT; 544 545 if ( pattern[0] != '=' ) { 546 Debug( LDAP_DEBUG_ANY, 547 "%s : line %d: missing '=' in " 548 "\"group[/objectClass[/attributeType]]" 549 "=<pattern>\" in " 550 "\"limits <pattern> <limits>\" line.\n", 551 fname, lineno, 0 ); 552 return( -1 ); 553 } 554 555 /* skip '=' (required) */ 556 pattern++; 557 } 558 559 /* get the limits */ 560 for ( i = 2; i < argc; i++ ) { 561 if ( limits_parse_one( argv[i], &limit ) ) { 562 563 Debug( LDAP_DEBUG_ANY, 564 "%s : line %d: unknown limit values \"%s\" in " 565 "\"limits <pattern> <limits>\" line.\n", 566 fname, lineno, argv[i] ); 567 568 return( 1 ); 569 } 570 } 571 572 /* 573 * sanity checks ... 574 * 575 * FIXME: add warnings? 576 */ 577 if ( limit.lms_t_hard > 0 && 578 ( limit.lms_t_hard < limit.lms_t_soft 579 || limit.lms_t_soft == -1 ) ) { 580 limit.lms_t_hard = limit.lms_t_soft; 581 } 582 583 if ( limit.lms_s_hard > 0 && 584 ( limit.lms_s_hard < limit.lms_s_soft 585 || limit.lms_s_soft == -1 ) ) { 586 limit.lms_s_hard = limit.lms_s_soft; 587 } 588 589 /* 590 * defaults ... 591 * 592 * lms_t_hard: 593 * -1 => no limits 594 * 0 => same as soft 595 * > 0 => limit (in seconds) 596 * 597 * lms_s_hard: 598 * -1 => no limits 599 * 0 0> same as soft 600 * > 0 => limit (in entries) 601 * 602 * lms_s_pr_total: 603 * -2 => disable the control 604 * -1 => no limits 605 * 0 => same as soft 606 * > 0 => limit (in entries) 607 * 608 * lms_s_pr: 609 * -1 => no limits 610 * 0 => no limits? 611 * > 0 => limit size (in entries) 612 */ 613 if ( limit.lms_s_pr_total > 0 && 614 limit.lms_s_pr > limit.lms_s_pr_total ) { 615 limit.lms_s_pr = limit.lms_s_pr_total; 616 } 617 618 rc = limits_add( be, flags, pattern, group_oc, group_ad, &limit ); 619 if ( rc ) { 620 621 Debug( LDAP_DEBUG_ANY, 622 "%s : line %d: unable to add limit in " 623 "\"limits <pattern> <limits>\" line.\n", 624 fname, lineno, 0 ); 625 } 626 627 return( rc ); 628} 629 630int 631limits_parse_one( 632 const char *arg, 633 struct slap_limits_set *limit 634) 635{ 636 assert( arg != NULL ); 637 assert( limit != NULL ); 638 639 if ( STRSTART( arg, "time" ) ) { 640 arg += STRLENOF( "time" ); 641 642 if ( arg[0] == '.' ) { 643 arg++; 644 if ( STRSTART( arg, "soft=" ) ) { 645 arg += STRLENOF( "soft=" ); 646 if ( strcasecmp( arg, "unlimited" ) == 0 647 || strcasecmp( arg, "none" ) == 0 ) 648 { 649 limit->lms_t_soft = -1; 650 651 } else { 652 int soft; 653 654 if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) { 655 return( 1 ); 656 } 657 658 if ( soft == -1 ) { 659 /* FIXME: use "unlimited" instead; issue warning? */ 660 } 661 662 limit->lms_t_soft = soft; 663 } 664 665 } else if ( STRSTART( arg, "hard=" ) ) { 666 arg += STRLENOF( "hard=" ); 667 if ( strcasecmp( arg, "soft" ) == 0 ) { 668 limit->lms_t_hard = 0; 669 670 } else if ( strcasecmp( arg, "unlimited" ) == 0 671 || strcasecmp( arg, "none" ) == 0 ) 672 { 673 limit->lms_t_hard = -1; 674 675 } else { 676 int hard; 677 678 if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) { 679 return( 1 ); 680 } 681 682 if ( hard == -1 ) { 683 /* FIXME: use "unlimited" instead */ 684 } 685 686 if ( hard == 0 ) { 687 /* FIXME: use "soft" instead */ 688 } 689 690 limit->lms_t_hard = hard; 691 } 692 693 } else { 694 return( 1 ); 695 } 696 697 } else if ( arg[0] == '=' ) { 698 arg++; 699 if ( strcasecmp( arg, "unlimited" ) == 0 700 || strcasecmp( arg, "none" ) == 0 ) 701 { 702 limit->lms_t_soft = -1; 703 704 } else { 705 if ( lutil_atoi( &limit->lms_t_soft, arg ) != 0 706 || limit->lms_t_soft < -1 ) 707 { 708 return( 1 ); 709 } 710 } 711 limit->lms_t_hard = 0; 712 713 } else { 714 return( 1 ); 715 } 716 717 } else if ( STRSTART( arg, "size" ) ) { 718 arg += STRLENOF( "size" ); 719 720 if ( arg[0] == '.' ) { 721 arg++; 722 if ( STRSTART( arg, "soft=" ) ) { 723 arg += STRLENOF( "soft=" ); 724 if ( strcasecmp( arg, "unlimited" ) == 0 725 || strcasecmp( arg, "none" ) == 0 ) 726 { 727 limit->lms_s_soft = -1; 728 729 } else { 730 int soft; 731 732 if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) { 733 return( 1 ); 734 } 735 736 if ( soft == -1 ) { 737 /* FIXME: use "unlimited" instead */ 738 } 739 740 limit->lms_s_soft = soft; 741 } 742 743 } else if ( STRSTART( arg, "hard=" ) ) { 744 arg += STRLENOF( "hard=" ); 745 if ( strcasecmp( arg, "soft" ) == 0 ) { 746 limit->lms_s_hard = 0; 747 748 } else if ( strcasecmp( arg, "unlimited" ) == 0 749 || strcasecmp( arg, "none" ) == 0 ) 750 { 751 limit->lms_s_hard = -1; 752 753 } else { 754 int hard; 755 756 if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) { 757 return( 1 ); 758 } 759 760 if ( hard == -1 ) { 761 /* FIXME: use "unlimited" instead */ 762 } 763 764 if ( hard == 0 ) { 765 /* FIXME: use "soft" instead */ 766 } 767 768 limit->lms_s_hard = hard; 769 } 770 771 } else if ( STRSTART( arg, "unchecked=" ) ) { 772 arg += STRLENOF( "unchecked=" ); 773 if ( strcasecmp( arg, "unlimited" ) == 0 774 || strcasecmp( arg, "none" ) == 0 ) 775 { 776 limit->lms_s_unchecked = -1; 777 778 } else if ( strcasecmp( arg, "disabled" ) == 0 ) { 779 limit->lms_s_unchecked = 0; 780 781 } else { 782 int unchecked; 783 784 if ( lutil_atoi( &unchecked, arg ) != 0 || unchecked < -1 ) { 785 return( 1 ); 786 } 787 788 if ( unchecked == -1 ) { 789 /* FIXME: use "unlimited" instead */ 790 } 791 792 limit->lms_s_unchecked = unchecked; 793 } 794 795 } else if ( STRSTART( arg, "pr=" ) ) { 796 arg += STRLENOF( "pr=" ); 797 if ( strcasecmp( arg, "noEstimate" ) == 0 ) { 798 limit->lms_s_pr_hide = 1; 799 800 } else if ( strcasecmp( arg, "unlimited" ) == 0 801 || strcasecmp( arg, "none" ) == 0 ) 802 { 803 limit->lms_s_pr = -1; 804 805 } else { 806 int pr; 807 808 if ( lutil_atoi( &pr, arg ) != 0 || pr < -1 ) { 809 return( 1 ); 810 } 811 812 if ( pr == -1 ) { 813 /* FIXME: use "unlimited" instead */ 814 } 815 816 limit->lms_s_pr = pr; 817 } 818 819 } else if ( STRSTART( arg, "prtotal=" ) ) { 820 arg += STRLENOF( "prtotal=" ); 821 822 if ( strcasecmp( arg, "unlimited" ) == 0 823 || strcasecmp( arg, "none" ) == 0 ) 824 { 825 limit->lms_s_pr_total = -1; 826 827 } else if ( strcasecmp( arg, "disabled" ) == 0 ) { 828 limit->lms_s_pr_total = -2; 829 830 } else if ( strcasecmp( arg, "hard" ) == 0 ) { 831 limit->lms_s_pr_total = 0; 832 833 } else { 834 int total; 835 836 if ( lutil_atoi( &total, arg ) != 0 || total < -1 ) { 837 return( 1 ); 838 } 839 840 if ( total == -1 ) { 841 /* FIXME: use "unlimited" instead */ 842 } 843 844 if ( total == 0 ) { 845 /* FIXME: use "pr=disable" instead */ 846 } 847 848 limit->lms_s_pr_total = total; 849 } 850 851 } else { 852 return( 1 ); 853 } 854 855 } else if ( arg[0] == '=' ) { 856 arg++; 857 if ( strcasecmp( arg, "unlimited" ) == 0 858 || strcasecmp( arg, "none" ) == 0 ) 859 { 860 limit->lms_s_soft = -1; 861 862 } else { 863 if ( lutil_atoi( &limit->lms_s_soft, arg ) != 0 864 || limit->lms_s_soft < -1 ) 865 { 866 return( 1 ); 867 } 868 } 869 limit->lms_s_hard = 0; 870 871 } else { 872 return( 1 ); 873 } 874 } 875 876 return 0; 877} 878 879/* Helper macros for limits_unparse() and limits_unparse_one(): 880 * Write to ptr, but not past bufEnd. Move ptr past the new text. 881 * Return (success && enough room ? 0 : -1). 882 */ 883#define ptr_APPEND_BV(bv) /* Append a \0-terminated berval */ \ 884 (WHATSLEFT <= (bv).bv_len ? -1 : \ 885 ((void) (ptr = lutil_strcopy( ptr, (bv).bv_val )), 0)) 886#define ptr_APPEND_LIT(str) /* Append a string literal */ \ 887 (WHATSLEFT <= STRLENOF( "" str "" ) ? -1 : \ 888 ((void) (ptr = lutil_strcopy( ptr, str )), 0)) 889#define ptr_APPEND_FMT(args) /* Append formatted text */ \ 890 (WHATSLEFT <= (tmpLen = snprintf args) ? -1 : ((void) (ptr += tmpLen), 0)) 891#define ptr_APPEND_FMT1(fmt, arg) ptr_APPEND_FMT(( ptr, WHATSLEFT, fmt, arg )) 892#define WHATSLEFT ((ber_len_t) (bufEnd - ptr)) 893 894/* Caller must provide an adequately sized buffer in bv */ 895int 896limits_unparse( struct slap_limits *lim, struct berval *bv, ber_len_t buflen ) 897{ 898 struct berval btmp; 899 char *ptr, *bufEnd; /* Updated/used by ptr_APPEND_*()/WHATSLEFT */ 900 ber_len_t tmpLen; /* Used by ptr_APPEND_FMT*() */ 901 unsigned type, style; 902 int rc = 0; 903 904 if ( !bv || !bv->bv_val ) return -1; 905 906 ptr = bv->bv_val; 907 bufEnd = ptr + buflen; 908 type = lim->lm_flags & SLAP_LIMITS_TYPE_MASK; 909 910 if ( type == SLAP_LIMITS_TYPE_GROUP ) { 911 rc = ptr_APPEND_FMT(( ptr, WHATSLEFT, "group/%s/%s=\"%s\"", 912 lim->lm_group_oc->soc_cname.bv_val, 913 lim->lm_group_ad->ad_cname.bv_val, 914 lim->lm_pat.bv_val )); 915 } else { 916 style = lim->lm_flags & SLAP_LIMITS_MASK; 917 switch( style ) { 918 case SLAP_LIMITS_ANONYMOUS: 919 case SLAP_LIMITS_USERS: 920 case SLAP_LIMITS_ANY: 921 rc = ptr_APPEND_BV( lmpats[style] ); 922 break; 923 case SLAP_LIMITS_UNDEFINED: 924 case SLAP_LIMITS_EXACT: 925 case SLAP_LIMITS_ONE: 926 case SLAP_LIMITS_SUBTREE: 927 case SLAP_LIMITS_CHILDREN: 928 case SLAP_LIMITS_REGEX: 929 rc = ptr_APPEND_FMT(( ptr, WHATSLEFT, "dn.%s%s=\"%s\"", 930 type == SLAP_LIMITS_TYPE_SELF ? "" : "this.", 931 lmpats[style].bv_val, lim->lm_pat.bv_val )); 932 break; 933 } 934 } 935 if ( rc == 0 ) { 936 bv->bv_len = ptr - bv->bv_val; 937 btmp.bv_val = ptr; 938 btmp.bv_len = 0; 939 rc = limits_unparse_one( &lim->lm_limits, 940 SLAP_LIMIT_SIZE | SLAP_LIMIT_TIME, 941 &btmp, WHATSLEFT ); 942 if ( rc == 0 ) 943 bv->bv_len += btmp.bv_len; 944 } 945 return rc; 946} 947 948/* Caller must provide an adequately sized buffer in bv */ 949int 950limits_unparse_one( 951 struct slap_limits_set *lim, 952 int which, 953 struct berval *bv, 954 ber_len_t buflen ) 955{ 956 char *ptr, *bufEnd; /* Updated/used by ptr_APPEND_*()/WHATSLEFT */ 957 ber_len_t tmpLen; /* Used by ptr_APPEND_FMT*() */ 958 959 if ( !bv || !bv->bv_val ) return -1; 960 961 ptr = bv->bv_val; 962 bufEnd = ptr + buflen; 963 964 if ( which & SLAP_LIMIT_SIZE ) { 965 if ( lim->lms_s_soft != SLAPD_DEFAULT_SIZELIMIT ) { 966 967 /* If same as global limit, drop it */ 968 if ( lim != &frontendDB->be_def_limit && 969 lim->lms_s_soft == frontendDB->be_def_limit.lms_s_soft ) 970 { 971 goto s_hard; 972 /* If there's also a hard limit, fully qualify this one */ 973 } else if ( lim->lms_s_hard ) { 974 if ( ptr_APPEND_LIT( " size.soft=" ) ) return -1; 975 976 /* If doing both size & time, qualify this */ 977 } else if ( which & SLAP_LIMIT_TIME ) { 978 if ( ptr_APPEND_LIT( " size=" ) ) return -1; 979 } 980 981 if ( lim->lms_s_soft == -1 982 ? ptr_APPEND_LIT( "unlimited " ) 983 : ptr_APPEND_FMT1( "%d ", lim->lms_s_soft ) ) 984 return -1; 985 } 986s_hard: 987 if ( lim->lms_s_hard ) { 988 if ( ptr_APPEND_LIT( " size.hard=" ) ) return -1; 989 if ( lim->lms_s_hard == -1 990 ? ptr_APPEND_LIT( "unlimited " ) 991 : ptr_APPEND_FMT1( "%d ", lim->lms_s_hard ) ) 992 return -1; 993 } 994 if ( lim->lms_s_unchecked != -1 ) { 995 if ( ptr_APPEND_LIT( " size.unchecked=" ) ) return -1; 996 if ( lim->lms_s_unchecked == 0 997 ? ptr_APPEND_LIT( "disabled " ) 998 : ptr_APPEND_FMT1( "%d ", lim->lms_s_unchecked ) ) 999 return -1; 1000 } 1001 if ( lim->lms_s_pr_hide ) { 1002 if ( ptr_APPEND_LIT( " size.pr=noEstimate " ) ) return -1; 1003 } 1004 if ( lim->lms_s_pr ) { 1005 if ( ptr_APPEND_LIT( " size.pr=" ) ) return -1; 1006 if ( lim->lms_s_pr == -1 1007 ? ptr_APPEND_LIT( "unlimited " ) 1008 : ptr_APPEND_FMT1( "%d ", lim->lms_s_pr ) ) 1009 return -1; 1010 } 1011 if ( lim->lms_s_pr_total ) { 1012 if ( ptr_APPEND_LIT( " size.prtotal=" ) ) return -1; 1013 if ( lim->lms_s_pr_total == -1 ? ptr_APPEND_LIT( "unlimited " ) 1014 : lim->lms_s_pr_total == -2 ? ptr_APPEND_LIT( "disabled " ) 1015 : ptr_APPEND_FMT1( "%d ", lim->lms_s_pr_total ) ) 1016 return -1; 1017 } 1018 } 1019 1020 if ( which & SLAP_LIMIT_TIME ) { 1021 if ( lim->lms_t_soft != SLAPD_DEFAULT_TIMELIMIT ) { 1022 1023 /* If same as global limit, drop it */ 1024 if ( lim != &frontendDB->be_def_limit && 1025 lim->lms_t_soft == frontendDB->be_def_limit.lms_t_soft ) 1026 { 1027 goto t_hard; 1028 1029 /* If there's also a hard limit, fully qualify this one */ 1030 } else if ( lim->lms_t_hard ) { 1031 if ( ptr_APPEND_LIT( " time.soft=" ) ) return -1; 1032 1033 /* If doing both size & time, qualify this */ 1034 } else if ( which & SLAP_LIMIT_SIZE ) { 1035 if ( ptr_APPEND_LIT( " time=" ) ) return -1; 1036 } 1037 1038 if ( lim->lms_t_soft == -1 1039 ? ptr_APPEND_LIT( "unlimited " ) 1040 : ptr_APPEND_FMT1( "%d ", lim->lms_t_soft ) ) 1041 return -1; 1042 } 1043t_hard: 1044 if ( lim->lms_t_hard ) { 1045 if ( ptr_APPEND_LIT( " time.hard=" ) ) return -1; 1046 if ( lim->lms_t_hard == -1 1047 ? ptr_APPEND_LIT( "unlimited " ) 1048 : ptr_APPEND_FMT1( "%d ", lim->lms_t_hard ) ) 1049 return -1; 1050 } 1051 } 1052 if ( ptr != bv->bv_val ) { 1053 ptr--; 1054 *ptr = '\0'; 1055 bv->bv_len = ptr - bv->bv_val; 1056 } 1057 1058 return 0; 1059} 1060 1061int 1062limits_check( Operation *op, SlapReply *rs ) 1063{ 1064 assert( op != NULL ); 1065 assert( rs != NULL ); 1066 /* FIXME: should this be always true? */ 1067 assert( op->o_tag == LDAP_REQ_SEARCH); 1068 1069 /* protocol only allows 0..maxInt; 1070 * 1071 * internal searches: 1072 * - may use SLAP_NO_LIMIT ( = -1 ) to indicate no limits; 1073 * - should use slimit = N and tlimit = SLAP_NO_LIMIT to 1074 * indicate searches that should return exactly N matches, 1075 * and handle errors thru a callback (see for instance 1076 * slap_sasl_match() and slap_sasl2dn()) 1077 */ 1078 if ( op->ors_tlimit == SLAP_NO_LIMIT && op->ors_slimit == SLAP_NO_LIMIT ) { 1079 return 0; 1080 } 1081 1082 /* allow root to set no limit */ 1083 if ( be_isroot( op ) ) { 1084 op->ors_limit = NULL; 1085 1086 if ( op->ors_tlimit == 0 ) { 1087 op->ors_tlimit = SLAP_NO_LIMIT; 1088 } 1089 1090 if ( op->ors_slimit == 0 ) { 1091 op->ors_slimit = SLAP_NO_LIMIT; 1092 } 1093 1094 /* if paged results and slimit are requested */ 1095 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED && 1096 op->ors_slimit != SLAP_NO_LIMIT ) { 1097 PagedResultsState *ps = op->o_pagedresults_state; 1098 int total = op->ors_slimit - ps->ps_count; 1099 if ( total > 0 ) { 1100 op->ors_slimit = total; 1101 } else { 1102 op->ors_slimit = 0; 1103 } 1104 } 1105 1106 /* if not root, get appropriate limits */ 1107 } else { 1108 ( void ) limits_get( op, &op->ors_limit ); 1109 1110 assert( op->ors_limit != NULL ); 1111 1112 /* if no limit is required, use soft limit */ 1113 if ( op->ors_tlimit == 0 ) { 1114 op->ors_tlimit = op->ors_limit->lms_t_soft; 1115 1116 /* limit required: check if legal */ 1117 } else { 1118 if ( op->ors_limit->lms_t_hard == 0 ) { 1119 if ( op->ors_limit->lms_t_soft > 0 1120 && ( op->ors_tlimit > op->ors_limit->lms_t_soft ) ) { 1121 op->ors_tlimit = op->ors_limit->lms_t_soft; 1122 } 1123 1124 } else if ( op->ors_limit->lms_t_hard > 0 ) { 1125#ifdef ABOVE_HARD_LIMIT_IS_ERROR 1126 if ( op->ors_tlimit == SLAP_MAX_LIMIT ) { 1127 op->ors_tlimit = op->ors_limit->lms_t_hard; 1128 1129 } else if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) { 1130 /* error if exceeding hard limit */ 1131 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1132 send_ldap_result( op, rs ); 1133 rs->sr_err = LDAP_SUCCESS; 1134 return -1; 1135 } 1136#else /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1137 if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) { 1138 op->ors_tlimit = op->ors_limit->lms_t_hard; 1139 } 1140#endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1141 } 1142 } 1143 1144 /* else leave as is */ 1145 1146 /* don't even get to backend if candidate check is disabled */ 1147 if ( op->ors_limit->lms_s_unchecked == 0 ) { 1148 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1149 send_ldap_result( op, rs ); 1150 rs->sr_err = LDAP_SUCCESS; 1151 return -1; 1152 } 1153 1154 /* if paged results is requested */ 1155 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) { 1156 int slimit = -2; 1157 int pr_total; 1158 PagedResultsState *ps = op->o_pagedresults_state; 1159 1160 /* paged results is not allowed */ 1161 if ( op->ors_limit->lms_s_pr_total == -2 ) { 1162 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1163 rs->sr_text = "pagedResults control not allowed"; 1164 send_ldap_result( op, rs ); 1165 rs->sr_err = LDAP_SUCCESS; 1166 rs->sr_text = NULL; 1167 return -1; 1168 } 1169 1170 if ( op->ors_limit->lms_s_pr > 0 1171 && ps->ps_size > op->ors_limit->lms_s_pr ) 1172 { 1173 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1174 rs->sr_text = "illegal pagedResults page size"; 1175 send_ldap_result( op, rs ); 1176 rs->sr_err = LDAP_SUCCESS; 1177 rs->sr_text = NULL; 1178 return -1; 1179 } 1180 1181 if ( op->ors_limit->lms_s_pr_total == 0 ) { 1182 if ( op->ors_limit->lms_s_hard == 0 ) { 1183 pr_total = op->ors_limit->lms_s_soft; 1184 } else { 1185 pr_total = op->ors_limit->lms_s_hard; 1186 } 1187 } else { 1188 pr_total = op->ors_limit->lms_s_pr_total; 1189 } 1190 1191 if ( pr_total == -1 ) { 1192 if ( op->ors_slimit == 0 || op->ors_slimit == SLAP_MAX_LIMIT ) { 1193 slimit = -1; 1194 1195 } else { 1196 slimit = op->ors_slimit - ps->ps_count; 1197 } 1198 1199#ifdef ABOVE_HARD_LIMIT_IS_ERROR 1200 } else if ( pr_total > 0 && op->ors_slimit != SLAP_MAX_LIMIT 1201 && ( op->ors_slimit == SLAP_NO_LIMIT 1202 || op->ors_slimit > pr_total ) ) 1203 { 1204 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1205 send_ldap_result( op, rs ); 1206 rs->sr_err = LDAP_SUCCESS; 1207 return -1; 1208#endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1209 1210 } else { 1211 /* if no limit is required, use soft limit */ 1212 int total; 1213 int slimit2; 1214 1215 /* first round of pagedResults: 1216 * set count to any appropriate limit */ 1217 1218 /* if the limit is set, check that it does 1219 * not violate any server-side limit */ 1220#ifdef ABOVE_HARD_LIMIT_IS_ERROR 1221 if ( op->ors_slimit == SLAP_MAX_LIMIT ) 1222#else /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1223 if ( op->ors_slimit == SLAP_MAX_LIMIT 1224 || op->ors_slimit > pr_total ) 1225#endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1226 { 1227 slimit2 = op->ors_slimit = pr_total; 1228 1229 } else if ( op->ors_slimit == 0 ) { 1230 slimit2 = pr_total; 1231 1232 } else { 1233 slimit2 = op->ors_slimit; 1234 } 1235 1236 total = slimit2 - ps->ps_count; 1237 1238 if ( total >= 0 ) { 1239 if ( op->ors_limit->lms_s_pr > 0 ) { 1240 /* use the smallest limit set by total/per page */ 1241 if ( total < op->ors_limit->lms_s_pr ) { 1242 slimit = total; 1243 1244 } else { 1245 /* use the perpage limit if any 1246 * NOTE: + 1 because given value must be legal */ 1247 slimit = op->ors_limit->lms_s_pr + 1; 1248 } 1249 1250 } else { 1251 /* use the total limit if any */ 1252 slimit = total; 1253 } 1254 1255 } else if ( op->ors_limit->lms_s_pr > 0 ) { 1256 /* use the perpage limit if any 1257 * NOTE: + 1 because the given value must be legal */ 1258 slimit = op->ors_limit->lms_s_pr + 1; 1259 1260 } else { 1261 /* use the standard hard/soft limit if any */ 1262 slimit = op->ors_limit->lms_s_hard; 1263 } 1264 } 1265 1266 /* if got any limit, use it */ 1267 if ( slimit != -2 ) { 1268 if ( op->ors_slimit == 0 ) { 1269 op->ors_slimit = slimit; 1270 1271 } else if ( slimit > 0 ) { 1272 if ( op->ors_slimit - ps->ps_count > slimit ) { 1273 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1274 send_ldap_result( op, rs ); 1275 rs->sr_err = LDAP_SUCCESS; 1276 return -1; 1277 } 1278 op->ors_slimit = slimit; 1279 1280 } else if ( slimit == 0 ) { 1281 op->ors_slimit = 0; 1282 } 1283 1284 } else { 1285 /* use the standard hard/soft limit if any */ 1286 op->ors_slimit = pr_total; 1287 } 1288 1289 /* no limit requested: use soft, whatever it is */ 1290 } else if ( op->ors_slimit == 0 ) { 1291 op->ors_slimit = op->ors_limit->lms_s_soft; 1292 1293 /* limit requested: check if legal */ 1294 } else { 1295 /* hard limit as soft (traditional behavior) */ 1296 if ( op->ors_limit->lms_s_hard == 0 ) { 1297 if ( op->ors_limit->lms_s_soft > 0 1298 && op->ors_slimit > op->ors_limit->lms_s_soft ) { 1299 op->ors_slimit = op->ors_limit->lms_s_soft; 1300 } 1301 1302 /* explicit hard limit: error if violated */ 1303 } else if ( op->ors_limit->lms_s_hard > 0 ) { 1304#ifdef ABOVE_HARD_LIMIT_IS_ERROR 1305 if ( op->ors_slimit == SLAP_MAX_LIMIT ) { 1306 op->ors_slimit = op->ors_limit->lms_s_hard; 1307 1308 } else if ( op->ors_slimit > op->ors_limit->lms_s_hard ) { 1309 /* if limit exceeds hard, error */ 1310 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 1311 send_ldap_result( op, rs ); 1312 rs->sr_err = LDAP_SUCCESS; 1313 return -1; 1314 } 1315#else /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1316 if ( op->ors_slimit > op->ors_limit->lms_s_hard ) { 1317 op->ors_slimit = op->ors_limit->lms_s_hard; 1318 } 1319#endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */ 1320 } 1321 } 1322 1323 /* else leave as is */ 1324 } 1325 1326 return 0; 1327} 1328 1329void 1330limits_free_one( 1331 struct slap_limits *lm ) 1332{ 1333 if ( ( lm->lm_flags & SLAP_LIMITS_MASK ) == SLAP_LIMITS_REGEX ) 1334 regfree( &lm->lm_regex ); 1335 1336 if ( !BER_BVISNULL( &lm->lm_pat ) ) 1337 ch_free( lm->lm_pat.bv_val ); 1338 1339 ch_free( lm ); 1340} 1341 1342void 1343limits_destroy( 1344 struct slap_limits **lm ) 1345{ 1346 int i; 1347 1348 if ( lm == NULL ) { 1349 return; 1350 } 1351 1352 for ( i = 0; lm[ i ]; i++ ) { 1353 limits_free_one( lm[ i ] ); 1354 } 1355 1356 ch_free( lm ); 1357} 1358