Blob Blame Raw
From 8d6121731175f3a9a1fa1fbe3752763b0b48a67d Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 31 Oct 2016 15:06:36 -0400
Subject: [PATCH] Add OCSP cache and timeout tuning knobs

NSS provides functions to control the timeout for connecting to
an OCSP server and for caching the results. This includes the
number of responses to cache and the duration to cache them.

Based on a patch by Jack Magne
---
 docs/mod_nss.html   | 42 ++++++++++++++++++++++++++
 mod_nss.c           | 12 ++++++++
 mod_nss.h           |  8 +++++
 nss_engine_config.c | 86 +++++++++++++++++++++++++++++++++++++++++++----------
 nss_engine_init.c   | 47 +++++++++++++++++++++++++++++
 5 files changed, 179 insertions(+), 16 deletions(-)

diff --git a/docs/mod_nss.html b/docs/mod_nss.html
index 65d0bd8..655d2f2 100644
--- a/docs/mod_nss.html
+++ b/docs/mod_nss.html
@@ -544,6 +544,48 @@ Example</span><br>
 <br>
 <code>NSSOCSP on</code><br>
 <br>
+<big><big>NSSOCSPTimeout</big></big><br>
+<br>
+Configure the maximum time to wait for an OCSP response in seconds.
+There are no constraints or special meanings for this value. The default
+is 60 seconds.
+<br>
+<br><span style="font-weight: bold;">
+Example</span><br>
+<br>
+<code>NSSOCSPTimeout 30</code><br>
+<br>
+<big><big>NSSOCSPCacheSize</big></big><br>
+<br>
+Configures the maximum number of entries in the OCSP cache. A value of
+-1 will disable the cache completely. A value of 0 configures an unlimited
+number of cache entries. The default is 1000.
+<br>
+<br><span style="font-weight: bold;">
+Example</span><br>
+<br>
+<code>NSSOCSPCacheSize 300</code><br>
+<br>
+<big><big>NSSOCSPMinCacheEntryDuration</big></big><br>
+<br>
+Configure the minimum amount of time an OCSP response is cached in seconds.
+The default is 3600 seconds (1 hour).
+<br>
+<br><span style="font-weight: bold;">
+Example</span><br>
+<br>
+<code>NSSOCSPMinCacheEntryDuration 30</code><br>
+<br>
+<big><big>NSSOCSPMaxCacheEntryDuration</big></big><br>
+<br>
+Configure the maximum amount of time an OCSP response is cached in seconds
+before being updated. The default is 86400 seconds (24 hours).
+<br>
+<br><span style="font-weight: bold;">
+Example</span><br>
+<br>
+<code>NSSOCSPMaxCacheEntryDuration 300</code><br>
+<br>
 <big><big>NSSCipherSuite<br>
 </big></big><br>
 There are two options for configuring the available ciphers. mod_nss
