Aside from the technical change (that IIUC it is really nice), I really love when the code is so well documented. Having comments in the code and in the commit message is great for whoever is in a similar situation like mine (namely some knowledge of httpd internals but still very far from Yann/Eric/Ruediger/Joe/Stefan/etc..'s level :D). So all the extra time put into documentation is really helpful!
Luca On Mon, May 4, 2020 at 10:37 AM ste...@eissing.org <ste...@eissing.org> wrote: > > Neat. > > > Am 04.05.2020 um 10:32 schrieb jor...@apache.org: > > > > Author: jorton > > Date: Mon May 4 08:32:23 2020 > > New Revision: 1877345 > > > > URL: http://svn.apache.org/viewvc?rev=1877345&view=rev > > Log: > > 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 > > > > Modified: > > httpd/httpd/trunk/modules/ssl/ssl_engine_config.c > > httpd/httpd/trunk/modules/ssl/ssl_engine_init.c > > httpd/httpd/trunk/modules/ssl/ssl_engine_pphrase.c > > httpd/httpd/trunk/modules/ssl/ssl_private.h > > > > Modified: httpd/httpd/trunk/modules/ssl/ssl_engine_config.c > > URL: > > http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_engine_config.c?rev=1877345&r1=1877344&r2=1877345&view=diff > > ============================================================================== > > --- httpd/httpd/trunk/modules/ssl/ssl_engine_config.c (original) > > +++ httpd/httpd/trunk/modules/ssl/ssl_engine_config.c Mon May 4 08:32:23 > > 2020 > > @@ -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_creat > > 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_creat > > 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_ > > { > > SSLSrvConfigRec *sc = ssl_config_server_new(p); > > > > - sc->mc = ssl_config_global_create(s); > > + sc->mc = ssl_config_global_create(p, s); > > > > return sc; > > } > > > > Modified: httpd/httpd/trunk/modules/ssl/ssl_engine_init.c > > URL: > > http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_engine_init.c?rev=1877345&r1=1877344&r2=1877345&view=diff > > ============================================================================== > > --- httpd/httpd/trunk/modules/ssl/ssl_engine_init.c (original) > > +++ httpd/httpd/trunk/modules/ssl/ssl_engine_init.c Mon May 4 08:32:23 2020 > > @@ -226,6 +226,8 @@ apr_status_t ssl_init_Module(apr_pool_t > > 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 > > /* > > * 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 > > 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_cert > > /* 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)) { > > > > Modified: httpd/httpd/trunk/modules/ssl/ssl_engine_pphrase.c > > URL: > > http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_engine_pphrase.c?rev=1877345&r1=1877344&r2=1877345&view=diff > > ============================================================================== > > --- httpd/httpd/trunk/modules/ssl/ssl_engine_pphrase.c (original) > > +++ httpd/httpd/trunk/modules/ssl/ssl_engine_pphrase.c Mon May 4 08:32:23 > > 2020 > > @@ -71,38 +71,25 @@ static apr_status_t exists_and_readable( > > 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(ser > > { > > 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(ser > > * 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(ser > > > > /* 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 */ > > > > Modified: httpd/httpd/trunk/modules/ssl/ssl_private.h > > URL: > > http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_private.h?rev=1877345&r1=1877344&r2=1877345&view=diff > > ============================================================================== > > --- httpd/httpd/trunk/modules/ssl/ssl_private.h (original) > > +++ httpd/httpd/trunk/modules/ssl/ssl_private.h Mon May 4 08:32:23 2020 > > @@ -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_p > > 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 *); > > > > >