mod_ssl is hardwired only to initialize certain things on the first module
init during startup. the only reason i can see is because the builtin
SSLPassPhraseDialog can only read the passphrase from the tty before
detach. but if SSLPassPhraseDialog is exec: or the server key is not
passphrase encrypted, there is no reason not to do a full startup/teardown
of these things each time on restart.
currently it is not possible to add LoadModule mod_ssl.so to an already
running server (core dumps), nor is it possible to change the server
cert/key on restart (continues to use the cert/key read at initial
startup).
patch below makes both possible by removing the init counter and doing a
full startup/teardown on restarts. adds a special case when
SSLPassPhraseDialog is builtin and server has detached, to reuse the
existing private key for a vhost if the key source file/mtime have not
changed.
Index: modules/ssl/mod_ssl.h
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/mod_ssl.h,v
retrieving revision 1.55
diff -u -r1.55 mod_ssl.h
--- modules/ssl/mod_ssl.h 18 Jan 2002 23:26:46 -0000 1.55
+++ modules/ssl/mod_ssl.h 22 Feb 2002 03:58:00 -0000
@@ -462,6 +462,8 @@
typedef struct {
long int nData;
unsigned char *cpData;
+ char *source_file;
+ apr_time_t source_mtime;
} ssl_asn1_t;
/*
@@ -501,7 +503,6 @@
pid_t pid;
apr_pool_t *pPool;
BOOL bFixed;
- int nInitCount;
int nSessionCacheMode;
char *szSessionCacheDataFile;
int nSessionCacheDataSize;
Index: modules/ssl/ssl_engine_config.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_engine_config.c,v
retrieving revision 1.20
diff -u -r1.20 ssl_engine_config.c
--- modules/ssl/ssl_engine_config.c 29 Nov 2001 06:15:01 -0000 1.20
+++ modules/ssl/ssl_engine_config.c 22 Feb 2002 03:58:00 -0000
@@ -90,7 +90,6 @@
/*
* initialize per-module configuration
*/
- mc->nInitCount = 0;
mc->nSessionCacheMode = SSL_SCMODE_UNSET;
mc->szSessionCacheDataFile = NULL;
mc->nSessionCacheDataSize = 0;
Index: modules/ssl/ssl_engine_init.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_engine_init.c,v
retrieving revision 1.25
diff -u -r1.25 ssl_engine_init.c
--- modules/ssl/ssl_engine_init.c 16 Feb 2002 18:35:21 -0000 1.25
+++ modules/ssl/ssl_engine_init.c 22 Feb 2002 03:58:01 -0000
@@ -89,8 +89,6 @@
ssl_config_global_create(s); /* just to avoid problems */
ssl_config_global_fix(mc);
- mc->nInitCount++;
-
/*
* try to fix the configuration and open the dedicated SSL
* logfile as early as possible
@@ -121,78 +119,22 @@
/*
* Identification
*/
- if (mc->nInitCount == 1) {
- ssl_log(s, SSL_LOG_INFO, "Server: %s, Interface: %s, Library: %s",
- AP_SERVER_BASEVERSION,
- ssl_var_lookup(p, s, NULL, NULL, "SSL_VERSION_INTERFACE"),
- ssl_var_lookup(p, s, NULL, NULL, "SSL_VERSION_LIBRARY"));
- }
-
- /*
- * Initialization round information
- */
- if (mc->nInitCount == 1)
- ssl_log(s, SSL_LOG_INFO, "Init: 1st startup round (still not detached)");
- else if (mc->nInitCount == 2)
- ssl_log(s, SSL_LOG_INFO, "Init: 2nd startup round (already detached)");
- else
- ssl_log(s, SSL_LOG_INFO, "Init: %d%s restart round (already detached)",
- mc->nInitCount-2, (mc->nInitCount-2) == 1 ? "st" : "nd");
+ ssl_log(s, SSL_LOG_INFO, "Server: %s, Interface: %s, Library: %s",
+ AP_SERVER_BASEVERSION,
+ ssl_var_lookup(p, s, NULL, NULL, "SSL_VERSION_INTERFACE"),
+ ssl_var_lookup(p, s, NULL, NULL, "SSL_VERSION_LIBRARY"));
- /*
- * The initialization phase inside the Apache API is totally bogus.
- * We actually have three non-trivial problems:
- *
- * 1. Under Unix the API does a 2-round initialization of modules while
- * under Win32 it doesn't. This means we have to make sure that at
- * least the pass phrase dialog doesn't occur twice. We overcome this
- * problem by using a counter (mc->nInitCount) which has to
- * survive the init rounds.
- *
- * 2. Between the first and the second round Apache detaches from
- * the terminal under Unix. This means that our pass phrase dialog
- * _has_ to be done in the first round and _cannot_ be done in the
- * second round.
- *
- * 3. When Dynamic Shared Object (DSO) mechanism is used under Unix the
- * module segment (code & data) gets unloaded and re-loaded between
- * the first and the second round. This means no global data survives
- * between first and the second init round. We overcome this by using
- * an entry ("ssl_module") inside the process_rec->pool->user_data.
- *
- * The situation as a table:
- *
- * Unix/static Unix/DSO Win32 Action Required
- * (-DSHARED_MODULE) (-DWIN32)
- * ----------- ----------------- --------- -----------------------------------
- * - load module - -
- * init init init SSL library init, Pass Phrase Dialog
- * detach detach - -
- * - reload module - -
- * init init - SSL library init, mod_ssl init
- *
- * Ok, now try to solve this totally ugly situation...
- */
+ ssl_log(s, SSL_LOG_INFO, "Init: Initializing %s library",
+ SSL_LIBRARY_NAME);
-#ifdef SHARED_MODULE
- ssl_log(s, SSL_LOG_INFO, "Init: %snitializing %s library",
- mc->nInitCount == 1 ? "I" : "Rei", SSL_LIBRARY_NAME);
ssl_init_SSLLibrary();
-#else
- if (mc->nInitCount <= 2) {
- ssl_log(s, SSL_LOG_INFO, "Init: %snitializing %s library",
- mc->nInitCount == 1 ? "I" : "Rei", SSL_LIBRARY_NAME);
- ssl_init_SSLLibrary();
- }
-#endif
+
#if APR_HAS_THREADS
ssl_util_thread_setup(s, p);
#endif
- if (mc->nInitCount == 1) {
- ssl_pphrase_Handle(s, p);
- ssl_init_TmpKeysHandle(SSL_TKP_GEN, s, p);
- return OK;
- }
+
+ ssl_pphrase_Handle(s, p);
+ ssl_init_TmpKeysHandle(SSL_TKP_GEN, s, p);
/*
* SSL external crypto device ("engine") support
Index: modules/ssl/ssl_engine_pphrase.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_engine_pphrase.c,v
retrieving revision 1.12
diff -u -r1.12 ssl_engine_pphrase.c
--- modules/ssl/ssl_engine_pphrase.c 9 Jan 2002 19:24:32 -0000 1.12
+++ modules/ssl/ssl_engine_pphrase.c 22 Feb 2002 03:58:01 -0000
@@ -67,7 +67,7 @@
* Return true if the named file exists and is readable
*/
-static apr_status_t exists_and_readable(char *fname, apr_pool_t *pool)
+static apr_status_t exists_and_readable(char *fname, apr_pool_t *pool, apr_time_t
+*mtime)
{
apr_status_t stat;
apr_finfo_t sbuf;
@@ -82,6 +82,10 @@
if ((stat = apr_file_open(&fd, fname, APR_READ, 0, pool)) != APR_SUCCESS)
return stat;
+ if (mtime) {
+ *mtime = sbuf.mtime;
+ }
+
apr_file_close(fd);
return APR_SUCCESS;
}
@@ -121,7 +125,8 @@
ssl_algo_t algoCert, algoKey, at;
char *an;
char *cp;
-
+ apr_time_t pkey_mtime = 0;
+ int isterm = 1;
/*
* Start with a fresh pass phrase array
*/
@@ -158,7 +163,7 @@
for (i = 0, j = 0; i < SSL_AIDX_MAX && sc->szPublicCertFile[i] != NULL; i++) {
apr_cpystrn(szPath, sc->szPublicCertFile[i], sizeof(szPath));
- if ( exists_and_readable(szPath, p) != APR_SUCCESS ) {
+ if ( exists_and_readable(szPath, p, NULL) != APR_SUCCESS ) {
ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
"Init: Can't open server certificate file %s", szPath);
ssl_die();
@@ -249,15 +254,42 @@
* the callback function which serves the pass
* phrases to OpenSSL
*/
- if ( exists_and_readable(szPath, p) != APR_SUCCESS ) {
+ if ( exists_and_readable(szPath, p, &pkey_mtime) != APR_SUCCESS ) {
ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
"Init: Can't open server private key file %s",szPath);
ssl_die();
}
+
+ /*
+ * isatty() returns false once httpd has detached from the terminal.
+ * if the private key is encrypted and SSLPassPhraseDialog is
+configured to "builtin"
+ * it isn't possible to prompt for a password. in this case if we
+already have a
+ * private key and the file name/mtime hasn't changed, then reuse the
+existing key.
+ * of course this will not work if the server was started without
+LoadModule ssl_module
+ * configured, then restarted with it configured. but we fall
+through with a chance of
+ * success if the key is not encrypted. and in the case of
+fallthrough, pkey_mtime and
+ * isterm values are used to give a better idea as to what failed.
+ */
+ if ((sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) &&
+ !(isterm = isatty(fileno(stdout)))) /* XXX: apr_isatty() */
+ {
+ char *key_id = apr_psprintf(p, "%s:%s", cpVHostID, "RSA"); /*
+XXX: check for DSA key too? */
+ ssl_asn1_t *asn1 = (ssl_asn1_t
+*)ssl_ds_table_get(mc->tPrivateKey, key_id);
+
+ if (asn1 && (asn1->source_mtime == pkey_mtime) &&
+ !strcmp(asn1->source_file, szPath))
+ {
+ ssl_log(pServ, SSL_LOG_INFO,
+ "%s reusing existing private key on restart",
+ cpVHostID);
+ return;
+ }
+ }
+
cpPassPhraseCur = NULL;
bReadable = ((pPrivateKey = SSL_read_PrivateKey(szPath, NULL,
ssl_pphrase_Handle_CB, s)) != NULL ? TRUE : FALSE);
-
+
/*
* when the private key file now was readable,
* it's fine and we go out of the loop
@@ -298,12 +330,20 @@
/*
* Ok, anything else now means a fatal error.
*/
- if (cpPassPhraseCur == NULL)
- ssl_log(pServ, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Init: Private key
not found");
+ if (cpPassPhraseCur == NULL) {
+ if (nPassPhraseDialogCur && pkey_mtime && !isterm) {
+ ssl_log(pServ, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: Unable read passphrase "
+ "[Hint: key introduced or changed before restart?]");
+ }
+ else {
+ ssl_log(pServ, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Init: Private
+key not found");
+ }
if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) {
fprintf(stdout, "Apache:mod_ssl:Error: Private key not
found.\n");
fprintf(stdout, "**Stopped\n");
}
+ }
else {
ssl_log(pServ, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Init: Pass phrase
incorrect");
if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) {
@@ -371,6 +411,9 @@
asn1->nData = i2d_PrivateKey(pPrivateKey, NULL);
asn1->cpData = apr_palloc(mc->pPool, asn1->nData);
ucp = asn1->cpData; i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg
increments */
+
+ asn1->source_file = apr_pstrdup(mc->pPool, szPath);
+ asn1->source_mtime = pkey_mtime;
/*
* Free the private key structure