]> granicus.if.org Git - icinga2/commitdiff
ApiListener: verify peer
authorAlexander A. Klimov <alexander.klimov@icinga.com>
Tue, 12 Feb 2019 13:56:25 +0000 (14:56 +0100)
committerAlexander A. Klimov <alexander.klimov@icinga.com>
Mon, 1 Apr 2019 09:40:14 +0000 (11:40 +0200)
lib/remote/apilistener.cpp

index 578e1a68241acfcee720b2d8564cb6b6f1777a01..4adb9035b59c2225dbb82b4bb9ae88be6965ab9e 100644 (file)
 #include <boost/asio/spawn.hpp>
 #include <boost/asio/ssl/context.hpp>
 #include <boost/asio/ssl/stream.hpp>
+#include <boost/asio/ssl/verify_context.hpp>
 #include <boost/asio/ssl/verify_mode.hpp>
 #include <climits>
 #include <fstream>
 #include <memory>
+#include <openssl/ssl.h>
 #include <openssl/tls1.h>
+#include <openssl/x509.h>
 #include <sstream>
 
 using namespace icinga;
@@ -668,6 +671,23 @@ void ApiListener::NewClientHandlerInternal(boost::asio::yield_context yc, const
 
        client->set_verify_mode(ssl::verify_peer | ssl::verify_client_once);
 
+       bool verify_ok = false;
+       String verifyError;
+
+       client->set_verify_callback([&verify_ok, &verifyError](bool preverified, ssl::verify_context& ctx) {
+               verify_ok = preverified;
+
+               if (!preverified) {
+                       std::ostringstream msgbuf;
+                       int err = X509_STORE_CTX_get_error(ctx.native_handle());
+
+                       msgbuf << "code " << err << ": " << X509_verify_cert_error_string(err);
+                       verifyError = msgbuf.str();
+               }
+
+               return preverified;
+       });
+
        if (role == RoleClient) {
                String environmentName = Application::GetAppEnvironment();
                String serverName = hostname;
@@ -687,6 +707,51 @@ void ApiListener::NewClientHandlerInternal(boost::asio::yield_context yc, const
        } catch (const std::exception& ex) {
                Log(LogCritical, "ApiListener")
                        << "Client TLS handshake failed (" << conninfo << "): " << DiagnosticInformation(ex, false);
+               return;
+       }
+
+       std::shared_ptr<X509> cert (SSL_get_peer_certificate(client->native_handle()), X509_free);
+       String identity;
+       Endpoint::Ptr endpoint;
+
+       if (cert) {
+               try {
+                       identity = GetCertificateCN(cert);
+               } catch (const std::exception&) {
+                       Log(LogCritical, "ApiListener")
+                               << "Cannot get certificate common name from cert path: '" << GetDefaultCertPath() << "'.";
+                       return;
+               }
+
+               if (!hostname.IsEmpty()) {
+                       if (identity != hostname) {
+                               Log(LogWarning, "ApiListener")
+                                       << "Unexpected certificate common name while connecting to endpoint '"
+                                       << hostname << "': got '" << identity << "'";
+                               return;
+                       } else if (!verify_ok) {
+                               Log(LogWarning, "ApiListener")
+                                       << "Certificate validation failed for endpoint '" << hostname
+                                       << "': " << verifyError;
+                       }
+               }
+
+               if (verify_ok) {
+                       endpoint = Endpoint::GetByName(identity);
+               }
+
+               Log log(LogInformation, "ApiListener");
+
+               log << "New client connection for identity '" << identity << "' " << conninfo;
+
+               if (!verify_ok) {
+                       log << " (certificate validation failed: " << verifyError << ")";
+               } else if (!endpoint) {
+                       log << " (no Endpoint object found for identity)";
+               }
+       } else {
+               Log(LogInformation, "ApiListener")
+                       << "New client connection " << conninfo << " (no client certificate)";
        }
 }