]> git.sur5r.net Git - openldap/blob - servers/slapd/limits.c
sounds like we're there...
[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-2004 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
26 int
27 limits_get( 
28         Operation               *op,
29         struct berval           *ndn, 
30         struct slap_limits_set  **limit
31 )
32 {
33         struct slap_limits **lm;
34
35         assert( op );
36         assert( limit );
37
38         /*
39          * default values
40          */
41         *limit = &op->o_bd->be_def_limit;
42
43         if ( op->o_bd->be_limits == NULL ) {
44                 return( 0 );
45         }
46
47         for ( lm = op->o_bd->be_limits; lm[0] != NULL; lm++ ) {
48                 unsigned        style = lm[0]->lm_flags & SLAP_LIMITS_MASK;
49                 unsigned        type = lm[0]->lm_flags & SLAP_LIMITS_TYPE_MASK;
50
51                 switch ( style ) {
52                 case SLAP_LIMITS_EXACT:
53                         if ( ndn->bv_len == 0 ) {
54                                 break;
55                         }
56
57                         if ( type == SLAP_LIMITS_TYPE_GROUP ) {
58                                 int     rc;
59
60                                 rc = backend_group( op, NULL,
61                                                 &lm[0]->lm_pat, ndn,
62                                                 lm[0]->lm_group_oc,
63                                                 lm[0]->lm_group_ad );
64                                 if ( rc == 0 ) {
65                                         *limit = &lm[0]->lm_limits;
66                                         return( 0 );
67                                 }
68                         }
69                         
70                         if ( dn_match( &lm[0]->lm_pat, ndn ) ) {
71                                 *limit = &lm[0]->lm_limits;
72                                 return( 0 );
73                         }
74                         break;
75
76                 case SLAP_LIMITS_ONE:
77                 case SLAP_LIMITS_SUBTREE:
78                 case SLAP_LIMITS_CHILDREN: {
79                         size_t d;
80                         
81                         if ( ndn->bv_len == 0 ) {
82                                 break;
83                         }
84
85                         /* ndn shorter than dn_pat */
86                         if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) {
87                                 break;
88                         }
89                         d = ndn->bv_len - lm[0]->lm_pat.bv_len;
90
91                         /* allow exact match for SUBTREE only */
92                         if ( d == 0 ) {
93                                 if ( style != SLAP_LIMITS_SUBTREE ) {
94                                         break;
95                                 }
96                         } else {
97                                 /* check for unescaped rdn separator */
98                                 if ( !DN_SEPARATOR( ndn->bv_val[d-1] ) ) {
99                                         break;
100                                 }
101                         }
102
103                         /* in case of (sub)match ... */
104                         if ( lm[0]->lm_pat.bv_len == ( ndn->bv_len - d )
105                                         && strcmp( lm[0]->lm_pat.bv_val,
106                                                 &ndn->bv_val[d] ) == 0 )
107                         {
108                                 /* check for exactly one rdn in case of ONE */
109                                 if ( style == SLAP_LIMITS_ONE ) {
110                                         /*
111                                          * if ndn is more that one rdn
112                                          * below dn_pat, continue
113                                          */
114                                         if ( (size_t) dn_rdnlen( NULL, ndn )
115                                                         != d - 1 )
116                                         {
117                                                 break;
118                                         }
119                                 }
120
121                                 *limit = &lm[0]->lm_limits;
122                                 return( 0 );
123                         }
124
125                         break;
126                 }
127
128                 case SLAP_LIMITS_REGEX:
129                         if ( ndn->bv_len == 0 ) {
130                                 break;
131                         }
132                         if ( regexec( &lm[0]->lm_regex, ndn->bv_val,
133                                                 0, NULL, 0 ) == 0 )
134                         {
135                                 *limit = &lm[0]->lm_limits;
136                                 return( 0 );
137                         }
138                         break;
139
140                 case SLAP_LIMITS_ANONYMOUS:
141                         if ( ndn->bv_len == 0 ) {
142                                 *limit = &lm[0]->lm_limits;
143                                 return( 0 );
144                         }
145                         break;
146
147                 case SLAP_LIMITS_USERS:
148                         if ( ndn->bv_len != 0 ) {
149                                 *limit = &lm[0]->lm_limits;
150                                 return( 0 );
151                         }
152                         break;
153
154                 case SLAP_LIMITS_ANY:
155                         *limit = &lm[0]->lm_limits;
156                         return( 0 );
157
158                 default:
159                         assert( 0 );    /* unreachable */
160                         return( -1 );
161                 }
162         }
163
164         return( 0 );
165 }
166
167 static int
168 limits_add(
169         Backend                 *be,
170         unsigned                flags,
171         const char              *pattern,
172         ObjectClass             *group_oc,
173         AttributeDescription    *group_ad,
174         struct slap_limits_set  *limit
175 )
176 {
177         int                     i;
178         struct slap_limits      *lm;
179         unsigned                type, style;
180         
181         assert( be );
182         assert( limit );
183
184         type = flags & SLAP_LIMITS_TYPE_MASK;
185         style = flags & SLAP_LIMITS_MASK;
186
187         switch ( style ) {
188         case SLAP_LIMITS_ANONYMOUS:
189         case SLAP_LIMITS_USERS:
190         case SLAP_LIMITS_ANY:
191                 for ( i = 0; be->be_limits && be->be_limits[ i ]; i++ ) {
192                         if ( be->be_limits[ i ]->lm_flags == style ) {
193                                 return( -1 );
194                         }
195                 }
196                 break;
197         }
198
199
200         lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
201
202         switch ( style ) {
203         case SLAP_LIMITS_UNDEFINED:
204                 style = SLAP_LIMITS_EXACT;
205                 /* continue to next cases */
206         case SLAP_LIMITS_EXACT:
207         case SLAP_LIMITS_ONE:
208         case SLAP_LIMITS_SUBTREE:
209         case SLAP_LIMITS_CHILDREN:
210                 lm->lm_flags = style | type;
211                 {
212                         int rc;
213                         struct berval bv;
214                         bv.bv_val = (char *) pattern;
215                         bv.bv_len = strlen( pattern );
216
217                         rc = dnNormalize( 0, NULL, NULL, &bv, &lm->lm_pat, NULL );
218                         if ( rc != LDAP_SUCCESS ) {
219                                 ch_free( lm );
220                                 return( -1 );
221                         }
222                 }
223                 break;
224                 
225         case SLAP_LIMITS_REGEX:
226                 lm->lm_flags = style | type;
227                 ber_str2bv( pattern, 0, 1, &lm->lm_pat );
228                 if ( regcomp( &lm->lm_regex, lm->lm_pat.bv_val, 
229                                         REG_EXTENDED | REG_ICASE ) ) {
230                         free( lm->lm_pat.bv_val );
231                         ch_free( lm );
232                         return( -1 );
233                 }
234                 break;
235
236         case SLAP_LIMITS_ANONYMOUS:
237         case SLAP_LIMITS_USERS:
238         case SLAP_LIMITS_ANY:
239                 lm->lm_flags = style | type;
240                 lm->lm_pat.bv_val = NULL;
241                 lm->lm_pat.bv_len = 0;
242                 break;
243         }
244
245         switch ( type ) {
246         case SLAP_LIMITS_TYPE_GROUP:
247                 assert( group_oc );
248                 assert( group_ad );
249                 lm->lm_group_oc = group_oc;
250                 lm->lm_group_ad = group_ad;
251                 break;
252         }
253
254         lm->lm_limits = *limit;
255
256         i = 0;
257         if ( be->be_limits != NULL ) {
258                 for ( ; be->be_limits[i]; i++ );
259         }
260
261         be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits,
262                         sizeof( struct slap_limits * ) * ( i + 2 ) );
263         be->be_limits[i] = lm;
264         be->be_limits[i+1] = NULL;
265         
266         return( 0 );
267 }
268
269 int
270 limits_parse(
271         Backend     *be,
272         const char  *fname,
273         int         lineno,
274         int         argc,
275         char        **argv
276 )
277 {
278         int                     flags = SLAP_LIMITS_UNDEFINED;
279         char                    *pattern;
280         struct slap_limits_set  limit;
281         int                     i, rc = 0;
282         ObjectClass             *group_oc = NULL;
283         AttributeDescription    *group_ad = NULL;
284
285         assert( be );
286
287         if ( argc < 3 ) {
288 #ifdef NEW_LOGGING
289                 LDAP_LOG( CONFIG, CRIT, 
290                         "%s : line %d: missing arg(s) in "
291                         "\"limits <pattern> <limits>\" line.\n", fname, lineno, 0 );
292 #else
293                 Debug( LDAP_DEBUG_ANY,
294                         "%s : line %d: missing arg(s) in "
295                         "\"limits <pattern> <limits>\" line.\n%s",
296                         fname, lineno, "" );
297 #endif
298                 return( -1 );
299         }
300
301         limit = be->be_def_limit;
302
303         /*
304          * syntax:
305          *
306          * "limits" <pattern> <limit> [ ... ]
307          * 
308          * 
309          * <pattern>:
310          * 
311          * "anonymous"
312          * "users"
313          * [ "dn" [ "." { "exact" | "base" | "onelevel" | "subtree" | children"
314          *      | "regex" | "anonymous" } ] "=" ] <dn pattern>
315          *
316          * Note:
317          *      "exact" and "base" are the same (exact match);
318          *      "onelevel" means exactly one rdn below, NOT including pattern
319          *      "subtree" means any rdn below, including pattern
320          *      "children" means any rdn below, NOT including pattern
321          *      
322          *      "anonymous" may be deprecated in favour 
323          *      of the pattern = "anonymous" form
324          *
325          * "group[/objectClass[/attributeType]]" "=" "<dn pattern>"
326          *
327          * <limit>:
328          *
329          * "time" [ "." { "soft" | "hard" } ] "=" <integer>
330          *
331          * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
332          */
333         
334         pattern = argv[1];
335         if ( strcmp( pattern, "*" ) == 0) {
336                 flags = SLAP_LIMITS_ANY;
337
338         } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
339                 flags = SLAP_LIMITS_ANONYMOUS;
340
341         } else if ( strcasecmp( pattern, "users" ) == 0 ) {
342                 flags = SLAP_LIMITS_USERS;
343                 
344         } else if ( strncasecmp( pattern, "dn", STRLENOF( "dn" ) ) == 0 ) {
345                 pattern += STRLENOF( "dn" );
346                 if ( pattern[0] == '.' ) {
347                         pattern++;
348                         if ( strncasecmp( pattern, "exact", STRLENOF( "exact" )) == 0 ) {
349                                 flags = SLAP_LIMITS_EXACT;
350                                 pattern += STRLENOF( "exact" );
351
352                         } else if ( strncasecmp( pattern, "base", STRLENOF( "base" ) ) == 0 ) {
353                                 flags = SLAP_LIMITS_BASE;
354                                 pattern += STRLENOF( "base" );
355
356                         } else if ( strncasecmp( pattern, "one", STRLENOF( "one" ) ) == 0 ) {
357                                 flags = SLAP_LIMITS_ONE;
358                                 pattern += STRLENOF( "one" );
359                                 if ( strncasecmp( pattern, "level", STRLENOF( "level" ) ) == 0 ) {
360                                         pattern += STRLENOF( "level" );
361
362                                 } else {
363 #ifdef NEW_LOGGING
364                                         LDAP_LOG( CONFIG, WARNING , 
365                                                 "%s : line %d: deprecated \"one\" style "
366                                                 "\"limits <pattern> <limits>\" line; "
367                                                 "use \"onelevel\" instead.\n", fname, lineno, 0 );
368 #else
369                                         Debug( LDAP_DEBUG_ANY,
370                                                 "%s : line %d: deprecated \"one\" style "
371                                                 "\"limits <pattern> <limits>\" line; "
372                                                 "use \"onelevel\" instead.\n", fname, lineno, 0 );
373 #endif
374                                 }
375
376                         } else if ( strncasecmp( pattern, "sub", STRLENOF( "sub" ) ) == 0 ) {
377                                 flags = SLAP_LIMITS_SUBTREE;
378                                 pattern += STRLENOF( "sub" );
379                                 if ( strncasecmp( pattern, "tree", STRLENOF( "tree" ) ) == 0 ) {
380                                         pattern += STRLENOF( "tree" );
381
382                                 } else {
383 #ifdef NEW_LOGGING
384                                         LDAP_LOG( CONFIG, WARNING , 
385                                                 "%s : line %d: deprecated \"sub\" style "
386                                                 "\"limits <pattern> <limits>\" line; "
387                                                 "use \"subtree\" instead.\n", fname, lineno, 0 );
388 #else
389                                         Debug( LDAP_DEBUG_ANY,
390                                                 "%s : line %d: deprecated \"sub\" style "
391                                                 "\"limits <pattern> <limits>\" line; "
392                                                 "use \"subtree\" instead.\n", fname, lineno, 0 );
393 #endif
394                                 }
395
396                         } else if ( strncasecmp( pattern, "children", STRLENOF( "children" ) ) == 0 ) {
397                                 flags = SLAP_LIMITS_CHILDREN;
398                                 pattern += STRLENOF( "children" );
399
400                         } else if ( strncasecmp( pattern, "regex", STRLENOF( "regex" ) ) == 0 ) {
401                                 flags = SLAP_LIMITS_REGEX;
402                                 pattern += STRLENOF( "regex" );
403
404                         /* 
405                          * this could be deprecated in favour
406                          * of the pattern = "anonymous" form
407                          */
408                         } else if ( strncasecmp( pattern, "anonymous", STRLENOF( "anonymous" ) ) == 0 ) {
409                                 flags = SLAP_LIMITS_ANONYMOUS;
410                                 pattern = NULL;
411                         }
412                 }
413
414                 /* pre-check the data */
415                 switch ( flags ) {
416                 case SLAP_LIMITS_ANONYMOUS:
417                 case SLAP_LIMITS_USERS:
418
419                         /* no need for pattern */
420                         pattern = NULL;
421                         break;
422
423                 default:
424                         if ( pattern[0] != '=' ) {
425 #ifdef NEW_LOGGING
426                                 LDAP_LOG( CONFIG, CRIT, 
427                                         "%s : line %d: missing '=' in "
428                                         "\"dn[.{exact|base|onelevel|subtree"
429                                         "|children|regex|anonymous}]" "=<pattern>\" in "
430                                         "\"limits <pattern> <limits>\" line.\n", fname, lineno, 0 );
431 #else
432                                 Debug( LDAP_DEBUG_ANY,
433                                         "%s : line %d: missing '=' in "
434                                         "\"dn[.{exact|base|onelevel|subtree"
435                                         "|children|regex|anonymous}]"
436                                         "=<pattern>\" in "
437                                         "\"limits <pattern> <limits>\" "
438                                         "line.\n%s",
439                                         fname, lineno, "" );
440 #endif
441                                 return( -1 );
442                         }
443
444                         /* skip '=' (required) */
445                         pattern++;
446
447                         /* trim obvious cases */
448                         if ( strcmp( pattern, "*" ) == 0 ) {
449                                 flags = SLAP_LIMITS_ANY;
450                                 pattern = NULL;
451
452                         } else if ( flags == SLAP_LIMITS_REGEX
453                                         && strcmp( pattern, ".*" ) == 0 ) {
454                                 flags = SLAP_LIMITS_ANY;
455                                 pattern = NULL;
456                         }
457                 }
458
459         } else if (strncasecmp( pattern, "group", STRLENOF( "group" ) ) == 0 ) {
460                 pattern += STRLENOF( "group" );
461
462                 if ( pattern[0] == '/' ) {
463                         struct berval   oc, ad;
464
465                         oc.bv_val = pattern + 1;
466
467                         ad.bv_val = strchr(pattern, '/');
468                         if ( ad.bv_val != NULL ) {
469                                 const char      *text = NULL;
470                                 int             rc;
471
472                                 oc.bv_len = ad.bv_val - oc.bv_val;
473
474                                 ad.bv_val++;
475                                 ad.bv_len = strlen( ad.bv_val );
476                                 rc = slap_bv2ad( &ad, &group_ad, &text );
477                                 if ( rc != LDAP_SUCCESS ) {
478                                         goto no_ad;
479                                 }
480
481                                 pattern = ad.bv_val + ad.bv_len;
482
483                         } else {
484                                 oc.bv_len = strlen( oc.bv_val );
485
486                                 pattern = oc.bv_val + oc.bv_len;
487                         }
488
489                         group_oc = oc_bvfind( &oc );
490                         if ( group_oc == NULL ) {
491                                 goto no_oc;
492                         }
493                 }
494
495                 if ( group_oc == NULL ) {
496                         group_oc = oc_find( SLAPD_GROUP_CLASS );
497                         if ( group_oc == NULL ) {
498 no_oc:;
499                                 return( -1 );
500                         }
501                 }
502
503                 if ( group_ad == NULL ) {
504                         const char      *text = NULL;
505                         int             rc;
506                         
507                         rc = slap_str2ad( SLAPD_GROUP_ATTR, &group_ad, &text );
508
509                         if ( rc != LDAP_SUCCESS ) {
510 no_ad:;
511                                 return( -1 );
512                         }
513                 }
514
515                 flags = SLAP_LIMITS_TYPE_GROUP | SLAP_LIMITS_EXACT;
516
517                 if ( pattern[0] != '=' ) {
518 #ifdef NEW_LOGGING
519                         LDAP_LOG( CONFIG, CRIT, 
520                                 "%s : line %d: missing '=' in "
521                                 "\"group[/objectClass[/attributeType]]"
522                                 "=<pattern>\" in "
523                                 "\"limits <pattern> <limits>\" line.\n",
524                                 fname, lineno, 0 );
525 #else
526                         Debug( LDAP_DEBUG_ANY,
527                                 "%s : line %d: missing '=' in "
528                                 "\"group[/objectClass[/attributeType]]"
529                                 "=<pattern>\" in "
530                                 "\"limits <pattern> <limits>\" line.\n",
531                                 fname, lineno, 0 );
532 #endif
533                         return( -1 );
534                 }
535
536                 /* skip '=' (required) */
537                 pattern++;
538         }
539
540         /* get the limits */
541         for ( i = 2; i < argc; i++ ) {
542                 if ( limits_parse_one( argv[i], &limit ) ) {
543
544 #ifdef NEW_LOGGING
545                         LDAP_LOG( CONFIG, CRIT, 
546                                 "%s : line %d: unknown limit values \"%s\" in "
547                                 "\"limits <pattern> <limits>\" line.\n",
548                                 fname, lineno, argv[i] );
549 #else
550                         Debug( LDAP_DEBUG_ANY,
551                                 "%s : line %d: unknown limit values \"%s\" in "
552                                 "\"limits <pattern> <limits>\" line.\n",
553                         fname, lineno, argv[i] );
554 #endif
555
556                         return( 1 );
557                 }
558         }
559
560         /*
561          * sanity checks ...
562          *
563          * FIXME: add warnings?
564          */
565         if ( limit.lms_t_hard > 0 && 
566                         ( limit.lms_t_hard < limit.lms_t_soft 
567                           || limit.lms_t_soft == -1 ) ) {
568                 limit.lms_t_hard = limit.lms_t_soft;
569         }
570         
571         if ( limit.lms_s_hard > 0 && 
572                         ( limit.lms_s_hard < limit.lms_s_soft 
573                           || limit.lms_s_soft == -1 ) ) {
574                 limit.lms_s_hard = limit.lms_s_soft;
575         }
576
577         /*
578          * defaults ...
579          * 
580          * lms_t_hard:
581          *      -1      => no limits
582          *      0       => same as soft
583          *      > 0     => limit (in seconds)
584          *
585          * lms_s_hard:
586          *      -1      => no limits
587          *      0       0> same as soft
588          *      > 0     => limit (in entries)
589          *
590          * lms_s_pr_total:
591          *      -2      => disable the control
592          *      -1      => no limits
593          *      0       => same as soft
594          *      > 0     => limit (in entries)
595          *
596          * lms_s_pr:
597          *      -1      => no limits
598          *      0       => no limits?
599          *      > 0     => limit size (in entries)
600          */
601         if ( limit.lms_s_pr_total > 0 &&
602                         limit.lms_s_pr > limit.lms_s_pr_total ) {
603                 limit.lms_s_pr = limit.lms_s_pr_total;
604         }
605
606         rc = limits_add( be, flags, pattern, group_oc, group_ad, &limit );
607         if ( rc ) {
608
609 #ifdef NEW_LOGGING
610                 LDAP_LOG( CONFIG, CRIT, 
611                         "%s : line %d: unable to add limit in "
612                         "\"limits <pattern> <limits>\" line.\n",
613                         fname, lineno, 0 );
614 #else
615                 Debug( LDAP_DEBUG_ANY,
616                         "%s : line %d: unable to add limit in "
617                         "\"limits <pattern> <limits>\" line.\n",
618                 fname, lineno, 0 );
619 #endif
620         }
621
622         return( rc );
623 }
624
625 int
626 limits_parse_one(
627         const char              *arg,
628         struct slap_limits_set  *limit
629 )
630 {
631         assert( arg );
632         assert( limit );
633
634         if ( strncasecmp( arg, "time", STRLENOF( "time" ) ) == 0 ) {
635                 arg += STRLENOF( "time" );
636
637                 if ( arg[0] == '.' ) {
638                         arg++;
639                         if ( strncasecmp( arg, "soft=", STRLENOF( "soft=" ) ) == 0 ) {
640                                 arg += STRLENOF( "soft=" );
641                                 if ( strcasecmp( arg, "none" ) == 0 ) {
642                                         limit->lms_t_soft = -1;
643
644                                 } else {
645                                         char    *next = NULL;
646                                         int     soft = strtol( arg, &next, 10 );
647
648                                         if ( next == arg || next[ 0 ] != '\0' ) {
649                                                 return( 1 );
650                                         }
651
652                                         if ( soft < -1 ) {
653                                                 return( 1 );
654                                         }
655
656                                         if ( soft == -1 ) {
657                                                 /* FIXME: use "none" instead */
658                                         }
659
660                                         limit->lms_t_soft = soft;
661                                 }
662                                 
663                         } else if ( strncasecmp( arg, "hard=", STRLENOF( "hard=" ) ) == 0 ) {
664                                 arg += STRLENOF( "hard=" );
665                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
666                                         limit->lms_t_hard = 0;
667
668                                 } else if ( strcasecmp( arg, "none" ) == 0 ) {
669                                         limit->lms_t_hard = -1;
670
671                                 } else {
672                                         char    *next = NULL;
673                                         int     hard = strtol( arg, &next, 10 );
674
675                                         if ( next == arg || next[ 0 ] != '\0' ) {
676                                                 return( 1 );
677                                         }
678
679                                         if ( hard < -1 ) {
680                                                 return( 1 );
681                                         }
682
683                                         if ( hard == -1 ) {
684                                                 /* FIXME: use "none" instead */
685                                         }
686
687                                         if ( hard == 0 ) {
688                                                 /* FIXME: use "soft" instead */
689                                         }
690
691                                         limit->lms_t_hard = hard;
692                                 }
693                                 
694                         } else {
695                                 return( 1 );
696                         }
697                         
698                 } else if ( arg[0] == '=' ) {
699                         arg++;
700                         if ( strcasecmp( arg, "none" ) == 0 ) {
701                                 limit->lms_t_soft = -1;
702
703                         } else {
704                                 char    *next = NULL;
705
706                                 limit->lms_t_soft = strtol( arg, &next, 10 );
707                                 if ( next == arg || limit->lms_t_soft < -1 ) {
708                                         return( 1 );
709                                 }
710                         }
711                         limit->lms_t_hard = 0;
712                         
713                 } else {
714                         return( 1 );
715                 }
716
717         } else if ( strncasecmp( arg, "size", STRLENOF( "size" ) ) == 0 ) {
718                 arg += STRLENOF( "size" );
719                 
720                 if ( arg[0] == '.' ) {
721                         arg++;
722                         if ( strncasecmp( arg, "soft=", STRLENOF( "soft=" ) ) == 0 ) {
723                                 arg += STRLENOF( "soft=" );
724                                 if ( strcasecmp( arg, "none" ) == 0 ) {
725                                         limit->lms_s_soft = -1;
726
727                                 } else {
728                                         char    *next = NULL;
729                                         int     soft = strtol( arg, &next, 10 );
730
731                                         if ( next == arg || next[ 0 ] != '\0' ) {
732                                                 return( 1 );
733                                         }
734
735                                         if ( soft < -1 ) {
736                                                 return( 1 );
737                                         }
738
739                                         if ( soft == -1 ) {
740                                                 /* FIXME: use "none" instead */
741                                         }
742
743                                         limit->lms_s_soft = soft;
744                                 }
745                                 
746                         } else if ( strncasecmp( arg, "hard=", STRLENOF( "hard=" ) ) == 0 ) {
747                                 arg += STRLENOF( "hard=" );
748                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
749                                         limit->lms_s_hard = 0;
750
751                                 } else if ( strcasecmp( arg, "none" ) == 0 ) {
752                                         limit->lms_s_hard = -1;
753
754                                 } else {
755                                         char    *next = NULL;
756                                         int     hard = strtol( arg, &next, 10 );
757
758                                         if ( next == arg || next[ 0 ] != '\0' ) {
759                                                 return( 1 );
760                                         }
761
762                                         if ( hard < -1 ) {
763                                                 return( 1 );
764                                         }
765
766                                         if ( hard == -1 ) {
767                                                 /* FIXME: use "none" instead */
768                                         }
769
770                                         if ( hard == 0 ) {
771                                                 /* FIXME: use "soft" instead */
772                                         }
773
774                                         limit->lms_s_hard = hard;
775                                 }
776                                 
777                         } else if ( strncasecmp( arg, "unchecked=", STRLENOF( "unchecked=" ) ) == 0 ) {
778                                 arg += STRLENOF( "unchecked=" );
779                                 if ( strcasecmp( arg, "none" ) == 0 ) {
780                                         limit->lms_s_unchecked = -1;
781
782                                 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
783                                         limit->lms_s_unchecked = 0;
784
785                                 } else {
786                                         char    *next = NULL;
787                                         int     unchecked = strtol( arg, &next, 10 );
788
789                                         if ( next == arg || next[ 0 ] != '\0' ) {
790                                                 return( 1 );
791                                         }
792
793                                         if ( unchecked < -1 ) {
794                                                 return( 1 );
795                                         }
796
797                                         if ( unchecked == -1 ) {
798                                                 /*  FIXME: use "none" instead */
799                                         }
800
801                                         limit->lms_s_unchecked = unchecked;
802                                 }
803
804                         } else if ( strncasecmp( arg, "pr=", STRLENOF( "pr=" ) ) == 0 ) {
805                                 arg += STRLENOF( "pr=" );
806                                 if ( strcasecmp( arg, "noEstimate" ) == 0 ) {
807                                         limit->lms_s_pr_hide = 1;
808
809                                 } else if ( strcasecmp( arg, "none" ) == 0 ) {
810                                         limit->lms_s_pr = -1;
811
812                                 } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
813                                         limit->lms_s_pr_total = -2;
814
815                                 } else {
816                                         char    *next = NULL;
817                                         int     pr = strtol( arg, &next, 10 );
818
819                                         if ( next == arg || next[ 0 ] != '\0' ) {
820                                                 return( 1 );
821                                         }
822
823                                         if ( pr < -1 ) {
824                                                 return( 1 );
825                                         }
826
827                                         if ( pr == -1 ) {
828                                                 /* FIXME: use "none" 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, "none" ) == 0 ) {
838                                         limit->lms_s_pr_total = -1;
839
840                                 } else if ( strcasecmp( arg, "hard" ) == 0 ) {
841                                         limit->lms_s_pr_total = 0;
842
843                                 } else {
844                                         char    *next = NULL;
845                                         int     total;
846
847                                         total = strtol( arg, &next, 10 );
848                                         if ( next == arg || next[ 0 ] != '\0' ) {
849                                                 return( 1 );
850                                         }
851
852                                         if ( total < -1 ) {
853                                                 return( 1 );
854                                         }
855
856                                         if ( total == -1 ) {
857                                                 /* FIXME: use "none" instead */
858                                         }
859
860                                         if ( total == 0 ) {
861                                                 /* FIXME: use "pr=disable" instead */
862                                         }
863
864                                         limit->lms_s_pr_total = total;
865                                 }
866
867                         } else {
868                                 return( 1 );
869                         }
870                         
871                 } else if ( arg[0] == '=' ) {
872                         arg++;
873                         if ( strcasecmp( arg, "none" ) == 0 ) {
874                                 limit->lms_s_soft = -1;
875
876                         } else {
877                                 char    *next = NULL;
878
879                                 limit->lms_s_soft = strtol( arg, &next, 10 );
880                                 if ( next == arg || limit->lms_s_soft < -1 ) {
881                                         return( 1 );
882                                 }
883                         }
884                         limit->lms_s_hard = 0;
885                         
886                 } else {
887                         return( 1 );
888                 }
889         }
890
891         return 0;
892 }
893
894
895 int
896 limits_check( Operation *op, SlapReply *rs )
897 {
898         assert( op );
899         assert( rs );
900         /* FIXME: should this be always true? */
901         assert( op->o_tag == LDAP_REQ_SEARCH);
902         
903         /* allow root to set no limit */
904         if ( be_isroot( op ) ) {
905                 op->ors_limit = NULL;
906
907                 if ( op->ors_tlimit == 0 ) {
908                         op->ors_tlimit = -1;
909                 }
910
911                 if ( op->ors_slimit == 0 ) {
912                         op->ors_slimit = -1;
913                 }
914
915         /* if not root, get appropriate limits */
916         } else {
917                 ( void ) limits_get( op, &op->o_ndn, &op->ors_limit );
918
919                 assert( op->ors_limit != NULL );
920
921                 /* if no limit is required, use soft limit */
922                 if ( op->ors_tlimit == 0 ) {
923                         op->ors_tlimit = op->ors_limit->lms_t_soft;
924
925                 /* limit required: check if legal */
926                 } else {
927                         if ( op->ors_limit->lms_t_hard == 0 ) {
928                                 if ( op->ors_limit->lms_t_soft > 0
929                                                 && ( op->ors_tlimit < 0 || op->ors_tlimit > op->ors_limit->lms_t_soft ) ) {
930                                         op->ors_tlimit = op->ors_limit->lms_t_soft;
931                                 }
932
933                         } else if ( op->ors_limit->lms_t_hard > 0 ) {
934                                 if ( op->ors_tlimit < 0 || op->ors_tlimit > op->ors_limit->lms_t_hard ) {
935                                         /* error if exceeding hard limit */
936                                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
937                                         send_ldap_result( op, rs );
938                                         rs->sr_err = LDAP_SUCCESS;
939                                         return -1;
940                                 }
941                         }
942                 }
943
944                 /* else leave as is */
945
946                 /* don't even get to backend if candidate check is disabled */
947                 if ( op->ors_limit->lms_s_unchecked == 0 ) {
948                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
949                         send_ldap_result( op, rs );
950                         rs->sr_err = LDAP_SUCCESS;
951                         return -1;
952                 }
953
954                 /* if paged results is requested */     
955                 if ( get_pagedresults( op ) ) {
956                         int     slimit = -2;
957                         int     pr_total;
958
959                         /* paged results is not allowed */
960                         if ( op->ors_limit->lms_s_pr_total == -2 ) {
961                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
962                                 rs->sr_text = "pagedResults control not allowed";
963                                 send_ldap_result( op, rs );
964                                 rs->sr_err = LDAP_SUCCESS;
965                                 rs->sr_text = NULL;
966                                 return -1;
967                         }
968                         
969                         if ( op->ors_limit->lms_s_pr > 0 && op->o_pagedresults_size > op->ors_limit->lms_s_pr ) {
970                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
971                                 rs->sr_text = "illegal pagedResults page size";
972                                 send_ldap_result( op, rs );
973                                 rs->sr_err = LDAP_SUCCESS;
974                                 rs->sr_text = NULL;
975                                 return -1;
976                         }
977
978                         if ( op->ors_limit->lms_s_pr_total == 0 ) {
979                                 if ( op->ors_limit->lms_s_hard == 0 ) {
980                                         pr_total = op->ors_limit->lms_s_soft;
981                                 } else {
982                                         pr_total = op->ors_limit->lms_s_hard;
983                                 }
984                         } else {
985                                 pr_total = op->ors_limit->lms_s_pr_total;
986                         }
987
988                         if ( op->ors_limit->lms_s_pr_total == -1 ) {
989                                 slimit = -1;
990
991                         } else if ( pr_total > 0 && ( op->ors_slimit == -1 || op->ors_slimit > pr_total ) ) {
992                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
993                                 send_ldap_result( op, rs );
994                                 rs->sr_err = LDAP_SUCCESS;
995                                 return -1;
996         
997                         } else {
998                                 /* if no limit is required, use soft limit */
999                                 int     total;
1000                                 int     slimit2;
1001
1002                                 /* first round of pagedResults: set count to any appropriate limit */
1003
1004                                 /* if the limit is set, check that it does not violate any limit */
1005                                 if ( op->ors_slimit > 0 ) {
1006                                         slimit2 = op->ors_slimit;
1007
1008                                 } else if ( op->ors_slimit == 0 ) {
1009                                         slimit2 = pr_total;
1010
1011                                 } else {
1012                                         slimit2 = -1;
1013                                 }
1014
1015                                 total = slimit2 - op->o_pagedresults_state.ps_count;
1016
1017                                 if ( total >= 0 ) {
1018                                         if ( op->ors_limit->lms_s_pr > 0 ) {
1019                                                 /* use the smallest limit set by total/per page */
1020                                                 if ( total < op->ors_limit->lms_s_pr ) {
1021                                                         slimit = total;
1022         
1023                                                 } else {
1024                                                         /* use the perpage limit if any 
1025                                                          * NOTE: + 1 because the given value must be legal */
1026                                                         slimit = op->ors_limit->lms_s_pr + 1;
1027                                                 }
1028
1029                                         } else {
1030                                                 /* use the total limit if any */
1031                                                 slimit = total;
1032                                         }
1033
1034                                 } else if ( op->ors_limit->lms_s_pr > 0 ) {
1035                                         /* use the perpage limit if any 
1036                                          * NOTE: + 1 because the given value must be legal */
1037                                         slimit = op->ors_limit->lms_s_pr + 1;
1038
1039                                 } else {
1040                                         /* use the standard hard/soft limit if any */
1041                                         slimit = op->ors_limit->lms_s_hard;
1042                                 }
1043                         }
1044                 
1045                         /* if got any limit, use it */
1046                         if ( slimit != -2 ) {
1047                                 if ( op->ors_slimit <= 0 ) {
1048                                         op->ors_slimit = slimit;
1049
1050                                 } else if ( op->ors_slimit - op->o_pagedresults_state.ps_count > slimit ) {
1051                                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1052                                         send_ldap_result( op, rs );
1053                                         rs->sr_err = LDAP_SUCCESS;
1054                                         return -1;
1055
1056                                 } else {
1057                                         op->ors_slimit = slimit;
1058                                 }
1059
1060                         } else {
1061                                 /* use the standard hard/soft limit if any */
1062                                 op->ors_slimit = op->ors_limit->lms_s_hard;
1063                         }
1064
1065                 /* no limit requested: use soft, whatever it is */
1066                 } else if ( op->ors_slimit == 0 ) {
1067                         op->ors_slimit = op->ors_limit->lms_s_soft;
1068
1069                 /* limit requested: check if legal */
1070                 } else {
1071                         /* hard limit as soft (traditional behavior) */
1072                         if ( op->ors_limit->lms_s_hard == 0 ) {
1073                                 if ( op->ors_limit->lms_s_soft > 0
1074                                                 && op->ors_slimit > op->ors_limit->lms_s_soft ) {
1075                                         op->ors_slimit = op->ors_limit->lms_s_soft;
1076                                 }
1077
1078                         /* explicit hard limit: error if violated */
1079                         } else if ( op->ors_limit->lms_s_hard > 0 ) {
1080                                 if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
1081                                         /* if limit exceeds hard, error */
1082                                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1083                                         send_ldap_result( op, rs );
1084                                         rs->sr_err = LDAP_SUCCESS;
1085                                         return -1;
1086                                 }
1087                         }
1088                 }
1089
1090                 /* else leave as is */
1091         }
1092
1093         return 0;
1094 }
1095