From e5b146b7922bf4e88fd9d02f2d5bf55cbdfaadd3 Mon Sep 17 00:00:00 2001
From: Gunnar Beutner <gunnar.beutner@netways.de>
Date: Tue, 24 Apr 2012 15:56:48 +0200
Subject: [PATCH] Updated authentication subsystem to use SSL certificates.

---
 base/tlsclient.cpp                          | 16 +++--
 base/tlsclient.h                            |  7 +-
 base/utility.cpp                            | 12 ++++
 base/utility.h                              |  1 +
 components/configrpc/configrpccomponent.cpp | 32 +++++----
 components/configrpc/configrpccomponent.h   |  2 +-
 icinga/authenticationcomponent.cpp          | 73 ---------------------
 icinga/authenticationcomponent.h            | 23 -------
 icinga/discoverycomponent.cpp               | 18 ++---
 icinga/discoverycomponent.h                 |  3 +-
 icinga/endpoint.cpp                         |  2 +
 icinga/endpoint.h                           |  1 +
 icinga/i2-icinga.h                          |  1 -
 icinga/icinga.vcxproj                       |  2 -
 icinga/icingaapplication.cpp                |  3 -
 icinga/jsonrpcendpoint.cpp                  |  9 +++
 icinga/jsonrpcendpoint.h                    |  1 +
 17 files changed, 65 insertions(+), 141 deletions(-)
 delete mode 100644 icinga/authenticationcomponent.cpp
 delete mode 100644 icinga/authenticationcomponent.h

diff --git a/base/tlsclient.cpp b/base/tlsclient.cpp
index 7a6d75e0e..d8984e508 100644
--- a/base/tlsclient.cpp
+++ b/base/tlsclient.cpp
@@ -10,14 +10,19 @@ TLSClient::TLSClient(TCPClientRole role, shared_ptr<SSL_CTX> sslContext) : TCPCl
 	m_SSLContext = sslContext;
 }
 
-X509 *TLSClient::GetClientCertificate(void) const
+void TLSClient::NullCertificateDeleter(X509 *certificate)
 {
-	return SSL_get_certificate(m_SSL.get());
+	/* Nothing to do here. */
 }
 
-X509 *TLSClient::GetPeerCertificate(void) const
+shared_ptr<X509> TLSClient::GetClientCertificate(void) const
 {
-	return SSL_get_peer_certificate(m_SSL.get());
+	return shared_ptr<X509>(SSL_get_certificate(m_SSL.get()), &TLSClient::NullCertificateDeleter);
+}
+
+shared_ptr<X509> TLSClient::GetPeerCertificate(void) const
+{
+	return shared_ptr<X509>(SSL_get_peer_certificate(m_SSL.get()), X509_free);
 }
 
 void TLSClient::Start(void)
@@ -27,7 +32,7 @@ void TLSClient::Start(void)
 	m_SSL = shared_ptr<SSL>(SSL_new(m_SSLContext.get()), SSL_free);
 
 	if (!m_SSL)
-		; /* TODO: deal with error */
+		throw OpenSSLException("SSL_new failed", ERR_get_error());
 
 	if (!GetClientCertificate())
 		throw InvalidArgumentException("No X509 client certificate was specified.");
@@ -161,6 +166,7 @@ int TLSClient::SSLVerifyCertificate(int ok, X509_STORE_CTX *x509Context)
 	vcea.Source = client->shared_from_this();
 	vcea.ValidCertificate = (ok != 0);
 	vcea.Context = x509Context;
+	vcea.Certificate = shared_ptr<X509>(x509Context->cert, &TLSClient::NullCertificateDeleter);
 	client->OnVerifyCertificate(vcea);
 
 	return (int)vcea.ValidCertificate;
