]> git.sur5r.net Git - openldap/blob - servers/slapd/limits.c
ITS#4467: Fix buffer overflow tests with snprintf / 'unsigned WHATSLEFT'<=0.
[openldap] / servers / slapd / limits.c
1 /* limits.c - routines to handle regex-based size and time limits */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2008 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
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>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/regex.h>
22 #include <ac/string.h>
23
24 #include "slap.h"
25 #include "lutil.h"
26
27 /* define to get an error if requesting limit higher than hard */
28 #undef ABOVE_HARD_LIMIT_IS_ERROR
29
30 static const struct berval lmpats[] = {
31         BER_BVC( "base" ),
32         BER_BVC( "base" ),
33         BER_BVC( "onelevel" ),
34         BER_BVC( "subtree" ),
35         BER_BVC( "children" ),
36         BER_BVC( "regex" ),
37         BER_BVC( "anonymous" ),
38         BER_BVC( "users" ),
39         BER_BVC( "*" )
40 };
41
42 #ifdef LDAP_DEBUG
43 static const char *const dn_source[2] = { "DN", "DN.THIS" };
44 static const char *const lmpats_out[] = {
45         "UNDEFINED",
46         "EXACT",
47         "ONELEVEL",
48         "SUBTREE",
49         "CHILDREN",
50         "REGEX",
51         "ANONYMOUS",
52         "USERS",
53         "ANY"
54 };
55
56 static const char *
57 limits2str( unsigned i )
58 {
59         return i < (sizeof( lmpats_out ) / sizeof( lmpats_out[0] ))
60                 ? lmpats_out[i] : "UNKNOWN";
61 }
62 #endif /* LDAP_DEBUG */
63
64 static int
65 limits_get( 
66         Operation               *op,
67         struct slap_limits_set  **limit
68 )
69 {
70         struct slap_limits **lm;
71         struct berval           *ndns[2];
72
73         assert( op != NULL );
74         assert( limit != NULL );
75
76         ndns[0] = &op->o_ndn;
77         ndns[1] = &op->o_req_ndn;
78
79         Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s self=\"%s\" this=\"%s\"\n",
80                         op->o_log_prefix,
81                         BER_BVISNULL( ndns[0] ) ? "[anonymous]" : ndns[0]->bv_val,
82                         BER_BVISNULL( ndns[1] ) ? "" : ndns[1]->bv_val );
83         /*
84          * default values
85          */
86         *limit = &op->o_bd->be_def_limit;
87
88         if ( op->o_bd->be_limits == NULL ) {
89                 return( 0 );
90         }
91
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];
97
98                 switch ( style ) {
99                 case SLAP_LIMITS_EXACT:
100                         if ( BER_BVISEMPTY( ndn ) ) {
101                                 break;
102                         }
103
104                         if ( type == SLAP_LIMITS_TYPE_GROUP ) {
105                                 int     rc = backend_group( op, NULL,
106                                                 &lm[0]->lm_pat, ndn,
107                                                 lm[0]->lm_group_oc,
108                                                 lm[0]->lm_group_ad );
109                                 if ( rc == 0 ) {
110                                         goto found_group;
111                                 }
112                         } else {
113                                 if ( dn_match( &lm[0]->lm_pat, ndn ) ) {
114                                         goto found_dn;
115                                 }
116                         }
117                         break;
118
119                 case SLAP_LIMITS_ONE:
120                 case SLAP_LIMITS_SUBTREE:
121                 case SLAP_LIMITS_CHILDREN: {
122                         ber_len_t d;
123                         
124                         if ( BER_BVISEMPTY( ndn ) ) {
125                                 break;
126                         }
127
128                         /* ndn shorter than lm_pat */
129                         if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) {
130                                 break;
131                         }
132                         d = ndn->bv_len - lm[0]->lm_pat.bv_len;
133
134                         if ( d == 0 ) {
135                                 /* allow exact match for SUBTREE only */
136                                 if ( style != SLAP_LIMITS_SUBTREE ) {
137                                         break;
138                                 }
139                         } else {
140                                 /* check for unescaped rdn separator */
141                                 if ( !DN_SEPARATOR( ndn->bv_val[d - 1] ) ) {
142                                         break;
143                                 }
144                         }
145
146                         /* check that ndn ends with lm_pat */
147                         if ( strcmp( lm[0]->lm_pat.bv_val, &ndn->bv_val[d] ) != 0 ) {
148                                 break;
149                         }
150
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 ) {
154                                         break;
155                                 }
156                         }
157
158                         goto found_dn;
159                 }
160
161                 case SLAP_LIMITS_REGEX:
162                         if ( BER_BVISEMPTY( ndn ) ) {
163                                 break;
164                         }
165                         if ( regexec( &lm[0]->lm_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) {
166                                 goto found_dn;
167                         }
168                         break;
169
170                 case SLAP_LIMITS_ANONYMOUS:
171                         if ( BER_BVISEMPTY( ndn ) ) {
172                                 goto found;
173                         }
174                         break;
175
176                 case SLAP_LIMITS_USERS:
177                         if ( !BER_BVISEMPTY( ndn ) ) {
178                                 goto found;
179                         }
180                         break;
181
182                 case SLAP_LIMITS_ANY:
183                         *limit = &lm[0]->lm_limits;
184                         return( 0 );
185
186                 found:
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;
190                         return( 0 );
191
192                 found_dn:
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;
197                         return( 0 );
198
199                 found_group:
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;
206                         return( 0 );
207
208                 default:
209                         assert( 0 );    /* unreachable */
210                         return( -1 );
211                 }
212         }
213
214         return( 0 );
215 }
216
217 static int
218 limits_add(
219         Backend                 *be,
220         unsigned                flags,
221         const char              *pattern,
222         ObjectClass             *group_oc,
223         AttributeDescription    *group_ad,
224         struct slap_limits_set  *limit
225 )
226 {
227         int                     i;
228         struct slap_limits      *lm;
229         unsigned                type, style;
230         
231         assert( be != NULL );
232         assert( limit != NULL );
233
234         type = flags & SLAP_LIMITS_TYPE_MASK;
235         style = flags & SLAP_LIMITS_MASK;
236
237         switch ( style ) {
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 ) {
244                                 return( -1 );
245                         }
246                 }
247                 break;
248         }
249
250
251         lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
252
253         switch ( style ) {
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;
262                 {
263                         int rc;
264                         struct berval bv;
265
266                         ber_str2bv( pattern, 0, 0, &bv );
267
268                         rc = dnNormalize( 0, NULL, NULL, &bv, &lm->lm_pat, NULL );
269                         if ( rc != LDAP_SUCCESS ) {
270                                 ch_free( lm );
271                                 return( -1 );
272                         }
273                 }
274                 break;
275                 
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 );
282                         ch_free( lm );
283                         return( -1 );
284                 }
285                 break;
286
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 );
292                 break;
293         }
294
295         switch ( type ) {
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;
301                 break;
302         }
303
304         lm->lm_limits = *limit;
305
306         i = 0;
307         if ( be->be_limits != NULL ) {
308                 for ( ; be->be_limits[i]; i++ );
309         }
310
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;
315         
316         return( 0 );
317 }
318
319 #define STRSTART( s, m ) (strncasecmp( s, m, STRLENOF( "" m "" )) == 0)
320
321 int
322 limits_parse(
323         Backend     *be,
324         const char  *fname,
325         int         lineno,
326         int         argc,
327         char        **argv
328 )
329 {
330         int                     flags = SLAP_LIMITS_UNDEFINED;
331         char                    *pattern;
332         struct slap_limits_set  limit;
333         int                     i, rc = 0;
334         ObjectClass             *group_oc = NULL;
335         AttributeDescription    *group_ad = NULL;
336
337         assert( be != NULL );
338
339         if ( argc < 3 ) {
340                 Debug( LDAP_DEBUG_ANY,
341                         "%s : line %d: missing arg(s) in "
342                         "\"limits <pattern> <limits>\" line.\n%s",
343                         fname, lineno, "" );
344                 return( -1 );
345         }
346
347         limit = be->be_def_limit;
348
349         /*
350          * syntax:
351          *
352          * "limits" <pattern> <limit> [ ... ]
353          * 
354          * 
355          * <pattern>:
356          * 
357          * "anonymous"
358          * "users"
359          * [ "dn" [ "." { "this" | "self" } ] [ "." { "exact" | "base" |
360          *      "onelevel" | "subtree" | "children" | "regex" | "anonymous" } ]
361          *      "=" ] <dn pattern>
362          *
363          * Note:
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
369          *      
370          *      "anonymous" may be deprecated in favour 
371          *      of the pattern = "anonymous" form
372          *
373          * "group[/objectClass[/attributeType]]" "=" "<dn pattern>"
374          *
375          * <limit>:
376          *
377          * "time" [ "." { "soft" | "hard" } ] "=" <integer>
378          *
379          * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
380          */
381         
382         pattern = argv[1];
383         if ( strcmp( pattern, "*" ) == 0) {
384                 flags = SLAP_LIMITS_ANY;
385
386         } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
387                 flags = SLAP_LIMITS_ANONYMOUS;
388
389         } else if ( strcasecmp( pattern, "users" ) == 0 ) {
390                 flags = SLAP_LIMITS_USERS;
391                 
392         } else if ( STRSTART( pattern, "dn" ) ) {
393                 pattern += STRLENOF( "dn" );
394                 flags = SLAP_LIMITS_TYPE_SELF;
395                 if ( pattern[0] == '.' ) {
396                         pattern++;
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" );
402                         } else {
403                                 goto got_dn_dot;
404                         }
405                 }
406                 if ( pattern[0] == '.' ) {
407                         pattern++;
408                 got_dn_dot:
409                         if ( STRSTART( pattern, "exact" ) ) {
410                                 flags |= SLAP_LIMITS_EXACT;
411                                 pattern += STRLENOF( "exact" );
412
413                         } else if ( STRSTART( pattern, "base" ) ) {
414                                 flags |= SLAP_LIMITS_BASE;
415                                 pattern += STRLENOF( "base" );
416
417                         } else if ( STRSTART( pattern, "one" ) ) {
418                                 flags |= SLAP_LIMITS_ONE;
419                                 pattern += STRLENOF( "one" );
420                                 if ( STRSTART( pattern, "level" ) ) {
421                                         pattern += STRLENOF( "level" );
422
423                                 } else {
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 );
428                                 }
429
430                         } else if ( STRSTART( pattern, "sub" ) ) {
431                                 flags |= SLAP_LIMITS_SUBTREE;
432                                 pattern += STRLENOF( "sub" );
433                                 if ( STRSTART( pattern, "tree" ) ) {
434                                         pattern += STRLENOF( "tree" );
435
436                                 } else {
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 );
441                                 }
442
443                         } else if ( STRSTART( pattern, "children" ) ) {
444                                 flags |= SLAP_LIMITS_CHILDREN;
445                                 pattern += STRLENOF( "children" );
446
447                         } else if ( STRSTART( pattern, "regex" ) ) {
448                                 flags |= SLAP_LIMITS_REGEX;
449                                 pattern += STRLENOF( "regex" );
450
451                         /* 
452                          * this could be deprecated in favour
453                          * of the pattern = "anonymous" form
454                          */
455                         } else if ( STRSTART( pattern, "anonymous" )
456                                         && flags == SLAP_LIMITS_TYPE_SELF )
457                         {
458                                 flags = SLAP_LIMITS_ANONYMOUS;
459                                 pattern = NULL;
460                         }
461                 }
462
463                 /* pre-check the data */
464                 switch ( flags ) {
465                 case SLAP_LIMITS_ANONYMOUS:
466                 case SLAP_LIMITS_USERS:
467
468                         /* no need for pattern */
469                         pattern = NULL;
470                         break;
471
472                 default:
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}]"
478                                         "=<pattern>\" in "
479                                         "\"limits <pattern> <limits>\" "
480                                         "line.\n%s",
481                                         fname, lineno, "" );
482                                 return( -1 );
483                         }
484
485                         /* skip '=' (required) */
486                         pattern++;
487
488                         /* trim obvious cases */
489                         if ( strcmp( pattern, "*" ) == 0 ) {
490                                 flags = SLAP_LIMITS_ANY;
491                                 pattern = NULL;
492
493                         } else if ( flags == SLAP_LIMITS_REGEX
494                                         && strcmp( pattern, ".*" ) == 0 ) {
495                                 flags = SLAP_LIMITS_ANY;
496                                 pattern = NULL;
497                         }
498                 }
499
500         } else if (STRSTART( pattern, "group" ) ) {
501                 pattern += STRLENOF( "group" );
502
503                 if ( pattern[0] == '/' ) {
504                         struct berval   oc, ad;
505
506                         oc.bv_val = pattern + 1;
507                         pattern = strchr( pattern, '=' );
508                         if ( pattern == NULL ) {
509                                 return -1;
510                         }
511
512                         ad.bv_val = strchr( oc.bv_val, '/' );
513                         if ( ad.bv_val != NULL ) {
514                                 const char      *text = NULL;
515
516                                 oc.bv_len = ad.bv_val - oc.bv_val;
517
518                                 ad.bv_val++;
519                                 ad.bv_len = pattern - ad.bv_val;
520                                 rc = slap_bv2ad( &ad, &group_ad, &text );
521                                 if ( rc != LDAP_SUCCESS ) {
522                                         goto no_ad;
523                                 }
524
525                         } else {
526                                 oc.bv_len = pattern - oc.bv_val;
527                         }
528
529                         group_oc = oc_bvfind( &oc );
530                         if ( group_oc == NULL ) {
531                                 goto no_oc;
532                         }
533                 }
534
535                 if ( group_oc == NULL ) {
536                         group_oc = oc_find( SLAPD_GROUP_CLASS );
537                         if ( group_oc == NULL ) {
538 no_oc:;
539                                 return( -1 );
540                         }
541                 }
542
543                 if ( group_ad == NULL ) {
544                         const char      *text = NULL;
545                         
546                         rc = slap_str2ad( SLAPD_GROUP_ATTR, &group_ad, &text );
547
548                         if ( rc != LDAP_SUCCESS ) {
549 no_ad:;
550                                 return( -1 );
551                         }
552                 }
553
554                 flags = SLAP_LIMITS_TYPE_GROUP | SLAP_LIMITS_EXACT;
555
556                 if ( pattern[0] != '=' ) {
557                         Debug( LDAP_DEBUG_ANY,
558                                 "%s : line %d: missing '=' in "
559                                 "\"group[/objectClass[/attributeType]]"
560                                 "=<pattern>\" in "
561                                 "\"limits <pattern> <limits>\" line.\n",
562                                 fname, lineno, 0 );
563                         return( -1 );
564                 }
565
566                 /* skip '=' (required) */
567                 pattern++;
568         }
569
570         /* get the limits */
571         for ( i = 2; i < argc; i++ ) {
572                 if ( limits_parse_one( argv[i], &limit ) ) {
573
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] );
578
579                         return( 1 );
580                 }
581         }
582
583         /*
584          * sanity checks ...
585          *
586          * FIXME: add warnings?
587          */
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;
592         }
593         
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;
598         }
599
600         /*
601          * defaults ...
602          * 
603          * lms_t_hard:
604          *      -1      => no limits
605          *      0       => same as soft
606          *      > 0     => limit (in seconds)
607          *
608          * lms_s_hard:
609          *      -1      => no limits
610          *      0       0> same as soft
611          *      > 0     => limit (in entries)
612          *
613          * lms_s_pr_total:
614          *      -2      => disable the control
615          *      -1      => no limits
616          *      0       => same as soft
617          *      > 0     => limit (in entries)
618          *
619          * lms_s_pr:
620          *      -1      => no limits
621          *      0       => no limits?
622          *      > 0     => limit size (in entries)
623          */
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;
627         }
628
629         rc = limits_add( be, flags, pattern, group_oc, group_ad, &limit );
630         if ( rc ) {
631
632                 Debug( LDAP_DEBUG_ANY,
633                         "%s : line %d: unable to add limit in "
634                         "\"limits <pattern> <limits>\" line.\n",
635                 fname, lineno, 0 );
636         }
637
638         return( rc );
639 }
640
641 int
642 limits_parse_one(
643         const char              *arg,
644         struct slap_limits_set  *limit
645 )
646 {
647         assert( arg != NULL );
648         assert( limit != NULL );
649
650         if ( STRSTART( arg, "time" ) ) {
651                 arg += STRLENOF( "time" );
652
653                 if ( arg[0] == '.' ) {
654                         arg++;
655                         if ( STRSTART( arg, "soft=" ) ) {
656                                 arg += STRLENOF( "soft=" );
657                                 if ( strcasecmp( arg, "unlimited" ) == 0
658                                         || strcasecmp( arg, "none" ) == 0 )
659                                 {
660                                         limit->lms_t_soft = -1;
661
662                                 } else {
663                                         int     soft;
664
665                                         if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
666                                                 return( 1 );
667                                         }
668
669                                         if ( soft == -1 ) {
670                                                 /* FIXME: use "unlimited" instead; issue warning? */
671                                         }
672
673                                         limit->lms_t_soft = soft;
674                                 }
675                                 
676                         } else if ( STRSTART( arg, "hard=" ) ) {
677                                 arg += STRLENOF( "hard=" );
678                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
679                                         limit->lms_t_hard = 0;
680
681                                 } else if ( strcasecmp( arg, "unlimited" ) == 0
682                                                 || strcasecmp( arg, "none" ) == 0 )
683                                 {
684                                         limit->lms_t_hard = -1;
685
686                                 } else {
687                                         int     hard;
688
689                                         if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
690                                                 return( 1 );
691                                         }
692
693                                         if ( hard == -1 ) {
694                                                 /* FIXME: use "unlimited" instead */
695                                         }
696
697                                         if ( hard == 0 ) {
698                                                 /* FIXME: use "soft" instead */
699                                         }
700
701                                         limit->lms_t_hard = hard;
702                                 }
703                                 
704                         } else {
705                                 return( 1 );
706                         }
707                         
708                 } else if ( arg[0] == '=' ) {
709                         arg++;
710                         if ( strcasecmp( arg, "unlimited" ) == 0
711                                 || strcasecmp( arg, "none" ) == 0 )
712                         {
713                                 limit->lms_t_soft = -1;
714
715                         } else {
716                                 if ( lutil_atoi( &limit->lms_t_soft, arg ) != 0 
717                                         || limit->lms_t_soft < -1 )
718                                 {
719                                         return( 1 );
720                                 }
721                         }
722                         limit->lms_t_hard = 0;
723                         
724                 } else {
725                         return( 1 );
726                 }
727
728         } else if ( STRSTART( arg, "size" ) ) {
729                 arg += STRLENOF( "size" );
730                 
731                 if ( arg[0] == '.' ) {
732                         arg++;
733                         if ( STRSTART( arg, "soft=" ) ) {
734                                 arg += STRLENOF( "soft=" );
735                                 if ( strcasecmp( arg, "unlimited" ) == 0
736                                         || strcasecmp( arg, "none" ) == 0 )
737                                 {
738                                         limit->lms_s_soft = -1;
739
740                                 } else {
741                                         int     soft;
742
743                                         if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
744                                                 return( 1 );
745                                         }
746
747                                         if ( soft == -1 ) {
748                                                 /* FIXME: use "unlimited" instead */
749                                         }
750
751                                         limit->lms_s_soft = soft;
752                                 }
753                                 
754                         } else if ( STRSTART( arg, "hard=" ) ) {
755                                 arg += STRLENOF( "hard=" );
756                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
757                                         limit->lms_s_hard = 0;
758
759                                 } else if ( strcasecmp( arg, "unlimited" ) == 0
760                                                 || strcasecmp( arg, "none" ) == 0 )
761                                 {
762                                         limit->lms_s_hard = -1;
763
764                                 } else {
765                                         int     hard;
766
767                                         if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
768                                                 return( 1 );
769                                         }
770
771                                         if ( hard == -1 ) {
772                                                 /* FIXME: use "unlimited" instead */
773                                         }
774
775                                         if ( hard == 0 ) {
776                                                 /* FIXME: use "soft" instead */
777                                         }
778
779                                         limit->lms_s_hard = hard;
780                                 }
781                                 
782                         } else if ( STRSTART( arg, "unchecked=" ) ) {
783                                 arg += STRLENOF( "unchecked=" );
784                                 if ( strcasecmp( arg, "unlimited" ) == 0
785                                         || strcasecmp( arg, "none" ) == 0 )
786                                 {
787                                         limit->lms_s_unchecked = -1;
788
789                                 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
790                                         limit->lms_s_unchecked = 0;
791
792                                 } else {
793                                         int     unchecked;
794
795                                         if ( lutil_atoi( &unchecked, arg ) != 0 || unchecked < -1 ) {
796                                                 return( 1 );
797                                         }
798
799                                         if ( unchecked == -1 ) {
800                                                 /*  FIXME: use "unlimited" instead */
801                                         }
802
803                                         limit->lms_s_unchecked = unchecked;
804                                 }
805
806                         } else if ( STRSTART( arg, "pr=" ) ) {
807                                 arg += STRLENOF( "pr=" );
808                                 if ( strcasecmp( arg, "noEstimate" ) == 0 ) {
809                                         limit->lms_s_pr_hide = 1;
810
811                                 } else if ( strcasecmp( arg, "unlimited" ) == 0
812                                                 || strcasecmp( arg, "none" ) == 0 )
813                                 {
814                                         limit->lms_s_pr = -1;
815
816                                 } else {
817                                         int     pr;
818
819                                         if ( lutil_atoi( &pr, arg ) != 0 || pr < -1 ) {
820                                                 return( 1 );
821                                         }
822
823                                         if ( pr == -1 ) {
824                                                 /* FIXME: use "unlimited" instead */
825                                         }
826
827                                         limit->lms_s_pr = pr;
828                                 }
829
830                         } else if ( STRSTART( arg, "prtotal=" ) ) {
831                                 arg += STRLENOF( "prtotal=" );
832
833                                 if ( strcasecmp( arg, "unlimited" ) == 0
834                                         || strcasecmp( arg, "none" ) == 0 )
835                                 {
836                                         limit->lms_s_pr_total = -1;
837
838                                 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
839                                         limit->lms_s_pr_total = -2;
840
841                                 } else if ( strcasecmp( arg, "hard" ) == 0 ) {
842                                         limit->lms_s_pr_total = 0;
843
844                                 } else {
845                                         int     total;
846
847                                         if ( lutil_atoi( &total, arg ) != 0 || total < -1 ) {
848                                                 return( 1 );
849                                         }
850
851                                         if ( total == -1 ) {
852                                                 /* FIXME: use "unlimited" instead */
853                                         }
854
855                                         if ( total == 0 ) {
856                                                 /* FIXME: use "pr=disable" instead */
857                                         }
858
859                                         limit->lms_s_pr_total = total;
860                                 }
861
862                         } else {
863                                 return( 1 );
864                         }
865                         
866                 } else if ( arg[0] == '=' ) {
867                         arg++;
868                         if ( strcasecmp( arg, "unlimited" ) == 0
869                                 || strcasecmp( arg, "none" ) == 0 )
870                         {
871                                 limit->lms_s_soft = -1;
872
873                         } else {
874                                 if ( lutil_atoi( &limit->lms_s_soft, arg ) != 0
875                                         || limit->lms_s_soft < -1 )
876                                 {
877                                         return( 1 );
878                                 }
879                         }
880                         limit->lms_s_hard = 0;
881                         
882                 } else {
883                         return( 1 );
884                 }
885         }
886
887         return 0;
888 }
889
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).
893  */
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))
904
905 /* Caller must provide an adequately sized buffer in bv */
906 int
907 limits_unparse( struct slap_limits *lim, struct berval *bv, ber_len_t buflen )
908 {
909         struct berval btmp;
910         char *ptr, *bufEnd;                     /* Updated/used by ptr_APPEND_*()/WHATSLEFT */
911         ber_len_t tmpLen;                       /* Used by ptr_APPEND_FMT*() */
912         unsigned type, style;
913         int rc = 0;
914
915         if ( !bv || !bv->bv_val ) return -1;
916
917         ptr = bv->bv_val;
918         bufEnd = ptr + buflen;
919         type = lim->lm_flags & SLAP_LIMITS_TYPE_MASK;
920
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 ));
926         } else {
927                 style = lim->lm_flags & SLAP_LIMITS_MASK;
928                 switch( style ) {
929                 case SLAP_LIMITS_ANONYMOUS:
930                 case SLAP_LIMITS_USERS:
931                 case SLAP_LIMITS_ANY:
932                         rc = ptr_APPEND_BV( lmpats[style] );
933                         break;
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 ));
943                         break;
944                 }
945         }
946         if ( rc == 0 ) {
947                 bv->bv_len = ptr - bv->bv_val;
948                 btmp.bv_val = ptr;
949                 btmp.bv_len = 0;
950                 rc = limits_unparse_one( &lim->lm_limits,
951                         SLAP_LIMIT_SIZE | SLAP_LIMIT_TIME,
952                         &btmp, WHATSLEFT );
953                 if ( rc == 0 )
954                         bv->bv_len += btmp.bv_len;
955         }
956         return rc;
957 }
958
959 /* Caller must provide an adequately sized buffer in bv */
960 int
961 limits_unparse_one(
962         struct slap_limits_set  *lim,
963         int                             which,
964         struct berval   *bv,
965         ber_len_t               buflen )
966 {
967         char *ptr, *bufEnd;                     /* Updated/used by ptr_APPEND_*()/WHATSLEFT */
968         ber_len_t tmpLen;                       /* Used by ptr_APPEND_FMT*() */
969
970         if ( !bv || !bv->bv_val ) return -1;
971
972         ptr = bv->bv_val;
973         bufEnd = ptr + buflen;
974
975         if ( which & SLAP_LIMIT_SIZE ) {
976                 if ( lim->lms_s_soft != SLAPD_DEFAULT_SIZELIMIT ) {
977
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 )
981                         {
982                                 goto s_hard;
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;
986
987                         /* If doing both size & time, qualify this */
988                         } else if ( which & SLAP_LIMIT_TIME ) {
989                                 if ( ptr_APPEND_LIT( " size=" ) ) return -1;
990                         }
991
992                         if ( lim->lms_s_soft == -1
993                                         ? ptr_APPEND_LIT( "unlimited " )
994                                         : ptr_APPEND_FMT1( "%d ", lim->lms_s_soft ) )
995                                 return -1;
996                 }
997 s_hard:
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 ) )
1003                                 return -1;
1004                 }
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 ) )
1010                                 return -1;
1011                 }
1012                 if ( lim->lms_s_pr_hide ) {
1013                         if ( ptr_APPEND_LIT( " size.pr=noEstimate " ) ) return -1;
1014                 }
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 ) )
1020                                 return -1;
1021                 }
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 ) )
1027                                 return -1;
1028                 }
1029         }
1030
1031         if ( which & SLAP_LIMIT_TIME ) {
1032                 if ( lim->lms_t_soft != SLAPD_DEFAULT_TIMELIMIT ) {
1033
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 )
1037                         {
1038                                 goto t_hard;
1039
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;
1043
1044                         /* If doing both size & time, qualify this */
1045                         } else if ( which & SLAP_LIMIT_SIZE ) {
1046                                 if ( ptr_APPEND_LIT( " time=" ) ) return -1;
1047                         }
1048
1049                         if ( lim->lms_t_soft == -1
1050                                         ? ptr_APPEND_LIT( "unlimited " )
1051                                         : ptr_APPEND_FMT1( "%d ", lim->lms_t_soft ) )
1052                                 return -1;
1053                 }
1054 t_hard:
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 ) )
1060                                 return -1;
1061                 }
1062         }
1063         if ( ptr != bv->bv_val ) {
1064                 ptr--;
1065                 *ptr = '\0';
1066                 bv->bv_len = ptr - bv->bv_val;
1067         }
1068
1069         return 0;
1070 }
1071
1072 int
1073 limits_check( Operation *op, SlapReply *rs )
1074 {
1075         assert( op != NULL );
1076         assert( rs != NULL );
1077         /* FIXME: should this be always true? */
1078         assert( op->o_tag == LDAP_REQ_SEARCH);
1079
1080         /* protocol only allows 0..maxInt;
1081          *
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())
1088          */
1089         if ( op->ors_tlimit == SLAP_NO_LIMIT && op->ors_slimit == SLAP_NO_LIMIT ) {
1090                 return 0;
1091         }
1092
1093         /* allow root to set no limit */
1094         if ( be_isroot( op ) ) {
1095                 op->ors_limit = NULL;
1096
1097                 if ( op->ors_tlimit == 0 ) {
1098                         op->ors_tlimit = SLAP_NO_LIMIT;
1099                 }
1100
1101                 if ( op->ors_slimit == 0 ) {
1102                         op->ors_slimit = SLAP_NO_LIMIT;
1103                 }
1104
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;
1110                         if ( total > 0 ) {
1111                                 op->ors_slimit = total;
1112                         } else {
1113                                 op->ors_slimit = 0;
1114                         }
1115                 }
1116
1117         /* if not root, get appropriate limits */
1118         } else {
1119                 ( void ) limits_get( op, &op->ors_limit );
1120
1121                 assert( op->ors_limit != NULL );
1122
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;
1126
1127                 /* limit required: check if legal */
1128                 } else {
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;
1133                                 }
1134
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;
1139
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;
1145                                         return -1;
1146                                 }
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;
1150                                 }
1151 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1152                         }
1153                 }
1154
1155                 /* else leave as is */
1156
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;
1162                         return -1;
1163                 }
1164
1165                 /* if paged results is requested */     
1166                 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
1167                         int     slimit = -2;
1168                         int     pr_total;
1169                         PagedResultsState *ps = op->o_pagedresults_state;
1170
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;
1177                                 rs->sr_text = NULL;
1178                                 return -1;
1179                         }
1180                         
1181                         if ( op->ors_limit->lms_s_pr > 0
1182                                 && ps->ps_size > op->ors_limit->lms_s_pr )
1183                         {
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;
1188                                 rs->sr_text = NULL;
1189                                 return -1;
1190                         }
1191
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;
1195                                 } else {
1196                                         pr_total = op->ors_limit->lms_s_hard;
1197                                 }
1198                         } else {
1199                                 pr_total = op->ors_limit->lms_s_pr_total;
1200                         }
1201
1202                         if ( pr_total == -1 ) {
1203                                 if ( op->ors_slimit == 0 || op->ors_slimit == SLAP_MAX_LIMIT ) {
1204                                         slimit = -1;
1205
1206                                 } else {
1207                                         slimit = op->ors_slimit - ps->ps_count;
1208                                 }
1209
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 ) )
1214                         {
1215                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1216                                 send_ldap_result( op, rs );
1217                                 rs->sr_err = LDAP_SUCCESS;
1218                                 return -1;
1219 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1220         
1221                         } else {
1222                                 /* if no limit is required, use soft limit */
1223                                 int     total;
1224                                 int     slimit2;
1225
1226                                 /* first round of pagedResults:
1227                                  * set count to any appropriate limit */
1228
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 */
1237                                 {
1238                                         slimit2 = op->ors_slimit = pr_total;
1239
1240                                 } else if ( op->ors_slimit == 0 ) {
1241                                         slimit2 = pr_total;
1242
1243                                 } else {
1244                                         slimit2 = op->ors_slimit;
1245                                 }
1246
1247                                 total = slimit2 - ps->ps_count;
1248
1249                                 if ( total >= 0 ) {
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 ) {
1253                                                         slimit = total;
1254         
1255                                                 } else {
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;
1259                                                 }
1260
1261                                         } else {
1262                                                 /* use the total limit if any */
1263                                                 slimit = total;
1264                                         }
1265
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;
1270
1271                                 } else {
1272                                         /* use the standard hard/soft limit if any */
1273                                         slimit = op->ors_limit->lms_s_hard;
1274                                 }
1275                         }
1276                 
1277                         /* if got any limit, use it */
1278                         if ( slimit != -2 ) {
1279                                 if ( op->ors_slimit == 0 ) {
1280                                         op->ors_slimit = slimit;
1281
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;
1287                                                 return -1;
1288                                         }
1289                                         op->ors_slimit = slimit;
1290
1291                                 } else if ( slimit == 0 ) {
1292                                         op->ors_slimit = 0;
1293                                 }
1294
1295                         } else {
1296                                 /* use the standard hard/soft limit if any */
1297                                 op->ors_slimit = pr_total;
1298                         }
1299
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;
1303
1304                 /* limit requested: check if legal */
1305                 } else {
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;
1311                                 }
1312
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;
1318
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;
1324                                         return -1;
1325                                 }
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;
1329                                 }
1330 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1331                         }
1332                 }
1333
1334                 /* else leave as is */
1335         }
1336
1337         return 0;
1338 }
1339
1340 void
1341 limits_destroy( 
1342         struct slap_limits      **lm )
1343 {
1344         int             i;
1345
1346         if ( lm == NULL ) {
1347                 return;
1348         }
1349
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 );
1354                         break;
1355
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 );
1362                         }
1363                         break;
1364
1365                 default:
1366                         break;
1367                 }
1368
1369                 ch_free( lm[ i ] );
1370         }
1371
1372         ch_free( lm );
1373 }