]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
mod_ssl: Use retained data API for storing private keys across reloads.
authorJoe Orton <jorton@apache.org>
Mon, 4 May 2020 08:32:23 +0000 (08:32 +0000)
committerJoe Orton <jorton@apache.org>
Mon, 4 May 2020 08:32:23 +0000 (08:32 +0000)
Allocate SSLModConfigRec from pconf rather than the process pool.

* modules/ssl/ssl_private.h: Add modssl_retained_data_t structure and
  move private key storage here from SSLModConfigRec.  Add retained
  pointer to SSLModConfigRec.

* modules/ssl/ssl_engine_config.c (ssl_config_global_create): Take
  pool argument; allocate SSLModConfigRec from there and
  initialize mc->retained.  SSLModConfigRec no longer cached for the
  process lifetime.
  (ssl_init_Module): Sanity check that sc->mc is correct.
  (ssl_init_server_certs): Use private keys from mc->retained.

* modules/ssl/ssl_engine_pphrase.c
  (privkey_vhost_keyid): Rename from asn1_table_vhost_key and
  update to use the retained structure.
  (ssl_load_encrypted_pkey): Update for above.

* modules/ssl/ssl_engine_init.c (ssl_init_Module): Remove
  (apparently) redundant call to ssl_config_global_create and
  add debug asserts to validate that is safe.

Github: closes #119

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1877345 13f79535-47bb-0310-9956-ffa450edef68

modules/ssl/ssl_engine_config.c
modules/ssl/ssl_engine_init.c
modules/ssl/ssl_engine_pphrase.c
modules/ssl/ssl_private.h

index 8c6c4e57e08ca3d1c4809c4d3d9a8691bcd8aa6c..b106e1467d2a840d62a0208b0d9a1f935cbe5c79 100644 (file)
 **  _________________________________________________________________
 */
 
-#define SSL_MOD_CONFIG_KEY "ssl_module"
 
-SSLModConfigRec *ssl_config_global_create(server_rec *s)
+static SSLModConfigRec *ssl_config_global_create(apr_pool_t *pool, server_rec *s)
 {
-    apr_pool_t *pool = s->process->pool;
     SSLModConfigRec *mc;
-    void *vmc;
 
-    apr_pool_userdata_get(&vmc, SSL_MOD_CONFIG_KEY, pool);
-    if (vmc) {
-        return vmc; /* reused for lifetime of the server */
+    if (ap_server_conf && s != ap_server_conf) {
+        SSLSrvConfigRec *sc = mySrvConfig(ap_server_conf);
+
+        AP_DEBUG_ASSERT(sc->mc);
+
+        return sc->mc;
     }
 
     /*
@@ -68,8 +68,6 @@ SSLModConfigRec *ssl_config_global_create(server_rec *s)
     mc->pMutex                 = NULL;
     mc->aRandSeed              = apr_array_make(pool, 4,
                                                 sizeof(ssl_randseed_t));
-    mc->tVHostKeys             = apr_hash_make(pool);
-    mc->tPrivateKey            = apr_hash_make(pool);
 #if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
     mc->szCryptoDevice         = NULL;
 #endif
@@ -86,9 +84,15 @@ SSLModConfigRec *ssl_config_global_create(server_rec *s)
     mc->fips = UNSET;
 #endif
 
-    apr_pool_userdata_set(mc, SSL_MOD_CONFIG_KEY,
-                          apr_pool_cleanup_null,
-                          pool);
+    mc->retained = ap_retained_data_get(MODSSL_RETAINED_KEY);
+    if (!mc->retained) {
+        /* Allocate the retained data; the hash table is allocated out
+         * of the process pool. */
+        mc->retained = ap_retained_data_create(MODSSL_RETAINED_KEY,
+                                               sizeof *mc->retained);
+        mc->retained->privkeys = apr_hash_make(s->process->pool);
+        mc->retained->key_ids = apr_hash_make(s->process->pool);
+    }
 
     return mc;
 }
@@ -248,7 +252,7 @@ void *ssl_config_server_create(apr_pool_t *p, server_rec *s)
 {
     SSLSrvConfigRec *sc = ssl_config_server_new(p);
 
-    sc->mc = ssl_config_global_create(s);
+    sc->mc = ssl_config_global_create(p, s);
 
     return sc;
 }
index 61c2dc36a74851fe8aaa8127ac3f861f1cd9118a..a59a055bab4f87b7b26b1f767c2068754594bf72 100644 (file)
@@ -226,6 +226,8 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
     apr_status_t rv;
     apr_array_header_t *pphrases;
 