diff --git a/mod_nss.c b/mod_nss.c
index 38098c8..dca5a73 100644
--- a/mod_nss.c
+++ b/mod_nss.c
@@ -66,6 +66,18 @@ static const command_rec nss_config_cmds[] = {
     SSL_CMD_SRV(OCSP, FLAG,
                 "OCSP (Online Certificate Status Protocol)"
                 "(`on', `off')")
+    SSL_CMD_SRV(OCSPTimeout, TAKE1,
+                "OCSP Timeout"
+                "(`N' - Max number of seconds to wait for an OCSP response.)")
+    SSL_CMD_SRV(OCSPCacheSize, TAKE1,
+                "OCSP Cache size"
+                "(`N' - number of entries -1 for no cache)")
+    SSL_CMD_SRV(OCSPMinCacheEntryDuration, TAKE1,
+                "OCSP Minimum time until next fetch attempt"
+                "(`N' - Time in seconds)")
+    SSL_CMD_SRV(OCSPMaxCacheEntryDuration, TAKE1,
+                "OCSP Maximum time until next fetch attempt"
+                "(`N' - Time in seconds)")
     SSL_CMD_SRV(OCSPDefaultResponder, FLAG,
                 "Use a default OCSP Responder"
                 "(`on', `off')")
diff --git a/mod_nss.h b/mod_nss.h
index 226f7a8..8643e88 100644
--- a/mod_nss.h
+++ b/mod_nss.h
@@ -325,6 +325,10 @@ struct SSLSrvConfigRec {
     const char      *ocsp_url;
     const char      *ocsp_name;
     BOOL             ocsp;
+    int              ocsp_timeout;
+    int              ocsp_cache_size;
+    int              ocsp_min_cache_entry_duration;
+    int              ocsp_max_cache_entry_duration;
     BOOL             enabled;
     BOOL             sni;
     BOOL             strict_sni_vhost_check;
@@ -398,6 +402,10 @@ const char *nss_cmd_NSSSNI(cmd_parms *, void *, int);
 const char *nss_cmd_NSSStrictSNIVHostCheck(cmd_parms *, void *, int);
 const char *nss_cmd_NSSEngine(cmd_parms *, void *, int);
 const char *nss_cmd_NSSOCSP(cmd_parms *, void *, int);
+const char *nss_cmd_NSSOCSPTimeout(cmd_parms *, void *, const char *arg);
+const char *nss_cmd_NSSOCSPCacheSize(cmd_parms *, void *, const char *arg);
+const char *nss_cmd_NSSOCSPMinCacheEntryDuration(cmd_parms *, void *, const char *arg);
+const char *nss_cmd_NSSOCSPMaxCacheEntryDuration(cmd_parms *, void *, const char *arg);
 const char *nss_cmd_NSSOCSPDefaultResponder(cmd_parms *, void *, int);
 const char *nss_cmd_NSSOCSPDefaultURL(cmd_parms *, void *dcfg, const char *arg);
 const char *nss_cmd_NSSOCSPDefaultName(cmd_parms *, void *, const char *arg);
diff --git a/nss_engine_config.c b/nss_engine_config.c
index e1fbe41..597d56d 100644
--- a/nss_engine_config.c
+++ b/nss_engine_config.c
@@ -129,22 +129,26 @@ static SSLSrvConfigRec *nss_config_server_new(apr_pool_t *p)
 {
     SSLSrvConfigRec *sc = apr_palloc(p, sizeof(*sc));
 
-    sc->mc                          = NULL;
-    sc->ocsp                        = UNSET;
-    sc->ocsp_default                = UNSET;
-    sc->ocsp_url                    = NULL;
-    sc->ocsp_name                   = NULL;
-    sc->fips                        = UNSET;
-    sc->enabled                     = UNSET;
-    sc->sni                         = TRUE;
-    sc->strict_sni_vhost_check      = TRUE;
-    sc->proxy_enabled               = UNSET;
-    sc->vhost_id                    = NULL;  /* set during module init */
-    sc->vhost_id_len                = 0;     /* set during module init */
-    sc->proxy                       = NULL;
-    sc->server                      = NULL;
-    sc->proxy_ssl_check_peer_cn     = TRUE;
-    sc->session_tickets             = FALSE;
+    sc->mc                            = NULL;
+    sc->ocsp                          = UNSET;
+    sc->ocsp_timeout                  = 60;
+    sc->ocsp_cache_size               = 1000;
+    sc->ocsp_min_cache_entry_duration = 1*60*60L;
+    sc->ocsp_max_cache_entry_duration = 24*60*60L;
+    sc->ocsp_default                  = UNSET;
+    sc->ocsp_url                      = NULL;
+    sc->ocsp_name                     = NULL;
+    sc->fips                          = UNSET;
+    sc->enabled                       = UNSET;
+    sc->sni                           = TRUE;
+    sc->strict_sni_vhost_check        = TRUE;
+    sc->proxy_enabled                 = UNSET;
+    sc->vhost_id                      = NULL;  /* set during module init */
+    sc->vhost_id_len                  = 0;     /* set during module init */
+    sc->proxy                         = NULL;
+    sc->server                        = NULL;
+    sc->proxy_ssl_check_peer_cn       = TRUE;
+    sc->session_tickets               = FALSE;
 
     modnss_ctx_init_proxy(sc, p);
 
@@ -213,6 +217,10 @@ void *nss_config_server_merge(apr_pool_t *p, void *basev, void *addv) {
 
     cfgMerge(mc, NULL);
     cfgMergeBool(ocsp);
+    cfgMergeInt(ocsp_timeout);
+    cfgMergeInt(ocsp_cache_size);
+    cfgMergeInt(ocsp_min_cache_entry_duration);
+    cfgMergeInt(ocsp_max_cache_entry_duration);
     cfgMergeBool(ocsp_default);
     cfgMerge(ocsp_url, NULL);
     cfgMerge(ocsp_name, NULL);
@@ -376,6 +384,52 @@ const char *nss_cmd_NSSOCSP(cmd_parms *cmd, void *dcfg, int flag)
     return NULL;
 }
 
+const char *nss_cmd_NSSOCSPTimeout(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->ocsp_timeout = atoi(arg);
+
+    return NULL;
+}
+
+const char *nss_cmd_NSSOCSPCacheSize(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->ocsp_cache_size = atoi(arg);
+
+    if (sc->ocsp_cache_size < -1) {
+        return "NSSOCSPCacheSize: must be >= -1";
+    }
+
+    return NULL;
+}
+
+const char *nss_cmd_NSSOCSPMinCacheEntryDuration(cmd_parms *cmd, void *dcfg,
+                                                 const char *arg)
+{
+
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->ocsp_min_cache_entry_duration = atoi(arg);
+
+    return NULL;
+}
+
+const char *nss_cmd_NSSOCSPMaxCacheEntryDuration(cmd_parms *cmd, void *dcfg,
+                                                 const char *arg)
+{
+
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->ocsp_max_cache_entry_duration = atoi(arg);
+
+    return NULL;
+}
+
 const char *nss_cmd_NSSOCSPDefaultResponder(cmd_parms *cmd, void *dcfg, int flag)
 {
     SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
diff --git a/nss_engine_init.c b/nss_engine_init.c
index 14f86d8..2571591 100644
--- a/nss_engine_init.c
+++ b/nss_engine_init.c
@@ -174,6 +174,18 @@ static void nss_init_SSLLibrary(server_rec *base_server, apr_pool_t *p)
         }
     }
 
+    if (ocspenabled) {
+        if (sc->ocsp_min_cache_entry_duration > sc->ocsp_max_cache_entry_duration)  {
+            ap_log_error(APLOG_MARK,APLOG_ERR, 0, base_server,
+                "OCSP minimum cache duration must be less than the maximum.");
+
+            if (mc->nInitCount == 1)
+                nss_die();
+            else
+                return;
+        }
+    }
+
     if (strncasecmp(mc->pCertificateDatabase, "sql:", 4) == 0)
         dbdir = (char *)mc->pCertificateDatabase + 4;
     else
@@ -343,10 +355,45 @@ static void nss_init_SSLLibrary(server_rec *base_server, apr_pool_t *p)
     }
 
     if (ocspenabled) {
+        SECStatus rv;
+
         CERT_EnableOCSPChecking(CERT_GetDefaultCertDB());
         ap_log_error(APLOG_MARK, APLOG_INFO, 0, base_server,
             "OCSP is enabled.");
 
+        /* Set desired OCSP Cache Settings, values already checked. */
+        rv = CERT_OCSPCacheSettings((PRInt32)sc->ocsp_cache_size,
+                                    (PRUint32)sc->ocsp_min_cache_entry_duration,
+                                    (PRUint32)sc->ocsp_max_cache_entry_duration);
+
+        if (rv == SECFailure) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server,
+                        "Unable to set the OCSP cache settings.");
+            nss_log_nss_error(APLOG_MARK, APLOG_ERR, base_server);
+            if (mc->nInitCount == 1)
+                nss_die();
+            else
+                return;
+        } else {
+            ap_log_error(APLOG_MARK, APLOG_INFO, 0, base_server,
+                         "OCSP cache size %d, duration %d - %d seconds.", sc->ocsp_cache_size, sc->ocsp_min_cache_entry_duration, sc->ocsp_max_cache_entry_duration);
+        }
+
+        /* Set OCSP timeout. */
+        rv = CERT_SetOCSPTimeout((PRUint32) sc->ocsp_timeout);
+        if (rv == SECFailure) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server,
+                         "Unable to set the OCSP timeout. (this shouldn't happen.");
+            nss_log_nss_error(APLOG_MARK, APLOG_ERR, base_server);
+            if (mc->nInitCount == 1)
+                nss_die();
+            else
+                return;
+        } else {
+            ap_log_error(APLOG_MARK, APLOG_INFO, 0, base_server,
+                         "OCSP timeout set to %d.", sc->ocsp_timeout);
+        }
+
         /* We ensure that ocspname and ocspurl are not NULL above. */
         if (ocspdefault) {
             SECStatus sv;
-- 
2.9.3