Attention is currently required from: cron2, plaisthos.
Hello cron2, flichtenheld,
I'd like you to reexamine a change. Please visit
http://gerrit.openvpn.net/c/openvpn/+/466?usp=email
to look at the new patch set (#8).
Change subject: Implement the --tls-export-cert feature
......................................................................
Implement the --tls-export-cert feature
This is a re-implementation of the --tls-export-cert feature. This
was necessary to due to missing approval to re-license the old
(now removed) code. The re-implementation is based on the following
description of the feature provided by David:
Add an option to export certificate in PEM format of the remote
peer to a given directory.
For example: --tls-export-cert /var/tmp
This option should use a randomised filename, which is provided via a
"peer_cert" environment variable for the --tls-verify script or the
OPENVPN_PLUGIN_TLS_VERIFY plug-in hook.
Once the script or plugin call has completed, OpenVPN should delete
this file.
Change-Id: Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2
Signed-off-by: Arne Schwabe <[email protected]>
---
M doc/man-sections/script-options.rst
M src/openvpn/init.c
M src/openvpn/options.c
M src/openvpn/options.h
M src/openvpn/ssl_common.h
M src/openvpn/ssl_verify.c
M src/openvpn/ssl_verify_backend.h
M src/openvpn/ssl_verify_mbedtls.c
M src/openvpn/ssl_verify_openssl.c
9 files changed, 174 insertions(+), 5 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/66/466/8
diff --git a/doc/man-sections/script-options.rst
b/doc/man-sections/script-options.rst
index 6f90e14..53c9f97 100644
--- a/doc/man-sections/script-options.rst
+++ b/doc/man-sections/script-options.rst
@@ -423,6 +423,15 @@
See the `Environmental Variables`_ section below for additional
parameters passed as environmental variables.
+--tls-export-cert dir
+ Adds an environment variable ``peer_cert_{x}`` (and an alias
+ ``peer_cert`` for ``peer_cert_0`` for compatibility) when calling the
+ ``--tls-verify`` script or executing the OPENVPN_PLUGIN_TLS_VERIFY plugin
+ hook to verify the certificate.
+
+ The environment variable contains the path to a PEM encoded certificate
+ of the current peer certificate in the directory ``dir``.
+
--up cmd
Run command ``cmd`` after successful TUN/TAP device open (pre ``--user``
UID change).
@@ -763,6 +772,15 @@
modifier is specified, and deleted from the environment after the script
returns.
+:code:`peer_cert_{n}`
+ If the option ``--tls-export-cert`` is enabled, this option contains
+ the path to the current peer certificate to be verified in PEM format
+ where ``n`` is the verification level.
+
+:code:`peer_cert`
+ Identical to `peer_cert_0` for compatibility with older
+ versions.
+
:code:`proto`
The ``--proto`` parameter. Set on program initiation and reset on
SIGHUP.
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 9e2b3845..c5cc154 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -3336,6 +3336,7 @@
to.auth_user_pass_verify_script_via_file =
options->auth_user_pass_verify_script_via_file;
to.client_crresponse_script = options->client_crresponse_script;
to.tmp_dir = options->tmp_dir;
+ to.export_peer_cert_dir = options->tls_export_peer_cert_dir;
if (options->ccd_exclusive)
{
to.client_config_dir_exclusive = options->client_config_dir;
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index e498114..ecbc63e 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -1986,6 +1986,7 @@
SHOW_STR(cipher_list_tls13);
SHOW_STR(tls_cert_profile);
SHOW_STR(tls_verify);
+ SHOW_STR(tls_export_peer_cert_path);
SHOW_INT(verify_x509_type);
SHOW_STR(verify_x509_name);
SHOW_STR_INLINE(crl_file);
@@ -3048,6 +3049,7 @@
MUST_BE_UNDEF(cipher_list_tls13);
MUST_BE_UNDEF(tls_cert_profile);
MUST_BE_UNDEF(tls_verify);
+ MUST_BE_UNDEF(tls_export_peer_cert_path);
MUST_BE_UNDEF(verify_x509_name);
MUST_BE_UNDEF(tls_timeout);
MUST_BE_UNDEF(renegotiate_bytes);
@@ -4053,6 +4055,13 @@
R_OK, "--crl-verify");
}
+ if (options->tls_export_peer_cert_dir)
+ {
+ errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE,
+ options->tls_export_peer_cert_dir,
+ W_OK, "--tls-export-cert");
+ }
+
ASSERT(options->connection_list);
for (int i = 0; i < options->connection_list->len; ++i)
{
@@ -8997,6 +9006,11 @@
string_substitute(p[1], ',', ' ', &options->gc),
"tls-verify", true);
}
+ else if (streq(p[0], "tls-export-cert") && p[1] && !p[2])
+ {
+ VERIFY_PERMISSION(OPT_P_SCRIPT);
+ options->tls_export_peer_cert_dir = p[1];
+ }
else if (streq(p[0], "compat-names"))
{
VERIFY_PERMISSION(OPT_P_GENERAL);
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index c4514e1..e52a953 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -592,6 +592,7 @@
const char *tls_cert_profile;
const char *ecdh_curve;
const char *tls_verify;
+ const char *tls_export_peer_cert_dir;
int verify_x509_type;
const char *verify_x509_name;
const char *crl_file;
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 925660b..f085e0d 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -374,6 +374,7 @@
const char *client_crresponse_script;
bool auth_user_pass_verify_script_via_file;
const char *tmp_dir;
+ const char *export_peer_cert_dir;
const char *auth_user_pass_file;
bool auth_user_pass_file_inline;
diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c
index bd7e512..35d3377 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -31,6 +31,7 @@
#endif
#include "syshead.h"
+#include <string.h>
#include "base64.h"
#include "manage.h"
@@ -457,6 +458,54 @@
gc_free(&gc);
}
+/**
+ * Exports the certificate in \c peer_cert into the environment and adds
+ * the filname
+ */
+static bool
+verify_cert_cert_export_env(struct env_set *es, openvpn_x509_cert_t *peer_cert,
+ int cert_depth, const char *pem_export_fname)
+{
+ char envname[64];
+ /* Make copy of the filename to manage that copy by the gc_arena */
+ char *pem_export_filename = strdup(pem_export_fname);
+
+ if (!pem_export_filename)
+ {
+ return false;
+ }
+
+ /* export the path to the certificate in pem file format */
+ openvpn_snprintf(envname, sizeof(envname), "peer_cert_%d", cert_depth);
+ setenv_str(es, envname, pem_export_filename);
+
+ /* compatibility with older scripts/plugins that expect peer_cert without
+ * suffix */
+ if (cert_depth == 0)
+ {
+ setenv_str(es, "peer_cert", pem_export_filename);
+ }
+
+ return backend_x509_write_pem(peer_cert, pem_export_filename) == SUCCESS;
+}
+
+static void
+verify_cert_cert_delete_env(struct env_set *es, int cert_depth,
+ const char *pem_export_fname)
+{
+ char envname[64];
+ openvpn_snprintf(envname, sizeof(envname), "peer_cert_%d", cert_depth);
+ env_set_del(es, envname);
+
+ /* compatibility with older scripts/plugins that expect peer_cert without
+ * suffix */
+ if (cert_depth == 0)
+ {
+ env_set_del(es, "peer_cert");
+ }
+ unlink(pem_export_fname);
+}
+
/*
* call --tls-verify plug-in(s)
*/
@@ -572,18 +621,19 @@
result_t
verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int
cert_depth)
{
+ /* need to define these variables here so goto cleanup will always have
+ * them defined */
result_t ret = FAILURE;
- char *subject = NULL;
- const struct tls_options *opt;
struct gc_arena gc = gc_new();
+ const char *pem_export_fname = NULL;
- opt = session->opt;
+ const struct tls_options *opt = session->opt;
ASSERT(opt);
session->verified = false;
/* get the X509 name */
- subject = x509_get_subject(cert, &gc);
+ char *subject = x509_get_subject(cert, &gc);
if (!subject)
{
msg(D_TLS_ERRORS, "VERIFY ERROR: depth=%d, could not extract X509 "
@@ -706,6 +756,20 @@
session->verify_maxlevel = max_int(session->verify_maxlevel, cert_depth);
+ if (opt->export_peer_cert_dir)
+ {
+ pem_export_fname = platform_create_temp_file(opt->export_peer_cert_dir,
+ "pef", &gc);
+
+ if (!pem_export_fname
+ || !verify_cert_cert_export_env(opt->es, cert, cert_depth,
+ pem_export_fname))
+ {
+ msg(D_TLS_ERRORS, "TLS Error: Failed to export certificate for "
+ "--tls-export-cert in %s", opt->export_peer_cert_dir);
+ goto cleanup;
+ }
+ }
/* export certificate values to the environment */
verify_cert_set_env(opt->es, cert, cert_depth, subject, common_name,
opt->x509_track);
@@ -757,12 +821,13 @@
ret = SUCCESS;
cleanup:
-
+ verify_cert_cert_delete_env(opt->es, cert_depth, pem_export_fname);
if (ret != SUCCESS)
{
tls_clear_error(); /* always? */
session->verified = false; /* double sure? */
}
+
gc_free(&gc);
return ret;
diff --git a/src/openvpn/ssl_verify_backend.h b/src/openvpn/ssl_verify_backend.h
index d402b1f..5301a51 100644
--- a/src/openvpn/ssl_verify_backend.h
+++ b/src/openvpn/ssl_verify_backend.h
@@ -161,6 +161,17 @@
struct gc_arena *gc);
/*
+ * Write the certificate to the file in PEM format.
+ *
+ *
+ * @param cert Certificate to serialise.
+ *
+ * @return \c FAILURE, \c or SUCCESS
+ */
+result_t backend_x509_write_pem(openvpn_x509_cert_t *cert,
+ const char *filename);
+
+/*
* Save X509 fields to environment, using the naming convention:
*
* X509_{cert_depth}_{name}={value}
diff --git a/src/openvpn/ssl_verify_mbedtls.c b/src/openvpn/ssl_verify_mbedtls.c
index 5612139..24a89c3 100644
--- a/src/openvpn/ssl_verify_mbedtls.c
+++ b/src/openvpn/ssl_verify_mbedtls.c
@@ -218,6 +218,41 @@
return buf;
}
+result_t
+backend_x509_write_pem(openvpn_x509_cert_t *cert, const char *filename)
+{
+ /* mbed TLS does not make it easy to write a certificate in PEM format.
+ * The only way is to directly access the DER encoded raw certificate
+ * and PEM encode it ourselves */
+
+ struct gc_arena gc = gc_new();
+ /* just do a very loose upper bound for the base64 based PEM encoding
+ * using 3 times the space for the base64 and 100 bytes for the
+ * headers and footer */
+ struct buffer pem = alloc_buf_gc(cert->raw.len * 3 + 100, &gc);
+
+ struct buffer der = {};
+ buf_set_read(&der, cert->raw.p, cert->raw.len);
+
+ if (!crypto_pem_encode("CERTIFICATE", &pem, &der, &gc))
+ {
+ goto err;
+ }
+
+ if (!buffer_write_file(filename, &pem))
+ {
+ goto err;
+ }
+
+ gc_free(&gc);
+ return SUCCESS;
+err:
+ msg(D_TLS_DEBUG_LOW, "Error writing X509 certificate to file %s",
+ filename);
+ gc_free(&gc);
+ return FAILURE;
+}
+
static struct buffer
x509_get_fingerprint(const mbedtls_md_info_t *md_info, mbedtls_x509_crt *cert,
struct gc_arena *gc)
diff --git a/src/openvpn/ssl_verify_openssl.c b/src/openvpn/ssl_verify_openssl.c
index 5afffc1..00fdec3 100644
--- a/src/openvpn/ssl_verify_openssl.c
+++ b/src/openvpn/ssl_verify_openssl.c
@@ -320,6 +320,29 @@
return format_hex_ex(asn1_i->data, asn1_i->length, 0, 1, ":", gc);
}
+result_t
+backend_x509_write_pem(openvpn_x509_cert_t *cert, const char *filename)
+{
+ BIO *out = BIO_new_file(filename, "w");
+ if (!out)
+ {
+ goto err;
+ }
+
+ if (!PEM_write_bio_X509(out, cert))
+ {
+ goto err;
+ }
+ BIO_free(out);
+
+ return SUCCESS;
+err:
+ BIO_free(out);
+ crypto_msg(D_TLS_DEBUG_LOW, "Error writing X509 certificate to file %s",
+ filename);
+ return FAILURE;
+}
+
struct buffer
x509_get_sha1_fingerprint(X509 *cert, struct gc_arena *gc)
{
--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/466?usp=email
To unsubscribe, or for help writing mail filters, visit
http://gerrit.openvpn.net/settings
Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2
Gerrit-Change-Number: 466
Gerrit-PatchSet: 8
Gerrit-Owner: plaisthos <[email protected]>
Gerrit-Reviewer: cron2 <[email protected]>
Gerrit-Reviewer: flichtenheld <[email protected]>
Gerrit-CC: openvpn-devel <[email protected]>
Gerrit-Attention: plaisthos <[email protected]>
Gerrit-Attention: cron2 <[email protected]>
Gerrit-MessageType: newpatchset
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel