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
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>
.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.
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
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
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;
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;
/*
* 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;
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,
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,
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 */
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?
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;
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 );
break;
}
- lm->lm_timelimit = timelimit;
- lm->lm_sizelimit = sizelimit;
+ lm->lm_limits = *limit;
i = 0;
if ( be->be_limits != NULL ) {
{
int type = SLAP_LIMITS_UNDEFINED;
char *pattern;
- int timelimit;
- int sizelimit;
+ struct slap_limits_set limit;
int i;
assert( be );
return( -1 );
}
- timelimit = be->be_timelimit;
- sizelimit = be->be_sizelimit;
+ limit = be->be_def_limit;
/*
* syntax:
*
- * "limits" <pattern> <limit> [ <limit> [ ... ] ]
+ * "limits" <pattern> <limit> [ ... ]
*
*
* <pattern>:
*
* <limit>:
*
- * { "time" | "size" } "=" <value>
+ * "time" [ "." { "soft" | "hard" } ] "=" <integer>
+ *
+ * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
*/
pattern = argv[1];
}
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;
}
* 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
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;
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;
+};
+
struct slap_limits {
int lm_type; /* type of pattern */
#define SLAP_LIMITS_UNDEFINED 0x0000
#define SLAP_LIMITS_REGEX 0x0002
regex_t lm_dn_regex; /* regex-based size and time limits */
char *lm_dn_pat; /* ndn for EXACT; pattern for REGEX */
- int lm_timelimit;
- int lm_sizelimit;
+ struct slap_limits_set lm_limits;
};
/* temporary aliases */
char *be_root_ndn; /* the magic "root" normalized dn for this db */
struct berval be_root_pw; /* the magic "root" password for this db */
unsigned int be_max_deref_depth; /* limit for depth of an alias deref */
- int be_sizelimit; /* size limit for this backend */
- int be_timelimit; /* time limit for this backend */
+#define be_sizelimit be_def_limit.lms_s_soft
+#define be_timelimit be_def_limit.lms_t_soft
+ struct slap_limits_set be_def_limit; /* default limits */
struct slap_limits **be_limits; /* regex-based size and time limits */
AccessControl *be_acl; /* access control list for this backend */
slap_access_t be_dfltaccess; /* access given if no acl matches */
return 0;
}
-int get_limits( Backend *be, const char *ndn, int *timelimit, int *sizelimit )
+int parse_limit( const char *arg, struct slap_limits_set *limit )
+{
+ return 0;
+}
+
+int get_limits( Backend *be, const char *ndn, struct slap_limits_set **limit )
{
return 0;
}