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-2008 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>.
22 #include <ac/string.h>
27 /* define to get an error if requesting limit higher than hard */
28 #undef ABOVE_HARD_LIMIT_IS_ERROR
30 static const struct berval lmpats[] = {
33 BER_BVC( "onelevel" ),
35 BER_BVC( "children" ),
37 BER_BVC( "anonymous" ),
43 static const char *const dn_source[2] = { "DN", "DN.THIS" };
44 static const char *const lmpats_out[] = {
57 limits2str( unsigned i )
59 return i < (sizeof( lmpats_out ) / sizeof( lmpats_out[0] ))
60 ? lmpats_out[i] : "UNKNOWN";
62 #endif /* LDAP_DEBUG */
67 struct slap_limits_set **limit
70 struct slap_limits **lm;
71 struct berval *ndns[2];
74 assert( limit != NULL );
77 ndns[1] = &op->o_req_ndn;
79 Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s self=\"%s\" this=\"%s\"\n",
81 BER_BVISNULL( ndns[0] ) ? "[anonymous]" : ndns[0]->bv_val,
82 BER_BVISNULL( ndns[1] ) ? "" : ndns[1]->bv_val );
86 *limit = &op->o_bd->be_def_limit;
88 if ( op->o_bd->be_limits == NULL ) {
92 for ( lm = op->o_bd->be_limits; lm[0] != NULL; lm++ ) {
93 unsigned style = lm[0]->lm_flags & SLAP_LIMITS_MASK;
94 unsigned type = lm[0]->lm_flags & SLAP_LIMITS_TYPE_MASK;
95 unsigned isthis = type == SLAP_LIMITS_TYPE_THIS;
96 struct berval *ndn = ndns[isthis];
99 case SLAP_LIMITS_EXACT:
100 if ( BER_BVISEMPTY( ndn ) ) {
104 if ( type == SLAP_LIMITS_TYPE_GROUP ) {
105 int rc = backend_group( op, NULL,
108 lm[0]->lm_group_ad );
113 if ( dn_match( &lm[0]->lm_pat, ndn ) ) {
119 case SLAP_LIMITS_ONE:
120 case SLAP_LIMITS_SUBTREE:
121 case SLAP_LIMITS_CHILDREN: {
124 if ( BER_BVISEMPTY( ndn ) ) {
128 /* ndn shorter than lm_pat */
129 if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) {
132 d = ndn->bv_len - lm[0]->lm_pat.bv_len;
135 /* allow exact match for SUBTREE only */
136 if ( style != SLAP_LIMITS_SUBTREE ) {
140 /* check for unescaped rdn separator */
141 if ( !DN_SEPARATOR( ndn->bv_val[d - 1] ) ) {
146 /* check that ndn ends with lm_pat */
147 if ( strcmp( lm[0]->lm_pat.bv_val, &ndn->bv_val[d] ) != 0 ) {
151 /* in case of ONE, require exactly one rdn below lm_pat */
152 if ( style == SLAP_LIMITS_ONE ) {
153 if ( dn_rdnlen( NULL, ndn ) != d - 1 ) {
161 case SLAP_LIMITS_REGEX:
162 if ( BER_BVISEMPTY( ndn ) ) {
165 if ( regexec( &lm[0]->lm_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) {
170 case SLAP_LIMITS_ANONYMOUS:
171 if ( BER_BVISEMPTY( ndn ) ) {
176 case SLAP_LIMITS_USERS:
177 if ( !BER_BVISEMPTY( ndn ) ) {
182 case SLAP_LIMITS_ANY:
183 *limit = &lm[0]->lm_limits;
187 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n",
188 dn_source[isthis], limits2str( style ), 0 );
189 *limit = &lm[0]->lm_limits;
193 Debug( LDAP_DEBUG_TRACE,
194 "<== limits_get: type=%s match=%s dn=\"%s\"\n",
195 dn_source[isthis], limits2str( style ), lm[0]->lm_pat.bv_val );
196 *limit = &lm[0]->lm_limits;
200 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=GROUP match=EXACT "
201 "dn=\"%s\" oc=\"%s\" ad=\"%s\"\n",
202 lm[0]->lm_pat.bv_val,
203 lm[0]->lm_group_oc->soc_cname.bv_val,
204 lm[0]->lm_group_ad->ad_cname.bv_val );
205 *limit = &lm[0]->lm_limits;
209 assert( 0 ); /* unreachable */
222 ObjectClass *group_oc,
223 AttributeDescription *group_ad,
224 struct slap_limits_set *limit
228 struct slap_limits *lm;
229 unsigned type, style;
231 assert( be != NULL );
232 assert( limit != NULL );
234 type = flags & SLAP_LIMITS_TYPE_MASK;
235 style = flags & SLAP_LIMITS_MASK;
238 case SLAP_LIMITS_ANONYMOUS:
239 case SLAP_LIMITS_USERS:
240 case SLAP_LIMITS_ANY:
241 /* For these styles, type == 0 (SLAP_LIMITS_TYPE_SELF). */
242 for ( i = 0; be->be_limits && be->be_limits[ i ]; i++ ) {
243 if ( be->be_limits[ i ]->lm_flags == style ) {
251 lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
254 case SLAP_LIMITS_UNDEFINED:
255 style = SLAP_LIMITS_EXACT;
256 /* continue to next cases */
257 case SLAP_LIMITS_EXACT:
258 case SLAP_LIMITS_ONE:
259 case SLAP_LIMITS_SUBTREE:
260 case SLAP_LIMITS_CHILDREN:
261 lm->lm_flags = style | type;
266 ber_str2bv( pattern, 0, 0, &bv );
268 rc = dnNormalize( 0, NULL, NULL, &bv, &lm->lm_pat, NULL );
269 if ( rc != LDAP_SUCCESS ) {
276 case SLAP_LIMITS_REGEX:
277 lm->lm_flags = style | type;
278 ber_str2bv( pattern, 0, 1, &lm->lm_pat );
279 if ( regcomp( &lm->lm_regex, lm->lm_pat.bv_val,
280 REG_EXTENDED | REG_ICASE ) ) {
281 free( lm->lm_pat.bv_val );
287 case SLAP_LIMITS_ANONYMOUS:
288 case SLAP_LIMITS_USERS:
289 case SLAP_LIMITS_ANY:
290 lm->lm_flags = style | type;
291 BER_BVZERO( &lm->lm_pat );
296 case SLAP_LIMITS_TYPE_GROUP:
297 assert( group_oc != NULL );
298 assert( group_ad != NULL );
299 lm->lm_group_oc = group_oc;
300 lm->lm_group_ad = group_ad;
304 lm->lm_limits = *limit;
307 if ( be->be_limits != NULL ) {
308 for ( ; be->be_limits[i]; i++ );
311 be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits,
312 sizeof( struct slap_limits * ) * ( i + 2 ) );
313 be->be_limits[i] = lm;
314 be->be_limits[i+1] = NULL;
319 #define STRSTART( s, m ) (strncasecmp( s, m, STRLENOF( "" m "" )) == 0)
330 int flags = SLAP_LIMITS_UNDEFINED;
332 struct slap_limits_set limit;
334 ObjectClass *group_oc = NULL;
335 AttributeDescription *group_ad = NULL;
337 assert( be != NULL );
340 Debug( LDAP_DEBUG_ANY,
341 "%s : line %d: missing arg(s) in "
342 "\"limits <pattern> <limits>\" line.\n%s",
347 limit = be->be_def_limit;
352 * "limits" <pattern> <limit> [ ... ]
359 * [ "dn" [ "." { "this" | "self" } ] [ "." { "exact" | "base" |
360 * "onelevel" | "subtree" | "children" | "regex" | "anonymous" } ]
364 * "this" is the baseobject, "self" (the default) is the bound DN
365 * "exact" and "base" are the same (exact match);
366 * "onelevel" means exactly one rdn below, NOT including pattern
367 * "subtree" means any rdn below, including pattern
368 * "children" means any rdn below, NOT including pattern
370 * "anonymous" may be deprecated in favour
371 * of the pattern = "anonymous" form
373 * "group[/objectClass[/attributeType]]" "=" "<dn pattern>"
377 * "time" [ "." { "soft" | "hard" } ] "=" <integer>
379 * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
383 if ( strcmp( pattern, "*" ) == 0) {
384 flags = SLAP_LIMITS_ANY;
386 } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
387 flags = SLAP_LIMITS_ANONYMOUS;
389 } else if ( strcasecmp( pattern, "users" ) == 0 ) {
390 flags = SLAP_LIMITS_USERS;
392 } else if ( STRSTART( pattern, "dn" ) ) {
393 pattern += STRLENOF( "dn" );
394 flags = SLAP_LIMITS_TYPE_SELF;
395 if ( pattern[0] == '.' ) {
397 if ( STRSTART( pattern, "this" ) ) {
398 flags = SLAP_LIMITS_TYPE_THIS;
399 pattern += STRLENOF( "this" );
400 } else if ( STRSTART( pattern, "self" ) ) {
401 pattern += STRLENOF( "self" );
406 if ( pattern[0] == '.' ) {
409 if ( STRSTART( pattern, "exact" ) ) {
410 flags |= SLAP_LIMITS_EXACT;
411 pattern += STRLENOF( "exact" );
413 } else if ( STRSTART( pattern, "base" ) ) {
414 flags |= SLAP_LIMITS_BASE;
415 pattern += STRLENOF( "base" );
417 } else if ( STRSTART( pattern, "one" ) ) {
418 flags |= SLAP_LIMITS_ONE;
419 pattern += STRLENOF( "one" );
420 if ( STRSTART( pattern, "level" ) ) {
421 pattern += STRLENOF( "level" );
424 Debug( LDAP_DEBUG_ANY,
425 "%s : line %d: deprecated \"one\" style "
426 "\"limits <pattern> <limits>\" line; "
427 "use \"onelevel\" instead.\n", fname, lineno, 0 );
430 } else if ( STRSTART( pattern, "sub" ) ) {
431 flags |= SLAP_LIMITS_SUBTREE;
432 pattern += STRLENOF( "sub" );
433 if ( STRSTART( pattern, "tree" ) ) {
434 pattern += STRLENOF( "tree" );
437 Debug( LDAP_DEBUG_ANY,
438 "%s : line %d: deprecated \"sub\" style "
439 "\"limits <pattern> <limits>\" line; "
440 "use \"subtree\" instead.\n", fname, lineno, 0 );
443 } else if ( STRSTART( pattern, "children" ) ) {
444 flags |= SLAP_LIMITS_CHILDREN;
445 pattern += STRLENOF( "children" );
447 } else if ( STRSTART( pattern, "regex" ) ) {
448 flags |= SLAP_LIMITS_REGEX;
449 pattern += STRLENOF( "regex" );
452 * this could be deprecated in favour
453 * of the pattern = "anonymous" form
455 } else if ( STRSTART( pattern, "anonymous" )
456 && flags == SLAP_LIMITS_TYPE_SELF )
458 flags = SLAP_LIMITS_ANONYMOUS;
463 /* pre-check the data */
465 case SLAP_LIMITS_ANONYMOUS:
466 case SLAP_LIMITS_USERS:
468 /* no need for pattern */
473 if ( pattern[0] != '=' ) {
474 Debug( LDAP_DEBUG_ANY,
475 "%s : line %d: missing '=' in "
476 "\"dn[.{this|self}][.{exact|base"
477 "|onelevel|subtree|children|regex}]"
479 "\"limits <pattern> <limits>\" "
485 /* skip '=' (required) */
488 /* trim obvious cases */
489 if ( strcmp( pattern, "*" ) == 0 ) {
490 flags = SLAP_LIMITS_ANY;
493 } else if ( flags == SLAP_LIMITS_REGEX
494 && strcmp( pattern, ".*" ) == 0 ) {
495 flags = SLAP_LIMITS_ANY;
500 } else if (STRSTART( pattern, "group" ) ) {
501 pattern += STRLENOF( "group" );
503 if ( pattern[0] == '/' ) {
504 struct berval oc, ad;
506 oc.bv_val = pattern + 1;
507 pattern = strchr( pattern, '=' );
508 if ( pattern == NULL ) {
512 ad.bv_val = strchr( oc.bv_val, '/' );
513 if ( ad.bv_val != NULL ) {
514 const char *text = NULL;
516 oc.bv_len = ad.bv_val - oc.bv_val;
519 ad.bv_len = pattern - ad.bv_val;
520 rc = slap_bv2ad( &ad, &group_ad, &text );
521 if ( rc != LDAP_SUCCESS ) {
526 oc.bv_len = pattern - oc.bv_val;
529 group_oc = oc_bvfind( &oc );
530 if ( group_oc == NULL ) {
535 if ( group_oc == NULL ) {
536 group_oc = oc_find( SLAPD_GROUP_CLASS );
537 if ( group_oc == NULL ) {
543 if ( group_ad == NULL ) {
544 const char *text = NULL;
546 rc = slap_str2ad( SLAPD_GROUP_ATTR, &group_ad, &text );
548 if ( rc != LDAP_SUCCESS ) {
554 flags = SLAP_LIMITS_TYPE_GROUP | SLAP_LIMITS_EXACT;
556 if ( pattern[0] != '=' ) {
557 Debug( LDAP_DEBUG_ANY,
558 "%s : line %d: missing '=' in "
559 "\"group[/objectClass[/attributeType]]"
561 "\"limits <pattern> <limits>\" line.\n",
566 /* skip '=' (required) */
571 for ( i = 2; i < argc; i++ ) {
572 if ( limits_parse_one( argv[i], &limit ) ) {
574 Debug( LDAP_DEBUG_ANY,
575 "%s : line %d: unknown limit values \"%s\" in "
576 "\"limits <pattern> <limits>\" line.\n",
577 fname, lineno, argv[i] );
586 * FIXME: add warnings?
588 if ( limit.lms_t_hard > 0 &&
589 ( limit.lms_t_hard < limit.lms_t_soft
590 || limit.lms_t_soft == -1 ) ) {
591 limit.lms_t_hard = limit.lms_t_soft;
594 if ( limit.lms_s_hard > 0 &&
595 ( limit.lms_s_hard < limit.lms_s_soft
596 || limit.lms_s_soft == -1 ) ) {
597 limit.lms_s_hard = limit.lms_s_soft;
606 * > 0 => limit (in seconds)
611 * > 0 => limit (in entries)
614 * -2 => disable the control
617 * > 0 => limit (in entries)
622 * > 0 => limit size (in entries)
624 if ( limit.lms_s_pr_total > 0 &&
625 limit.lms_s_pr > limit.lms_s_pr_total ) {
626 limit.lms_s_pr = limit.lms_s_pr_total;
629 rc = limits_add( be, flags, pattern, group_oc, group_ad, &limit );
632 Debug( LDAP_DEBUG_ANY,
633 "%s : line %d: unable to add limit in "
634 "\"limits <pattern> <limits>\" line.\n",
644 struct slap_limits_set *limit
647 assert( arg != NULL );
648 assert( limit != NULL );
650 if ( STRSTART( arg, "time" ) ) {
651 arg += STRLENOF( "time" );
653 if ( arg[0] == '.' ) {
655 if ( STRSTART( arg, "soft=" ) ) {
656 arg += STRLENOF( "soft=" );
657 if ( strcasecmp( arg, "unlimited" ) == 0
658 || strcasecmp( arg, "none" ) == 0 )
660 limit->lms_t_soft = -1;
665 if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
670 /* FIXME: use "unlimited" instead; issue warning? */
673 limit->lms_t_soft = soft;
676 } else if ( STRSTART( arg, "hard=" ) ) {
677 arg += STRLENOF( "hard=" );
678 if ( strcasecmp( arg, "soft" ) == 0 ) {
679 limit->lms_t_hard = 0;
681 } else if ( strcasecmp( arg, "unlimited" ) == 0
682 || strcasecmp( arg, "none" ) == 0 )
684 limit->lms_t_hard = -1;
689 if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
694 /* FIXME: use "unlimited" instead */
698 /* FIXME: use "soft" instead */
701 limit->lms_t_hard = hard;
708 } else if ( arg[0] == '=' ) {
710 if ( strcasecmp( arg, "unlimited" ) == 0
711 || strcasecmp( arg, "none" ) == 0 )
713 limit->lms_t_soft = -1;
716 if ( lutil_atoi( &limit->lms_t_soft, arg ) != 0
717 || limit->lms_t_soft < -1 )
722 limit->lms_t_hard = 0;
728 } else if ( STRSTART( arg, "size" ) ) {
729 arg += STRLENOF( "size" );
731 if ( arg[0] == '.' ) {
733 if ( STRSTART( arg, "soft=" ) ) {
734 arg += STRLENOF( "soft=" );
735 if ( strcasecmp( arg, "unlimited" ) == 0
736 || strcasecmp( arg, "none" ) == 0 )
738 limit->lms_s_soft = -1;
743 if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
748 /* FIXME: use "unlimited" instead */
751 limit->lms_s_soft = soft;
754 } else if ( STRSTART( arg, "hard=" ) ) {
755 arg += STRLENOF( "hard=" );
756 if ( strcasecmp( arg, "soft" ) == 0 ) {
757 limit->lms_s_hard = 0;
759 } else if ( strcasecmp( arg, "unlimited" ) == 0
760 || strcasecmp( arg, "none" ) == 0 )
762 limit->lms_s_hard = -1;
767 if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
772 /* FIXME: use "unlimited" instead */
776 /* FIXME: use "soft" instead */
779 limit->lms_s_hard = hard;
782 } else if ( STRSTART( arg, "unchecked=" ) ) {
783 arg += STRLENOF( "unchecked=" );
784 if ( strcasecmp( arg, "unlimited" ) == 0
785 || strcasecmp( arg, "none" ) == 0 )
787 limit->lms_s_unchecked = -1;
789 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
790 limit->lms_s_unchecked = 0;
795 if ( lutil_atoi( &unchecked, arg ) != 0 || unchecked < -1 ) {
799 if ( unchecked == -1 ) {
800 /* FIXME: use "unlimited" instead */
803 limit->lms_s_unchecked = unchecked;
806 } else if ( STRSTART( arg, "pr=" ) ) {
807 arg += STRLENOF( "pr=" );
808 if ( strcasecmp( arg, "noEstimate" ) == 0 ) {
809 limit->lms_s_pr_hide = 1;
811 } else if ( strcasecmp( arg, "unlimited" ) == 0
812 || strcasecmp( arg, "none" ) == 0 )
814 limit->lms_s_pr = -1;
819 if ( lutil_atoi( &pr, arg ) != 0 || pr < -1 ) {
824 /* FIXME: use "unlimited" instead */
827 limit->lms_s_pr = pr;
830 } else if ( STRSTART( arg, "prtotal=" ) ) {
831 arg += STRLENOF( "prtotal=" );
833 if ( strcasecmp( arg, "unlimited" ) == 0
834 || strcasecmp( arg, "none" ) == 0 )
836 limit->lms_s_pr_total = -1;
838 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
839 limit->lms_s_pr_total = -2;
841 } else if ( strcasecmp( arg, "hard" ) == 0 ) {
842 limit->lms_s_pr_total = 0;
847 if ( lutil_atoi( &total, arg ) != 0 || total < -1 ) {
852 /* FIXME: use "unlimited" instead */
856 /* FIXME: use "pr=disable" instead */
859 limit->lms_s_pr_total = total;
866 } else if ( arg[0] == '=' ) {
868 if ( strcasecmp( arg, "unlimited" ) == 0
869 || strcasecmp( arg, "none" ) == 0 )
871 limit->lms_s_soft = -1;
874 if ( lutil_atoi( &limit->lms_s_soft, arg ) != 0
875 || limit->lms_s_soft < -1 )
880 limit->lms_s_hard = 0;
890 /* Helper macros for limits_unparse() and limits_unparse_one():
891 * Write to ptr, but not past bufEnd. Move ptr past the new text.
892 * Return (success && enough room ? 0 : -1).
894 #define ptr_APPEND_BV(bv) /* Append a \0-terminated berval */ \
895 (WHATSLEFT <= (bv).bv_len ? -1 : \
896 ((void) (ptr = lutil_strcopy( ptr, (bv).bv_val )), 0))
897 #define ptr_APPEND_LIT(str) /* Append a string literal */ \
898 (WHATSLEFT <= STRLENOF( "" str "" ) ? -1 : \
899 ((void) (ptr = lutil_strcopy( ptr, str )), 0))
900 #define ptr_APPEND_FMT(args) /* Append formatted text */ \
901 (WHATSLEFT <= (tmpLen = snprintf args) ? -1 : ((void) (ptr += tmpLen), 0))
902 #define ptr_APPEND_FMT1(fmt, arg) ptr_APPEND_FMT(( ptr, WHATSLEFT, fmt, arg ))
903 #define WHATSLEFT ((ber_len_t) (bufEnd - ptr))
905 /* Caller must provide an adequately sized buffer in bv */
907 limits_unparse( struct slap_limits *lim, struct berval *bv, ber_len_t buflen )
910 char *ptr, *bufEnd; /* Updated/used by ptr_APPEND_*()/WHATSLEFT */
911 ber_len_t tmpLen; /* Used by ptr_APPEND_FMT*() */
912 unsigned type, style;
915 if ( !bv || !bv->bv_val ) return -1;
918 bufEnd = ptr + buflen;
919 type = lim->lm_flags & SLAP_LIMITS_TYPE_MASK;
921 if ( type == SLAP_LIMITS_TYPE_GROUP ) {
922 rc = ptr_APPEND_FMT(( ptr, WHATSLEFT, "group/%s/%s=\"%s\"",
923 lim->lm_group_oc->soc_cname.bv_val,
924 lim->lm_group_ad->ad_cname.bv_val,
925 lim->lm_pat.bv_val ));
927 style = lim->lm_flags & SLAP_LIMITS_MASK;
929 case SLAP_LIMITS_ANONYMOUS:
930 case SLAP_LIMITS_USERS:
931 case SLAP_LIMITS_ANY:
932 rc = ptr_APPEND_BV( lmpats[style] );
934 case SLAP_LIMITS_UNDEFINED:
935 case SLAP_LIMITS_EXACT:
936 case SLAP_LIMITS_ONE:
937 case SLAP_LIMITS_SUBTREE:
938 case SLAP_LIMITS_CHILDREN:
939 case SLAP_LIMITS_REGEX:
940 rc = ptr_APPEND_FMT(( ptr, WHATSLEFT, "dn.%s%s=\"%s\"",
941 type == SLAP_LIMITS_TYPE_SELF ? "" : "this.",
942 lmpats[style].bv_val, lim->lm_pat.bv_val ));
947 bv->bv_len = ptr - bv->bv_val;
950 rc = limits_unparse_one( &lim->lm_limits,
951 SLAP_LIMIT_SIZE | SLAP_LIMIT_TIME,
954 bv->bv_len += btmp.bv_len;
959 /* Caller must provide an adequately sized buffer in bv */
962 struct slap_limits_set *lim,
967 char *ptr, *bufEnd; /* Updated/used by ptr_APPEND_*()/WHATSLEFT */
968 ber_len_t tmpLen; /* Used by ptr_APPEND_FMT*() */
970 if ( !bv || !bv->bv_val ) return -1;
973 bufEnd = ptr + buflen;
975 if ( which & SLAP_LIMIT_SIZE ) {
976 if ( lim->lms_s_soft != SLAPD_DEFAULT_SIZELIMIT ) {
978 /* If same as global limit, drop it */
979 if ( lim != &frontendDB->be_def_limit &&
980 lim->lms_s_soft == frontendDB->be_def_limit.lms_s_soft )
983 /* If there's also a hard limit, fully qualify this one */
984 } else if ( lim->lms_s_hard ) {
985 if ( ptr_APPEND_LIT( " size.soft=" ) ) return -1;
987 /* If doing both size & time, qualify this */
988 } else if ( which & SLAP_LIMIT_TIME ) {
989 if ( ptr_APPEND_LIT( " size=" ) ) return -1;
992 if ( lim->lms_s_soft == -1
993 ? ptr_APPEND_LIT( "unlimited " )
994 : ptr_APPEND_FMT1( "%d ", lim->lms_s_soft ) )
998 if ( lim->lms_s_hard ) {
999 if ( ptr_APPEND_LIT( " size.hard=" ) ) return -1;
1000 if ( lim->lms_s_hard == -1
1001 ? ptr_APPEND_LIT( "unlimited " )
1002 : ptr_APPEND_FMT1( "%d ", lim->lms_s_hard ) )
1005 if ( lim->lms_s_unchecked != -1 ) {
1006 if ( ptr_APPEND_LIT( " size.unchecked=" ) ) return -1;
1007 if ( lim->lms_s_unchecked == 0
1008 ? ptr_APPEND_LIT( "disabled " )
1009 : ptr_APPEND_FMT1( "%d ", lim->lms_s_unchecked ) )
1012 if ( lim->lms_s_pr_hide ) {
1013 if ( ptr_APPEND_LIT( " size.pr=noEstimate " ) ) return -1;
1015 if ( lim->lms_s_pr ) {
1016 if ( ptr_APPEND_LIT( " size.pr=" ) ) return -1;
1017 if ( lim->lms_s_pr == -1
1018 ? ptr_APPEND_LIT( "unlimited " )
1019 : ptr_APPEND_FMT1( "%d ", lim->lms_s_pr ) )
1022 if ( lim->lms_s_pr_total ) {
1023 if ( ptr_APPEND_LIT( " size.prtotal=" ) ) return -1;
1024 if ( lim->lms_s_pr_total == -1 ? ptr_APPEND_LIT( "unlimited " )
1025 : lim->lms_s_pr_total == -2 ? ptr_APPEND_LIT( "disabled " )
1026 : ptr_APPEND_FMT1( "%d ", lim->lms_s_pr_total ) )
1031 if ( which & SLAP_LIMIT_TIME ) {
1032 if ( lim->lms_t_soft != SLAPD_DEFAULT_TIMELIMIT ) {
1034 /* If same as global limit, drop it */
1035 if ( lim != &frontendDB->be_def_limit &&
1036 lim->lms_t_soft == frontendDB->be_def_limit.lms_t_soft )
1040 /* If there's also a hard limit, fully qualify this one */
1041 } else if ( lim->lms_t_hard ) {
1042 if ( ptr_APPEND_LIT( " time.soft=" ) ) return -1;
1044 /* If doing both size & time, qualify this */
1045 } else if ( which & SLAP_LIMIT_SIZE ) {
1046 if ( ptr_APPEND_LIT( " time=" ) ) return -1;
1049 if ( lim->lms_t_soft == -1
1050 ? ptr_APPEND_LIT( "unlimited " )
1051 : ptr_APPEND_FMT1( "%d ", lim->lms_t_soft ) )
1055 if ( lim->lms_t_hard ) {
1056 if ( ptr_APPEND_LIT( " time.hard=" ) ) return -1;
1057 if ( lim->lms_t_hard == -1
1058 ? ptr_APPEND_LIT( "unlimited " )
1059 : ptr_APPEND_FMT1( "%d ", lim->lms_t_hard ) )
1063 if ( ptr != bv->bv_val ) {
1066 bv->bv_len = ptr - bv->bv_val;
1073 limits_check( Operation *op, SlapReply *rs )
1075 assert( op != NULL );
1076 assert( rs != NULL );
1077 /* FIXME: should this be always true? */
1078 assert( op->o_tag == LDAP_REQ_SEARCH);
1080 /* protocol only allows 0..maxInt;
1082 * internal searches:
1083 * - may use SLAP_NO_LIMIT ( = -1 ) to indicate no limits;
1084 * - should use slimit = N and tlimit = SLAP_NO_LIMIT to
1085 * indicate searches that should return exactly N matches,
1086 * and handle errors thru a callback (see for instance
1087 * slap_sasl_match() and slap_sasl2dn())
1089 if ( op->ors_tlimit == SLAP_NO_LIMIT && op->ors_slimit == SLAP_NO_LIMIT ) {
1093 /* allow root to set no limit */
1094 if ( be_isroot( op ) ) {
1095 op->ors_limit = NULL;
1097 if ( op->ors_tlimit == 0 ) {
1098 op->ors_tlimit = SLAP_NO_LIMIT;
1101 if ( op->ors_slimit == 0 ) {
1102 op->ors_slimit = SLAP_NO_LIMIT;
1105 /* if paged results and slimit are requested */
1106 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED &&
1107 op->ors_slimit != SLAP_NO_LIMIT ) {
1108 PagedResultsState *ps = op->o_pagedresults_state;
1109 int total = op->ors_slimit - ps->ps_count;
1111 op->ors_slimit = total;
1117 /* if not root, get appropriate limits */
1119 ( void ) limits_get( op, &op->ors_limit );
1121 assert( op->ors_limit != NULL );
1123 /* if no limit is required, use soft limit */
1124 if ( op->ors_tlimit == 0 ) {
1125 op->ors_tlimit = op->ors_limit->lms_t_soft;
1127 /* limit required: check if legal */
1129 if ( op->ors_limit->lms_t_hard == 0 ) {
1130 if ( op->ors_limit->lms_t_soft > 0
1131 && ( op->ors_tlimit > op->ors_limit->lms_t_soft ) ) {
1132 op->ors_tlimit = op->ors_limit->lms_t_soft;
1135 } else if ( op->ors_limit->lms_t_hard > 0 ) {
1136 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1137 if ( op->ors_tlimit == SLAP_MAX_LIMIT ) {
1138 op->ors_tlimit = op->ors_limit->lms_t_hard;
1140 } else if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
1141 /* error if exceeding hard limit */
1142 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1143 send_ldap_result( op, rs );
1144 rs->sr_err = LDAP_SUCCESS;
1147 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1148 if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
1149 op->ors_tlimit = op->ors_limit->lms_t_hard;
1151 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1155 /* else leave as is */
1157 /* don't even get to backend if candidate check is disabled */
1158 if ( op->ors_limit->lms_s_unchecked == 0 ) {
1159 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1160 send_ldap_result( op, rs );
1161 rs->sr_err = LDAP_SUCCESS;
1165 /* if paged results is requested */
1166 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
1169 PagedResultsState *ps = op->o_pagedresults_state;
1171 /* paged results is not allowed */
1172 if ( op->ors_limit->lms_s_pr_total == -2 ) {
1173 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1174 rs->sr_text = "pagedResults control not allowed";
1175 send_ldap_result( op, rs );
1176 rs->sr_err = LDAP_SUCCESS;
1181 if ( op->ors_limit->lms_s_pr > 0
1182 && ps->ps_size > op->ors_limit->lms_s_pr )
1184 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1185 rs->sr_text = "illegal pagedResults page size";
1186 send_ldap_result( op, rs );
1187 rs->sr_err = LDAP_SUCCESS;
1192 if ( op->ors_limit->lms_s_pr_total == 0 ) {
1193 if ( op->ors_limit->lms_s_hard == 0 ) {
1194 pr_total = op->ors_limit->lms_s_soft;
1196 pr_total = op->ors_limit->lms_s_hard;
1199 pr_total = op->ors_limit->lms_s_pr_total;
1202 if ( pr_total == -1 ) {
1203 if ( op->ors_slimit == 0 || op->ors_slimit == SLAP_MAX_LIMIT ) {
1207 slimit = op->ors_slimit - ps->ps_count;
1210 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1211 } else if ( pr_total > 0 && op->ors_slimit != SLAP_MAX_LIMIT
1212 && ( op->ors_slimit == SLAP_NO_LIMIT
1213 || op->ors_slimit > pr_total ) )
1215 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1216 send_ldap_result( op, rs );
1217 rs->sr_err = LDAP_SUCCESS;
1219 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1222 /* if no limit is required, use soft limit */
1226 /* first round of pagedResults:
1227 * set count to any appropriate limit */
1229 /* if the limit is set, check that it does
1230 * not violate any server-side limit */
1231 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1232 if ( op->ors_slimit == SLAP_MAX_LIMIT )
1233 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1234 if ( op->ors_slimit == SLAP_MAX_LIMIT
1235 || op->ors_slimit > pr_total )
1236 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1238 slimit2 = op->ors_slimit = pr_total;
1240 } else if ( op->ors_slimit == 0 ) {
1244 slimit2 = op->ors_slimit;
1247 total = slimit2 - ps->ps_count;
1250 if ( op->ors_limit->lms_s_pr > 0 ) {
1251 /* use the smallest limit set by total/per page */
1252 if ( total < op->ors_limit->lms_s_pr ) {
1256 /* use the perpage limit if any
1257 * NOTE: + 1 because given value must be legal */
1258 slimit = op->ors_limit->lms_s_pr + 1;
1262 /* use the total limit if any */
1266 } else if ( op->ors_limit->lms_s_pr > 0 ) {
1267 /* use the perpage limit if any
1268 * NOTE: + 1 because the given value must be legal */
1269 slimit = op->ors_limit->lms_s_pr + 1;
1272 /* use the standard hard/soft limit if any */
1273 slimit = op->ors_limit->lms_s_hard;
1277 /* if got any limit, use it */
1278 if ( slimit != -2 ) {
1279 if ( op->ors_slimit == 0 ) {
1280 op->ors_slimit = slimit;
1282 } else if ( slimit > 0 ) {
1283 if ( op->ors_slimit - ps->ps_count > slimit ) {
1284 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1285 send_ldap_result( op, rs );
1286 rs->sr_err = LDAP_SUCCESS;
1289 op->ors_slimit = slimit;
1291 } else if ( slimit == 0 ) {
1296 /* use the standard hard/soft limit if any */
1297 op->ors_slimit = pr_total;
1300 /* no limit requested: use soft, whatever it is */
1301 } else if ( op->ors_slimit == 0 ) {
1302 op->ors_slimit = op->ors_limit->lms_s_soft;
1304 /* limit requested: check if legal */
1306 /* hard limit as soft (traditional behavior) */
1307 if ( op->ors_limit->lms_s_hard == 0 ) {
1308 if ( op->ors_limit->lms_s_soft > 0
1309 && op->ors_slimit > op->ors_limit->lms_s_soft ) {
1310 op->ors_slimit = op->ors_limit->lms_s_soft;
1313 /* explicit hard limit: error if violated */
1314 } else if ( op->ors_limit->lms_s_hard > 0 ) {
1315 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1316 if ( op->ors_slimit == SLAP_MAX_LIMIT ) {
1317 op->ors_slimit = op->ors_limit->lms_s_hard;
1319 } else if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
1320 /* if limit exceeds hard, error */
1321 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1322 send_ldap_result( op, rs );
1323 rs->sr_err = LDAP_SUCCESS;
1326 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1327 if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
1328 op->ors_slimit = op->ors_limit->lms_s_hard;
1330 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1334 /* else leave as is */
1342 struct slap_limits **lm )
1350 for ( i = 0; lm[ i ]; i++ ) {
1351 switch ( lm[ i ]->lm_flags & SLAP_LIMITS_MASK ) {
1352 case SLAP_LIMITS_REGEX:
1353 regfree( &lm[ i ]->lm_regex );
1356 case SLAP_LIMITS_EXACT:
1357 case SLAP_LIMITS_ONE:
1358 case SLAP_LIMITS_SUBTREE:
1359 case SLAP_LIMITS_CHILDREN:
1360 if ( !BER_BVISNULL( &lm[ i ]->lm_pat ) ) {
1361 ch_free( lm[ i ]->lm_pat.bv_val );