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