From 8471ef7ed02632d2352189eddfaf806886b2de24 Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Wed, 1 Aug 2001 10:09:04 +0000 Subject: [PATCH] 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 --- doc/man/man5/slapd.conf.5 | 83 ++++++++++++++-- servers/slapd/back-ldbm/search.c | 76 ++++++++++++--- servers/slapd/backend.c | 3 +- servers/slapd/config.c | 71 ++++++++++++-- servers/slapd/limits.c | 161 ++++++++++++++++++++++++------- servers/slapd/proto-slap.h | 9 +- servers/slapd/slap.h | 19 +++- servers/slapd/tools/mimic.c | 7 +- 8 files changed, 352 insertions(+), 77 deletions(-) diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5 index e6797abc35..6a3841f9c6 100644 --- a/doc/man/man5/slapd.conf.5 +++ b/doc/man/man5/slapd.conf.5 @@ -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}]=] [ [...]] +.B limits [dn[.{exact|regex}]=] [...] 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}]= , +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}]= , +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 = -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 @@ -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 +.B sizelimit +.TP +.B sizelimit size[.{soft|hard|unchecked}]= 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 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 +.TP +.B sizelimit size[.{soft|hard}]= 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 diff --git a/servers/slapd/back-ldbm/search.c b/servers/slapd/back-ldbm/search.c index 6af3cf97c9..b4a9a29fb3 100644 --- a/servers/slapd/back-ldbm/search.c +++ b/servers/slapd/back-ldbm/search.c @@ -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; diff --git a/servers/slapd/backend.c b/servers/slapd/backend.c index fa749b85e9..105e95b75c 100644 --- a/servers/slapd/backend.c +++ b/servers/slapd/backend.c @@ -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; diff --git a/servers/slapd/config.c b/servers/slapd/config.c index ed3d49646f..3547d7548d 100644 --- a/servers/slapd/config.c +++ b/servers/slapd/config.c @@ -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 \"" + " line.\n", + fname, lineno, cargv[1] )); +#else + Debug( LDAP_DEBUG_ANY, + "%s: line %d: unable to parse value \"%s\" in \"sizelimit \" 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 \"" + " line.\n", + fname, lineno, cargv[1] )); +#else + Debug( LDAP_DEBUG_ANY, + "%s: line %d: unable to parse value \"%s\" in \"timelimit \" line\n", + fname, lineno, cargv[1] ); +#endif } /* set regex-based limits */ diff --git a/servers/slapd/limits.c b/servers/slapd/limits.c index ef6ccc9da6..5e27a2d832 100644 --- a/servers/slapd/limits.c +++ b/servers/slapd/limits.c @@ -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" [ [ ... ] ] + * "limits" [ ... ] * * * : @@ -171,7 +163,9 @@ parse_limits( * * : * - * { "time" | "size" } "=" + * "time" [ "." { "soft" | "hard" } ] "=" + * + * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" */ 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 \" line " - "(ignored).\n", + "\"limits \" line.\n", fname, lineno, argv[i] )); #else Debug( LDAP_DEBUG_ANY, "%s : line %d: unknown limit type \"%s\" in " - "\"limits \" line " - "(ignored).\n", + "\"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; } diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index 88af06bf2c..e8bf36c2f8 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -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; diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index 0dcecd3fa4..aaefe20868 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -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; +}; + struct slap_limits { int lm_type; /* type of pattern */ #define SLAP_LIMITS_UNDEFINED 0x0000 @@ -821,8 +832,7 @@ struct slap_limits { #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 */ @@ -920,8 +930,9 @@ struct slap_backend_db { 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 */ diff --git a/servers/slapd/tools/mimic.c b/servers/slapd/tools/mimic.c index f8ce0e07ab..fb3a1f24c3 100644 --- a/servers/slapd/tools/mimic.c +++ b/servers/slapd/tools/mimic.c @@ -209,7 +209,12 @@ int parse_limits( Backend *be, const char *fname, int lineno, int argc, char **a 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; } -- 2.39.5