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