]> granicus.if.org Git - icinga2/commitdiff
Implemented error handling and certificate verification for SSL.
authorGunnar Beutner <gunnar.beutner@netways.de>
Tue, 24 Apr 2012 12:54:05 +0000 (14:54 +0200)
committerGunnar Beutner <gunnar.beutner@netways.de>
Tue, 24 Apr 2012 12:54:05 +0000 (14:54 +0200)
base/exception.cpp
base/exception.h
base/tlsclient.cpp
base/tlsclient.h

index f421a7b90457988689eb9ad13ab0b2b54274aac1..95d46e2319cc0166dd871baa77af41835ef0dcea 100644 (file)
@@ -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;
+}
index b97846c92109808770b43ac024ce1afe2d5afd19..6cce39b91ab0b548059b2a99f20b5d94cce3b25d 100644 (file)
@@ -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 */
index f749d1397545cd83ff8050f4c29141f9c5d25f0c..7a6d75e0e157fd72771fae4a671439baf98cffbe 100644 (file)
@@ -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<SSL_CTX> sslContext) : TCPClient(role)
 {
        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)
@@ -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<SSL_CTX> 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;
+}
index 8e28b188f900e10254ff9bba4237060453bb94dc..fd248449d2776ac38dd1bd7aabe5ab6d9590e231 100644 (file)
@@ -16,16 +16,24 @@ private:
        shared_ptr<SSL_CTX> m_SSLContext;
        shared_ptr<SSL> 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<SSL_CTX> sslContext);
 
-       shared_ptr<X509> GetClientCertificate(void) const;
-       shared_ptr<X509> GetPeerCertificate(void) const;
+       X509 *GetClientCertificate(void) const;
+       X509 *GetPeerCertificate(void) const;
 
        virtual void Start(void);