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