Implemented error handling and certificate verification for SSL.

This commit is contained in:
Gunnar Beutner 2012-04-24 14:54:05 +02:00
parent b89c6abab6
commit 4f6aa3236c
4 changed files with 85 additions and 8 deletions

View File

@ -44,3 +44,13 @@ string PosixException::FormatErrorCode(int code)
{ {
return strerror(code); return strerror(code);
} }
string OpenSSLException::FormatErrorCode(int code)
{
char *message = ERR_error_string(code, NULL);
if (message == NULL)
message = "Unknown error.";
return message;
}

View File

@ -73,6 +73,17 @@ public:
static string FormatErrorCode(int code); 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 */ #endif /* EXCEPTION_H */

View File

@ -2,19 +2,22 @@
using namespace icinga; using namespace icinga;
int I2_EXPORT TLSClient::m_SSLIndex;
bool I2_EXPORT TLSClient::m_SSLIndexInitialized = false;
TLSClient::TLSClient(TCPClientRole role, shared_ptr<SSL_CTX> sslContext) : TCPClient(role) TLSClient::TLSClient(TCPClientRole role, shared_ptr<SSL_CTX> sslContext) : TCPClient(role)
{ {
m_SSLContext = sslContext; m_SSLContext = sslContext;
} }
shared_ptr<X509> TLSClient::GetClientCertificate(void) const X509 *TLSClient::GetClientCertificate(void) const
{ {
return shared_ptr<X509>(SSL_get_certificate(m_SSL.get()), X509_free); return SSL_get_certificate(m_SSL.get());
} }
shared_ptr<X509> TLSClient::GetPeerCertificate(void) const X509 *TLSClient::GetPeerCertificate(void) const
{ {
return shared_ptr<X509>(SSL_get_peer_certificate(m_SSL.get()), X509_free); return SSL_get_peer_certificate(m_SSL.get());
} }
void TLSClient::Start(void) void TLSClient::Start(void)
@ -26,6 +29,19 @@ void TLSClient::Start(void)
if (!m_SSL) if (!m_SSL)
; /* TODO: deal with error */ ; /* 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); BIO *bio = BIO_new_socket(GetFD(), 0);
SSL_set_bio(m_SSL.get(), bio, bio); SSL_set_bio(m_SSL.get(), bio, bio);
@ -53,7 +69,7 @@ int TLSClient::ReadableEventHandler(const EventArgs& ea)
return 0; return 0;
default: default:
/* TODO: deal with error */ HandleSSLError();
return 0; return 0;
} }
@ -84,7 +100,7 @@ int TLSClient::WritableEventHandler(const EventArgs& ea)
return 0; return 0;
default: default:
/* TODO: deal with error */ HandleSSLError();
return 0; return 0;
} }
@ -113,7 +129,39 @@ void TLSClient::CloseInternal(bool from_dtor)
TCPClient::CloseInternal(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<SSL_CTX> sslContext) TCPClient::Ptr icinga::TLSClientFactory(TCPClientRole role, shared_ptr<SSL_CTX> sslContext)
{ {
return make_shared<TLSClient>(role, sslContext); return make_shared<TLSClient>(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;
}

View File

@ -16,16 +16,24 @@ private:
shared_ptr<SSL_CTX> m_SSLContext; shared_ptr<SSL_CTX> m_SSLContext;
shared_ptr<SSL> m_SSL; shared_ptr<SSL> m_SSL;
static int m_SSLIndex;
static bool m_SSLIndexInitialized;
virtual int ReadableEventHandler(const EventArgs& ea); virtual int ReadableEventHandler(const EventArgs& ea);
virtual int WritableEventHandler(const EventArgs& ea); virtual int WritableEventHandler(const EventArgs& ea);
virtual void CloseInternal(bool from_dtor); virtual void CloseInternal(bool from_dtor);
static int SSLVerifyCertificate(int ok, X509_STORE_CTX *x509Context);
protected:
void HandleSSLError(void);
public: public:
TLSClient(TCPClientRole role, shared_ptr<SSL_CTX> sslContext); TLSClient(TCPClientRole role, shared_ptr<SSL_CTX> sslContext);
shared_ptr<X509> GetClientCertificate(void) const; X509 *GetClientCertificate(void) const;
shared_ptr<X509> GetPeerCertificate(void) const; X509 *GetPeerCertificate(void) const;
virtual void Start(void); virtual void Start(void);