Commit 8471ef7e authored by Pierangelo Masarati's avatar Pierangelo Masarati
Browse files

add global, per backend and per op_ndn time/size soft, hard and to-be-checked...

add global, per backend and per op_ndn time/size soft, hard and to-be-checked limits (exploited by back-ldbm); see slapd.conf(5) for details
parent a8e36690
......@@ -158,7 +158,7 @@ feature. The default is 0.
Read additional configuration information from the given file before
continuing with the next line of the current file.
.TP
.B limits [dn[.{exact|regex}]=]<pattern> <limit> [<limit> [...]]
.B limits [dn[.{exact|regex}]=]<pattern> <limit> [...]
Specify time and size limits based on the distinguished name that
initiated an operation.
The argument
......@@ -169,14 +169,69 @@ It is a distinguished name in case of
match, or an Extended Regex pattern in case of
.BR regex
match (the default).
The currently supported limits are "size" and "time".
The syntax for time limits is
.BR time[.{soft|hard}]=<integer> ,
where
.BR integer
is the number of seconds slapd will spend answering a search request.
If no time limit is explicitly requested by the client, the
.BR soft
limit is used; if the requested time limit exceedes the
.BR hard
limit, an "Unwilling to perform" is returned.
If the
.BR hard
limit is set to zero, the soft limit is used in either case;
if it is set to -1, no hard limit is enforced.
Explicit requests for time limits smaller or equal to the
.BR hard
limit are honored.
If no flag is set, the value is assigned to the
.BR soft
limit, and the
.BR hard
limit is set to zero, to preserve the original behavior.
The syntax for size limits is
.BR size[.{soft|hard|unchecked}]=<integer> ,
where
.BR integer
is the maximum number of entries slapd will return answering a search
request.
If no size limit is explicitly requested by the client, the
.BR soft
limit is used; if the requested size limit exceedes the
.BR hard
limit, an "Unwilling to perform" is returned.
If the
.BR hard
limit is set to zero, the soft limit is used in either case;
if it is set to -1, no hard limit is enforced.
Explicit requests for size limits smaller or equal to the
.BR hard
limit are honored.
The
.BR limit
argument(s) take the form
.BR <name>=<value>
where the currently supported names are "size" and "time", whose values
are the maximum number of entries that are returned by a search
and the number of seconds slapd will spend answering a search request.
.BR unchecked
flag sets a limit on the number of candidates a search request is allowed
to examine.
If the selected candidates exceed the
.BR unchecked
limit, the search will abort with "Unwilling to perform".
If no flag is set, the value is assigned to the
.BR soft
limit, and the
.BR hard
limit is set to zero, to preserve the original behavior.
In case of no match, the global limits are used.
The default values are the same of
.BR sizelimit
and
.BR timelimit ;
no limit is set on
.BR unchecked .
This feature is currently exploited by the ldbm backend only.
.TP
.B loglevel <integer>
......@@ -455,9 +510,15 @@ e.g. ldapi:// (and eventually IPSEC). It is not normally used.
.B schemacheck { on | off }
Turn schema checking on or off. The default is on.
.TP
.B sizelimit <integer>
.B sizelimit <integer>
.TP
.B sizelimit size[.{soft|hard|unchecked}]=<integer>
Specify the maximum number of entries to return from a search operation.
The default size limit is 500.
The second format allows a fine grain setting of the size limits.
See
.BR limits
for an explanation of the different flags.
.TP
.B sockbuf_max_incoming <integer>
Specify the maximum incoming LDAP PDU size for anonymous sessions.
......@@ -477,9 +538,15 @@ Specify the maximum size of the primary thread pool.
The default is 32.
.TP
.B timelimit <integer>
.TP
.B sizelimit size[.{soft|hard}]=<integer>
Specify the maximum number of seconds (in real time)
.B slapd
will spend answering a search request. The default time limit is 3600.
The second format allows a fine grain setting of the time limits.
See
.BR limits
for an explanation of the different flags.
.SH TLS OPTIONS
If
.B slapd
......
......@@ -53,7 +53,7 @@ ldbm_back_search(
int nentries = 0;
int manageDSAit = get_manageDSAit( op );
int timelimit = -1, sizelimit = -1;
struct slap_limits_set *limit = NULL;
int isroot = 0;
#ifdef NEW_LOGGING
......@@ -184,28 +184,74 @@ searchit:
goto done;
}
/* if not root, get appropriate limits */
if ( be_isroot( be, op->o_ndn ) ) {
isroot = 1;
} else {
if ( get_limits( be, op->o_ndn, &timelimit, &sizelimit) ) {
timelimit = be->be_timelimit;
sizelimit = be->be_sizelimit;
( void ) get_limits( be, op->o_ndn, &limit );
}
/* if candidates exceed to-be-checked entries, abort */
if ( !isroot && limit->lms_s_unchecked != -1 ) {
if ( ID_BLOCK_NIDS( candidates ) > limit->lms_s_unchecked ) {
send_search_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
NULL, NULL, NULL, NULL, 0 );
rc = 0;
goto done;
}
}
/* if no time limit requested, use soft limit (unless root!) */
if ( tlimit <= 0 ) {
if ( isroot ) {
tlimit = -1; /* allow root to set no limit */
} else {
tlimit = limit->lms_t_soft;
}
/* if requested limit higher than hard limit, abort */
} else if ( tlimit > limit->lms_t_hard ) {
/* no hard limit means use soft instead */
if ( limit->lms_t_hard == 0 ) {
tlimit = limit->lms_t_soft;
/* positive hard limit means abort */
} else if ( limit->lms_t_hard > 0 ) {
send_search_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
NULL, NULL, NULL, NULL, 0 );
rc = 0;
goto done;
}
if ( tlimit == 0 && isroot ) {
tlimit = -1; /* allow root to set no limit */
} else {
tlimit = (tlimit > timelimit || tlimit < 1) ?
timelimit : tlimit;
stoptime = op->o_time + tlimit;
/* negative hard limit means no limit */
}
if ( slimit == 0 && isroot ) {
slimit = -1; /* allow root to set no limit */
} else {
slimit = (slimit > sizelimit || slimit < 1) ?
sizelimit : slimit;
/* compute it anyway; root does not use it */
stoptime = op->o_time + tlimit;
/* if no size limit requested, use soft limit (unless root!) */
if ( slimit == 0 ) {
if ( isroot ) {
slimit = -1; /* allow root to set no limit */
} else {
slimit = limit->lms_s_soft;
}
/* if requested limit higher than hard limit, abort */
} else if ( slimit > limit->lms_s_hard ) {
/* no hard limit means use soft instead */
if ( limit->lms_s_hard == 0 ) {
slimit = limit->lms_s_soft;
/* positive hard limit means abort */
} else if ( limit->lms_s_hard > 0 ) {
send_search_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
NULL, NULL, NULL, NULL, 0 );
rc = 0;
goto done;
}
/* negative hard limit means no limit */
}
for ( id = idl_firstid( candidates, &cursor ); id != NOID;
......
......@@ -469,8 +469,7 @@ backend_db_init(
be = &backends[nbackends++];
be->bd_info = bi;
be->be_sizelimit = defsize;
be->be_timelimit = deftime;
be->be_def_limit = deflimit;
be->be_dfltaccess = global_default_access;
be->be_restrictops = global_restrictops;
......
......@@ -22,8 +22,15 @@
/*
* defaults for various global variables
*/
int defsize = SLAPD_DEFAULT_SIZELIMIT;
int deftime = SLAPD_DEFAULT_TIMELIMIT;
struct slap_limits_set deflimit = {
SLAPD_DEFAULT_TIMELIMIT, /* backward compatible limits */
0,
SLAPD_DEFAULT_SIZELIMIT, /* backward compatible limits */
0,
-1 /* no limit on unchecked size */
};
AccessControl *global_acl = NULL;
slap_access_t global_default_access = ACL_READ;
slap_mask_t global_restrictops = 0;
......@@ -685,8 +692,11 @@ read_config( const char *fname )
return( 1 );
}
/* set time limit */
/* set size limit */
} else if ( strcasecmp( cargv[0], "sizelimit" ) == 0 ) {
int rc = 0;
struct slap_limits_set *lim;
if ( cargc < 2 ) {
#ifdef NEW_LOGGING
LDAP_LOG(( "config", LDAP_LEVEL_CRIT,
......@@ -700,14 +710,39 @@ read_config( const char *fname )
return( 1 );
}
if ( be == NULL ) {
defsize = atoi( cargv[1] );
lim = &deflimit;
} else {
be->be_sizelimit = atoi( cargv[1] );
lim = &be->be_def_limit;
}
if ( strncasecmp( cargv[1], "size", 4 ) == 0 ) {
rc = parse_limit( cargv[1], lim );
} else {
lim->lms_s_soft = atoi( cargv[1] );
lim->lms_s_hard = 0;
}
if ( rc ) {
#ifdef NEW_LOGGING
LDAP_LOG(( "config", LDAP_LEVEL_CRIT,
"%s: line %d: unable to parse value"
" \"%s\" in \"sizelimit <limit>\""
" line.\n",
fname, lineno, cargv[1] ));
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: unable to parse value \"%s\" in \"sizelimit <limit>\" line\n",
fname, lineno, cargv[1] );
#endif
}
/* set time limit */
} else if ( strcasecmp( cargv[0], "timelimit" ) == 0 ) {
int rc = 0;
struct slap_limits_set *lim;
if ( cargc < 2 ) {
#ifdef NEW_LOGGING
LDAP_LOG(( "config", LDAP_LEVEL_CRIT,
......@@ -721,10 +756,32 @@ read_config( const char *fname )
return( 1 );
}
if ( be == NULL ) {
deftime = atoi( cargv[1] );
lim = &deflimit;
} else {
lim = &be->be_def_limit;
}
if ( strncasecmp( cargv[1], "time", 4 ) == 0 ) {
rc = parse_limit( cargv[1], lim );
} else {
be->be_timelimit = atoi( cargv[1] );
lim->lms_t_soft = atoi( cargv[1] );
lim->lms_t_hard = 0;
}
if ( rc ) {
#ifdef NEW_LOGGING
LDAP_LOG(( "config", LDAP_LEVEL_CRIT,
"%s: line %d: unable to parse value"
" \"%s\" in \"timelimit <limit>\""
" line.\n",
fname, lineno, cargv[1] ));
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: unable to parse value \"%s\" in \"timelimit <limit>\" line\n",
fname, lineno, cargv[1] );
#endif
}
/* set regex-based limits */
......
......@@ -15,23 +15,20 @@
int
get_limits(
Backend *be,
const char *ndn,
int *timelimit,
int *sizelimit
Backend *be,
const char *ndn,
struct slap_limits_set **limit
)
{
struct slap_limits **lm;
assert( be );
assert( timelimit );
assert( sizelimit );
assert( limit );
/*
* default values
*/
*timelimit = be->be_timelimit;
*sizelimit = be->be_sizelimit;
*limit = &be->be_def_limit;
/*
* anonymous or no regex-based limits?
......@@ -44,16 +41,14 @@ get_limits(
switch ( lm[0]->lm_type) {
case SLAP_LIMITS_EXACT:
if ( strcmp( lm[0]->lm_dn_pat, ndn ) == 0 ) {
*timelimit = lm[0]->lm_timelimit;
*sizelimit = lm[0]->lm_sizelimit;
*limit = &lm[0]->lm_limits;
return( 0 );
}
break;
case SLAP_LIMITS_REGEX:
if ( regexec( &lm[0]->lm_dn_regex, ndn, 0, NULL, 0) == 0 ) {
*timelimit = lm[0]->lm_timelimit;
*sizelimit = lm[0]->lm_sizelimit;
*limit = &lm[0]->lm_limits;
return( 0 );
}
break;
......@@ -69,18 +64,18 @@ get_limits(
int
add_limits(
Backend *be,
int type,
const char *pattern,
int timelimit,
int sizelimit
Backend *be,
int type,
const char *pattern,
struct slap_limits_set *limit
)
{
int i;
struct slap_limits *lm;
assert( be );
assert( pattern);
assert( pattern );
assert( limit );
lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
......@@ -107,8 +102,7 @@ add_limits(
break;
}
lm->lm_timelimit = timelimit;
lm->lm_sizelimit = sizelimit;
lm->lm_limits = *limit;
i = 0;
if ( be->be_limits != NULL ) {
......@@ -134,8 +128,7 @@ parse_limits(
{
int type = SLAP_LIMITS_UNDEFINED;
char *pattern;
int timelimit;
int sizelimit;
struct slap_limits_set limit;
int i;
assert( be );
......@@ -155,13 +148,12 @@ parse_limits(
return( -1 );
}
timelimit = be->be_timelimit;
sizelimit = be->be_sizelimit;
limit = be->be_def_limit;
/*
* syntax:
*
* "limits" <pattern> <limit> [ <limit> [ ... ] ]
* "limits" <pattern> <limit> [ ... ]
*
*
* <pattern>:
......@@ -171,7 +163,9 @@ parse_limits(
*
* <limit>:
*
* { "time" | "size" } "=" <value>
* "time" [ "." { "soft" | "hard" } ] "=" <integer>
*
* "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
*/
pattern = argv[1];
......@@ -210,27 +204,122 @@ parse_limits(
}
for ( i = 2; i < argc; i++ ) {
if ( strncasecmp( argv[i], "time=", 5) == 0 ) {
timelimit = atoi( argv[i]+5 );
} else if ( strncasecmp( argv[i], "size=", 5) == 0 ) {
sizelimit = atoi( argv[i]+5 );
} else {
if ( parse_limit( argv[i], &limit ) ) {
#ifdef NEW_LOGGING
LDAP_LOG(( "config", LDAP_LEVEL_CRIT,
"%s : line %d: unknown limit type \"%s\" in "
"\"limits <pattern> <limits>\" line "
"(ignored).\n",
"\"limits <pattern> <limits>\" line.\n",
fname, lineno, argv[i] ));
#else
Debug( LDAP_DEBUG_ANY,
"%s : line %d: unknown limit type \"%s\" in "
"\"limits <pattern> <limits>\" line "
"(ignored).\n",
"\"limits <pattern> <limits>\" line.\n",
fname, lineno, argv[i] );
#endif
return( 1 );
}
}
/*
* sanity checks ...
*/
if ( limit.lms_t_hard > 0 && limit.lms_t_hard < limit.lms_t_soft ) {
limit.lms_t_hard = limit.lms_t_soft;
}
if ( limit.lms_s_hard > 0 && limit.lms_s_hard < limit.lms_s_soft ) {
limit.lms_s_hard = limit.lms_s_soft;
}
return( add_limits( be, type, pattern, timelimit, sizelimit ) );
return( add_limits( be, type, pattern, &limit ) );
}
int
parse_limit(
const char *arg,
struct slap_limits_set *limit
)
{
assert( arg );
assert( limit );
if ( strncasecmp( arg, "time", 4 ) == 0 ) {
arg += 4;
if ( arg[0] == '.' ) {
arg++;
if ( strncasecmp( arg, "soft", 4 ) == 0 ) {
arg += 4;
if ( arg[0] != '=' ) {
return( 1 );
}
arg++;
limit->lms_t_soft = atoi( arg );
} else if ( strncasecmp( arg, "hard", 4 ) == 0 ) {
arg += 4;
if ( arg[0] != '=' ) {
return( 1 );
}
arg++;
limit->lms_t_hard = atoi( arg );
} else {
return( 1 );
}
} else if ( arg[0] == '=' ) {
limit->lms_t_soft = atoi( arg );
limit->lms_t_hard = 0;
} else {
return( 1 );
}
} else if ( strncasecmp( arg, "size", 4 ) == 0 ) {
arg += 4;
if ( arg[0] == '.' ) {
arg++;
if ( strncasecmp( arg, "soft", 4 ) == 0 ) {
arg += 4;
if ( arg[0] != '=' ) {
return( 1 );
}
arg++;
limit->lms_s_soft = atoi( arg );
} else if ( strncasecmp( arg, "hard", 4 ) == 0 ) {
arg += 4;
if ( arg[0] != '=' ) {
return( 1 );
}
arg++;
limit->lms_s_hard = atoi( arg );
} else if ( strncasecmp( arg, "unchecked", 9 ) == 0 ) {
arg += 9;
if ( arg[0] != '=' ) {
return( 1 );
}
arg++;
limit->lms_s_unchecked = atoi( arg );
} else {
return( 1 );
}
} else if ( arg[0] == '=' ) {
limit->lms_s_soft = atoi( arg );
limit->lms_s_hard = 0;
} else {
return( 1 );
}
}
return 0;
}
......@@ -400,12 +400,14 @@ LDAP_SLAPD_F (int) test_filter LDAP_P((
* limits.c
*/
LDAP_SLAPD_F (int) get_limits LDAP_P((
Backend *be, const char *ndn, int *timelimit, int *sizelimit ));
Backend *be, const char *ndn, struct slap_limits_set **limit ));
LDAP_SLAPD_F (int) add_limits LDAP_P((
Backend *be, int type, const char *pattern,
int timelimit, int sizelimit ));
struct slap_limits_set *limit ));
LDAP_SLAPD_F (int) parse_limits LDAP_P((
Backend *be, const char *fname, int lineno, int argc, char **argv ));
LDAP_SLAPD_F (int) parse_limit LDAP_P(( const char *arg,
struct slap_limits_set *limit ));
/*
* lock.c
......@@ -830,8 +832,7 @@ LDAP_SLAPD_F (slap_ssf_set_t) global_ssf_set;
LDAP_SLAPD_F (struct berval **) default_referral;
LDAP_SLAPD_F (char *) replogfile;
LDAP_SLAPD_F (const char) Versionstr[];
LDAP_SLAPD_F (int) defsize;
LDAP_SLAPD_F (int) deftime;
LDAP_SLAPD_F (struct slap_limits_set) deflimit;
LDAP_SLAPD_F (int) g_argc;
LDAP_SLAPD_F (slap_access_t) global_default_access;
LDAP_SLAPD_F (int) global_lastmod;
......
......@@ -814,6 +814,17 @@ struct slap_replica_info {
char **ri_nsuffix; /* array of suffixes this replica accepts */
};
struct slap_limits_set {
/* time limits */
int lms_t_soft;
int lms_t_hard;
/* size limits */
int lms_s_soft;
int lms_s_hard;
int lms_s_unchecked;
};