1 /* limits.c - routines to handle regex-based size and time limits */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2013 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
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>.
23 #include <ac/string.h>
28 /* define to get an error if requesting limit higher than hard */
29 #undef ABOVE_HARD_LIMIT_IS_ERROR
31 static const struct berval lmpats[] = {
34 BER_BVC( "onelevel" ),
36 BER_BVC( "children" ),
38 BER_BVC( "anonymous" ),
44 static const char *const dn_source[2] = { "DN", "DN.THIS" };
45 static const char *const lmpats_out[] = {
58 limits2str( unsigned i )
60 return i < (sizeof( lmpats_out ) / sizeof( lmpats_out[0] ))
61 ? lmpats_out[i] : "UNKNOWN";
63 #endif /* LDAP_DEBUG */
68 struct slap_limits_set **limit
71 static struct berval empty_dn = BER_BVC( "" );
72 struct slap_limits **lm;
73 struct berval *ndns[2];
76 assert( limit != NULL );
79 ndns[1] = &op->o_req_ndn;
81 Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s self=\"%s\" this=\"%s\"\n",
83 BER_BVISNULL( ndns[0] ) ? "[anonymous]" : ndns[0]->bv_val,
84 BER_BVISNULL( ndns[1] ) ? "" : ndns[1]->bv_val );
88 *limit = &op->o_bd->be_def_limit;
90 if ( op->o_bd->be_limits == NULL ) {
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];
100 if ( style == SLAP_LIMITS_ANY )
103 if ( BER_BVISEMPTY( ndn ) ) {
104 if ( style == SLAP_LIMITS_ANONYMOUS )
112 case SLAP_LIMITS_EXACT:
113 if ( type == SLAP_LIMITS_TYPE_GROUP ) {
114 int rc = backend_group( op, NULL,
117 lm[0]->lm_group_ad );
122 if ( dn_match( &lm[0]->lm_pat, ndn ) ) {
128 case SLAP_LIMITS_ONE:
129 case SLAP_LIMITS_SUBTREE:
130 case SLAP_LIMITS_CHILDREN: {
133 /* ndn shorter than lm_pat */
134 if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) {
137 d = ndn->bv_len - lm[0]->lm_pat.bv_len;
140 /* allow exact match for SUBTREE only */
141 if ( style != SLAP_LIMITS_SUBTREE ) {
145 /* check for unescaped rdn separator */
146 if ( !DN_SEPARATOR( ndn->bv_val[d - 1] ) ) {
151 /* check that ndn ends with lm_pat */
152 if ( strcmp( lm[0]->lm_pat.bv_val, &ndn->bv_val[d] ) != 0 ) {
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 ) {
166 case SLAP_LIMITS_REGEX:
167 if ( regexec( &lm[0]->lm_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) {
172 case SLAP_LIMITS_ANONYMOUS:
175 case SLAP_LIMITS_USERS:
177 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n",
178 dn_source[isthis], limits2str( style ), 0 );
180 *limit = &lm[0]->lm_limits;
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;
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;
200 assert( 0 ); /* unreachable */
213 ObjectClass *group_oc,
214 AttributeDescription *group_ad,
215 struct slap_limits_set *limit
219 struct slap_limits *lm;
220 unsigned type, style;
222 assert( be != NULL );
223 assert( limit != NULL );
225 type = flags & SLAP_LIMITS_TYPE_MASK;
226 style = flags & SLAP_LIMITS_MASK;
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 ) {
242 lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
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:
256 ber_str2bv( pattern, 0, 0, &bv );
258 rc = dnNormalize( 0, NULL, NULL, &bv, &lm->lm_pat, NULL );
259 if ( rc != LDAP_SUCCESS ) {
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 );
276 case SLAP_LIMITS_ANONYMOUS:
277 case SLAP_LIMITS_USERS:
278 case SLAP_LIMITS_ANY:
279 BER_BVZERO( &lm->lm_pat );
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;
292 lm->lm_flags = style | type;
293 lm->lm_limits = *limit;
296 if ( be->be_limits != NULL ) {
297 for ( ; be->be_limits[i]; i++ );
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;
308 #define STRSTART( s, m ) (strncasecmp( s, m, STRLENOF( "" m "" )) == 0)
319 int flags = SLAP_LIMITS_UNDEFINED;
321 struct slap_limits_set limit;
323 ObjectClass *group_oc = NULL;
324 AttributeDescription *group_ad = NULL;
326 assert( be != NULL );
329 Debug( LDAP_DEBUG_ANY,
330 "%s : line %d: missing arg(s) in "
331 "\"limits <pattern> <limits>\" line.\n%s",
336 limit = be->be_def_limit;
341 * "limits" <pattern> <limit> [ ... ]
348 * [ "dn" [ "." { "this" | "self" } ] [ "." { "exact" | "base" |
349 * "onelevel" | "subtree" | "children" | "regex" | "anonymous" } ]
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
359 * "anonymous" may be deprecated in favour
360 * of the pattern = "anonymous" form
362 * "group[/objectClass[/attributeType]]" "=" "<dn pattern>"
366 * "time" [ "." { "soft" | "hard" } ] "=" <integer>
368 * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
372 if ( strcmp( pattern, "*" ) == 0) {
373 flags = SLAP_LIMITS_ANY;
375 } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
376 flags = SLAP_LIMITS_ANONYMOUS;
378 } else if ( strcasecmp( pattern, "users" ) == 0 ) {
379 flags = SLAP_LIMITS_USERS;
381 } else if ( STRSTART( pattern, "dn" ) ) {
382 pattern += STRLENOF( "dn" );
383 flags = SLAP_LIMITS_TYPE_SELF;
384 if ( pattern[0] == '.' ) {
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" );
395 if ( pattern[0] == '.' ) {
398 if ( STRSTART( pattern, "exact" ) ) {
399 flags |= SLAP_LIMITS_EXACT;
400 pattern += STRLENOF( "exact" );
402 } else if ( STRSTART( pattern, "base" ) ) {
403 flags |= SLAP_LIMITS_BASE;
404 pattern += STRLENOF( "base" );
406 } else if ( STRSTART( pattern, "one" ) ) {
407 flags |= SLAP_LIMITS_ONE;
408 pattern += STRLENOF( "one" );
409 if ( STRSTART( pattern, "level" ) ) {
410 pattern += STRLENOF( "level" );
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 );
419 } else if ( STRSTART( pattern, "sub" ) ) {
420 flags |= SLAP_LIMITS_SUBTREE;
421 pattern += STRLENOF( "sub" );
422 if ( STRSTART( pattern, "tree" ) ) {
423 pattern += STRLENOF( "tree" );
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 );
432 } else if ( STRSTART( pattern, "children" ) ) {
433 flags |= SLAP_LIMITS_CHILDREN;
434 pattern += STRLENOF( "children" );
436 } else if ( STRSTART( pattern, "regex" ) ) {
437 flags |= SLAP_LIMITS_REGEX;
438 pattern += STRLENOF( "regex" );
441 * this could be deprecated in favour
442 * of the pattern = "anonymous" form
444 } else if ( STRSTART( pattern, "anonymous" )
445 && flags == SLAP_LIMITS_TYPE_SELF )
447 flags = SLAP_LIMITS_ANONYMOUS;
451 /* force error below */
452 if ( *pattern == '=' )
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",
467 isalnum( (unsigned char)pattern[0] )
468 ? "unknown DN modifier" : "missing '='" );
472 /* skip '=' (required) */
475 /* trim obvious cases */
476 if ( strcmp( pattern, "*" ) == 0 ) {
477 flags = SLAP_LIMITS_ANY;
480 } else if ( (flags & SLAP_LIMITS_MASK) == SLAP_LIMITS_REGEX
481 && strcmp( pattern, ".*" ) == 0 ) {
482 flags = SLAP_LIMITS_ANY;
487 } else if (STRSTART( pattern, "group" ) ) {
488 pattern += STRLENOF( "group" );
490 if ( pattern[0] == '/' ) {
491 struct berval oc, ad;
493 oc.bv_val = pattern + 1;
494 pattern = strchr( pattern, '=' );
495 if ( pattern == NULL ) {
499 ad.bv_val = strchr( oc.bv_val, '/' );
500 if ( ad.bv_val != NULL ) {
501 const char *text = NULL;
503 oc.bv_len = ad.bv_val - oc.bv_val;
506 ad.bv_len = pattern - ad.bv_val;
507 rc = slap_bv2ad( &ad, &group_ad, &text );
508 if ( rc != LDAP_SUCCESS ) {
513 oc.bv_len = pattern - oc.bv_val;
516 group_oc = oc_bvfind( &oc );
517 if ( group_oc == NULL ) {
522 if ( group_oc == NULL ) {
523 group_oc = oc_find( SLAPD_GROUP_CLASS );
524 if ( group_oc == NULL ) {
530 if ( group_ad == NULL ) {
531 const char *text = NULL;
533 rc = slap_str2ad( SLAPD_GROUP_ATTR, &group_ad, &text );
535 if ( rc != LDAP_SUCCESS ) {
541 flags = SLAP_LIMITS_TYPE_GROUP | SLAP_LIMITS_EXACT;
543 if ( pattern[0] != '=' ) {
544 Debug( LDAP_DEBUG_ANY,
545 "%s : line %d: missing '=' in "
546 "\"group[/objectClass[/attributeType]]"
548 "\"limits <pattern> <limits>\" line.\n",
553 /* skip '=' (required) */
558 for ( i = 2; i < argc; i++ ) {
559 if ( limits_parse_one( argv[i], &limit ) ) {
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] );
573 * FIXME: add warnings?
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;
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;
593 * > 0 => limit (in seconds)
598 * > 0 => limit (in entries)
601 * -2 => disable the control
604 * > 0 => limit (in entries)
609 * > 0 => limit size (in entries)
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;
616 rc = limits_add( be, flags, pattern, group_oc, group_ad, &limit );
619 Debug( LDAP_DEBUG_ANY,
620 "%s : line %d: unable to add limit in "
621 "\"limits <pattern> <limits>\" line.\n",
631 struct slap_limits_set *limit
634 assert( arg != NULL );
635 assert( limit != NULL );
637 if ( STRSTART( arg, "time" ) ) {
638 arg += STRLENOF( "time" );
640 if ( arg[0] == '.' ) {
642 if ( STRSTART( arg, "soft=" ) ) {
643 arg += STRLENOF( "soft=" );
644 if ( strcasecmp( arg, "unlimited" ) == 0
645 || strcasecmp( arg, "none" ) == 0 )
647 limit->lms_t_soft = -1;
652 if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
657 /* FIXME: use "unlimited" instead; issue warning? */
660 limit->lms_t_soft = soft;
663 } else if ( STRSTART( arg, "hard=" ) ) {
664 arg += STRLENOF( "hard=" );
665 if ( strcasecmp( arg, "soft" ) == 0 ) {
666 limit->lms_t_hard = 0;
668 } else if ( strcasecmp( arg, "unlimited" ) == 0
669 || strcasecmp( arg, "none" ) == 0 )
671 limit->lms_t_hard = -1;
676 if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
681 /* FIXME: use "unlimited" instead */
685 /* FIXME: use "soft" instead */
688 limit->lms_t_hard = hard;
695 } else if ( arg[0] == '=' ) {
697 if ( strcasecmp( arg, "unlimited" ) == 0
698 || strcasecmp( arg, "none" ) == 0 )
700 limit->lms_t_soft = -1;
703 if ( lutil_atoi( &limit->lms_t_soft, arg ) != 0
704 || limit->lms_t_soft < -1 )
709 limit->lms_t_hard = 0;
715 } else if ( STRSTART( arg, "size" ) ) {
716 arg += STRLENOF( "size" );
718 if ( arg[0] == '.' ) {
720 if ( STRSTART( arg, "soft=" ) ) {
721 arg += STRLENOF( "soft=" );
722 if ( strcasecmp( arg, "unlimited" ) == 0
723 || strcasecmp( arg, "none" ) == 0 )
725 limit->lms_s_soft = -1;
730 if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
735 /* FIXME: use "unlimited" instead */
738 limit->lms_s_soft = soft;
741 } else if ( STRSTART( arg, "hard=" ) ) {
742 arg += STRLENOF( "hard=" );
743 if ( strcasecmp( arg, "soft" ) == 0 ) {
744 limit->lms_s_hard = 0;
746 } else if ( strcasecmp( arg, "unlimited" ) == 0
747 || strcasecmp( arg, "none" ) == 0 )
749 limit->lms_s_hard = -1;
754 if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
759 /* FIXME: use "unlimited" instead */
763 /* FIXME: use "soft" instead */
766 limit->lms_s_hard = hard;
769 } else if ( STRSTART( arg, "unchecked=" ) ) {
770 arg += STRLENOF( "unchecked=" );
771 if ( strcasecmp( arg, "unlimited" ) == 0
772 || strcasecmp( arg, "none" ) == 0 )
774 limit->lms_s_unchecked = -1;
776 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
777 limit->lms_s_unchecked = 0;
782 if ( lutil_atoi( &unchecked, arg ) != 0 || unchecked < -1 ) {
786 if ( unchecked == -1 ) {
787 /* FIXME: use "unlimited" instead */
790 limit->lms_s_unchecked = unchecked;
793 } else if ( STRSTART( arg, "pr=" ) ) {
794 arg += STRLENOF( "pr=" );
795 if ( strcasecmp( arg, "noEstimate" ) == 0 ) {
796 limit->lms_s_pr_hide = 1;
798 } else if ( strcasecmp( arg, "unlimited" ) == 0
799 || strcasecmp( arg, "none" ) == 0 )
801 limit->lms_s_pr = -1;
806 if ( lutil_atoi( &pr, arg ) != 0 || pr < -1 ) {
811 /* FIXME: use "unlimited" instead */
814 limit->lms_s_pr = pr;
817 } else if ( STRSTART( arg, "prtotal=" ) ) {
818 arg += STRLENOF( "prtotal=" );
820 if ( strcasecmp( arg, "unlimited" ) == 0
821 || strcasecmp( arg, "none" ) == 0 )
823 limit->lms_s_pr_total = -1;
825 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
826 limit->lms_s_pr_total = -2;
828 } else if ( strcasecmp( arg, "hard" ) == 0 ) {
829 limit->lms_s_pr_total = 0;
834 if ( lutil_atoi( &total, arg ) != 0 || total < -1 ) {
839 /* FIXME: use "unlimited" instead */
843 /* FIXME: use "pr=disable" instead */
846 limit->lms_s_pr_total = total;
853 } else if ( arg[0] == '=' ) {
855 if ( strcasecmp( arg, "unlimited" ) == 0
856 || strcasecmp( arg, "none" ) == 0 )
858 limit->lms_s_soft = -1;
861 if ( lutil_atoi( &limit->lms_s_soft, arg ) != 0
862 || limit->lms_s_soft < -1 )
867 limit->lms_s_hard = 0;
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).
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))
892 /* Caller must provide an adequately sized buffer in bv */
894 limits_unparse( struct slap_limits *lim, struct berval *bv, ber_len_t buflen )
897 char *ptr, *bufEnd; /* Updated/used by ptr_APPEND_*()/WHATSLEFT */
898 ber_len_t tmpLen; /* Used by ptr_APPEND_FMT*() */
899 unsigned type, style;
902 if ( !bv || !bv->bv_val ) return -1;
905 bufEnd = ptr + buflen;
906 type = lim->lm_flags & SLAP_LIMITS_TYPE_MASK;
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 ));
914 style = lim->lm_flags & SLAP_LIMITS_MASK;
916 case SLAP_LIMITS_ANONYMOUS:
917 case SLAP_LIMITS_USERS:
918 case SLAP_LIMITS_ANY:
919 rc = ptr_APPEND_BV( lmpats[style] );
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 ));
934 bv->bv_len = ptr - bv->bv_val;
937 rc = limits_unparse_one( &lim->lm_limits,
938 SLAP_LIMIT_SIZE | SLAP_LIMIT_TIME,
941 bv->bv_len += btmp.bv_len;
946 /* Caller must provide an adequately sized buffer in bv */
949 struct slap_limits_set *lim,
954 char *ptr, *bufEnd; /* Updated/used by ptr_APPEND_*()/WHATSLEFT */
955 ber_len_t tmpLen; /* Used by ptr_APPEND_FMT*() */
957 if ( !bv || !bv->bv_val ) return -1;
960 bufEnd = ptr + buflen;
962 if ( which & SLAP_LIMIT_SIZE ) {
963 if ( lim->lms_s_soft != SLAPD_DEFAULT_SIZELIMIT ) {
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 )
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;
974 /* If doing both size & time, qualify this */
975 } else if ( which & SLAP_LIMIT_TIME ) {
976 if ( ptr_APPEND_LIT( " size=" ) ) return -1;
979 if ( lim->lms_s_soft == -1
980 ? ptr_APPEND_LIT( "unlimited " )
981 : ptr_APPEND_FMT1( "%d ", lim->lms_s_soft ) )
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 ) )
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 ) )
999 if ( lim->lms_s_pr_hide ) {
1000 if ( ptr_APPEND_LIT( " size.pr=noEstimate " ) ) return -1;
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 ) )
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 ) )
1018 if ( which & SLAP_LIMIT_TIME ) {
1019 if ( lim->lms_t_soft != SLAPD_DEFAULT_TIMELIMIT ) {
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 )
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;
1031 /* If doing both size & time, qualify this */
1032 } else if ( which & SLAP_LIMIT_SIZE ) {
1033 if ( ptr_APPEND_LIT( " time=" ) ) return -1;
1036 if ( lim->lms_t_soft == -1
1037 ? ptr_APPEND_LIT( "unlimited " )
1038 : ptr_APPEND_FMT1( "%d ", lim->lms_t_soft ) )
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 ) )
1050 if ( ptr != bv->bv_val ) {
1053 bv->bv_len = ptr - bv->bv_val;
1060 limits_check( Operation *op, SlapReply *rs )
1062 assert( op != NULL );
1063 assert( rs != NULL );
1064 /* FIXME: should this be always true? */
1065 assert( op->o_tag == LDAP_REQ_SEARCH);
1067 /* protocol only allows 0..maxInt;
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())
1076 if ( op->ors_tlimit == SLAP_NO_LIMIT && op->ors_slimit == SLAP_NO_LIMIT ) {
1080 /* allow root to set no limit */
1081 if ( be_isroot( op ) ) {
1082 op->ors_limit = NULL;
1084 if ( op->ors_tlimit == 0 ) {
1085 op->ors_tlimit = SLAP_NO_LIMIT;
1088 if ( op->ors_slimit == 0 ) {
1089 op->ors_slimit = SLAP_NO_LIMIT;
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;
1098 op->ors_slimit = total;
1104 /* if not root, get appropriate limits */
1106 ( void ) limits_get( op, &op->ors_limit );
1108 assert( op->ors_limit != NULL );
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;
1114 /* limit required: check if legal */
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;
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;
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;
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;
1138 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1142 /* else leave as is */
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;
1152 /* if paged results is requested */
1153 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
1156 PagedResultsState *ps = op->o_pagedresults_state;
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;
1168 if ( op->ors_limit->lms_s_pr > 0
1169 && ps->ps_size > op->ors_limit->lms_s_pr )
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;
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;
1183 pr_total = op->ors_limit->lms_s_hard;
1186 pr_total = op->ors_limit->lms_s_pr_total;
1189 if ( pr_total == -1 ) {
1190 if ( op->ors_slimit == 0 || op->ors_slimit == SLAP_MAX_LIMIT ) {
1194 slimit = op->ors_slimit - ps->ps_count;
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 ) )
1202 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1203 send_ldap_result( op, rs );
1204 rs->sr_err = LDAP_SUCCESS;
1206 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1209 /* if no limit is required, use soft limit */
1213 /* first round of pagedResults:
1214 * set count to any appropriate limit */
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 */
1225 slimit2 = op->ors_slimit = pr_total;
1227 } else if ( op->ors_slimit == 0 ) {
1231 slimit2 = op->ors_slimit;
1234 total = slimit2 - ps->ps_count;
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 ) {
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;
1249 /* use the total limit if any */
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;
1259 /* use the standard hard/soft limit if any */
1260 slimit = op->ors_limit->lms_s_hard;
1264 /* if got any limit, use it */
1265 if ( slimit != -2 ) {
1266 if ( op->ors_slimit == 0 ) {
1267 op->ors_slimit = slimit;
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;
1276 op->ors_slimit = slimit;
1278 } else if ( slimit == 0 ) {
1283 /* use the standard hard/soft limit if any */
1284 op->ors_slimit = pr_total;
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;
1291 /* limit requested: check if legal */
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;
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;
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;
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;
1317 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1321 /* else leave as is */
1329 struct slap_limits *lm )
1331 if ( ( lm->lm_flags & SLAP_LIMITS_MASK ) == SLAP_LIMITS_REGEX )
1332 regfree( &lm->lm_regex );
1334 if ( !BER_BVISNULL( &lm->lm_pat ) )
1335 ch_free( lm->lm_pat.bv_val );
1342 struct slap_limits **lm )
1350 for ( i = 0; lm[ i ]; i++ ) {
1351 limits_free_one( lm[ i ] );