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