diff --git a/base/tlsclient.h b/base/tlsclient.h
index fd248449d..e441293c6 100644
--- a/base/tlsclient.h
+++ b/base/tlsclient.h
@@ -8,6 +8,7 @@ struct I2_BASE_API VerifyCertificateEventArgs : public EventArgs
 {
 	bool ValidCertificate;
 	X509_STORE_CTX *Context;
+	shared_ptr<X509> Certificate;
 };
 
 class I2_BASE_API TLSClient : public TCPClient
@@ -24,6 +25,8 @@ private:
 
 	virtual void CloseInternal(bool from_dtor);
 
+	static void NullCertificateDeleter(X509 *certificate);
+
 	static int SSLVerifyCertificate(int ok, X509_STORE_CTX *x509Context);
 
 protected:
@@ -32,8 +35,8 @@ protected:
 public:
 	TLSClient(TCPClientRole role, shared_ptr<SSL_CTX> sslContext);
 
-	X509 *GetClientCertificate(void) const;
-	X509 *GetPeerCertificate(void) const;
+	shared_ptr<X509> GetClientCertificate(void) const;
+	shared_ptr<X509> GetPeerCertificate(void) const;
 
 	virtual void Start(void);
 
diff --git a/base/utility.cpp b/base/utility.cpp
index 4dd4c7b7a..bc9663505 100644
--- a/base/utility.cpp
+++ b/base/utility.cpp
@@ -74,3 +74,15 @@ shared_ptr<SSL_CTX> Utility::MakeSSLContext(string pubkey, string privkey, strin
 
 	return sslContext;
 }
+
+string Utility::GetCertificateCN(const shared_ptr<X509>& certificate)
+{
+	char buffer[256];
+
+	int rc = X509_NAME_get_text_by_NID(X509_get_subject_name(certificate.get()), NID_commonName, buffer, sizeof(buffer));
+
+	if (rc == -1)
+		throw InvalidArgumentException("X509 certificate has no CN attribute.");
+
+	return buffer;
+}
diff --git a/base/utility.h b/base/utility.h
index 18c310d81..785ff8127 100644
--- a/base/utility.h
+++ b/base/utility.h
@@ -45,6 +45,7 @@ public:
 	static void Daemonize(void);
 
 	static shared_ptr<SSL_CTX> MakeSSLContext(string pubkey, string privkey, string cakey);
+	static string GetCertificateCN(const shared_ptr<X509>& certificate);
 };
 
 }
diff --git a/components/configrpc/configrpccomponent.cpp b/components/configrpc/configrpccomponent.cpp
index d516e70a0..b4a82f86c 100644
--- a/components/configrpc/configrpccomponent.cpp
+++ b/components/configrpc/configrpccomponent.cpp
@@ -16,7 +16,8 @@ void ConfigRpcComponent::Start(void)
 
 	long configSource;
 	if (GetConfig()->GetPropertyInteger("configSource", &configSource) && configSource != 0) {
-		m_ConfigRpcEndpoint->RegisterMethodHandler("config::FetchObjects", bind_weak(&ConfigRpcComponent::FetchObjectsHandler, shared_from_this()));
+		m_ConfigRpcEndpoint->RegisterMethodHandler("config::FetchObjects",
+		    bind_weak(&ConfigRpcComponent::FetchObjectsHandler, shared_from_this()));
 
 		configHive->OnObjectCreated += bind_weak(&ConfigRpcComponent::LocalObjectCreatedHandler, shared_from_this());
 		configHive->OnObjectRemoved += bind_weak(&ConfigRpcComponent::LocalObjectRemovedHandler, shared_from_this());
@@ -27,11 +28,12 @@ void ConfigRpcComponent::Start(void)
 		m_ConfigRpcEndpoint->RegisterMethodSource("config::PropertyChanged");
 	}
 
