Updated authentication subsystem to use SSL certificates.

This commit is contained in:
Gunnar Beutner 2012-04-24 15:56:48 +02:00
parent 4f6aa3236c
commit e5b146b792
17 changed files with 65 additions and 141 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
};
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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:

View File

@ -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

View File

@ -66,6 +66,7 @@ public:
int CountMethodSources(void) const;
Event<EventArgs> OnIdentityChanged;
Event<EventArgs> OnSessionEstablished;
};
}

View File

@ -19,7 +19,6 @@
#include "icingacomponent.h"
#include "subscriptioncomponent.h"
#include "subscriptionmessage.h"
#include "authenticationcomponent.h"
#include "identitymessage.h"
#include "discoverycomponent.h"

View File

@ -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" />

View File

@ -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);

View File

@ -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;
}

View File

@ -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;