From: Art Cancro Date: Mon, 27 Dec 2021 22:30:09 +0000 (-0500) Subject: Citadel Server and WebCit (classic) now both reload the key and cert if the modificat... X-Git-Tag: v943~11 X-Git-Url: https://code.citadel.org/?p=citadel.git;a=commitdiff_plain;h=ab376f8217b148b8d0896a31e6a2502100f134ac Citadel Server and WebCit (classic) now both reload the key and cert if the modification time of either one changes. This should allow us to replace or renew the certificate during normal operation without restarting. --- diff --git a/citadel/modules/crypto/serv_crypto.c b/citadel/modules/crypto/serv_crypto.c index 16be6d567..98cded3c9 100644 --- a/citadel/modules/crypto/serv_crypto.c +++ b/citadel/modules/crypto/serv_crypto.c @@ -110,6 +110,28 @@ void bind_to_key_and_certificate(void) { } +// Check the modification time of the key and certificate -- reload if they changed +void update_key_and_cert_if_needed(void) { + static time_t cert_mtime = 0; + struct stat keystat; + struct stat certstat; + + if (stat(file_crpt_file_key, &keystat) != 0) { + syslog(LOG_ERR, "%s: %s", file_crpt_file_key, strerror(errno)); + return; + } + if (stat(file_crpt_file_cer, &certstat) != 0) { + syslog(LOG_ERR, "%s: %s", file_crpt_file_cer, strerror(errno)); + return; + } + + if ((keystat.st_mtime > cert_mtime) || (certstat.st_mtime > cert_mtime)) { + bind_to_key_and_certificate(); + cert_mtime = certstat.st_mtime; + } +} + + void init_ssl(void) { const SSL_METHOD *ssl_method; RSA *rsa = NULL; @@ -499,6 +521,9 @@ void CtdlStartTLS(char *ok_response, char *nosup_response, char *error_response) } return; } + + update_key_and_cert_if_needed(); // did someone update the key or cert? if so, re-bind them + if (!(CC->ssl = SSL_new(ssl_ctx))) { syslog(LOG_ERR, "crypto: SSL_new failed: %s", ERR_reason_error_string(ERR_get_error())); if (error_response != NULL) { diff --git a/webcit/crypto.c b/webcit/crypto.c index c396e2d0e..d0804beaf 100644 --- a/webcit/crypto.c +++ b/webcit/crypto.c @@ -16,6 +16,8 @@ SSL_CTX *ssl_ctx; // Global SSL context +char key_file[PATH_MAX] = ""; +char cert_file[PATH_MAX] = ""; char *ssl_cipher_list = DEFAULT_SSL_CIPHER_LIST; pthread_key_t ThreadSSL; // Per-thread SSL context @@ -25,6 +27,27 @@ void shutdown_ssl(void) { } +// Set the private key and certificate chain for the global SSL Context. +// This is called during initialization, and can be called again later if the certificate changes. +void bind_to_key_and_certificate(void) { + if (IsEmptyStr(key_file)) { + snprintf(key_file, sizeof key_file, "%s/keys/citadel.key", ctdl_dir); + } + if (IsEmptyStr(cert_file)) { + snprintf(cert_file, sizeof key_file, "%s/keys/citadel.cer", ctdl_dir); + } + + syslog(LOG_DEBUG, "crypto: [re]installing key \"%s\" and certificate \"%s\"", key_file, cert_file); + + SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file); + SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM); + + if ( !SSL_CTX_check_private_key(ssl_ctx) ) { + syslog(LOG_WARNING, "crypto: cannot install certificate: %s", ERR_reason_error_string(ERR_get_error())); + } +} + + // initialize ssl engine, load certs and initialize openssl internals void init_ssl(void) { const SSL_METHOD *ssl_method; @@ -57,29 +80,41 @@ void init_ssl(void) { // Now try to bind to the key and certificate. - // Note that we use SSL_CTX_use_certificate_chain_file() which allows - // the certificate file to contain intermediate certificates. + bind_to_key_and_certificate(); +} - char *key_file[PATH_MAX]; - char *cert_file[PATH_MAX]; - snprintf(key_file, sizeof key_file, "%s/keys/citadel.key", ctdl_dir); - snprintf(cert_file, sizeof key_file, "%s/keys/citadel.cer", ctdl_dir); +// Check the modification time of the key and certificate -- reload if they changed +void update_key_and_cert_if_needed(void) { + static time_t cert_mtime = 0; + struct stat keystat; + struct stat certstat; - SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file); - SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM); + if (stat(key_file, &keystat) != 0) { + syslog(LOG_ERR, "%s: %s", key_file, strerror(errno)); + return; + } + if (stat(cert_file, &certstat) != 0) { + syslog(LOG_ERR, "%s: %s", cert_file, strerror(errno)); + return; + } - if ( !SSL_CTX_check_private_key(ssl_ctx) ) { - syslog(LOG_WARNING, "crypto: cannot install certificate: %s", ERR_reason_error_string(ERR_get_error())); + if ((keystat.st_mtime > cert_mtime) || (certstat.st_mtime > cert_mtime)) { + bind_to_key_and_certificate(); + cert_mtime = certstat.st_mtime; } } // starts SSL/TLS encryption for the current session. int starttls(int sock) { - int retval, bits, alg_bits; SSL *newssl; + int retval, bits, alg_bits; + // Check the modification time of the key and certificate -- reload if they changed + update_key_and_cert_if_needed(); + + // SSL is a thread-specific thing, I think. pthread_setspecific(ThreadSSL, NULL); if (!ssl_ctx) { @@ -191,7 +226,7 @@ int client_write_ssl(const StrBuf *Buf) { sleeeeeeeeeep(1); continue; } - syslog(LOG_WARNING, "SSL_write got error %ld, ret %d", errval, retval); + syslog(LOG_WARNING, "SSL_write: %s", ERR_reason_error_string(ERR_get_error())); if (retval == -1) { syslog(LOG_WARNING, "errno is %d\n", errno); } @@ -228,7 +263,7 @@ int client_read_sslbuffer(StrBuf *buf, int timeout) { sleeeeeeeeeep(1); continue; } - syslog(LOG_WARNING, "SSL_read got error %ld", errval); + syslog(LOG_WARNING, "SSL_read in client_read: %s", ERR_reason_error_string(ERR_get_error())); endtls(); return (-1); } diff --git a/webcit/serv_func.c b/webcit/serv_func.c index 7f26f7222..88af2024f 100644 --- a/webcit/serv_func.c +++ b/webcit/serv_func.c @@ -161,9 +161,6 @@ ServInfo *get_serv_info(StrBuf *browser_host, StrBuf *user_agent) { int GetConnected (void) { StrBuf *Buf; - TRACE; - syslog(LOG_DEBUG, "GetConnected() has been called, and ctdl_dir is \"%s\"", ctdl_dir); - if (WC->ReadBuf == NULL) { WC->ReadBuf = NewStrBufPlain(NULL, SIZ * 4); }