Skip to content

Commit

Permalink
Added :verify_peer support for requesting and verifying an X509 certi…
Browse files Browse the repository at this point in the history
…ficate from a remote SSL peer.

Signed-off-by: Aman Gupta <[email protected]>
  • Loading branch information
jakedouglas authored and tmm1 committed Mar 29, 2009
1 parent 691fbfa commit f27e862
Show file tree
Hide file tree
Showing 11 changed files with 333 additions and 26 deletions.
22 changes: 18 additions & 4 deletions ext/cmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,17 +302,17 @@ extern "C" void evma_start_tls (const char *binding)
evma_set_tls_parms
******************/

extern "C" void evma_set_tls_parms (const char *binding, const char *privatekey_filename, const char *certchain_filename)
extern "C" void evma_set_tls_parms (const char *binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer)
{
ensure_eventmachine("evma_set_tls_parms");
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
if (ed)
ed->SetTlsParms (privatekey_filename, certchain_filename);
ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false));
}

/**************
/******************
evma_get_peer_cert
**************/
******************/

#ifdef WITH_SSL
extern "C" X509 *evma_get_peer_cert (const char *binding)
Expand All @@ -325,6 +325,20 @@ extern "C" X509 *evma_get_peer_cert (const char *binding)
}
#endif

/********************
evma_accept_ssl_peer
********************/

#ifdef WITH_SSL
extern "C" void evma_accept_ssl_peer (const char *binding)
{
ensure_eventmachine("evma_accept_ssl_peer");
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
if (cd)
cd->AcceptSslPeer();
}
#endif

/*****************
evma_get_peername
*****************/
Expand Down
44 changes: 41 additions & 3 deletions ext/ed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ ConnectionDescriptor::ConnectionDescriptor (int sd, EventMachine_t *em):
#ifdef WITH_SSL
SslBox (NULL),
bHandshakeSignaled (false),
bSslVerifyPeer (false),
bSslPeerAccepted(false),
#endif
bIsServer (false),
LastIo (gCurrentLoopTime),
Expand Down Expand Up @@ -502,8 +504,14 @@ void ConnectionDescriptor::_DispatchInboundData (const char *buffer, int size)
if (EventCallback)
(*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, B, s);
}

// If our SSL handshake had a problem, shut down the connection.
if (s == -2) {
ScheduleClose(false);
return;
}

_CheckHandshakeStatus();
// INCOMPLETE, s may indicate an SSL error that would force the connection down.
_DispatchCiphertext();
}
else {
Expand Down Expand Up @@ -772,7 +780,7 @@ void ConnectionDescriptor::StartTls()
if (SslBox)
throw std::runtime_error ("SSL/TLS already running on connection");

SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename);
SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, GetBinding().c_str());
_DispatchCiphertext();
#endif

Expand All @@ -786,7 +794,7 @@ void ConnectionDescriptor::StartTls()
ConnectionDescriptor::SetTlsParms
*********************************/

void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename)
void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer)
{
#ifdef WITH_SSL
if (SslBox)
Expand All @@ -795,6 +803,7 @@ void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char
PrivateKeyFilename = privkey_filename;
if (certchain_filename && *certchain_filename)
CertChainFilename = certchain_filename;
bSslVerifyPeer = verify_peer;
#endif

#ifdef WITHOUT_SSL
Expand All @@ -817,6 +826,35 @@ X509 *ConnectionDescriptor::GetPeerCert()
#endif


/***********************************
ConnectionDescriptor::VerifySslPeer
***********************************/

#ifdef WITH_SSL
bool ConnectionDescriptor::VerifySslPeer(const char *cert)
{
bSslPeerAccepted = false;

if (EventCallback)
(*EventCallback)(GetBinding().c_str(), EM_SSL_VERIFY, cert, strlen(cert));

return bSslPeerAccepted;
}
#endif


/***********************************
ConnectionDescriptor::AcceptSslPeer
***********************************/

#ifdef WITH_SSL
void ConnectionDescriptor::AcceptSslPeer()
{
bSslPeerAccepted = true;
}
#endif


/*****************************************
ConnectionDescriptor::_DispatchCiphertext
*****************************************/
Expand Down
8 changes: 6 additions & 2 deletions ext/ed.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class EventableDescriptor: public Bindable_t
virtual bool GetSubprocessPid (pid_t*) {return false;}

virtual void StartTls() {}
virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename) {}
virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer) {}

