X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fngircd%2Fconn-ssl.c;h=4dd335ea9209f6a01fa11f459fb3e0d976e44a21;hb=dc6807338e240d8093f43337dab7bfe488c35c4a;hp=595cb615e6e78ed192f154a016c5ea5d3778e2d2;hpb=849f85a05c17828c592bed26bd99707f211fad1c;p=ngircd-alex.git diff --git a/src/ngircd/conn-ssl.c b/src/ngircd/conn-ssl.c index 595cb615..4dd335ea 100644 --- a/src/ngircd/conn-ssl.c +++ b/src/ngircd/conn-ssl.c @@ -1,6 +1,13 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c) 2005-2008 Florian Westphal + * Copyright (c)2005-2008 Florian Westphal (fw@strlen.de). + * Copyright (c)2008-2014 Alexander Barton (alex@barton.de) and Contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * Please read the file COPYING, README and AUTHORS for more information. */ #include "portab.h" @@ -10,7 +17,6 @@ * SSL wrapper functions */ -#include "imp.h" #include "conf-ssl.h" #ifdef SSL_SUPPORT @@ -29,7 +35,6 @@ #include "conn-ssl.h" #include "log.h" -#include "exp.h" #include "defines.h" extern struct SSLOptions Conf_SSLOptions; @@ -37,6 +42,7 @@ extern struct SSLOptions Conf_SSLOptions; #ifdef HAVE_LIBSSL #include #include +#include static SSL_CTX * ssl_ctx; static DH *dh_params; @@ -56,12 +62,21 @@ static bool ConnSSL_LoadServerKey_openssl PARAMS(( SSL_CTX *c )); #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; + gnutls_dh_params_t dh_params; +} x509_cred_slot; + +static array x509_creds = INIT_ARRAY; +static size_t x509_cred_idx; + static gnutls_dh_params_t dh_params; +static gnutls_priority_t priorities_cache = NULL; static bool ConnSSL_LoadServerKey_gnutls PARAMS(( void )); #endif -#define SHA1_STRING_LEN (20 * 2 + 1) +#define SHA256_STRING_LEN (32 * 2 + 1) static bool ConnSSL_Init_SSL PARAMS(( CONNECTION *c )); static int ConnectAccept PARAMS(( CONNECTION *c, bool connect )); @@ -259,6 +274,21 @@ void ConnSSL_Free(CONNECTION *c) gnutls_bye(sess, GNUTLS_SHUT_RDWR); 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); + gnutls_certificate_free_keys(slot->x509_cred); + gnutls_certificate_free_credentials(slot->x509_cred); + slot->x509_cred = NULL; + gnutls_dh_params_deinit(slot->dh_params); + slot->dh_params = NULL; + slot->refcnt = 0; + } #endif 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. */ @@ -277,10 +307,12 @@ ConnSSL_InitLibrary( void ) #ifdef HAVE_LIBSSL SSL_CTX *newctx; +#if OPENSSL_API_COMPAT < 0x10100000L if (!ssl_ctx) { SSL_library_init(); SSL_load_error_strings(); } +#endif if (!RAND_status()) { Log(LOG_ERR, "OpenSSL PRNG not seeded: /dev/urandom missing?"); @@ -302,16 +334,33 @@ ConnSSL_InitLibrary( void ) return false; } - if (!ConnSSL_LoadServerKey_openssl(newctx)) + if (!ConnSSL_LoadServerKey_openssl(newctx)) { + /* Failed to read new key but an old ssl context + * already exists -> reuse old context */ + if (ssl_ctx) { + SSL_CTX_free(newctx); + Log(LOG_WARNING, + "Re-Initializing of SSL failed, using old keys!"); + return true; + } + /* No preexisting old context -> error. */ + goto out; + } + + if (SSL_CTX_set_cipher_list(newctx, Conf_SSLOptions.CipherList) == 0) { + Log(LOG_ERR, "Failed to apply OpenSSL cipher list \"%s\"!", + Conf_SSLOptions.CipherList); goto out; + } + SSL_CTX_set_session_id_context(newctx, (unsigned char *)"ngircd", 6); SSL_CTX_set_options(newctx, SSL_OP_SINGLE_DH_USE|SSL_OP_NO_SSLv2); SSL_CTX_set_mode(newctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_verify(newctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, Verify_openssl); SSL_CTX_free(ssl_ctx); ssl_ctx = newctx; - Log(LOG_INFO, "%s initialized.", SSLeay_version(SSLEAY_VERSION)); + Log(LOG_INFO, "%s initialized.", OpenSSL_version(OPENSSL_VERSION)); return true; out: SSL_CTX_free(newctx); @@ -322,26 +371,35 @@ out: int err; static bool initialized; - if (initialized) { - /* TODO: cannot reload gnutls keys: can't simply free x509 - * context -- it may still be in use */ - return false; + if (!initialized) { + err = gnutls_global_init(); + if (err) { + 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)); - array_free(&Conf_SSLOptions.ListenPorts); - return false; + if (!ConnSSL_LoadServerKey_gnutls()) + goto out; + + if (priorities_cache != NULL) { + gnutls_priority_deinit(priorities_cache); } - if (!ConnSSL_LoadServerKey_gnutls()) { - array_free(&Conf_SSLOptions.ListenPorts); - return false; + if (gnutls_priority_init(&priorities_cache, Conf_SSLOptions.CipherList, + NULL) != GNUTLS_E_SUCCESS) { + Log(LOG_ERR, + "Failed to apply GnuTLS cipher list \"%s\"!", + Conf_SSLOptions.CipherList); + goto out; } + Log(LOG_INFO, "GnuTLS %s initialized.", gnutls_check_version(NULL)); initialized = true; return true; +out: + array_free(&Conf_SSLOptions.ListenPorts); + return false; #endif } @@ -353,6 +411,9 @@ ConnSSL_LoadServerKey_gnutls(void) int err; const char *cert_file; + x509_cred_slot *slot = NULL; + gnutls_certificate_credentials_t x509_cred; + err = gnutls_certificate_allocate_credentials(&x509_cred); if (err < 0) { Log(LOG_ERR, "Failed to allocate certificate credentials: %s", @@ -368,7 +429,7 @@ ConnSSL_LoadServerKey_gnutls(void) if (array_bytes(&Conf_SSLOptions.KeyFilePassword)) Log(LOG_WARNING, - "Ignoring KeyFilePassword: Not supported by GnuTLS."); + "Ignoring SSL \"KeyFilePassword\": Not supported by GnuTLS."); if (!Load_DH_params()) return false; @@ -383,6 +444,43 @@ ConnSSL_LoadServerKey_gnutls(void) gnutls_strerror(err)); 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); + gnutls_certificate_free_keys(slot->x509_cred); + gnutls_certificate_free_credentials(slot->x509_cred); + slot->x509_cred = NULL; + gnutls_dh_params_deinit(slot->dh_params); + slot->dh_params = 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; } #endif @@ -438,7 +536,10 @@ static bool ConnSSL_Init_SSL(CONNECTION *c) { int ret; + + LogDebug("Initializing SSL ..."); assert(c != NULL); + #ifdef HAVE_LIBSSL if (!ssl_ctx) { Log(LOG_ERR, @@ -453,6 +554,7 @@ ConnSSL_Init_SSL(CONNECTION *c) LogOpenSSLError("Failed to create SSL structure", NULL); return false; } + Conn_OPTION_ADD(c, CONN_SSL); ret = SSL_set_fd(c->ssl_state.ssl, c->sock); if (ret != 1) { @@ -462,9 +564,10 @@ ConnSSL_Init_SSL(CONNECTION *c) } #endif #ifdef HAVE_LIBGNUTLS - ret = gnutls_set_default_priority(c->ssl_state.gnutls_session); - if (ret < 0) { - Log(LOG_ERR, "Failed to set GnuTLS default priority: %s", + Conn_OPTION_ADD(c, CONN_SSL); + ret = gnutls_priority_set(c->ssl_state.gnutls_session, priorities_cache); + if (ret != GNUTLS_E_SUCCESS) { + Log(LOG_ERR, "Failed to set GnuTLS session priorities: %s", gnutls_strerror(ret)); ConnSSL_Free(c); return false; @@ -475,17 +578,25 @@ ConnSSL_Init_SSL(CONNECTION *c) * There doesn't seem to be an alternate GNUTLS API we could use instead, see e.g. * http://www.mail-archive.com/help-gnutls@gnu.org/msg00286.html */ - gnutls_transport_set_ptr(c->ssl_state.gnutls_session, (gnutls_transport_ptr_t) (long) c->sock); - gnutls_certificate_server_set_request(c->ssl_state.gnutls_session, GNUTLS_CERT_REQUEST); - ret = gnutls_credentials_set(c->ssl_state.gnutls_session, GNUTLS_CRD_CERTIFICATE, x509_cred); - if (ret < 0) { - Log(LOG_ERR, "Failed to set SSL credentials: %s", gnutls_strerror(ret)); + gnutls_transport_set_ptr(c->ssl_state.gnutls_session, + (gnutls_transport_ptr_t) (long) c->sock); + gnutls_certificate_server_set_request(c->ssl_state.gnutls_session, + 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, + GNUTLS_CRD_CERTIFICATE, slot->x509_cred); + if (ret != 0) { + Log(LOG_ERR, "Failed to set SSL credentials: %s", + gnutls_strerror(ret)); ConnSSL_Free(c); return false; } gnutls_dh_set_prime_bits(c->ssl_state.gnutls_session, DH_BITS_MIN); #endif - Conn_OPTION_ADD(c, CONN_SSL); return true; } @@ -653,7 +764,6 @@ ConnSSL_Accept( CONNECTION *c ) return false; } #endif - LogDebug("Initializing SSL data ..."); if (!ConnSSL_Init_SSL(c)) return -1; } @@ -687,7 +797,7 @@ ConnSSL_InitCertFp( CONNECTION *c ) if (!cert) return 0; - if (!X509_digest(cert, EVP_sha1(), digest, &digest_size)) { + if (!X509_digest(cert, EVP_sha256(), digest, &digest_size)) { X509_free(cert); return 0; } @@ -701,7 +811,8 @@ ConnSSL_InitCertFp( CONNECTION *c ) unsigned char digest[MAX_HASH_SIZE]; size_t digest_size; - if (gnutls_certificate_type_get(c->ssl_state.gnutls_session) != GNUTLS_CRT_X509) + if (gnutls_certificate_type_get(c->ssl_state.gnutls_session) != + GNUTLS_CRT_X509) return 0; if (gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS) @@ -714,14 +825,16 @@ ConnSSL_InitCertFp( CONNECTION *c ) gnutls_x509_crt_deinit(cert); return 0; } - - if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) { + + if (gnutls_x509_crt_import(cert, &cert_list[0], + GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) { gnutls_x509_crt_deinit(cert); return 0; } digest_size = sizeof(digest); - if (gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, digest, &digest_size)) { + if (gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, digest, + &digest_size)) { gnutls_x509_crt_deinit(cert); return 0; } @@ -731,7 +844,7 @@ ConnSSL_InitCertFp( CONNECTION *c ) assert(c->ssl_state.fingerprint == NULL); - c->ssl_state.fingerprint = malloc(SHA1_STRING_LEN); + c->ssl_state.fingerprint = malloc(SHA256_STRING_LEN); if (!c->ssl_state.fingerprint) return 0; @@ -866,7 +979,7 @@ bool ConnSSL_SetCertFp(CONNECTION *c, const char *fingerprint) { assert (c != NULL); - c->ssl_state.fingerprint = strndup(fingerprint, SHA1_STRING_LEN - 1); + c->ssl_state.fingerprint = strndup(fingerprint, SHA256_STRING_LEN - 1); return c->ssl_state.fingerprint != NULL; } #else @@ -879,5 +992,3 @@ ConnSSL_InitLibrary(void) #endif /* SSL_SUPPORT */ /* -eof- */ - -