* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --export-peer-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
@ 2023-12-06 12:27 ` plaisthos (Code Review)
2023-12-06 12:57 ` cron2 (Code Review)
` (27 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2023-12-06 12:27 UTC (permalink / raw)
To: flichtenheld <frank@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 12474 bytes --]
Attention is currently required from: flichtenheld.
Hello flichtenheld,
I'd like you to do a code review.
Please visit
http://gerrit.openvpn.net/c/openvpn/+/466?usp=email
to review the following change.
Change subject: Implement the --export-peer-cert feature
......................................................................
Implement the --export-peer-cert feature
This is a re-implementation of the --export-peer-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: --export-peer-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 <arne@...1227...>
---
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, 152 insertions(+), 4 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/66/466/1
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
index 38dcfa2..edf636d 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.
+--export-peer-cert-path dir
+ Adds a an environment variables ``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,14 @@
modifier is specified, and deleted from the environment after the script
returns.
+:code:`peer_cert_{n}`
+ If the option ``--export-peer-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..917ae33 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_path;
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 1521872..e63ef84 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_path)
+ {
+ errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE,
+ options->tls_export_peer_cert_path,
+ W_OK, "--export-peer-cert");
+ }
+
ASSERT(options->connection_list);
for (int i = 0; i < options->connection_list->len; ++i)
{
@@ -8998,6 +9007,11 @@
string_substitute(p[1], ',', ' ', &options->gc),
"tls-verify", true);
}
+ else if (streq(p[0], "export-peer-cert") && p[1] && !p[2])
+ {
+ VERIFY_PERMISSION(OPT_P_SCRIPT);
+ options->tls_export_peer_cert_path = 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..3b9a7f6 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_path;
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..3d794cc 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -457,6 +457,32 @@
gc_free(&gc);
}
+static bool
+verify_cert_cert_export_env(struct env_set *es, openvpn_x509_cert_t *peer_cert,
+ int cert_depth, const char *pem_export_fn)
+{
+ char envname[64];
+ struct gc_arena gc = gc_new();
+ /* export the certificate itself as pem when the enabled */
+ openvpn_snprintf(envname, sizeof(envname), "peer_cert_%d", cert_depth);
+ setenv_str(es, envname, pem_export_fn);
+
+ /* compatibility with older scripts/plugins that expect peer_cert without
+ * suffix */
+ if (cert_depth == 0)
+ {
+ setenv_str(es, "peer_cert", pem_export_fn);
+ }
+
+ bool ret = true;
+
+ ret = (backend_x509_write_pem(peer_cert, pem_export_fn) == SUCCESS);
+
+ gc_free(&gc);
+ return ret;
+}
+
+
/*
* call --tls-verify plug-in(s)
*/
@@ -572,18 +598,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
+ * these variables defined */
result_t ret = FAILURE;
- char *subject = NULL;
- const struct tls_options *opt;
struct gc_arena gc = gc_new();
+ const char *pem_export_fn = 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 +733,19 @@
session->verify_maxlevel = max_int(session->verify_maxlevel, cert_depth);
+ if (opt->export_peer_cert_dir)
+ {
+ pem_export_fn = platform_create_temp_file(opt->export_peer_cert_dir,
+ "pef", &gc);
+
+ if (!pem_export_fn
+ || !verify_cert_cert_export_env(opt->es, cert, cert_depth, pem_export_fn))
+ {
+ msg(D_TLS_ERRORS, "TLS Error: Failed to export certificate for "
+ "--export-peer-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);
@@ -763,6 +803,11 @@
tls_clear_error(); /* always? */
session->verified = false; /* double sure? */
}
+ if (pem_export_fn)
+ {
+ platform_unlink(pem_export_fn);
+ }
+
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..a48526e 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 to is 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 needing 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: 1
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-MessageType: newchange
[-- Attachment #2: Type: text/html, Size: 24017 bytes --]
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --export-peer-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
2023-12-06 12:27 ` [Openvpn-devel] [M] Change in openvpn[master]: Implement the --export-peer-cert feature plaisthos (Code Review)
@ 2023-12-06 12:57 ` cron2 (Code Review)
2023-12-06 14:04 ` [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature plaisthos (Code Review)
` (26 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: cron2 (Code Review) @ 2023-12-06 12:57 UTC (permalink / raw)
To: plaisthos <arne-openvpn@; +Cc: flichtenheld <frank@
[-- Attachment #1: Type: text/plain, Size: 1494 bytes --]
Attention is currently required from: flichtenheld, plaisthos.
cron2 has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
Change subject: Implement the --export-peer-cert feature
......................................................................
Patch Set 1:
(1 comment)
File doc/man-sections/script-options.rst:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/3f968f88_17232d6f :
PS1, Line 426: --export-peer-cert-path dir
so this is a new option, which is incompatible and will break people's config (if they use the old option). So we should go for the same option name "--tls-export-cert" - or alternatively provide a Changes.rst explaining the reasons.
--
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: 1
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: cron2 <gert@...1296...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Comment-Date: Wed, 06 Dec 2023 12:57:27 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 2592 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
2023-12-06 12:27 ` [Openvpn-devel] [M] Change in openvpn[master]: Implement the --export-peer-cert feature plaisthos (Code Review)
2023-12-06 12:57 ` cron2 (Code Review)
@ 2023-12-06 14:04 ` plaisthos (Code Review)
2023-12-06 14:04 ` plaisthos (Code Review)
` (25 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2023-12-06 14:04 UTC (permalink / raw)
To: flichtenheld <frank@; +Cc: cron2 <gert@
[-- Attachment #1: Type: text/plain, Size: 12571 bytes --]
Attention is currently required from: flichtenheld, plaisthos.
Hello 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 (#2).
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 <arne@...1227...>
---
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, 152 insertions(+), 4 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/66/466/2
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
index 38dcfa2..26b5434 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-path dir
+ Adds a an environment variables ``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,14 @@
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..917ae33 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_path;
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 1521872..503e832 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_path)
+ {
+ errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE,
+ options->tls_export_peer_cert_path,
+ W_OK, "--tls-export-cert");
+ }
+
ASSERT(options->connection_list);
for (int i = 0; i < options->connection_list->len; ++i)
{
@@ -8998,6 +9007,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_path = 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..3b9a7f6 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_path;
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..263b6d4 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -457,6 +457,32 @@
gc_free(&gc);
}
+static bool
+verify_cert_cert_export_env(struct env_set *es, openvpn_x509_cert_t *peer_cert,
+ int cert_depth, const char *pem_export_fn)
+{
+ char envname[64];
+ struct gc_arena gc = gc_new();
+ /* export the certificate itself as pem when the enabled */
+ openvpn_snprintf(envname, sizeof(envname), "peer_cert_%d", cert_depth);
+ setenv_str(es, envname, pem_export_fn);
+
+ /* compatibility with older scripts/plugins that expect peer_cert without
+ * suffix */
+ if (cert_depth == 0)
+ {
+ setenv_str(es, "peer_cert", pem_export_fn);
+ }
+
+ bool ret = true;
+
+ ret = (backend_x509_write_pem(peer_cert, pem_export_fn) == SUCCESS);
+
+ gc_free(&gc);
+ return ret;
+}
+
+
/*
* call --tls-verify plug-in(s)
*/
@@ -572,18 +598,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
+ * these variables defined */
result_t ret = FAILURE;
- char *subject = NULL;
- const struct tls_options *opt;
struct gc_arena gc = gc_new();
+ const char *pem_export_fn = 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 +733,19 @@
session->verify_maxlevel = max_int(session->verify_maxlevel, cert_depth);
+ if (opt->export_peer_cert_dir)
+ {
+ pem_export_fn = platform_create_temp_file(opt->export_peer_cert_dir,
+ "pef", &gc);
+
+ if (!pem_export_fn
+ || !verify_cert_cert_export_env(opt->es, cert, cert_depth, pem_export_fn))
+ {
+ 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);
@@ -763,6 +803,11 @@
tls_clear_error(); /* always? */
session->verified = false; /* double sure? */
}
+ if (pem_export_fn)
+ {
+ platform_unlink(pem_export_fn);
+ }
+
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..a48526e 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 to is 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 needing 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: 2
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: cron2 <gert@...1296...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-MessageType: newpatchset
[-- Attachment #2: Type: text/html, Size: 24190 bytes --]
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (2 preceding siblings ...)
2023-12-06 14:04 ` [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature plaisthos (Code Review)
@ 2023-12-06 14:04 ` plaisthos (Code Review)
2023-12-06 17:30 ` flichtenheld (Code Review)
` (24 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2023-12-06 14:04 UTC (permalink / raw)
Cc: cron2 <gert@
[-- Attachment #1: Type: text/plain, Size: 1407 bytes --]
Attention is currently required from: cron2, flichtenheld.
plaisthos has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 2:
(1 comment)
File doc/man-sections/script-options.rst:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/337ef9e3_0f8bbba4 :
PS1, Line 426: --export-peer-cert-path dir
> so this is a new option, which is incompatible and will break people's config (if they use the old o […]
Done
--
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: 2
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: cron2 <gert@...1296...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Comment-Date: Wed, 06 Dec 2023 14:04:25 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: cron2 <gert@...1296...>
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 2622 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (3 preceding siblings ...)
2023-12-06 14:04 ` plaisthos (Code Review)
@ 2023-12-06 17:30 ` flichtenheld (Code Review)
2023-12-07 11:27 ` plaisthos (Code Review)
` (23 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: flichtenheld (Code Review) @ 2023-12-06 17:30 UTC (permalink / raw)
To: plaisthos <arne-openvpn@; +Cc: cron2 <gert@
[-- Attachment #1: Type: text/plain, Size: 2270 bytes --]
Attention is currently required from: cron2, plaisthos.
flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 2: Code-Review-1
(6 comments)
File doc/man-sections/script-options.rst:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/738f9728_f9eb156f :
PS2, Line 427: Adds a an environment variables ``peer_cert_{x}`` (and an alias
Remove "a"
File src/openvpn/ssl_verify.c:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/dd7a5b9a_bf660274 :
PS2, Line 466: /* export the certificate itself as pem when the enabled */
missing words?
http://gerrit.openvpn.net/c/openvpn/+/466/comment/f487e7c8_793bcb7e :
PS2, Line 477: bool ret = true;
Can combine with the next line.
http://gerrit.openvpn.net/c/openvpn/+/466/comment/2e703085_bdc2ffba :
PS2, Line 602: * these variables defined */
can replace repetition of "these variables" with "them"
File src/openvpn/ssl_verify_mbedtls.c:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/74783133_a96d6da5 :
PS2, Line 225: * The only way to is directly access the DER encoded raw certificate
"to is" -> "is to"
http://gerrit.openvpn.net/c/openvpn/+/466/comment/b48e3170_e0e585b4 :
PS2, Line 230: * using needing 3 times the space for the base64 and 100 bytes for the
drop one of "using needing"
--
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: 2
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: cron2 <gert@...1296...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Comment-Date: Wed, 06 Dec 2023 17:30:24 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 4774 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (4 preceding siblings ...)
2023-12-06 17:30 ` flichtenheld (Code Review)
@ 2023-12-07 11:27 ` plaisthos (Code Review)
2023-12-07 17:49 ` plaisthos (Code Review)
` (22 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2023-12-07 11:27 UTC (permalink / raw)
Cc: flichtenheld <frank@
[-- Attachment #1: Type: text/plain, Size: 2353 bytes --]
Attention is currently required from: cron2, flichtenheld.
plaisthos has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 2:
(6 comments)
File doc/man-sections/script-options.rst:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/dccfc964_cfd463f3 :
PS2, Line 427: Adds a an environment variables ``peer_cert_{x}`` (and an alias
> Remove "a"
Done
File src/openvpn/ssl_verify.c:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/2e49f9a9_cc253bc2 :
PS2, Line 466: /* export the certificate itself as pem when the enabled */
> missing words?
Done
http://gerrit.openvpn.net/c/openvpn/+/466/comment/99393580_9e01f59d :
PS2, Line 477: bool ret = true;
> Can combine with the next line.
Done
http://gerrit.openvpn.net/c/openvpn/+/466/comment/9626e926_96849b76 :
PS2, Line 602: * these variables defined */
> can replace repetition of "these variables" with "them"
Done
File src/openvpn/ssl_verify_mbedtls.c:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/32a92534_6ee0a5db :
PS2, Line 225: * The only way to is directly access the DER encoded raw certificate
> "to is" -> "is to"
Done
http://gerrit.openvpn.net/c/openvpn/+/466/comment/85213291_05de576e :
PS2, Line 230: * using needing 3 times the space for the base64 and 100 bytes for the
> drop one of "using needing"
Done
--
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: 2
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: cron2 <gert@...1296...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Comment-Date: Thu, 07 Dec 2023 11:27:23 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: flichtenheld <frank@...2641...>
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 5355 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (5 preceding siblings ...)
2023-12-07 11:27 ` plaisthos (Code Review)
@ 2023-12-07 17:49 ` plaisthos (Code Review)
2023-12-12 14:39 ` flichtenheld (Code Review)
` (21 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2023-12-07 17:49 UTC (permalink / raw)
To: flichtenheld <frank@; +Cc: cron2 <gert@
[-- Attachment #1: Type: text/plain, Size: 12602 bytes --]
Attention is currently required from: cron2, flichtenheld.
Hello 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 (#3).
The following approvals got outdated and were removed:
Code-Review-1 by flichtenheld
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 <arne@...1227...>
---
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, 150 insertions(+), 4 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/66/466/3
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
index 38dcfa2..cde0034 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-path dir
+ Adds an environment variables ``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,14 @@
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..917ae33 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_path;
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 1521872..503e832 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_path)
+ {
+ errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE,
+ options->tls_export_peer_cert_path,
+ W_OK, "--tls-export-cert");
+ }
+
ASSERT(options->connection_list);
for (int i = 0; i < options->connection_list->len; ++i)
{
@@ -8998,6 +9007,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_path = 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..3b9a7f6 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_path;
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..5cf518e 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -457,6 +457,30 @@
gc_free(&gc);
}
+static bool
+verify_cert_cert_export_env(struct env_set *es, openvpn_x509_cert_t *peer_cert,
+ int cert_depth, const char *pem_export_fn)
+{
+ char envname[64];
+ struct gc_arena gc = gc_new();
+ /* 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_fn);
+
+ /* compatibility with older scripts/plugins that expect peer_cert without
+ * suffix */
+ if (cert_depth == 0)
+ {
+ setenv_str(es, "peer_cert", pem_export_fn);
+ }
+
+ bool ret = (backend_x509_write_pem(peer_cert, pem_export_fn) == SUCCESS);
+
+ gc_free(&gc);
+ return ret;
+}
+
+
/*
* call --tls-verify plug-in(s)
*/
@@ -572,18 +596,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_fn = 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 +731,19 @@
session->verify_maxlevel = max_int(session->verify_maxlevel, cert_depth);
+ if (opt->export_peer_cert_dir)
+ {
+ pem_export_fn = platform_create_temp_file(opt->export_peer_cert_dir,
+ "pef", &gc);
+
+ if (!pem_export_fn
+ || !verify_cert_cert_export_env(opt->es, cert, cert_depth, pem_export_fn))
+ {
+ 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);
@@ -763,6 +801,11 @@
tls_clear_error(); /* always? */
session->verified = false; /* double sure? */
}
+ if (pem_export_fn)
+ {
+ platform_unlink(pem_export_fn);
+ }
+
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: 3
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: cron2 <gert@...1296...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-MessageType: newpatchset
[-- Attachment #2: Type: text/html, Size: 24114 bytes --]
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (6 preceding siblings ...)
2023-12-07 17:49 ` plaisthos (Code Review)
@ 2023-12-12 14:39 ` flichtenheld (Code Review)
2023-12-12 18:24 ` plaisthos (Code Review)
` (20 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: flichtenheld (Code Review) @ 2023-12-12 14:39 UTC (permalink / raw)
To: plaisthos <arne-openvpn@; +Cc: cron2 <gert@
[-- Attachment #1: Type: text/plain, Size: 1551 bytes --]
Attention is currently required from: cron2, plaisthos.
flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 4: Code-Review-1
(2 comments)
File doc/man-sections/script-options.rst:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/36b80616_1a510d1a :
PS4, Line 427: Adds an environment variables ``peer_cert_{x}`` (and an alias
"variable" or remove "an"
http://gerrit.openvpn.net/c/openvpn/+/466/comment/42a94055_6568f3e0 :
PS4, Line 780: :code:`peer_cert` identical to `peer_cert_0` for compatibility with older
Missing line break before "identical". Breaks formatting
--
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: 4
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: cron2 <gert@...1296...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Comment-Date: Tue, 12 Dec 2023 14:39:26 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 3002 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (7 preceding siblings ...)
2023-12-12 14:39 ` flichtenheld (Code Review)
@ 2023-12-12 18:24 ` plaisthos (Code Review)
2023-12-12 18:24 ` plaisthos (Code Review)
` (19 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2023-12-12 18:24 UTC (permalink / raw)
To: flichtenheld <frank@; +Cc: cron2 <gert@
[-- Attachment #1: Type: text/plain, Size: 12675 bytes --]
Attention is currently required from: cron2, flichtenheld, plaisthos.
Hello 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 (#5).
The following approvals got outdated and were removed:
Code-Review-1 by flichtenheld
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 <arne@...1227...>
---
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, 151 insertions(+), 4 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/66/466/5
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
index 38dcfa2..ba700a0 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-path 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..917ae33 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_path;
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 1521872..503e832 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_path)
+ {
+ errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE,
+ options->tls_export_peer_cert_path,
+ W_OK, "--tls-export-cert");
+ }
+
ASSERT(options->connection_list);
for (int i = 0; i < options->connection_list->len; ++i)
{
@@ -8998,6 +9007,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_path = 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..3b9a7f6 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_path;
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..5cf518e 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -457,6 +457,30 @@
gc_free(&gc);
}
+static bool
+verify_cert_cert_export_env(struct env_set *es, openvpn_x509_cert_t *peer_cert,
+ int cert_depth, const char *pem_export_fn)
+{
+ char envname[64];
+ struct gc_arena gc = gc_new();
+ /* 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_fn);
+
+ /* compatibility with older scripts/plugins that expect peer_cert without
+ * suffix */
+ if (cert_depth == 0)
+ {
+ setenv_str(es, "peer_cert", pem_export_fn);
+ }
+
+ bool ret = (backend_x509_write_pem(peer_cert, pem_export_fn) == SUCCESS);
+
+ gc_free(&gc);
+ return ret;
+}
+
+
/*
* call --tls-verify plug-in(s)
*/
@@ -572,18 +596,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_fn = 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 +731,19 @@
session->verify_maxlevel = max_int(session->verify_maxlevel, cert_depth);
+ if (opt->export_peer_cert_dir)
+ {
+ pem_export_fn = platform_create_temp_file(opt->export_peer_cert_dir,
+ "pef", &gc);
+
+ if (!pem_export_fn
+ || !verify_cert_cert_export_env(opt->es, cert, cert_depth, pem_export_fn))
+ {
+ 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);
@@ -763,6 +801,11 @@
tls_clear_error(); /* always? */
session->verified = false; /* double sure? */
}
+ if (pem_export_fn)
+ {
+ platform_unlink(pem_export_fn);
+ }
+
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: 5
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: cron2 <gert@...1296...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-MessageType: newpatchset
[-- Attachment #2: Type: text/html, Size: 24284 bytes --]
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (8 preceding siblings ...)
2023-12-12 18:24 ` plaisthos (Code Review)
@ 2023-12-12 18:24 ` plaisthos (Code Review)
2023-12-13 14:29 ` flichtenheld (Code Review)
` (18 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2023-12-12 18:24 UTC (permalink / raw)
Cc: flichtenheld <frank@
[-- Attachment #1: Type: text/plain, Size: 1590 bytes --]
Attention is currently required from: cron2, flichtenheld.
plaisthos has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 4:
(2 comments)
File doc/man-sections/script-options.rst:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/f51f4a78_549de142 :
PS4, Line 427: Adds an environment variables ``peer_cert_{x}`` (and an alias
> "variable" or remove "an"
Done
http://gerrit.openvpn.net/c/openvpn/+/466/comment/eed49c31_f5661e3b :
PS4, Line 780: :code:`peer_cert` identical to `peer_cert_0` for compatibility with older
> Missing line break before "identical". […]
Done
--
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: 4
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: cron2 <gert@...1296...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Comment-Date: Tue, 12 Dec 2023 18:24:05 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: flichtenheld <frank@...2641...>
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 3147 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (9 preceding siblings ...)
2023-12-12 18:24 ` plaisthos (Code Review)
@ 2023-12-13 14:29 ` flichtenheld (Code Review)
2023-12-14 11:17 ` [Openvpn-devel] [PATCH v6] " Frank Lichtenheld
` (17 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: flichtenheld (Code Review) @ 2023-12-13 14:29 UTC (permalink / raw)
To: plaisthos <arne-openvpn@; +Cc: cron2 <gert@
[-- Attachment #1: Type: text/plain, Size: 1196 bytes --]
Attention is currently required from: cron2, plaisthos.
flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 6: Code-Review+2
(1 comment)
Patchset:
PS5:
Did some basic testing with both OpenSSL and mbedTLS. Looks good to me.
--
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: 6
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: cron2 <gert@...1296...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Comment-Date: Wed, 13 Dec 2023 14:29:01 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 2413 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [PATCH v6] Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (10 preceding siblings ...)
2023-12-13 14:29 ` flichtenheld (Code Review)
@ 2023-12-14 11:17 ` Frank Lichtenheld
2023-12-18 17:42 ` [Openvpn-devel] [M] Change in openvpn[master]: " cron2 (Code Review)
` (16 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: Frank Lichtenheld @ 2023-12-14 11:17 UTC (permalink / raw)
To: openvpn-devel
From: Arne Schwabe <arne@...1227...>
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 <arne@...1227...>
Acked-by: Frank Lichtenheld <frank@...2641...>
---
This change was reviewed on Gerrit and approved by at least one
developer. I request to merge it to master and release/2.6.
Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/466
This mail reflects revision 6 of this Change.
Acked-by according to Gerrit (reflected above):
Frank Lichtenheld <frank@...2641...>
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
index 38dcfa2..ba700a0 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-path 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..917ae33 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_path;
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 1521872..503e832 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_path)
+ {
+ errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE,
+ options->tls_export_peer_cert_path,
+ W_OK, "--tls-export-cert");
+ }
+
ASSERT(options->connection_list);
for (int i = 0; i < options->connection_list->len; ++i)
{
@@ -8998,6 +9007,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_path = 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..3b9a7f6 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_path;
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..5cf518e 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -457,6 +457,30 @@
gc_free(&gc);
}
+static bool
+verify_cert_cert_export_env(struct env_set *es, openvpn_x509_cert_t *peer_cert,
+ int cert_depth, const char *pem_export_fn)
+{
+ char envname[64];
+ struct gc_arena gc = gc_new();
+ /* 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_fn);
+
+ /* compatibility with older scripts/plugins that expect peer_cert without
+ * suffix */
+ if (cert_depth == 0)
+ {
+ setenv_str(es, "peer_cert", pem_export_fn);
+ }
+
+ bool ret = (backend_x509_write_pem(peer_cert, pem_export_fn) == SUCCESS);
+
+ gc_free(&gc);
+ return ret;
+}
+
+
/*
* call --tls-verify plug-in(s)
*/
@@ -572,18 +596,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_fn = 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 +731,19 @@
session->verify_maxlevel = max_int(session->verify_maxlevel, cert_depth);
+ if (opt->export_peer_cert_dir)
+ {
+ pem_export_fn = platform_create_temp_file(opt->export_peer_cert_dir,
+ "pef", &gc);
+
+ if (!pem_export_fn
+ || !verify_cert_cert_export_env(opt->es, cert, cert_depth, pem_export_fn))
+ {
+ 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);
@@ -763,6 +801,11 @@
tls_clear_error(); /* always? */
session->verified = false; /* double sure? */
}
+ if (pem_export_fn)
+ {
+ platform_unlink(pem_export_fn);
+ }
+
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)
{
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (11 preceding siblings ...)
2023-12-14 11:17 ` [Openvpn-devel] [PATCH v6] " Frank Lichtenheld
@ 2023-12-18 17:42 ` cron2 (Code Review)
2023-12-20 11:57 ` flichtenheld (Code Review)
` (15 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: cron2 (Code Review) @ 2023-12-18 17:42 UTC (permalink / raw)
To: plaisthos <arne-openvpn@; +Cc: flichtenheld <frank@
[-- Attachment #1: Type: text/plain, Size: 2912 bytes --]
Attention is currently required from: plaisthos.
cron2 has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
The change is no longer submittable: Code-Review is unsatisfied now.
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 6: Code-Review-2
(4 comments)
Patchset:
PS6:
Tested, does not crash, but only exports level 0 cert (level 1 variable is set, but no such file exists).
File doc/man-sections/script-options.rst:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/bff18d9b_f279e622 :
PS6, Line 426: --tls-export-cert-path dir
the manpage calls the option "tls-export-cert-path", while options.c checks for "tls-export-cert" (only, no "new option and also old option for compat reasons") - this needs to be resolved (and keeping the old option name everywhere is better for not breaking people's configs - so the documentation needs to be fixed)
File src/openvpn/init.c:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/189e173c_161fa78f :
PS6, Line 3339: to.export_peer_cert_dir = options->tls_export_peer_cert_path;
why call this "_dir" in the to, and "_path" in options-> ?
File src/openvpn/ssl_verify.c:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/97340248_881cd351 :
PS6, Line 734: if (opt->export_peer_cert_dir)
Something is not right here. So the function does set up multiple environment variables, but only one file is ever created...
I do a "ls -l $peer_cert_2 $peer_cert_1 $peer_cert0" in my tls-verify-script, and this is what I see
peer_cert_1=/var/tmp/openvpn_pef_6a5f2055b342424a15139e5787303c57.tmp
peer_cert_0=/var/tmp/openvpn_pef_18e5d27eafdb9fb54c12a8c446b56c76.tmp
peer_cert=/var/tmp/openvpn_pef_18e5d27eafdb9fb54c12a8c446b56c76.tmp
-rw------- 1 root root 1830 Dec 18 18:39 /var/tmp/openvpn_pef_18e5d27eafdb9fb54c12a8c446b56c76.tmp
... only one file.
For "multiple files", I would have expected to find the filenames in an array so they can all be deleted at the end (and no dangling files), but if only one file is ever created, no array is needed...
--
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: 6
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Comment-Date: Mon, 18 Dec 2023 17:42:02 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 5316 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (12 preceding siblings ...)
2023-12-18 17:42 ` [Openvpn-devel] [M] Change in openvpn[master]: " cron2 (Code Review)
@ 2023-12-20 11:57 ` flichtenheld (Code Review)
2024-01-02 13:46 ` plaisthos (Code Review)
` (14 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: flichtenheld (Code Review) @ 2023-12-20 11:57 UTC (permalink / raw)
To: plaisthos <arne-openvpn@; +Cc: cron2 <gert@
[-- Attachment #1: Type: text/plain, Size: 1695 bytes --]
Attention is currently required from: cron2, plaisthos.
flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 6: -Code-Review
(1 comment)
File src/openvpn/ssl_verify.c:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/2e6bc2d2_f0a6cbd6 :
PS6, Line 734: if (opt->export_peer_cert_dir)
> Something is not right here. […]
So I tried to understand why it seemed to work in my testing. It turns out that I only ever looked at the cert indicated by $depth argument to the hook. So indeed all the certs are exported correctly, but the problem is that after the depth 1 hook is run the file indicated by peer_cert_1 is deleted already. So the behavior is not very useful.
--
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: 6
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Comment-Date: Wed, 20 Dec 2023 11:57:07 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Comment-In-Reply-To: cron2 <gert@...1296...>
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 3043 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (13 preceding siblings ...)
2023-12-20 11:57 ` flichtenheld (Code Review)
@ 2024-01-02 13:46 ` plaisthos (Code Review)
2024-01-02 16:44 ` plaisthos (Code Review)
` (13 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2024-01-02 13:46 UTC (permalink / raw)
To: cron2 <gert@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 13529 bytes --]
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 (#7).
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 <arne@...1227...>
---
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/7
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
index 6f90e14..0e60ab5 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-path 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..917ae33 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_path;
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..714a578 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_path)
+ {
+ errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE,
+ options->tls_export_peer_cert_path,
+ 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_path = 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..3b9a7f6 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_path;
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: 7
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-MessageType: newpatchset
[-- Attachment #2: Type: text/html, Size: 26545 bytes --]
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (14 preceding siblings ...)
2024-01-02 13:46 ` plaisthos (Code Review)
@ 2024-01-02 16:44 ` plaisthos (Code Review)
2024-01-02 16:45 ` plaisthos (Code Review)
` (12 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2024-01-02 16:44 UTC (permalink / raw)
To: cron2 <gert@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 13519 bytes --]
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 <arne@...1227...>
---
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 <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-MessageType: newpatchset
[-- Attachment #2: Type: text/html, Size: 26535 bytes --]
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (15 preceding siblings ...)
2024-01-02 16:44 ` plaisthos (Code Review)
@ 2024-01-02 16:45 ` plaisthos (Code Review)
2024-01-02 16:52 ` plaisthos (Code Review)
` (11 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2024-01-02 16:45 UTC (permalink / raw)
Cc: cron2 <gert@
[-- Attachment #1: Type: text/plain, Size: 2407 bytes --]
Attention is currently required from: cron2, flichtenheld.
plaisthos has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 7:
(4 comments)
Patchset:
PS6:
So we have to decide how to go about this. The current patch only So just exporting and providing
File doc/man-sections/script-options.rst:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/f8e03d25_01b36152 :
PS6, Line 426: --tls-export-cert-path dir
> the manpage calls the option "tls-export-cert-path", while options. […]
Ooops missed that one.
File src/openvpn/init.c:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/e88dae53_952dc331 :
PS6, Line 3339: to.export_peer_cert_dir = options->tls_export_peer_cert_path;
> why call this "_dir" in the to, and "_path" in options-> ?
Fixed and now using always _dir
File src/openvpn/ssl_verify.c:
http://gerrit.openvpn.net/c/openvpn/+/466/comment/6cb0e20f_1acc6fec :
PS6, Line 734: if (opt->export_peer_cert_dir)
> So I tried to understand why it seemed to work in my testing. […]
This version of the patch now removes the environment variable together with the file. That is not as intrusive and should give at least backwards compatibility for now. The better solution is more complicated and requires modification to env handling (or even lot bigger refactoring) and is moved to follow up patches.
--
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: 7
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Comment-Date: Tue, 02 Jan 2024 16:45:08 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: cron2 <gert@...1296...>
Comment-In-Reply-To: flichtenheld <frank@...2641...>
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 4807 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (16 preceding siblings ...)
2024-01-02 16:45 ` plaisthos (Code Review)
@ 2024-01-02 16:52 ` plaisthos (Code Review)
2024-01-02 17:16 ` plaisthos (Code Review)
` (10 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2024-01-02 16:52 UTC (permalink / raw)
To: cron2 <gert@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 13517 bytes --]
Attention is currently required from: cron2, flichtenheld.
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 (#9).
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 <arne@...1227...>
---
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/9
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..1c0a6bd 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_dir);
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: 9
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-MessageType: newpatchset
[-- Attachment #2: Type: text/html, Size: 26530 bytes --]
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (17 preceding siblings ...)
2024-01-02 16:52 ` plaisthos (Code Review)
@ 2024-01-02 17:16 ` plaisthos (Code Review)
2024-01-06 15:31 ` cron2 (Code Review)
` (9 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2024-01-02 17:16 UTC (permalink / raw)
To: cron2 <gert@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 13519 bytes --]
Attention is currently required from: cron2, flichtenheld.
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 (#10).
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 <arne@...1227...>
---
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/10
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..b3b0a5f 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_dir);
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_dir);
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: 10
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-MessageType: newpatchset
[-- Attachment #2: Type: text/html, Size: 26532 bytes --]
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (18 preceding siblings ...)
2024-01-02 17:16 ` plaisthos (Code Review)
@ 2024-01-06 15:31 ` cron2 (Code Review)
2024-01-12 16:56 ` plaisthos (Code Review)
` (8 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: cron2 (Code Review) @ 2024-01-06 15:31 UTC (permalink / raw)
To: plaisthos <arne-openvpn@; +Cc: flichtenheld <frank@
[-- Attachment #1: Type: text/plain, Size: 1915 bytes --]
Attention is currently required from: flichtenheld, plaisthos.
cron2 has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 10: Code-Review+2
(1 comment)
Patchset:
PS10:
OK, this seems to be doing what the (old) manpage leads me to expect
- for each level of certificates, --tls-verify is called once (so "1x for level 0, 1x for level 1" if no intermediate CAs are used) - this was not clear to me initially, that it's indeed called multiple times.
- depending on the level of call, exactly one `$peer_cert_<n>` env variable is set, and that certificate file exists
- on level 0, `$peer_cert` is set as well
- indeed, different certs show up in these files
The old code only ever sets `peer_cert`, though, independent of the level, so I'm not sure having a (single) `$peer_cert_<n>` variable is that useful - if it's only one, `peer_cert` is maybe good enough?
So we could move onward, or move to "always `$peer_cert`, no `_<n>`...
--
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: 10
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Comment-Date: Sat, 06 Jan 2024 15:31:06 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 3330 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (19 preceding siblings ...)
2024-01-06 15:31 ` cron2 (Code Review)
@ 2024-01-12 16:56 ` plaisthos (Code Review)
2024-01-12 18:19 ` cron2 (Code Review)
` (7 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2024-01-12 16:56 UTC (permalink / raw)
To: cron2 <gert@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 12962 bytes --]
Attention is currently required from: cron2, flichtenheld, 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 (#11).
The following approvals got outdated and were removed:
Code-Review+2 by cron2
The change is no longer submittable: Code-Review and checks~ChecksSubmitRule are unsatisfied now.
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 <arne@...1227...>
---
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, 142 insertions(+), 5 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/66/466/11
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
index 6f90e14..e05100a 100644
--- a/doc/man-sections/script-options.rst
+++ b/doc/man-sections/script-options.rst
@@ -423,6 +423,14 @@
See the `Environmental Variables`_ section below for additional
parameters passed as environmental variables.
+--tls-export-cert dir
+ Adds an environment variable ``peer_cert`` 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).
@@ -633,6 +641,7 @@
Name of first ``--config`` file. Set on program initiation and reset on
SIGHUP.
+
:code:`daemon`
Set to "1" if the ``--daemon`` directive is specified, or "0" otherwise.
Set on program initiation and reset on SIGHUP.
@@ -763,6 +772,11 @@
modifier is specified, and deleted from the environment after the script
returns.
+:code:`peer_cert`
+ If the option ``--tls-export-cert`` is enabled, this option contains
+ the path to the current peer certificate to be verified in PEM format.
+ See also the argument certificate_depth to the ``--tls-verify`` command.
+
: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 f54f276..6975cbe 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -1995,6 +1995,7 @@
SHOW_STR(cipher_list_tls13);
SHOW_STR(tls_cert_profile);
SHOW_STR(tls_verify);
+ SHOW_STR(tls_export_peer_cert_dir);
SHOW_INT(verify_x509_type);
SHOW_STR(verify_x509_name);
SHOW_STR_INLINE(crl_file);
@@ -3062,6 +3063,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_dir);
MUST_BE_UNDEF(verify_x509_name);
MUST_BE_UNDEF(tls_timeout);
MUST_BE_UNDEF(renegotiate_bytes);
@@ -4092,6 +4094,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)
{
@@ -9041,6 +9050,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 cbfff18..85de887 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..55b3cf0 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,27 @@
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,
+ const char *pem_export_fname)
+{
+ /* export the path to the current certificate in pem file format */
+ setenv_str(es, "peer_cert", pem_export_fname);
+
+ return backend_x509_write_pem(peer_cert, pem_export_fname) == SUCCESS;
+}
+
+static void
+verify_cert_cert_delete_env(struct env_set *es, const char *pem_export_fname)
+{
+ env_set_del(es, "peer_cert");
+ unlink(pem_export_fname);
+}
+
/*
* call --tls-verify plug-in(s)
*/
@@ -572,18 +594,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 +729,19 @@
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, 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 +793,13 @@
ret = SUCCESS;
cleanup:
-
+ verify_cert_cert_delete_env(opt->es, 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: 11
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-MessageType: newpatchset
[-- Attachment #2: Type: text/html, Size: 24502 bytes --]
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (20 preceding siblings ...)
2024-01-12 16:56 ` plaisthos (Code Review)
@ 2024-01-12 18:19 ` cron2 (Code Review)
2024-01-12 18:19 ` [Openvpn-devel] [PATCH v11] " Gert Doering
` (6 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: cron2 (Code Review) @ 2024-01-12 18:19 UTC (permalink / raw)
To: plaisthos <arne-openvpn@; +Cc: flichtenheld <frank@
[-- Attachment #1: Type: text/plain, Size: 1185 bytes --]
Attention is currently required from: flichtenheld, plaisthos.
cron2 has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 11: Code-Review+2
(1 comment)
Patchset:
PS11:
as simple as it gets now :-) - I like that.
--
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: 11
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Comment-Date: Fri, 12 Jan 2024 18:19:01 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 2414 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [PATCH v11] Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (21 preceding siblings ...)
2024-01-12 18:19 ` cron2 (Code Review)
@ 2024-01-12 18:19 ` Gert Doering
2024-01-13 12:11 ` [Openvpn-devel] [M] Change in openvpn[master]: " cron2 (Code Review)
` (5 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: Gert Doering @ 2024-01-12 18:19 UTC (permalink / raw)
To: openvpn-devel
From: Arne Schwabe <arne@...1227...>
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 <arne@...1227...>
Acked-by: Gert Doering <gert@...1296...>
---
This change was reviewed on Gerrit and approved by at least one
developer. I request to merge it to master.
Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/466
This mail reflects revision 11 of this Change.
Acked-by according to Gerrit (reflected above):
Gert Doering <gert@...1296...>
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
index 6f90e14..e05100a 100644
--- a/doc/man-sections/script-options.rst
+++ b/doc/man-sections/script-options.rst
@@ -423,6 +423,14 @@
See the `Environmental Variables`_ section below for additional
parameters passed as environmental variables.
+--tls-export-cert dir
+ Adds an environment variable ``peer_cert`` 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).
@@ -633,6 +641,7 @@
Name of first ``--config`` file. Set on program initiation and reset on
SIGHUP.
+
:code:`daemon`
Set to "1" if the ``--daemon`` directive is specified, or "0" otherwise.
Set on program initiation and reset on SIGHUP.
@@ -763,6 +772,11 @@
modifier is specified, and deleted from the environment after the script
returns.
+:code:`peer_cert`
+ If the option ``--tls-export-cert`` is enabled, this option contains
+ the path to the current peer certificate to be verified in PEM format.
+ See also the argument certificate_depth to the ``--tls-verify`` command.
+
: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 f54f276..6975cbe 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -1995,6 +1995,7 @@
SHOW_STR(cipher_list_tls13);
SHOW_STR(tls_cert_profile);
SHOW_STR(tls_verify);
+ SHOW_STR(tls_export_peer_cert_dir);
SHOW_INT(verify_x509_type);
SHOW_STR(verify_x509_name);
SHOW_STR_INLINE(crl_file);
@@ -3062,6 +3063,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_dir);
MUST_BE_UNDEF(verify_x509_name);
MUST_BE_UNDEF(tls_timeout);
MUST_BE_UNDEF(renegotiate_bytes);
@@ -4092,6 +4094,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)
{
@@ -9041,6 +9050,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 cbfff18..85de887 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..55b3cf0 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,27 @@
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,
+ const char *pem_export_fname)
+{
+ /* export the path to the current certificate in pem file format */
+ setenv_str(es, "peer_cert", pem_export_fname);
+
+ return backend_x509_write_pem(peer_cert, pem_export_fname) == SUCCESS;
+}
+
+static void
+verify_cert_cert_delete_env(struct env_set *es, const char *pem_export_fname)
+{
+ env_set_del(es, "peer_cert");
+ unlink(pem_export_fname);
+}
+
/*
* call --tls-verify plug-in(s)
*/
@@ -572,18 +594,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 +729,19 @@
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, 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 +793,13 @@
ret = SUCCESS;
cleanup:
-
+ verify_cert_cert_delete_env(opt->es, 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)
{
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (22 preceding siblings ...)
2024-01-12 18:19 ` [Openvpn-devel] [PATCH v11] " Gert Doering
@ 2024-01-13 12:11 ` cron2 (Code Review)
2024-01-15 12:46 ` plaisthos (Code Review)
` (4 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: cron2 (Code Review) @ 2024-01-13 12:11 UTC (permalink / raw)
To: plaisthos <arne-openvpn@; +Cc: flichtenheld <frank@
[-- Attachment #1: Type: text/plain, Size: 1363 bytes --]
Attention is currently required from: flichtenheld, plaisthos.
cron2 has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
The change is no longer submittable: Code-Review is unsatisfied now.
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 11: Code-Review-1
(1 comment)
Patchset:
PS11:
As much as it pains me, this needs to do another round - it now calls "unlink(NULL)" if the feature is not active. GHA/ASAN on ubuntu 20 found this.
--
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: 11
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Comment-Date: Sat, 13 Jan 2024 12:11:20 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 2605 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (23 preceding siblings ...)
2024-01-13 12:11 ` [Openvpn-devel] [M] Change in openvpn[master]: " cron2 (Code Review)
@ 2024-01-15 12:46 ` plaisthos (Code Review)
2024-01-16 10:15 ` cron2 (Code Review)
` (3 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: plaisthos (Code Review) @ 2024-01-15 12:46 UTC (permalink / raw)
To: cron2 <gert@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 12910 bytes --]
Attention is currently required from: cron2, flichtenheld, 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 (#12).
The following approvals got outdated and were removed:
Code-Review-1 by cron2
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 <arne@...1227...>
---
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, 145 insertions(+), 5 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/66/466/12
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
index 6f90e14..e05100a 100644
--- a/doc/man-sections/script-options.rst
+++ b/doc/man-sections/script-options.rst
@@ -423,6 +423,14 @@
See the `Environmental Variables`_ section below for additional
parameters passed as environmental variables.
+--tls-export-cert dir
+ Adds an environment variable ``peer_cert`` 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).
@@ -633,6 +641,7 @@
Name of first ``--config`` file. Set on program initiation and reset on
SIGHUP.
+
:code:`daemon`
Set to "1" if the ``--daemon`` directive is specified, or "0" otherwise.
Set on program initiation and reset on SIGHUP.
@@ -763,6 +772,11 @@
modifier is specified, and deleted from the environment after the script
returns.
+:code:`peer_cert`
+ If the option ``--tls-export-cert`` is enabled, this option contains
+ the path to the current peer certificate to be verified in PEM format.
+ See also the argument certificate_depth to the ``--tls-verify`` command.
+
: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 f54f276..6975cbe 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -1995,6 +1995,7 @@
SHOW_STR(cipher_list_tls13);
SHOW_STR(tls_cert_profile);
SHOW_STR(tls_verify);
+ SHOW_STR(tls_export_peer_cert_dir);
SHOW_INT(verify_x509_type);
SHOW_STR(verify_x509_name);
SHOW_STR_INLINE(crl_file);
@@ -3062,6 +3063,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_dir);
MUST_BE_UNDEF(verify_x509_name);
MUST_BE_UNDEF(tls_timeout);
MUST_BE_UNDEF(renegotiate_bytes);
@@ -4092,6 +4094,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)
{
@@ -9041,6 +9050,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 cbfff18..85de887 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..75a4b2e 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,30 @@
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,
+ const char *pem_export_fname)
+{
+ /* export the path to the current certificate in pem file format */
+ setenv_str(es, "peer_cert", pem_export_fname);
+
+ return backend_x509_write_pem(peer_cert, pem_export_fname) == SUCCESS;
+}
+
+static void
+verify_cert_cert_delete_env(struct env_set *es, const char *pem_export_fname)
+{
+ env_set_del(es, "peer_cert");
+ if (pem_export_fname)
+ {
+ unlink(pem_export_fname);
+ }
+}
+
/*
* call --tls-verify plug-in(s)
*/
@@ -572,18 +597,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 +732,19 @@
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, 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 +796,13 @@
ret = SUCCESS;
cleanup:
-
+ verify_cert_cert_delete_env(opt->es, 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: 12
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: cron2 <gert@...1296...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-MessageType: newpatchset
[-- Attachment #2: Type: text/html, Size: 24599 bytes --]
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (24 preceding siblings ...)
2024-01-15 12:46 ` plaisthos (Code Review)
@ 2024-01-16 10:15 ` cron2 (Code Review)
2024-01-16 10:15 ` [Openvpn-devel] [PATCH v12] " Gert Doering
` (2 subsequent siblings)
28 siblings, 0 replies; 30+ messages in thread
From: cron2 (Code Review) @ 2024-01-16 10:15 UTC (permalink / raw)
To: plaisthos <arne-openvpn@; +Cc: flichtenheld <frank@
[-- Attachment #1: Type: text/plain, Size: 1100 bytes --]
Attention is currently required from: flichtenheld, plaisthos.
cron2 has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
Change subject: Implement the --tls-export-cert feature
......................................................................
Patch Set 12: Code-Review+2
--
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: 12
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Comment-Date: Tue, 16 Jan 2024 10:15:28 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment
[-- Attachment #2: Type: text/html, Size: 2021 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [PATCH v12] Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (25 preceding siblings ...)
2024-01-16 10:15 ` cron2 (Code Review)
@ 2024-01-16 10:15 ` Gert Doering
2024-01-16 11:12 ` [Openvpn-devel] [PATCH applied] " Gert Doering
2024-01-16 11:13 ` [Openvpn-devel] [M] Change in openvpn[master]: " cron2 (Code Review)
2024-01-16 11:13 ` cron2 (Code Review)
28 siblings, 1 reply; 30+ messages in thread
From: Gert Doering @ 2024-01-16 10:15 UTC (permalink / raw)
To: openvpn-devel
From: Arne Schwabe <arne@...1227...>
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 <arne@...1227...>
Acked-by: Gert Doering <gert@...1296...>
---
This change was reviewed on Gerrit and approved by at least one
developer. I request to merge it to master.
Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/466
This mail reflects revision 12 of this Change.
Acked-by according to Gerrit (reflected above):
Gert Doering <gert@...1296...>
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
index 6f90e14..e05100a 100644
--- a/doc/man-sections/script-options.rst
+++ b/doc/man-sections/script-options.rst
@@ -423,6 +423,14 @@
See the `Environmental Variables`_ section below for additional
parameters passed as environmental variables.
+--tls-export-cert dir
+ Adds an environment variable ``peer_cert`` 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).
@@ -633,6 +641,7 @@
Name of first ``--config`` file. Set on program initiation and reset on
SIGHUP.
+
:code:`daemon`
Set to "1" if the ``--daemon`` directive is specified, or "0" otherwise.
Set on program initiation and reset on SIGHUP.
@@ -763,6 +772,11 @@
modifier is specified, and deleted from the environment after the script
returns.
+:code:`peer_cert`
+ If the option ``--tls-export-cert`` is enabled, this option contains
+ the path to the current peer certificate to be verified in PEM format.
+ See also the argument certificate_depth to the ``--tls-verify`` command.
+
: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 f54f276..6975cbe 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -1995,6 +1995,7 @@
SHOW_STR(cipher_list_tls13);
SHOW_STR(tls_cert_profile);
SHOW_STR(tls_verify);
+ SHOW_STR(tls_export_peer_cert_dir);
SHOW_INT(verify_x509_type);
SHOW_STR(verify_x509_name);
SHOW_STR_INLINE(crl_file);
@@ -3062,6 +3063,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_dir);
MUST_BE_UNDEF(verify_x509_name);
MUST_BE_UNDEF(tls_timeout);
MUST_BE_UNDEF(renegotiate_bytes);
@@ -4092,6 +4094,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)
{
@@ -9041,6 +9050,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 cbfff18..85de887 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..75a4b2e 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,30 @@
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,
+ const char *pem_export_fname)
+{
+ /* export the path to the current certificate in pem file format */
+ setenv_str(es, "peer_cert", pem_export_fname);
+
+ return backend_x509_write_pem(peer_cert, pem_export_fname) == SUCCESS;
+}
+
+static void
+verify_cert_cert_delete_env(struct env_set *es, const char *pem_export_fname)
+{
+ env_set_del(es, "peer_cert");
+ if (pem_export_fname)
+ {
+ unlink(pem_export_fname);
+ }
+}
+
/*
* call --tls-verify plug-in(s)
*/
@@ -572,18 +597,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 +732,19 @@
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, 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 +796,13 @@
ret = SUCCESS;
cleanup:
-
+ verify_cert_cert_delete_env(opt->es, 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)
{
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [PATCH applied] Re: Implement the --tls-export-cert feature
2024-01-16 10:15 ` [Openvpn-devel] [PATCH v12] " Gert Doering
@ 2024-01-16 11:12 ` Gert Doering
0 siblings, 0 replies; 30+ messages in thread
From: Gert Doering @ 2024-01-16 11:12 UTC (permalink / raw)
To: Arne Schwabe <arne@; +Cc: openvpn-devel
Tested a few times. Single difference between v11 and v12 is that
it no longer calls unlink(NULL) for "no cert file created" - GHA found
that. All the rest is as fine as it was in v11, and a reasonable
re-implementation of the code removed due to the relicensing project.
Your patch has been applied to the master and release/2.6 branch
(re-implement missing functionality).
commit c58c7c3c669461805956dabc703c1279fe58eeee (master)
commit d27cb14891f3ac40e86062c475df139bbe2c6066 (release/2.6)
Author: Arne Schwabe
Date: Tue Jan 16 11:15:56 2024 +0100
Implement the --tls-export-cert feature
Signed-off-by: Arne Schwabe <arne@...1227...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20240116101556.2257-1-gert@...1296...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg28014.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (26 preceding siblings ...)
2024-01-16 10:15 ` [Openvpn-devel] [PATCH v12] " Gert Doering
@ 2024-01-16 11:13 ` cron2 (Code Review)
2024-01-16 11:13 ` cron2 (Code Review)
28 siblings, 0 replies; 30+ messages in thread
From: cron2 (Code Review) @ 2024-01-16 11:13 UTC (permalink / raw)
To: plaisthos <arne-openvpn@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 12881 bytes --]
cron2 has uploaded a new patch set (#13) to the change originally created by plaisthos. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
The following approvals got outdated and were removed:
Code-Review+2 by cron2
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 <arne@...1227...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20240116101556.2257-1-gert@...1296...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg28014.html
Signed-off-by: Gert Doering <gert@...1296...>
---
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, 145 insertions(+), 5 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/66/466/13
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
index 6f90e14..e05100a 100644
--- a/doc/man-sections/script-options.rst
+++ b/doc/man-sections/script-options.rst
@@ -423,6 +423,14 @@
See the `Environmental Variables`_ section below for additional
parameters passed as environmental variables.
+--tls-export-cert dir
+ Adds an environment variable ``peer_cert`` 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).
@@ -633,6 +641,7 @@
Name of first ``--config`` file. Set on program initiation and reset on
SIGHUP.
+
:code:`daemon`
Set to "1" if the ``--daemon`` directive is specified, or "0" otherwise.
Set on program initiation and reset on SIGHUP.
@@ -763,6 +772,11 @@
modifier is specified, and deleted from the environment after the script
returns.
+:code:`peer_cert`
+ If the option ``--tls-export-cert`` is enabled, this option contains
+ the path to the current peer certificate to be verified in PEM format.
+ See also the argument certificate_depth to the ``--tls-verify`` command.
+
: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 f54f276..6975cbe 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -1995,6 +1995,7 @@
SHOW_STR(cipher_list_tls13);
SHOW_STR(tls_cert_profile);
SHOW_STR(tls_verify);
+ SHOW_STR(tls_export_peer_cert_dir);
SHOW_INT(verify_x509_type);
SHOW_STR(verify_x509_name);
SHOW_STR_INLINE(crl_file);
@@ -3062,6 +3063,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_dir);
MUST_BE_UNDEF(verify_x509_name);
MUST_BE_UNDEF(tls_timeout);
MUST_BE_UNDEF(renegotiate_bytes);
@@ -4092,6 +4094,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)
{
@@ -9041,6 +9050,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 cbfff18..85de887 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..75a4b2e 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,30 @@
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,
+ const char *pem_export_fname)
+{
+ /* export the path to the current certificate in pem file format */
+ setenv_str(es, "peer_cert", pem_export_fname);
+
+ return backend_x509_write_pem(peer_cert, pem_export_fname) == SUCCESS;
+}
+
+static void
+verify_cert_cert_delete_env(struct env_set *es, const char *pem_export_fname)
+{
+ env_set_del(es, "peer_cert");
+ if (pem_export_fname)
+ {
+ unlink(pem_export_fname);
+ }
+}
+
/*
* call --tls-verify plug-in(s)
*/
@@ -572,18 +597,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 +732,19 @@
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, 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 +796,13 @@
ret = SUCCESS;
cleanup:
-
+ verify_cert_cert_delete_env(opt->es, 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: 13
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-MessageType: newpatchset
[-- Attachment #2: Type: text/html, Size: 24517 bytes --]
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
` (27 preceding siblings ...)
2024-01-16 11:13 ` [Openvpn-devel] [M] Change in openvpn[master]: " cron2 (Code Review)
@ 2024-01-16 11:13 ` cron2 (Code Review)
28 siblings, 0 replies; 30+ messages in thread
From: cron2 (Code Review) @ 2024-01-16 11:13 UTC (permalink / raw)
To: plaisthos <arne-openvpn@; +Cc: flichtenheld <frank@
[-- Attachment #1: Type: text/plain, Size: 12664 bytes --]
cron2 has submitted this change. ( http://gerrit.openvpn.net/c/openvpn/+/466?usp=email )
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 <arne@...1227...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20240116101556.2257-1-gert@...1296...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg28014.html
Signed-off-by: Gert Doering <gert@...1296...>
---
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, 145 insertions(+), 5 deletions(-)
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
index 6f90e14..e05100a 100644
--- a/doc/man-sections/script-options.rst
+++ b/doc/man-sections/script-options.rst
@@ -423,6 +423,14 @@
See the `Environmental Variables`_ section below for additional
parameters passed as environmental variables.
+--tls-export-cert dir
+ Adds an environment variable ``peer_cert`` 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).
@@ -633,6 +641,7 @@
Name of first ``--config`` file. Set on program initiation and reset on
SIGHUP.
+
:code:`daemon`
Set to "1" if the ``--daemon`` directive is specified, or "0" otherwise.
Set on program initiation and reset on SIGHUP.
@@ -763,6 +772,11 @@
modifier is specified, and deleted from the environment after the script
returns.
+:code:`peer_cert`
+ If the option ``--tls-export-cert`` is enabled, this option contains
+ the path to the current peer certificate to be verified in PEM format.
+ See also the argument certificate_depth to the ``--tls-verify`` command.
+
: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 f54f276..6975cbe 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -1995,6 +1995,7 @@
SHOW_STR(cipher_list_tls13);
SHOW_STR(tls_cert_profile);
SHOW_STR(tls_verify);
+ SHOW_STR(tls_export_peer_cert_dir);
SHOW_INT(verify_x509_type);
SHOW_STR(verify_x509_name);
SHOW_STR_INLINE(crl_file);
@@ -3062,6 +3063,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_dir);
MUST_BE_UNDEF(verify_x509_name);
MUST_BE_UNDEF(tls_timeout);
MUST_BE_UNDEF(renegotiate_bytes);
@@ -4092,6 +4094,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)
{
@@ -9041,6 +9050,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 cbfff18..85de887 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..75a4b2e 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,30 @@
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,
+ const char *pem_export_fname)
+{
+ /* export the path to the current certificate in pem file format */
+ setenv_str(es, "peer_cert", pem_export_fname);
+
+ return backend_x509_write_pem(peer_cert, pem_export_fname) == SUCCESS;
+}
+
+static void
+verify_cert_cert_delete_env(struct env_set *es, const char *pem_export_fname)
+{
+ env_set_del(es, "peer_cert");
+ if (pem_export_fname)
+ {
+ unlink(pem_export_fname);
+ }
+}
+
/*
* call --tls-verify plug-in(s)
*/
@@ -572,18 +597,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 +732,19 @@
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, 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 +796,13 @@
ret = SUCCESS;
cleanup:
-
+ verify_cert_cert_delete_env(opt->es, 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: 13
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: cron2 <gert@...1296...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-MessageType: merged
[-- Attachment #2: Type: text/html, Size: 24276 bytes --]
^ permalink raw reply related [flat|nested] 30+ messages in thread
end of thread, other threads:[~2024-01-16 11:13 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <gerrit.1701865618000.Ia9b3f1813d2d0d492d17c87348b4cebd0bf19ce2@...2715...>
2023-12-06 12:27 ` [Openvpn-devel] [M] Change in openvpn[master]: Implement the --export-peer-cert feature plaisthos (Code Review)
2023-12-06 12:57 ` cron2 (Code Review)
2023-12-06 14:04 ` [Openvpn-devel] [M] Change in openvpn[master]: Implement the --tls-export-cert feature plaisthos (Code Review)
2023-12-06 14:04 ` plaisthos (Code Review)
2023-12-06 17:30 ` flichtenheld (Code Review)
2023-12-07 11:27 ` plaisthos (Code Review)
2023-12-07 17:49 ` plaisthos (Code Review)
2023-12-12 14:39 ` flichtenheld (Code Review)
2023-12-12 18:24 ` plaisthos (Code Review)
2023-12-12 18:24 ` plaisthos (Code Review)
2023-12-13 14:29 ` flichtenheld (Code Review)
2023-12-14 11:17 ` [Openvpn-devel] [PATCH v6] " Frank Lichtenheld
2023-12-18 17:42 ` [Openvpn-devel] [M] Change in openvpn[master]: " cron2 (Code Review)
2023-12-20 11:57 ` flichtenheld (Code Review)
2024-01-02 13:46 ` plaisthos (Code Review)
2024-01-02 16:44 ` plaisthos (Code Review)
2024-01-02 16:45 ` plaisthos (Code Review)
2024-01-02 16:52 ` plaisthos (Code Review)
2024-01-02 17:16 ` plaisthos (Code Review)
2024-01-06 15:31 ` cron2 (Code Review)
2024-01-12 16:56 ` plaisthos (Code Review)
2024-01-12 18:19 ` cron2 (Code Review)
2024-01-12 18:19 ` [Openvpn-devel] [PATCH v11] " Gert Doering
2024-01-13 12:11 ` [Openvpn-devel] [M] Change in openvpn[master]: " cron2 (Code Review)
2024-01-15 12:46 ` plaisthos (Code Review)
2024-01-16 10:15 ` cron2 (Code Review)
2024-01-16 10:15 ` [Openvpn-devel] [PATCH v12] " Gert Doering
2024-01-16 11:12 ` [Openvpn-devel] [PATCH applied] " Gert Doering
2024-01-16 11:13 ` [Openvpn-devel] [M] Change in openvpn[master]: " cron2 (Code Review)
2024-01-16 11:13 ` cron2 (Code Review)
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.