From 505ab978e2002c38f30969fe56fdd724fb6b6a7e Mon Sep 17 00:00:00 2001
From: Noriko Hosoi <nhosoi@redhat.com>
Date: Mon, 1 Jun 2015 08:44:02 -0700
Subject: [PATCH] Ticket #48191 - RFE: Adding nsslapd-maxsimplepaged-per-conn
 Adding nsslapd-maxsimplepaged-per-conn

Description: Asynchronous simple paged results requests could add too much
load the server can handle. Adding a config parameter to restrict the requests.

cn=config
nsslapd-maxsimplepaged-per-conn: INT

If nsslapd-maxsimplepaged-per-conn is configured with a positive integer,
Asynchronous simple paged results requests per connection is limitted by
the value.  If the requests exceed the value, it returns LDAP_ADMINLIMIT_
EXCEEDED.

If the value is -1, there is no limit (default behaviour).
If the value is 0, a simple paged results is disabled.
---
 ldap/servers/slapd/libglobs.c     | 52 ++++++++++++++++++++++++++++++++++++++-
 ldap/servers/slapd/opshared.c     |  8 ++++--
 ldap/servers/slapd/pagedresults.c | 15 +++++++++++
 ldap/servers/slapd/proto-slap.h   |  4 +++
 ldap/servers/slapd/slap.h         |  5 ++++
 5 files changed, 81 insertions(+), 3 deletions(-)

diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c
index 5aee1c4..3b53f32 100644
--- a/ldap/servers/slapd/libglobs.c
+++ b/ldap/servers/slapd/libglobs.c
@@ -186,6 +186,8 @@ static int invalid_sasl_mech(char *str);
 #define DEFAULT_MAXBERSIZE 2097152
 #define DEFAULT_SASL_MAXBUFSIZE "2097152"
 #define SLAPD_DEFAULT_SASL_MAXBUFSIZE 2097152
+#define DEFAULT_MAXSIMPLEPAGED_PER_CONN (-1)
+#define DEFAULT_MAXSIMPLEPAGED_PER_CONN_STR "-1"
 #ifdef MEMPOOL_EXPERIMENTAL
 #define DEFAULT_MEMPOOL_MAXFREELIST "1024"
 #endif
