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