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