+    AP_DEBUG_ASSERT(mc);
+
     if (SSLeay() < MODSSL_LIBRARY_VERSION) {
         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(01882)
                      "Init: this version of mod_ssl was compiled against "
@@ -250,7 +252,6 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
     /*
      * Any init round fixes the global config
      */
-    ssl_config_global_create(base_server); /* just to avoid problems */
     ssl_config_global_fix(mc);
 
     /*
@@ -260,6 +261,8 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
     for (s = base_server; s; s = s->next) {
         sc = mySrvConfig(s);
 
+        AP_DEBUG_ASSERT(sc->mc == mc);
+
         if (sc->server) {
             sc->server->sc = sc;
         }
@@ -1441,7 +1444,7 @@ static apr_status_t ssl_init_server_certs(server_rec *s,
             /* perhaps it's an encrypted private key, so try again */
             ssl_load_encrypted_pkey(s, ptemp, i, keyfile, &pphrases);
 
-            if (!(asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id)) ||
+            if (!(asn1 = ssl_asn1_table_get(mc->retained->privkeys, key_id)) ||
                 !(ptr = asn1->cpData) ||
                 !(pkey = d2i_AutoPrivateKey(NULL, &ptr, asn1->nData)) ||
                 (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1)) {
index 9f6f84f70e1945007431528ebc190e0e5b06f635..f1613913dd2acd5e3d82b51cc193e8f96cc5d542 100644 (file)
@@ -71,38 +71,25 @@ static apr_status_t exists_and_readable(const char *fname, apr_pool_t *pool,
     return APR_SUCCESS;
 }
 
-/*
- * reuse vhost keys for asn1 tables where keys are allocated out
- * of s->process->pool to prevent "leaking" each time we format
- * a vhost key.  since the key is stored in a table with lifetime
- * of s->process->pool, the key needs to have the same lifetime.
- *
- * XXX: probably seems silly to use a hash table with keys and values
- * being the same, but it is easier than doing a linear search
- * and will make it easier to remove keys if needed in the future.
- * also have the problem with apr_array_header_t that if we
- * underestimate the number of vhost keys when we apr_array_make(),
- * the array will get resized when we push past the initial number
- * of elts.  this resizing in the s->process->pool means "leaking"
- * since apr_array_push() will apr_alloc arr->nalloc * 2 elts,
- * leaving the original arr->elts to waste.
- */
-static const char *asn1_table_vhost_key(SSLModConfigRec *mc, apr_pool_t *p,
-                                  const char *id, int i)
+/* Returns the vhost-key-id which is the index into the
+ * mc->retained->privkeys hash table.  The returned string is
+ * allocated from the same pool as that hash table, to ensure it has
+ * the correct (process) lifetime of the retained data. */
+static const char *privkey_vhost_keyid(SSLModConfigRec *mc, apr_pool_t *p,
+                                       const char *id, int i)
 {
     /* 'p' pool used here is cleared on restarts (or sooner) */
     char *key = apr_psprintf(p, "%s:%d", id, i);
-    void *keyptr = apr_hash_get(mc->tVHostKeys, key,
-                                APR_HASH_KEY_STRING);
+    const char *keyptr = apr_hash_get(mc->retained->key_ids, key,
+                                      APR_HASH_KEY_STRING);
 
     if (!keyptr) {
-        /* make a copy out of s->process->pool */
-        keyptr = apr_pstrdup(mc->pPool, key);
-        apr_hash_set(mc->tVHostKeys, keyptr,
-                     APR_HASH_KEY_STRING, keyptr);
+        /* Make a copy in the (process) pool used for the retained data. */
+        keyptr = apr_pstrdup(apr_hash_pool_get(mc->retained->privkeys), key);
+        apr_hash_set(mc->retained->key_ids, keyptr, APR_HASH_KEY_STRING, keyptr);
     }
 
-    return (char *)keyptr;
+    return keyptr;
 }
 
 /*  _________________________________________________________________
@@ -134,7 +121,7 @@ apr_status_t ssl_load_encrypted_pkey(server_rec *s, apr_pool_t *p, int idx,
 {
     SSLModConfigRec *mc = myModConfig(s);
     SSLSrvConfigRec *sc = mySrvConfig(s);
-    const char *key_id = asn1_table_vhost_key(mc, p, sc->vhost_id, idx);
+    const char *key_id = privkey_vhost_keyid(mc, p, sc->vhost_id, idx);
     EVP_PKEY *pPrivateKey = NULL;
     ssl_asn1_t *asn1;
     int nPassPhrase = (*pphrases)->nelts;
@@ -187,7 +174,7 @@ apr_status_t ssl_load_encrypted_pkey(server_rec *s, apr_pool_t *p, int idx,
      * are used to give a better idea as to what failed.
      */
     if (pkey_mtime) {
-        ssl_asn1_t *asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id);
+        ssl_asn1_t *asn1 = ssl_asn1_table_get(mc->retained->privkeys, key_id);
         if (asn1 && (asn1->source_mtime == pkey_mtime)) {
             ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02575)
                          "Reusing existing private key from %s on restart",
@@ -345,7 +332,7 @@ apr_status_t ssl_load_encrypted_pkey(server_rec *s, apr_pool_t *p, int idx,
 
     /* Cache the private key in the global module configuration so it
      * can be used after subsequent reloads. */
-    asn1 = ssl_asn1_table_set(mc->tPrivateKey, key_id, pPrivateKey);
+    asn1 = ssl_asn1_table_set(mc->retained->privkeys, key_id, pPrivateKey);
 
     if (ppcb_arg.nPassPhraseDialogCur != 0) {
         /* remember mtime of encrypted keys */
index 0f588978317f855f8b9a30162d42a08fe1d94e39..6f48e1586bcbb09f171e3f88676b0498450438fc 100644 (file)
@@ -553,34 +553,37 @@ typedef struct {
     int vhost_found;          /* whether we found vhost from SNI already */
 } SSLConnRec;
 
-/* BIG FAT WARNING: SSLModConfigRec has unusual memory lifetime: it is
- * allocated out of the "process" pool and only a single such
- * structure is created and used for the lifetime of the process.
- * (The process pool is s->process->pool and is stored in the .pPool
- * field.)  Most members of this structure are likewise allocated out
- * of the process pool, but notably sesscache and sesscache_context
- * are not.
+/* Private keys are retained across reloads, since decryption
+ * passphrases can only be entered during startup (before detaching
+ * from a terminal).  This structure is stored via the ap_retained_*
+ * API and retrieved on later module reloads.  If the structure
+ * changes, the key name must be changed by increasing the digit at
+ * the end, to avoid an updated version of mod_ssl loading retained
+ * data with a different struct definition.
  *
- * The structure is treated as mostly immutable after a single config
- * parse has completed; the post_config hook (ssl_init_Module) flips
- * the bFixed flag to true and subsequent invocations of the config
- * callbacks hence do nothing.
- *
- * This odd lifetime strategy is used so that encrypted private keys
- * can be decrypted once at startup and continue to be used across
- * subsequent server reloads where the interactive password prompt is
- * not possible.
-
- * It is really an ABI nightmare waiting to happen since DSOs are
- * reloaded across restarts, and nothing prevents the struct type
- * changing across such reloads, yet the cached structure will be
- * assumed to match regardless.
- *
- * This should really be fixed using a smaller structure which only
- * stores that which is absolutely necessary (the private keys, maybe
- * the random seed), and have that structure be strictly ABI-versioned
- * for safety.
- */
+ * All objects used here must be allocated from the process pool
+ * (s->process->pool) so they also survives restarts. */
+#define MODSSL_RETAINED_KEY "mod_ssl-retained-1"
+
+typedef struct {
+    /* A hash table of vhost key-IDs used to index the privkeys hash,
+     * for example the string "vhost.example.com:443:0".  For each
+     * (key, value) pair the value is the same as the key, allowing
+     * the keys to be retrieved on subsequent reloads rather than
+     * rellocated.  ### This optimisation seems to be of dubious
+     * value.  Allocating the vhost-key-ids from pconf and duping them
+     * when storing them in ->privkeys would be simpler. */
+    apr_hash_t *key_ids;
+
+    /* A hash table of pointers to ssl_asn1_t structures.  The
+     * structures are used to store private keys in raw DER format
+     * (serialized OpenSSL PrivateKey structures).  The table is
+     * indexed by key-IDs from the key_ids hash table. */
+    apr_hash_t *privkeys;
+
+    /* Do NOT add fields here without changing the key name, as above. */
+} modssl_retained_data_t;
+
 typedef struct {
     pid_t           pid;
     apr_pool_t     *pPool;
@@ -589,6 +592,9 @@ typedef struct {
     /* OpenSSL SSL_SESS_CACHE_* flags: */
     long            sesscache_mode;
 
+    /* Data retained across reloads. */
+    modssl_retained_data_t *retained;
+
     /* The configured provider, and associated private data
      * structure. */
     const ap_socache_provider_t *sesscache;
@@ -596,13 +602,6 @@ typedef struct {
 
     apr_global_mutex_t   *pMutex;
     apr_array_header_t   *aRandSeed;
-    apr_hash_t     *tVHostKeys;
-
-    /* A hash table of pointers to ssl_asn1_t structures.  The structures
-     * are used to store private keys in raw DER format (serialized OpenSSL
-     * PrivateKey structures).  The table is indexed by (vhost-id,
-     * index), for example the string "vhost.example.com:443:0". */
-    apr_hash_t     *tPrivateKey;
 
 #if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
     const char     *szCryptoDevice;
@@ -814,7 +813,6 @@ SSLSrvConfigRec *ssl_policy_lookup(apr_pool_t *pool, const char *name);
 extern module AP_MODULE_DECLARE_DATA ssl_module;
 
 /**  configuration handling   */
-SSLModConfigRec *ssl_config_global_create(server_rec *);
 void         ssl_config_global_fix(SSLModConfigRec *);
 BOOL         ssl_config_global_isfixed(SSLModConfigRec *);
 void        *ssl_config_server_create(apr_pool_t *, server_rec *);