1
0
mirror of https://github.com/osmarks/ngircd.git synced 2024-10-27 20:36:18 +00:00

Add support for GnuTLS certificate reload

This requires keeping track of currently active certificates, so those
are stored separately, along with a reference counter, and discarded
when they are no longer in use.
This commit is contained in:
Hilko Bengen 2020-04-17 17:34:12 +02:00
parent 9c5e42458e
commit eead4a631f
2 changed files with 75 additions and 13 deletions

View File

@ -40,6 +40,7 @@ struct ConnSSL_State {
gnutls_session_t gnutls_session; gnutls_session_t gnutls_session;
void *cookie; /* pointer to server configuration structure void *cookie; /* pointer to server configuration structure
(for outgoing connections), or NULL. */ (for outgoing connections), or NULL. */
size_t x509_cred_idx; /* index of active x509 credential record */
#endif #endif
char *fingerprint; char *fingerprint;
}; };

View File

@ -62,7 +62,14 @@ static bool ConnSSL_LoadServerKey_openssl PARAMS(( SSL_CTX *c ));
#define MAX_HASH_SIZE 64 /* from gnutls-int.h */ #define MAX_HASH_SIZE 64 /* from gnutls-int.h */
static gnutls_certificate_credentials_t x509_cred; typedef struct {
int refcnt;
gnutls_certificate_credentials_t x509_cred;
} x509_cred_slot;
static array x509_creds = INIT_ARRAY;
static size_t x509_cred_idx;
static gnutls_dh_params_t dh_params; static gnutls_dh_params_t dh_params;
static gnutls_priority_t priorities_cache; static gnutls_priority_t priorities_cache;
static bool ConnSSL_LoadServerKey_gnutls PARAMS(( void )); static bool ConnSSL_LoadServerKey_gnutls PARAMS(( void ));
@ -266,6 +273,20 @@ void ConnSSL_Free(CONNECTION *c)
gnutls_bye(sess, GNUTLS_SHUT_RDWR); gnutls_bye(sess, GNUTLS_SHUT_RDWR);
gnutls_deinit(sess); gnutls_deinit(sess);
} }
x509_cred_slot *slot = array_get(&x509_creds, sizeof(x509_cred_slot), c->ssl_state.x509_cred_idx);
assert(slot != NULL);
assert(slot->refcnt > 0);
assert(slot->x509_cred != NULL);
slot->refcnt--;
if ((c->ssl_state.x509_cred_idx != x509_cred_idx) && (slot->refcnt <= 0)) {
Log(LOG_INFO, "Discarding X509 certificate credentials from slot %zd.",
c->ssl_state.x509_cred_idx);
/* TODO/FIXME: DH parameters will still leak memory. */
gnutls_certificate_free_keys(slot->x509_cred);
gnutls_certificate_free_credentials(slot->x509_cred);
slot->x509_cred = NULL;
slot->refcnt = 0;
}
#endif #endif
assert(Conn_OPTION_ISSET(c, CONN_SSL)); assert(Conn_OPTION_ISSET(c, CONN_SSL));
/* can't just set bitmask to 0 -- there are other, non-ssl related flags, e.g. CONN_ZIP. */ /* can't just set bitmask to 0 -- there are other, non-ssl related flags, e.g. CONN_ZIP. */
@ -348,17 +369,13 @@ out:
int err; int err;
static bool initialized; static bool initialized;
if (initialized) { if (!initialized) {
/* TODO: cannot reload gnutls keys: can't simply free x509 err = gnutls_global_init();
* context -- it may still be in use */ if (err) {
return false; Log(LOG_ERR, "Failed to initialize GnuTLS: %s",
} gnutls_strerror(err));
goto out;
err = gnutls_global_init(); }
if (err) {
Log(LOG_ERR, "Failed to initialize GnuTLS: %s",
gnutls_strerror(err));
goto out;
} }
if (!ConnSSL_LoadServerKey_gnutls()) if (!ConnSSL_LoadServerKey_gnutls())
@ -389,6 +406,9 @@ ConnSSL_LoadServerKey_gnutls(void)
int err; int err;
const char *cert_file; const char *cert_file;
x509_cred_slot *slot = NULL;
gnutls_certificate_credentials_t x509_cred;
err = gnutls_certificate_allocate_credentials(&x509_cred); err = gnutls_certificate_allocate_credentials(&x509_cred);
if (err < 0) { if (err < 0) {
Log(LOG_ERR, "Failed to allocate certificate credentials: %s", Log(LOG_ERR, "Failed to allocate certificate credentials: %s",
@ -419,6 +439,42 @@ ConnSSL_LoadServerKey_gnutls(void)
gnutls_strerror(err)); gnutls_strerror(err));
return false; return false;
} }
/* Free currently active x509 context (if any) unless it is still in use */
slot = array_get(&x509_creds, sizeof(x509_cred_slot), x509_cred_idx);
if ((slot != NULL) && (slot->refcnt <= 0) && (slot->x509_cred != NULL)) {
Log(LOG_INFO, "Discarding X509 certificate credentials from slot %zd.", x509_cred_idx);
/* TODO/FIXME: DH parameters will still leak memory. */
gnutls_certificate_free_keys(slot->x509_cred);
gnutls_certificate_free_credentials(slot->x509_cred);
slot->x509_cred = NULL;
slot->refcnt = 0;
}
/* Find free slot */
x509_cred_idx = (size_t) -1;
size_t i;
for (slot = array_start(&x509_creds), i = 0;
i < array_length(&x509_creds, sizeof(x509_cred_slot));
slot++, i++) {
if (slot->refcnt <= 0) {
x509_cred_idx = i;
break;
}
}
/* ... allocate new slot otherwise. */
if (x509_cred_idx == (size_t) -1) {
x509_cred_idx = array_length(&x509_creds, sizeof(x509_cred_slot));
slot = array_alloc(&x509_creds, sizeof(x509_cred_slot), x509_cred_idx);
if (slot == NULL) {
Log(LOG_ERR, "Failed to allocate new slot for certificate credentials");
return false;
}
}
Log(LOG_INFO, "Storing new X509 certificate credentials in slot %zd.", x509_cred_idx);
slot->x509_cred = x509_cred;
slot->refcnt = 0;
return true; return true;
} }
#endif #endif
@ -520,8 +576,13 @@ ConnSSL_Init_SSL(CONNECTION *c)
(gnutls_transport_ptr_t) (long) c->sock); (gnutls_transport_ptr_t) (long) c->sock);
gnutls_certificate_server_set_request(c->ssl_state.gnutls_session, gnutls_certificate_server_set_request(c->ssl_state.gnutls_session,
GNUTLS_CERT_REQUEST); GNUTLS_CERT_REQUEST);
Log(LOG_INFO, "Using X509 credentials from slot %zd", x509_cred_idx);
c->ssl_state.x509_cred_idx = x509_cred_idx;
x509_cred_slot *slot = array_get(&x509_creds, sizeof(x509_cred_slot), x509_cred_idx);
slot->refcnt++;
ret = gnutls_credentials_set(c->ssl_state.gnutls_session, ret = gnutls_credentials_set(c->ssl_state.gnutls_session,
GNUTLS_CRD_CERTIFICATE, x509_cred); GNUTLS_CRD_CERTIFICATE, slot->x509_cred);
if (ret != 0) { if (ret != 0) {
Log(LOG_ERR, "Failed to set SSL credentials: %s", Log(LOG_ERR, "Failed to set SSL credentials: %s",
gnutls_strerror(ret)); gnutls_strerror(ret));