]> git.sur5r.net Git - openldap/blob - servers/slapd/limits.c
Completely untested built-in EXTERNAL implementation
[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-2003 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/regex.h>
22 #include <ac/string.h>
23
24 #include "slap.h"
25
26 int
27 get_limits( 
28         Backend                 *be, 
29         struct berval           *ndn, 
30         struct slap_limits_set  **limit
31 )
32 {
33         struct slap_limits **lm;
34
35         assert( be );
36         assert( limit );
37
38         /*
39          * default values
40          */
41         *limit = &be->be_def_limit;
42
43         if ( be->be_limits == NULL ) {
44                 return( 0 );
45         }
46
47         for ( lm = be->be_limits; lm[0] != NULL; lm++ ) {
48                 switch ( lm[0]->lm_type ) {
49                 case SLAP_LIMITS_EXACT:
50                         if ( ndn->bv_len == 0 ) {
51                                 break;
52                         }
53                         if ( dn_match( &lm[0]->lm_dn_pat, ndn ) ) {
54                                 *limit = &lm[0]->lm_limits;
55                                 return( 0 );
56                         }
57                         break;
58
59                 case SLAP_LIMITS_ONE:
60                 case SLAP_LIMITS_SUBTREE:
61                 case SLAP_LIMITS_CHILDREN: {
62                         size_t d;
63                         
64                         if ( ndn->bv_len == 0 ) {
65                                 break;
66                         }
67
68                         /* ndn shorter than dn_pat */
69                         if ( ndn->bv_len < lm[0]->lm_dn_pat.bv_len ) {
70                                 break;
71                         }
72                         d = ndn->bv_len - lm[0]->lm_dn_pat.bv_len;
73
74                         /* allow exact match for SUBTREE only */
75                         if ( d == 0 ) {
76                                 if ( lm[0]->lm_type != SLAP_LIMITS_SUBTREE ) {
77                                         break;
78                                 }
79                         } else {
80                                 /* check for unescaped rdn separator */
81                                 if ( !DN_SEPARATOR( ndn->bv_val[d-1] ) ) {
82                                         break;
83                                 }
84                         }
85
86                         /* in case of (sub)match ... */
87                         if ( lm[0]->lm_dn_pat.bv_len == ( ndn->bv_len - d )
88                                         && strcmp( lm[0]->lm_dn_pat.bv_val, &ndn->bv_val[d] ) == 0 ) {
89                                 /* check for exactly one rdn in case of ONE */
90                                 if ( lm[0]->lm_type == SLAP_LIMITS_ONE ) {
91                                         /*
92                                          * if ndn is more that one rdn
93                                          * below dn_pat, continue
94                                          */
95                                         if ( (size_t) dn_rdnlen( NULL, ndn ) != d - 1 ) {
96                                                 break;
97                                         }
98                                 }
99
100                                 *limit = &lm[0]->lm_limits;
101                                 return( 0 );
102                         }
103
104                         break;
105                 }
106
107                 case SLAP_LIMITS_REGEX:
108                         if ( ndn->bv_len == 0 ) {
109                                 break;
110                         }
111                         if ( regexec( &lm[0]->lm_dn_regex, ndn->bv_val, 0, NULL, 0 )
112                                 == 0 )
113                         {
114                                 *limit = &lm[0]->lm_limits;
115                                 return( 0 );
116                         }
117                         break;
118
119                 case SLAP_LIMITS_ANONYMOUS:
120                         if ( ndn->bv_len == 0 ) {
121                                 *limit = &lm[0]->lm_limits;
122                                 return( 0 );
123                         }
124                         break;
125
126                 case SLAP_LIMITS_USERS:
127                         if ( ndn->bv_len != 0 ) {
128                                 *limit = &lm[0]->lm_limits;
129                                 return( 0 );
130                         }
131                         break;
132
133                 case SLAP_LIMITS_ANY:
134                         *limit = &lm[0]->lm_limits;
135                         return( 0 );
136
137                 default:
138                         assert( 0 );    /* unreachable */
139                         return( -1 );
140                 }
141         }
142
143         return( 0 );
144 }
145
146 static int
147 add_limits(
148         Backend                 *be,
149         int                     type,
150         const char              *pattern,
151         struct slap_limits_set  *limit
152 )
153 {
154         int                     i;
155         struct slap_limits      *lm;
156         
157         assert( be );
158         assert( limit );
159
160         switch ( type ) {
161         case SLAP_LIMITS_ANONYMOUS:
162         case SLAP_LIMITS_USERS:
163         case SLAP_LIMITS_ANY:
164                 for ( i = 0; be->be_limits && be->be_limits[ i ]; i++ ) {
165                         if ( be->be_limits[ i ]->lm_type == type ) {
166                                 return( -1 );
167                         }
168                 }
169                 break;
170         }
171
172
173         lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
174
175         switch ( type ) {
176         case SLAP_LIMITS_EXACT:
177         case SLAP_LIMITS_ONE:
178         case SLAP_LIMITS_SUBTREE:
179         case SLAP_LIMITS_CHILDREN:
180                 lm->lm_type = type;
181                 {
182                         int rc;
183                         struct berval bv;
184                         bv.bv_val = (char *) pattern;
185                         bv.bv_len = strlen( pattern );
186
187                         rc = dnNormalize( 0, NULL, NULL, &bv, &lm->lm_dn_pat, NULL );
188                         if ( rc != LDAP_SUCCESS ) {
189                                 ch_free( lm );
190                                 return( -1 );
191                         }
192                 }
193                 break;
194                 
195         case SLAP_LIMITS_REGEX:
196         case SLAP_LIMITS_UNDEFINED:
197                 lm->lm_type = SLAP_LIMITS_REGEX;
198                 ber_str2bv( pattern, 0, 1, &lm->lm_dn_pat );
199                 if ( regcomp( &lm->lm_dn_regex, lm->lm_dn_pat.bv_val, 
200                                         REG_EXTENDED | REG_ICASE ) ) {
201                         free( lm->lm_dn_pat.bv_val );
202                         ch_free( lm );
203                         return( -1 );
204                 }
205                 break;
206
207         case SLAP_LIMITS_ANONYMOUS:
208         case SLAP_LIMITS_USERS:
209         case SLAP_LIMITS_ANY:
210                 lm->lm_type = type;
211                 lm->lm_dn_pat.bv_val = NULL;
212                 lm->lm_dn_pat.bv_len = 0;
213                 break;
214         }
215
216         lm->lm_limits = *limit;
217
218         i = 0;
219         if ( be->be_limits != NULL ) {
220                 for ( ; be->be_limits[i]; i++ );
221         }
222
223         be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits,
224                         sizeof( struct slap_limits * ) * ( i + 2 ) );
225         be->be_limits[i] = lm;
226         be->be_limits[i+1] = NULL;
227         
228         return( 0 );
229 }
230
231 int
232 parse_limits(
233         Backend     *be,
234         const char  *fname,
235         int         lineno,
236         int         argc,
237         char        **argv
238 )
239 {
240         int     type = SLAP_LIMITS_UNDEFINED;
241         char    *pattern;
242         struct slap_limits_set limit;
243         int     i, rc = 0;
244
245         assert( be );
246
247         if ( argc < 3 ) {
248 #ifdef NEW_LOGGING
249                 LDAP_LOG( CONFIG, CRIT, 
250                         "%s : line %d: missing arg(s) in "
251                         "\"limits <pattern> <limits>\" line.\n", fname, lineno, 0 );
252 #else
253                 Debug( LDAP_DEBUG_ANY,
254                         "%s : line %d: missing arg(s) in "
255                         "\"limits <pattern> <limits>\" line.\n%s",
256                         fname, lineno, "" );
257 #endif
258                 return( -1 );
259         }
260
261         limit = be->be_def_limit;
262
263         /*
264          * syntax:
265          *
266          * "limits" <pattern> <limit> [ ... ]
267          * 
268          * 
269          * <pattern>:
270          * 
271          * "anonymous"
272          * "users"
273          * [ "dn" [ "." { "exact" | "base" | "one" | "sub" | children" 
274          *      | "regex" | "anonymous" } ] "=" ] <dn pattern>
275          *
276          * Note:
277          *      "exact" and "base" are the same (exact match);
278          *      "one" means exactly one rdn below, NOT including the pattern
279          *      "sub" means any rdn below, including the pattern
280          *      "children" means any rdn below, NOT including the pattern
281          *      
282          *      "anonymous" may be deprecated in favour 
283          *      of the pattern = "anonymous" form
284          *
285          *
286          * <limit>:
287          *
288          * "time" [ "." { "soft" | "hard" } ] "=" <integer>
289          *
290          * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
291          */
292         
293         pattern = argv[1];
294         if ( strcmp( pattern, "*" ) == 0) {
295                 type = SLAP_LIMITS_ANY;
296
297         } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
298                 type = SLAP_LIMITS_ANONYMOUS;
299
300         } else if ( strcasecmp( pattern, "users" ) == 0 ) {
301                 type = SLAP_LIMITS_USERS;
302                 
303         } else if ( strncasecmp( pattern, "dn", sizeof( "dn") - 1 ) == 0 ) {
304                 pattern += 2;
305                 if ( pattern[0] == '.' ) {
306                         pattern++;
307                         if ( strncasecmp( pattern, "exact", sizeof( "exact" ) - 1 ) == 0 ) {
308                                 type = SLAP_LIMITS_EXACT;
309                                 pattern += 5;
310
311                         } else if ( strncasecmp( pattern, "base", sizeof( "base " ) - 1 ) == 0 ) {
312                                 type = SLAP_LIMITS_BASE;
313                                 pattern += 4;
314
315                         } else if ( strncasecmp( pattern, "one", sizeof( "one" ) - 1 ) == 0 ) {
316                                 type = SLAP_LIMITS_ONE;
317                                 pattern += 3;
318
319                         } else if ( strncasecmp( pattern, "subtree", sizeof( "subtree" ) - 1 ) == 0 ) {
320                                 type = SLAP_LIMITS_SUBTREE;
321                                 pattern += 7;
322
323                         } else if ( strncasecmp( pattern, "children", sizeof( "children" ) - 1 ) == 0 ) {
324                                 type = SLAP_LIMITS_CHILDREN;
325                                 pattern += 8;
326
327                         } else if ( strncasecmp( pattern, "regex", sizeof( "regex" ) - 1 ) == 0 ) {
328                                 type = SLAP_LIMITS_REGEX;
329                                 pattern += 5;
330
331                         /* 
332                          * this could be deprecated in favour
333                          * of the pattern = "anonymous" form
334                          */
335                         } else if ( strncasecmp( pattern, "anonymous", sizeof( "anonymous" ) - 1 ) == 0 ) {
336                                 type = SLAP_LIMITS_ANONYMOUS;
337                                 pattern = NULL;
338                         }
339                 }
340
341                 /* pre-check the data */
342                 switch ( type ) {
343                 case SLAP_LIMITS_ANONYMOUS:
344                 case SLAP_LIMITS_USERS:
345
346                         /* no need for pattern */
347                         pattern = NULL;
348                         break;
349
350                 default:
351                         if ( pattern[0] != '=' ) {
352 #ifdef NEW_LOGGING
353                                 LDAP_LOG( CONFIG, CRIT, 
354                                         "%s : line %d: missing '=' in "
355                                         "\"dn[.{exact|base|one|subtree"
356                                         "|children|regex|anonymous}]" "=<pattern>\" in "
357                                         "\"limits <pattern> <limits>\" line.\n", fname, lineno, 0 );
358 #else
359                                 Debug( LDAP_DEBUG_ANY,
360                                         "%s : line %d: missing '=' in "
361                                         "\"dn[.{exact|base|one|subtree"
362                                         "|children|regex|anonymous}]"
363                                         "=<pattern>\" in "
364                                         "\"limits <pattern> <limits>\" "
365                                         "line.\n%s",
366                                         fname, lineno, "" );
367 #endif
368                                 return( -1 );
369                         }
370
371                         /* skip '=' (required) */
372                         pattern++;
373
374                         /* trim obvious cases */
375                         if ( strcmp( pattern, "*" ) == 0 ) {
376                                 type = SLAP_LIMITS_ANY;
377                                 pattern = NULL;
378
379                         } else if ( ( type == SLAP_LIMITS_REGEX || type == SLAP_LIMITS_UNDEFINED ) 
380                                         && strcmp( pattern, ".*" ) == 0 ) {
381                                 type = SLAP_LIMITS_ANY;
382                                 pattern = NULL;
383                         }
384                 }
385         }
386
387         /* get the limits */
388         for ( i = 2; i < argc; i++ ) {
389                 if ( parse_limit( argv[i], &limit ) ) {
390
391 #ifdef NEW_LOGGING
392                         LDAP_LOG( CONFIG, CRIT, 
393                                 "%s : line %d: unknown limit type \"%s\" in "
394                                 "\"limits <pattern> <limits>\" line.\n",
395                                 fname, lineno, argv[i] );
396 #else
397                         Debug( LDAP_DEBUG_ANY,
398                                 "%s : line %d: unknown limit type \"%s\" in "
399                                 "\"limits <pattern> <limits>\" line.\n",
400                         fname, lineno, argv[i] );
401 #endif
402
403                         return( 1 );
404                 }
405         }
406
407         /*
408          * sanity checks ...
409          */
410         if ( limit.lms_t_hard > 0 && 
411                         ( limit.lms_t_hard < limit.lms_t_soft 
412                           || limit.lms_t_soft == -1 ) ) {
413                 limit.lms_t_hard = limit.lms_t_soft;
414         }
415         
416         if ( limit.lms_s_hard > 0 && 
417                         ( limit.lms_s_hard < limit.lms_s_soft 
418                           || limit.lms_s_soft == -1 ) ) {
419                 limit.lms_s_hard = limit.lms_s_soft;
420         }
421         
422         rc = add_limits( be, type, pattern, &limit );
423         if ( rc ) {
424
425 #ifdef NEW_LOGGING
426                 LDAP_LOG( CONFIG, CRIT, 
427                         "%s : line %d: unable to add limit in "
428                         "\"limits <pattern> <limits>\" line.\n",
429                         fname, lineno, 0 );
430 #else
431                 Debug( LDAP_DEBUG_ANY,
432                         "%s : line %d: unable to add limit in "
433                         "\"limits <pattern> <limits>\" line.\n",
434                 fname, lineno, 0 );
435 #endif
436         }
437
438         return( rc );
439 }
440
441 int
442 parse_limit(
443         const char              *arg,
444         struct slap_limits_set  *limit
445 )
446 {
447         assert( arg );
448         assert( limit );
449
450         if ( strncasecmp( arg, "time", sizeof( "time" ) - 1 ) == 0 ) {
451                 arg += 4;
452
453                 if ( arg[0] == '.' ) {
454                         arg++;
455                         if ( strncasecmp( arg, "soft", sizeof( "soft" ) - 1 ) == 0 ) {
456                                 arg += 4;
457                                 if ( arg[0] != '=' ) {
458                                         return( 1 );
459                                 }
460                                 arg++;
461                                 if ( strcasecmp( arg, "none" ) == 0 ) {
462                                         limit->lms_t_soft = -1;
463                                 } else {
464                                         char    *next = NULL;
465
466                                         limit->lms_t_soft = 
467                                                 strtol( arg, &next, 10 );
468                                         if ( next == arg || limit->lms_t_soft < -1 ) {
469                                                 return( 1 );
470                                         }
471                                 }
472                                 
473                         } else if ( strncasecmp( arg, "hard", sizeof( "hard" ) - 1 ) == 0 ) {
474                                 arg += 4;
475                                 if ( arg[0] != '=' ) {
476                                         return( 1 );
477                                 }
478                                 arg++;
479                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
480                                         limit->lms_t_hard = 0;
481                                 } else if ( strcasecmp( arg, "none" ) == 0 ) {
482                                         limit->lms_t_hard = -1;
483                                 } else {
484                                         char    *next = NULL;
485
486                                         limit->lms_t_hard = 
487                                                 strtol( arg, &next, 10 );
488                                         if ( next == arg || limit->lms_t_hard < -1 ) {
489                                                 return( 1 );
490                                         }
491                                 }
492                                 
493                         } else {
494                                 return( 1 );
495                         }
496                         
497                 } else if ( arg[0] == '=' ) {
498                         arg++;
499                         if ( strcasecmp( arg, "none" ) == 0 ) {
500                                 limit->lms_t_soft = -1;
501                         } else {
502                                 char    *next = NULL;
503
504                                 limit->lms_t_soft = strtol( arg, &next, 10 );
505                                 if ( next == arg || limit->lms_t_soft < -1 ) {
506                                         return( 1 );
507                                 }
508                         }
509                         limit->lms_t_hard = 0;
510                         
511                 } else {
512                         return( 1 );
513                 }
514
515         } else if ( strncasecmp( arg, "size", sizeof( "size" ) - 1 ) == 0 ) {
516                 arg += 4;
517                 
518                 if ( arg[0] == '.' ) {
519                         arg++;
520                         if ( strncasecmp( arg, "soft", sizeof( "soft" ) - 1 ) == 0 ) {
521                                 arg += 4;
522                                 if ( arg[0] != '=' ) {
523                                         return( 1 );
524                                 }
525                                 arg++;
526                                 if ( strcasecmp( arg, "none" ) == 0 ) {
527                                         limit->lms_s_soft = -1;
528                                 } else {
529                                         char    *next = NULL;
530
531                                         limit->lms_s_soft = 
532                                                 strtol( arg, &next, 10 );
533                                         if ( next == arg || limit->lms_s_soft < -1 ) {
534                                                 return( 1 );
535                                         }
536                                 }
537                                 
538                         } else if ( strncasecmp( arg, "hard", sizeof( "hard" ) - 1 ) == 0 ) {
539                                 arg += 4;
540                                 if ( arg[0] != '=' ) {
541                                         return( 1 );
542                                 }
543                                 arg++;
544                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
545                                         limit->lms_s_hard = 0;
546                                 } else if ( strcasecmp( arg, "none" ) == 0 ) {
547                                         limit->lms_s_hard = -1;
548                                 } else {
549                                         char    *next = NULL;
550
551                                         limit->lms_s_hard = 
552                                                 strtol( arg, &next, 10 );
553                                         if ( next == arg || limit->lms_s_hard < -1 ) {
554                                                 return( 1 );
555                                         }
556                                 }
557                                 
558                         } else if ( strncasecmp( arg, "unchecked", sizeof( "unchecked" ) - 1 ) == 0 ) {
559                                 arg += 9;
560                                 if ( arg[0] != '=' ) {
561                                         return( 1 );
562                                 }
563                                 arg++;
564                                 if ( strcasecmp( arg, "none" ) == 0 ) {
565                                         limit->lms_s_unchecked = -1;
566                                 } else {
567                                         char    *next = NULL;
568
569                                         limit->lms_s_unchecked = 
570                                                 strtol( arg, &next, 10 );
571                                         if ( next == arg || limit->lms_s_unchecked < -1 ) {
572                                                 return( 1 );
573                                         }
574                                 }
575
576                         } else if ( strncasecmp( arg, "pr", sizeof( "pr" ) - 1 ) == 0 ) {
577                                 arg += sizeof( "pr" ) - 1;
578                                 if ( arg[0] != '=' ) {
579                                         return( 1 );
580                                 }
581                                 arg++;
582                                 if ( strcasecmp( arg, "noEstimate" ) == 0 ) {
583                                         limit->lms_s_pr_hide = 1;
584                                 } else {
585                                         char    *next = NULL;
586
587                                         limit->lms_s_pr = 
588                                                 strtol( arg, &next, 10 );
589                                         if ( next == arg || limit->lms_s_pr < -1 ) {
590                                                 return( 1 );
591                                         }
592                                 }
593                                 
594                         } else {
595                                 return( 1 );
596                         }
597                         
598                 } else if ( arg[0] == '=' ) {
599                         arg++;
600                         if ( strcasecmp( arg, "none" ) == 0 ) {
601                                 limit->lms_s_soft = -1;
602                         } else {
603                                 char    *next = NULL;
604
605                                 limit->lms_s_soft = strtol( arg, &next, 10 );
606                                 if ( next == arg || limit->lms_s_soft < -1 ) {
607                                         return( 1 );
608                                 }
609                         }
610                         limit->lms_s_hard = 0;
611                         
612                 } else {
613                         return( 1 );
614                 }
615         }
616
617         return 0;
618 }
619