-	m_ConfigRpcEndpoint->RegisterMethodHandler("auth::Welcome", bind_weak(&ConfigRpcComponent::WelcomeMessageHandler, shared_from_this()));
-
-	m_ConfigRpcEndpoint->RegisterMethodHandler("config::ObjectCreated", bind_weak(&ConfigRpcComponent::RemoteObjectUpdatedHandler, shared_from_this()));
-	m_ConfigRpcEndpoint->RegisterMethodHandler("config::ObjectRemoved", bind_weak(&ConfigRpcComponent::RemoteObjectRemovedHandler, shared_from_this()));
-	m_ConfigRpcEndpoint->RegisterMethodHandler("config::PropertyChanged", bind_weak(&ConfigRpcComponent::RemoteObjectUpdatedHandler, shared_from_this()));
+	m_ConfigRpcEndpoint->RegisterMethodHandler("config::ObjectCreated",
+	    bind_weak(&ConfigRpcComponent::RemoteObjectUpdatedHandler, shared_from_this()));
+	m_ConfigRpcEndpoint->RegisterMethodHandler("config::ObjectRemoved",
+	    bind_weak(&ConfigRpcComponent::RemoteObjectRemovedHandler, shared_from_this()));
+	m_ConfigRpcEndpoint->RegisterMethodHandler("config::PropertyChanged",
+	    bind_weak(&ConfigRpcComponent::RemoteObjectUpdatedHandler, shared_from_this()));
 
 	endpointManager->RegisterEndpoint(m_ConfigRpcEndpoint);
 
@@ -46,22 +48,18 @@ void ConfigRpcComponent::Stop(void)
 
 int ConfigRpcComponent::NewEndpointHandler(const NewEndpointEventArgs& ea)
 {
-	if (ea.Endpoint->HasIdentity()) {
-		JsonRpcRequest request;
-		request.SetMethod("config::FetchObjects");
-
-		GetEndpointManager()->SendUnicastRequest(m_ConfigRpcEndpoint, ea.Endpoint, request);
-	}
+	ea.Endpoint->OnSessionEstablished += bind_weak(&ConfigRpcComponent::SessionEstablishedHandler, shared_from_this());
 
 	return 0;
 }
 
-int ConfigRpcComponent::WelcomeMessageHandler(const NewRequestEventArgs& ea)
+int ConfigRpcComponent::SessionEstablishedHandler(const EventArgs& ea)
 {
-	NewEndpointEventArgs neea;
-	neea.Source = shared_from_this();
-	neea.Endpoint = ea.Sender;
-	NewEndpointHandler(neea);
+	JsonRpcRequest request;
+	request.SetMethod("config::FetchObjects");
+
+	Endpoint::Ptr endpoint = static_pointer_cast<Endpoint>(ea.Source);
+	GetEndpointManager()->SendUnicastRequest(m_ConfigRpcEndpoint, endpoint, request);
 
 	return 0;
 }
diff --git a/components/configrpc/configrpccomponent.h b/components/configrpc/configrpccomponent.h
index 0efe8bec6..27ce4bb3b 100644
--- a/components/configrpc/configrpccomponent.h
+++ b/components/configrpc/configrpccomponent.h
@@ -10,7 +10,7 @@ private:
 	VirtualEndpoint::Ptr m_ConfigRpcEndpoint;
 
 	int NewEndpointHandler(const NewEndpointEventArgs& ea);
-	int WelcomeMessageHandler(const NewRequestEventArgs& ea);
+	int SessionEstablishedHandler(const EventArgs& ea);
 
 	int LocalObjectCreatedHandler(const EventArgs& ea);
 	int LocalObjectRemovedHandler(const EventArgs& ea);
