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