Home | History | Annotate | Line # | Download | only in slapd
      1 /*	$NetBSD: limits.c,v 1.4 2025/09/05 21:16:25 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-2024 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.4 2025/09/05 21:16:25 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 *
     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
     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
    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
    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
    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
    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
    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
   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
   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
   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