0
0
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:
Arne Schwabe 2024-05-21 17:05:43 +02:00 committed by Jenkins-dev
parent 0cafed2016
commit d554fdcd8e
5 changed files with 113 additions and 76 deletions

View File

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

View File

@ -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().

View File

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

View File

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

View File

@ -242,13 +242,14 @@ class Client : public ClientBase
std::string epki_ca; std::string epki_ca;
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
{
start_cert_check(certcheck_cert, certcheck_pkey.render_pem());
}
} }
else else
{ {
start_cert_check(cert_pem, pkey_pem); 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