#ifdef WITH_SSL
virtual X509 *GetPeerCert() {return NULL;}
Expand Down Expand Up @@ -167,10 +167,12 @@ class ConnectionDescriptor: public EventableDescriptor
virtual int GetOutboundDataSize() {return OutboundDataSize;}

virtual void StartTls();
virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename);
virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer);

#ifdef WITH_SSL
virtual X509 *GetPeerCert();
virtual bool VerifySslPeer(const char*);
virtual void AcceptSslPeer();
#endif

void SetServerMode() {bIsServer = true;}
Expand Down Expand Up @@ -208,6 +210,8 @@ class ConnectionDescriptor: public EventableDescriptor
std::string CertChainFilename;
std::string PrivateKeyFilename;
bool bHandshakeSignaled;
bool bSslVerifyPeer;
bool bSslPeerAccepted;
#endif
bool bIsServer;

Expand Down
6 changes: 4 additions & 2 deletions ext/eventmachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ extern "C" {
EM_LOOPBREAK_SIGNAL = 105,
EM_CONNECTION_NOTIFY_READABLE = 106,
EM_CONNECTION_NOTIFY_WRITABLE = 107,
EM_SSL_HANDSHAKE_COMPLETED = 108
EM_SSL_HANDSHAKE_COMPLETED = 108,
EM_SSL_VERIFY = 109

};

Expand All @@ -52,11 +53,12 @@ extern "C" {
const char *evma_create_unix_domain_server (const char *filename);
const char *evma_open_datagram_socket (const char *server, int port);
const char *evma_open_keyboard();
void evma_set_tls_parms (const char *binding, const char *privatekey_filename, const char *certchain_filenane);
void evma_set_tls_parms (const char *binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer);
void evma_start_tls (const char *binding);

#ifdef WITH_SSL
X509 *evma_get_peer_cert (const char *binding);
void evma_accept_ssl_peer (const char *binding);
#endif

int evma_get_peername (const char *binding, struct sockaddr*);
Expand Down
21 changes: 16 additions & 5 deletions ext/rubymain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ static VALUE Intern_delete;
static VALUE Intern_call;
static VALUE Intern_receive_data;
static VALUE Intern_ssl_handshake_completed;
static VALUE Intern_ssl_verify_peer;
static VALUE Intern_notify_readable;
static VALUE Intern_notify_writable;

Expand Down Expand Up @@ -108,6 +109,15 @@ static void event_callback (struct em_event* e)
rb_raise (EM_eConnectionNotBound, "unknown connection: %s", a1);
rb_funcall (q, Intern_ssl_handshake_completed, 0);
}
else if (a2 == EM_SSL_VERIFY) {
VALUE t = rb_ivar_get (EmModule, Intern_at_conns);
VALUE q = rb_hash_aref (t, rb_str_new2(a1));
if (q == Qnil)
rb_raise (EM_eConnectionNotBound, "unknown connection: %s", a1);
VALUE r = rb_funcall (q, Intern_ssl_verify_peer, 1, rb_str_new(a3, a4));
if (RTEST(r))
evma_accept_ssl_peer (a1);
}
else
rb_funcall (EmModule, Intern_event_callback, 3, rb_str_new2(a1), (a2 << 1) | 1, rb_str_new(a3,a4));
}
Expand Down Expand Up @@ -238,20 +248,20 @@ static VALUE t_start_tls (VALUE self, VALUE signature)
t_set_tls_parms
***************/

static VALUE t_set_tls_parms (VALUE self, VALUE signature, VALUE privkeyfile, VALUE certchainfile)
static VALUE t_set_tls_parms (VALUE self, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer)
{
/* set_tls_parms takes a series of positional arguments for specifying such things
* as private keys and certificate chains.
* It's expected that the parameter list will grow as we add more supported features.
* ALL of these parameters are optional, and can be specified as empty or NULL strings.
*/
evma_set_tls_parms (StringValuePtr (signature), StringValuePtr (privkeyfile), StringValuePtr (certchainfile) );
evma_set_tls_parms (StringValuePtr (signature), StringValuePtr (privkeyfile), StringValuePtr (certchainfile), (verify_peer == Qtrue ? 1 : 0));
return Qnil;
}

