]> git.sur5r.net Git - openldap/blob - servers/slapd/limits.c
740eeb449c2f82adc2a820066669a5fec4d7e001
[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         static struct berval empty_dn = BER_BVC( "" );
71         struct slap_limits **lm;
72         struct berval           *ndns[2];
73
74         assert( op != NULL );
75         assert( limit != NULL );
76
77         ndns[0] = &op->o_ndn;
78         ndns[1] = &op->o_req_ndn;
79
80         Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s self=\"%s\" this=\"%s\"\n",
81                         op->o_log_prefix,
82                         BER_BVISNULL( ndns[0] ) ? "[anonymous]" : ndns[0]->bv_val,
83                         BER_BVISNULL( ndns[1] ) ? "" : ndns[1]->bv_val );
84         /*
85          * default values
86          */
87         *limit = &op->o_bd->be_def_limit;
88
89         if ( op->o_bd->be_limits == NULL ) {
90                 return( 0 );
91         }
92
93         for ( lm = op->o_bd->be_limits; lm[0] != NULL; lm++ ) {
94                 unsigned        style = lm[0]->lm_flags & SLAP_LIMITS_MASK;
95                 unsigned        type = lm[0]->lm_flags & SLAP_LIMITS_TYPE_MASK;
96                 unsigned        isthis = type == SLAP_LIMITS_TYPE_THIS;
97                 struct berval *ndn = ndns[isthis];
98
99                 if ( style == SLAP_LIMITS_ANY )
100                         goto found_any;
101
102                 if ( BER_BVISEMPTY( ndn ) ) {
103                         if ( style == SLAP_LIMITS_ANONYMOUS )
104                                 goto found_nodn;
105                         if ( !isthis )
106                                 continue;
107                         ndn = &empty_dn;
108                 }
109
110                 switch ( style ) {
111                 case SLAP_LIMITS_EXACT:
112                         if ( type == SLAP_LIMITS_TYPE_GROUP ) {
113                                 int     rc = backend_group( op, NULL,
114                                                 &lm[0]->lm_pat, ndn,
115                                                 lm[0]->lm_group_oc,
116                                                 lm[0]->lm_group_ad );
117                                 if ( rc == 0 ) {
118                                         goto found_group;
119                                 }
120                         } else {
121                                 if ( dn_match( &lm[0]->lm_pat, ndn ) ) {
122                                         goto found_dn;
123                                 }
124                         }
125                         break;
126
127                 case SLAP_LIMITS_ONE:
128                 case SLAP_LIMITS_SUBTREE:
129                 case SLAP_LIMITS_CHILDREN: {
130                         ber_len_t d;
131                         
132                         /* ndn shorter than lm_pat */
133                         if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) {
134                                 break;
135                         }
136                         d = ndn->bv_len - lm[0]->lm_pat.bv_len;
137
138                         if ( d == 0 ) {
139                                 /* allow exact match for SUBTREE only */
140                                 if ( style != SLAP_LIMITS_SUBTREE ) {
141                                         break;
142                                 }
143                         } else {
144                                 /* check for unescaped rdn separator */
145                                 if ( !DN_SEPARATOR( ndn->bv_val[d - 1] ) ) {
146                                         break;
147                                 }
148                         }
149
150                         /* check that ndn ends with lm_pat */
151                         if ( strcmp( lm[0]->lm_pat.bv_val, &ndn->bv_val[d] ) != 0 ) {
152                                 break;
153                         }
154
155                         /* in case of ONE, require exactly one rdn below lm_pat */
156                         if ( style == SLAP_LIMITS_ONE ) {
157                                 if ( dn_rdnlen( NULL, ndn ) != d - 1 ) {
158                                         break;
159                                 }
160                         }
161
162                         goto found_dn;
163                 }
164
165                 case SLAP_LIMITS_REGEX:
166                         if ( regexec( &lm[0]->lm_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) {
167                                 goto found_dn;
168                         }
169                         break;
170
171                 case SLAP_LIMITS_ANONYMOUS:
172                         break;
173
174                 case SLAP_LIMITS_USERS:
175                 found_nodn:
176                         Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n",
177                                 dn_source[isthis], limits2str( style ), 0 );
178                 found_any:
179                         *limit = &lm[0]->lm_limits;
180                         return( 0 );
181
182                 found_dn:
183                         Debug( LDAP_DEBUG_TRACE,
184                                 "<== limits_get: type=%s match=%s dn=\"%s\"\n",
185                                 dn_source[isthis], limits2str( style ), lm[0]->lm_pat.bv_val );
186                         *limit = &lm[0]->lm_limits;
187                         return( 0 );
188
189                 found_group:
190                         Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=GROUP match=EXACT "
191                                 "dn=\"%s\" oc=\"%s\" ad=\"%s\"\n",
192                                 lm[0]->lm_pat.bv_val,
193                                 lm[0]->lm_group_oc->soc_cname.bv_val,
194                                 lm[0]->lm_group_ad->ad_cname.bv_val );
195                         *limit = &lm[0]->lm_limits;
196                         return( 0 );
197
198                 default:
199                         assert( 0 );    /* unreachable */
200                         return( -1 );
201                 }
202         }
203
204         return( 0 );
205 }
206
207 static int
208 limits_add(
209         Backend                 *be,
210         unsigned                flags,
211         const char              *pattern,
212         ObjectClass             *group_oc,
213         AttributeDescription    *group_ad,
214         struct slap_limits_set  *limit
215 )
216 {
217         int                     i;
218         struct slap_limits      *lm;
219         unsigned                type, style;
220         
221         assert( be != NULL );
222         assert( limit != NULL );
223
224         type = flags & SLAP_LIMITS_TYPE_MASK;
225         style = flags & SLAP_LIMITS_MASK;
226
227         switch ( style ) {
228         case SLAP_LIMITS_ANONYMOUS:
229         case SLAP_LIMITS_USERS:
230         case SLAP_LIMITS_ANY:
231                 /* For these styles, type == 0 (SLAP_LIMITS_TYPE_SELF). */
232                 for ( i = 0; be->be_limits && be->be_limits[ i ]; i++ ) {
233                         if ( be->be_limits[ i ]->lm_flags == style ) {
234                                 return( -1 );
235                         }
236                 }
237                 break;
238         }
239
240
241         lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
242
243         switch ( style ) {
244         case SLAP_LIMITS_UNDEFINED:
245                 style = SLAP_LIMITS_EXACT;
246                 /* continue to next cases */
247         case SLAP_LIMITS_EXACT:
248         case SLAP_LIMITS_ONE:
249         case SLAP_LIMITS_SUBTREE:
250         case SLAP_LIMITS_CHILDREN:
251                 lm->lm_flags = style | type;
252                 {
253                         int rc;
254                         struct berval bv;
255
256                         ber_str2bv( pattern, 0, 0, &bv );
257
258                         rc = dnNormalize( 0, NULL, NULL, &bv, &lm->lm_pat, NULL );
259                         if ( rc != LDAP_SUCCESS ) {
260                                 ch_free( lm );
261                                 return( -1 );
262                         }
263                 }
264                 break;
265                 
266         case SLAP_LIMITS_REGEX:
267                 lm->lm_flags = style | type;
268                 ber_str2bv( pattern, 0, 1, &lm->lm_pat );
269                 if ( regcomp( &lm->lm_regex, lm->lm_pat.bv_val, 
270                                         REG_EXTENDED | REG_ICASE ) ) {
271                         free( lm->lm_pat.bv_val );
272                         ch_free( lm );
273                         return( -1 );
274                 }
275                 break;
276
277         case SLAP_LIMITS_ANONYMOUS:
278         case SLAP_LIMITS_USERS:
279         case SLAP_LIMITS_ANY:
280                 lm->lm_flags = style | type;
281                 BER_BVZERO( &lm->lm_pat );
282                 break;
283         }
284
285         switch ( type ) {
286         case SLAP_LIMITS_TYPE_GROUP:
287                 assert( group_oc != NULL );
288                 assert( group_ad != NULL );
289                 lm->lm_group_oc = group_oc;
290                 lm->lm_group_ad = group_ad;
291                 break;
292         }
293
294         lm->lm_limits = *limit;
295
296         i = 0;
297         if ( be->be_limits != NULL ) {
298                 for ( ; be->be_limits[i]; i++ );
299         }
300
301         be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits,
302                         sizeof( struct slap_limits * ) * ( i + 2 ) );
303         be->be_limits[i] = lm;
304         be->be_limits[i+1] = NULL;
305         
306         return( 0 );
307 }
308
309 #define STRSTART( s, m ) (strncasecmp( s, m, STRLENOF( "" m "" )) == 0)
310
311 int
312 limits_parse(
313         Backend     *be,
314         const char  *fname,
315         int         lineno,
316         int         argc,
317         char        **argv
318 )
319 {
320         int                     flags = SLAP_LIMITS_UNDEFINED;
321         char                    *pattern;
322         struct slap_limits_set  limit;
323         int                     i, rc = 0;
324         ObjectClass             *group_oc = NULL;
325         AttributeDescription    *group_ad = NULL;
326
327         assert( be != NULL );
328
329         if ( argc < 3 ) {
330                 Debug( LDAP_DEBUG_ANY,
331                         "%s : line %d: missing arg(s) in "
332                         "\"limits <pattern> <limits>\" line.\n%s",
333                         fname, lineno, "" );
334                 return( -1 );
335         }
336
337         limit = be->be_def_limit;
338
339         /*
340          * syntax:
341          *
342          * "limits" <pattern> <limit> [ ... ]
343          * 
344          * 
345          * <pattern>:
346          * 
347          * "anonymous"
348          * "users"
349          * [ "dn" [ "." { "this" | "self" } ] [ "." { "exact" | "base" |
350          *      "onelevel" | "subtree" | "children" | "regex" | "anonymous" } ]
351          *      "=" ] <dn pattern>
352          *
353          * Note:
354          *      "this" is the baseobject, "self" (the default) is the bound DN
355          *      "exact" and "base" are the same (exact match);
356          *      "onelevel" means exactly one rdn below, NOT including pattern
357          *      "subtree" means any rdn below, including pattern
358          *      "children" means any rdn below, NOT including pattern
359          *      
360          *      "anonymous" may be deprecated in favour 
361          *      of the pattern = "anonymous" form
362          *
363          * "group[/objectClass[/attributeType]]" "=" "<dn pattern>"
364          *
365          * <limit>:
366          *
367          * "time" [ "." { "soft" | "hard" } ] "=" <integer>
368          *
369          * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
370          */
371         
372         pattern = argv[1];
373         if ( strcmp( pattern, "*" ) == 0) {
374                 flags = SLAP_LIMITS_ANY;
375
376         } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
377                 flags = SLAP_LIMITS_ANONYMOUS;
378
379         } else if ( strcasecmp( pattern, "users" ) == 0 ) {
380                 flags = SLAP_LIMITS_USERS;
381                 
382         } else if ( STRSTART( pattern, "dn" ) ) {
383                 pattern += STRLENOF( "dn" );
384                 flags = SLAP_LIMITS_TYPE_SELF;
385                 if ( pattern[0] == '.' ) {
386                         pattern++;
387                         if ( STRSTART( pattern, "this" ) ) {
388                                 flags = SLAP_LIMITS_TYPE_THIS;
389                                 pattern += STRLENOF( "this" );
390                         } else if ( STRSTART( pattern, "self" ) ) {
391                                 pattern += STRLENOF( "self" );
392                         } else {
393                                 goto got_dn_dot;
394                         }
395                 }
396                 if ( pattern[0] == '.' ) {
397                         pattern++;
398                 got_dn_dot:
399                         if ( STRSTART( pattern, "exact" ) ) {
400                                 flags |= SLAP_LIMITS_EXACT;
401                                 pattern += STRLENOF( "exact" );
402
403                         } else if ( STRSTART( pattern, "base" ) ) {
404                                 flags |= SLAP_LIMITS_BASE;
405                                 pattern += STRLENOF( "base" );
406
407                         } else if ( STRSTART( pattern, "one" ) ) {
408                                 flags |= SLAP_LIMITS_ONE;
409                                 pattern += STRLENOF( "one" );
410                                 if ( STRSTART( pattern, "level" ) ) {
411                                         pattern += STRLENOF( "level" );
412
413                                 } else {
414                                         Debug( LDAP_DEBUG_ANY,
415                                                 "%s : line %d: deprecated \"one\" style "
416                                                 "\"limits <pattern> <limits>\" line; "
417                                                 "use \"onelevel\" instead.\n", fname, lineno, 0 );
418                                 }
419
420                         } else if ( STRSTART( pattern, "sub" ) ) {
421                                 flags |= SLAP_LIMITS_SUBTREE;
422                                 pattern += STRLENOF( "sub" );
423                                 if ( STRSTART( pattern, "tree" ) ) {
424                                         pattern += STRLENOF( "tree" );
425
426                                 } else {
427                                         Debug( LDAP_DEBUG_ANY,
428                                                 "%s : line %d: deprecated \"sub\" style "
429                                                 "\"limits <pattern> <limits>\" line; "
430                                                 "use \"subtree\" instead.\n", fname, lineno, 0 );
431                                 }
432
433                         } else if ( STRSTART( pattern, "children" ) ) {
434                                 flags |= SLAP_LIMITS_CHILDREN;
435                                 pattern += STRLENOF( "children" );
436
437                         } else if ( STRSTART( pattern, "regex" ) ) {
438                                 flags |= SLAP_LIMITS_REGEX;
439                                 pattern += STRLENOF( "regex" );
440
441                         /* 
442                          * this could be deprecated in favour
443                          * of the pattern = "anonymous" form
444                          */
445                         } else if ( STRSTART( pattern, "anonymous" )
446                                         && flags == SLAP_LIMITS_TYPE_SELF )
447                         {
448                                 flags = SLAP_LIMITS_ANONYMOUS;
449                                 pattern = NULL;
450                         }
451                 }
452
453                 /* pre-check the data */
454                 if ( pattern != NULL ) {
455                         if ( pattern[0] != '=' ) {
456                                 Debug( LDAP_DEBUG_ANY,
457                                         "%s : line %d: missing '=' in "
458                                         "\"dn[.{this|self}][.{exact|base"
459                                         "|onelevel|subtree|children|regex}]"
460                                         "=<pattern>\" in "
461                                         "\"limits <pattern> <limits>\" "
462                                         "line.\n%s",
463                                         fname, lineno, "" );
464                                 return( -1 );
465                         }
466
467                         /* skip '=' (required) */
468                         pattern++;
469
470                         /* trim obvious cases */
471                         if ( strcmp( pattern, "*" ) == 0 ) {
472                                 flags = SLAP_LIMITS_ANY;
473                                 pattern = NULL;
474
475                         } else if ( (flags & SLAP_LIMITS_MASK) == SLAP_LIMITS_REGEX
476                                         && strcmp( pattern, ".*" ) == 0 ) {
477                                 flags = SLAP_LIMITS_ANY;
478                                 pattern = NULL;
479                         }
480                 }
481
482         } else if (STRSTART( pattern, "group" ) ) {
483                 pattern += STRLENOF( "group" );
484
485                 if ( pattern[0] == '/' ) {
486                         struct berval   oc, ad;
487
488                         oc.bv_val = pattern + 1;
489                         pattern = strchr( pattern, '=' );
490                         if ( pattern == NULL ) {
491                                 return -1;
492                         }
493
494                         ad.bv_val = strchr( oc.bv_val, '/' );
495                         if ( ad.bv_val != NULL ) {
496                                 const char      *text = NULL;
497
498                                 oc.bv_len = ad.bv_val - oc.bv_val;
499
500                                 ad.bv_val++;
501                                 ad.bv_len = pattern - ad.bv_val;
502                                 rc = slap_bv2ad( &ad, &group_ad, &text );
503                                 if ( rc != LDAP_SUCCESS ) {
504                                         goto no_ad;
505                                 }
506
507                         } else {
508                                 oc.bv_len = pattern - oc.bv_val;
509                         }
510
511                         group_oc = oc_bvfind( &oc );
512                         if ( group_oc == NULL ) {
513                                 goto no_oc;
514                         }
515                 }
516
517                 if ( group_oc == NULL ) {
518                         group_oc = oc_find( SLAPD_GROUP_CLASS );
519                         if ( group_oc == NULL ) {
520 no_oc:;
521                                 return( -1 );
522                         }
523                 }
524
525                 if ( group_ad == NULL ) {
526                         const char      *text = NULL;
527                         
528                         rc = slap_str2ad( SLAPD_GROUP_ATTR, &group_ad, &text );
529
530                         if ( rc != LDAP_SUCCESS ) {
531 no_ad:;
532                                 return( -1 );
533                         }
534                 }
535
536                 flags = SLAP_LIMITS_TYPE_GROUP | SLAP_LIMITS_EXACT;
537
538                 if ( pattern[0] != '=' ) {
539                         Debug( LDAP_DEBUG_ANY,
540                                 "%s : line %d: missing '=' in "
541                                 "\"group[/objectClass[/attributeType]]"
542                                 "=<pattern>\" in "
543                                 "\"limits <pattern> <limits>\" line.\n",
544                                 fname, lineno, 0 );
545                         return( -1 );
546                 }
547
548                 /* skip '=' (required) */
549                 pattern++;
550         }
551
552         /* get the limits */
553         for ( i = 2; i < argc; i++ ) {
554                 if ( limits_parse_one( argv[i], &limit ) ) {
555
556                         Debug( LDAP_DEBUG_ANY,
557                                 "%s : line %d: unknown limit values \"%s\" in "
558                                 "\"limits <pattern> <limits>\" line.\n",
559                         fname, lineno, argv[i] );
560
561                         return( 1 );
562                 }
563         }
564
565         /*
566          * sanity checks ...
567          *
568          * FIXME: add warnings?
569          */
570         if ( limit.lms_t_hard > 0 && 
571                         ( limit.lms_t_hard < limit.lms_t_soft 
572                           || limit.lms_t_soft == -1 ) ) {
573                 limit.lms_t_hard = limit.lms_t_soft;
574         }
575         
576         if ( limit.lms_s_hard > 0 && 
577                         ( limit.lms_s_hard < limit.lms_s_soft 
578                           || limit.lms_s_soft == -1 ) ) {
579                 limit.lms_s_hard = limit.lms_s_soft;
580         }
581
582         /*
583          * defaults ...
584          * 
585          * lms_t_hard:
586          *      -1      => no limits
587          *      0       => same as soft
588          *      > 0     => limit (in seconds)
589          *
590          * lms_s_hard:
591          *      -1      => no limits
592          *      0       0> same as soft
593          *      > 0     => limit (in entries)
594          *
595          * lms_s_pr_total:
596          *      -2      => disable the control
597          *      -1      => no limits
598          *      0       => same as soft
599          *      > 0     => limit (in entries)
600          *
601          * lms_s_pr:
602          *      -1      => no limits
603          *      0       => no limits?
604          *      > 0     => limit size (in entries)
605          */
606         if ( limit.lms_s_pr_total > 0 &&
607                         limit.lms_s_pr > limit.lms_s_pr_total ) {
608                 limit.lms_s_pr = limit.lms_s_pr_total;
609         }
610
611         rc = limits_add( be, flags, pattern, group_oc, group_ad, &limit );
612         if ( rc ) {
613
614                 Debug( LDAP_DEBUG_ANY,
615                         "%s : line %d: unable to add limit in "
616                         "\"limits <pattern> <limits>\" line.\n",
617                 fname, lineno, 0 );
618         }
619
620         return( rc );
621 }
622
623 int
624 limits_parse_one(
625         const char              *arg,
626         struct slap_limits_set  *limit
627 )
628 {
629         assert( arg != NULL );
630         assert( limit != NULL );
631
632         if ( STRSTART( arg, "time" ) ) {
633                 arg += STRLENOF( "time" );
634
635                 if ( arg[0] == '.' ) {
636                         arg++;
637                         if ( STRSTART( arg, "soft=" ) ) {
638                                 arg += STRLENOF( "soft=" );
639                                 if ( strcasecmp( arg, "unlimited" ) == 0
640                                         || strcasecmp( arg, "none" ) == 0 )
641                                 {
642                                         limit->lms_t_soft = -1;
643
644                                 } else {
645                                         int     soft;
646
647                                         if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
648                                                 return( 1 );
649                                         }
650
651                                         if ( soft == -1 ) {
652                                                 /* FIXME: use "unlimited" instead; issue warning? */
653                                         }
654
655                                         limit->lms_t_soft = soft;
656                                 }
657                                 
658                         } else if ( STRSTART( arg, "hard=" ) ) {
659                                 arg += STRLENOF( "hard=" );
660                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
661                                         limit->lms_t_hard = 0;
662
663                                 } else if ( strcasecmp( arg, "unlimited" ) == 0
664                                                 || strcasecmp( arg, "none" ) == 0 )
665                                 {
666                                         limit->lms_t_hard = -1;
667
668                                 } else {
669                                         int     hard;
670
671                                         if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
672                                                 return( 1 );
673                                         }
674
675                                         if ( hard == -1 ) {
676                                                 /* FIXME: use "unlimited" instead */
677                                         }
678
679                                         if ( hard == 0 ) {
680                                                 /* FIXME: use "soft" instead */
681                                         }
682
683                                         limit->lms_t_hard = hard;
684                                 }
685                                 
686                         } else {
687                                 return( 1 );
688                         }
689                         
690                 } else if ( arg[0] == '=' ) {
691                         arg++;
692                         if ( strcasecmp( arg, "unlimited" ) == 0
693                                 || strcasecmp( arg, "none" ) == 0 )
694                         {
695                                 limit->lms_t_soft = -1;
696
697                         } else {
698                                 if ( lutil_atoi( &limit->lms_t_soft, arg ) != 0 
699                                         || limit->lms_t_soft < -1 )
700                                 {
701                                         return( 1 );
702                                 }
703                         }
704                         limit->lms_t_hard = 0;
705                         
706                 } else {
707                         return( 1 );
708                 }
709
710         } else if ( STRSTART( arg, "size" ) ) {
711                 arg += STRLENOF( "size" );
712                 
713                 if ( arg[0] == '.' ) {
714                         arg++;
715                         if ( STRSTART( arg, "soft=" ) ) {
716                                 arg += STRLENOF( "soft=" );
717                                 if ( strcasecmp( arg, "unlimited" ) == 0
718                                         || strcasecmp( arg, "none" ) == 0 )
719                                 {
720                                         limit->lms_s_soft = -1;
721
722                                 } else {
723                                         int     soft;
724
725                                         if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
726                                                 return( 1 );
727                                         }
728
729                                         if ( soft == -1 ) {
730                                                 /* FIXME: use "unlimited" instead */
731                                         }
732
733                                         limit->lms_s_soft = soft;
734                                 }
735                                 
736                         } else if ( STRSTART( arg, "hard=" ) ) {
737                                 arg += STRLENOF( "hard=" );
738                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
739                                         limit->lms_s_hard = 0;
740
741                                 } else if ( strcasecmp( arg, "unlimited" ) == 0
742                                                 || strcasecmp( arg, "none" ) == 0 )
743                                 {
744                                         limit->lms_s_hard = -1;
745
746                                 } else {
747                                         int     hard;
748
749                                         if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
750                                                 return( 1 );
751                                         }
752
753                                         if ( hard == -1 ) {
754                                                 /* FIXME: use "unlimited" instead */
755                                         }
756
757                                         if ( hard == 0 ) {
758                                                 /* FIXME: use "soft" instead */
759                                         }
760
761                                         limit->lms_s_hard = hard;
762                                 }
763                                 
764                         } else if ( STRSTART( arg, "unchecked=" ) ) {
765                                 arg += STRLENOF( "unchecked=" );
766                                 if ( strcasecmp( arg, "unlimited" ) == 0
767                                         || strcasecmp( arg, "none" ) == 0 )
768                                 {
769                                         limit->lms_s_unchecked = -1;
770
771                                 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
772                                         limit->lms_s_unchecked = 0;
773
774                                 } else {
775                                         int     unchecked;
776
777                                         if ( lutil_atoi( &unchecked, arg ) != 0 || unchecked < -1 ) {
778                                                 return( 1 );
779                                         }
780
781                                         if ( unchecked == -1 ) {
782                                                 /*  FIXME: use "unlimited" instead */
783                                         }
784
785                                         limit->lms_s_unchecked = unchecked;
786                                 }
787
788                         } else if ( STRSTART( arg, "pr=" ) ) {
789                                 arg += STRLENOF( "pr=" );
790                                 if ( strcasecmp( arg, "noEstimate" ) == 0 ) {
791                                         limit->lms_s_pr_hide = 1;
792
793                                 } else if ( strcasecmp( arg, "unlimited" ) == 0
794                                                 || strcasecmp( arg, "none" ) == 0 )
795                                 {
796                                         limit->lms_s_pr = -1;
797
798                                 } else {
799                                         int     pr;
800
801                                         if ( lutil_atoi( &pr, arg ) != 0 || pr < -1 ) {
802                                                 return( 1 );
803                                         }
804
805                                         if ( pr == -1 ) {
806                                                 /* FIXME: use "unlimited" instead */
807                                         }
808
809                                         limit->lms_s_pr = pr;
810                                 }
811
812                         } else if ( STRSTART( arg, "prtotal=" ) ) {
813                                 arg += STRLENOF( "prtotal=" );
814
815                                 if ( strcasecmp( arg, "unlimited" ) == 0
816                                         || strcasecmp( arg, "none" ) == 0 )
817                                 {
818                                         limit->lms_s_pr_total = -1;
819
820                                 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
821                                         limit->lms_s_pr_total = -2;
822
823                                 } else if ( strcasecmp( arg, "hard" ) == 0 ) {
824                                         limit->lms_s_pr_total = 0;
825
826                                 } else {
827                                         int     total;
828
829                                         if ( lutil_atoi( &total, arg ) != 0 || total < -1 ) {
830                                                 return( 1 );
831                                         }
832
833                                         if ( total == -1 ) {
834                                                 /* FIXME: use "unlimited" instead */
835                                         }
836
837                                         if ( total == 0 ) {
838                                                 /* FIXME: use "pr=disable" instead */
839                                         }
840
841                                         limit->lms_s_pr_total = total;
842                                 }
843
844                         } else {
845                                 return( 1 );
846                         }
847                         
848                 } else if ( arg[0] == '=' ) {
849                         arg++;
850                         if ( strcasecmp( arg, "unlimited" ) == 0
851                                 || strcasecmp( arg, "none" ) == 0 )
852                         {
853                                 limit->lms_s_soft = -1;
854
855                         } else {
856                                 if ( lutil_atoi( &limit->lms_s_soft, arg ) != 0
857                                         || limit->lms_s_soft < -1 )
858                                 {
859                                         return( 1 );
860                                 }
861                         }
862                         limit->lms_s_hard = 0;
863                         
864                 } else {
865                         return( 1 );
866                 }
867         }
868
869         return 0;
870 }
871
872 /* Helper macros for limits_unparse() and limits_unparse_one():
873  * Write to ptr, but not past bufEnd.  Move ptr past the new text.
874  * Return (success && enough room ? 0 : -1).
875  */
876 #define ptr_APPEND_BV(bv) /* Append a \0-terminated berval */ \
877         (WHATSLEFT <= (bv).bv_len ? -1 : \
878          ((void) (ptr = lutil_strcopy( ptr, (bv).bv_val )), 0))
879 #define ptr_APPEND_LIT(str) /* Append a string literal */ \
880         (WHATSLEFT <= STRLENOF( "" str "" ) ? -1 : \
881          ((void) (ptr = lutil_strcopy( ptr, str )), 0))
882 #define ptr_APPEND_FMT(args) /* Append formatted text */ \
883         (WHATSLEFT <= (tmpLen = snprintf args) ? -1 : ((void) (ptr += tmpLen), 0))
884 #define ptr_APPEND_FMT1(fmt, arg) ptr_APPEND_FMT(( ptr, WHATSLEFT, fmt, arg ))
885 #define WHATSLEFT ((ber_len_t) (bufEnd - ptr))
886
887 /* Caller must provide an adequately sized buffer in bv */
888 int
889 limits_unparse( struct slap_limits *lim, struct berval *bv, ber_len_t buflen )
890 {
891         struct berval btmp;
892         char *ptr, *bufEnd;                     /* Updated/used by ptr_APPEND_*()/WHATSLEFT */
893         ber_len_t tmpLen;                       /* Used by ptr_APPEND_FMT*() */
894         unsigned type, style;
895         int rc = 0;
896
897         if ( !bv || !bv->bv_val ) return -1;
898
899         ptr = bv->bv_val;
900         bufEnd = ptr + buflen;
901         type = lim->lm_flags & SLAP_LIMITS_TYPE_MASK;
902
903         if ( type == SLAP_LIMITS_TYPE_GROUP ) {
904                 rc = ptr_APPEND_FMT(( ptr, WHATSLEFT, "group/%s/%s=\"%s\"",
905                         lim->lm_group_oc->soc_cname.bv_val,
906                         lim->lm_group_ad->ad_cname.bv_val,
907                         lim->lm_pat.bv_val ));
908         } else {
909                 style = lim->lm_flags & SLAP_LIMITS_MASK;
910                 switch( style ) {
911                 case SLAP_LIMITS_ANONYMOUS:
912                 case SLAP_LIMITS_USERS:
913                 case SLAP_LIMITS_ANY:
914                         rc = ptr_APPEND_BV( lmpats[style] );
915                         break;
916                 case SLAP_LIMITS_UNDEFINED:
917                 case SLAP_LIMITS_EXACT:
918                 case SLAP_LIMITS_ONE:
919                 case SLAP_LIMITS_SUBTREE:
920                 case SLAP_LIMITS_CHILDREN:
921                 case SLAP_LIMITS_REGEX:
922                         rc = ptr_APPEND_FMT(( ptr, WHATSLEFT, "dn.%s%s=\"%s\"",
923                                 type == SLAP_LIMITS_TYPE_SELF ? "" : "this.",
924                                 lmpats[style].bv_val, lim->lm_pat.bv_val ));
925                         break;
926                 }
927         }
928         if ( rc == 0 ) {
929                 bv->bv_len = ptr - bv->bv_val;
930                 btmp.bv_val = ptr;
931                 btmp.bv_len = 0;
932                 rc = limits_unparse_one( &lim->lm_limits,
933                         SLAP_LIMIT_SIZE | SLAP_LIMIT_TIME,
934                         &btmp, WHATSLEFT );
935                 if ( rc == 0 )
936                         bv->bv_len += btmp.bv_len;
937         }
938         return rc;
939 }
940
941 /* Caller must provide an adequately sized buffer in bv */
942 int
943 limits_unparse_one(
944         struct slap_limits_set  *lim,
945         int                             which,
946         struct berval   *bv,
947         ber_len_t               buflen )
948 {
949         char *ptr, *bufEnd;                     /* Updated/used by ptr_APPEND_*()/WHATSLEFT */
950         ber_len_t tmpLen;                       /* Used by ptr_APPEND_FMT*() */
951
952         if ( !bv || !bv->bv_val ) return -1;
953
954         ptr = bv->bv_val;
955         bufEnd = ptr + buflen;
956
957         if ( which & SLAP_LIMIT_SIZE ) {
958                 if ( lim->lms_s_soft != SLAPD_DEFAULT_SIZELIMIT ) {
959
960                         /* If same as global limit, drop it */
961                         if ( lim != &frontendDB->be_def_limit &&
962                                 lim->lms_s_soft == frontendDB->be_def_limit.lms_s_soft )
963                         {
964                                 goto s_hard;
965                         /* If there's also a hard limit, fully qualify this one */
966                         } else if ( lim->lms_s_hard ) {
967                                 if ( ptr_APPEND_LIT( " size.soft=" ) ) return -1;
968
969                         /* If doing both size & time, qualify this */
970                         } else if ( which & SLAP_LIMIT_TIME ) {
971                                 if ( ptr_APPEND_LIT( " size=" ) ) return -1;
972                         }
973
974                         if ( lim->lms_s_soft == -1
975                                         ? ptr_APPEND_LIT( "unlimited " )
976                                         : ptr_APPEND_FMT1( "%d ", lim->lms_s_soft ) )
977                                 return -1;
978                 }
979 s_hard:
980                 if ( lim->lms_s_hard ) {
981                         if ( ptr_APPEND_LIT( " size.hard=" ) ) return -1;
982                         if ( lim->lms_s_hard == -1
983                                         ? ptr_APPEND_LIT( "unlimited " )
984                                         : ptr_APPEND_FMT1( "%d ", lim->lms_s_hard ) )
985                                 return -1;
986                 }
987                 if ( lim->lms_s_unchecked != -1 ) {
988                         if ( ptr_APPEND_LIT( " size.unchecked=" ) ) return -1;
989                         if ( lim->lms_s_unchecked == 0
990                                         ? ptr_APPEND_LIT( "disabled " )
991                                         : ptr_APPEND_FMT1( "%d ", lim->lms_s_unchecked ) )
992                                 return -1;
993                 }
994                 if ( lim->lms_s_pr_hide ) {
995                         if ( ptr_APPEND_LIT( " size.pr=noEstimate " ) ) return -1;
996                 }
997                 if ( lim->lms_s_pr ) {
998                         if ( ptr_APPEND_LIT( " size.pr=" ) ) return -1;
999                         if ( lim->lms_s_pr == -1
1000                                         ? ptr_APPEND_LIT( "unlimited " )
1001                                         : ptr_APPEND_FMT1( "%d ", lim->lms_s_pr ) )
1002                                 return -1;
1003                 }
1004                 if ( lim->lms_s_pr_total ) {
1005                         if ( ptr_APPEND_LIT( " size.prtotal=" ) ) return -1;
1006                         if ( lim->lms_s_pr_total  == -1 ? ptr_APPEND_LIT( "unlimited " )
1007                                 : lim->lms_s_pr_total == -2 ? ptr_APPEND_LIT( "disabled " )
1008                                 : ptr_APPEND_FMT1( "%d ", lim->lms_s_pr_total ) )
1009                                 return -1;
1010                 }
1011         }
1012
1013         if ( which & SLAP_LIMIT_TIME ) {
1014                 if ( lim->lms_t_soft != SLAPD_DEFAULT_TIMELIMIT ) {
1015
1016                         /* If same as global limit, drop it */
1017                         if ( lim != &frontendDB->be_def_limit &&
1018                                 lim->lms_t_soft == frontendDB->be_def_limit.lms_t_soft )
1019                         {
1020                                 goto t_hard;
1021
1022                         /* If there's also a hard limit, fully qualify this one */
1023                         } else if ( lim->lms_t_hard ) {
1024                                 if ( ptr_APPEND_LIT( " time.soft=" ) ) return -1;
1025
1026                         /* If doing both size & time, qualify this */
1027                         } else if ( which & SLAP_LIMIT_SIZE ) {
1028                                 if ( ptr_APPEND_LIT( " time=" ) ) return -1;
1029                         }
1030
1031                         if ( lim->lms_t_soft == -1
1032                                         ? ptr_APPEND_LIT( "unlimited " )
1033                                         : ptr_APPEND_FMT1( "%d ", lim->lms_t_soft ) )
1034                                 return -1;
1035                 }
1036 t_hard:
1037                 if ( lim->lms_t_hard ) {
1038                         if ( ptr_APPEND_LIT( " time.hard=" ) ) return -1;
1039                         if ( lim->lms_t_hard == -1
1040                                         ? ptr_APPEND_LIT( "unlimited " )
1041                                         : ptr_APPEND_FMT1( "%d ", lim->lms_t_hard ) )
1042                                 return -1;
1043                 }
1044         }
1045         if ( ptr != bv->bv_val ) {
1046                 ptr--;
1047                 *ptr = '\0';
1048                 bv->bv_len = ptr - bv->bv_val;
1049         }
1050
1051         return 0;
1052 }
1053
1054 int
1055 limits_check( Operation *op, SlapReply *rs )
1056 {
1057         assert( op != NULL );
1058         assert( rs != NULL );
1059         /* FIXME: should this be always true? */
1060         assert( op->o_tag == LDAP_REQ_SEARCH);
1061
1062         /* protocol only allows 0..maxInt;
1063          *
1064          * internal searches:
1065          * - may use SLAP_NO_LIMIT ( = -1 ) to indicate no limits;
1066          * - should use slimit = N and tlimit = SLAP_NO_LIMIT to
1067          *   indicate searches that should return exactly N matches,
1068          *   and handle errors thru a callback (see for instance
1069          *   slap_sasl_match() and slap_sasl2dn())
1070          */
1071         if ( op->ors_tlimit == SLAP_NO_LIMIT && op->ors_slimit == SLAP_NO_LIMIT ) {
1072                 return 0;
1073         }
1074
1075         /* allow root to set no limit */
1076         if ( be_isroot( op ) ) {
1077                 op->ors_limit = NULL;
1078
1079                 if ( op->ors_tlimit == 0 ) {
1080                         op->ors_tlimit = SLAP_NO_LIMIT;
1081                 }
1082
1083                 if ( op->ors_slimit == 0 ) {
1084                         op->ors_slimit = SLAP_NO_LIMIT;
1085                 }
1086
1087                 /* if paged results and slimit are requested */ 
1088                 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED &&
1089                         op->ors_slimit != SLAP_NO_LIMIT ) {
1090                         PagedResultsState *ps = op->o_pagedresults_state;
1091                         int total = op->ors_slimit - ps->ps_count;
1092                         if ( total > 0 ) {
1093                                 op->ors_slimit = total;
1094                         } else {
1095                                 op->ors_slimit = 0;
1096                         }
1097                 }
1098
1099         /* if not root, get appropriate limits */
1100         } else {
1101                 ( void ) limits_get( op, &op->ors_limit );
1102
1103                 assert( op->ors_limit != NULL );
1104
1105                 /* if no limit is required, use soft limit */
1106                 if ( op->ors_tlimit == 0 ) {
1107                         op->ors_tlimit = op->ors_limit->lms_t_soft;
1108
1109                 /* limit required: check if legal */
1110                 } else {
1111                         if ( op->ors_limit->lms_t_hard == 0 ) {
1112                                 if ( op->ors_limit->lms_t_soft > 0
1113                                                 && ( op->ors_tlimit > op->ors_limit->lms_t_soft ) ) {
1114                                         op->ors_tlimit = op->ors_limit->lms_t_soft;
1115                                 }
1116
1117                         } else if ( op->ors_limit->lms_t_hard > 0 ) {
1118 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1119                                 if ( op->ors_tlimit == SLAP_MAX_LIMIT ) {
1120                                         op->ors_tlimit = op->ors_limit->lms_t_hard;
1121
1122                                 } else if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
1123                                         /* error if exceeding hard limit */
1124                                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1125                                         send_ldap_result( op, rs );
1126                                         rs->sr_err = LDAP_SUCCESS;
1127                                         return -1;
1128                                 }
1129 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1130                                 if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
1131                                         op->ors_tlimit = op->ors_limit->lms_t_hard;
1132                                 }
1133 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1134                         }
1135                 }
1136
1137                 /* else leave as is */
1138
1139                 /* don't even get to backend if candidate check is disabled */
1140                 if ( op->ors_limit->lms_s_unchecked == 0 ) {
1141                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1142                         send_ldap_result( op, rs );
1143                         rs->sr_err = LDAP_SUCCESS;
1144                         return -1;
1145                 }
1146
1147                 /* if paged results is requested */     
1148                 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
1149                         int     slimit = -2;
1150                         int     pr_total;
1151                         PagedResultsState *ps = op->o_pagedresults_state;
1152
1153                         /* paged results is not allowed */
1154                         if ( op->ors_limit->lms_s_pr_total == -2 ) {
1155                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1156                                 rs->sr_text = "pagedResults control not allowed";
1157                                 send_ldap_result( op, rs );
1158                                 rs->sr_err = LDAP_SUCCESS;
1159                                 rs->sr_text = NULL;
1160                                 return -1;
1161                         }
1162                         
1163                         if ( op->ors_limit->lms_s_pr > 0
1164                                 && ps->ps_size > op->ors_limit->lms_s_pr )
1165                         {
1166                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1167                                 rs->sr_text = "illegal pagedResults page size";
1168                                 send_ldap_result( op, rs );
1169                                 rs->sr_err = LDAP_SUCCESS;
1170                                 rs->sr_text = NULL;
1171                                 return -1;
1172                         }
1173
1174                         if ( op->ors_limit->lms_s_pr_total == 0 ) {
1175                                 if ( op->ors_limit->lms_s_hard == 0 ) {
1176                                         pr_total = op->ors_limit->lms_s_soft;
1177                                 } else {
1178                                         pr_total = op->ors_limit->lms_s_hard;
1179                                 }
1180                         } else {
1181                                 pr_total = op->ors_limit->lms_s_pr_total;
1182                         }
1183
1184                         if ( pr_total == -1 ) {
1185                                 if ( op->ors_slimit == 0 || op->ors_slimit == SLAP_MAX_LIMIT ) {
1186                                         slimit = -1;
1187
1188                                 } else {
1189                                         slimit = op->ors_slimit - ps->ps_count;
1190                                 }
1191
1192 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1193                         } else if ( pr_total > 0 && op->ors_slimit != SLAP_MAX_LIMIT
1194                                         && ( op->ors_slimit == SLAP_NO_LIMIT
1195                                                 || op->ors_slimit > pr_total ) )
1196                         {
1197                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1198                                 send_ldap_result( op, rs );
1199                                 rs->sr_err = LDAP_SUCCESS;
1200                                 return -1;
1201 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1202         
1203                         } else {
1204                                 /* if no limit is required, use soft limit */
1205                                 int     total;
1206                                 int     slimit2;
1207
1208                                 /* first round of pagedResults:
1209                                  * set count to any appropriate limit */
1210
1211                                 /* if the limit is set, check that it does
1212                                  * not violate any server-side limit */
1213 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1214                                 if ( op->ors_slimit == SLAP_MAX_LIMIT )
1215 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1216                                 if ( op->ors_slimit == SLAP_MAX_LIMIT
1217                                         || op->ors_slimit > pr_total )
1218 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1219                                 {
1220                                         slimit2 = op->ors_slimit = pr_total;
1221
1222                                 } else if ( op->ors_slimit == 0 ) {
1223                                         slimit2 = pr_total;
1224
1225                                 } else {
1226                                         slimit2 = op->ors_slimit;
1227                                 }
1228
1229                                 total = slimit2 - ps->ps_count;
1230
1231                                 if ( total >= 0 ) {
1232                                         if ( op->ors_limit->lms_s_pr > 0 ) {
1233                                                 /* use the smallest limit set by total/per page */
1234                                                 if ( total < op->ors_limit->lms_s_pr ) {
1235                                                         slimit = total;
1236         
1237                                                 } else {
1238                                                         /* use the perpage limit if any 
1239                                                          * NOTE: + 1 because given value must be legal */
1240                                                         slimit = op->ors_limit->lms_s_pr + 1;
1241                                                 }
1242
1243                                         } else {
1244                                                 /* use the total limit if any */
1245                                                 slimit = total;
1246                                         }
1247
1248                                 } else if ( op->ors_limit->lms_s_pr > 0 ) {
1249                                         /* use the perpage limit if any 
1250                                          * NOTE: + 1 because the given value must be legal */
1251                                         slimit = op->ors_limit->lms_s_pr + 1;
1252
1253                                 } else {
1254                                         /* use the standard hard/soft limit if any */
1255                                         slimit = op->ors_limit->lms_s_hard;
1256                                 }
1257                         }
1258                 
1259                         /* if got any limit, use it */
1260                         if ( slimit != -2 ) {
1261                                 if ( op->ors_slimit == 0 ) {
1262                                         op->ors_slimit = slimit;
1263
1264                                 } else if ( slimit > 0 ) {
1265                                         if ( op->ors_slimit - ps->ps_count > slimit ) {
1266                                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1267                                                 send_ldap_result( op, rs );
1268                                                 rs->sr_err = LDAP_SUCCESS;
1269                                                 return -1;
1270                                         }
1271                                         op->ors_slimit = slimit;
1272
1273                                 } else if ( slimit == 0 ) {
1274                                         op->ors_slimit = 0;
1275                                 }
1276
1277                         } else {
1278                                 /* use the standard hard/soft limit if any */
1279                                 op->ors_slimit = pr_total;
1280                         }
1281
1282                 /* no limit requested: use soft, whatever it is */
1283                 } else if ( op->ors_slimit == 0 ) {
1284                         op->ors_slimit = op->ors_limit->lms_s_soft;
1285
1286                 /* limit requested: check if legal */
1287                 } else {
1288                         /* hard limit as soft (traditional behavior) */
1289                         if ( op->ors_limit->lms_s_hard == 0 ) {
1290                                 if ( op->ors_limit->lms_s_soft > 0
1291                                                 && op->ors_slimit > op->ors_limit->lms_s_soft ) {
1292                                         op->ors_slimit = op->ors_limit->lms_s_soft;
1293                                 }
1294
1295                         /* explicit hard limit: error if violated */
1296                         } else if ( op->ors_limit->lms_s_hard > 0 ) {
1297 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1298                                 if ( op->ors_slimit == SLAP_MAX_LIMIT ) {
1299                                         op->ors_slimit = op->ors_limit->lms_s_hard;
1300
1301                                 } else if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
1302                                         /* if limit exceeds hard, error */
1303                                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1304                                         send_ldap_result( op, rs );
1305                                         rs->sr_err = LDAP_SUCCESS;
1306                                         return -1;
1307                                 }
1308 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1309                                 if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
1310                                         op->ors_slimit = op->ors_limit->lms_s_hard;
1311                                 }
1312 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1313                         }
1314                 }
1315
1316                 /* else leave as is */
1317         }
1318
1319         return 0;
1320 }
1321
1322 void
1323 limits_destroy( 
1324         struct slap_limits      **lm )
1325 {
1326         int             i;
1327
1328         if ( lm == NULL ) {
1329                 return;
1330         }
1331
1332         for ( i = 0; lm[ i ]; i++ ) {
1333                 switch ( lm[ i ]->lm_flags & SLAP_LIMITS_MASK ) {
1334                 case SLAP_LIMITS_REGEX:
1335                         regfree( &lm[ i ]->lm_regex );
1336                         break;
1337
1338                 case SLAP_LIMITS_EXACT:
1339                 case SLAP_LIMITS_ONE:
1340                 case SLAP_LIMITS_SUBTREE:
1341                 case SLAP_LIMITS_CHILDREN:
1342                         if ( !BER_BVISNULL( &lm[ i ]->lm_pat ) ) {
1343                                 ch_free( lm[ i ]->lm_pat.bv_val );
1344                         }
1345                         break;
1346
1347                 default:
1348                         break;
1349                 }
1350
1351                 ch_free( lm[ i ] );
1352         }
1353
1354         ch_free( lm );
1355 }