X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Fconn-ssl.c;h=3f482dc7ff60263ce40822d41d20c4a7b345e409;hp=1721aac560be1b6f8585754a35a42b48c61dc53e;hb=e7cb9b1a001a97b1edf0e862808cbd0be5264a7a;hpb=dd4c60cf392ecabeeadd616c7fc561af87a7cef5 diff --git a/src/ngircd/conn-ssl.c b/src/ngircd/conn-ssl.c index 1721aac5..3f482dc7 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; @@ -58,10 +64,11 @@ static bool ConnSSL_LoadServerKey_openssl PARAMS(( SSL_CTX *c )); static gnutls_certificate_credentials_t x509_cred; static gnutls_dh_params_t dh_params; +static gnutls_priority_t priorities_cache; static bool ConnSSL_LoadServerKey_gnutls PARAMS(( void )); #endif -#define CERTFP_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 )); @@ -113,17 +120,26 @@ out: #ifdef HAVE_LIBSSL +/** + * Log OpenSSL error message. + * + * @param msg The error message. + * @param info Additional information text or NULL. + */ static void -LogOpenSSLError( const char *msg, const char *msg2 ) +LogOpenSSLError(const char *error, const char *info) { unsigned long err = ERR_get_error(); - char * errmsg = err ? ERR_error_string(err, NULL) : "Unable to determine error"; + char * errmsg = err + ? ERR_error_string(err, NULL) + : "Unable to determine error"; + + assert(error != NULL); - if (!msg) msg = "SSL Error"; - if (msg2) - Log( LOG_ERR, "%s: %s: %s", msg, msg2, errmsg); + if (info) + Log(LOG_ERR, "%s: %s (%s)", error, info, errmsg); else - Log( LOG_ERR, "%s: %s", msg, errmsg); + Log(LOG_ERR, "%s: %s", error, errmsg); } @@ -142,7 +158,7 @@ pem_passwd_cb(char *buf, int size, int rwflag, void *password) LogDebug("pem_passwd_cb buf size %d, array size %d", size, passlen); assert(passlen >= 0); if (passlen <= 0) { - Log(LOG_ERR, "pem_passwd_cb: password required, but not set"); + Log(LOG_ERR, "PEM password required but not set [in pem_passwd_cb()]!"); return 0; } size = passlen > size ? size : passlen; @@ -177,7 +193,7 @@ Load_DH_params(void) } dh_params = PEM_read_DHparams(fp, NULL, NULL, NULL); if (!dh_params) { - Log(LOG_ERR, "%s: PEM_read_DHparams failed!", + Log(LOG_ERR, "%s: Failed to read SSL DH parameters!", Conf_SSLOptions.DHFile); ret = false; } @@ -191,7 +207,8 @@ Load_DH_params(void) err = gnutls_dh_params_init(&tmp_dh_params); if (err < 0) { - Log(LOG_ERR, "gnutls_dh_params_init: %s", gnutls_strerror(err)); + Log(LOG_ERR, "Failed to initialize SSL DH parameters: %s", + gnutls_strerror(err)); return false; } if (Conf_SSLOptions.DHFile) { @@ -204,7 +221,9 @@ Load_DH_params(void) if (err == 0) need_dhgenerate = false; else - Log(LOG_ERR, "gnutls_dh_params_init: %s", gnutls_strerror(err)); + Log(LOG_ERR, + "Failed to initialize SSL DH parameters: %s", + gnutls_strerror(err)); memset(dhparms.data, 0, size); free(dhparms.data); @@ -216,7 +235,8 @@ Load_DH_params(void) DH_BITS); err = gnutls_dh_params_generate2(tmp_dh_params, DH_BITS); if (err < 0) { - Log(LOG_ERR, "gnutls_dh_params_generate2: %s", gnutls_strerror(err)); + Log(LOG_ERR, "Failed to generate SSL DH parameters: %s", + gnutls_strerror(err)); return false; } } @@ -264,16 +284,20 @@ 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?"); /* - * it is probably best to fail and let the user install EGD or a similar program if no kernel random device is available. - * According to OpenSSL RAND_egd(3): "The automatic query of /var/run/egd-pool et al was added in OpenSSL 0.9.7"; + * it is probably best to fail and let the user install EGD or + * a similar program if no kernel random device is available. + * According to OpenSSL RAND_egd(3): "The automatic query of + * /var/run/egd-pool et al was added in OpenSSL 0.9.7"; * so it makes little sense to deal with PRNGD seeding ourselves. */ array_free(&Conf_SSLOptions.ListenPorts); @@ -282,20 +306,38 @@ ConnSSL_InitLibrary( void ) newctx = SSL_CTX_new(SSLv23_method()); if (!newctx) { - LogOpenSSLError("SSL_CTX_new()", NULL); + LogOpenSSLError("Failed to create SSL context", NULL); array_free(&Conf_SSLOptions.ListenPorts); 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_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); @@ -305,22 +347,37 @@ out: #ifdef HAVE_LIBGNUTLS int err; static bool initialized; - if (initialized) /* TODO: cannot reload gnutls keys: can't simply free x509 context -- it may still be in use */ + + if (initialized) { + /* TODO: cannot reload gnutls keys: can't simply free x509 + * context -- it may still be in use */ return false; + } err = gnutls_global_init(); if (err) { - Log(LOG_ERR, "gnutls_global_init(): %s", gnutls_strerror(err)); - array_free(&Conf_SSLOptions.ListenPorts); - return false; + Log(LOG_ERR, "Failed to initialize GnuTLS: %s", + gnutls_strerror(err)); + goto out; } - if (!ConnSSL_LoadServerKey_gnutls()) { - array_free(&Conf_SSLOptions.ListenPorts); - return false; + + if (!ConnSSL_LoadServerKey_gnutls()) + goto out; + + 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)); + + Log(LOG_INFO, "GnuTLS %s initialized.", gnutls_check_version(NULL)); initialized = true; return true; +out: + array_free(&Conf_SSLOptions.ListenPorts); + return false; #endif } @@ -334,7 +391,8 @@ ConnSSL_LoadServerKey_gnutls(void) err = gnutls_certificate_allocate_credentials(&x509_cred); if (err < 0) { - Log(LOG_ERR, "gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err)); + Log(LOG_ERR, "Failed to allocate certificate credentials: %s", + gnutls_strerror(err)); return false; } @@ -346,7 +404,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; @@ -354,8 +412,11 @@ ConnSSL_LoadServerKey_gnutls(void) gnutls_certificate_set_dh_params(x509_cred, dh_params); err = gnutls_certificate_set_x509_key_file(x509_cred, cert_file, Conf_SSLOptions.KeyFile, GNUTLS_X509_FMT_PEM); if (err < 0) { - Log(LOG_ERR, "gnutls_certificate_set_x509_key_file (cert %s, key %s): %s", - cert_file, Conf_SSLOptions.KeyFile ? Conf_SSLOptions.KeyFile : "(NULL)", gnutls_strerror(err)); + Log(LOG_ERR, + "Failed to set certificate key file (cert %s, key %s): %s", + cert_file, + Conf_SSLOptions.KeyFile ? Conf_SSLOptions.KeyFile : "(NULL)", + gnutls_strerror(err)); return false; } return true; @@ -380,26 +441,26 @@ ConnSSL_LoadServerKey_openssl(SSL_CTX *ctx) if (SSL_CTX_use_PrivateKey_file(ctx, Conf_SSLOptions.KeyFile, SSL_FILETYPE_PEM) != 1) { array_free_wipe(&Conf_SSLOptions.KeyFilePassword); - LogOpenSSLError("SSL_CTX_use_PrivateKey_file", Conf_SSLOptions.KeyFile); + LogOpenSSLError("Failed to add private key", Conf_SSLOptions.KeyFile); return false; } cert_key = Conf_SSLOptions.CertFile ? Conf_SSLOptions.CertFile:Conf_SSLOptions.KeyFile; if (SSL_CTX_use_certificate_chain_file(ctx, cert_key) != 1) { array_free_wipe(&Conf_SSLOptions.KeyFilePassword); - LogOpenSSLError("SSL_CTX_use_certificate_file", cert_key); + LogOpenSSLError("Failed to load certificate chain", cert_key); return false; } array_free_wipe(&Conf_SSLOptions.KeyFilePassword); if (!SSL_CTX_check_private_key(ctx)) { - LogOpenSSLError("Server Private Key does not match certificate", NULL); + LogOpenSSLError("Server private key does not match certificate", NULL); return false; } if (Load_DH_params()) { if (SSL_CTX_set_tmp_dh(ctx, dh_params) != 1) - LogOpenSSLError("Error setting DH Parameters", Conf_SSLOptions.DHFile); + LogOpenSSLError("Error setting DH parameters", Conf_SSLOptions.DHFile); /* don't return false here: the non-DH modes will still work */ DH_free(dh_params); dh_params = NULL; @@ -413,10 +474,14 @@ static bool ConnSSL_Init_SSL(CONNECTION *c) { int ret; + + LogDebug("Initializing SSL ..."); assert(c != NULL); + #ifdef HAVE_LIBSSL if (!ssl_ctx) { - Log(LOG_ERR, "Cannot init ssl_ctx: OpenSSL initialization failed at startup"); + Log(LOG_ERR, + "Can't initialize SSL context, OpenSSL initialization failed at startup!"); return false; } assert(c->ssl_state.ssl == NULL); @@ -424,21 +489,24 @@ ConnSSL_Init_SSL(CONNECTION *c) c->ssl_state.ssl = SSL_new(ssl_ctx); if (!c->ssl_state.ssl) { - LogOpenSSLError("SSL_new()", NULL); + 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) { - LogOpenSSLError("SSL_set_fd()", NULL); + LogOpenSSLError("Failed to set SSL file descriptor", NULL); ConnSSL_Free(c); return false; } #endif #ifdef HAVE_LIBGNUTLS - ret = gnutls_set_default_priority(c->ssl_state.gnutls_session); - if (ret < 0) { - Log(LOG_ERR, "gnutls_set_default_priority: %s", gnutls_strerror(ret)); + 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; } @@ -448,17 +516,20 @@ 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, "gnutls_credentials_set: %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); + 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)); 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; } @@ -472,7 +543,8 @@ ConnSSL_PrepareConnect(CONNECTION *c, UNUSED CONF_SERVER *s) err = gnutls_init(&c->ssl_state.gnutls_session, GNUTLS_CLIENT); if (err) { - Log(LOG_ERR, "gnutls_init: %s", gnutls_strerror(err)); + Log(LOG_ERR, "Failed to initialize new SSL session: %s", + gnutls_strerror(err)); return false; } #endif @@ -489,13 +561,13 @@ ConnSSL_PrepareConnect(CONNECTION *c, UNUSED CONF_SERVER *s) /** - * Check and handle error return codes after failed calls to SSL/TLS functions. + * Check and handle error return codes after failed calls to SSL functions. * * OpenSSL: * SSL_connect(), SSL_accept(), SSL_do_handshake(), SSL_read(), SSL_peek(), or * SSL_write() on ssl. * - * GNUTLS: + * GnuTLS: * gnutlsssl_read(), gnutls_write() or gnutls_handshake(). * * @param c The connection handle. @@ -512,6 +584,7 @@ ConnSSL_HandleError(CONNECTION * c, const int code, const char *fname) int real_errno = errno; ret = SSL_get_error(c->ssl_state.ssl, code); + switch (ret) { case SSL_ERROR_WANT_READ: io_event_del(c->sock, IO_WANTWRITE); @@ -523,32 +596,33 @@ ConnSSL_HandleError(CONNECTION * c, const int code, const char *fname) case SSL_ERROR_NONE: return 0; /* try again later */ case SSL_ERROR_ZERO_RETURN: - LogDebug("TLS/SSL connection shut down normally"); + LogDebug("SSL connection shut down normally."); break; case SSL_ERROR_SYSCALL: /* SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT, * and SSL_ERROR_WANT_X509_LOOKUP */ sslerr = ERR_get_error(); if (sslerr) { - Log(LOG_ERR, "%s: %s", fname, - ERR_error_string(sslerr, NULL)); + Log(LOG_ERR, "SSL error: %s [in %s()]!", + ERR_error_string(sslerr, NULL), fname); } else { - switch (code) { /* EOF that violated protocol */ case 0: - Log(LOG_ERR, "%s: Client Disconnected", fname); + Log(LOG_ERR, + "SSL error, client disconnected [in %s()]!", + fname); break; case -1: /* low level socket I/O error, check errno */ - Log(LOG_ERR, "%s: %s", fname, - strerror(real_errno)); + Log(LOG_ERR, "SSL error: %s [in %s()]!", + strerror(real_errno), fname); } } break; case SSL_ERROR_SSL: - LogOpenSSLError("TLS/SSL Protocol Error", fname); + LogOpenSSLError("SSL protocol error", fname); break; default: - Log(LOG_ERR, "%s: Unknown error %d!", fname, ret); + Log(LOG_ERR, "Unknown SSL error %d [in %s()]!", ret, fname); } ConnSSL_Free(c); return -1; @@ -568,7 +642,8 @@ ConnSSL_HandleError(CONNECTION * c, const int code, const char *fname) default: assert(code < 0); if (gnutls_error_is_fatal(code)) { - Log(LOG_ERR, "%s: %s", fname, gnutls_strerror(code)); + Log(LOG_ERR, "SSL error: %s [%s].", + gnutls_strerror(code), fname); ConnSSL_Free(c); return -1; } @@ -617,11 +692,11 @@ ConnSSL_Accept( CONNECTION *c ) #ifdef HAVE_LIBGNUTLS int err = gnutls_init(&c->ssl_state.gnutls_session, GNUTLS_SERVER); if (err) { - Log(LOG_ERR, "gnutls_init: %s", gnutls_strerror(err)); + Log(LOG_ERR, "Failed to initialize new SSL session: %s", + gnutls_strerror(err)); return false; } #endif - LogDebug("ConnSSL_Accept: Initializing SSL data"); if (!ConnSSL_Init_SSL(c)) return -1; } @@ -655,7 +730,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; } @@ -669,7 +744,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) @@ -682,14 +758,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; } @@ -699,7 +777,7 @@ ConnSSL_InitCertFp( CONNECTION *c ) assert(c->ssl_state.fingerprint == NULL); - c->ssl_state.fingerprint = malloc(CERTFP_LEN); + c->ssl_state.fingerprint = malloc(SHA256_STRING_LEN); if (!c->ssl_state.fingerprint) return 0; @@ -834,7 +912,7 @@ bool ConnSSL_SetCertFp(CONNECTION *c, const char *fingerprint) { assert (c != NULL); - c->ssl_state.fingerprint = strdup(fingerprint); + c->ssl_state.fingerprint = strndup(fingerprint, SHA256_STRING_LEN - 1); return c->ssl_state.fingerprint != NULL; } #else @@ -847,5 +925,3 @@ ConnSSL_InitLibrary(void) #endif /* SSL_SUPPORT */ /* -eof- */ - -