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