From 4f6aa3236c64a44a4044beab60075ae0c4a131f4 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 24 Apr 2012 14:54:05 +0200 Subject: [PATCH] Implemented error handling and certificate verification for SSL. --- base/exception.cpp | 10 ++++++++ base/exception.h | 11 +++++++++ base/tlsclient.cpp | 60 +++++++++++++++++++++++++++++++++++++++++----- base/tlsclient.h | 12 ++++++++-- 4 files changed, 85 insertions(+), 8 deletions(-) diff --git a/base/exception.cpp b/base/exception.cpp index f421a7b90..95d46e231 100644 --- a/base/exception.cpp +++ b/base/exception.cpp @@ -44,3 +44,13 @@ string PosixException::FormatErrorCode(int code) { return strerror(code); } + +string OpenSSLException::FormatErrorCode(int code) +{ + char *message = ERR_error_string(code, NULL); + + if (message == NULL) + message = "Unknown error."; + + return message; +} diff --git a/base/exception.h b/base/exception.h index b97846c92..6cce39b91 100644 --- a/base/exception.h +++ b/base/exception.h @@ -73,6 +73,17 @@ public: static string FormatErrorCode(int code); }; +class OpenSSLException : public Exception +{ +public: + inline OpenSSLException(const string& message, int errorCode) + { + SetMessage(message + ": " + FormatErrorCode(errorCode)); + } + + static string FormatErrorCode(int code); +}; + } #endif /* EXCEPTION_H */ diff --git a/base/tlsclient.cpp b/base/tlsclient.cpp index f749d1397..7a6d75e0e 100644 --- a/base/tlsclient.cpp +++ b/base/tlsclient.cpp @@ -2,19 +2,22 @@ using namespace icinga; +int I2_EXPORT TLSClient::m_SSLIndex; +bool I2_EXPORT TLSClient::m_SSLIndexInitialized = false; + TLSClient::TLSClient(TCPClientRole role, shared_ptr sslContext) : TCPClient(role) { m_SSLContext = sslContext; } -shared_ptr TLSClient::GetClientCertificate(void) const +X509 *TLSClient::GetClientCertificate(void) const { - return shared_ptr(SSL_get_certificate(m_SSL.get()), X509_free); + return SSL_get_certificate(m_SSL.get()); } -shared_ptr TLSClient::GetPeerCertificate(void) const +X509 *TLSClient::GetPeerCertificate(void) const { - return shared_ptr(SSL_get_peer_certificate(m_SSL.get()), X509_free); + return SSL_get_peer_certificate(m_SSL.get()); } void TLSClient::Start(void) @@ -26,6 +29,19 @@ void TLSClient::Start(void) if (!m_SSL) ; /* TODO: deal with error */ + if (!GetClientCertificate()) + throw InvalidArgumentException("No X509 client certificate was specified."); + + if (!m_SSLIndexInitialized) { + m_SSLIndex = SSL_get_ex_new_index(0, (void *)"TLSClient", NULL, NULL, NULL); + m_SSLIndexInitialized = true; + } + + SSL_set_ex_data(m_SSL.get(), m_SSLIndex, this); + + SSL_set_verify(m_SSL.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + &TLSClient::SSLVerifyCertificate); + BIO *bio = BIO_new_socket(GetFD(), 0); SSL_set_bio(m_SSL.get(), bio, bio); @@ -53,7 +69,7 @@ int TLSClient::ReadableEventHandler(const EventArgs& ea) return 0; default: - /* TODO: deal with error */ + HandleSSLError(); return 0; } @@ -84,7 +100,7 @@ int TLSClient::WritableEventHandler(const EventArgs& ea) return 0; default: - /* TODO: deal with error */ + HandleSSLError(); return 0; } @@ -113,7 +129,39 @@ void TLSClient::CloseInternal(bool from_dtor) TCPClient::CloseInternal(from_dtor); } +void TLSClient::HandleSSLError(void) +{ + int code = ERR_get_error(); + + if (code != 0) { + SocketErrorEventArgs sea; + sea.Code = code; + sea.Message = OpenSSLException::FormatErrorCode(sea.Code); + OnError(sea); + } + + Close(); + return; +} + TCPClient::Ptr icinga::TLSClientFactory(TCPClientRole role, shared_ptr sslContext) { return make_shared(role, sslContext); } + +int TLSClient::SSLVerifyCertificate(int ok, X509_STORE_CTX *x509Context) +{ + SSL *ssl = (SSL *)X509_STORE_CTX_get_ex_data(x509Context, SSL_get_ex_data_X509_STORE_CTX_idx()); + TLSClient *client = (TLSClient *)SSL_get_ex_data(ssl, m_SSLIndex); + + if (client == NULL) + return 0; + + VerifyCertificateEventArgs vcea; + vcea.Source = client->shared_from_this(); + vcea.ValidCertificate = (ok != 0); + vcea.Context = x509Context; + client->OnVerifyCertificate(vcea); + + return (int)vcea.ValidCertificate; +} diff --git a/base/tlsclient.h b/base/tlsclient.h index 8e28b188f..fd248449d 100644 --- a/base/tlsclient.h +++ b/base/tlsclient.h @@ -16,16 +16,24 @@ private: shared_ptr m_SSLContext; shared_ptr m_SSL; + static int m_SSLIndex; + static bool m_SSLIndexInitialized; + virtual int ReadableEventHandler(const EventArgs& ea); virtual int WritableEventHandler(const EventArgs& ea); virtual void CloseInternal(bool from_dtor); + static int SSLVerifyCertificate(int ok, X509_STORE_CTX *x509Context); + +protected: + void HandleSSLError(void); + public: TLSClient(TCPClientRole role, shared_ptr sslContext); - shared_ptr GetClientCertificate(void) const; - shared_ptr GetPeerCertificate(void) const; + X509 *GetClientCertificate(void) const; + X509 *GetPeerCertificate(void) const; virtual void Start(void);