Skip to content
Snippets Groups Projects
Commit d1c741d0 authored by Deucе's avatar Deucе :ok_hand_tone4:
Browse files

Incremental backoff on loading SSL cert

Try for 16 seconds (14 loops) to load the SSL certificate.

On the first time through the loop, create self-signed certificate
if configured to do so.

This also splits the generation of the self-signed certificate into
a separate function.

While we're here, split the new SSL epoch out into a separate function
as well, and explicitly call it when we create a new self-signed
cert. This at least partially fixes the epoch thing, but there's
still the possibility of creating it multiple times in the same
second... the file date isn't really enough.
parent 7f1fd2c9
Branches
No related tags found
No related merge requests found
Pipeline #8675 failed
...@@ -375,31 +375,22 @@ bool is_crypt_initialized(void) ...@@ -375,31 +375,22 @@ bool is_crypt_initialized(void)
return cryptlib_initialized; return cryptlib_initialized;
} }
bool ssl_sync(scfg_t *scfg, int (*lprintf)(int level, const char* fmt, ...)) static bool
ssl_new_epoch(scfg_t *scfg, int (*lprintf)(int level, const char* fmt, ...))
{ {
time_t epoch_date; lprintf(LOG_DEBUG, "New SSL Cert Epoch");
if (!rwlock_wrlock(&tls_cert_file_date_lock)) {
if (!do_cryptInit(lprintf)) lprintf(LOG_ERR, "Unable to lock tls_cert_file_date_lock for write at %d", __LINE__);
return false;
if (!cert_path[0])
SAFEPRINTF2(cert_path, "%s%s", scfg->ctrl_dir, "ssl.cert");
time_t fd = fdate(cert_path);
if (!rwlock_rdlock(&tls_cert_file_date_lock)) {
lprintf(LOG_ERR, "Unable to lock tls_cert_file_date_lock for read at %d", __LINE__);
return false;
}
epoch_date = tls_cert_file_date;
if (!rwlock_unlock(&tls_cert_file_date_lock)) {
lprintf(LOG_ERR, "Unable to unlock tls_cert_file_date_lock for read at %d", __LINE__);
return false; return false;
} }
if (epoch_date != 0) { tls_cert_file_date = fdate(cert_path);
if (fd != epoch_date) { if (!rwlock_unlock(&tls_cert_file_date_lock))
lprintf(LOG_DEBUG, "Destroying TLS private keys"); lprintf(LOG_ERR, "Unable to unlock tls_cert_file_date_lock for write at %d", __LINE__);
if (!rwlock_wrlock(&cert_epoch_lock)) { if (!rwlock_wrlock(&cert_epoch_lock)) {
lprintf(LOG_ERR, "Unable to lock cert_epoch_lock for write at %d", __LINE__); lprintf(LOG_ERR, "Unable to lock cert_epoch_lock for write at %d", __LINE__);
return false;
} }
else {
cert_epoch++; cert_epoch++;
// Paranoia... keep zero as initial value only. // Paranoia... keep zero as initial value only.
if (cert_epoch == 0) if (cert_epoch == 0)
...@@ -413,11 +404,32 @@ bool ssl_sync(scfg_t *scfg, int (*lprintf)(int level, const char* fmt, ...)) ...@@ -413,11 +404,32 @@ bool ssl_sync(scfg_t *scfg, int (*lprintf)(int level, const char* fmt, ...))
free(old); free(old);
} }
assert_pthread_mutex_unlock(&ssl_cert_list_mutex); assert_pthread_mutex_unlock(&ssl_cert_list_mutex);
if (!rwlock_unlock(&cert_epoch_lock)) { if (!rwlock_unlock(&cert_epoch_lock))
lprintf(LOG_ERR, "Unable to unlock cert_epoch_lock for write at %d", __LINE__); lprintf(LOG_ERR, "Unable to unlock cert_epoch_lock for write at %d", __LINE__);
return true;
} }
bool ssl_sync(scfg_t *scfg, int (*lprintf)(int level, const char* fmt, ...))
{
time_t epoch_date;
if (!do_cryptInit(lprintf))
return false;
if (!cert_path[0])
SAFEPRINTF2(cert_path, "%s%s", scfg->ctrl_dir, "ssl.cert");
time_t fd = fdate(cert_path);
if (!rwlock_rdlock(&tls_cert_file_date_lock)) {
lprintf(LOG_ERR, "Unable to lock tls_cert_file_date_lock for read at %d", __LINE__);
return false;
} }
epoch_date = tls_cert_file_date;
if (!rwlock_unlock(&tls_cert_file_date_lock)) {
lprintf(LOG_ERR, "Unable to unlock tls_cert_file_date_lock for read at %d", __LINE__);
return false;
} }
if (epoch_date != 0) {
if (fd != epoch_date)
return ssl_new_epoch(scfg, lprintf);
} }
return true; return true;
} }
...@@ -436,70 +448,34 @@ log_cryptlib_error(int status, int line, CRYPT_HANDLE handle, const char *action ...@@ -436,70 +448,34 @@ log_cryptlib_error(int status, int line, CRYPT_HANDLE handle, const char *action
#define DO(action, handle, x) (cryptStatusOK((DOtmp = x)) ? true : log_cryptlib_error(DOtmp, __LINE__, handle, action, lprintf)) #define DO(action, handle, x) (cryptStatusOK((DOtmp = x)) ? true : log_cryptlib_error(DOtmp, __LINE__, handle, action, lprintf))
static struct cert_list * get_ssl_cert(scfg_t *cfg, int (*lprintf)(int level, const char* fmt, ...)) static bool
create_self_signed_cert(scfg_t *cfg, int (*lprintf)(int level, const char* fmt, ...))
{ {
CRYPT_CONTEXT cert;
CRYPT_KEYSET ssl_keyset; CRYPT_KEYSET ssl_keyset;
CRYPT_CERTIFICATE ssl_cert; CRYPT_CERTIFICATE ssl_cert;
char sysop_email[sizeof(cfg->sys_inetaddr) + 6];
struct cert_list *cert_entry;
int DOtmp; int DOtmp;
char sysop_email[sizeof(cfg->sys_inetaddr) + 6];
if (!do_cryptInit(lprintf))
return NULL;
ssl_sync(cfg, lprintf);
cert_entry = malloc(sizeof(*cert_entry));
if (cert_entry == NULL) {
lprintf(LOG_CRIT, "%s line %d: FAILED TO ALLOCATE %u bytes of memory", __FUNCTION__, __LINE__, sizeof *cert_entry);
return NULL;
}
cert_entry->sess = -1;
if (rwlock_rdlock(&cert_epoch_lock)) {
cert_entry->epoch = cert_epoch;
if (!rwlock_unlock(&cert_epoch_lock)) {
lprintf(LOG_ERR, "Unable to unlock cert_epoc_lock at %d", __LINE__);
}
}
else {
lprintf(LOG_ERR, "Unable to lock cert_epoc_lock at %d", __LINE__);
free(cert_entry);
return NULL;
}
cert_entry->next = NULL;
assert_pthread_mutex_lock(&get_ssl_cert_mutex);
/* Get the certificate... first try loading it from a file... */
if (cryptStatusOK(cryptKeysetOpen(&ssl_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cert_path, CRYPT_KEYOPT_READONLY))) {
if (!DO("getting private key", ssl_keyset, cryptGetPrivateKey(ssl_keyset, &cert_entry->cert, CRYPT_KEYID_NAME, "ssl_cert", cfg->sys_pass))) {
assert_pthread_mutex_unlock(&get_ssl_cert_mutex);
free(cert_entry);
return NULL;
}
}
else {
lprintf(LOG_WARNING, "Failed to open/read TLS certificate: %s", cert_path);
if (!cfg->create_self_signed_cert) {
assert_pthread_mutex_unlock(&get_ssl_cert_mutex);
free(cert_entry);
return NULL;
}
lprintf(LOG_NOTICE, "Creating self-signed TLS certificate"); lprintf(LOG_NOTICE, "Creating self-signed TLS certificate");
/* Couldn't do that... create a new context and use the cert from there... */ if (!cert_path[0]) {
if (!DO("creating TLS context", CRYPT_UNUSED, cryptCreateContext(&cert_entry->cert, CRYPT_UNUSED, CRYPT_ALGO_RSA))) { lprintf(LOG_INFO, "cert_path not set");
assert_pthread_mutex_unlock(&get_ssl_cert_mutex); return false;
free(cert_entry);
return NULL;
} }
if (!DO("setting label", cert_entry->cert, cryptSetAttributeString(cert_entry->cert, CRYPT_CTXINFO_LABEL, "ssl_cert", 8))) /* Create a new context and cert... */
if (!DO("creating TLS context", CRYPT_UNUSED, cryptCreateContext(&cert, CRYPT_UNUSED, CRYPT_ALGO_RSA)))
return false;
if (!DO("setting label", cert, cryptSetAttributeString(cert, CRYPT_CTXINFO_LABEL, "ssl_cert", 8)))
goto failure_return_1; goto failure_return_1;
if (!DO("generating key", cert_entry->cert, cryptGenerateKey(cert_entry->cert))) if (!DO("generating key", cert, cryptGenerateKey(cert)))
goto failure_return_1; goto failure_return_1;
if (!DO("opening keyset", CRYPT_UNUSED, cryptKeysetOpen(&ssl_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cert_path, CRYPT_KEYOPT_CREATE))) if (!DO("opening keyset", CRYPT_UNUSED, cryptKeysetOpen(&ssl_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cert_path, CRYPT_KEYOPT_CREATE)))
goto failure_return_1; goto failure_return_1;
if (!DO("adding private key", ssl_keyset, cryptAddPrivateKey(ssl_keyset, cert_entry->cert, cfg->sys_pass))) if (!DO("adding private key", ssl_keyset, cryptAddPrivateKey(ssl_keyset, cert, cfg->sys_pass)))
goto failure_return_2; goto failure_return_2;
if (!DO("creating certificate", CRYPT_UNUSED, cryptCreateCert(&ssl_cert, CRYPT_UNUSED, CRYPT_CERTTYPE_CERTIFICATE))) if (!DO("creating certificate", CRYPT_UNUSED, cryptCreateCert(&ssl_cert, CRYPT_UNUSED, CRYPT_CERTTYPE_CERTIFICATE)))
goto failure_return_2; goto failure_return_2;
if (!DO("setting public key", ssl_cert, cryptSetAttribute(ssl_cert, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, cert_entry->cert))) if (!DO("setting public key", ssl_cert, cryptSetAttribute(ssl_cert, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, cert)))
goto failure_return_3; goto failure_return_3;
if (!DO("signing certificate", ssl_cert, cryptSetAttribute(ssl_cert, CRYPT_CERTINFO_SELFSIGNED, 1))) if (!DO("signing certificate", ssl_cert, cryptSetAttribute(ssl_cert, CRYPT_CERTINFO_SELFSIGNED, 1)))
goto failure_return_3; goto failure_return_3;
...@@ -516,34 +492,79 @@ static struct cert_list * get_ssl_cert(scfg_t *cfg, int (*lprintf)(int level, co ...@@ -516,34 +492,79 @@ static struct cert_list * get_ssl_cert(scfg_t *cfg, int (*lprintf)(int level, co
sprintf(sysop_email, "sysop@%s", cfg->sys_inetaddr); sprintf(sysop_email, "sysop@%s", cfg->sys_inetaddr);
if (!DO("setting email", ssl_cert, cryptSetAttributeString(ssl_cert, CRYPT_CERTINFO_RFC822NAME, sysop_email, strlen(sysop_email)))) if (!DO("setting email", ssl_cert, cryptSetAttributeString(ssl_cert, CRYPT_CERTINFO_RFC822NAME, sysop_email, strlen(sysop_email))))
goto failure_return_3; goto failure_return_3;
if (!DO("signing certificate", ssl_cert, cryptSignCert(ssl_cert, cert_entry->cert))) if (!DO("signing certificate", ssl_cert, cryptSignCert(ssl_cert, cert)))
goto failure_return_3; goto failure_return_3;
if (!DO("adding public key", ssl_keyset, cryptAddPublicKey(ssl_keyset, ssl_cert))) if (!DO("adding public key", ssl_keyset, cryptAddPublicKey(ssl_keyset, ssl_cert)))
goto failure_return_3; goto failure_return_3;
cryptDestroyCert(ssl_cert); cryptDestroyCert(ssl_cert);
cryptKeysetClose(ssl_keyset); cryptKeysetClose(ssl_keyset);
cryptDestroyContext(cert_entry->cert); cryptDestroyContext(cert);
ssl_new_epoch(cfg, lprintf);
return true;
failure_return_3:
cryptDestroyCert(ssl_cert);
failure_return_2:
cryptKeysetClose(ssl_keyset);
failure_return_1:
cryptDestroyContext(cert);
cert_path[0] = 0;
return false;
}
static struct cert_list * get_ssl_cert(scfg_t *cfg, int (*lprintf)(int level, const char* fmt, ...))
{
CRYPT_KEYSET ssl_keyset;
struct cert_list *cert_entry;
int DOtmp;
if (!do_cryptInit(lprintf))
return NULL;
ssl_sync(cfg, lprintf);
cert_entry = malloc(sizeof(*cert_entry));
if (cert_entry == NULL) {
lprintf(LOG_CRIT, "%s line %d: FAILED TO ALLOCATE %u bytes of memory", __FUNCTION__, __LINE__, sizeof *cert_entry);
return NULL;
}
cert_entry->sess = -1;
cert_entry->next = NULL;
cert_entry->cert = -1; cert_entry->cert = -1;
// Finally, load it from the file.
size_t backoff_ms = 1;
unsigned loops = 0;
while (cert_entry->cert == -1) {
assert_pthread_mutex_lock(&get_ssl_cert_mutex);
/* Get the certificate... first try loading it from a file... */
if (cryptStatusOK(cryptKeysetOpen(&ssl_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cert_path, CRYPT_KEYOPT_READONLY))) { if (cryptStatusOK(cryptKeysetOpen(&ssl_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cert_path, CRYPT_KEYOPT_READONLY))) {
if (!DO("getting private key", ssl_keyset, cryptGetPrivateKey(ssl_keyset, &cert_entry->cert, CRYPT_KEYID_NAME, "ssl_cert", cfg->sys_pass))) { DO("getting private key", ssl_keyset, cryptGetPrivateKey(ssl_keyset, &cert_entry->cert, CRYPT_KEYID_NAME, "ssl_cert", cfg->sys_pass));
cert_entry->cert = -1; cryptKeysetClose(ssl_keyset);
}
else {
if (!rwlock_wrlock(&tls_cert_file_date_lock)) {
lprintf(LOG_ERR, "Unable to lock tls_cert_file_date_lock for write at %d", __LINE__);
} }
tls_cert_file_date = fdate(cert_path); if (cert_entry->cert == -1) {
if (!rwlock_unlock(&tls_cert_file_date_lock)) { lprintf(LOG_WARNING, "Failed to open/read TLS certificate: %s", cert_path);
lprintf(LOG_ERR, "Unable to unlock tls_cert_file_date_lock for write at %d", __LINE__); if (cfg->create_self_signed_cert) {
// Only try to create cert first time through the loop.
if (loops == 0) {
lprintf(LOG_NOTICE, "Creating self-signed TLS certificate");
if (create_self_signed_cert(cfg, lprintf)) {
loops++;
assert_pthread_mutex_unlock(&get_ssl_cert_mutex);
continue;
} }
ssl_sync(cfg, lprintf);
} }
} }
} }
cryptKeysetClose(ssl_keyset);
assert_pthread_mutex_unlock(&get_ssl_cert_mutex); assert_pthread_mutex_unlock(&get_ssl_cert_mutex);
// Backoff...
loops++;
// Total wait time is (1 << (total loops)) - 1 ms
// ie: 14 loops is 16.383s
if (loops > 14)
break;
SLEEP(backoff_ms);
backoff_ms *= 2;
}
lprintf(LOG_DEBUG, "Total times through cert loop: %u (%ums)", loops, (1 << loops) - 1);
if (cert_entry->cert == -1) { if (cert_entry->cert == -1) {
free(cert_entry); free(cert_entry);
...@@ -554,17 +575,6 @@ static struct cert_list * get_ssl_cert(scfg_t *cfg, int (*lprintf)(int level, co ...@@ -554,17 +575,6 @@ static struct cert_list * get_ssl_cert(scfg_t *cfg, int (*lprintf)(int level, co
lprintf(LOG_DEBUG, "Created TLS private key and certificate %d", cert_entry->cert); lprintf(LOG_DEBUG, "Created TLS private key and certificate %d", cert_entry->cert);
} }
return cert_entry; return cert_entry;
failure_return_3:
cryptDestroyCert(ssl_cert);
failure_return_2:
cryptKeysetClose(ssl_keyset);
failure_return_1:
cryptDestroyContext(cert_entry->cert);
assert_pthread_mutex_unlock(&get_ssl_cert_mutex);
cert_path[0] = 0;
free(cert_entry);
return NULL;
} }
#undef DO #undef DO
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment