]> git.sur5r.net Git - openldap/blob - servers/slapd/limits.c
Sync with HEAD in prep for 2.3.3beta
[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-2005 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 );
76         assert( limit );
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 ( ndn->bv_len == 0 ) {
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 ( ndn->bv_len == 0 ) {
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 ( ndn->bv_len == 0 ) {
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 ( ndn->bv_len == 0 ) {
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 ( ndn->bv_len != 0 ) {
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 );
242         assert( limit );
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                         bv.bv_val = (char *) pattern;
275                         bv.bv_len = strlen( pattern );
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                 lm->lm_pat.bv_val = NULL;
301                 lm->lm_pat.bv_len = 0;
302                 break;
303         }
304
305         switch ( type ) {
306         case SLAP_LIMITS_TYPE_GROUP:
307                 assert( group_oc );
308                 assert( group_ad );
309                 lm->lm_group_oc = group_oc;
310                 lm->lm_group_ad = group_ad;
311                 break;
312         }
313
314         lm->lm_limits = *limit;
315
316         i = 0;
317         if ( be->be_limits != NULL ) {
318                 for ( ; be->be_limits[i]; i++ );
319         }
320
321         be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits,
322                         sizeof( struct slap_limits * ) * ( i + 2 ) );
323         be->be_limits[i] = lm;
324         be->be_limits[i+1] = NULL;
325         
326         return( 0 );
327 }
328
329 int
330 limits_parse(
331         Backend     *be,
332         const char  *fname,
333         int         lineno,
334         int         argc,
335         char        **argv
336 )
337 {
338         int                     flags = SLAP_LIMITS_UNDEFINED;
339         char                    *pattern;
340         struct slap_limits_set  limit;
341         int                     i, rc = 0;
342         ObjectClass             *group_oc = NULL;
343         AttributeDescription    *group_ad = NULL;
344
345         assert( be );
346
347         if ( argc < 3 ) {
348                 Debug( LDAP_DEBUG_ANY,
349                         "%s : line %d: missing arg(s) in "
350                         "\"limits <pattern> <limits>\" line.\n%s",
351                         fname, lineno, "" );
352                 return( -1 );
353         }
354
355         limit = be->be_def_limit;
356
357         /*
358          * syntax:
359          *
360          * "limits" <pattern> <limit> [ ... ]
361          * 
362          * 
363          * <pattern>:
364          * 
365          * "anonymous"
366          * "users"
367          * [ "dn" [ "." { "exact" | "base" | "onelevel" | "subtree" | children"
368          *      | "regex" | "anonymous" } ] "=" ] <dn pattern>
369          *
370          * Note:
371          *      "exact" and "base" are the same (exact match);
372          *      "onelevel" means exactly one rdn below, NOT including pattern
373          *      "subtree" means any rdn below, including pattern
374          *      "children" means any rdn below, NOT including pattern
375          *      
376          *      "anonymous" may be deprecated in favour 
377          *      of the pattern = "anonymous" form
378          *
379          * "group[/objectClass[/attributeType]]" "=" "<dn pattern>"
380          *
381          * <limit>:
382          *
383          * "time" [ "." { "soft" | "hard" } ] "=" <integer>
384          *
385          * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
386          */
387         
388         pattern = argv[1];
389         if ( strcmp( pattern, "*" ) == 0) {
390                 flags = SLAP_LIMITS_ANY;
391
392         } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
393                 flags = SLAP_LIMITS_ANONYMOUS;
394
395         } else if ( strcasecmp( pattern, "users" ) == 0 ) {
396                 flags = SLAP_LIMITS_USERS;
397                 
398         } else if ( strncasecmp( pattern, "dn", STRLENOF( "dn" ) ) == 0 ) {
399                 pattern += STRLENOF( "dn" );
400                 if ( pattern[0] == '.' ) {
401                         pattern++;
402                         if ( strncasecmp( pattern, "exact", STRLENOF( "exact" )) == 0 ) {
403                                 flags = SLAP_LIMITS_EXACT;
404                                 pattern += STRLENOF( "exact" );
405
406                         } else if ( strncasecmp( pattern, "base", STRLENOF( "base" ) ) == 0 ) {
407                                 flags = SLAP_LIMITS_BASE;
408                                 pattern += STRLENOF( "base" );
409
410                         } else if ( strncasecmp( pattern, "one", STRLENOF( "one" ) ) == 0 ) {
411                                 flags = SLAP_LIMITS_ONE;
412                                 pattern += STRLENOF( "one" );
413                                 if ( strncasecmp( pattern, "level", STRLENOF( "level" ) ) == 0 ) {
414                                         pattern += STRLENOF( "level" );
415
416                                 } else {
417                                         Debug( LDAP_DEBUG_ANY,
418                                                 "%s : line %d: deprecated \"one\" style "
419                                                 "\"limits <pattern> <limits>\" line; "
420                                                 "use \"onelevel\" instead.\n", fname, lineno, 0 );
421                                 }
422
423                         } else if ( strncasecmp( pattern, "sub", STRLENOF( "sub" ) ) == 0 ) {
424                                 flags = SLAP_LIMITS_SUBTREE;
425                                 pattern += STRLENOF( "sub" );
426                                 if ( strncasecmp( pattern, "tree", STRLENOF( "tree" ) ) == 0 ) {
427                                         pattern += STRLENOF( "tree" );
428
429                                 } else {
430                                         Debug( LDAP_DEBUG_ANY,
431                                                 "%s : line %d: deprecated \"sub\" style "
432                                                 "\"limits <pattern> <limits>\" line; "
433                                                 "use \"subtree\" instead.\n", fname, lineno, 0 );
434                                 }
435
436                         } else if ( strncasecmp( pattern, "children", STRLENOF( "children" ) ) == 0 ) {
437                                 flags = SLAP_LIMITS_CHILDREN;
438                                 pattern += STRLENOF( "children" );
439
440                         } else if ( strncasecmp( pattern, "regex", STRLENOF( "regex" ) ) == 0 ) {
441                                 flags = SLAP_LIMITS_REGEX;
442                                 pattern += STRLENOF( "regex" );
443
444                         /* 
445                          * this could be deprecated in favour
446                          * of the pattern = "anonymous" form
447                          */
448                         } else if ( strncasecmp( pattern, "anonymous", STRLENOF( "anonymous" ) ) == 0 ) {
449                                 flags = SLAP_LIMITS_ANONYMOUS;
450                                 pattern = NULL;
451                         }
452                 }
453
454                 /* pre-check the data */
455                 switch ( flags ) {
456                 case SLAP_LIMITS_ANONYMOUS:
457                 case SLAP_LIMITS_USERS:
458
459                         /* no need for pattern */
460                         pattern = NULL;
461                         break;
462
463                 default:
464                         if ( pattern[0] != '=' ) {
465                                 Debug( LDAP_DEBUG_ANY,
466                                         "%s : line %d: missing '=' in "
467                                         "\"dn[.{exact|base|onelevel|subtree"
468                                         "|children|regex|anonymous}]"
469                                         "=<pattern>\" in "
470                                         "\"limits <pattern> <limits>\" "
471                                         "line.\n%s",
472                                         fname, lineno, "" );
473                                 return( -1 );
474                         }
475
476                         /* skip '=' (required) */
477                         pattern++;
478
479                         /* trim obvious cases */
480                         if ( strcmp( pattern, "*" ) == 0 ) {
481                                 flags = SLAP_LIMITS_ANY;
482                                 pattern = NULL;
483
484                         } else if ( flags == SLAP_LIMITS_REGEX
485                                         && strcmp( pattern, ".*" ) == 0 ) {
486                                 flags = SLAP_LIMITS_ANY;
487                                 pattern = NULL;
488                         }
489                 }
490
491         } else if (strncasecmp( pattern, "group", STRLENOF( "group" ) ) == 0 ) {
492                 pattern += STRLENOF( "group" );
493
494                 if ( pattern[0] == '/' ) {
495                         struct berval   oc, ad;
496
497                         oc.bv_val = pattern + 1;
498                         pattern = strchr( pattern, '=' );
499                         if ( pattern == NULL ) {
500                                 return -1;
501                         }
502
503                         ad.bv_val = strchr( oc.bv_val, '/' );
504                         if ( ad.bv_val != NULL ) {
505                                 const char      *text = NULL;
506                                 int             rc;
507
508                                 oc.bv_len = ad.bv_val - oc.bv_val;
509
510                                 ad.bv_val++;
511                                 ad.bv_len = pattern - ad.bv_val;
512                                 rc = slap_bv2ad( &ad, &group_ad, &text );
513                                 if ( rc != LDAP_SUCCESS ) {
514                                         goto no_ad;
515                                 }
516
517                         } else {
518                                 oc.bv_len = pattern - oc.bv_val;
519                         }
520
521                         group_oc = oc_bvfind( &oc );
522                         if ( group_oc == NULL ) {
523                                 goto no_oc;
524                         }
525                 }
526
527                 if ( group_oc == NULL ) {
528                         group_oc = oc_find( SLAPD_GROUP_CLASS );
529                         if ( group_oc == NULL ) {
530 no_oc:;
531                                 return( -1 );
532                         }
533                 }
534
535                 if ( group_ad == NULL ) {
536                         const char      *text = NULL;
537                         int             rc;
538                         
539                         rc = slap_str2ad( SLAPD_GROUP_ATTR, &group_ad, &text );
540
541                         if ( rc != LDAP_SUCCESS ) {
542 no_ad:;
543                                 return( -1 );
544                         }
545                 }
546
547                 flags = SLAP_LIMITS_TYPE_GROUP | SLAP_LIMITS_EXACT;
548
549                 if ( pattern[0] != '=' ) {
550                         Debug( LDAP_DEBUG_ANY,
551                                 "%s : line %d: missing '=' in "
552                                 "\"group[/objectClass[/attributeType]]"
553                                 "=<pattern>\" in "
554                                 "\"limits <pattern> <limits>\" line.\n",
555                                 fname, lineno, 0 );
556                         return( -1 );
557                 }
558
559                 /* skip '=' (required) */
560                 pattern++;
561         }
562
563         /* get the limits */
564         for ( i = 2; i < argc; i++ ) {
565                 if ( limits_parse_one( argv[i], &limit ) ) {
566
567                         Debug( LDAP_DEBUG_ANY,
568                                 "%s : line %d: unknown limit values \"%s\" in "
569                                 "\"limits <pattern> <limits>\" line.\n",
570                         fname, lineno, argv[i] );
571
572                         return( 1 );
573                 }
574         }
575
576         /*
577          * sanity checks ...
578          *
579          * FIXME: add warnings?
580          */
581         if ( limit.lms_t_hard > 0 && 
582                         ( limit.lms_t_hard < limit.lms_t_soft 
583                           || limit.lms_t_soft == -1 ) ) {
584                 limit.lms_t_hard = limit.lms_t_soft;
585         }
586         
587         if ( limit.lms_s_hard > 0 && 
588                         ( limit.lms_s_hard < limit.lms_s_soft 
589                           || limit.lms_s_soft == -1 ) ) {
590                 limit.lms_s_hard = limit.lms_s_soft;
591         }
592
593         /*
594          * defaults ...
595          * 
596          * lms_t_hard:
597          *      -1      => no limits
598          *      0       => same as soft
599          *      > 0     => limit (in seconds)
600          *
601          * lms_s_hard:
602          *      -1      => no limits
603          *      0       0> same as soft
604          *      > 0     => limit (in entries)
605          *
606          * lms_s_pr_total:
607          *      -2      => disable the control
608          *      -1      => no limits
609          *      0       => same as soft
610          *      > 0     => limit (in entries)
611          *
612          * lms_s_pr:
613          *      -1      => no limits
614          *      0       => no limits?
615          *      > 0     => limit size (in entries)
616          */
617         if ( limit.lms_s_pr_total > 0 &&
618                         limit.lms_s_pr > limit.lms_s_pr_total ) {
619                 limit.lms_s_pr = limit.lms_s_pr_total;
620         }
621
622         rc = limits_add( be, flags, pattern, group_oc, group_ad, &limit );
623         if ( rc ) {
624
625                 Debug( LDAP_DEBUG_ANY,
626                         "%s : line %d: unable to add limit in "
627                         "\"limits <pattern> <limits>\" line.\n",
628                 fname, lineno, 0 );
629         }
630
631         return( rc );
632 }
633
634 int
635 limits_parse_one(
636         const char              *arg,
637         struct slap_limits_set  *limit
638 )
639 {
640         assert( arg );
641         assert( limit );
642
643         if ( strncasecmp( arg, "time", STRLENOF( "time" ) ) == 0 ) {
644                 arg += STRLENOF( "time" );
645
646                 if ( arg[0] == '.' ) {
647                         arg++;
648                         if ( strncasecmp( arg, "soft=", STRLENOF( "soft=" ) ) == 0 ) {
649                                 arg += STRLENOF( "soft=" );
650                                 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
651                                         limit->lms_t_soft = -1;
652
653                                 } else {
654                                         char    *next = NULL;
655                                         int     soft = strtol( arg, &next, 10 );
656
657                                         if ( next == arg || next[ 0 ] != '\0' ) {
658                                                 return( 1 );
659                                         }
660
661                                         if ( soft < -1 ) {
662                                                 return( 1 );
663                                         }
664
665                                         if ( soft == -1 ) {
666                                                 /* FIXME: use "unlimited" instead; issue warning? */
667                                         }
668
669                                         limit->lms_t_soft = soft;
670                                 }
671                                 
672                         } else if ( strncasecmp( arg, "hard=", STRLENOF( "hard=" ) ) == 0 ) {
673                                 arg += STRLENOF( "hard=" );
674                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
675                                         limit->lms_t_hard = 0;
676
677                                 } else if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
678                                         limit->lms_t_hard = -1;
679
680                                 } else {
681                                         char    *next = NULL;
682                                         int     hard = strtol( arg, &next, 10 );
683
684                                         if ( next == arg || next[ 0 ] != '\0' ) {
685                                                 return( 1 );
686                                         }
687
688                                         if ( hard < -1 ) {
689                                                 return( 1 );
690                                         }
691
692                                         if ( hard == -1 ) {
693                                                 /* FIXME: use "unlimited" instead */
694                                         }
695
696                                         if ( hard == 0 ) {
697                                                 /* FIXME: use "soft" instead */
698                                         }
699
700                                         limit->lms_t_hard = hard;
701                                 }
702                                 
703                         } else {
704                                 return( 1 );
705                         }
706                         
707                 } else if ( arg[0] == '=' ) {
708                         arg++;
709                         if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
710                                 limit->lms_t_soft = -1;
711
712                         } else {
713                                 char    *next = NULL;
714
715                                 limit->lms_t_soft = strtol( arg, &next, 10 );
716                                 if ( next == arg || limit->lms_t_soft < -1 ) {
717                                         return( 1 );
718                                 }
719                         }
720                         limit->lms_t_hard = 0;
721                         
722                 } else {
723                         return( 1 );
724                 }
725
726         } else if ( strncasecmp( arg, "size", STRLENOF( "size" ) ) == 0 ) {
727                 arg += STRLENOF( "size" );
728                 
729                 if ( arg[0] == '.' ) {
730                         arg++;
731                         if ( strncasecmp( arg, "soft=", STRLENOF( "soft=" ) ) == 0 ) {
732                                 arg += STRLENOF( "soft=" );
733                                 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
734                                         limit->lms_s_soft = -1;
735
736                                 } else {
737                                         char    *next = NULL;
738                                         int     soft = strtol( arg, &next, 10 );
739
740                                         if ( next == arg || next[ 0 ] != '\0' ) {
741                                                 return( 1 );
742                                         }
743
744                                         if ( soft < -1 ) {
745                                                 return( 1 );
746                                         }
747
748                                         if ( soft == -1 ) {
749                                                 /* FIXME: use "unlimited" instead */
750                                         }
751
752                                         limit->lms_s_soft = soft;
753                                 }
754                                 
755                         } else if ( strncasecmp( arg, "hard=", STRLENOF( "hard=" ) ) == 0 ) {
756                                 arg += STRLENOF( "hard=" );
757                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
758                                         limit->lms_s_hard = 0;
759
760                                 } else if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
761                                         limit->lms_s_hard = -1;
762
763                                 } else {
764                                         char    *next = NULL;
765                                         int     hard = strtol( arg, &next, 10 );
766
767                                         if ( next == arg || next[ 0 ] != '\0' ) {
768                                                 return( 1 );
769                                         }
770
771                                         if ( hard < -1 ) {
772                                                 return( 1 );
773                                         }
774
775                                         if ( hard == -1 ) {
776                                                 /* FIXME: use "unlimited" instead */
777                                         }
778
779                                         if ( hard == 0 ) {
780                                                 /* FIXME: use "soft" instead */
781                                         }
782
783                                         limit->lms_s_hard = hard;
784                                 }
785                                 
786                         } else if ( strncasecmp( arg, "unchecked=", STRLENOF( "unchecked=" ) ) == 0 ) {
787                                 arg += STRLENOF( "unchecked=" );
788                                 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
789                                         limit->lms_s_unchecked = -1;
790
791                                 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
792                                         limit->lms_s_unchecked = 0;
793
794                                 } else {
795                                         char    *next = NULL;
796                                         int     unchecked = strtol( arg, &next, 10 );
797
798                                         if ( next == arg || next[ 0 ] != '\0' ) {
799                                                 return( 1 );
800                                         }
801
802                                         if ( unchecked < -1 ) {
803                                                 return( 1 );
804                                         }
805
806                                         if ( unchecked == -1 ) {
807                                                 /*  FIXME: use "unlimited" instead */
808                                         }
809
810                                         limit->lms_s_unchecked = unchecked;
811                                 }
812
813                         } else if ( strncasecmp( arg, "pr=", STRLENOF( "pr=" ) ) == 0 ) {
814                                 arg += STRLENOF( "pr=" );
815                                 if ( strcasecmp( arg, "noEstimate" ) == 0 ) {
816                                         limit->lms_s_pr_hide = 1;
817
818                                 } else if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
819                                         limit->lms_s_pr = -1;
820
821                                 } else {
822                                         char    *next = NULL;
823                                         int     pr = strtol( arg, &next, 10 );
824
825                                         if ( next == arg || next[ 0 ] != '\0' ) {
826                                                 return( 1 );
827                                         }
828
829                                         if ( pr < -1 ) {
830                                                 return( 1 );
831                                         }
832
833                                         if ( pr == -1 ) {
834                                                 /* FIXME: use "unlimited" instead */
835                                         }
836
837                                         limit->lms_s_pr = pr;
838                                 }
839
840                         } else if ( strncasecmp( arg, "prtotal=", STRLENOF( "prtotal=" ) ) == 0 ) {
841                                 arg += STRLENOF( "prtotal=" );
842
843                                 if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
844                                         limit->lms_s_pr_total = -1;
845
846                                 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
847                                         limit->lms_s_pr_total = -2;
848
849                                 } else if ( strcasecmp( arg, "hard" ) == 0 ) {
850                                         limit->lms_s_pr_total = 0;
851
852                                 } else {
853                                         char    *next = NULL;
854                                         int     total;
855
856                                         total = strtol( arg, &next, 10 );
857                                         if ( next == arg || next[ 0 ] != '\0' ) {
858                                                 return( 1 );
859                                         }
860
861                                         if ( total < -1 ) {
862                                                 return( 1 );
863                                         }
864
865                                         if ( total == -1 ) {
866                                                 /* FIXME: use "unlimited" instead */
867                                         }
868
869                                         if ( total == 0 ) {
870                                                 /* FIXME: use "pr=disable" instead */
871                                         }
872
873                                         limit->lms_s_pr_total = total;
874                                 }
875
876                         } else {
877                                 return( 1 );
878                         }
879                         
880                 } else if ( arg[0] == '=' ) {
881                         arg++;
882                         if ( strcasecmp( arg, "unlimited" ) == 0 || strcasecmp( arg, "none" ) == 0 ) {
883                                 limit->lms_s_soft = -1;
884
885                         } else {
886                                 char    *next = NULL;
887
888                                 limit->lms_s_soft = strtol( arg, &next, 10 );
889                                 if ( next == arg || limit->lms_s_soft < -1 ) {
890                                         return( 1 );
891                                 }
892                         }
893                         limit->lms_s_hard = 0;
894                         
895                 } else {
896                         return( 1 );
897                 }
898         }
899
900         return 0;
901 }
902
903 static const char *lmpats[] = {
904         "base",
905         "base",
906         "onelevel",
907         "subtree",
908         "children",
909         "regex",
910         "anonymous",
911         "users",
912         "*"
913 };
914
915 /* Caller must provide an adequately sized buffer in bv */
916 void
917 limits_unparse( struct slap_limits *lim, struct berval *bv )
918 {
919         struct berval btmp;
920         char *ptr;
921         int lm;
922
923         if ( !bv || !bv->bv_val ) return;
924
925         ptr = bv->bv_val;
926
927         if (( lim->lm_flags & SLAP_LIMITS_TYPE_MASK ) == SLAP_LIMITS_TYPE_GROUP ) {
928                 ptr = lutil_strcopy( ptr, "group/" );
929                 ptr = lutil_strcopy( ptr, lim->lm_group_oc->soc_cname.bv_val );
930                 *ptr++ = '/';
931                 ptr = lutil_strcopy( ptr, lim->lm_group_ad->ad_cname.bv_val );
932                 ptr = lutil_strcopy( ptr, "=\"" );
933                 ptr = lutil_strcopy( ptr, lim->lm_pat.bv_val );
934                 *ptr++ = '"';
935         } else {
936                 lm = lim->lm_flags & SLAP_LIMITS_MASK;
937                 switch( lm ) {
938                 case SLAP_LIMITS_ANONYMOUS:
939                 case SLAP_LIMITS_USERS:
940                 case SLAP_LIMITS_ANY:
941                         ptr = lutil_strcopy( ptr, lmpats[lm] );
942                         break;
943                 case SLAP_LIMITS_UNDEFINED:
944                 case SLAP_LIMITS_EXACT:
945                 case SLAP_LIMITS_ONE:
946                 case SLAP_LIMITS_SUBTREE:
947                 case SLAP_LIMITS_CHILDREN:
948                 case SLAP_LIMITS_REGEX:
949                         ptr = lutil_strcopy( ptr, "dn." );
950                         ptr = lutil_strcopy( ptr, lmpats[lm] );
951                         *ptr++ = '=';
952                         *ptr++ = '"';
953                         ptr = lutil_strcopy( ptr, lim->lm_pat.bv_val );
954                         *ptr++ = '"';
955                         break;
956                 }
957         }
958         *ptr++ = ' ';
959         bv->bv_len = ptr - bv->bv_val;
960         btmp.bv_val = ptr;
961         btmp.bv_len = 0;
962         limits_unparse_one( &lim->lm_limits, SLAP_LIMIT_SIZE|SLAP_LIMIT_TIME, &btmp );
963         bv->bv_len += btmp.bv_len;
964 }
965
966 /* Caller must provide an adequately sized buffer in bv */
967 void
968 limits_unparse_one( struct slap_limits_set *lim, int which, struct berval *bv )
969 {
970         char *ptr;
971
972         if ( !bv || !bv->bv_val ) return;
973
974         ptr = bv->bv_val;
975
976         if ( which & SLAP_LIMIT_SIZE ) {
977                 if ( lim->lms_s_soft != SLAPD_DEFAULT_SIZELIMIT ) {
978
979                         /* If same as global limit, drop it */
980                         if ( lim != &frontendDB->be_def_limit &&
981                                 lim->lms_s_soft == frontendDB->be_def_limit.lms_s_soft )
982                                 goto s_hard;
983                         /* If there's also a hard limit, fully qualify this one */
984                         else if ( lim->lms_s_hard )
985                                 ptr = lutil_strcopy( ptr, " size.soft=" );
986
987                         /* If doing both size & time, qualify this */
988                         else if ( which & SLAP_LIMIT_TIME )
989                                 ptr = lutil_strcopy( ptr, " size=" );
990
991                         if ( lim->lms_s_soft == -1 )
992                                 ptr = lutil_strcopy( ptr, "unlimited" );
993                         else
994                                 ptr += sprintf( ptr, "%d", lim->lms_s_soft );
995                         *ptr++ = ' ';
996                 }
997 s_hard:
998                 if ( lim->lms_s_hard ) {
999                         ptr = lutil_strcopy( ptr, " size.hard=" );
1000                         if ( lim->lms_s_hard == -1 )
1001                                 ptr = lutil_strcopy( ptr, "unlimited" );
1002                         else
1003                                 ptr += sprintf( ptr, "%d", lim->lms_s_hard );
1004                         *ptr++ = ' ';
1005                 }
1006                 if ( lim->lms_s_unchecked != -1 ) {
1007                         ptr = lutil_strcopy( ptr, " size.unchecked=" );
1008                         if ( lim->lms_s_unchecked == 0 )
1009                                 ptr = lutil_strcopy( ptr, "disabled" );
1010                         else
1011                                 ptr += sprintf( ptr, "%d", lim->lms_s_unchecked );
1012                         *ptr++ = ' ';
1013                 }
1014                 if ( lim->lms_s_pr_hide ) {
1015                         ptr = lutil_strcopy( ptr, " size.pr=noEstimate " );
1016                 }
1017                 if ( lim->lms_s_pr ) {
1018                         ptr = lutil_strcopy( ptr, " size.pr=" );
1019                         if ( lim->lms_s_pr == -1 )
1020                                 ptr = lutil_strcopy( ptr, "unlimited" );
1021                         else
1022                                 ptr += sprintf( ptr, "%d", lim->lms_s_pr );
1023                         *ptr++ = ' ';
1024                 }
1025                 if ( lim->lms_s_pr_total ) {
1026                         ptr = lutil_strcopy( ptr, " size.prtotal=" );
1027                         if ( lim->lms_s_pr_total == -1 )
1028                                 ptr = lutil_strcopy( ptr, "unlimited" );
1029                         else if ( lim->lms_s_pr_total == -2 )
1030                                 ptr = lutil_strcopy( ptr, "disabled" );
1031                         else 
1032                                 ptr += sprintf( ptr, "%d", lim->lms_s_pr_total );
1033                         *ptr++ = ' ';
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                                 goto t_hard;
1043
1044                         /* If there's also a hard limit, fully qualify this one */
1045                         else if ( lim->lms_t_hard ) 
1046                                 ptr = lutil_strcopy( ptr, " time.soft=" );
1047
1048                         /* If doing both size & time, qualify this */
1049                         else if ( which & SLAP_LIMIT_SIZE )
1050                                 ptr = lutil_strcopy( ptr, " time=" );
1051
1052                         if ( lim->lms_t_soft == -1 )
1053                                 ptr = lutil_strcopy( ptr, "unlimited" );
1054                         else
1055                                 ptr += sprintf( ptr, "%d", lim->lms_t_soft );
1056                         *ptr++ = ' ';
1057                 }
1058 t_hard:
1059                 if ( lim->lms_t_hard ) {
1060                         ptr = lutil_strcopy( ptr, " time.hard=" );
1061                         if ( lim->lms_t_hard == -1 )
1062                                 ptr = lutil_strcopy( ptr, "unlimited" );
1063                         else
1064                                 ptr += sprintf( ptr, "%d", lim->lms_t_hard );
1065                         *ptr++ = ' ';
1066                 }
1067         }
1068         if ( ptr != bv->bv_val ) {
1069                 ptr--;
1070                 *ptr = '\0';
1071                 bv->bv_len = ptr - bv->bv_val;
1072         }
1073 }
1074
1075 int
1076 limits_check( Operation *op, SlapReply *rs )
1077 {
1078         assert( op );
1079         assert( rs );
1080         /* FIXME: should this be always true? */
1081         assert( op->o_tag == LDAP_REQ_SEARCH);
1082
1083         /* protocol only allows 0..maxInt;
1084          *
1085          * internal searches:
1086          * - may use SLAP_NO_LIMIT ( = -1 ) to indicate no limits;
1087          * - should use slimit = N and tlimit = SLAP_NO_LIMIT to
1088          *   indicate searches that should return exactly N matches,
1089          *   and handle errors thru a callback (see for instance
1090          *   slap_sasl_match() and slap_sasl2dn())
1091          */
1092         if ( op->ors_tlimit == SLAP_NO_LIMIT && op->ors_slimit == SLAP_NO_LIMIT ) {
1093                 return 0;
1094         }
1095
1096         /* allow root to set no limit */
1097         if ( be_isroot( op ) ) {
1098                 op->ors_limit = NULL;
1099
1100                 if ( op->ors_tlimit == 0 ) {
1101                         op->ors_tlimit = SLAP_NO_LIMIT;
1102                 }
1103
1104                 if ( op->ors_slimit == 0 ) {
1105                         op->ors_slimit = SLAP_NO_LIMIT;
1106                 }
1107
1108         /* if not root, get appropriate limits */
1109         } else {
1110                 ( void ) limits_get( op, &op->o_ndn, &op->ors_limit );
1111
1112                 assert( op->ors_limit != NULL );
1113
1114                 /* if no limit is required, use soft limit */
1115                 if ( op->ors_tlimit == 0 ) {
1116                         op->ors_tlimit = op->ors_limit->lms_t_soft;
1117
1118                 /* limit required: check if legal */
1119                 } else {
1120                         if ( op->ors_limit->lms_t_hard == 0 ) {
1121                                 if ( op->ors_limit->lms_t_soft > 0
1122                                                 && ( op->ors_tlimit > op->ors_limit->lms_t_soft ) ) {
1123                                         op->ors_tlimit = op->ors_limit->lms_t_soft;
1124                                 }
1125
1126                         } else if ( op->ors_limit->lms_t_hard > 0 ) {
1127 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1128                                 if ( op->ors_tlimit == SLAP_MAX_LIMIT ) {
1129                                         op->ors_tlimit = op->ors_limit->lms_t_hard;
1130
1131                                 } else if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
1132                                         /* error if exceeding hard limit */
1133                                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1134                                         send_ldap_result( op, rs );
1135                                         rs->sr_err = LDAP_SUCCESS;
1136                                         return -1;
1137                                 }
1138 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1139                                 if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
1140                                         op->ors_tlimit = op->ors_limit->lms_t_hard;
1141                                 }
1142 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1143                         }
1144                 }
1145
1146                 /* else leave as is */
1147
1148                 /* don't even get to backend if candidate check is disabled */
1149                 if ( op->ors_limit->lms_s_unchecked == 0 ) {
1150                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1151                         send_ldap_result( op, rs );
1152                         rs->sr_err = LDAP_SUCCESS;
1153                         return -1;
1154                 }
1155
1156                 /* if paged results is requested */     
1157                 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
1158                         int     slimit = -2;
1159                         int     pr_total;
1160                         PagedResultsState *ps = op->o_pagedresults_state;
1161
1162                         /* paged results is not allowed */
1163                         if ( op->ors_limit->lms_s_pr_total == -2 ) {
1164                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1165                                 rs->sr_text = "pagedResults control not allowed";
1166                                 send_ldap_result( op, rs );
1167                                 rs->sr_err = LDAP_SUCCESS;
1168                                 rs->sr_text = NULL;
1169                                 return -1;
1170                         }
1171                         
1172                         if ( op->ors_limit->lms_s_pr > 0 && ps->ps_size > op->ors_limit->lms_s_pr ) {
1173                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1174                                 rs->sr_text = "illegal pagedResults page size";
1175                                 send_ldap_result( op, rs );
1176                                 rs->sr_err = LDAP_SUCCESS;
1177                                 rs->sr_text = NULL;
1178                                 return -1;
1179                         }
1180
1181                         if ( op->ors_limit->lms_s_pr_total == 0 ) {
1182                                 if ( op->ors_limit->lms_s_hard == 0 ) {
1183                                         pr_total = op->ors_limit->lms_s_soft;
1184                                 } else {
1185                                         pr_total = op->ors_limit->lms_s_hard;
1186                                 }
1187                         } else {
1188                                 pr_total = op->ors_limit->lms_s_pr_total;
1189                         }
1190
1191                         if ( pr_total == -1 ) {
1192                                 if ( op->ors_slimit == 0 || op->ors_slimit == SLAP_MAX_LIMIT ) {
1193                                         slimit = -1;
1194
1195                                 } else {
1196                                         slimit = op->ors_slimit - ps->ps_count;
1197                                 }
1198
1199 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1200                         } else if ( pr_total > 0 && op->ors_slimit != SLAP_MAX_LIMIT
1201                                         && ( op->ors_slimit == SLAP_NO_LIMIT || op->ors_slimit > pr_total ) )
1202                         {
1203                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1204                                 send_ldap_result( op, rs );
1205                                 rs->sr_err = LDAP_SUCCESS;
1206                                 return -1;
1207 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1208         
1209                         } else {
1210                                 /* if no limit is required, use soft limit */
1211                                 int     total;
1212                                 int     slimit2;
1213
1214                                 /* first round of pagedResults: set count to any appropriate limit */
1215
1216                                 /* if the limit is set, check that it does not violate any server-side limit */
1217 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1218                                 if ( op->ors_slimit == SLAP_MAX_LIMIT ) {
1219                                         slimit2 = op->ors_slimit = pr_total;
1220 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1221                                 if ( op->ors_slimit == SLAP_MAX_LIMIT || op->ors_slimit > pr_total ) {
1222                                         slimit2 = op->ors_slimit = pr_total;
1223 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1224
1225                                 } else if ( op->ors_slimit == 0 ) {
1226                                         slimit2 = pr_total;
1227
1228                                 } else {
1229                                         slimit2 = op->ors_slimit;
1230                                 }
1231
1232                                 total = slimit2 - ps->ps_count;
1233
1234                                 if ( total >= 0 ) {
1235                                         if ( op->ors_limit->lms_s_pr > 0 ) {
1236                                                 /* use the smallest limit set by total/per page */
1237                                                 if ( total < op->ors_limit->lms_s_pr ) {
1238                                                         slimit = total;
1239         
1240                                                 } else {
1241                                                         /* use the perpage limit if any 
1242                                                          * NOTE: + 1 because the given value must be legal */
1243                                                         slimit = op->ors_limit->lms_s_pr + 1;
1244                                                 }
1245
1246                                         } else {
1247                                                 /* use the total limit if any */
1248                                                 slimit = total;
1249                                         }
1250
1251                                 } else if ( op->ors_limit->lms_s_pr > 0 ) {
1252                                         /* use the perpage limit if any 
1253                                          * NOTE: + 1 because the given value must be legal */
1254                                         slimit = op->ors_limit->lms_s_pr + 1;
1255
1256                                 } else {
1257                                         /* use the standard hard/soft limit if any */
1258                                         slimit = op->ors_limit->lms_s_hard;
1259                                 }
1260                         }
1261                 
1262                         /* if got any limit, use it */
1263                         if ( slimit != -2 ) {
1264                                 if ( op->ors_slimit == 0 ) {
1265                                         op->ors_slimit = slimit;
1266
1267                                 } else if ( slimit > 0 ) {
1268                                         if ( op->ors_slimit - ps->ps_count > slimit ) {
1269                                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1270                                                 send_ldap_result( op, rs );
1271                                                 rs->sr_err = LDAP_SUCCESS;
1272                                                 return -1;
1273                                         }
1274                                         op->ors_slimit = slimit;
1275                                 }
1276
1277                         } else {
1278                                 /* use the standard hard/soft limit if any */
1279                                 op->ors_slimit = pr_total;
1280                         }
1281
1282                 /* no limit requested: use soft, whatever it is */
1283                 } else if ( op->ors_slimit == 0 ) {
1284                         op->ors_slimit = op->ors_limit->lms_s_soft;
1285
1286                 /* limit requested: check if legal */
1287                 } else {
1288                         /* hard limit as soft (traditional behavior) */
1289                         if ( op->ors_limit->lms_s_hard == 0 ) {
1290                                 if ( op->ors_limit->lms_s_soft > 0
1291                                                 && op->ors_slimit > op->ors_limit->lms_s_soft ) {
1292                                         op->ors_slimit = op->ors_limit->lms_s_soft;
1293                                 }
1294
1295                         /* explicit hard limit: error if violated */
1296                         } else if ( op->ors_limit->lms_s_hard > 0 ) {
1297 #ifdef ABOVE_HARD_LIMIT_IS_ERROR
1298                                 if ( op->ors_slimit == SLAP_MAX_LIMIT ) {
1299                                         op->ors_slimit = op->ors_limit->lms_s_hard;
1300
1301                                 } else if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
1302                                         /* if limit exceeds hard, error */
1303                                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1304                                         send_ldap_result( op, rs );
1305                                         rs->sr_err = LDAP_SUCCESS;
1306                                         return -1;
1307                                 }
1308 #else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1309                                 if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
1310                                         op->ors_slimit = op->ors_limit->lms_s_hard;
1311                                 }
1312 #endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
1313                         }
1314                 }
1315
1316                 /* else leave as is */
1317         }
1318
1319         return 0;
1320 }
1321