diff --git a/icinga/authenticationcomponent.cpp b/icinga/authenticationcomponent.cpp
deleted file mode 100644
index 14520036e..000000000
--- a/icinga/authenticationcomponent.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "i2-icinga.h"
-
-using namespace icinga;
-
-string AuthenticationComponent::GetName(void) const
-{
-	return "authenticationcomponent";
-}
-
-void AuthenticationComponent::Start(void)
-{
-	m_AuthenticationEndpoint = make_shared<VirtualEndpoint>();
-	m_AuthenticationEndpoint->RegisterMethodHandler("auth::SetIdentity", bind_weak(&AuthenticationComponent::IdentityMessageHandler, shared_from_this()));
-	m_AuthenticationEndpoint->RegisterMethodSource("auth::Welcome");
-
-	EndpointManager::Ptr mgr = GetEndpointManager();
-	mgr->OnNewEndpoint += bind_weak(&AuthenticationComponent::NewEndpointHandler, shared_from_this());
-	mgr->ForeachEndpoint(bind(&AuthenticationComponent::NewEndpointHandler, this, _1));
-	mgr->RegisterEndpoint(m_AuthenticationEndpoint);
-}
-
-void AuthenticationComponent::Stop(void)
-{
-	EndpointManager::Ptr mgr = GetEndpointManager();
-
-	if (mgr)
-		mgr->UnregisterEndpoint(m_AuthenticationEndpoint);
-}
-
-int AuthenticationComponent::NewEndpointHandler(const NewEndpointEventArgs& neea)
-{
-	if (neea.Endpoint->IsLocal() || neea.Endpoint->HasIdentity())
-		return 0;
-
-	neea.Endpoint->AddAllowedMethodSinkPrefix("auth::");
-	neea.Endpoint->AddAllowedMethodSourcePrefix("auth::");
-
-	neea.Endpoint->RegisterMethodSink("auth::SetIdentity");
-
-	JsonRpcRequest request;
-	request.SetMethod("auth::SetIdentity");
-
-	IdentityMessage params;
-	params.SetIdentity("keks");
-	request.SetParams(params);
-
-	GetEndpointManager()->SendUnicastRequest(m_AuthenticationEndpoint, neea.Endpoint, request);
-
-	return 0;
-}
-
-int AuthenticationComponent::IdentityMessageHandler(const NewRequestEventArgs& nrea)
-{
-	Message params;
-	if (!nrea.Request.GetParams(&params))
-		return 0;
-
-	IdentityMessage identityMessage = params;
-
-	string identity;
-	if (!identityMessage.GetIdentity(&identity))
-		return 0;
-
-	nrea.Sender->SetIdentity(identity);
-
-	/* there's no authentication for now, just tell them it's ok to send messages */
-	JsonRpcRequest request;
-	request.SetMethod("auth::Welcome");
-
-	GetEndpointManager()->SendUnicastRequest(m_AuthenticationEndpoint, nrea.Sender, request);
-
-	return 0;
-}
diff --git a/icinga/authenticationcomponent.h b/icinga/authenticationcomponent.h
deleted file mode 100644
index 60442b84e..000000000
--- a/icinga/authenticationcomponent.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef AUTHENTICATIONCOMPONENT_H
-#define AUTHENTICATIONCOMPONENT_H
-
-namespace icinga
-{
-
-class AuthenticationComponent : public IcingaComponent
-{
-private:
-	VirtualEndpoint::Ptr m_AuthenticationEndpoint;
-
-	int NewEndpointHandler(const NewEndpointEventArgs& neea);
-	int IdentityMessageHandler(const NewRequestEventArgs& nrea);
-
-public:
-	virtual string GetName(void) const;
-	virtual void Start(void);
-	virtual void Stop(void);
-};
-
-}
-
-#endif /* AUTHENTICATIONCOMPONENT_H */
diff --git a/icinga/discoverycomponent.cpp b/icinga/discoverycomponent.cpp
index 88b7bbb0f..d2fd5bee7 100644
--- a/icinga/discoverycomponent.cpp
+++ b/icinga/discoverycomponent.cpp
@@ -11,8 +11,6 @@ void DiscoveryComponent::Start(void)
 {
 	m_DiscoveryEndpoint = make_shared<VirtualEndpoint>();
 	m_DiscoveryEndpoint->RegisterMethodSource("discovery::PeerAvailable");
-	m_DiscoveryEndpoint->RegisterMethodHandler("auth::Welcome",
-	    bind_weak(&DiscoveryComponent::WelcomeMessageHandler, shared_from_this()));
 	m_DiscoveryEndpoint->RegisterMethodHandler("discovery::GetPeers",
 		bind_weak(&DiscoveryComponent::GetPeersMessageHandler, shared_from_this()));
 
@@ -29,26 +27,22 @@ void DiscoveryComponent::Stop(void)
 
 int DiscoveryComponent::NewEndpointHandler(const NewEndpointEventArgs& neea)
 {
-	neea.Endpoint->OnIdentityChanged += bind_weak(&DiscoveryComponent::IdentityChangedHandler, shared_from_this());
+	neea.Endpoint->OnSessionEstablished += bind_weak(&DiscoveryComponent::SessionEstablishedHandler, shared_from_this());
 
 	/* TODO: register handler for new sink/source */
 
 	return 0;
 }
 
-int DiscoveryComponent::IdentityChangedHandler(const EventArgs& neea)
-{
-	/* TODO: send information about this client to all other clients */
-
-	return 0;
-}
-
-int DiscoveryComponent::WelcomeMessageHandler(const NewRequestEventArgs& nrea)
+int DiscoveryComponent::SessionEstablishedHandler(const EventArgs& neea)
 {
 	JsonRpcRequest request;
 	request.SetMethod("discovery::GetPeers");
 
-	GetEndpointManager()->SendUnicastRequest(m_DiscoveryEndpoint, nrea.Sender, request);
+	Endpoint::Ptr endpoint = static_pointer_cast<Endpoint>(neea.Source);
+	GetEndpointManager()->SendUnicastRequest(m_DiscoveryEndpoint, endpoint, request);
+
+	/* TODO: send information about this client to all other clients */
 
 	return 0;
 }
diff --git a/icinga/discoverycomponent.h b/icinga/discoverycomponent.h
index 76d1b148f..3e8325e95 100644
--- a/icinga/discoverycomponent.h
+++ b/icinga/discoverycomponent.h
@@ -12,8 +12,7 @@ private:
 	IcingaApplication::Ptr GetIcingaApplication(void) const;
 
 	int NewEndpointHandler(const NewEndpointEventArgs& neea);
-	int IdentityChangedHandler(const EventArgs& neea);
-	int WelcomeMessageHandler(const NewRequestEventArgs& nrea);
+	int SessionEstablishedHandler(const EventArgs& neea);
 	int GetPeersMessageHandler(const NewRequestEventArgs& nrea);
 
 public:
diff --git a/icinga/endpoint.cpp b/icinga/endpoint.cpp
index 981650329..7d5e82f4f 100644
--- a/icinga/endpoint.cpp
+++ b/icinga/endpoint.cpp
@@ -14,6 +14,8 @@ void Endpoint::SetIdentity(string identity)
 	EventArgs ea;
 	ea.Source = shared_from_this();
 	OnIdentityChanged(ea);
+
+	OnSessionEstablished(ea);
 }
 
 bool Endpoint::HasIdentity(void) const
