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