/***********
/***************
t_get_peer_cert
***********/
***************/

static VALUE t_get_peer_cert (VALUE self, VALUE signature)
{
Expand Down Expand Up @@ -859,6 +869,7 @@ extern "C" void Init_rubyeventmachine()
Intern_call = rb_intern ("call");
Intern_receive_data = rb_intern ("receive_data");
Intern_ssl_handshake_completed = rb_intern ("ssl_handshake_completed");
Intern_ssl_verify_peer = rb_intern ("ssl_verify_peer");
Intern_notify_readable = rb_intern ("notify_readable");
Intern_notify_writable = rb_intern ("notify_writable");

Expand All @@ -879,7 +890,7 @@ extern "C" void Init_rubyeventmachine()
rb_define_module_function (EmModule, "start_tcp_server", (VALUE(*)(...))t_start_server, 2);
rb_define_module_function (EmModule, "stop_tcp_server", (VALUE(*)(...))t_stop_server, 1);
rb_define_module_function (EmModule, "start_unix_server", (VALUE(*)(...))t_start_unix_server, 1);
rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 3);
rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 4);
rb_define_module_function (EmModule, "start_tls", (VALUE(*)(...))t_start_tls, 1);
rb_define_module_function (EmModule, "get_peer_cert", (VALUE(*)(...))t_get_peer_cert, 1);
rb_define_module_function (EmModule, "send_data", (VALUE(*)(...))t_send_data, 3);
Expand Down
39 changes: 38 additions & 1 deletion ext/ssl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,10 @@ SslContext_t::~SslContext_t()
SslBox_t::SslBox_t
******************/

SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile):
SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const char *binding):
bIsServer (is_server),
bHandshakeCompleted (false),
bVerifyPeer (verify_peer),
pSSL (NULL),
pbioRead (NULL),
pbioWrite (NULL)
Expand All @@ -232,6 +233,12 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer
assert (pSSL);
SSL_set_bio (pSSL, pbioRead, pbioWrite);

// Store a pointer to the binding signature in the SSL object so we can retrieve it later
SSL_set_ex_data(pSSL, 0, (void*) binding);

if (bVerifyPeer)
SSL_set_verify(pSSL, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, ssl_verify_wrapper);

if (!bIsServer)
SSL_connect (pSSL);
}
Expand Down Expand Up @@ -419,5 +426,35 @@ X509 *SslBox_t::GetPeerCert()
return cert;
}


/******************
ssl_verify_wrapper
*******************/

extern "C" int ssl_verify_wrapper(int preverify_ok, X509_STORE_CTX *ctx)
{
const char *binding;
X509 *cert;
SSL *ssl;
BUF_MEM *buf;
BIO *out;
int result;

cert = X509_STORE_CTX_get_current_cert(ctx);
ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
binding = (const char*) SSL_get_ex_data(ssl, 0);

out = BIO_new(BIO_s_mem());
PEM_write_bio_X509(out, cert);
BIO_write(out, "\0", 1);
BIO_get_mem_ptr(out, &buf);

ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject(binding));
result = (cd->VerifySslPeer(buf->data) == true ? 1 : 0);
BUF_MEM_free(buf);

return result;
}

#endif // WITH_SSL

6 changes: 5 additions & 1 deletion ext/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class SslBox_t
class SslBox_t
{
public:
SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile);
SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const char *binding);
virtual ~SslBox_t();

int PutPlaintext (const char*, int);
Expand All @@ -75,6 +75,7 @@ class SslBox_t
protected:
SslContext_t *Context;

bool bVerifyPeer;
bool bIsServer;
bool bHandshakeCompleted;
SSL *pSSL;
Expand All @@ -83,6 +84,9 @@ class SslBox_t

PageList OutboundQ;
};

extern "C" int ssl_verify_wrapper(int, X509_STORE_CTX*);

#endif // WITH_SSL


Expand Down
Loading

0 comments on commit f27e862

Please sign in to comment.