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

Reply via email to