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