@@ -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
@@ -116,6 +116,19 @@ 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. 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 (e.g., :code: `type=cert;` or :code: `type=private;` should not
+ be included)
+
--crl-verify args
Check peer certificate against a Certificate Revocation List.
@@ -208,11 +221,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*.
@@ -530,6 +530,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"
@@ -913,6 +918,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)
{
@@ -2606,6 +2617,17 @@ options_postprocess_verify_ce(const struct options *options,
if (options->tls_server || options->tls_client)
{
check_ca_required(options);
+#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])
{
@@ -3445,8 +3467,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,
@@ -3456,9 +3481,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,
@@ -8078,6 +8106,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])
{
@@ -8137,6 +8173,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])
{
@@ -8219,6 +8269,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])
{
@@ -658,6 +658,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)))
@@ -647,6 +647,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])
{
@@ -678,7 +691,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))
@@ -569,4 +569,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_ */
@@ -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 */