TLSClient: add hostname verifcation

The CN or subjectAltNames of the TLS certification is now matched with
the hostname connected to.

taskd.trust is now a tristate value (allow all, ignore hostname,
strict) to optionally disable the new hostname verification.
This commit is contained in:
Alexander Sulfrian 2014-03-18 19:21:49 +01:00 committed by Paul Beckingham
parent fdcc04d13e
commit 7fb1487993
5 changed files with 73 additions and 17 deletions

View file

@ -45,6 +45,7 @@
#include <TLSClient.h> #include <TLSClient.h>
#include <text.h> #include <text.h>
#include <i18n.h> #include <i18n.h>
#include <gnutls/x509.h>
#define MAX_BUF 16384 #define MAX_BUF 16384
@ -68,11 +69,13 @@ TLSClient::TLSClient ()
: _ca ("") : _ca ("")
, _cert ("") , _cert ("")
, _key ("") , _key ("")
, _host ("")
, _port ("")
, _session(0) , _session(0)
, _socket (0) , _socket (0)
, _limit (0) , _limit (0)
, _debug (false) , _debug (false)
, _trust(false) , _trust(strict)
{ {
} }
@ -109,13 +112,15 @@ void TLSClient::debug (int level)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TLSClient::trust (bool value) void TLSClient::trust (const enum trust_level value)
{ {
_trust = value; _trust = value;
if (_debug) if (_debug)
{ {
if (_trust) if (_trust == allow_all)
std::cout << "c: INFO Server certificate trusted automatically.\n"; std::cout << "c: INFO Server certificate trusted automatically.\n";
else if (_trust == ignore_hostname)
std::cout << "c: INFO Server certificate trust verified but hostname ignored.\n";
else else
std::cout << "c: INFO Server certificate trust verified.\n"; std::cout << "c: INFO Server certificate trust verified.\n";
} }
@ -179,6 +184,9 @@ void TLSClient::init (
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TLSClient::connect (const std::string& host, const std::string& port) void TLSClient::connect (const std::string& host, const std::string& port)
{ {
_host = host;
_port = port;
// Store the TLSClient instance, so that the verification callback can access // Store the TLSClient instance, so that the verification callback can access
// it during the handshake below and call the verifcation method. // it during the handshake below and call the verifcation method.
gnutls_session_set_ptr (_session, (void*) this); gnutls_session_set_ptr (_session, (void*) this);
@ -273,19 +281,55 @@ void TLSClient::bye ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int TLSClient::verify_certificate () const int TLSClient::verify_certificate () const
{ {
if (_trust) if (_trust == TLSClient::allow_all)
return 0; return 0;
// This verification function uses the trusted CAs in the credentials // This verification function uses the trusted CAs in the credentials
// structure. So you must have installed one or more CA certificates. // structure. So you must have installed one or more CA certificates.
unsigned int status = 0; unsigned int status = 0;
const char* hostname = _host.c_str();
#if GNUTLS_VERSION_NUMBER >= 0x030104 #if GNUTLS_VERSION_NUMBER >= 0x030104
int ret = gnutls_certificate_verify_peers3 (_session, NULL, &status); if (_trust == TLSClient::ignore_hostname)
#else hostname = NULL;
int ret = gnutls_certificate_verify_peers2 (_session, &status);
#endif int ret = gnutls_certificate_verify_peers3 (_session, hostname, &status);
if (ret < 0) if (ret < 0)
return GNUTLS_E_CERTIFICATE_ERROR; return GNUTLS_E_CERTIFICATE_ERROR;
#else
int ret = gnutls_certificate_verify_peers2 (_session, &status);
if (ret < 0)
return GNUTLS_E_CERTIFICATE_ERROR;
if ((status == 0) && (_trust != TLSClient::ignore_hostname))
{
if (gnutls_certificate_type_get (_session) == GNUTLS_CRT_X509)
{
const gnutls_datum* cert_list;
unsigned int cert_list_size;
gnutls_x509_crt cert;
cert_list = gnutls_certificate_get_peers (_session, &cert_list_size);
if (cert_list_size == 0)
return GNUTLS_E_CERTIFICATE_ERROR;
ret = gnutls_x509_crt_init (&cert);
if (ret < 0)
return GNUTLS_E_CERTIFICATE_ERROR;
ret = gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER);
if (ret < 0)
gnutls_x509_crt_deinit(cert);
status = GNUTLS_E_CERTIFICATE_ERROR;
if (gnutls_x509_crt_check_hostname (cert, hostname) == 0)
gnutls_x509_crt_deinit(cert);
return GNUTLS_E_CERTIFICATE_ERROR;
}
else
return GNUTLS_E_CERTIFICATE_ERROR;
}
#endif
#if GNUTLS_VERSION_NUMBER >= 0x030105 #if GNUTLS_VERSION_NUMBER >= 0x030105
gnutls_certificate_type_t type = gnutls_certificate_type_get (_session); gnutls_certificate_type_t type = gnutls_certificate_type_get (_session);

View file

@ -34,11 +34,13 @@
class TLSClient class TLSClient
{ {
public: public:
enum trust_level { strict, ignore_hostname, allow_all };
TLSClient (); TLSClient ();
~TLSClient (); ~TLSClient ();
void limit (int); void limit (int);
void debug (int); void debug (int);
void trust (bool); void trust (const enum trust_level);
void ciphers (const std::string&); void ciphers (const std::string&);
void init (const std::string&, const std::string&, const std::string&); void init (const std::string&, const std::string&, const std::string&);
void connect (const std::string&, const std::string&); void connect (const std::string&, const std::string&);
@ -53,12 +55,14 @@ private:
std::string _cert; std::string _cert;
std::string _key; std::string _key;
std::string _ciphers; std::string _ciphers;
std::string _host;
std::string _port;
gnutls_certificate_credentials_t _credentials; gnutls_certificate_credentials_t _credentials;
gnutls_session_t _session; gnutls_session_t _session;
int _socket; int _socket;
int _limit; int _limit;
bool _debug; bool _debug;
bool _trust; enum trust_level _trust;
}; };
#endif #endif

View file

@ -232,8 +232,12 @@ int CmdDiagnostics::execute (std::string& output)
? " (readable)" : " (not readable)") ? " (readable)" : " (not readable)")
<< "\n"; << "\n";
if (context.config.get ("taskd.trust") != "") if (context.config.get ("taskd.trust") == "allow all")
out << " Trust: override\n"; out << " Trust: allow all\n";
else if (context.config.get ("taskd.trust") == "ignore hostname")
out << " Trust: ignore hostanme\n";
else
out << " Trust: strict\n";
out << " Cert: " out << " Cert: "
<< context.config.get ("taskd.certificate") << context.config.get ("taskd.certificate")

View file

@ -29,7 +29,6 @@
#include <inttypes.h> #include <inttypes.h>
#include <signal.h> #include <signal.h>
#include <Context.h> #include <Context.h>
#include <TLSClient.h>
#include <Color.h> #include <Color.h>
#include <text.h> #include <text.h>
#include <util.h> #include <util.h>
@ -87,14 +86,18 @@ int CmdSync::execute (std::string& output)
if (credentials.size () != 3) if (credentials.size () != 3)
throw std::string (STRING_CMD_SYNC_BAD_CRED); throw std::string (STRING_CMD_SYNC_BAD_CRED);
bool trust = context.config.getBoolean ("taskd.trust"); enum TLSClient::trust_level trust = TLSClient::strict;
if (context.config.get ("taskd.trust") == "allow all")
trust = TLSClient::allow_all;
else if (context.config.get ("taskd.trust") == "ignore hostname")
trust = TLSClient::ignore_hostname;
// CA must exist, if provided. // CA must exist, if provided.
File ca (context.config.get ("taskd.ca")); File ca (context.config.get ("taskd.ca"));
if (ca._data != "" && ! ca.exists ()) if (ca._data != "" && ! ca.exists ())
throw std::string (STRING_CMD_SYNC_BAD_CA); throw std::string (STRING_CMD_SYNC_BAD_CA);
if (trust && ca._data != "") if (trust == TLSClient::allow_all && ca._data != "")
throw std::string (STRING_CMD_SYNC_TRUST_CA); throw std::string (STRING_CMD_SYNC_TRUST_CA);
File certificate (context.config.get ("taskd.certificate")); File certificate (context.config.get ("taskd.certificate"));
@ -319,7 +322,7 @@ bool CmdSync::send (
const std::string& ca, const std::string& ca,
const std::string& certificate, const std::string& certificate,
const std::string& key, const std::string& key,
bool trust, const enum TLSClient::trust_level trust,
const Msg& request, const Msg& request,
Msg& response) Msg& response)
{ {

View file

@ -30,6 +30,7 @@
#include <string> #include <string>
#include <Command.h> #include <Command.h>
#include <Msg.h> #include <Msg.h>
#include <TLSClient.h>
class CmdSync : public Command class CmdSync : public Command
{ {
@ -38,7 +39,7 @@ public:
int execute (std::string&); int execute (std::string&);
private: private:
bool send (const std::string&, const std::string&, const std::string&, const std::string&, bool, const Msg&, Msg&); bool send (const std::string&, const std::string&, const std::string&, const std::string&, const enum TLSClient::trust_level, const Msg&, Msg&);
}; };
#endif #endif