1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-08 15:02:10 +03:00

mod_ssl: Use retained data API for storing private keys across reloads.

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
This commit is contained in:
Joe Orton
2020-05-04 08:32:23 +00:00
parent dd3b1ab98b
commit 31dfb9b476
4 changed files with 69 additions and 77 deletions

View File

@@ -40,17 +40,17 @@
** _________________________________________________________________
*/
#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;
}

View 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)) {

View 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 */

View 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.
* 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;
* 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.
*/
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 *);