0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 12:12:15 +02:00

Added support to new core for remote-cert-tls, remote-cert-ku,

and remote-cert-eku directives.
This commit is contained in:
James Yonan 2012-10-31 14:46:40 +00:00
parent 522783e1a4
commit f72aab4b40
14 changed files with 354 additions and 61 deletions

View File

@ -292,6 +292,14 @@ namespace openvpn {
stop();
}
break;
case Error::CERT_VERIFY_FAIL:
{
ClientEvent::Base::Ptr ev = new ClientEvent::CertVerifyFail(client->fatal_reason());
client_options->events().add_event(ev);
client_options->stats().error(Error::CERT_VERIFY_FAIL);
stop();
}
break;
case Error::CLIENT_HALT:
{
ClientEvent::Base::Ptr ev = new ClientEvent::ClientHalt(client->fatal_reason());

View File

@ -33,6 +33,7 @@ namespace openvpn {
// start of errors, must be marked by ERROR_START
AUTH_FAILED,
CERT_VERIFY_FAIL,
CLIENT_HALT,
CLIENT_RESTART,
CONNECTION_TIMEOUT,
@ -66,6 +67,7 @@ namespace openvpn {
"PAUSE",
"RESUME",
"AUTH_FAILED",
"CERT_VERIFY_FAIL",
"CLIENT_HALT",
"CLIENT_RESTART",
"CONNECTION_TIMEOUT",
@ -223,6 +225,11 @@ namespace openvpn {
AuthFailed(const std::string& reason) : ReasonBase(AUTH_FAILED, reason) {}
};
struct CertVerifyFail : public ReasonBase
{
CertVerifyFail(const std::string& reason) : ReasonBase(CERT_VERIFY_FAIL, reason) {}
};
struct ClientHalt : public ReasonBase
{
ClientHalt(const std::string& reason) : ReasonBase(CLIENT_HALT, reason) {}

View File

@ -100,6 +100,7 @@ namespace openvpn {
proto_context_options(config.proto_context_options),
first_packet_received_(false),
sent_push_request(false),
cli_stats(config.cli_stats),
cli_events(config.cli_events),
connected_(false),
fatal_(Error::UNDEF),
@ -225,6 +226,13 @@ namespace openvpn {
// schedule housekeeping wakeup
set_housekeeping_timer();
}
catch (const ExceptionCode& e)
{
if (e.fatal())
transport_error((Error::Type)e.code(), e.what());
else
cli_stats->error((Error::Type)e.code());
}
catch (const std::exception& e)
{
process_exception(e, "transport_recv");
@ -625,6 +633,8 @@ namespace openvpn {
bool first_packet_received_;
bool sent_push_request;
SessionStats::Ptr cli_stats;
ClientEvent::Queue::Ptr cli_events;
bool connected_;

View File

@ -31,8 +31,49 @@ namespace openvpn {
return e.message();
}
class ExceptionCode : public std::exception
{
enum {
FATAL_FLAG = 0x80000000
};
public:
ExceptionCode()
: code_(0) {}
ExceptionCode(const unsigned int code)
: code_(code) {}
ExceptionCode(const unsigned int code, const bool fatal)
: code_(mkcode(code, fatal)) {}
void set_code(const unsigned int code)
{
code_ = code;
}
void set_code(const unsigned int code, const bool fatal)
{
code_ = mkcode(code, fatal);
}
unsigned int code() const { return code_ & ~FATAL_FLAG; }
bool fatal() const { return (code_ & FATAL_FLAG) != 0; }
virtual ~ExceptionCode() throw() {}
private:
static unsigned int mkcode(const unsigned int code, const bool fatal)
{
unsigned int ret = code;
if (fatal)
ret |= FATAL_FLAG;
return ret;
}
unsigned int code_;
};
// string exception class
class Exception : public std::exception
class Exception : public openvpn::ExceptionCode
{
public:
Exception(std::string err) : err_(err) {}
@ -44,7 +85,7 @@ namespace openvpn {
// define a simple custom exception class with no extra info
# define OPENVPN_SIMPLE_EXCEPTION(C) \
class C : public std::exception { \
class C : public openvpn::ExceptionCode { \
public: \
virtual const char* what() const throw() { return #C OPENVPN_FILE_LINE; } \
}

View File

@ -82,6 +82,30 @@ namespace openvpn {
throw parse_hex_error(); // straggler char
}
// note -- currently doesn't detect overflow
template <typename T>
inline T parse_hex_number(const char *str)
{
if (!str[0])
throw parse_hex_error(); // empty string
size_t i = 0;
T ret = T(0);
while (true)
{
const char c = str[i++];
const int hd = parse_hex_char(c);
if (hd >= 0)
{
ret *= T(16);
ret += T(hd);
}
else if (!c)
return ret;
else
throw parse_hex_error(); // non-hex-digit
}
}
} // namespace openvpn
#endif // OPENVPN_COMMON_HEXSTR_H

View File

@ -91,6 +91,14 @@ namespace openvpn {
return "";
}
const std::string* get_ptr(const size_t index) const
{
if (index < size())
return &(*this)[index];
else
return NULL;
}
std::string render() const
{
std::ostringstream out;

View File

@ -8,6 +8,7 @@
#ifndef OPENVPN_POLARSSL_SSL_SSLCTX_H
#define OPENVPN_POLARSSL_SSL_SSLCTX_H
#include <vector>
#include <string>
#include <cstring>
#include <sstream>
@ -26,6 +27,8 @@
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/pki/cclist.hpp>
#include <openvpn/pki/epkibase.hpp>
#include <openvpn/ssl/kuparse.hpp>
#include <openvpn/ssl/nscert.hpp>
#include <openvpn/polarssl/pki/x509cert.hpp>
#include <openvpn/polarssl/pki/dh.hpp>
@ -72,15 +75,9 @@ namespace openvpn {
};
typedef unsigned int Flags;
enum CertType {
CERT_TYPE_NONE,
CERT_TYPE_NS_CLIENT,
CERT_TYPE_NS_SERVER
};
Config() : external_pki(NULL),
flags(0),
cert_type(CERT_TYPE_NONE) {}
ns_cert_type(NSCert::NONE) {}
Mode mode;
PolarSSLPKI::X509Cert::Ptr crt_chain; // local cert chain (including client cert + extra certs)
@ -90,7 +87,9 @@ namespace openvpn {
ExternalPKIBase* external_pki;
Frame::Ptr frame;
Flags flags;
CertType cert_type;
NSCert::Type ns_cert_type;
std::vector<unsigned int> ku; // if defined, peer cert X509 key usage must match one of these values
std::string eku; // if defined, peer cert X509 extended key usage must match this OID/string
typename RAND_API::Ptr rng; // random data source
void enable_debug()
@ -173,31 +172,18 @@ namespace openvpn {
load_dh(dh_txt);
}
// ns-cert-type
{
const Option* o = opt.get_ptr("ns-cert-type");
if (o)
{
const std::string& ct = o->get_optional(1);
if (ct == "server")
cert_type = CERT_TYPE_NS_SERVER;
else if (ct == "client")
cert_type = CERT_TYPE_NS_CLIENT;
else
throw option_error("ns-cert-type must be 'client' or 'server'");
}
}
// parse ns-cert-type
ns_cert_type = NSCert::ns_cert_type(opt);
// unsupported cert checkers
// parse remote-cert-x options
KUParse::remote_cert_tls(opt, ku, eku);
KUParse::remote_cert_ku(opt, ku);
KUParse::remote_cert_eku(opt, eku);
// unsupported cert verification options
{
if (opt.get_ptr("tls-remote"))
throw option_error("tls-remote not supported");
if (opt.get_ptr("remote-cert-tls"))
throw option_error("remote-cert-tls not supported");
if (opt.get_ptr("remote-cert-ku"))
throw option_error("remote-cert-ku not supported");
if (opt.get_ptr("remote-cert-eku"))
throw option_error("remote-cert-eku not supported");
}
}
};
@ -487,15 +473,79 @@ namespace openvpn {
}
private:
// ns-cert-type verification
bool ns_cert_type_defined() const
{
return config.ns_cert_type != NSCert::NONE;
}
bool verify_ns_cert_type(const x509_cert *cert) const
{
if (config.cert_type == Config::CERT_TYPE_NS_SERVER)
if (config.ns_cert_type == NSCert::SERVER)
return bool(cert->ns_cert_type & NS_CERT_TYPE_SSL_SERVER);
else if (config.cert_type == Config::CERT_TYPE_NS_CLIENT)
else if (config.ns_cert_type == NSCert::CLIENT)
return bool(cert->ns_cert_type & NS_CERT_TYPE_SSL_CLIENT);
else
return false;
}
// remote-cert-ku verification
bool x509_cert_ku_defined() const
{
return config.ku.size() > 0;
}
bool verify_x509_cert_ku(const x509_cert *cert)
{
if (cert->ext_types & EXT_KEY_USAGE)
{
const unsigned int ku = cert->key_usage;
for (std::vector<unsigned int>::const_iterator i = config.ku.begin(); i != config.ku.end(); ++i)
{
if (ku == *i)
return true;
}
}
return false;
}
// remote-cert-eku verification
bool x509_cert_eku_defined() const
{
return !config.eku.empty();
}
bool verify_x509_cert_eku(x509_cert *cert)
{
if (cert->ext_types & EXT_EXTENDED_KEY_USAGE)
{
x509_sequence *oid_seq = &cert->ext_key_usage;
while (oid_seq != NULL)
{
x509_buf *oid = &oid_seq->buf;
// first compare against description
{
const char *oid_str = x509_oid_get_description(oid);
if (oid_str && config.eku == oid_str)
return true;
}
// next compare against OID numeric string
{
char oid_num_str[256];
const int status = x509_oid_get_numeric_string(oid_num_str, sizeof(oid_num_str), oid);
if (status >= 0 && config.eku == oid_num_str)
return true;
}
oid_seq = oid_seq->next;
}
}
return false;
}
static int verify_callback(void *arg, x509_cert *cert, int depth, int preverify_ok)
{
@ -506,13 +556,31 @@ namespace openvpn {
<< ": depth=" << depth
<< std::endl << cert_info(cert));
// leaf-cert verification
if (depth == 0)
{
// verify ns-cert-type
if (depth == 0 && !self->verify_ns_cert_type(cert))
if (self->ns_cert_type_defined() && !self->verify_ns_cert_type(cert))
{
OPENVPN_LOG_SSL("VERIFY FAIL -- bad ns-cert-type in leaf certificate");
preverify_ok = false;
}
// verify X509 key usage
if (self->x509_cert_ku_defined() && !self->verify_x509_cert_ku(cert))
{
OPENVPN_LOG_SSL("VERIFY FAIL -- bad X509 key usage in leaf certificate");
preverify_ok = false;
}
// verify X509 extended key usage
if (self->x509_cert_eku_defined() && !self->verify_x509_cert_eku(cert))
{
OPENVPN_LOG_SSL("VERIFY FAIL -- bad X509 extended key usage in leaf certificate");
preverify_ok = false;
}
}
return preverify_ok ? 0 : POLARSSL_ERR_SSL_PEER_VERIFY_FAILED;
}

View File

@ -13,11 +13,12 @@
#include <polarssl/error.h>
#include <openvpn/common/exception.hpp>
#include <openvpn/error/error.hpp>
namespace openvpn {
// string exception class
class PolarSSLException : public std::exception
class PolarSSLException : public ExceptionCode
{
public:
PolarSSLException()
@ -36,6 +37,13 @@ namespace openvpn {
{
errnum = polarssl_errnum;
errtxt = "PolarSSL: " + error_text + " : " + polarssl_errtext(polarssl_errnum);
// for certain PolarSSL errors, translate them to an OpenVPN error code
switch (errnum) {
case POLARSSL_ERR_X509_CERT_VERIFY_FAILED:
set_code(Error::CERT_VERIFY_FAIL, true);
break;
}
}
virtual const char* what() const throw() { return errtxt.c_str(); }

75
openvpn/ssl/kuparse.hpp Normal file
View File

@ -0,0 +1,75 @@
//
// kuparse.hpp
// OpenVPN
//
// Copyright (c) 2012 OpenVPN Technologies, Inc. All rights reserved.
//
#ifndef OPENVPN_SSL_KUPARSE_H
#define OPENVPN_SSL_KUPARSE_H
#include <vector>
#include <string>
#include <openvpn/common/types.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/hexstr.hpp>
#include <openvpn/common/options.hpp>
namespace openvpn {
namespace KUParse {
inline void remote_cert_tls(const OptionList& opt, std::vector<unsigned int>& ku, std::string& eku)
{
const Option* o = opt.get_ptr("remote-cert-tls");
if (o)
{
const std::string& ct = o->get_optional(1);
if (ct == "server")
{
ku.push_back(0xa0);
ku.push_back(0x88);
eku = "TLS Web Server Authentication";
}
else if (ct == "client")
{
ku.push_back(0x80);
ku.push_back(0x08);
ku.push_back(0x88);
eku = "TLS Web Client Authentication";
}
else
throw option_error("remote-cert-tls must be 'client' or 'server'");
}
}
inline void remote_cert_ku(const OptionList& opt, std::vector<unsigned int>& ku)
{
const Option* o = opt.get_ptr("remote-cert-ku");
if (o)
{
if (o->empty())
throw option_error("remote-cert-ku: no hex values specified");
else
{
try {
for (size_t i = 1; i < o->size(); ++i)
ku.push_back(parse_hex_number<unsigned int>((*o)[i].c_str()));
}
catch (parse_hex_error& e)
{
throw option_error("remote-cert-ku: error parsing hex value list");
}
}
}
}
inline void remote_cert_eku(const OptionList& opt, std::string& eku)
{
const Option* o = opt.get_ptr("remote-cert-eku");
if (o)
eku = o->get(1);
}
}
}
#endif

42
openvpn/ssl/nscert.hpp Normal file
View File

@ -0,0 +1,42 @@
//
// nscert.hpp
// OpenVPN
//
// Copyright (c) 2012 OpenVPN Technologies, Inc. All rights reserved.
//
#ifndef OPENVPN_SSL_NSCERT_H
#define OPENVPN_SSL_NSCERT_H
#include <string>
#include <openvpn/common/types.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/options.hpp>
namespace openvpn {
namespace NSCert {
enum Type {
NONE,
CLIENT,
SERVER
};
inline Type ns_cert_type(const OptionList& opt) {
const Option* o = opt.get_ptr("ns-cert-type");
if (o)
{
const std::string& ct = o->get_optional(1);
if (ct == "server")
return SERVER;
else if (ct == "client")
return CLIENT;
else
throw option_error("ns-cert-type must be 'client' or 'server'");
}
return NONE;
}
}
}
#endif

View File

@ -11,9 +11,11 @@ if(ENABLE_SERVER)
add_definitions(-DPOLARSSL_SSL_SRV_C)
endif()
if(MINICRYPTO_DIR)
if(MINICRYPTO)
if(MINICRYPTO_DIR)
add_library(minicrypto STATIC IMPORTED)
set_property(TARGET minicrypto PROPERTY IMPORTED_LOCATION "${MINICRYPTO_DIR}/libminicrypto.a")
endif()
add_definitions(-DPOLARSSL_USE_OPENSSL_AES)
add_definitions(-DPOLARSSL_USE_OPENSSL_SHA1)
add_definitions(-DPOLARSSL_USE_OPENSSL_SHA2)

View File

@ -76,11 +76,6 @@ fi
# set options
OPT=""
# add special support for AES-NI?
if [ "$AES_NI" = "1" ] && [ "$USE_MINICRYPTO" = "1" ]; then
OPT="$OPT -DOPENSSL_AES_NI=1"
fi
# RNG
if [ "$EXTERNAL_RNG" = "1" ]; then
OPT="$OPT -DEXTERNAL_RNG=1"
@ -102,13 +97,14 @@ elif [ "$APPLE_FAMILY" = "1" ]; then
fi
# OpenSSL
if [ "$MINICRYPTO_DIR" ] && [ "$USE_MINICRYPTO" = "1" ]; then
OPT="$OPT -DMINICRYPTO_DIR=$MINICRYPTO_DIR"
fi
# OpenSSL
if [ "$MINICRYPTO_DIR" ] && [ "$USE_MINICRYPTO" = "1" ]; then
if [ "$USE_MINICRYPTO" = "1" ]; then
OPT="$OPT -DMINICRYPTO=1"
if [ "$MINICRYPTO_DIR" ]; then
OPT="$OPT -DMINICRYPTO_DIR=$MINICRYPTO_DIR"
fi
if [ "$AES_NI" = "1" ]; then
OPT="$OPT -DOPENSSL_AES_NI=1"
fi
fi
# Enable SSL/TLS server

View File

@ -82,11 +82,14 @@ fi
if [ "$APPLE_FAMILY" = "1" ]; then
# On Mac, only link with OpenSSL if OSSL is defined.
# On other platforms, usually link with OpenSSL.
if [ "$OPENSSL_SYS" == "1" ]; then
NO_DEPRECATE="-Wno-deprecated-declarations"
if [ "$OPENSSL_SYS" != "1" ]; then
else
NO_DEPRECATE=""
fi
if [ "$OSSL" = "1" ]; then
if [ "$OPENSSL_LINK" = "1" ]; then
LIBS="$LIBS -lcrypto -lssl"
elif [ "$OSSL" = "1" ]; then
CPPFLAGS="$CPPFLAGS -DUSE_OPENSSL"
LIBS="$LIBS -lcrypto -lssl"
[ "$CLANG" = "1" ] && [ "$NO_DEPRECATE" ] && FLAGS="$FLAGS $NO_DEPRECATE"
@ -97,6 +100,7 @@ if [ "$APPLE_FAMILY" = "1" ]; then
elif [ "$PSSL" = "1" ]; then
true
else
NOSSL=1
CPPFLAGS="$CPPFLAGS -DUSE_APPLE_SSL"
fi
LIBS="$LIBS -framework Security"
@ -106,7 +110,7 @@ else
LIBS="$LIBS -lssl -lcrypto -ldl"
fi
fi
if [ "$OPENSSL_SYS" != "1" ] && [ "$NOSSL" != "1" ]; then
if [ "$OPENSSL_SYS" != "1" ] && [ "$OPENSSL_LINK" != "1" ] && [ "$NOSSL" != "1" ]; then
CPPFLAGS="$CPPFLAGS -I$DEP_DIR/openssl/openssl-$PLATFORM/include"
LIBDIRS="$LIBDIRS -L$DEP_DIR/openssl/openssl-$PLATFORM/lib"
fi

View File

@ -29,6 +29,6 @@ fi
# osx
for target in osx osx-dbg ; do
echo '***************' TARGET $target
VERBOSE=1 TARGET=$target ENABLE_SERVER=1 $O3/polarssl/build-polarssl
VERBOSE=1 TARGET=$target USE_MINICRYPTO=1 ENABLE_SERVER=1 $O3/polarssl/build-polarssl
mv polarssl-$target polarssl/
done