@@ -1130,7 +1132,11 @@ static struct config_get_and_set {
 	{CONFIG_GLOBAL_BACKEND_LOCK, config_set_global_backend_lock,
 		NULL, 0,
 		(void**)&global_slapdFrontendConfig.global_backend_lock,
-		CONFIG_ON_OFF, (ConfigGetFunc)config_get_global_backend_lock, &init_global_backend_local}
+		CONFIG_ON_OFF, (ConfigGetFunc)config_get_global_backend_lock, &init_global_backend_local},
+	{CONFIG_MAXSIMPLEPAGED_PER_CONN_ATTRIBUTE, config_set_maxsimplepaged_per_conn,
+		NULL, 0,
+		(void**)&global_slapdFrontendConfig.maxsimplepaged_per_conn,
+		CONFIG_INT, config_get_maxsimplepaged_per_conn, DEFAULT_MAXSIMPLEPAGED_PER_CONN_STR},
 #ifdef ENABLE_NUNC_STANS
 	,{CONFIG_ENABLE_NUNC_STANS, config_set_enable_nunc_stans,
 		NULL, 0,
@@ -1585,6 +1591,7 @@ FrontendConfig_init () {
   init_dynamic_plugins = cfg->dynamic_plugins = LDAP_OFF;
   init_cn_uses_dn_syntax_in_dns = cfg->cn_uses_dn_syntax_in_dns = LDAP_OFF;
   init_global_backend_local = LDAP_OFF;
+  cfg->maxsimplepaged_per_conn = DEFAULT_MAXSIMPLEPAGED_PER_CONN;
 #ifdef ENABLE_NUNC_STANS
   init_enable_nunc_stans = cfg->enable_nunc_stans = LDAP_OFF;
 #endif
@@ -7864,6 +7871,49 @@ config_set_auditlog_enabled(int value){
     CFG_ONOFF_UNLOCK_WRITE(slapdFrontendConfig);
 }
 
+int
+config_set_maxsimplepaged_per_conn( const char *attrname, char *value, char *errorbuf, int apply )
+{
+  int retVal =  LDAP_SUCCESS;
+  slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+  long size;
+  char *endp;
+  
+  if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+    return LDAP_OPERATIONS_ERROR;
+  }
+  
+  errno = 0;
+  size = strtol(value, &endp, 10);
+  if ( *endp != '\0' || errno == ERANGE){
+    retVal = LDAP_OPERATIONS_ERROR;
+    PR_snprintf(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "(%s) value (%s) is invalid\n",
+                attrname, value);
+    return retVal;
+  }
+
+  if ( !apply ) {
+    return retVal;
+  }
+
+  CFG_LOCK_WRITE(slapdFrontendConfig);
+
+  slapdFrontendConfig->maxsimplepaged_per_conn = size;
+  
+  CFG_UNLOCK_WRITE(slapdFrontendConfig);
+  return retVal;
+}
+
+int
+config_get_maxsimplepaged_per_conn()
+{
+  slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+  int retVal;
+
+  retVal = slapdFrontendConfig->maxsimplepaged_per_conn;
+  return retVal; 
+}
+
 #if defined(LINUX)
 int
 config_set_malloc_mxfast(const char *attrname, char *value, char *errorbuf, int apply)
diff --git a/ldap/servers/slapd/opshared.c b/ldap/servers/slapd/opshared.c
index fa88053..b08c056 100644
--- a/ldap/servers/slapd/opshared.c
+++ b/ldap/servers/slapd/opshared.c
@@ -517,8 +517,7 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
           rc = pagedresults_parse_control_value(pb, ctl_value, &pagesize, &pr_idx, be);
           /* Let's set pr_idx even if it fails; in case, pr_idx == -1. */
           slapi_pblock_set(pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx);
-          if ((LDAP_SUCCESS == rc) ||
-              (LDAP_CANCELLED == rc) || (0 == pagesize)) {
+          if ((LDAP_SUCCESS == rc) || (LDAP_CANCELLED == rc) || (0 == pagesize)) {
               unsigned int opnote = SLAPI_OP_NOTE_SIMPLEPAGED;
               op_set_pagedresults(operation);
               pr_be = pagedresults_get_current_be(pb->pb_conn, pr_idx);
@@ -554,6 +553,11 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
                   rc = LDAP_SUCCESS;
                   goto free_and_return;
               }
+          } else if (LDAP_ADMINLIMIT_EXCEEDED == rc) {
+              send_ldap_result(pb, 0, NULL,
+                               "Simple Paged Results Search exceeded administration limit",
+                               0, NULL);
+              goto free_and_return;
           } else {
               /* parse paged-results-control failed */
               if (iscritical) { /* return an error since it's critical */
diff --git a/ldap/servers/slapd/pagedresults.c b/ldap/servers/slapd/pagedresults.c
index 327da54..31621a4 100644
--- a/ldap/servers/slapd/pagedresults.c
+++ b/ldap/servers/slapd/pagedresults.c
@@ -65,6 +65,7 @@ pagedresults_parse_control_value( Slapi_PBlock *pb,
     Connection *conn = pb->pb_conn;
     Operation *op = pb->pb_op;
     BerElement *ber = NULL;
+    int maxreqs = config_get_maxsimplepaged_per_conn();
 
     LDAPDebug0Args(LDAP_DEBUG_TRACE, "--> pagedresults_parse_control_value\n");
     if ( NULL == conn || NULL == op || NULL == pagesize || NULL == index ) {
@@ -94,6 +95,13 @@ pagedresults_parse_control_value( Slapi_PBlock *pb,
              "<-- pagedresults_parse_control_value: corrupted control value\n");
         return LDAP_PROTOCOL_ERROR;
     }
+    if ( !maxreqs )
+    {
+        LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                      "pagedresults_parse_control_value: simple paged results requests per conn exeeded the limit: %d\n",
+                      maxreqs);
+        return LDAP_ADMINLIMIT_EXCEEDED;
+    }
 
     PR_Lock(conn->c_mutex);
     /* the ber encoding is no longer needed */
@@ -125,6 +133,13 @@ pagedresults_parse_control_value( Slapi_PBlock *pb,
                 }
             }
         }
+        if ((maxreqs > 0) && (*index >= maxreqs)) {
+            rc = LDAP_ADMINLIMIT_EXCEEDED;
+            LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                          "pagedresults_parse_control_value: simple paged results requests per conn exeeded the limit: %d\n",
+                          maxreqs);
+            goto bail;
+        }
         if ((*index > -1) && (*index < conn->c_pagedresults.prl_maxlen) &&
             !conn->c_pagedresults.prl_list[*index].pr_mutex) {
             conn->c_pagedresults.prl_list[*index].pr_mutex = PR_NewLock();
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index ee32bd0..8fa52c3 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -428,6 +428,8 @@ int config_set_mempool_switch( const char *attrname, char *value, char *errorbuf
 int config_set_mempool_maxfreelist( const char *attrname, char *value, char *errorbuf, int apply );
 #endif /* MEMPOOL_EXPERIMENTAL */
 
+int config_set_maxsimplepaged_per_conn( const char *attrname, char *value, char *errorbuf, int apply );
+
 int config_get_SSLclientAuth();
 int config_get_ssl_check_hostname();
 char *config_get_SSL3ciphers();
@@ -613,6 +615,8 @@ int config_get_malloc_trim_threshold();
 int config_get_malloc_mmap_threshold();
 #endif
 
+int config_get_maxsimplepaged_per_conn();
+
 int is_abspath(const char *);
 char* rel2abspath( char * );
 char* rel2abspath_ext( char *, char * );
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
index b7c6e80..2556f65 100644
--- a/ldap/servers/slapd/slap.h
+++ b/ldap/servers/slapd/slap.h
@@ -2169,6 +2169,10 @@ typedef struct _slapdEntryPoints {
 
 #define CONFIG_CN_USES_DN_SYNTAX_IN_DNS "nsslapd-cn-uses-dn-syntax-in-dns"
 
+#define CONFIG_CN_USES_DN_SYNTAX_IN_DNS "nsslapd-cn-uses-dn-syntax-in-dns"
+
+#define CONFIG_MAXSIMPLEPAGED_PER_CONN_ATTRIBUTE "nsslapd-maxsimplepaged-per-conn"
+
 /* getenv alternative */
 #define CONFIG_MALLOC_MXFAST "nsslapd-malloc-mxfast"
 #define CONFIG_MALLOC_TRIM_THRESHOLD "nsslapd-malloc-trim-threshold"
@@ -2435,6 +2439,7 @@ typedef struct _slapdFrontendConfig {
   slapi_onoff_t dynamic_plugins; /* allow plugins to be dynamically enabled/disabled */
   slapi_onoff_t cn_uses_dn_syntax_in_dns; /* indicates the cn value in dns has dn syntax */
   slapi_onoff_t global_backend_lock;
+  slapi_int_t maxsimplepaged_per_conn;/* max simple paged results reqs handled per connection */
 #ifdef ENABLE_NUNC_STANS
   slapi_onoff_t enable_nunc_stans;
 #endif
-- 
1.9.3

