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