mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-19 19:52:15 +02:00
Properly implement certcheck using EPKI
This also moves some of the reasonsibility from ClientProto to OpenVPNClient. This plays better with the EPKI implementation and also does not break the idea of the current certcheck implementation as we now just give the certcheck in client protocol a preconfigured SSL Config instead of all the certificates individually.
This commit is contained in:
parent
0cafed2016
commit
d554fdcd8e
@ -1336,6 +1336,36 @@ OPENVPN_CLIENT_EXPORT void OpenVPNClient::send_app_control_channel_msg(const std
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SSLLib::SSLAPI::Config::Ptr setup_certcheck_ssl_config(const std::string &client_cert,
|
||||||
|
const std::string &extra_certs,
|
||||||
|
const std::optional<const std::string> &ca)
|
||||||
|
{
|
||||||
|
SSLLib::SSLAPI::Config::Ptr config = new SSLLib::SSLAPI::Config;
|
||||||
|
config->set_frame(new Frame(Frame::Context(128, 4096, 4096 - 128, 0, 16, 0)));
|
||||||
|
config->set_mode(Mode(Mode::CLIENT));
|
||||||
|
config->load_cert(client_cert, extra_certs);
|
||||||
|
unsigned int flags = SSLConst::LOG_VERIFY_STATUS;
|
||||||
|
|
||||||
|
if (ca)
|
||||||
|
config->load_ca(*ca, false);
|
||||||
|
else
|
||||||
|
flags |= SSLConfigAPI::LF_ALLOW_CLIENT_CERT_NOT_REQUIRED;
|
||||||
|
|
||||||
|
config->set_flags(flags);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Start up the cert check handshake using the given certs and key
|
||||||
|
@param client_cert String containing the properly encoded client certificate
|
||||||
|
@param clientkey String containing the properly encoded private key for \p client_cert
|
||||||
|
@param ca String containing the properly encoded authority
|
||||||
|
|
||||||
|
Creates, initializes,and installs an SSLLib::SSLAPI::Config object into the TLS
|
||||||
|
handshake object we use for the certcheck function. Then begins the handshake
|
||||||
|
with Client Hello via the ACC by calling start_acc_certcheck.
|
||||||
|
*/
|
||||||
OPENVPN_CLIENT_EXPORT void OpenVPNClient::start_cert_check(const std::string &client_cert,
|
OPENVPN_CLIENT_EXPORT void OpenVPNClient::start_cert_check(const std::string &client_cert,
|
||||||
const std::string &clientkey,
|
const std::string &clientkey,
|
||||||
const std::optional<const std::string> &ca)
|
const std::optional<const std::string> &ca)
|
||||||
@ -1344,18 +1374,39 @@ OPENVPN_CLIENT_EXPORT void OpenVPNClient::start_cert_check(const std::string &cl
|
|||||||
{
|
{
|
||||||
ClientConnect *session = state->session.get();
|
ClientConnect *session = state->session.get();
|
||||||
if (session)
|
if (session)
|
||||||
session->start_acc_certcheck(client_cert, clientkey, ca);
|
{
|
||||||
|
SSLLib::SSLAPI::Config::Ptr config = setup_certcheck_ssl_config(client_cert, "", ca);
|
||||||
|
config->load_private_key(clientkey);
|
||||||
|
|
||||||
|
session->start_acc_certcheck(config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OPENVPN_CLIENT_EXPORT void OpenVPNClient::start_cert_check(ExternalPKIBase *external_pki_arg,
|
OPENVPN_CLIENT_EXPORT void OpenVPNClient::start_cert_check_epki(const std::string &alias, const std::optional<const std::string> &ca)
|
||||||
const std::optional<const std::string> &ca)
|
|
||||||
{
|
{
|
||||||
if (state->is_foreign_thread_access())
|
if (state->is_foreign_thread_access())
|
||||||
{
|
{
|
||||||
ClientConnect *session = state->session.get();
|
ClientConnect *session = state->session.get();
|
||||||
if (session)
|
if (session)
|
||||||
session->start_acc_certcheck(external_pki_arg, ca);
|
{
|
||||||
|
ClientAPI::ExternalPKICertRequest req;
|
||||||
|
req.alias = alias;
|
||||||
|
external_pki_cert_request(req);
|
||||||
|
|
||||||
|
if (req.error)
|
||||||
|
{
|
||||||
|
external_pki_error(req, Error::EPKI_CERT_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSLLib::SSLAPI::Config::Ptr config = setup_certcheck_ssl_config(req.cert, req.supportingChain, ca);
|
||||||
|
|
||||||
|
config->set_external_pki_callback(this, alias);
|
||||||
|
|
||||||
|
|
||||||
|
session->start_acc_certcheck(config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,15 +696,14 @@ class OpenVPNClient : public TunBuilderBase, // expose tun builder v
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Start up the cert check handshake using the given epki_alias string
|
@brief Start up the cert check handshake using the given epki_alias string
|
||||||
@param external_pki_arg EPKI callback object
|
@param alias string containing the epki used for callbacks for certificate and signing operations
|
||||||
@param ca Optional string containing the properly encoded authority
|
@param ca Optional string containing the properly encoded authority
|
||||||
|
|
||||||
This function forwards to ClientProto::Session::start_acc_certcheck, which sets up the
|
This function forwards to ClientProto::Session::start_acc_certcheck, which sets up the
|
||||||
session ACC certcheck TLS handshake object. Every time this function is called the state of
|
session ACC certcheck TLS handshake object. Every time this function is called the state of
|
||||||
the handshake object will be reset and the handshake will be restarted.
|
the handshake object will be reset and the handshake will be restarted.
|
||||||
*/
|
*/
|
||||||
void start_cert_check(ExternalPKIBase *external_pki_arg,
|
void start_cert_check_epki(const std::string &alias, const std::optional<const std::string> &ca);
|
||||||
const std::optional<const std::string> &ca = std::nullopt);
|
|
||||||
|
|
||||||
// Callback for delivering events during connect() call.
|
// Callback for delivering events during connect() call.
|
||||||
// Will be called from the thread executing connect().
|
// Will be called from the thread executing connect().
|
||||||
|
@ -184,8 +184,7 @@ For the MVP the start_cert_check API accepts encoded certs and keys as std::stri
|
|||||||
|
|
||||||
Alternatively one can use the EPKI enabled API:
|
Alternatively one can use the EPKI enabled API:
|
||||||
|
|
||||||
void start_cert_check_epki(ExternalPKIBase *external_pki_arg,
|
void start_cert_check_epki(const std::string &alias);
|
||||||
const std::optional<const std::string> &ca = std::nullopt);
|
|
||||||
|
|
||||||
Both found in ovpn3/core/client/ovpncli.hpp
|
Both found in ovpn3/core/client/ovpncli.hpp
|
||||||
|
|
||||||
|
@ -307,60 +307,14 @@ class Session : ProtoContextCallbackInterface,
|
|||||||
return temp_fail_backoff_;
|
return temp_fail_backoff_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SSLLib::SSLAPI::Config::Ptr setup_certcheck_ssl_config(const std::string &client_cert,
|
|
||||||
const std::optional<const std::string> &ca)
|
|
||||||
{
|
|
||||||
SSLLib::SSLAPI::Config::Ptr config = new SSLLib::SSLAPI::Config;
|
|
||||||
config->set_frame(new Frame(Frame::Context(128, 4096, 4096 - 128, 0, 16, 0)));
|
|
||||||
config->set_mode(Mode(Mode::CLIENT));
|
|
||||||
config->load_cert(client_cert);
|
|
||||||
unsigned int flags = SSLConst::LOG_VERIFY_STATUS;
|
|
||||||
|
|
||||||
if (ca)
|
|
||||||
config->load_ca(*ca, false);
|
|
||||||
else
|
|
||||||
flags |= SSLConfigAPI::LF_ALLOW_CLIENT_CERT_NOT_REQUIRED;
|
|
||||||
|
|
||||||
config->set_flags(flags);
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Start up the cert check handshake using the given certs and key
|
@brief Start up the cert check handshake using the given certs and key
|
||||||
@param client_cert String containing the properly encoded client certificate
|
@param config SSL Config setup with the correct keys and certificates
|
||||||
@param clientkey String containing the properly encoded private key for \p client_cert
|
|
||||||
@param ca String containing the properly encoded authority
|
|
||||||
|
|
||||||
Creates, initializes,and installs an SSLLib::SSLAPI::Config object into the TLS
|
Begins the handshake with Client Hello via the ACC.
|
||||||
handshake object we use for the certcheck function. Then begins the handshake
|
|
||||||
with Client Hello via the ACC.
|
|
||||||
*/
|
*/
|
||||||
void start_acc_certcheck(const std::string &client_cert,
|
void start_acc_certcheck(SSLLib::SSLAPI::Config::Ptr config)
|
||||||
const std::string &clientkey,
|
|
||||||
const std::optional<const std::string> &ca)
|
|
||||||
{
|
{
|
||||||
SSLLib::SSLAPI::Config::Ptr config = setup_certcheck_ssl_config(client_cert, ca);
|
|
||||||
config->load_private_key(clientkey);
|
|
||||||
certcheck_hs.reset(std::move(config));
|
|
||||||
do_acc_certcheck(std::string(""));
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
@brief Start up the cert check handshake using the given epki_alias string
|
|
||||||
@param external_pki_arg EPKI callback object
|
|
||||||
@param ca String containing the properly encoded authority
|
|
||||||
|
|
||||||
Creates, initializes,and installs an SSLLib::SSLAPI::Config object into the TLS
|
|
||||||
handshake object we use for the certcheck function. Then begins the handshake
|
|
||||||
with Client Hello via the ACC.
|
|
||||||
*/
|
|
||||||
void start_acc_certcheck(ExternalPKIBase *external_pki_arg,
|
|
||||||
const std::optional<const std::string> &ca)
|
|
||||||
{
|
|
||||||
SSLLib::SSLAPI::Config::Ptr config = setup_certcheck_ssl_config("", ca);
|
|
||||||
config->set_external_pki_callback(external_pki_arg, "certcheck");
|
|
||||||
|
|
||||||
certcheck_hs.reset(std::move(config));
|
certcheck_hs.reset(std::move(config));
|
||||||
do_acc_certcheck(std::string(""));
|
do_acc_certcheck(std::string(""));
|
||||||
}
|
}
|
||||||
|
@ -243,12 +243,13 @@ class Client : public ClientBase
|
|||||||
std::string epki_cert;
|
std::string epki_cert;
|
||||||
#if defined(USE_MBEDTLS)
|
#if defined(USE_MBEDTLS)
|
||||||
MbedTLSPKI::PKContext epki_ctx; // external PKI context
|
MbedTLSPKI::PKContext epki_ctx; // external PKI context
|
||||||
|
MbedTLSPKI::PKContext certcheck_pkey; // external PKI context
|
||||||
#elif defined(USE_OPENSSL)
|
#elif defined(USE_OPENSSL)
|
||||||
openvpn::OpenSSLPKI::PKey epki_pkey;
|
openvpn::OpenSSLPKI::PKey epki_pkey;
|
||||||
|
openvpn::OpenSSLPKI::PKey certcheck_pkey;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string certcheck_cert_fn; ///< file name of properly encoded server cert
|
std::string certcheck_cert; ///< file name of properly encoded server cert
|
||||||
std::string certcheck_pkey_fn; ///< file name of properly encoded private key
|
|
||||||
std::string certcheck_clientca_fn; ///< file name of properly encoded client ca
|
std::string certcheck_clientca_fn; ///< file name of properly encoded client ca
|
||||||
|
|
||||||
void set_clock_tick_action(const ClockTickAction action)
|
void set_clock_tick_action(const ClockTickAction action)
|
||||||
@ -361,23 +362,33 @@ class Client : public ClientBase
|
|||||||
*/
|
*/
|
||||||
void handle_certcheck_request()
|
void handle_certcheck_request()
|
||||||
{
|
{
|
||||||
if (certcheck_cert_fn.empty() || certcheck_pkey_fn.empty())
|
if (certcheck_cert.empty() || !certcheck_pkey.defined())
|
||||||
{
|
{
|
||||||
std::cout << "ACC CERTCHECK FAILED: MISSING PARAMETERS" << std::endl;
|
std::cout << "ACC CERTCHECK FAILED: MISSING PARAMETERS" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cert_pem = ::openvpn::read_text_utf8(certcheck_cert_fn);
|
std::string clientca_pem = "";
|
||||||
auto pkey_pem = ::openvpn::read_text_utf8(certcheck_pkey_fn);
|
|
||||||
|
|
||||||
if (!certcheck_clientca_fn.empty())
|
if (!certcheck_clientca_fn.empty())
|
||||||
|
clientca_pem = ::openvpn::read_text_utf8(certcheck_clientca_fn);
|
||||||
|
|
||||||
|
if constexpr (false)
|
||||||
{
|
{
|
||||||
auto clientca_pem = ::openvpn::read_text_utf8(certcheck_clientca_fn);
|
/* cli.cpp typically goes the long complicated way of using the epki API to do its checks */
|
||||||
start_cert_check(cert_pem, pkey_pem, clientca_pem);
|
|
||||||
|
if (!clientca_pem.empty())
|
||||||
|
{
|
||||||
|
start_cert_check(certcheck_cert, certcheck_pkey.render_pem(), clientca_pem);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
start_cert_check(cert_pem, pkey_pem);
|
start_cert_check(certcheck_cert, certcheck_pkey.render_pem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
start_cert_check_epki("certcheck", clientca_pem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,11 +473,16 @@ class Client : public ClientBase
|
|||||||
|
|
||||||
virtual void external_pki_cert_request(ClientAPI::ExternalPKICertRequest &certreq) override
|
virtual void external_pki_cert_request(ClientAPI::ExternalPKICertRequest &certreq) override
|
||||||
{
|
{
|
||||||
if (!epki_cert.empty())
|
if (certreq.alias == "epki" && !epki_cert.empty())
|
||||||
{
|
{
|
||||||
certreq.cert = epki_cert;
|
certreq.cert = epki_cert;
|
||||||
certreq.supportingChain = epki_ca;
|
certreq.supportingChain = epki_ca;
|
||||||
}
|
}
|
||||||
|
else if (certreq.alias == "certcheck" && !certcheck_cert.empty())
|
||||||
|
{
|
||||||
|
certreq.cert = certcheck_cert;
|
||||||
|
certreq.supportingChain = ::openvpn::read_text_utf8(certcheck_clientca_fn);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
certreq.error = true;
|
certreq.error = true;
|
||||||
@ -483,7 +499,8 @@ class Client : public ClientBase
|
|||||||
base64->decode(signdata, signreq.data);
|
base64->decode(signdata, signreq.data);
|
||||||
|
|
||||||
EVP_PKEY *pkey = epki_pkey.obj();
|
EVP_PKEY *pkey = epki_pkey.obj();
|
||||||
|
if (signreq.alias == "certcheck")
|
||||||
|
pkey = certcheck_pkey.obj();
|
||||||
|
|
||||||
PKEY_CTX_unique_ptr pkey_ctx(EVP_PKEY_CTX_new(pkey, nullptr), EVP_PKEY_CTX_free);
|
PKEY_CTX_unique_ptr pkey_ctx(EVP_PKEY_CTX_new(pkey, nullptr), EVP_PKEY_CTX_free);
|
||||||
|
|
||||||
@ -574,6 +591,9 @@ class Client : public ClientBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
EVP_PKEY *pkey = epki_pkey.obj();
|
EVP_PKEY *pkey = epki_pkey.obj();
|
||||||
|
if (signreq.alias == "certcheck")
|
||||||
|
pkey = certcheck_pkey.obj();
|
||||||
|
|
||||||
OSSL_PARAM params[6] = {OSSL_PARAM_END};
|
OSSL_PARAM params[6] = {OSSL_PARAM_END};
|
||||||
|
|
||||||
char *hashalg = const_cast<char *>(signreq.hashalg.c_str());
|
char *hashalg = const_cast<char *>(signreq.hashalg.c_str());
|
||||||
@ -678,7 +698,9 @@ class Client : public ClientBase
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
#elif defined(USE_OPENSSL)
|
#elif defined(USE_OPENSSL)
|
||||||
if (epki_pkey.defined())
|
|
||||||
|
if ((epki_pkey.defined() && signreq.alias == "epki")
|
||||||
|
|| (certcheck_pkey.defined() && signreq.alias == "certcheck"))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -1058,8 +1080,8 @@ int openvpn_client(int argc, char *argv[], const std::string *profile_content)
|
|||||||
std::string gremlin;
|
std::string gremlin;
|
||||||
std::string ssoMethods;
|
std::string ssoMethods;
|
||||||
std::string appCustomProtocols;
|
std::string appCustomProtocols;
|
||||||
std::string certcheck_cert;
|
std::string certcheck_cert_fn;
|
||||||
std::string certcheck_pkey;
|
std::string certcheck_pkey_fn;
|
||||||
std::string certcheck_clientca;
|
std::string certcheck_clientca;
|
||||||
bool eval = false;
|
bool eval = false;
|
||||||
bool self_test = false;
|
bool self_test = false;
|
||||||
@ -1087,6 +1109,7 @@ int openvpn_client(int argc, char *argv[], const std::string *profile_content)
|
|||||||
std::string epki_cert_fn;
|
std::string epki_cert_fn;
|
||||||
std::string epki_ca_fn;
|
std::string epki_ca_fn;
|
||||||
std::string epki_key_fn;
|
std::string epki_key_fn;
|
||||||
|
|
||||||
#ifdef OPENVPN_REMOTE_OVERRIDE
|
#ifdef OPENVPN_REMOTE_OVERRIDE
|
||||||
std::string remote_override_cmd;
|
std::string remote_override_cmd;
|
||||||
#endif
|
#endif
|
||||||
@ -1241,10 +1264,10 @@ int openvpn_client(int argc, char *argv[], const std::string *profile_content)
|
|||||||
appCustomProtocols = optarg;
|
appCustomProtocols = optarg;
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
certcheck_cert = optarg;
|
certcheck_cert_fn = optarg;
|
||||||
break;
|
break;
|
||||||
case 'O':
|
case 'O':
|
||||||
certcheck_pkey = optarg;
|
certcheck_pkey_fn = optarg;
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
certcheck_clientca = optarg;
|
certcheck_clientca = optarg;
|
||||||
@ -1433,8 +1456,6 @@ int openvpn_client(int argc, char *argv[], const std::string *profile_content)
|
|||||||
OPENVPN_THROW_EXCEPTION("creds error: " << creds_status.message);
|
OPENVPN_THROW_EXCEPTION("creds error: " << creds_status.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
client.certcheck_cert_fn = certcheck_cert;
|
|
||||||
client.certcheck_pkey_fn = certcheck_pkey;
|
|
||||||
client.certcheck_clientca_fn = certcheck_clientca;
|
client.certcheck_clientca_fn = certcheck_clientca;
|
||||||
|
|
||||||
// external PKI
|
// external PKI
|
||||||
@ -1460,6 +1481,19 @@ int openvpn_client(int argc, char *argv[], const std::string *profile_content)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Certcheck
|
||||||
|
if (!certcheck_cert_fn.empty())
|
||||||
|
{
|
||||||
|
client.certcheck_cert = read_text_utf8(certcheck_cert_fn);
|
||||||
|
const std::string epki_key_txt = read_text_utf8(certcheck_pkey_fn);
|
||||||
|
#ifdef USE_OPENSSL
|
||||||
|
client.certcheck_pkey.parse_pem(epki_key_txt, "certcheck private key", nullptr);
|
||||||
|
#else
|
||||||
|
auto mbedrng = std::make_unique<MbedTLSRandom>();
|
||||||
|
client.certcheck_pkey.parse(epki_key_txt, "Certcheck", privateKeyPassword, *mbedrng);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef OPENVPN_REMOTE_OVERRIDE
|
#ifdef OPENVPN_REMOTE_OVERRIDE
|
||||||
client.set_remote_override_cmd(remote_override_cmd);
|
client.set_remote_override_cmd(remote_override_cmd);
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user