diff --git a/icinga/endpoint.h b/icinga/endpoint.h
index 1980fdd48..0f22867de 100644
--- a/icinga/endpoint.h
+++ b/icinga/endpoint.h
@@ -66,6 +66,7 @@ public:
 	int CountMethodSources(void) const;
 
 	Event<EventArgs> OnIdentityChanged;
+	Event<EventArgs> OnSessionEstablished;
 };
 
 }
diff --git a/icinga/i2-icinga.h b/icinga/i2-icinga.h
index 6115bf4fb..8209e08de 100644
--- a/icinga/i2-icinga.h
+++ b/icinga/i2-icinga.h
@@ -19,7 +19,6 @@
 #include "icingacomponent.h"
 #include "subscriptioncomponent.h"
 #include "subscriptionmessage.h"
-#include "authenticationcomponent.h"
 #include "identitymessage.h"
 #include "discoverycomponent.h"
 
diff --git a/icinga/icinga.vcxproj b/icinga/icinga.vcxproj
index b081daada..a4ac17866 100644
--- a/icinga/icinga.vcxproj
+++ b/icinga/icinga.vcxproj
@@ -11,7 +11,6 @@
     </ProjectConfiguration>
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="authenticationcomponent.cpp" />
     <ClCompile Include="discoverycomponent.cpp" />
     <ClCompile Include="endpoint.cpp" />
     <ClCompile Include="endpointmanager.cpp" />
