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