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
31 static const char *const dn_source[2] = { "DN", "DN.THIS" };
35 limits2str( unsigned i )
38 case SLAP_LIMITS_UNDEFINED:
41 case SLAP_LIMITS_EXACT:
47 case SLAP_LIMITS_SUBTREE:
50 case SLAP_LIMITS_CHILDREN:
53 case SLAP_LIMITS_REGEX:
56 case SLAP_LIMITS_ANONYMOUS:
59 case SLAP_LIMITS_USERS:
73 struct slap_limits_set **limit
76 struct slap_limits **lm;
77 struct berval *ndns[2];
80 assert( limit != NULL );
83 ndns[1] = &op->o_req_ndn;
85 Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s self=\"%s\" this=\"%s\"\n",
87 BER_BVISNULL( ndns[0] ) ? "[anonymous]" : ndns[0]->bv_val,
88 BER_BVISNULL( ndns[1] ) ? "" : ndns[1]->bv_val );
92 *limit = &op->o_bd->be_def_limit;
94 if ( op->o_bd->be_limits == NULL ) {
98 for ( lm = op->o_bd->be_limits; lm[0] != NULL; lm++ ) {
99 unsigned style = lm[0]->lm_flags & SLAP_LIMITS_MASK;
100 unsigned type = lm[0]->lm_flags & SLAP_LIMITS_TYPE_MASK;
101 unsigned isthis = type == SLAP_LIMITS_TYPE_THIS;
102 struct berval *ndn = ndns[isthis];
105 case SLAP_LIMITS_EXACT:
106 if ( BER_BVISEMPTY( ndn ) ) {
110 if ( type == SLAP_LIMITS_TYPE_GROUP ) {
113 rc = backend_group( op, NULL,
116 lm[0]->lm_group_ad );
118 *limit = &lm[0]->lm_limits;
119 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=GROUP match=EXACT "
120 "dn=\"%s\" oc=\"%s\" ad=\"%s\"\n",
121 lm[0]->lm_pat.bv_val,
122 lm[0]->lm_group_oc->soc_cname.bv_val,
123 lm[0]->lm_group_ad->ad_cname.bv_val );
129 if ( dn_match( &lm[0]->lm_pat, ndn ) ) {
130 *limit = &lm[0]->lm_limits;
131 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=EXACT dn=\"%s\"\n",
132 dn_source[isthis], lm[0]->lm_pat.bv_val, 0 );
138 case SLAP_LIMITS_ONE:
139 case SLAP_LIMITS_SUBTREE:
140 case SLAP_LIMITS_CHILDREN: {
143 if ( BER_BVISEMPTY( ndn ) ) {
147 /* ndn shorter than dn_pat */
148 if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) {
151 d = ndn->bv_len - lm[0]->lm_pat.bv_len;
153 /* allow exact match for SUBTREE only */
155 if ( style != SLAP_LIMITS_SUBTREE ) {
159 /* check for unescaped rdn separator */
160 if ( !DN_SEPARATOR( ndn->bv_val[d - 1] ) ) {
165 /* in case of (sub)match ... */
166 if ( lm[0]->lm_pat.bv_len == ( ndn->bv_len - d )
167 && strcmp( lm[0]->lm_pat.bv_val,
168 &ndn->bv_val[d] ) == 0 )
170 /* check for exactly one rdn in case of ONE */
171 if ( style == SLAP_LIMITS_ONE ) {
173 * if ndn is more that one rdn
174 * below dn_pat, continue
176 if ( (size_t) dn_rdnlen( NULL, ndn )
183 *limit = &lm[0]->lm_limits;
184 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s dn=\"%s\"\n",
185 dn_source[isthis], limits2str( style ), lm[0]->lm_pat.bv_val );
192 case SLAP_LIMITS_REGEX:
193 if ( BER_BVISEMPTY( ndn ) ) {
196 if ( regexec( &lm[0]->lm_regex, ndn->bv_val,
199 *limit = &lm[0]->lm_limits;
200 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s dn=\"%s\"\n",
201 dn_source[isthis], limits2str( style ), lm[0]->lm_pat.bv_val );
206 case SLAP_LIMITS_ANONYMOUS:
207 if ( BER_BVISEMPTY( ndn ) ) {
208 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n",
209 dn_source[isthis], limits2str( style ), 0 );
210 *limit = &lm[0]->lm_limits;
215 case SLAP_LIMITS_USERS:
216 if ( !BER_BVISEMPTY( ndn ) ) {
217 *limit = &lm[0]->lm_limits;
218 Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n",
219 dn_source[isthis], limits2str( style ), 0 );
224 case SLAP_LIMITS_ANY:
225 *limit = &lm[0]->lm_limits;
229 assert( 0 ); /* unreachable */
242 ObjectClass *group_oc,
243 AttributeDescription *group_ad,
244 struct slap_limits_set *limit
248 struct slap_limits *lm;
249 unsigned type, style;
251 assert( be != NULL );
252 assert( limit != NULL );
254 type = flags & SLAP_LIMITS_TYPE_MASK;
255 style = flags & SLAP_LIMITS_MASK;
258 case SLAP_LIMITS_ANONYMOUS:
259 case SLAP_LIMITS_USERS:
260 case SLAP_LIMITS_ANY:
261 /* For these styles, type == 0 (SLAP_LIMITS_TYPE_SELF). */
262 for ( i = 0; be->be_limits && be->be_limits[ i ]; i++ ) {
263 if ( be->be_limits[ i ]->lm_flags == style ) {
271 lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
274 case SLAP_LIMITS_UNDEFINED:
275 style = SLAP_LIMITS_EXACT;
276 /* continue to next cases */
277 case SLAP_LIMITS_EXACT:
278 case SLAP_LIMITS_ONE:
279 case SLAP_LIMITS_SUBTREE:
280 case SLAP_LIMITS_CHILDREN:
281 lm->lm_flags = style | type;
286 ber_str2bv( pattern, 0, 0, &bv );
288 rc = dnNormalize( 0, NULL, NULL, &bv, &lm->lm_pat, NULL );
289 if ( rc != LDAP_SUCCESS ) {
296 case SLAP_LIMITS_REGEX:
297 lm->lm_flags = style | type;
298 ber_str2bv( pattern, 0, 1, &lm->lm_pat );
299 if ( regcomp( &lm->lm_regex, lm->lm_pat.bv_val,
300 REG_EXTENDED | REG_ICASE ) ) {
301 free( lm->lm_pat.bv_val );
307 case SLAP_LIMITS_ANONYMOUS:
308 case SLAP_LIMITS_USERS:
309 case SLAP_LIMITS_ANY:
310 lm->lm_flags = style | type;
311 BER_BVZERO( &lm->lm_pat );
316 case SLAP_LIMITS_TYPE_GROUP:
317 assert( group_oc != NULL );
318 assert( group_ad != NULL );
319 lm->lm_group_oc = group_oc;
320 lm->lm_group_ad = group_ad;
324 lm->lm_limits = *limit;
327 if ( be->be_limits != NULL ) {
328 for ( ; be->be_limits[i]; i++ );
331 be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits,
332 sizeof( struct slap_limits * ) * ( i + 2 ) );
333 be->be_limits[i] = lm;
334 be->be_limits[i+1] = NULL;
348 int flags = SLAP_LIMITS_UNDEFINED;
350 struct slap_limits_set limit;
352 ObjectClass *group_oc = NULL;
353 AttributeDescription *group_ad = NULL;
355 assert( be != NULL );
358 Debug( LDAP_DEBUG_ANY,
359 "%s : line %d: missing arg(s) in "
360 "\"limits <pattern> <limits>\" line.\n%s",
365 limit = be->be_def_limit;
370 * "limits" <pattern> <limit> [ ... ]
377 * [ "dn" [ "." { "this" | "self" } ] [ "." { "exact" | "base" |
378 * "onelevel" | "subtree" | "children" | "regex" | "anonymous" } ]
382 * "this" is the baseobject, "self" (the default) is the bound DN
383 * "exact" and "base" are the same (exact match);
384 * "onelevel" means exactly one rdn below, NOT including pattern
385 * "subtree" means any rdn below, including pattern
386 * "children" means any rdn below, NOT including pattern
388 * "anonymous" may be deprecated in favour
389 * of the pattern = "anonymous" form
391 * "group[/objectClass[/attributeType]]" "=" "<dn pattern>"
395 * "time" [ "." { "soft" | "hard" } ] "=" <integer>
397 * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
401 if ( strcmp( pattern, "*" ) == 0) {
402 flags = SLAP_LIMITS_ANY;
404 } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
405 flags = SLAP_LIMITS_ANONYMOUS;
407 } else if ( strcasecmp( pattern, "users" ) == 0 ) {
408 flags = SLAP_LIMITS_USERS;
410 } else if ( strncasecmp( pattern, "dn", STRLENOF( "dn" ) ) == 0 ) {
411 pattern += STRLENOF( "dn" );
412 flags = SLAP_LIMITS_TYPE_SELF;
413 if ( pattern[0] == '.' ) {
415 if ( strncasecmp( pattern, "this", STRLENOF( "this" )) == 0 ) {
416 flags = SLAP_LIMITS_TYPE_THIS;
417 pattern += STRLENOF( "this" );
418 } else if ( strncasecmp( pattern, "self", STRLENOF( "self" )) == 0 ) {
419 pattern += STRLENOF( "self" );
424 if ( pattern[0] == '.' ) {
427 if ( strncasecmp( pattern, "exact", STRLENOF( "exact" )) == 0 ) {
428 flags |= SLAP_LIMITS_EXACT;
429 pattern += STRLENOF( "exact" );
431 } else if ( strncasecmp( pattern, "base", STRLENOF( "base" ) ) == 0 ) {
432 flags |= SLAP_LIMITS_BASE;
433 pattern += STRLENOF( "base" );
435 } else if ( strncasecmp( pattern, "one", STRLENOF( "one" ) ) == 0 ) {
436 flags |= SLAP_LIMITS_ONE;
437 pattern += STRLENOF( "one" );
438 if ( strncasecmp( pattern, "level", STRLENOF( "level" ) ) == 0 ) {
439 pattern += STRLENOF( "level" );
442 Debug( LDAP_DEBUG_ANY,
443 "%s : line %d: deprecated \"one\" style "
444 "\"limits <pattern> <limits>\" line; "
445 "use \"onelevel\" instead.\n", fname, lineno, 0 );
448 } else if ( strncasecmp( pattern, "sub", STRLENOF( "sub" ) ) == 0 ) {
449 flags |= SLAP_LIMITS_SUBTREE;
450 pattern += STRLENOF( "sub" );
451 if ( strncasecmp( pattern, "tree", STRLENOF( "tree" ) ) == 0 ) {
452 pattern += STRLENOF( "tree" );
455 Debug( LDAP_DEBUG_ANY,
456 "%s : line %d: deprecated \"sub\" style "
457 "\"limits <pattern> <limits>\" line; "
458 "use \"subtree\" instead.\n", fname, lineno, 0 );
461 } else if ( strncasecmp( pattern, "children", STRLENOF( "children" ) ) == 0 ) {
462 flags |= SLAP_LIMITS_CHILDREN;
463 pattern += STRLENOF( "children" );
465 } else if ( strncasecmp( pattern, "regex", STRLENOF( "regex" ) ) == 0 ) {
466 flags |= SLAP_LIMITS_REGEX;
467 pattern += STRLENOF( "regex" );
470 * this could be deprecated in favour
471 * of the pattern = "anonymous" form
473 } else if ( strncasecmp( pattern, "anonymous", STRLENOF( "anonymous" ) ) == 0
474 && flags == SLAP_LIMITS_TYPE_SELF )
476 flags = SLAP_LIMITS_ANONYMOUS;
481 /* pre-check the data */
483 case SLAP_LIMITS_ANONYMOUS:
484 case SLAP_LIMITS_USERS:
486 /* no need for pattern */
491 if ( pattern[0] != '=' ) {
492 Debug( LDAP_DEBUG_ANY,
493 "%s : line %d: missing '=' in "
494 "\"dn[.{this|self}][.{exact|base"
495 "|onelevel|subtree|children|regex}]"
497 "\"limits <pattern> <limits>\" "
503 /* skip '=' (required) */
506 /* trim obvious cases */
507 if ( strcmp( pattern, "*" ) == 0 ) {
508 flags = SLAP_LIMITS_ANY;
511 } else if ( flags == SLAP_LIMITS_REGEX
512 && strcmp( pattern, ".*" ) == 0 ) {
513 flags = SLAP_LIMITS_ANY;
518 } else if (strncasecmp( pattern, "group", STRLENOF( "group" ) ) == 0 ) {
519 pattern += STRLENOF( "group" );
521 if ( pattern[0] == '/' ) {
522 struct berval oc, ad;
524 oc.bv_val = pattern + 1;
525 pattern = strchr( pattern, '=' );
526 if ( pattern == NULL ) {
530 ad.bv_val = strchr( oc.bv_val, '/' );
531 if ( ad.bv_val != NULL ) {
532 const char *text = NULL;
534 oc.bv_len = ad.bv_val - oc.bv_val;
537 ad.bv_len = pattern - ad.bv_val;
538 rc = slap_bv2ad( &ad, &group_ad, &text );
539 if ( rc != LDAP_SUCCESS ) {
544 oc.bv_len = pattern - oc.bv_val;
547 group_oc = oc_bvfind( &oc );
548 if ( group_oc == NULL ) {
553 if ( group_oc == NULL ) {
554 group_oc = oc_find( SLAPD_GROUP_CLASS );
555 if ( group_oc == NULL ) {
561 if ( group_ad == NULL ) {
562 const char *text = NULL;
564 rc = slap_str2ad( SLAPD_GROUP_ATTR, &group_ad, &text );
566 if ( rc != LDAP_SUCCESS ) {
572 flags = SLAP_LIMITS_TYPE_GROUP | SLAP_LIMITS_EXACT;
574 if ( pattern[0] != '=' ) {
575 Debug( LDAP_DEBUG_ANY,
576 "%s : line %d: missing '=' in "
577 "\"group[/objectClass[/attributeType]]"
579 "\"limits <pattern> <limits>\" line.\n",
584 /* skip '=' (required) */
589 for ( i = 2; i < argc; i++ ) {
590 if ( limits_parse_one( argv[i], &limit ) ) {
592 Debug( LDAP_DEBUG_ANY,
593 "%s : line %d: unknown limit values \"%s\" in "
594 "\"limits <pattern> <limits>\" line.\n",
595 fname, lineno, argv[i] );
604 * FIXME: add warnings?
606 if ( limit.lms_t_hard > 0 &&
607 ( limit.lms_t_hard < limit.lms_t_soft
608 || limit.lms_t_soft == -1 ) ) {
609 limit.lms_t_hard = limit.lms_t_soft;
612 if ( limit.lms_s_hard > 0 &&
613 ( limit.lms_s_hard < limit.lms_s_soft
614 || limit.lms_s_soft == -1 ) ) {
615 limit.lms_s_hard = limit.lms_s_soft;
624 * > 0 => limit (in seconds)
629 * > 0 => limit (in entries)
632 * -2 => disable the control
635 * > 0 => limit (in entries)
640 * > 0 => limit size (in entries)
642 if ( limit.lms_s_pr_total > 0 &&
643 limit.lms_s_pr > limit.lms_s_pr_total ) {
644 limit.lms_s_pr = limit.lms_s_pr_total;
647 rc = limits_add( be, flags, pattern, group_oc, group_ad, &limit );
650 Debug( LDAP_DEBUG_ANY,
651 "%s : line %d: unable to add limit in "
652 "\"limits <pattern> <limits>\" line.\n",
662 struct slap_limits_set *limit
665 assert( arg != NULL );
666 assert( limit != NULL );
668 if ( strncasecmp( arg, "time", STRLENOF( "time" ) ) == 0 ) {
669 arg += STRLENOF( "time" );
671 if ( arg[0] == '.' ) {
673 if ( strncasecmp( arg, "soft=", STRLENOF( "soft=" ) ) == 0 ) {
674 arg += STRLENOF( "soft=" );
675 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
676 limit->lms_t_soft = -1;
681 if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
686 /* FIXME: use "unlimited" instead; issue warning? */
689 limit->lms_t_soft = soft;
692 } else if ( strncasecmp( arg, "hard=", STRLENOF( "hard=" ) ) == 0 ) {
693 arg += STRLENOF( "hard=" );
694 if ( strcasecmp( arg, "soft" ) == 0 ) {
695 limit->lms_t_hard = 0;
697 } else if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
698 limit->lms_t_hard = -1;
703 if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
708 /* FIXME: use "unlimited" instead */
712 /* FIXME: use "soft" instead */
715 limit->lms_t_hard = hard;
722 } else if ( arg[0] == '=' ) {
724 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
725 limit->lms_t_soft = -1;
728 if ( lutil_atoi( &limit->lms_t_soft, arg ) != 0
729 || limit->lms_t_soft < -1 )
734 limit->lms_t_hard = 0;
740 } else if ( strncasecmp( arg, "size", STRLENOF( "size" ) ) == 0 ) {
741 arg += STRLENOF( "size" );
743 if ( arg[0] == '.' ) {
745 if ( strncasecmp( arg, "soft=", STRLENOF( "soft=" ) ) == 0 ) {
746 arg += STRLENOF( "soft=" );
747 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
748 limit->lms_s_soft = -1;
753 if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
758 /* FIXME: use "unlimited" instead */
761 limit->lms_s_soft = soft;
764 } else if ( strncasecmp( arg, "hard=", STRLENOF( "hard=" ) ) == 0 ) {
765 arg += STRLENOF( "hard=" );
766 if ( strcasecmp( arg, "soft" ) == 0 ) {
767 limit->lms_s_hard = 0;
769 } else if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
770 limit->lms_s_hard = -1;
775 if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
780 /* FIXME: use "unlimited" instead */
784 /* FIXME: use "soft" instead */
787 limit->lms_s_hard = hard;
790 } else if ( strncasecmp( arg, "unchecked=", STRLENOF( "unchecked=" ) ) == 0 ) {
791 arg += STRLENOF( "unchecked=" );
792 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
793 limit->lms_s_unchecked = -1;
795 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
796 limit->lms_s_unchecked = 0;
801 if ( lutil_atoi( &unchecked, arg ) != 0 || unchecked < -1 ) {
805 if ( unchecked == -1 ) {
806 /* FIXME: use "unlimited" instead */
809 limit->lms_s_unchecked = unchecked;
812 } else if ( strncasecmp( arg, "pr=", STRLENOF( "pr=" ) ) == 0 ) {
813 arg += STRLENOF( "pr=" );
814 if ( strcasecmp( arg, "noEstimate" ) == 0 ) {
815 limit->lms_s_pr_hide = 1;
817 } else if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
818 limit->lms_s_pr = -1;
823 if ( lutil_atoi( &pr, arg ) != 0 || pr < -1 ) {
828 /* FIXME: use "unlimited" instead */
831 limit->lms_s_pr = pr;
834 } else if ( strncasecmp( arg, "prtotal=", STRLENOF( "prtotal=" ) ) == 0 ) {
835 arg += STRLENOF( "prtotal=" );
837 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
838 limit->lms_s_pr_total = -1;
840 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
841 limit->lms_s_pr_total = -2;
843 } else if ( strcasecmp( arg, "hard" ) == 0 ) {
844 limit->lms_s_pr_total = 0;
849 if ( lutil_atoi( &total, arg ) != 0 || total < -1 ) {
854 /* FIXME: use "unlimited" instead */
858 /* FIXME: use "pr=disable" instead */
861 limit->lms_s_pr_total = total;
868 } else if ( arg[0] == '=' ) {
870 if ( strcasecmp( arg, "unlimited" ) == 0 || 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 static const char *lmpats[] = {
902 #define WHATSLEFT ( buflen - ( ptr - bv->bv_val ) )
904 /* Caller must provide an adequately sized buffer in bv */
906 limits_unparse( struct slap_limits *lim, struct berval *bv, ber_len_t buflen )
910 int type, lm, dntypelen;
912 if ( !bv || !bv->bv_val ) return -1;
915 type = lim->lm_flags & SLAP_LIMITS_TYPE_MASK;
917 if ( type == SLAP_LIMITS_TYPE_GROUP ) {
918 if ( WHATSLEFT <= STRLENOF( "group/" "/" "=\"" "\"" )
919 + lim->lm_group_oc->soc_cname.bv_len
920 + lim->lm_group_ad->ad_cname.bv_len
921 + lim->lm_pat.bv_len ) return -1;
923 ptr = lutil_strcopy( ptr, "group/" );
924 ptr = lutil_strcopy( ptr, lim->lm_group_oc->soc_cname.bv_val );
926 ptr = lutil_strcopy( ptr, lim->lm_group_ad->ad_cname.bv_val );
927 ptr = lutil_strcopy( ptr, "=\"" );
928 ptr = lutil_strcopy( ptr, lim->lm_pat.bv_val );
931 lm = lim->lm_flags & SLAP_LIMITS_MASK;
933 case SLAP_LIMITS_ANONYMOUS:
934 case SLAP_LIMITS_USERS:
935 case SLAP_LIMITS_ANY:
936 if ( WHATSLEFT <= strlen( lmpats[lm] ) ) return -1;
937 ptr = lutil_strcopy( ptr, lmpats[lm] );
939 case SLAP_LIMITS_UNDEFINED:
940 case SLAP_LIMITS_EXACT:
941 case SLAP_LIMITS_ONE:
942 case SLAP_LIMITS_SUBTREE:
943 case SLAP_LIMITS_CHILDREN:
944 case SLAP_LIMITS_REGEX:
945 dntypelen = type == SLAP_LIMITS_TYPE_SELF
946 ? STRLENOF( "dn." ) : STRLENOF( "dn.this." );
947 if ( WHATSLEFT <= dntypelen + STRLENOF( "=" "\"" "\"" )
948 + strlen( lmpats[lm] ) + lim->lm_pat.bv_len ) return -1;
949 ptr = lutil_strncopy( ptr, "dn.this.", dntypelen );
950 ptr = lutil_strcopy( ptr, lmpats[lm] );
953 ptr = lutil_strcopy( ptr, lim->lm_pat.bv_val );
958 bv->bv_len = ptr - bv->bv_val;
961 if ( limits_unparse_one( &lim->lm_limits,
962 SLAP_LIMIT_SIZE|SLAP_LIMIT_TIME,
967 bv->bv_len += btmp.bv_len;
971 /* Caller must provide an adequately sized buffer in bv */
973 limits_unparse_one( struct slap_limits_set *lim, int which, struct berval *bv, ber_len_t buflen )
977 if ( !bv || !bv->bv_val ) return -1;
981 if ( which & SLAP_LIMIT_SIZE ) {
982 if ( lim->lms_s_soft != SLAPD_DEFAULT_SIZELIMIT ) {
984 /* If same as global limit, drop it */
985 if ( lim != &frontendDB->be_def_limit &&
986 lim->lms_s_soft == frontendDB->be_def_limit.lms_s_soft )
989 /* If there's also a hard limit, fully qualify this one */
990 } else if ( lim->lms_s_hard ) {
991 if ( WHATSLEFT <= STRLENOF( " size.soft=" ) ) return -1;
992 ptr = lutil_strcopy( ptr, " size.soft=" );
994 /* If doing both size & time, qualify this */
995 } else if ( which & SLAP_LIMIT_TIME ) {
996 if ( WHATSLEFT <= STRLENOF( " size=" ) ) return -1;
997 ptr = lutil_strcopy( ptr, " size=" );
1000 if ( lim->lms_s_soft == -1 ) {
1001 if ( WHATSLEFT <= STRLENOF( "unlimited" ) ) return -1;
1002 ptr = lutil_strcopy( ptr, "unlimited" );
1004 ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_s_soft );
1005 if ( WHATSLEFT < 0 ) return -1;
1010 if ( lim->lms_s_hard ) {
1011 if ( WHATSLEFT <= STRLENOF( " size.hard=" ) ) return -1;
1012 ptr = lutil_strcopy( ptr, " size.hard=" );
1013 if ( lim->lms_s_hard == -1 ) {
1014 if ( WHATSLEFT <= STRLENOF( "unlimited" ) ) return -1;
1015 ptr = lutil_strcopy( ptr, "unlimited" );
1017 ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_s_hard );
1018 if ( WHATSLEFT < 0 ) return -1;
1022 if ( lim->lms_s_unchecked != -1 ) {
1023 if ( WHATSLEFT <= STRLENOF( " size.unchecked=" ) ) return -1;
1024 ptr = lutil_strcopy( ptr, " size.unchecked=" );
1025 if ( lim->lms_s_unchecked == 0 ) {
1026 if ( WHATSLEFT <= STRLENOF( "disabled" ) ) return -1;
1027 ptr = lutil_strcopy( ptr, "disabled" );
1029 ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_s_unchecked );
1030 if ( WHATSLEFT < 0 ) return -1;
1034 if ( lim->lms_s_pr_hide ) {
1035 if ( WHATSLEFT <= STRLENOF( " size.pr=noEstimate " ) ) return -1;
1036 ptr = lutil_strcopy( ptr, " size.pr=noEstimate " );
1038 if ( lim->lms_s_pr ) {
1039 if ( WHATSLEFT <= STRLENOF( " size.pr=" ) ) return -1;
1040 ptr = lutil_strcopy( ptr, " size.pr=" );
1041 if ( lim->lms_s_pr == -1 ) {
1042 if ( WHATSLEFT <= STRLENOF( "unlimited" ) ) return -1;
1043 ptr = lutil_strcopy( ptr, "unlimited" );
1045 ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_s_pr );
1046 if ( WHATSLEFT < 0 ) return -1;
1050 if ( lim->lms_s_pr_total ) {
1051 if ( WHATSLEFT <= STRLENOF( " size.prtotal=" ) ) return -1;
1052 ptr = lutil_strcopy( ptr, " size.prtotal=" );
1053 if ( lim->lms_s_pr_total == -1 ) {
1054 if ( WHATSLEFT <= STRLENOF( "unlimited" ) ) return -1;
1055 ptr = lutil_strcopy( ptr, "unlimited" );
1056 } else if ( lim->lms_s_pr_total == -2 ) {
1057 if ( WHATSLEFT <= STRLENOF( "disabled" ) ) return -1;
1058 ptr = lutil_strcopy( ptr, "disabled" );
1060 ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_s_pr_total );
1061 if ( WHATSLEFT < 0 ) return -1;
1067 if ( which & SLAP_LIMIT_TIME ) {
1068 if ( lim->lms_t_soft != SLAPD_DEFAULT_TIMELIMIT ) {
1070 /* If same as global limit, drop it */
1071 if ( lim != &frontendDB->be_def_limit &&
1072 lim->lms_t_soft == frontendDB->be_def_limit.lms_t_soft )
1076 /* If there's also a hard limit, fully qualify this one */
1077 } else if ( lim->lms_t_hard ) {
1078 if ( WHATSLEFT <= STRLENOF( " time.soft=" ) ) return -1;
1079 ptr = lutil_strcopy( ptr, " time.soft=" );
1081 /* If doing both size & time, qualify this */
1082 } else if ( which & SLAP_LIMIT_SIZE ) {
1083 if ( WHATSLEFT <= STRLENOF( " time=" ) ) return -1;
1084 ptr = lutil_strcopy( ptr, " time=" );
1087 if ( lim->lms_t_soft == -1 ) {
1088 if ( WHATSLEFT <= STRLENOF( "unlimited" ) ) return -1;
1089 ptr = lutil_strcopy( ptr, "unlimited" );
1091 ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_t_soft );
1092 if ( WHATSLEFT < 0 ) return -1;
1097 if ( lim->lms_t_hard ) {
1098 if ( WHATSLEFT <= STRLENOF( " time.hard=" ) ) return -1;
1099 ptr = lutil_strcopy( ptr, " time.hard=" );
1100 if ( lim->lms_t_hard == -1 ) {
1101 if ( WHATSLEFT <= STRLENOF( "unlimited" ) ) return -1;
1102 ptr = lutil_strcopy( ptr, "unlimited" );
1104 ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_t_hard );
1105 if ( WHATSLEFT < 0 ) return -1;
1110 if ( ptr != bv->bv_val ) {
1113 bv->bv_len = ptr - bv->bv_val;
1120 limits_check( Operation *op, SlapReply *rs )
1122 assert( op != NULL );
1123 assert( rs != NULL );
1124 /* FIXME: should this be always true? */
1125 assert( op->o_tag == LDAP_REQ_SEARCH);
1127 /* protocol only allows 0..maxInt;
1129 * internal searches:
1130 * - may use SLAP_NO_LIMIT ( = -1 ) to indicate no limits;
1131 * - should use slimit = N and tlimit = SLAP_NO_LIMIT to
1132 * indicate searches that should return exactly N matches,
1133 * and handle errors thru a callback (see for instance
1134 * slap_sasl_match() and slap_sasl2dn())
1136 if ( op->ors_tlimit == SLAP_NO_LIMIT && op->ors_slimit == SLAP_NO_LIMIT ) {
1140 /* allow root to set no limit */
1141 if ( be_isroot( op ) ) {
1142 op->ors_limit = NULL;
1144 if ( op->ors_tlimit == 0 ) {
1145 op->ors_tlimit = SLAP_NO_LIMIT;
1148 if ( op->ors_slimit == 0 ) {
1149 op->ors_slimit = SLAP_NO_LIMIT;
1152 /* if paged results and slimit are requested */
1153 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED &&
1154 op->ors_slimit != SLAP_NO_LIMIT ) {
1155 PagedResultsState *ps = op->o_pagedresults_state;
1156 int total = op->ors_slimit - ps->ps_count;
1158 op->ors_slimit = total;
1164 /* if not root, get appropriate limits */
1166 ( void ) limits_get( op, &op->ors_limit );
1168 assert( op->ors_limit != NULL );
1170 /* if no limit is required, use soft limit */
1171 if ( op->ors_tlimit == 0 ) {
1172 op->ors_tlimit = op->ors_limit->lms_t_soft;
1174 /* limit required: check if legal */
1176 if ( op->ors_limit->lms_t_hard == 0 ) {
1177 if ( op->ors_limit->lms_t_soft > 0
1178 && ( op->ors_tlimit > op->ors_limit->lms_t_soft ) ) {
1179 op->ors_tlimit = op->ors_limit->lms_t_soft;
1182 } else if ( op->ors_limit->lms_t_hard > 0 ) {
1183 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1184 if ( op->ors_tlimit == SLAP_MAX_LIMIT ) {
1185 op->ors_tlimit = op->ors_limit->lms_t_hard;
1187 } else if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
1188 /* error if exceeding hard limit */
1189 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1190 send_ldap_result( op, rs );
1191 rs->sr_err = LDAP_SUCCESS;
1194 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1195 if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
1196 op->ors_tlimit = op->ors_limit->lms_t_hard;
1198 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1202 /* else leave as is */
1204 /* don't even get to backend if candidate check is disabled */
1205 if ( op->ors_limit->lms_s_unchecked == 0 ) {
1206 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1207 send_ldap_result( op, rs );
1208 rs->sr_err = LDAP_SUCCESS;
1212 /* if paged results is requested */
1213 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
1216 PagedResultsState *ps = op->o_pagedresults_state;
1218 /* paged results is not allowed */
1219 if ( op->ors_limit->lms_s_pr_total == -2 ) {
1220 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1221 rs->sr_text = "pagedResults control not allowed";
1222 send_ldap_result( op, rs );
1223 rs->sr_err = LDAP_SUCCESS;
1228 if ( op->ors_limit->lms_s_pr > 0 && ps->ps_size > op->ors_limit->lms_s_pr ) {
1229 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1230 rs->sr_text = "illegal pagedResults page size";
1231 send_ldap_result( op, rs );
1232 rs->sr_err = LDAP_SUCCESS;
1237 if ( op->ors_limit->lms_s_pr_total == 0 ) {
1238 if ( op->ors_limit->lms_s_hard == 0 ) {
1239 pr_total = op->ors_limit->lms_s_soft;
1241 pr_total = op->ors_limit->lms_s_hard;
1244 pr_total = op->ors_limit->lms_s_pr_total;
1247 if ( pr_total == -1 ) {
1248 if ( op->ors_slimit == 0 || op->ors_slimit == SLAP_MAX_LIMIT ) {
1252 slimit = op->ors_slimit - ps->ps_count;
1255 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1256 } else if ( pr_total > 0 && op->ors_slimit != SLAP_MAX_LIMIT
1257 && ( op->ors_slimit == SLAP_NO_LIMIT || op->ors_slimit > pr_total ) )
1259 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1260 send_ldap_result( op, rs );
1261 rs->sr_err = LDAP_SUCCESS;
1263 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1266 /* if no limit is required, use soft limit */
1270 /* first round of pagedResults: set count to any appropriate limit */
1272 /* if the limit is set, check that it does not violate any server-side limit */
1273 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1274 if ( op->ors_slimit == SLAP_MAX_LIMIT ) {
1275 slimit2 = op->ors_slimit = pr_total;
1276 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1277 if ( op->ors_slimit == SLAP_MAX_LIMIT || op->ors_slimit > pr_total ) {
1278 slimit2 = op->ors_slimit = pr_total;
1279 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1281 } else if ( op->ors_slimit == 0 ) {
1285 slimit2 = op->ors_slimit;
1288 total = slimit2 - ps->ps_count;
1291 if ( op->ors_limit->lms_s_pr > 0 ) {
1292 /* use the smallest limit set by total/per page */
1293 if ( total < op->ors_limit->lms_s_pr ) {
1297 /* use the perpage limit if any
1298 * NOTE: + 1 because the given value must be legal */
1299 slimit = op->ors_limit->lms_s_pr + 1;
1303 /* use the total limit if any */
1307 } else if ( op->ors_limit->lms_s_pr > 0 ) {
1308 /* use the perpage limit if any
1309 * NOTE: + 1 because the given value must be legal */
1310 slimit = op->ors_limit->lms_s_pr + 1;
1313 /* use the standard hard/soft limit if any */
1314 slimit = op->ors_limit->lms_s_hard;
1318 /* if got any limit, use it */
1319 if ( slimit != -2 ) {
1320 if ( op->ors_slimit == 0 ) {
1321 op->ors_slimit = slimit;
1323 } else if ( slimit > 0 ) {
1324 if ( op->ors_slimit - ps->ps_count > slimit ) {
1325 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1326 send_ldap_result( op, rs );
1327 rs->sr_err = LDAP_SUCCESS;
1330 op->ors_slimit = slimit;
1332 } else if ( slimit == 0 ) {
1337 /* use the standard hard/soft limit if any */
1338 op->ors_slimit = pr_total;
1341 /* no limit requested: use soft, whatever it is */
1342 } else if ( op->ors_slimit == 0 ) {
1343 op->ors_slimit = op->ors_limit->lms_s_soft;
1345 /* limit requested: check if legal */
1347 /* hard limit as soft (traditional behavior) */
1348 if ( op->ors_limit->lms_s_hard == 0 ) {
1349 if ( op->ors_limit->lms_s_soft > 0
1350 && op->ors_slimit > op->ors_limit->lms_s_soft ) {
1351 op->ors_slimit = op->ors_limit->lms_s_soft;
1354 /* explicit hard limit: error if violated */
1355 } else if ( op->ors_limit->lms_s_hard > 0 ) {
1356 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1357 if ( op->ors_slimit == SLAP_MAX_LIMIT ) {
1358 op->ors_slimit = op->ors_limit->lms_s_hard;
1360 } else if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
1361 /* if limit exceeds hard, error */
1362 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1363 send_ldap_result( op, rs );
1364 rs->sr_err = LDAP_SUCCESS;
1367 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1368 if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
1369 op->ors_slimit = op->ors_limit->lms_s_hard;
1371 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1375 /* else leave as is */
1383 struct slap_limits **lm )
1391 for ( i = 0; lm[ i ]; i++ ) {
1392 switch ( lm[ i ]->lm_flags & SLAP_LIMITS_MASK ) {
1393 case SLAP_LIMITS_REGEX:
1394 regfree( &lm[ i ]->lm_regex );
1397 case SLAP_LIMITS_EXACT:
1398 case SLAP_LIMITS_ONE:
1399 case SLAP_LIMITS_SUBTREE:
1400 case SLAP_LIMITS_CHILDREN:
1401 if ( !BER_BVISNULL( &lm[ i ]->lm_pat ) ) {
1402 ch_free( lm[ i ]->lm_pat.bv_val );