diff --git a/src/openvpn/openssl_compat.h b/src/openvpn/openssl_compat.h index 7a323be5..f702afde 100644 --- a/src/openvpn/openssl_compat.h +++ b/src/openvpn/openssl_compat.h @@ -760,6 +760,14 @@ int EVP_PKEY_get_group_name(EVP_PKEY *pkey, char *gname, size_t gname_sz, #define EVP_CIPHER_get0_name EVP_CIPHER_name #define EVP_CIPHER_CTX_get_mode EVP_CIPHER_CTX_mode +/** Reduce SSL_CTX_new_ex() to SSL_CTX_new() for OpenSSL < 3 */ +#define SSL_CTX_new_ex(libctx, propq, method) \ + SSL_CTX_new((method)) + +/* Some safe typedefs to avoid too many ifdefs */ +typedef void OSSL_LIB_CTX; +typedef void OSSL_PROVIDER; + /* Mimics the functions but only when the default context without * options is chosen */ static inline const EVP_CIPHER * diff --git a/src/openvpn/options.c b/src/openvpn/options.c index bf8e7759..aafec1a2 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -5286,6 +5286,22 @@ show_compression_warning(struct compress_options *info) } #endif +bool key_is_external(const struct options *options) +{ + bool ret = false; +#ifdef ENABLE_MANAGEMENT + ret = ret || (options->management_flags & MF_EXTERNAL_KEY); +#endif +#ifdef ENABLE_PKCS11 + ret = ret || (options->pkcs11_providers[0] != NULL); +#endif +#ifdef ENABLE_CRYPTOAPI + ret = ret || options->cryptoapi_cert; +#endif + + return ret; +} + static void add_option(struct options *options, char *p[], diff --git a/src/openvpn/options.h b/src/openvpn/options.h index d4f41cd7..8dc06343 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -862,4 +862,6 @@ void options_string_import(struct options *options, unsigned int *option_types_found, struct env_set *es); +bool key_is_external(const struct options *options); + #endif /* ifndef OPTIONS_H */ diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index db0e7281..891355fb 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -604,6 +604,11 @@ init_ssl(const struct options *options, struct tls_root_ctx *new_ctx, bool in_ch tls_clear_error(); + if (key_is_external(options)) + { + load_xkey_provider(); + } + if (options->tls_server) { tls_ctx_server_new(new_ctx); diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h index b14453fe..784ddd32 100644 --- a/src/openvpn/ssl.h +++ b/src/openvpn/ssl.h @@ -627,4 +627,10 @@ show_available_tls_ciphers(const char *cipher_list, bool tls_session_generate_data_channel_keys(struct tls_session *session); +/** + * Load ovpn.xkey provider used for external key signing + */ +void +load_xkey_provider(void); + #endif /* ifndef OPENVPN_SSL_H */ diff --git a/src/openvpn/ssl_mbedtls.c b/src/openvpn/ssl_mbedtls.c index 5624c514..403499d1 100644 --- a/src/openvpn/ssl_mbedtls.c +++ b/src/openvpn/ssl_mbedtls.c @@ -1549,4 +1549,10 @@ get_ssl_library_version(void) return mbedtls_version; } +void +load_xkey_provider(void) +{ + return; /* no external key provider in mbedTLS build */ +} + #endif /* defined(ENABLE_CRYPTO_MBEDTLS) */ diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c index 724664bb..bdaa7a2b 100644 --- a/src/openvpn/ssl_openssl.c +++ b/src/openvpn/ssl_openssl.c @@ -45,6 +45,7 @@ #include "ssl_common.h" #include "base64.h" #include "openssl_compat.h" +#include "xkey_common.h" #ifdef ENABLE_CRYPTOAPI #include "cryptoapi.h" @@ -69,6 +70,10 @@ #include #endif +static OSSL_LIB_CTX *tls_libctx; + +static void unload_xkey_provider(void); + /* * Allocate space in SSL objects in which to store a struct tls_session * pointer back to parent. @@ -113,7 +118,7 @@ tls_ctx_server_new(struct tls_root_ctx *ctx) { ASSERT(NULL != ctx); - ctx->ctx = SSL_CTX_new(SSLv23_server_method()); + ctx->ctx = SSL_CTX_new_ex(tls_libctx, NULL, SSLv23_server_method()); if (ctx->ctx == NULL) { @@ -131,7 +136,7 @@ tls_ctx_client_new(struct tls_root_ctx *ctx) { ASSERT(NULL != ctx); - ctx->ctx = SSL_CTX_new(SSLv23_client_method()); + ctx->ctx = SSL_CTX_new_ex(tls_libctx, NULL, SSLv23_client_method()); if (ctx->ctx == NULL) { @@ -150,6 +155,7 @@ tls_ctx_free(struct tls_root_ctx *ctx) ASSERT(NULL != ctx); SSL_CTX_free(ctx->ctx); ctx->ctx = NULL; + unload_xkey_provider(); /* in case it is loaded */ } bool @@ -2284,4 +2290,87 @@ get_ssl_library_version(void) return OpenSSL_version(OPENSSL_VERSION); } + +/** Some helper routines for provider load/unload */ +#ifdef HAVE_XKEY_PROVIDER +static int +provider_load(OSSL_PROVIDER *prov, void *dest_libctx) +{ + const char *name = OSSL_PROVIDER_get0_name(prov); + OSSL_PROVIDER_load(dest_libctx, name); + return 1; +} + +static int +provider_unload(OSSL_PROVIDER *prov, void *unused) +{ + (void) unused; + OSSL_PROVIDER_unload(prov); + return 1; +} +#endif /* HAVE_XKEY_PROVIDER */ + +/** + * Setup ovpn.xey provider for signing with external keys. + * It is loaded into a custom library context so as not to pollute + * the default context. Alternatively we could override any + * system-wide property query set on the default context. But we + * want to avoid that. + */ +void +load_xkey_provider(void) +{ +#ifdef HAVE_XKEY_PROVIDER + + /* Make a new library context for use in TLS context */ + if (!tls_libctx) + { + tls_libctx = OSSL_LIB_CTX_new(); + check_malloc_return(tls_libctx); + + /* Load all providers in default LIBCTX into this libctx. + * OpenSSL has a child libctx functionality to automate this, + * but currently that is usable only from within providers. + * So we do something close to it manually here. + */ + OSSL_PROVIDER_do_all(NULL, provider_load, tls_libctx); + } + + if (!OSSL_PROVIDER_available(tls_libctx, "ovpn.xkey")) + { + OSSL_PROVIDER_add_builtin(tls_libctx, "ovpn.xkey", xkey_provider_init); + if (!OSSL_PROVIDER_load(tls_libctx, "ovpn.xkey")) + { + msg(M_NONFATAL, "ERROR: failed loading external key provider: " + "Signing with external keys will not work."); + } + } + + /* We only implement minimal functionality in ovpn.xkey, so we do not want + * methods in xkey to be picked unless absolutely required (i.e, when the key + * is external). Ensure this by setting a default propquery for the custom + * libctx that unprefers, but does not forbid, ovpn.xkey. See also man page + * of "property" in OpenSSL 3.0. + */ + EVP_set_default_properties(tls_libctx, "?provider!=ovpn.xkey"); + +#endif /* HAVE_XKEY_PROVIDER */ +} + +/** + * Undo steps in load_xkey_provider + */ +static void +unload_xkey_provider(void) +{ +#ifdef HAVE_XKEY_PROVIDER + if (tls_libctx) + { + OSSL_PROVIDER_do_all(tls_libctx, provider_unload, NULL); + OSSL_LIB_CTX_free(tls_libctx); + } +#endif /* HAVE_XKEY_PROVIDER */ + tls_libctx = NULL; +} + #endif /* defined(ENABLE_CRYPTO_OPENSSL) */ diff --git a/src/openvpn/xkey_common.h b/src/openvpn/xkey_common.h index db58d077..f46bacd2 100644 --- a/src/openvpn/xkey_common.h +++ b/src/openvpn/xkey_common.h @@ -28,7 +28,6 @@ #include #if OPENSSL_VERSION_NUMBER >= 0x30000010L && !defined(DISABLE_XKEY_PROVIDER) #define HAVE_XKEY_PROVIDER 1 - #include #include