]> git.sur5r.net Git - openldap/blob - servers/slapd/limits.c
ITS#1991 fix + use struct berval
[openldap] / servers / slapd / limits.c
1 /* limits.c - routines to handle regex-based size and time limits */
2 /*
3  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6
7 #include "portable.h"
8
9 #include <stdio.h>
10
11 #include <ac/regex.h>
12 #include <ac/string.h>
13
14 #include "slap.h"
15
16 int
17 get_limits( 
18         Backend                 *be, 
19         struct berval           *ndn, 
20         struct slap_limits_set  **limit
21 )
22 {
23         struct slap_limits **lm;
24
25         assert( be );
26         assert( limit );
27
28         /*
29          * default values
30          */
31         *limit = &be->be_def_limit;
32
33         if ( be->be_limits == NULL ) {
34                 return( 0 );
35         }
36
37         for ( lm = be->be_limits; lm[0] != NULL; lm++ ) {
38                 switch ( lm[0]->lm_type ) {
39                 case SLAP_LIMITS_EXACT:
40                         if ( ndn->bv_len == 0 ) {
41                                 break;
42                         }
43                         if ( dn_match( &lm[0]->lm_dn_pat, ndn ) ) {
44                                 *limit = &lm[0]->lm_limits;
45                                 return( 0 );
46                         }
47                         break;
48
49                 case SLAP_LIMITS_ONE:
50                 case SLAP_LIMITS_SUBTREE:
51                 case SLAP_LIMITS_CHILDREN: {
52                         size_t d;
53                         
54                         if ( ndn->bv_len == 0 ) {
55                                 break;
56                         }
57
58                         /* ndn shorter than dn_pat */
59                         if ( ndn->bv_len < lm[0]->lm_dn_pat.bv_len ) {
60                                 break;
61                         }
62                         d = ndn->bv_len - lm[0]->lm_dn_pat.bv_len;
63
64                         /* allow exact match for SUBTREE only */
65                         if ( d == 0 ) {
66                                 if ( lm[0]->lm_type != SLAP_LIMITS_SUBTREE ) {
67                                         break;
68                                 }
69                         } else {
70                                 /* check for unescaped rdn separator */
71                                 if ( !DN_SEPARATOR( ndn->bv_val[d-1] ) ) {
72                                         break;
73                                 }
74                         }
75
76                         /* in case of (sub)match ... */
77                         if ( lm[0]->lm_dn_pat.bv_len == ( ndn->bv_len - d )
78                                         && strcmp( lm[0]->lm_dn_pat.bv_val, &ndn->bv_val[d] ) == 0 ) {
79                                 /* check for exactly one rdn in case of ONE */
80                                 if ( lm[0]->lm_type == SLAP_LIMITS_ONE ) {
81                                         /*
82                                          * if ndn is more that one rdn
83                                          * below dn_pat, continue
84                                          */
85                                         if ( (size_t) dn_rdnlen( NULL, ndn ) != d - 1 ) {
86                                                 break;
87                                         }
88                                 }
89
90                                 *limit = &lm[0]->lm_limits;
91                                 return( 0 );
92                         }
93
94                         break;
95                 }
96
97                 case SLAP_LIMITS_REGEX:
98                         if ( ndn->bv_len == 0 ) {
99                                 break;
100                         }
101                         if ( regexec( &lm[0]->lm_dn_regex, ndn->bv_val, 0, NULL, 0 )
102                                 == 0 )
103                         {
104                                 *limit = &lm[0]->lm_limits;
105                                 return( 0 );
106                         }
107                         break;
108
109                 case SLAP_LIMITS_ANONYMOUS:
110                         if ( ndn->bv_len == 0 ) {
111                                 *limit = &lm[0]->lm_limits;
112                                 return( 0 );
113                         }
114                         break;
115
116                 case SLAP_LIMITS_USERS:
117                         if ( ndn->bv_len != 0 ) {
118                                 *limit = &lm[0]->lm_limits;
119                                 return( 0 );
120                         }
121                         break;
122
123                 case SLAP_LIMITS_ANY:
124                         *limit = &lm[0]->lm_limits;
125                         return( 0 );
126
127                 default:
128                         assert( 0 );    /* unreachable */
129                         return( -1 );
130                 }
131         }
132
133         return( 0 );
134 }
135
136 static int
137 add_limits(
138         Backend                 *be,
139         int                     type,
140         const char              *pattern,
141         struct slap_limits_set  *limit
142 )
143 {
144         int                     i;
145         struct slap_limits      *lm;
146         
147         assert( be );
148         assert( limit );
149
150         lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
151
152         switch ( type ) {
153         case SLAP_LIMITS_EXACT:
154         case SLAP_LIMITS_ONE:
155         case SLAP_LIMITS_SUBTREE:
156         case SLAP_LIMITS_CHILDREN:
157                 lm->lm_type = type;
158                 {
159                         int rc;
160                         struct berval bv;
161                         bv.bv_val = (char *) pattern;
162                         bv.bv_len = strlen( pattern );
163
164                         rc = dnNormalize2( NULL, &bv, &lm->lm_dn_pat );
165                         if ( rc != LDAP_SUCCESS ) {
166                                 ch_free( lm );
167                                 return( -1 );
168                         }
169                 }
170                 break;
171                 
172         case SLAP_LIMITS_REGEX:
173         case SLAP_LIMITS_UNDEFINED:
174                 lm->lm_type = SLAP_LIMITS_REGEX;
175                 ber_str2bv( pattern, 0, 1, &lm->lm_dn_pat );
176                 if ( regcomp( &lm->lm_dn_regex, lm->lm_dn_pat.bv_val, 
177                                         REG_EXTENDED | REG_ICASE ) ) {
178                         free( lm->lm_dn_pat.bv_val );
179                         ch_free( lm );
180                         return( -1 );
181                 }
182                 break;
183
184         case SLAP_LIMITS_ANONYMOUS:
185         case SLAP_LIMITS_USERS:
186         case SLAP_LIMITS_ANY:
187                 lm->lm_type = type;
188                 lm->lm_dn_pat.bv_val = NULL;
189                 lm->lm_dn_pat.bv_len = 0;
190                 break;
191         }
192
193         lm->lm_limits = *limit;
194
195         i = 0;
196         if ( be->be_limits != NULL ) {
197                 for ( ; be->be_limits[i]; i++ );
198         }
199
200         be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits,
201                         sizeof( struct slap_limits * ) * ( i + 2 ) );
202         be->be_limits[i] = lm;
203         be->be_limits[i+1] = NULL;
204         
205         return( 0 );
206 }
207
208 int
209 parse_limits(
210         Backend     *be,
211         const char  *fname,
212         int         lineno,
213         int         argc,
214         char        **argv
215 )
216 {
217         int     type = SLAP_LIMITS_UNDEFINED;
218         char    *pattern;
219         struct slap_limits_set limit;
220         int     i;
221
222         assert( be );
223
224         if ( argc < 3 ) {
225 #ifdef NEW_LOGGING
226                 LDAP_LOG( CONFIG, CRIT, 
227                         "%s : line %d: missing arg(s) in "
228                         "\"limits <pattern> <limits>\" line.\n", fname, lineno, 0 );
229 #else
230                 Debug( LDAP_DEBUG_ANY,
231                         "%s : line %d: missing arg(s) in "
232                         "\"limits <pattern> <limits>\" line.\n%s",
233                         fname, lineno, "" );
234 #endif
235                 return( -1 );
236         }
237
238         limit = be->be_def_limit;
239
240         /*
241          * syntax:
242          *
243          * "limits" <pattern> <limit> [ ... ]
244          * 
245          * 
246          * <pattern>:
247          * 
248          * "anonymous"
249          * "users"
250          * [ "dn" [ "." { "exact" | "base" | "one" | "sub" | children" 
251          *      | "regex" | "anonymous" } ] "=" ] <dn pattern>
252          *
253          * Note:
254          *      "exact" and "base" are the same (exact match);
255          *      "one" means exactly one rdn below, NOT including the pattern
256          *      "sub" means any rdn below, including the pattern
257          *      "children" means any rdn below, NOT including the pattern
258          *      
259          *      "anonymous" may be deprecated in favour 
260          *      of the pattern = "anonymous" form
261          *
262          *
263          * <limit>:
264          *
265          * "time" [ "." { "soft" | "hard" } ] "=" <integer>
266          *
267          * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
268          */
269         
270         pattern = argv[1];
271         if ( strcmp( pattern, "*" ) == 0) {
272                 type = SLAP_LIMITS_ANY;
273
274         } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
275                 type = SLAP_LIMITS_ANONYMOUS;
276
277         } else if ( strcasecmp( pattern, "users" ) == 0 ) {
278                 type = SLAP_LIMITS_USERS;
279                 
280         } else if ( strncasecmp( pattern, "dn", 2 ) == 0 ) {
281                 pattern += 2;
282                 if ( pattern[0] == '.' ) {
283                         pattern++;
284                         if ( strncasecmp( pattern, "exact", 5 ) == 0 ) {
285                                 type = SLAP_LIMITS_EXACT;
286                                 pattern += 5;
287
288                         } else if ( strncasecmp( pattern, "base", 4 ) == 0 ) {
289                                 type = SLAP_LIMITS_BASE;
290                                 pattern += 4;
291
292                         } else if ( strncasecmp( pattern, "one", 3 ) == 0 ) {
293                                 type = SLAP_LIMITS_ONE;
294                                 pattern += 3;
295
296                         } else if ( strncasecmp( pattern, "subtree", 7 ) == 0 ) {
297                                 type = SLAP_LIMITS_SUBTREE;
298                                 pattern += 7;
299
300                         } else if ( strncasecmp( pattern, "children", 8 ) == 0 ) {
301                                 type = SLAP_LIMITS_CHILDREN;
302                                 pattern += 8;
303
304                         } else if ( strncasecmp( pattern, "regex", 5 ) == 0 ) {
305                                 type = SLAP_LIMITS_REGEX;
306                                 pattern += 5;
307
308                         /* 
309                          * this could be deprecated in favour
310                          * of the pattern = "anonymous" form
311                          */
312                         } else if ( strncasecmp( pattern, "anonymous", 9 ) == 0 ) {
313                                 type = SLAP_LIMITS_ANONYMOUS;
314                                 pattern = NULL;
315                         }
316                 }
317
318                 /* pre-check the data */
319                 switch ( type ) {
320                 case SLAP_LIMITS_ANONYMOUS:
321                 case SLAP_LIMITS_USERS:
322
323                         /* no need for pattern */
324                         pattern = NULL;
325                         break;
326
327                 default:
328                         if ( pattern[0] != '=' ) {
329 #ifdef NEW_LOGGING
330                                 LDAP_LOG( CONFIG, CRIT, 
331                                         "%s : line %d: missing '=' in "
332                                         "\"dn[.{exact|base|one|subtree"
333                                         "|children|regex|anonymous}]" "=<pattern>\" in "
334                                         "\"limits <pattern> <limits>\" line.\n", fname, lineno, 0 );
335 #else
336                                 Debug( LDAP_DEBUG_ANY,
337                                         "%s : line %d: missing '=' in "
338                                         "\"dn[.{exact|base|one|subtree"
339                                         "|children|regex|anonymous}]"
340                                         "=<pattern>\" in "
341                                         "\"limits <pattern> <limits>\" "
342                                         "line.\n%s",
343                                         fname, lineno, "" );
344 #endif
345                                 return( -1 );
346                         }
347
348                         /* skip '=' (required) */
349                         pattern++;
350
351                         /* trim obvious cases */
352                         if ( strcmp( pattern, "*" ) == 0 ) {
353                                 type = SLAP_LIMITS_ANY;
354                                 pattern = NULL;
355
356                         } else if ( ( type == SLAP_LIMITS_REGEX || type == SLAP_LIMITS_UNDEFINED ) 
357                                         && strcmp( pattern, ".*" ) == 0 ) {
358                                 type = SLAP_LIMITS_ANY;
359                                 pattern = NULL;
360                         }
361                 }
362         }
363
364         /* get the limits */
365         for ( i = 2; i < argc; i++ ) {
366                 if ( parse_limit( argv[i], &limit ) ) {
367
368 #ifdef NEW_LOGGING
369                         LDAP_LOG( CONFIG, CRIT, 
370                                 "%s : line %d: unknown limit type \"%s\" in "
371                                 "\"limits <pattern> <limits>\" line.\n",
372                                 fname, lineno, argv[i] );
373 #else
374                         Debug( LDAP_DEBUG_ANY,
375                                 "%s : line %d: unknown limit type \"%s\" in "
376                                 "\"limits <pattern> <limits>\" line.\n",
377                         fname, lineno, argv[i] );
378 #endif
379
380                         return( 1 );
381                 }
382         }
383
384         /*
385          * sanity checks ...
386          */
387         if ( limit.lms_t_hard > 0 && limit.lms_t_hard < limit.lms_t_soft ) {
388                 limit.lms_t_hard = limit.lms_t_soft;
389         }
390         
391         if ( limit.lms_s_hard > 0 && limit.lms_s_hard < limit.lms_s_soft ) {
392                 limit.lms_s_hard = limit.lms_s_soft;
393         }
394         
395         return( add_limits( be, type, pattern, &limit ) );
396 }
397
398 int
399 parse_limit(
400         const char              *arg,
401         struct slap_limits_set  *limit
402 )
403 {
404         assert( arg );
405         assert( limit );
406
407         if ( strncasecmp( arg, "time", 4 ) == 0 ) {
408                 arg += 4;
409
410                 if ( arg[0] == '.' ) {
411                         arg++;
412                         if ( strncasecmp( arg, "soft", 4 ) == 0 ) {
413                                 arg += 4;
414                                 if ( arg[0] != '=' ) {
415                                         return( 1 );
416                                 }
417                                 arg++;
418                                 limit->lms_t_soft = atoi( arg );
419                                 
420                         } else if ( strncasecmp( arg, "hard", 4 ) == 0 ) {
421                                 arg += 4;
422                                 if ( arg[0] != '=' ) {
423                                         return( 1 );
424                                 }
425                                 arg++;
426                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
427                                         limit->lms_t_hard = 0;
428                                 } else if ( strcasecmp( arg, "none" ) == 0 ) {
429                                         limit->lms_t_hard = -1;
430                                 } else {
431                                         limit->lms_t_hard = atoi( arg );
432                                 }
433                                 
434                         } else {
435                                 return( 1 );
436                         }
437                         
438                 } else if ( arg[0] == '=' ) {
439                         arg++;
440                         limit->lms_t_soft = atoi( arg );
441                         limit->lms_t_hard = 0;
442                         
443                 } else {
444                         return( 1 );
445                 }
446
447         } else if ( strncasecmp( arg, "size", 4 ) == 0 ) {
448                 arg += 4;
449                 
450                 if ( arg[0] == '.' ) {
451                         arg++;
452                         if ( strncasecmp( arg, "soft", 4 ) == 0 ) {
453                                 arg += 4;
454                                 if ( arg[0] != '=' ) {
455                                         return( 1 );
456                                 }
457                                 arg++;
458                                 limit->lms_s_soft = atoi( arg );
459                                 
460                         } else if ( strncasecmp( arg, "hard", 4 ) == 0 ) {
461                                 arg += 4;
462                                 if ( arg[0] != '=' ) {
463                                         return( 1 );
464                                 }
465                                 arg++;
466                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
467                                         limit->lms_s_hard = 0;
468                                 } else if ( strcasecmp( arg, "none" ) == 0 ) {
469                                         limit->lms_s_hard = -1;
470                                 } else {
471                                         limit->lms_s_hard = atoi( arg );
472                                 }
473                                 
474                         } else if ( strncasecmp( arg, "unchecked", 9 ) == 0 ) {
475                                 arg += 9;
476                                 if ( arg[0] != '=' ) {
477                                         return( 1 );
478                                 }
479                                 arg++;
480                                 if ( strcasecmp( arg, "none" ) == 0 ) {
481                                         limit->lms_s_unchecked = -1;
482                                 } else {
483                                         limit->lms_s_unchecked = atoi( arg );
484                                 }
485                                 
486                         } else {
487                                 return( 1 );
488                         }
489                         
490                 } else if ( arg[0] == '=' ) {
491                         arg++;
492                         limit->lms_s_soft = atoi( arg );
493                         limit->lms_s_hard = 0;
494                         
495                 } else {
496                         return( 1 );
497                 }
498         }
499
500         return 0;
501 }
502