@@ -24,7 +23,6 @@
     <ClCompile Include="virtualendpoint.cpp" />
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="authenticationcomponent.h" />
     <ClInclude Include="discoverycomponent.h" />
     <ClInclude Include="endpoint.h" />
     <ClInclude Include="endpointmanager.h" />
diff --git a/icinga/icingaapplication.cpp b/icinga/icingaapplication.cpp
index 6e7f8ad86..833d561e4 100644
--- a/icinga/icingaapplication.cpp
+++ b/icinga/icingaapplication.cpp
@@ -53,9 +53,6 @@ int IcingaApplication::Main(const vector<string>& args)
 
 	connectionCollection->OnObjectRemoved += bind_weak(&IcingaApplication::DeletedRpcConnectionHandler, shared_from_this());
 
-	AuthenticationComponent::Ptr authenticationComponent = make_shared<AuthenticationComponent>();
-	RegisterComponent(authenticationComponent);
-
 	SubscriptionComponent::Ptr subscriptionComponent = make_shared<SubscriptionComponent>();
 	RegisterComponent(subscriptionComponent);
 
diff --git a/icinga/jsonrpcendpoint.cpp b/icinga/jsonrpcendpoint.cpp
index edb25c8bb..63590f8b4 100644
--- a/icinga/jsonrpcendpoint.cpp
+++ b/icinga/jsonrpcendpoint.cpp
@@ -79,6 +79,7 @@ void JsonRpcEndpoint::SetClient(JsonRpcClient::Ptr client)
 	client->OnNewMessage += bind_weak(&JsonRpcEndpoint::NewMessageHandler, shared_from_this());
 	client->OnClosed += bind_weak(&JsonRpcEndpoint::ClientClosedHandler, shared_from_this());
 	client->OnError += bind_weak(&JsonRpcEndpoint::ClientErrorHandler, shared_from_this());
+	client->OnVerifyCertificate += bind_weak(&JsonRpcEndpoint::VerifyCertificateHandler, shared_from_this());
 }
 
 bool JsonRpcEndpoint::IsLocal(void) const
@@ -185,3 +186,11 @@ int JsonRpcEndpoint::ClientReconnectHandler(const TimerEventArgs& ea)
 
 	return 0;
 }
+
+int JsonRpcEndpoint::VerifyCertificateHandler(const VerifyCertificateEventArgs& ea)
+{
+	if (ea.Certificate && ea.ValidCertificate)
+		SetIdentity(Utility::GetCertificateCN(ea.Certificate));
+
+	return 0;
+}
diff --git a/icinga/jsonrpcendpoint.h b/icinga/jsonrpcendpoint.h
index ac2cfeb1e..234ccfb0e 100644
--- a/icinga/jsonrpcendpoint.h
+++ b/icinga/jsonrpcendpoint.h
@@ -21,6 +21,7 @@ private:
 	int ClientClosedHandler(const EventArgs& ea);
 	int ClientErrorHandler(const SocketErrorEventArgs& ea);
 	int ClientReconnectHandler(const TimerEventArgs& ea);
+	int VerifyCertificateHandler(const VerifyCertificateEventArgs& ea);
 
 public:
 	typedef shared_ptr<JsonRpcEndpoint> Ptr;