1 /* limits.c - routines to handle regex-based size and time limits */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/limits.c,v 1.73.2.6 2008/02/11 23:26:44 kurt Exp $ */
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 limits2str( unsigned i
)
34 case SLAP_LIMITS_UNDEFINED
:
37 case SLAP_LIMITS_EXACT
:
43 case SLAP_LIMITS_SUBTREE
:
46 case SLAP_LIMITS_CHILDREN
:
49 case SLAP_LIMITS_REGEX
:
52 case SLAP_LIMITS_ANONYMOUS
:
55 case SLAP_LIMITS_USERS
:
70 struct slap_limits_set
**limit
73 struct slap_limits
**lm
;
76 assert( limit
!= NULL
);
78 Debug( LDAP_DEBUG_TRACE
, "==> limits_get: %s dn=\"%s\"\n",
80 BER_BVISNULL( ndn
) ? "[anonymous]" : ndn
->bv_val
, 0 );
84 *limit
= &op
->o_bd
->be_def_limit
;
86 if ( op
->o_bd
->be_limits
== NULL
) {
90 for ( lm
= op
->o_bd
->be_limits
; lm
[0] != NULL
; lm
++ ) {
91 unsigned style
= lm
[0]->lm_flags
& SLAP_LIMITS_MASK
;
92 unsigned type
= lm
[0]->lm_flags
& SLAP_LIMITS_TYPE_MASK
;
95 case SLAP_LIMITS_EXACT
:
96 if ( BER_BVISEMPTY( ndn
) ) {
100 if ( type
== SLAP_LIMITS_TYPE_GROUP
) {
103 rc
= backend_group( op
, NULL
,
106 lm
[0]->lm_group_ad
);
108 *limit
= &lm
[0]->lm_limits
;
109 Debug( LDAP_DEBUG_TRACE
, "<== limits_get: type=GROUP match=EXACT "
110 "dn=\"%s\" oc=\"%s\" ad=\"%s\"\n",
111 lm
[0]->lm_pat
.bv_val
,
112 lm
[0]->lm_group_oc
->soc_cname
.bv_val
,
113 lm
[0]->lm_group_ad
->ad_cname
.bv_val
);
119 if ( dn_match( &lm
[0]->lm_pat
, ndn
) ) {
120 *limit
= &lm
[0]->lm_limits
;
121 Debug( LDAP_DEBUG_TRACE
, "<== limits_get: type=DN match=EXACT dn=\"%s\"\n",
122 lm
[0]->lm_pat
.bv_val
, 0, 0 );
128 case SLAP_LIMITS_ONE
:
129 case SLAP_LIMITS_SUBTREE
:
130 case SLAP_LIMITS_CHILDREN
: {
133 if ( BER_BVISEMPTY( ndn
) ) {
137 /* ndn shorter than dn_pat */
138 if ( ndn
->bv_len
< lm
[0]->lm_pat
.bv_len
) {
141 d
= ndn
->bv_len
- lm
[0]->lm_pat
.bv_len
;
143 /* allow exact match for SUBTREE only */
145 if ( style
!= SLAP_LIMITS_SUBTREE
) {
149 /* check for unescaped rdn separator */
150 if ( !DN_SEPARATOR( ndn
->bv_val
[d
- 1] ) ) {
155 /* in case of (sub)match ... */
156 if ( lm
[0]->lm_pat
.bv_len
== ( ndn
->bv_len
- d
)
157 && strcmp( lm
[0]->lm_pat
.bv_val
,
158 &ndn
->bv_val
[d
] ) == 0 )
160 /* check for exactly one rdn in case of ONE */
161 if ( style
== SLAP_LIMITS_ONE
) {
163 * if ndn is more that one rdn
164 * below dn_pat, continue
166 if ( (size_t) dn_rdnlen( NULL
, ndn
)
173 *limit
= &lm
[0]->lm_limits
;
174 Debug( LDAP_DEBUG_TRACE
, "<== limits_get: type=DN match=%s dn=\"%s\"\n",
175 limits2str( style
), lm
[0]->lm_pat
.bv_val
, 0 );
182 case SLAP_LIMITS_REGEX
:
183 if ( BER_BVISEMPTY( ndn
) ) {
186 if ( regexec( &lm
[0]->lm_regex
, ndn
->bv_val
,
189 *limit
= &lm
[0]->lm_limits
;
190 Debug( LDAP_DEBUG_TRACE
, "<== limits_get: type=DN match=%s dn=\"%s\"\n",
191 limits2str( style
), lm
[0]->lm_pat
.bv_val
, 0 );
196 case SLAP_LIMITS_ANONYMOUS
:
197 if ( BER_BVISEMPTY( ndn
) ) {
198 Debug( LDAP_DEBUG_TRACE
, "<== limits_get: type=DN match=%s\n",
199 limits2str( style
), 0, 0 );
200 *limit
= &lm
[0]->lm_limits
;
205 case SLAP_LIMITS_USERS
:
206 if ( !BER_BVISEMPTY( ndn
) ) {
207 *limit
= &lm
[0]->lm_limits
;
208 Debug( LDAP_DEBUG_TRACE
, "<== limits_get: type=DN match=%s\n",
209 limits2str( style
), 0, 0 );
214 case SLAP_LIMITS_ANY
:
215 *limit
= &lm
[0]->lm_limits
;
219 assert( 0 ); /* unreachable */
232 ObjectClass
*group_oc
,
233 AttributeDescription
*group_ad
,
234 struct slap_limits_set
*limit
238 struct slap_limits
*lm
;
239 unsigned type
, style
;
241 assert( be
!= NULL
);
242 assert( limit
!= NULL
);
244 type
= flags
& SLAP_LIMITS_TYPE_MASK
;
245 style
= flags
& SLAP_LIMITS_MASK
;
248 case SLAP_LIMITS_ANONYMOUS
:
249 case SLAP_LIMITS_USERS
:
250 case SLAP_LIMITS_ANY
:
251 for ( i
= 0; be
->be_limits
&& be
->be_limits
[ i
]; i
++ ) {
252 if ( be
->be_limits
[ i
]->lm_flags
== style
) {
260 lm
= ( struct slap_limits
* )ch_calloc( sizeof( struct slap_limits
), 1 );
263 case SLAP_LIMITS_UNDEFINED
:
264 style
= SLAP_LIMITS_EXACT
;
265 /* continue to next cases */
266 case SLAP_LIMITS_EXACT
:
267 case SLAP_LIMITS_ONE
:
268 case SLAP_LIMITS_SUBTREE
:
269 case SLAP_LIMITS_CHILDREN
:
270 lm
->lm_flags
= style
| type
;
275 ber_str2bv( pattern
, 0, 0, &bv
);
277 rc
= dnNormalize( 0, NULL
, NULL
, &bv
, &lm
->lm_pat
, NULL
);
278 if ( rc
!= LDAP_SUCCESS
) {
285 case SLAP_LIMITS_REGEX
:
286 lm
->lm_flags
= style
| type
;
287 ber_str2bv( pattern
, 0, 1, &lm
->lm_pat
);
288 if ( regcomp( &lm
->lm_regex
, lm
->lm_pat
.bv_val
,
289 REG_EXTENDED
| REG_ICASE
) ) {
290 free( lm
->lm_pat
.bv_val
);
296 case SLAP_LIMITS_ANONYMOUS
:
297 case SLAP_LIMITS_USERS
:
298 case SLAP_LIMITS_ANY
:
299 lm
->lm_flags
= style
| type
;
300 BER_BVZERO( &lm
->lm_pat
);
305 case SLAP_LIMITS_TYPE_GROUP
:
306 assert( group_oc
!= NULL
);
307 assert( group_ad
!= NULL
);
308 lm
->lm_group_oc
= group_oc
;
309 lm
->lm_group_ad
= group_ad
;
313 lm
->lm_limits
= *limit
;
316 if ( be
->be_limits
!= NULL
) {
317 for ( ; be
->be_limits
[i
]; i
++ );
320 be
->be_limits
= ( struct slap_limits
** )ch_realloc( be
->be_limits
,
321 sizeof( struct slap_limits
* ) * ( i
+ 2 ) );
322 be
->be_limits
[i
] = lm
;
323 be
->be_limits
[i
+1] = NULL
;
337 int flags
= SLAP_LIMITS_UNDEFINED
;
339 struct slap_limits_set limit
;
341 ObjectClass
*group_oc
= NULL
;
342 AttributeDescription
*group_ad
= NULL
;
344 assert( be
!= NULL
);
347 Debug( LDAP_DEBUG_ANY
,
348 "%s : line %d: missing arg(s) in "
349 "\"limits <pattern> <limits>\" line.\n%s",
354 limit
= be
->be_def_limit
;
359 * "limits" <pattern> <limit> [ ... ]
366 * [ "dn" [ "." { "exact" | "base" | "onelevel" | "subtree" | children"
367 * | "regex" | "anonymous" } ] "=" ] <dn pattern>
370 * "exact" and "base" are the same (exact match);
371 * "onelevel" means exactly one rdn below, NOT including pattern
372 * "subtree" means any rdn below, including pattern
373 * "children" means any rdn below, NOT including pattern
375 * "anonymous" may be deprecated in favour
376 * of the pattern = "anonymous" form
378 * "group[/objectClass[/attributeType]]" "=" "<dn pattern>"
382 * "time" [ "." { "soft" | "hard" } ] "=" <integer>
384 * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
388 if ( strcmp( pattern
, "*" ) == 0) {
389 flags
= SLAP_LIMITS_ANY
;
391 } else if ( strcasecmp( pattern
, "anonymous" ) == 0 ) {
392 flags
= SLAP_LIMITS_ANONYMOUS
;
394 } else if ( strcasecmp( pattern
, "users" ) == 0 ) {
395 flags
= SLAP_LIMITS_USERS
;
397 } else if ( strncasecmp( pattern
, "dn", STRLENOF( "dn" ) ) == 0 ) {
398 pattern
+= STRLENOF( "dn" );
399 if ( pattern
[0] == '.' ) {
401 if ( strncasecmp( pattern
, "exact", STRLENOF( "exact" )) == 0 ) {
402 flags
= SLAP_LIMITS_EXACT
;
403 pattern
+= STRLENOF( "exact" );
405 } else if ( strncasecmp( pattern
, "base", STRLENOF( "base" ) ) == 0 ) {
406 flags
= SLAP_LIMITS_BASE
;
407 pattern
+= STRLENOF( "base" );
409 } else if ( strncasecmp( pattern
, "one", STRLENOF( "one" ) ) == 0 ) {
410 flags
= SLAP_LIMITS_ONE
;
411 pattern
+= STRLENOF( "one" );
412 if ( strncasecmp( pattern
, "level", STRLENOF( "level" ) ) == 0 ) {
413 pattern
+= STRLENOF( "level" );
416 Debug( LDAP_DEBUG_ANY
,
417 "%s : line %d: deprecated \"one\" style "
418 "\"limits <pattern> <limits>\" line; "
419 "use \"onelevel\" instead.\n", fname
, lineno
, 0 );
422 } else if ( strncasecmp( pattern
, "sub", STRLENOF( "sub" ) ) == 0 ) {
423 flags
= SLAP_LIMITS_SUBTREE
;
424 pattern
+= STRLENOF( "sub" );
425 if ( strncasecmp( pattern
, "tree", STRLENOF( "tree" ) ) == 0 ) {
426 pattern
+= STRLENOF( "tree" );
429 Debug( LDAP_DEBUG_ANY
,
430 "%s : line %d: deprecated \"sub\" style "
431 "\"limits <pattern> <limits>\" line; "
432 "use \"subtree\" instead.\n", fname
, lineno
, 0 );
435 } else if ( strncasecmp( pattern
, "children", STRLENOF( "children" ) ) == 0 ) {
436 flags
= SLAP_LIMITS_CHILDREN
;
437 pattern
+= STRLENOF( "children" );
439 } else if ( strncasecmp( pattern
, "regex", STRLENOF( "regex" ) ) == 0 ) {
440 flags
= SLAP_LIMITS_REGEX
;
441 pattern
+= STRLENOF( "regex" );
444 * this could be deprecated in favour
445 * of the pattern = "anonymous" form
447 } else if ( strncasecmp( pattern
, "anonymous", STRLENOF( "anonymous" ) ) == 0 ) {
448 flags
= SLAP_LIMITS_ANONYMOUS
;
453 /* pre-check the data */
455 case SLAP_LIMITS_ANONYMOUS
:
456 case SLAP_LIMITS_USERS
:
458 /* no need for pattern */
463 if ( pattern
[0] != '=' ) {
464 Debug( LDAP_DEBUG_ANY
,
465 "%s : line %d: missing '=' in "
466 "\"dn[.{exact|base|onelevel|subtree"
467 "|children|regex|anonymous}]"
469 "\"limits <pattern> <limits>\" "
475 /* skip '=' (required) */
478 /* trim obvious cases */
479 if ( strcmp( pattern
, "*" ) == 0 ) {
480 flags
= SLAP_LIMITS_ANY
;
483 } else if ( flags
== SLAP_LIMITS_REGEX
484 && strcmp( pattern
, ".*" ) == 0 ) {
485 flags
= SLAP_LIMITS_ANY
;
490 } else if (strncasecmp( pattern
, "group", STRLENOF( "group" ) ) == 0 ) {
491 pattern
+= STRLENOF( "group" );
493 if ( pattern
[0] == '/' ) {
494 struct berval oc
, ad
;
496 oc
.bv_val
= pattern
+ 1;
497 pattern
= strchr( pattern
, '=' );
498 if ( pattern
== NULL
) {
502 ad
.bv_val
= strchr( oc
.bv_val
, '/' );
503 if ( ad
.bv_val
!= NULL
) {
504 const char *text
= NULL
;
506 oc
.bv_len
= ad
.bv_val
- oc
.bv_val
;
509 ad
.bv_len
= pattern
- ad
.bv_val
;
510 rc
= slap_bv2ad( &ad
, &group_ad
, &text
);
511 if ( rc
!= LDAP_SUCCESS
) {
516 oc
.bv_len
= pattern
- oc
.bv_val
;
519 group_oc
= oc_bvfind( &oc
);
520 if ( group_oc
== NULL
) {
525 if ( group_oc
== NULL
) {
526 group_oc
= oc_find( SLAPD_GROUP_CLASS
);
527 if ( group_oc
== NULL
) {
533 if ( group_ad
== NULL
) {
534 const char *text
= NULL
;
536 rc
= slap_str2ad( SLAPD_GROUP_ATTR
, &group_ad
, &text
);
538 if ( rc
!= LDAP_SUCCESS
) {
544 flags
= SLAP_LIMITS_TYPE_GROUP
| SLAP_LIMITS_EXACT
;
546 if ( pattern
[0] != '=' ) {
547 Debug( LDAP_DEBUG_ANY
,
548 "%s : line %d: missing '=' in "
549 "\"group[/objectClass[/attributeType]]"
551 "\"limits <pattern> <limits>\" line.\n",
556 /* skip '=' (required) */
561 for ( i
= 2; i
< argc
; i
++ ) {
562 if ( limits_parse_one( argv
[i
], &limit
) ) {
564 Debug( LDAP_DEBUG_ANY
,
565 "%s : line %d: unknown limit values \"%s\" in "
566 "\"limits <pattern> <limits>\" line.\n",
567 fname
, lineno
, argv
[i
] );
576 * FIXME: add warnings?
578 if ( limit
.lms_t_hard
> 0 &&
579 ( limit
.lms_t_hard
< limit
.lms_t_soft
580 || limit
.lms_t_soft
== -1 ) ) {
581 limit
.lms_t_hard
= limit
.lms_t_soft
;
584 if ( limit
.lms_s_hard
> 0 &&
585 ( limit
.lms_s_hard
< limit
.lms_s_soft
586 || limit
.lms_s_soft
== -1 ) ) {
587 limit
.lms_s_hard
= limit
.lms_s_soft
;
596 * > 0 => limit (in seconds)
601 * > 0 => limit (in entries)
604 * -2 => disable the control
607 * > 0 => limit (in entries)
612 * > 0 => limit size (in entries)
614 if ( limit
.lms_s_pr_total
> 0 &&
615 limit
.lms_s_pr
> limit
.lms_s_pr_total
) {
616 limit
.lms_s_pr
= limit
.lms_s_pr_total
;
619 rc
= limits_add( be
, flags
, pattern
, group_oc
, group_ad
, &limit
);
622 Debug( LDAP_DEBUG_ANY
,
623 "%s : line %d: unable to add limit in "
624 "\"limits <pattern> <limits>\" line.\n",
634 struct slap_limits_set
*limit
637 assert( arg
!= NULL
);
638 assert( limit
!= NULL
);
640 if ( strncasecmp( arg
, "time", STRLENOF( "time" ) ) == 0 ) {
641 arg
+= STRLENOF( "time" );
643 if ( arg
[0] == '.' ) {
645 if ( strncasecmp( arg
, "soft=", STRLENOF( "soft=" ) ) == 0 ) {
646 arg
+= STRLENOF( "soft=" );
647 if ( strcasecmp( arg
, "unlimited" ) == 0 || strcasecmp( arg
, "none" ) == 0 ) {
648 limit
->lms_t_soft
= -1;
653 if ( lutil_atoi( &soft
, arg
) != 0 || soft
< -1 ) {
658 /* FIXME: use "unlimited" instead; issue warning? */
661 limit
->lms_t_soft
= soft
;
664 } else if ( strncasecmp( arg
, "hard=", STRLENOF( "hard=" ) ) == 0 ) {
665 arg
+= STRLENOF( "hard=" );
666 if ( strcasecmp( arg
, "soft" ) == 0 ) {
667 limit
->lms_t_hard
= 0;
669 } else if ( strcasecmp( arg
, "unlimited" ) == 0 || strcasecmp( arg
, "none" ) == 0 ) {
670 limit
->lms_t_hard
= -1;
675 if ( lutil_atoi( &hard
, arg
) != 0 || hard
< -1 ) {
680 /* FIXME: use "unlimited" instead */
684 /* FIXME: use "soft" instead */
687 limit
->lms_t_hard
= hard
;
694 } else if ( arg
[0] == '=' ) {
696 if ( strcasecmp( arg
, "unlimited" ) == 0 || strcasecmp( arg
, "none" ) == 0 ) {
697 limit
->lms_t_soft
= -1;
700 if ( lutil_atoi( &limit
->lms_t_soft
, arg
) != 0
701 || limit
->lms_t_soft
< -1 )
706 limit
->lms_t_hard
= 0;
712 } else if ( strncasecmp( arg
, "size", STRLENOF( "size" ) ) == 0 ) {
713 arg
+= STRLENOF( "size" );
715 if ( arg
[0] == '.' ) {
717 if ( strncasecmp( arg
, "soft=", STRLENOF( "soft=" ) ) == 0 ) {
718 arg
+= STRLENOF( "soft=" );
719 if ( strcasecmp( arg
, "unlimited" ) == 0 || strcasecmp( arg
, "none" ) == 0 ) {
720 limit
->lms_s_soft
= -1;
725 if ( lutil_atoi( &soft
, arg
) != 0 || soft
< -1 ) {
730 /* FIXME: use "unlimited" instead */
733 limit
->lms_s_soft
= soft
;
736 } else if ( strncasecmp( arg
, "hard=", STRLENOF( "hard=" ) ) == 0 ) {
737 arg
+= STRLENOF( "hard=" );
738 if ( strcasecmp( arg
, "soft" ) == 0 ) {
739 limit
->lms_s_hard
= 0;
741 } else if ( strcasecmp( arg
, "unlimited" ) == 0 || strcasecmp( arg
, "none" ) == 0 ) {
742 limit
->lms_s_hard
= -1;
747 if ( lutil_atoi( &hard
, arg
) != 0 || hard
< -1 ) {
752 /* FIXME: use "unlimited" instead */
756 /* FIXME: use "soft" instead */
759 limit
->lms_s_hard
= hard
;
762 } else if ( strncasecmp( arg
, "unchecked=", STRLENOF( "unchecked=" ) ) == 0 ) {
763 arg
+= STRLENOF( "unchecked=" );
764 if ( strcasecmp( arg
, "unlimited" ) == 0 || strcasecmp( arg
, "none" ) == 0 ) {
765 limit
->lms_s_unchecked
= -1;
767 } else if ( strcasecmp( arg
, "disabled" ) == 0 ) {
768 limit
->lms_s_unchecked
= 0;
773 if ( lutil_atoi( &unchecked
, arg
) != 0 || unchecked
< -1 ) {
777 if ( unchecked
== -1 ) {
778 /* FIXME: use "unlimited" instead */
781 limit
->lms_s_unchecked
= unchecked
;
784 } else if ( strncasecmp( arg
, "pr=", STRLENOF( "pr=" ) ) == 0 ) {
785 arg
+= STRLENOF( "pr=" );
786 if ( strcasecmp( arg
, "noEstimate" ) == 0 ) {
787 limit
->lms_s_pr_hide
= 1;
789 } else if ( strcasecmp( arg
, "unlimited" ) == 0 || strcasecmp( arg
, "none" ) == 0 ) {
790 limit
->lms_s_pr
= -1;
795 if ( lutil_atoi( &pr
, arg
) != 0 || pr
< -1 ) {
800 /* FIXME: use "unlimited" instead */
803 limit
->lms_s_pr
= pr
;
806 } else if ( strncasecmp( arg
, "prtotal=", STRLENOF( "prtotal=" ) ) == 0 ) {
807 arg
+= STRLENOF( "prtotal=" );
809 if ( strcasecmp( arg
, "unlimited" ) == 0 || strcasecmp( arg
, "none" ) == 0 ) {
810 limit
->lms_s_pr_total
= -1;
812 } else if ( strcasecmp( arg
, "disabled" ) == 0 ) {
813 limit
->lms_s_pr_total
= -2;
815 } else if ( strcasecmp( arg
, "hard" ) == 0 ) {
816 limit
->lms_s_pr_total
= 0;
821 if ( lutil_atoi( &total
, arg
) != 0 || total
< -1 ) {
826 /* FIXME: use "unlimited" instead */
830 /* FIXME: use "pr=disable" instead */
833 limit
->lms_s_pr_total
= total
;
840 } else if ( arg
[0] == '=' ) {
842 if ( strcasecmp( arg
, "unlimited" ) == 0 || strcasecmp( arg
, "none" ) == 0 ) {
843 limit
->lms_s_soft
= -1;
846 if ( lutil_atoi( &limit
->lms_s_soft
, arg
) != 0
847 || limit
->lms_s_soft
< -1 )
852 limit
->lms_s_hard
= 0;
862 static const char *lmpats
[] = {
874 #define WHATSLEFT ( buflen - ( ptr - bv->bv_val ) )
876 /* Caller must provide an adequately sized buffer in bv */
878 limits_unparse( struct slap_limits
*lim
, struct berval
*bv
, ber_len_t buflen
)
884 if ( !bv
|| !bv
->bv_val
) return -1;
888 if (( lim
->lm_flags
& SLAP_LIMITS_TYPE_MASK
) == SLAP_LIMITS_TYPE_GROUP
) {
889 if ( WHATSLEFT
<= STRLENOF( "group/" "/" "=\"" "\"" )
890 + lim
->lm_group_oc
->soc_cname
.bv_len
891 + lim
->lm_group_ad
->ad_cname
.bv_len
892 + lim
->lm_pat
.bv_len
) return -1;
894 ptr
= lutil_strcopy( ptr
, "group/" );
895 ptr
= lutil_strcopy( ptr
, lim
->lm_group_oc
->soc_cname
.bv_val
);
897 ptr
= lutil_strcopy( ptr
, lim
->lm_group_ad
->ad_cname
.bv_val
);
898 ptr
= lutil_strcopy( ptr
, "=\"" );
899 ptr
= lutil_strcopy( ptr
, lim
->lm_pat
.bv_val
);
902 lm
= lim
->lm_flags
& SLAP_LIMITS_MASK
;
904 case SLAP_LIMITS_ANONYMOUS
:
905 case SLAP_LIMITS_USERS
:
906 case SLAP_LIMITS_ANY
:
907 if ( WHATSLEFT
<= strlen( lmpats
[lm
] ) ) return -1;
908 ptr
= lutil_strcopy( ptr
, lmpats
[lm
] );
910 case SLAP_LIMITS_UNDEFINED
:
911 case SLAP_LIMITS_EXACT
:
912 case SLAP_LIMITS_ONE
:
913 case SLAP_LIMITS_SUBTREE
:
914 case SLAP_LIMITS_CHILDREN
:
915 case SLAP_LIMITS_REGEX
:
916 if ( WHATSLEFT
<= STRLENOF( "dn." "=" "\"" "\"" )
917 + strlen( lmpats
[lm
] ) + lim
->lm_pat
.bv_len
) return -1;
918 ptr
= lutil_strcopy( ptr
, "dn." );
919 ptr
= lutil_strcopy( ptr
, lmpats
[lm
] );
922 ptr
= lutil_strcopy( ptr
, lim
->lm_pat
.bv_val
);
927 bv
->bv_len
= ptr
- bv
->bv_val
;
930 if ( limits_unparse_one( &lim
->lm_limits
,
931 SLAP_LIMIT_SIZE
|SLAP_LIMIT_TIME
,
936 bv
->bv_len
+= btmp
.bv_len
;
940 /* Caller must provide an adequately sized buffer in bv */
942 limits_unparse_one( struct slap_limits_set
*lim
, int which
, struct berval
*bv
, ber_len_t buflen
)
946 if ( !bv
|| !bv
->bv_val
) return -1;
950 if ( which
& SLAP_LIMIT_SIZE
) {
951 if ( lim
->lms_s_soft
!= SLAPD_DEFAULT_SIZELIMIT
) {
953 /* If same as global limit, drop it */
954 if ( lim
!= &frontendDB
->be_def_limit
&&
955 lim
->lms_s_soft
== frontendDB
->be_def_limit
.lms_s_soft
)
958 /* If there's also a hard limit, fully qualify this one */
959 } else if ( lim
->lms_s_hard
) {
960 if ( WHATSLEFT
<= STRLENOF( " size.soft=" ) ) return -1;
961 ptr
= lutil_strcopy( ptr
, " size.soft=" );
963 /* If doing both size & time, qualify this */
964 } else if ( which
& SLAP_LIMIT_TIME
) {
965 if ( WHATSLEFT
<= STRLENOF( " size=" ) ) return -1;
966 ptr
= lutil_strcopy( ptr
, " size=" );
969 if ( lim
->lms_s_soft
== -1 ) {
970 if ( WHATSLEFT
<= STRLENOF( "unlimited" ) ) return -1;
971 ptr
= lutil_strcopy( ptr
, "unlimited" );
973 ptr
+= snprintf( ptr
, WHATSLEFT
, "%d", lim
->lms_s_soft
);
974 if ( WHATSLEFT
< 0 ) return -1;
979 if ( lim
->lms_s_hard
) {
980 if ( WHATSLEFT
<= STRLENOF( " size.hard=" ) ) return -1;
981 ptr
= lutil_strcopy( ptr
, " size.hard=" );
982 if ( lim
->lms_s_hard
== -1 ) {
983 if ( WHATSLEFT
<= STRLENOF( "unlimited" ) ) return -1;
984 ptr
= lutil_strcopy( ptr
, "unlimited" );
986 ptr
+= snprintf( ptr
, WHATSLEFT
, "%d", lim
->lms_s_hard
);
987 if ( WHATSLEFT
< 0 ) return -1;
991 if ( lim
->lms_s_unchecked
!= -1 ) {
992 if ( WHATSLEFT
<= STRLENOF( " size.unchecked=" ) ) return -1;
993 ptr
= lutil_strcopy( ptr
, " size.unchecked=" );
994 if ( lim
->lms_s_unchecked
== 0 ) {
995 if ( WHATSLEFT
<= STRLENOF( "disabled" ) ) return -1;
996 ptr
= lutil_strcopy( ptr
, "disabled" );
998 ptr
+= snprintf( ptr
, WHATSLEFT
, "%d", lim
->lms_s_unchecked
);
999 if ( WHATSLEFT
< 0 ) return -1;
1003 if ( lim
->lms_s_pr_hide
) {
1004 if ( WHATSLEFT
<= STRLENOF( " size.pr=noEstimate " ) ) return -1;
1005 ptr
= lutil_strcopy( ptr
, " size.pr=noEstimate " );
1007 if ( lim
->lms_s_pr
) {
1008 if ( WHATSLEFT
<= STRLENOF( " size.pr=" ) ) return -1;
1009 ptr
= lutil_strcopy( ptr
, " size.pr=" );
1010 if ( lim
->lms_s_pr
== -1 ) {
1011 if ( WHATSLEFT
<= STRLENOF( "unlimited" ) ) return -1;
1012 ptr
= lutil_strcopy( ptr
, "unlimited" );
1014 ptr
+= snprintf( ptr
, WHATSLEFT
, "%d", lim
->lms_s_pr
);
1015 if ( WHATSLEFT
< 0 ) return -1;
1019 if ( lim
->lms_s_pr_total
) {
1020 if ( WHATSLEFT
<= STRLENOF( " size.prtotal=" ) ) return -1;
1021 ptr
= lutil_strcopy( ptr
, " size.prtotal=" );
1022 if ( lim
->lms_s_pr_total
== -1 ) {
1023 if ( WHATSLEFT
<= STRLENOF( "unlimited" ) ) return -1;
1024 ptr
= lutil_strcopy( ptr
, "unlimited" );
1025 } else if ( lim
->lms_s_pr_total
== -2 ) {
1026 if ( WHATSLEFT
<= STRLENOF( "disabled" ) ) return -1;
1027 ptr
= lutil_strcopy( ptr
, "disabled" );
1029 ptr
+= snprintf( ptr
, WHATSLEFT
, "%d", lim
->lms_s_pr_total
);
1030 if ( WHATSLEFT
< 0 ) return -1;
1036 if ( which
& SLAP_LIMIT_TIME
) {
1037 if ( lim
->lms_t_soft
!= SLAPD_DEFAULT_TIMELIMIT
) {
1039 /* If same as global limit, drop it */
1040 if ( lim
!= &frontendDB
->be_def_limit
&&
1041 lim
->lms_t_soft
== frontendDB
->be_def_limit
.lms_t_soft
)
1045 /* If there's also a hard limit, fully qualify this one */
1046 } else if ( lim
->lms_t_hard
) {
1047 if ( WHATSLEFT
<= STRLENOF( " time.soft=" ) ) return -1;
1048 ptr
= lutil_strcopy( ptr
, " time.soft=" );
1050 /* If doing both size & time, qualify this */
1051 } else if ( which
& SLAP_LIMIT_SIZE
) {
1052 if ( WHATSLEFT
<= STRLENOF( " time=" ) ) return -1;
1053 ptr
= lutil_strcopy( ptr
, " time=" );
1056 if ( lim
->lms_t_soft
== -1 ) {
1057 if ( WHATSLEFT
<= STRLENOF( "unlimited" ) ) return -1;
1058 ptr
= lutil_strcopy( ptr
, "unlimited" );
1060 ptr
+= snprintf( ptr
, WHATSLEFT
, "%d", lim
->lms_t_soft
);
1061 if ( WHATSLEFT
< 0 ) return -1;
1066 if ( lim
->lms_t_hard
) {
1067 if ( WHATSLEFT
<= STRLENOF( " time.hard=" ) ) return -1;
1068 ptr
= lutil_strcopy( ptr
, " time.hard=" );
1069 if ( lim
->lms_t_hard
== -1 ) {
1070 if ( WHATSLEFT
<= STRLENOF( "unlimited" ) ) return -1;
1071 ptr
= lutil_strcopy( ptr
, "unlimited" );
1073 ptr
+= snprintf( ptr
, WHATSLEFT
, "%d", lim
->lms_t_hard
);
1074 if ( WHATSLEFT
< 0 ) return -1;
1079 if ( ptr
!= bv
->bv_val
) {
1082 bv
->bv_len
= ptr
- bv
->bv_val
;
1089 limits_check( Operation
*op
, SlapReply
*rs
)
1091 assert( op
!= NULL
);
1092 assert( rs
!= NULL
);
1093 /* FIXME: should this be always true? */
1094 assert( op
->o_tag
== LDAP_REQ_SEARCH
);
1096 /* protocol only allows 0..maxInt;
1098 * internal searches:
1099 * - may use SLAP_NO_LIMIT ( = -1 ) to indicate no limits;
1100 * - should use slimit = N and tlimit = SLAP_NO_LIMIT to
1101 * indicate searches that should return exactly N matches,
1102 * and handle errors thru a callback (see for instance
1103 * slap_sasl_match() and slap_sasl2dn())
1105 if ( op
->ors_tlimit
== SLAP_NO_LIMIT
&& op
->ors_slimit
== SLAP_NO_LIMIT
) {
1109 /* allow root to set no limit */
1110 if ( be_isroot( op
) ) {
1111 op
->ors_limit
= NULL
;
1113 if ( op
->ors_tlimit
== 0 ) {
1114 op
->ors_tlimit
= SLAP_NO_LIMIT
;
1117 if ( op
->ors_slimit
== 0 ) {
1118 op
->ors_slimit
= SLAP_NO_LIMIT
;
1121 /* if paged results and slimit are requested */
1122 if ( get_pagedresults( op
) > SLAP_CONTROL_IGNORED
&&
1123 op
->ors_slimit
!= SLAP_NO_LIMIT
) {
1124 PagedResultsState
*ps
= op
->o_pagedresults_state
;
1125 int total
= op
->ors_slimit
- ps
->ps_count
;
1127 op
->ors_slimit
= total
;
1133 /* if not root, get appropriate limits */
1135 ( void ) limits_get( op
, &op
->o_ndn
, &op
->ors_limit
);
1137 assert( op
->ors_limit
!= NULL
);
1139 /* if no limit is required, use soft limit */
1140 if ( op
->ors_tlimit
== 0 ) {
1141 op
->ors_tlimit
= op
->ors_limit
->lms_t_soft
;
1143 /* limit required: check if legal */
1145 if ( op
->ors_limit
->lms_t_hard
== 0 ) {
1146 if ( op
->ors_limit
->lms_t_soft
> 0
1147 && ( op
->ors_tlimit
> op
->ors_limit
->lms_t_soft
) ) {
1148 op
->ors_tlimit
= op
->ors_limit
->lms_t_soft
;
1151 } else if ( op
->ors_limit
->lms_t_hard
> 0 ) {
1152 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1153 if ( op
->ors_tlimit
== SLAP_MAX_LIMIT
) {
1154 op
->ors_tlimit
= op
->ors_limit
->lms_t_hard
;
1156 } else if ( op
->ors_tlimit
> op
->ors_limit
->lms_t_hard
) {
1157 /* error if exceeding hard limit */
1158 rs
->sr_err
= LDAP_ADMINLIMIT_EXCEEDED
;
1159 send_ldap_result( op
, rs
);
1160 rs
->sr_err
= LDAP_SUCCESS
;
1163 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1164 if ( op
->ors_tlimit
> op
->ors_limit
->lms_t_hard
) {
1165 op
->ors_tlimit
= op
->ors_limit
->lms_t_hard
;
1167 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1171 /* else leave as is */
1173 /* don't even get to backend if candidate check is disabled */
1174 if ( op
->ors_limit
->lms_s_unchecked
== 0 ) {
1175 rs
->sr_err
= LDAP_ADMINLIMIT_EXCEEDED
;
1176 send_ldap_result( op
, rs
);
1177 rs
->sr_err
= LDAP_SUCCESS
;
1181 /* if paged results is requested */
1182 if ( get_pagedresults( op
) > SLAP_CONTROL_IGNORED
) {
1185 PagedResultsState
*ps
= op
->o_pagedresults_state
;
1187 /* paged results is not allowed */
1188 if ( op
->ors_limit
->lms_s_pr_total
== -2 ) {
1189 rs
->sr_err
= LDAP_ADMINLIMIT_EXCEEDED
;
1190 rs
->sr_text
= "pagedResults control not allowed";
1191 send_ldap_result( op
, rs
);
1192 rs
->sr_err
= LDAP_SUCCESS
;
1197 if ( op
->ors_limit
->lms_s_pr
> 0 && ps
->ps_size
> op
->ors_limit
->lms_s_pr
) {
1198 rs
->sr_err
= LDAP_ADMINLIMIT_EXCEEDED
;
1199 rs
->sr_text
= "illegal pagedResults page size";
1200 send_ldap_result( op
, rs
);
1201 rs
->sr_err
= LDAP_SUCCESS
;
1206 if ( op
->ors_limit
->lms_s_pr_total
== 0 ) {
1207 if ( op
->ors_limit
->lms_s_hard
== 0 ) {
1208 pr_total
= op
->ors_limit
->lms_s_soft
;
1210 pr_total
= op
->ors_limit
->lms_s_hard
;
1213 pr_total
= op
->ors_limit
->lms_s_pr_total
;
1216 if ( pr_total
== -1 ) {
1217 if ( op
->ors_slimit
== 0 || op
->ors_slimit
== SLAP_MAX_LIMIT
) {
1221 slimit
= op
->ors_slimit
- ps
->ps_count
;
1224 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1225 } else if ( pr_total
> 0 && op
->ors_slimit
!= SLAP_MAX_LIMIT
1226 && ( op
->ors_slimit
== SLAP_NO_LIMIT
|| op
->ors_slimit
> pr_total
) )
1228 rs
->sr_err
= LDAP_ADMINLIMIT_EXCEEDED
;
1229 send_ldap_result( op
, rs
);
1230 rs
->sr_err
= LDAP_SUCCESS
;
1232 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1235 /* if no limit is required, use soft limit */
1239 /* first round of pagedResults: set count to any appropriate limit */
1241 /* if the limit is set, check that it does not violate any server-side limit */
1242 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1243 if ( op
->ors_slimit
== SLAP_MAX_LIMIT
) {
1244 slimit2
= op
->ors_slimit
= pr_total
;
1245 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1246 if ( op
->ors_slimit
== SLAP_MAX_LIMIT
|| op
->ors_slimit
> pr_total
) {
1247 slimit2
= op
->ors_slimit
= pr_total
;
1248 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1250 } else if ( op
->ors_slimit
== 0 ) {
1254 slimit2
= op
->ors_slimit
;
1257 total
= slimit2
- ps
->ps_count
;
1260 if ( op
->ors_limit
->lms_s_pr
> 0 ) {
1261 /* use the smallest limit set by total/per page */
1262 if ( total
< op
->ors_limit
->lms_s_pr
) {
1266 /* use the perpage limit if any
1267 * NOTE: + 1 because the given value must be legal */
1268 slimit
= op
->ors_limit
->lms_s_pr
+ 1;
1272 /* use the total limit if any */
1276 } else if ( op
->ors_limit
->lms_s_pr
> 0 ) {
1277 /* use the perpage limit if any
1278 * NOTE: + 1 because the given value must be legal */
1279 slimit
= op
->ors_limit
->lms_s_pr
+ 1;
1282 /* use the standard hard/soft limit if any */
1283 slimit
= op
->ors_limit
->lms_s_hard
;
1287 /* if got any limit, use it */
1288 if ( slimit
!= -2 ) {
1289 if ( op
->ors_slimit
== 0 ) {
1290 op
->ors_slimit
= slimit
;
1292 } else if ( slimit
> 0 ) {
1293 if ( op
->ors_slimit
- ps
->ps_count
> slimit
) {
1294 rs
->sr_err
= LDAP_ADMINLIMIT_EXCEEDED
;
1295 send_ldap_result( op
, rs
);
1296 rs
->sr_err
= LDAP_SUCCESS
;
1299 op
->ors_slimit
= slimit
;
1301 } else if ( slimit
== 0 ) {
1306 /* use the standard hard/soft limit if any */
1307 op
->ors_slimit
= pr_total
;
1310 /* no limit requested: use soft, whatever it is */
1311 } else if ( op
->ors_slimit
== 0 ) {
1312 op
->ors_slimit
= op
->ors_limit
->lms_s_soft
;
1314 /* limit requested: check if legal */
1316 /* hard limit as soft (traditional behavior) */
1317 if ( op
->ors_limit
->lms_s_hard
== 0 ) {
1318 if ( op
->ors_limit
->lms_s_soft
> 0
1319 && op
->ors_slimit
> op
->ors_limit
->lms_s_soft
) {
1320 op
->ors_slimit
= op
->ors_limit
->lms_s_soft
;
1323 /* explicit hard limit: error if violated */
1324 } else if ( op
->ors_limit
->lms_s_hard
> 0 ) {
1325 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1326 if ( op
->ors_slimit
== SLAP_MAX_LIMIT
) {
1327 op
->ors_slimit
= op
->ors_limit
->lms_s_hard
;
1329 } else if ( op
->ors_slimit
> op
->ors_limit
->lms_s_hard
) {
1330 /* if limit exceeds hard, error */
1331 rs
->sr_err
= LDAP_ADMINLIMIT_EXCEEDED
;
1332 send_ldap_result( op
, rs
);
1333 rs
->sr_err
= LDAP_SUCCESS
;
1336 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1337 if ( op
->ors_slimit
> op
->ors_limit
->lms_s_hard
) {
1338 op
->ors_slimit
= op
->ors_limit
->lms_s_hard
;
1340 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1344 /* else leave as is */
1352 struct slap_limits
**lm
)
1360 for ( i
= 0; lm
[ i
]; i
++ ) {
1361 switch ( lm
[ i
]->lm_flags
& SLAP_LIMITS_MASK
) {
1362 case SLAP_LIMITS_REGEX
:
1363 regfree( &lm
[ i
]->lm_regex
);
1366 case SLAP_LIMITS_EXACT
:
1367 case SLAP_LIMITS_ONE
:
1368 case SLAP_LIMITS_SUBTREE
:
1369 case SLAP_LIMITS_CHILDREN
:
1370 if ( !BER_BVISNULL( &lm
[ i
]->lm_pat
) ) {
1371 ch_free( lm
[ i
]->lm_pat
.bv_val
);