diff --git a/client/ovpncli.cpp b/client/ovpncli.cpp index 9bba9a79..ba755267 100644 --- a/client/ovpncli.cpp +++ b/client/ovpncli.cpp @@ -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 &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, const std::string &clientkey, const std::optional &ca) @@ -1344,18 +1374,39 @@ OPENVPN_CLIENT_EXPORT void OpenVPNClient::start_cert_check(const std::string &cl { ClientConnect *session = state->session.get(); 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, - const std::optional &ca) +OPENVPN_CLIENT_EXPORT void OpenVPNClient::start_cert_check_epki(const std::string &alias, const std::optional &ca) { if (state->is_foreign_thread_access()) { ClientConnect *session = state->session.get(); 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); + } } } diff --git a/client/ovpncli.hpp b/client/ovpncli.hpp index 1e9c4113..cf543314 100644 --- a/client/ovpncli.hpp +++ b/client/ovpncli.hpp @@ -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 - @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 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 the handshake object will be reset and the handshake will be restarted. */ - void start_cert_check(ExternalPKIBase *external_pki_arg, - const std::optional &ca = std::nullopt); + void start_cert_check_epki(const std::string &alias, const std::optional &ca); // Callback for delivering events during connect() call. // Will be called from the thread executing connect(). diff --git a/doc/core.md b/doc/core.md index 5fe94ba7..4d2c8d5f 100644 --- a/doc/core.md +++ b/doc/core.md @@ -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: - void start_cert_check_epki(ExternalPKIBase *external_pki_arg, - const std::optional &ca = std::nullopt); + void start_cert_check_epki(const std::string &alias); Both found in ovpn3/core/client/ovpncli.hpp diff --git a/openvpn/client/cliproto.hpp b/openvpn/client/cliproto.hpp index 5a0f8965..55e86a8b 100644 --- a/openvpn/client/cliproto.hpp +++ b/openvpn/client/cliproto.hpp @@ -307,60 +307,14 @@ class Session : ProtoContextCallbackInterface, return temp_fail_backoff_; } - - SSLLib::SSLAPI::Config::Ptr setup_certcheck_ssl_config(const std::string &client_cert, - const std::optional &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 - @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 + @param config SSL Config setup with the correct keys and certificates - 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. + Begins the handshake with Client Hello via the ACC. */ - void start_acc_certcheck(const std::string &client_cert, - const std::string &clientkey, - const std::optional &ca) + void start_acc_certcheck(SSLLib::SSLAPI::Config::Ptr config) { - 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 &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)); do_acc_certcheck(std::string("")); } diff --git a/test/ovpncli/cli.cpp b/test/ovpncli/cli.cpp index f0fd0a0c..23c9f0b8 100644 --- a/test/ovpncli/cli.cpp +++ b/test/ovpncli/cli.cpp @@ -242,13 +242,14 @@ class Client : public ClientBase std::string epki_ca; std::string epki_cert; #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) openvpn::OpenSSLPKI::PKey epki_pkey; + openvpn::OpenSSLPKI::PKey certcheck_pkey; #endif - std::string certcheck_cert_fn; ///< file name of properly encoded server cert - std::string certcheck_pkey_fn; ///< file name of properly encoded private key + std::string certcheck_cert; ///< file name of properly encoded server cert std::string certcheck_clientca_fn; ///< file name of properly encoded client ca void set_clock_tick_action(const ClockTickAction action) @@ -361,23 +362,33 @@ class Client : public ClientBase */ 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; return; } - auto cert_pem = ::openvpn::read_text_utf8(certcheck_cert_fn); - auto pkey_pem = ::openvpn::read_text_utf8(certcheck_pkey_fn); + std::string clientca_pem = ""; 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); - start_cert_check(cert_pem, pkey_pem, clientca_pem); + /* cli.cpp typically goes the long complicated way of using the epki API to do its checks */ + + 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 { - 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 { - if (!epki_cert.empty()) + if (certreq.alias == "epki" && !epki_cert.empty()) { certreq.cert = epki_cert; 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 { certreq.error = true; @@ -483,7 +499,8 @@ class Client : public ClientBase base64->decode(signdata, signreq.data); 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); @@ -574,6 +591,9 @@ class Client : public ClientBase } EVP_PKEY *pkey = epki_pkey.obj(); + if (signreq.alias == "certcheck") + pkey = certcheck_pkey.obj(); + OSSL_PARAM params[6] = {OSSL_PARAM_END}; char *hashalg = const_cast(signreq.hashalg.c_str()); @@ -678,7 +698,9 @@ class Client : public ClientBase } else #elif defined(USE_OPENSSL) - if (epki_pkey.defined()) + + if ((epki_pkey.defined() && signreq.alias == "epki") + || (certcheck_pkey.defined() && signreq.alias == "certcheck")) { try { @@ -1058,8 +1080,8 @@ int openvpn_client(int argc, char *argv[], const std::string *profile_content) std::string gremlin; std::string ssoMethods; std::string appCustomProtocols; - std::string certcheck_cert; - std::string certcheck_pkey; + std::string certcheck_cert_fn; + std::string certcheck_pkey_fn; std::string certcheck_clientca; bool eval = 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_ca_fn; std::string epki_key_fn; + #ifdef OPENVPN_REMOTE_OVERRIDE std::string remote_override_cmd; #endif @@ -1241,10 +1264,10 @@ int openvpn_client(int argc, char *argv[], const std::string *profile_content) appCustomProtocols = optarg; break; case 'o': - certcheck_cert = optarg; + certcheck_cert_fn = optarg; break; case 'O': - certcheck_pkey = optarg; + certcheck_pkey_fn = optarg; break; case 'b': 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); } - client.certcheck_cert_fn = certcheck_cert; - client.certcheck_pkey_fn = certcheck_pkey; client.certcheck_clientca_fn = certcheck_clientca; // external PKI @@ -1460,6 +1481,19 @@ int openvpn_client(int argc, char *argv[], const std::string *profile_content) #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(); + client.certcheck_pkey.parse(epki_key_txt, "Certcheck", privateKeyPassword, *mbedrng); +#endif + } + #ifdef OPENVPN_REMOTE_OVERRIDE client.set_remote_override_cmd(remote_override_cmd); #endif