It seems no one is interested in this to elicit a review.. I thought this would be a nifty feature ;)
On Sun, May 9, 2021 at 9:32 PM <selva.n...@gmail.com> wrote: > From: Selva Nair <selva.n...@gmail.com> > > v2 changes > - do not allow so-path embedded in cert and key uri > - add --pkcs11-engine option to optionally specify the > engine and provider module to use > > If either --cert or --key is specified as a PKCS#11 uri, try to > load the certificate and key from any accessible PKCS#11 device. > This does not require linking with any pkcs11 library, but needs > pkcs11 engine to be available on the target machine. > > In its simplest form, just have > > --cert 'pkcs11:id=%01' > > Either do not specify --key, or use the same uri for --key. > Do not include type=cert or type=private in the uri > as the same uri is used for both certificate and private key. > > That's all what is required if pkcs11 engine is installed in the > right location and optionally set up to load any necessary provider > libraries (e.g., via openssl.cnf or via PKCS11_MODULE_PATH). > > If both cert and key are specified, the last entry takes precedence > and is used to locate both the certificate and key. Use of different > uri's for the cert and key are not supported. Specifying --cert as > a file and --key as a uri or vice versa is treated as a usage error. > > If the engine cannot be automatically loaded, or a custom engine object > has to be loaded, the engine name or shared library may be specified > using the newly added option > > --pkcs11-engine engine [module_path] > > Here engine may the the engine-id that OpenSSL is configured to locate, > or the path to a shared object. The optional 'module_path' specifies > any provider module that must be loaded. It must be given as a path. > Use full path or relative path for these shared objects based on the > target system setup. > > Requires building with OpenSSL engine support although the pkcs11 or > a compatible engine, and provider libraries are required only at > run time. > > Signed-off-by: Selva Nair <selva.n...@gmail.com> > --- > Changes.rst | 6 + > doc/man-sections/tls-options.rst | 31 ++++++ > src/openvpn/options.c | 68 +++++++++++- > src/openvpn/options.h | 7 ++ > src/openvpn/ssl.c | 15 ++- > src/openvpn/ssl_backend.h | 10 ++ > src/openvpn/ssl_openssl.c | 183 ++++++++++++++++++++++++++++++- > 7 files changed, 316 insertions(+), 4 deletions(-) > > diff --git a/Changes.rst b/Changes.rst > index 9185b55f..19d311e3 100644 > --- a/Changes.rst > +++ b/Changes.rst > @@ -4,6 +4,12 @@ Overview of changes in 2.6 > > New features > ------------ > +Specification of private key and certificates as PKCS#11 URI > + ``--cert`` and ``--key`` options can take RFC7512 PKCS#11 > + URI's pointing to certificate and key in a token. Both cert > + and key must use the same URI. Requires OpenSSL with engine > + support and pkcs11 (or compatible) engine installed. > + > Keying Material Exporters (RFC 5705) based key generation > As part of the cipher negotiation OpenVPN will automatically prefer > the RFC5705 based key material generation to the current custom > diff --git a/doc/man-sections/tls-options.rst > b/doc/man-sections/tls-options.rst > index 00ea063a..7acfbdae 100644 > --- a/doc/man-sections/tls-options.rst > +++ b/doc/man-sections/tls-options.rst > @@ -116,6 +116,20 @@ certificates and keys: > https://github.com/OpenVPN/easy-rsa > authority functions, you must set up the files :code:`index.txt` (may be > empty) and :code:`serial` (initialize to :code:`01`). > > +--cert pkcs11-uri > + The local peer's certificate in a PKCS#11 token specified as a RFC 7512 > + uri with optional custom attributes described below. Cannot be used with > + ``--key file``. ``--key`` must be left unspecified or point to the same > + uri. All other requrements for the certificate described under > + ``--cert file`` applies. > + > + Requires OpenSSL with pkcs11 engine installed and configured. Also see > + the option ``--pkcs11-engine``. > + > + As the same uri is used for certificate and private key, do not include > type > + attribute (i.e., :code: `type=cert;` or :code: `type=private;` should > not > + be included) > + > --crl-verify args > Check peer certificate against a Certificate Revocation List. > > @@ -208,11 +222,28 @@ certificates and keys: > https://github.com/OpenVPN/easy-rsa > generated when you built your peer's certificate (see ``--cert file`` > above). > > +--key pkcs11-uri > + See ``--cert pkcs11-uri`` above. > + > --pkcs12 file > Specify a PKCS #12 file containing local private key, local certificate, > and root CA certificate. This option can be used instead of ``--ca``, > ``--cert``, and ``--key``. Not available with mbed TLS. > > +--pkcs11-engine engine-name [module-path] > + Specifiy the pkcs11-engine and the provider module to load when > + certificate and private key are given as a pkcs11 URI. > + > + If the option is unspecified, and a pkcs11 URI is used for cert/key, > + :code:`pkcs11` engine is loaded, if it can be automatically found by > + OpenSSL. > + > + If specified, the cert/key must be given as a pkcs11 URI. > + > + The engine name could be a valid engine-id or path to a shared object. > + The module-path should be the path to a shared object. Objects in > + non-standard locations would need to be specified as full paths. > + > --remote-cert-eku oid > Require that peer certificate was signed with an explicit *extended key > usage*. > diff --git a/src/openvpn/options.c b/src/openvpn/options.c > index db460796..559782ec 100644 > --- a/src/openvpn/options.c > +++ b/src/openvpn/options.c > @@ -531,6 +531,11 @@ static const char usage_message[] = > " nonce_secret_len=nsl. Set alg=none to disable > PRNG.\n" > #ifndef ENABLE_CRYPTO_MBEDTLS > "--engine [name] : Enable OpenSSL hardware crypto engine > functionality.\n" > +#endif > +#ifdef HAVE_OPENSSL_ENGINE > + "--pkcs11-engine name [module-path] : PKCS11 engine and provider > module to use\n" > + " for cert and key specified as a > pkcs11 URI.\n" > + " name could be an engine-id or a > path.\n" > #endif > "--no-replay : (DEPRECATED) Disable replay protection.\n" > "--mute-replay-warnings : Silence the output of replay warnings to > log file.\n" > @@ -915,6 +920,12 @@ struct pull_filter_list > struct pull_filter *tail; > }; > > +static bool > +is_pkcs11_uri(const char *uri) > +{ > + return (uri && !strncmp(uri, "pkcs11:", 7)); > +} > + > static const char * > pull_filter_type_name(int type) > { > @@ -2587,6 +2598,17 @@ options_postprocess_verify_ce(const struct options > *options, > > if (options->tls_server || options->tls_client) > { > +#ifdef HAVE_OPENSSL_ENGINE > + if (is_pkcs11_uri(options->cert_file) != > is_pkcs11_uri(options->priv_key_file)) > + { > + msg(M_USAGE, "Use of PKCS#11 uri for --cert or --key and file > name for the other is not supported"); > + } > + else > + if (options->pkcs11_engine && !is_pkcs11_uri(options->cert_file)) > + { > + msg(M_USAGE, "Use of --pkcs11-engine expects --cert to be > specified as a pkcs11: URI"); > + } > +#endif > #ifdef ENABLE_PKCS11 > if (options->pkcs11_providers[0]) > { > @@ -3455,8 +3477,11 @@ options_postprocess_filechecks(struct options > *options) > errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE, > options->ca_path, R_OK, "--capath"); > > - errs |= check_file_access_inline(options->cert_file_inline, > CHKACC_FILE, > + if (!is_pkcs11_uri(options->cert_file)) > + { > + errs |= check_file_access_inline(options->cert_file_inline, > CHKACC_FILE, > options->cert_file, R_OK, "--cert"); > + } > > errs |= check_file_access_inline(options->extra_certs_file, > CHKACC_FILE, > options->extra_certs_file, R_OK, > @@ -3466,9 +3491,12 @@ options_postprocess_filechecks(struct options > *options) > if (!(options->management_flags & MF_EXTERNAL_KEY)) > #endif > { > - errs |= check_file_access_inline(options->priv_key_file_inline, > + if (!is_pkcs11_uri(options->priv_key_file)) > + { > + errs |= > check_file_access_inline(options->priv_key_file_inline, > CHKACC_FILE|CHKACC_PRIVATE, > options->priv_key_file, R_OK, > "--key"); > + } > } > > errs |= check_file_access_inline(options->pkcs12_file_inline, > @@ -8097,6 +8125,14 @@ add_option(struct options *options, > } > } > #endif /* ENABLE_CRYPTO_MBEDTLS */ > +#ifdef HAVE_OPENSSL_ENGINE > + else if (streq(p[0], "pkcs11-engine") && p[1] && !p[3]) > + { > + VERIFY_PERMISSION(OPT_P_GENERAL); > + options->pkcs11_engine = p[1]; > + options->pkcs11_engine_module = p[2]; /* may be NULL */ > + } > +#endif /* HAVE_OPENSSL_ENGINE */ > #ifdef ENABLE_PREDICTION_RESISTANCE > else if (streq(p[0], "use-prediction-resistance") && !p[1]) > { > @@ -8156,6 +8192,20 @@ add_option(struct options *options, > VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); > options->cert_file = p[1]; > options->cert_file_inline = is_inline; > + if (is_pkcs11_uri(p[1])) > + { > +#ifndef HAVE_OPENSSL_ENGINE > + msg(msglevel, "Use of PKCS11 uri as cert and key file names > requires OpenSSL " > + "ENGINE support which is missing in this > binary.") > +#else > + options->priv_key_file = p[1]; > + options->cert_file_is_pkcs11_uri = true; > + } > + else > + { > + options->cert_file_is_pkcs11_uri = false; > +#endif /* HAVE_OPENSSL_ENGINE */ > + } > } > else if (streq(p[0], "extra-certs") && p[1] && !p[2]) > { > @@ -8238,6 +8288,20 @@ add_option(struct options *options, > VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); > options->priv_key_file = p[1]; > options->priv_key_file_inline = is_inline; > + if (is_pkcs11_uri(p[1])) > + { > +#ifndef HAVE_OPENSSL_ENGINE > + msg(msglevel, "Use of PKCS11 uri as cert and key file names > requires OpenSSL " > + "ENGINE support which is missing in this > binary.") > +#else > + options->cert_file = p[1]; > + options->cert_file_is_pkcs11_uri = true; > + } > + else > + { > + options->cert_file_is_pkcs11_uri = false; > +#endif /* HAVE_OPENSSL_ENGINE */ > + } > } > else if (streq(p[0], "tls-version-min") && p[1] && !p[3]) > { > diff --git a/src/openvpn/options.h b/src/openvpn/options.h > index 41e84f7e..15567980 100644 > --- a/src/openvpn/options.h > +++ b/src/openvpn/options.h > @@ -659,6 +659,13 @@ struct options > > /* data channel crypto flags set by push/pull. Reuses the CO_* > crypto_flags */ > unsigned int data_channel_crypto_flags; > + > +#ifdef HAVE_OPENSSL_ENGINE > + const char *pkcs11_engine; > + const char *pkcs11_engine_module; > + /* flag to indicate cert and key files are specified as pkcs11 uri */ > + bool cert_file_is_pkcs11_uri; > +#endif > }; > > #define streq(x, y) (!strcmp((x), (y))) > diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c > index b16f6bcc..dedfe8ad 100644 > --- a/src/openvpn/ssl.c > +++ b/src/openvpn/ssl.c > @@ -641,6 +641,19 @@ init_ssl(const struct options *options, struct > tls_root_ctx *new_ctx, bool in_ch > goto err; > } > } > +#ifdef HAVE_OPENSSL_ENGINE > + else if (options->cert_file_is_pkcs11_uri) > + { > + if (!tls_ctx_use_pkcs11_engine(new_ctx, options->cert_file, > + options->pkcs11_engine, > + options->pkcs11_engine_module)) > + { > + msg(M_WARN, "Cannot load certificate \"%s\" using PKCS#11 > engine", > + options->cert_file); > + goto err; > + } > + } > +#endif > #ifdef ENABLE_PKCS11 > else if (options->pkcs11_providers[0]) > { > @@ -672,7 +685,7 @@ init_ssl(const struct options *options, struct > tls_root_ctx *new_ctx, bool in_ch > tls_ctx_load_cert_file(new_ctx, options->cert_file, > options->cert_file_inline); > } > > - if (options->priv_key_file) > + if (options->priv_key_file && !options->cert_file_is_pkcs11_uri) > { > if (0 != tls_ctx_load_priv_file(new_ctx, options->priv_key_file, > options->priv_key_file_inline)) > diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h > index c3d12e5b..556fc2c8 100644 > --- a/src/openvpn/ssl_backend.h > +++ b/src/openvpn/ssl_backend.h > @@ -568,4 +568,14 @@ void get_highest_preference_tls_cipher(char *buf, int > size); > */ > const char *get_ssl_library_version(void); > > +/** > + * Load certificate and key into TLS context using pkcs11 engine > + * @param ctx TLS context > + * @param cert_id ceritificate and proivate key spec as pkcs11 URI > + * @param engine id or path of OpenSSL pkcs11 engine object (default: > pkcs11) > + * @param module path of optional provider module to load with the > engine > + */ > +int tls_ctx_use_pkcs11_engine(struct tls_root_ctx *tls_ctx, const char > *cert_id, > + const char *engine, const char *module); > + > #endif /* SSL_BACKEND_H_ */ > diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c > index 3120c51a..58fbde93 100644 > --- a/src/openvpn/ssl_openssl.c > +++ b/src/openvpn/ssl_openssl.c > @@ -2251,4 +2251,185 @@ get_ssl_library_version(void) > return OpenSSL_version(OPENSSL_VERSION); > } > > -#endif /* defined(ENABLE_CRYPTO_OPENSSL) */ > +#if HAVE_OPENSSL_ENGINE > +#include <openssl/ui.h> > +#include <openssl/engine.h> > + > +/* Call back method for user interface with pkcs11 engine > + * used for PIN prompt and possibly token insertion request. > + */ > +static int > +ui_reader(UI *ui, UI_STRING *uis) > +{ > + struct user_pass token_pass; > + int ret = 0; > + > + const char *uri = UI_get0_user_data(ui); > + const char *prompt = UI_get0_output_string(uis); > + > + token_pass.defined = false; > + token_pass.nocache = true; > + > + switch(UI_get_string_type(uis)) > + { > + case UIT_PROMPT: > + case UIT_VERIFY: > + if (get_user_pass(&token_pass, NULL, prompt, > + GET_USER_PASS_MANAGEMENT|GET_USER_PASS_PASSWORD_ONLY > + |GET_USER_PASS_NOFATAL)) > + { > + ret = 1; > + UI_set_result(ui, uis, token_pass.password); > + } > + break; > + case UIT_BOOLEAN: > + if (get_user_pass(&token_pass, NULL, > UI_get0_output_string(uis), > + GET_USER_PASS_MANAGEMENT|GET_USER_PASS_NEED_OK > + |GET_USER_PASS_NOFATAL)) > + { > + ret = (strcmp(token_pass.password, "ok") == 0); > + UI_set_result(ui, uis, token_pass.password); > + } > + case UIT_INFO: > + msg(M_INFO, "INFO prompt from token: <%s>", prompt); > + break; > + case UIT_ERROR: > + msg(M_INFO, "ERROR prompt from token: <%s>", prompt); > + break; > + default: > + break; > + } > + > + return ret; > +} > + > +static char * > +ui_prompt_constructor(UI *ui, const char *desc, const char *name) > +{ > + int len = strlen(desc) + strlen(name) + 6; > + char *s = malloc(len); > + openvpn_snprintf(s, len, "%s for %s", desc, name); > + return s; > +} > + > +static ENGINE * > +load_pkcs11_engine(const char *engine_id) > +{ > + ENGINE *e = ENGINE_by_id(engine_id); > + > + if (e) { > + return e; > + } > + > + /* try dynamic engine with engine-id as path to the engine shared > object */ > + e = ENGINE_by_id("dynamic"); > + if (e) > + { > + if (!ENGINE_ctrl_cmd_string(e, "SO_PATH", engine_id, 0) > + || !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) > + { > + ENGINE_free(e); > + e = NULL; > + } > + } > + if (!e) > + { > + msg(M_WARN, "PKCS11 engine <%s> not available", engine_id); > + } > + return e; > +} > + > +static ENGINE * > +setup_pkcs11_engine(const char *engine_id, const char *module_path, > UI_METHOD *ui) > +{ > + if (!engine_id) > + { > + engine_id = "pkcs11"; > + } > + > + msg(D_SHOW_PKCS11, "Loading pkcs11 engine <%s> with module <%s>", > + engine_id, (module_path ? module_path : "unspecified")); > + > + ENGINE *e = load_pkcs11_engine(engine_id); > + > + if (e) > + { > + if (module_path) > + { > + ENGINE_ctrl_cmd_string(e, "MODULE_PATH", module_path, 0); > + } > + ENGINE_ctrl_cmd(e, "SET_USER_INTERFACE", 0, ui, NULL, 0); > + } > + > + return e; > +} > + > +/** > + * Load certificate and key into TLS context using pkcs11 engine > + * @param ctx TLS context > + * @param cert_id ceritificate and proivate key spec as pkcs11 URI > + * @param engine id or path of OpenSSL pkcs11 engine object (default: > pkcs11) > + * @param module path of optional provider module to load with the > engine > + */ > +int > +tls_ctx_use_pkcs11_engine(struct tls_root_ctx *tls_ctx, const char > *cert_id, > + const char *engine, const char *module) > +{ > + int ret = 0; > + EVP_PKEY *pkey = NULL; > + > + UI_METHOD *ui = UI_create_method("openvpn"); > + if (!ui) > + { > + msg(M_WARN, "Failed to setup UI callback for engine"); > + return ret; > + } > + UI_method_set_reader(ui, ui_reader); > + UI_method_set_prompt_constructor(ui, ui_prompt_constructor); > + > + struct > + { > + const char *cert_id; > + X509* cert; > + } params = {cert_id, NULL}; > + > + ENGINE *e = setup_pkcs11_engine(engine, module, ui); > + if (!e || !ENGINE_init(e)) > + { > + goto cleanup; > + } > + ENGINE_ctrl_cmd(e, "SET_CALLBACK_DATA", 0, (void *)cert_id, NULL, 0); > + > + msg (D_SHOW_PKCS11, "Loading certificate <%s> using engine", > params.cert_id); > + > + if (!ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, ¶ms, NULL, 0) > + || !params.cert || !SSL_CTX_use_certificate(tls_ctx->ctx, > params.cert)) > + { > + msg (M_WARN, "Failed to load certificate <%s>", cert_id); > + goto finish; > + } > + > + msg (D_SHOW_PKCS11, "Loading private key <%s> using engine", > params.cert_id); > + > + pkey = ENGINE_load_private_key(e, cert_id, ui, (void *)cert_id); > + if (!pkey || !SSL_CTX_use_PrivateKey(tls_ctx->ctx, pkey)) > + { > + msg (M_WARN, "Failed to set private key <%s> using engine", > cert_id); > + goto finish; > + } > + ret = 1; > + > +finish: > + ENGINE_finish(e); > + > +cleanup: > + ENGINE_free(e); > + X509_free(params.cert); > + UI_destroy_method(ui); > + > + return ret; > +} > + > +#endif /* HAVE_OPENSSL_ENGINE */ > + > +#endif /* ENABLE_CRYPTO_OPENSSL */ > -- > 2.20.1 > >
_______________________________________________ Openvpn-devel mailing list Openvpn-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openvpn-devel