Hi,
I've set up a VPN service which authenticates users using certificates
provided by a 3rd party (which has manually authenticated the users
from paper documents and given out some 50k certs). Their tools allow
the end users to nicely export a PKCS#12 certificate which OpenVPN can
currently use out of the box. Grand!
The 3rd party does not give out server certificates, so my VPN server
uses a certificate signed by my own CA. Thus I need to pass my CA to
the VPN client using the --ca option.
Currently OpenVPN ignores CA and intermediate certificates inside the
PKCS#12 file if --ca is set. Without --ca it loads them. The problem
is that the 3rd party CA uses intermediate certificates, and rotates
them ~yearly without warning, so I need the client to load the
intermediates from PKCS#12. To get both client and server validation
working I need to load certs from both the PKCS#12 file and a PEM file
provided using --ca.
So, I added a client option 'pkcs12-additional-cas' to make the --ca
and pkcs12 CA certs additive, not exclusive either-or. Default
functionality is like before. Manual page updated, too.
Patch attached, feedback welcome. I'm not quite sure if the name of
the option (--pkcs12-additional-cas) is good.
---
doc/openvpn.8 | 16 ++++++++++++++++
src/openvpn/options.c | 16 ++++++++++++++++
src/openvpn/options.h | 1 +
src/openvpn/ssl.c | 2 +-
src/openvpn/ssl_openssl.c | 2 +-
5 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/doc/openvpn.8 b/doc/openvpn.8
index cbfc107..c758776 100644
--- a/doc/openvpn.8
+++ b/doc/openvpn.8
@@ -4225,6 +4225,22 @@ and
Not available with PolarSSL.
.\"*********************************************************
.TP
+.B \-\-pkcs12-additional-cas
+Load trusted CA and intermediate certificates from both the PKCS #12
+file specified using
+.B \-\-pkcs12
+and an additional PEM file specified using
+.B \-\-ca.
+The default is to not load CA or intermediate certificates from a
+PKCS #12 file if
+.B \-\-ca
+is set. This option can be used when the PKCS #12 client certificate
+is provided by a different CA than the server certificate, and
+contains intermediate certificates required for successful client
+authentication, but does not contain the CA certificate used for signing
+the server's certificate.
+.\"*********************************************************
+.TP
.B \-\-verify-hash hash
Specify SHA1 fingerprint for level-1 cert. The level-1 cert is the
CA (or intermediate cert) that signs the leaf certificate, and is
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index c5ed0d6..5175607 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -578,6 +578,10 @@ static const char usage_message[] =
#ifndef ENABLE_CRYPTO_POLARSSL
"--pkcs12 file : PKCS#12 file containing local private key, local
certificate\n"
" and optionally the root CA certificate.\n"
+ "--pkcs12-additional-cas : Load CA certificates from both the
PKCS#12 file and a\n"
+ " PEM file specified using --ca. Default
is to ignore\n"
+ " CAs and intermediate certificates
contained in the\n"
+ " PKCS#12 file if --ca is set.\n"
#endif
#ifdef ENABLE_X509ALTUSERNAME
"--x509-username-field : Field used in x509 certificate to be username.\n"
@@ -1594,6 +1598,7 @@ show_settings (const struct options *o)
SHOW_STR (priv_key_file);
#ifndef ENABLE_CRYPTO_POLARSSL
SHOW_STR (pkcs12_file);
+ SHOW_BOOL (pkcs12_additional_cas);
#endif
#ifdef ENABLE_CRYPTOAPI
SHOW_STR (cryptoapi_cert);
@@ -2286,6 +2291,12 @@ options_postprocess_verify_ce (const struct
options *options, const struct conne
notnull (options->priv_key_file, "private key file (--key) or
PKCS#12 file (--pkcs12)");
}
}
+
+ if (options->pkcs12_additional_cas && !options->ca_file &&
!options->pkcs12_file)
+ {
+ msg (M_USAGE, "When --pkcs12-additional-cas is set, CA
certificates are loaded from both a PEM file (--ca) and a PKCS#12 file
(--pkcs12). Both options must be specified.");
+ }
+
}
else
{
@@ -6458,6 +6469,11 @@ add_option (struct options *options,
options->pkcs12_file_inline = p[2];
}
}
+ else if (streq (p[0], "pkcs12-additional-cas"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->pkcs12_additional_cas = true;
+ }
#endif /* ENABLE_CRYPTO_POLARSSL */
else if (streq (p[0], "askpass"))
{
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index d2ad94c..2f2fe8b 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -504,6 +504,7 @@ struct options
const char *extra_certs_file;
const char *priv_key_file;
const char *pkcs12_file;
+ bool pkcs12_additional_cas;
const char *cipher_list;
const char *tls_verify;
int verify_x509_type;
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 09cf300..e4457fd 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -472,7 +472,7 @@ init_ssl (const struct options *options, struct
tls_root_ctx *new_ctx)
if (options->pkcs12_file)
{
if (0 != tls_ctx_load_pkcs12(new_ctx, options->pkcs12_file,
- options->pkcs12_file_inline, !options->ca_file))
+ options->pkcs12_file_inline, options->pkcs12_additional_cas ||
!options->ca_file))
goto err;
}
#ifdef ENABLE_PKCS11