]> git.sur5r.net Git - openldap/blob - servers/slapd/limits.c
Suck in HEAD changes since 2.1alpha
[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                 default:
124                         assert( 0 );    /* unreachable */
125                         return( -1 );
126                 }
127         }
128
129         return( 0 );
130 }
131
132 static int
133 add_limits(
134         Backend                 *be,
135         int                     type,
136         const char              *pattern,
137         struct slap_limits_set  *limit
138 )
139 {
140         int                     i;
141         struct slap_limits      *lm;
142         
143         assert( be );
144         assert( limit );
145
146         lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
147
148         switch ( type ) {
149         case SLAP_LIMITS_EXACT:
150         case SLAP_LIMITS_ONE:
151         case SLAP_LIMITS_SUBTREE:
152         case SLAP_LIMITS_CHILDREN:
153                 lm->lm_type = type;
154                 {
155                         int rc;
156                         struct berval bv;
157                         bv.bv_val = (char *) pattern;
158                         bv.bv_len = strlen( pattern );
159
160                         rc = dnNormalize2( NULL, &bv, &lm->lm_dn_pat );
161                         if ( rc != LDAP_SUCCESS ) {
162                                 ch_free( lm );
163                                 return( -1 );
164                         }
165                 }
166                 break;
167                 
168         case SLAP_LIMITS_REGEX:
169         case SLAP_LIMITS_UNDEFINED:
170                 lm->lm_type = SLAP_LIMITS_REGEX;
171                 ber_str2bv( pattern, 0, 1, &lm->lm_dn_pat );
172                 if ( regcomp( &lm->lm_dn_regex, lm->lm_dn_pat.bv_val, 
173                                         REG_EXTENDED | REG_ICASE ) ) {
174                         free( lm->lm_dn_pat.bv_val );
175                         ch_free( lm );
176                         return( -1 );
177                 }
178                 break;
179
180         case SLAP_LIMITS_ANONYMOUS:
181         case SLAP_LIMITS_USERS:
182                 lm->lm_type = type;
183                 lm->lm_dn_pat.bv_val = NULL;
184                 lm->lm_dn_pat.bv_len = 0;
185                 break;
186         }
187
188         lm->lm_limits = *limit;
189
190         i = 0;
191         if ( be->be_limits != NULL ) {
192                 for ( ; be->be_limits[i]; i++ );
193         }
194
195         be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits,
196                         sizeof( struct slap_limits * ) * ( i + 2 ) );
197         be->be_limits[i] = lm;
198         be->be_limits[i+1] = NULL;
199         
200         return( 0 );
201 }
202
203 int
204 parse_limits(
205         Backend     *be,
206         const char  *fname,
207         int         lineno,
208         int         argc,
209         char        **argv
210 )
211 {
212         int     type = SLAP_LIMITS_UNDEFINED;
213         char    *pattern;
214         struct slap_limits_set limit;
215         int     i;
216
217         assert( be );
218
219         if ( argc < 3 ) {
220 #ifdef NEW_LOGGING
221                 LDAP_LOG(( "config", LDAP_LEVEL_CRIT,
222                         "%s : line %d: missing arg(s) in "
223                         "\"limits <pattern> <limits>\" line.\n",
224                         fname, lineno ));
225 #else
226                 Debug( LDAP_DEBUG_ANY,
227                         "%s : line %d: missing arg(s) in "
228                         "\"limits <pattern> <limits>\" line.\n%s",
229                         fname, lineno, "" );
230 #endif
231                 return( -1 );
232         }
233
234         limit = be->be_def_limit;
235
236         /*
237          * syntax:
238          *
239          * "limits" <pattern> <limit> [ ... ]
240          * 
241          * 
242          * <pattern>:
243          * 
244          * "anonymous"
245          * "users"
246          * [ "dn" [ "." { "exact" | "base" | "one" | "sub" | children" 
247          *      | "regex" | "anonymous" } ] "=" ] <dn pattern>
248          *
249          * Note:
250          *      "exact" and "base" are the same (exact match);
251          *      "one" means exactly one rdn below, NOT including the pattern
252          *      "sub" means any rdn below, including the pattern
253          *      "children" means any rdn below, NOT including the pattern
254          *      
255          *      "anonymous" may be deprecated in favour 
256          *      of the pattern = "anonymous" form
257          *
258          *
259          * <limit>:
260          *
261          * "time" [ "." { "soft" | "hard" } ] "=" <integer>
262          *
263          * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
264          */
265         
266         pattern = argv[1];
267         if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
268                 type = SLAP_LIMITS_ANONYMOUS;
269
270         } else if ( strcasecmp( pattern, "users" ) == 0 ) {
271                 type = SLAP_LIMITS_USERS;
272                 
273         } else if ( strncasecmp( pattern, "dn", 2 ) == 0 ) {
274                 pattern += 2;
275                 if ( pattern[0] == '.' ) {
276                         pattern++;
277                         if ( strncasecmp( pattern, "exact", 5 ) == 0 ) {
278                                 type = SLAP_LIMITS_EXACT;
279                                 pattern += 5;
280
281                         } else if ( strncasecmp( pattern, "base", 4 ) == 0 ) {
282                                 type = SLAP_LIMITS_BASE;
283                                 pattern += 4;
284
285                         } else if ( strncasecmp( pattern, "one", 3 ) == 0 ) {
286                                 type = SLAP_LIMITS_ONE;
287                                 pattern += 3;
288
289                         } else if ( strncasecmp( pattern, "subtree", 7 ) == 0 ) {
290                                 type = SLAP_LIMITS_SUBTREE;
291                                 pattern += 7;
292
293                         } else if ( strncasecmp( pattern, "children", 8 ) == 0 ) {
294                                 type = SLAP_LIMITS_CHILDREN;
295                                 pattern += 8;
296
297                         } else if ( strncasecmp( pattern, "regex", 5 ) == 0 ) {
298                                 type = SLAP_LIMITS_REGEX;
299                                 pattern += 5;
300
301                         /* 
302                          * this could be deprecated in favour
303                          * of the pattern = "anonymous" form
304                          */
305                         } else if ( strncasecmp( pattern, "anonymous", 9 ) == 0 ) {
306                                 type = SLAP_LIMITS_ANONYMOUS;
307                                 pattern = NULL;
308                         }
309                 }
310
311                 /* pre-check the data */
312                 switch ( type ) {
313                 case SLAP_LIMITS_ANONYMOUS:
314                 case SLAP_LIMITS_USERS:
315
316                         /* no need for pattern */
317                         pattern = NULL;
318                         break;
319
320                 default:
321                         if ( pattern[0] != '=' ) {
322 #ifdef NEW_LOGGING
323                                 LDAP_LOG(( "config", LDAP_LEVEL_CRIT,
324                                         "%s : line %d: missing '=' in "
325                                         "\"dn[.{exact|base|one|subtree"
326                                         "|children|regex|anonymous}]"
327                                         "=<pattern>\" in "
328                                         "\"limits <pattern> <limits>\" line.\n",
329                                         fname, lineno ));
330 #else
331                                 Debug( LDAP_DEBUG_ANY,
332                                         "%s : line %d: missing '=' in "
333                                         "\"dn[.{exact|base|one|subtree"
334                                         "|children|regex|anonymous}]"
335                                         "=<pattern>\" in "
336                                         "\"limits <pattern> <limits>\" "
337                                         "line.\n%s",
338                                         fname, lineno, "" );
339 #endif
340                                 return( -1 );
341                         }
342
343                         /* skip '=' (required) */
344                         pattern++;
345                 }
346         }
347
348         /* get the limits */
349         for ( i = 2; i < argc; i++ ) {
350                 if ( parse_limit( argv[i], &limit ) ) {
351
352 #ifdef NEW_LOGGING
353                         LDAP_LOG(( "config", LDAP_LEVEL_CRIT,
354                                 "%s : line %d: unknown limit type \"%s\" in "
355                                 "\"limits <pattern> <limits>\" line.\n",
356                         fname, lineno, argv[i] ));
357 #else
358                         Debug( LDAP_DEBUG_ANY,
359                                 "%s : line %d: unknown limit type \"%s\" in "
360                                 "\"limits <pattern> <limits>\" line.\n",
361                         fname, lineno, argv[i] );
362 #endif
363
364                         return( 1 );
365                 }
366         }
367
368         /*
369          * sanity checks ...
370          */
371         if ( limit.lms_t_hard > 0 && limit.lms_t_hard < limit.lms_t_soft ) {
372                 limit.lms_t_hard = limit.lms_t_soft;
373         }
374         
375         if ( limit.lms_s_hard > 0 && limit.lms_s_hard < limit.lms_s_soft ) {
376                 limit.lms_s_hard = limit.lms_s_soft;
377         }
378         
379         return( add_limits( be, type, pattern, &limit ) );
380 }
381
382 int
383 parse_limit(
384         const char              *arg,
385         struct slap_limits_set  *limit
386 )
387 {
388         assert( arg );
389         assert( limit );
390
391         if ( strncasecmp( arg, "time", 4 ) == 0 ) {
392                 arg += 4;
393
394                 if ( arg[0] == '.' ) {
395                         arg++;
396                         if ( strncasecmp( arg, "soft", 4 ) == 0 ) {
397                                 arg += 4;
398                                 if ( arg[0] != '=' ) {
399                                         return( 1 );
400                                 }
401                                 arg++;
402                                 limit->lms_t_soft = atoi( arg );
403                                 
404                         } else if ( strncasecmp( arg, "hard", 4 ) == 0 ) {
405                                 arg += 4;
406                                 if ( arg[0] != '=' ) {
407                                         return( 1 );
408                                 }
409                                 arg++;
410                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
411                                         limit->lms_t_hard = 0;
412                                 } else if ( strcasecmp( arg, "none" ) == 0 ) {
413                                         limit->lms_t_hard = -1;
414                                 } else {
415                                         limit->lms_t_hard = atoi( arg );
416                                 }
417                                 
418                         } else {
419                                 return( 1 );
420                         }
421                         
422                 } else if ( arg[0] == '=' ) {
423                         limit->lms_t_soft = atoi( arg );
424                         limit->lms_t_hard = 0;
425                         
426                 } else {
427                         return( 1 );
428                 }
429
430         } else if ( strncasecmp( arg, "size", 4 ) == 0 ) {
431                 arg += 4;
432                 
433                 if ( arg[0] == '.' ) {
434                         arg++;
435                         if ( strncasecmp( arg, "soft", 4 ) == 0 ) {
436                                 arg += 4;
437                                 if ( arg[0] != '=' ) {
438                                         return( 1 );
439                                 }
440                                 arg++;
441                                 limit->lms_s_soft = atoi( arg );
442                                 
443                         } else if ( strncasecmp( arg, "hard", 4 ) == 0 ) {
444                                 arg += 4;
445                                 if ( arg[0] != '=' ) {
446                                         return( 1 );
447                                 }
448                                 arg++;
449                                 if ( strcasecmp( arg, "soft" ) == 0 ) {
450                                         limit->lms_s_hard = 0;
451                                 } else if ( strcasecmp( arg, "none" ) == 0 ) {
452                                         limit->lms_s_hard = -1;
453                                 } else {
454                                         limit->lms_s_hard = atoi( arg );
455                                 }
456                                 
457                         } else if ( strncasecmp( arg, "unchecked", 9 ) == 0 ) {
458                                 arg += 9;
459                                 if ( arg[0] != '=' ) {
460                                         return( 1 );
461                                 }
462                                 arg++;
463                                 if ( strcasecmp( arg, "none" ) == 0 ) {
464                                         limit->lms_s_unchecked = -1;
465                                 } else {
466                                         limit->lms_s_unchecked = atoi( arg );
467                                 }
468                                 
469                         } else {
470                                 return( 1 );
471                         }
472                         
473                 } else if ( arg[0] == '=' ) {
474                         limit->lms_s_soft = atoi( arg );
475                         limit->lms_s_hard = 0;
476                         
477                 } else {
478                         return( 1 );
479                 }
480         }
481
482         return 0;
483 }
484