]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
enhance tls-verify possibility
authorMathieu GIANNECCHINI <mat.giann@free.fr>
Mon, 1 Mar 2010 23:26:57 +0000 (00:26 +0100)
committerDavid Sommerseth <dazo@users.sourceforge.net>
Tue, 2 Mar 2010 20:24:07 +0000 (21:24 +0100)
It should be nice to enhance tls-verify check possibilities against peer
cert during a pending TLS connection like :
- OCSP verification
- check any X509 extensions of the peer certificate
- delta CRL verification
- ...

This patch add a new "tls-export-cert" option which allow to get peer
certificate in PEM format and to store it in an openvpn temporary file.
Peer certificate is stored before tls-script execution and deleted after.
The name of the related temporary file is available under tls-verify
script by an environment variable "peer_cert".

The patch was made from OpenVPN svn Beta21 branches.

Here is a very simple exemple of Tls-verify script which provide OCSP
support to OpenVPN (with tls-export-cert option) without any OpenVPN
"core" modification :

X509=$2

openssl ocsp \
      -issuer /etc/openvpn/ssl.crt/RootCA.pem \
      -CAfile /etc/openvpn/ssl.capath/OpenVPNServeur-cafile.pem \
      -cert $peer_cert \
      -url http://your-ocsp-url
      if [ $? -ne 0 ]
      then
          echo "error : OCSP check failed for ${X509}" | logger -t
"tls-verify"
          exit 1
       fi

This has been discussed here:
<http://thread.gmane.org/gmane.network.openvpn.devel/2492>
<http://thread.gmane.org/gmane.network.openvpn.devel/3150>
<http://thread.gmane.org/gmane.network.openvpn.devel/3217>

This patch has been modified by David Sommerseth, by fixing a few issues
which came up to during the code review process.  The man page has been
updated and tmp_file in ssl.c is checked for not being NULL before calling
delete_file().

Signed-off-by: David Sommerseth <dazo@users.sourceforge.net>
Acked-by: Gert Doering <gert@greenie.muc.de>
init.c
openvpn.8
options.c
options.h
ssl.c
ssl.h

diff --git a/init.c b/init.c
index 3748c2e3dd4adbf51a4205a4d406061ea3a7a0d5..19ac032861b9bf408bb362d46f69b6fa848ec1fe 100644 (file)
--- a/init.c
+++ b/init.c
@@ -1805,6 +1805,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
 #endif
 
   to.verify_command = options->tls_verify;
+  to.verify_export_cert = options->tls_export_cert;
   to.verify_x509name = options->tls_remote;
   to.crl_file = options->crl_file;
   to.ns_cert_type = options->ns_cert_type;
index 45e61fa53c01e64b46c7841f022fde56fdb88e9f..4846d34be0581c94b0476122c8503676ab800379 100644 (file)
--- a/openvpn.8
+++ b/openvpn.8
@@ -4258,6 +4258,14 @@ to
 to build a command line which will be passed to the script.
 .\"*********************************************************
 .TP
+.B --tls-export-cert directory
+Store the certificates the clients uses upon connection to this
+directory. This will be done before --tls-verify is called.  The
+certificates will use a temporary name and will be deleted when
+the tls-verify script returns.  The file name used for the certificate
+is available via the peer_cert environment variable.
+.\"*********************************************************
+.TP
 .B --tls-remote name
 Accept connections only from a host with X509 name
 or common name equal to
@@ -5242,6 +5250,11 @@ than their names as denoted on the command line
 or configuration file.
 .\"*********************************************************
 .TP
+.B peer_cert
+Temporary file name containing the client certificate upon
+connection.  Useful in conjunction with --tls-verify
+.\"*********************************************************
+.TP
 .B script_context
 Set to "init" or "restart" prior to up/down script execution.
 For more information, see
index 36b9913533a6cb848011d9d53582ce905e35eb23..e79f742dee4a19da25b00d0bf8a3ce1d7035b2b7 100644 (file)
--- a/options.c
+++ b/options.c
@@ -529,6 +529,9 @@ static const char usage_message[] =
   "                  tests of certification.  cmd should return 0 to allow\n"
   "                  TLS handshake to proceed, or 1 to fail.  (cmd is\n"
   "                  executed as 'cmd certificate_depth X509_NAME_oneline')\n"
+  "--tls-export-cert [directory] : Get peer cert in PEM format and store it \n"
+  "                  in an openvpn temporary file in [directory]. Peer cert is \n"
+  "                  stored before tls-verify script execution and deleted after.\n"
   "--tls-remote x509name: Accept connections only from a host with X509 name\n"
   "                  x509name. The remote host must also pass all other tests\n"
   "                  of verification.\n"
@@ -1325,6 +1328,7 @@ show_settings (const struct options *o)
 #endif
   SHOW_STR (cipher_list);
   SHOW_STR (tls_verify);
+  SHOW_STR (tls_export_cert);
   SHOW_STR (tls_remote);
   SHOW_STR (crl_file);
   SHOW_INT (ns_cert_type);
@@ -1914,6 +1918,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
       MUST_BE_UNDEF (pkcs12_file);
       MUST_BE_UNDEF (cipher_list);
       MUST_BE_UNDEF (tls_verify);
+      MUST_BE_UNDEF (tls_export_cert);
       MUST_BE_UNDEF (tls_remote);
       MUST_BE_UNDEF (tls_timeout);
       MUST_BE_UNDEF (renegotiate_bytes);
@@ -5525,6 +5530,11 @@ add_option (struct options *options,
        goto err;
       options->tls_verify = string_substitute (p[1], ',', ' ', &options->gc);
     }
+  else if (streq (p[0], "tls-export-cert") && p[1])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      options->tls_export_cert = p[1];
+    }
   else if (streq (p[0], "tls-remote") && p[1])
     {
       VERIFY_PERMISSION (OPT_P_GENERAL);
index 740e18ed7f9574e94715e88cc5f915d321bc7146..a3114e2fc415fc953d3effff6076d924a3cd0af8 100644 (file)
--- a/options.h
+++ b/options.h
@@ -440,6 +440,7 @@ struct options
   const char *pkcs12_file;
   const char *cipher_list;
   const char *tls_verify;
+  const char *tls_export_cert;
   const char *tls_remote;
   const char *crl_file;
 
diff --git a/ssl.c b/ssl.c
index 82e04a3b9b71e016383b20cf9813069c9ce95de1..d5230f7d6f438fecb1b03736d8ac62b6e1db7dc2 100644 (file)
--- a/ssl.c
+++ b/ssl.c
@@ -687,6 +687,49 @@ string_mod_sslname (char *str, const unsigned int restrictive_flags, const unsig
     string_mod (str, restrictive_flags, 0, '_');
 }
 
+/* Get peer cert and store it in pem format in a temporary file
+ * in tmp_dir
+ */
+
+const char *
+get_peer_cert(X509_STORE_CTX *ctx, const char *tmp_dir, struct gc_arena *gc)
+{
+  X509 *peercert;
+  FILE *peercert_file;
+  const char *peercert_filename="";
+
+  if(!tmp_dir)
+      return NULL;
+
+  /* get peer cert */
+  peercert = X509_STORE_CTX_get_current_cert(ctx);
+  if(!peercert)
+    {
+      msg (M_ERR, "Unable to get peer certificate from current context");
+      return NULL;
+    }
+
+  /* create tmp file to store peer cert */
+  peercert_filename = create_temp_filename (tmp_dir, "pcf", gc);
+
+  /* write peer-cert in tmp-file */
+  peercert_file = fopen(peercert_filename, "w+");
+  if(!peercert_file)
+    {
+      msg (M_ERR, "Failed to open temporary file : %s", peercert_filename);
+      return NULL;
+    }
+  if(PEM_write_X509(peercert_file,peercert)<0)
+    {
+      msg (M_ERR, "Failed to write peer certificate in PEM format");
+      fclose(peercert_file);
+      return NULL;
+    }
+
+  fclose(peercert_file);
+  return peercert_filename;
+}
+
 /*
  * Our verify callback function -- check
  * that an incoming peer certificate is good.
@@ -885,10 +928,21 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
   /* run --tls-verify script */
   if (opt->verify_command)
     {
+      const char *tmp_file;
+      struct gc_arena gc;
       int ret;
 
       setenv_str (opt->es, "script_type", "tls-verify");
 
+      if (opt->verify_export_cert)
+        {
+          gc = gc_new();
+          if (tmp_file=get_peer_cert(ctx, opt->verify_export_cert,&gc))
+           {
+             setenv_str(opt->es, "peer_cert", tmp_file);
+           }
+        }
+
       argv_printf (&argv, "%sc %d %s",
                   opt->verify_command,
                   ctx->error_depth,
@@ -896,6 +950,13 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
       argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command");
       ret = openvpn_execve (&argv, opt->es, S_SCRIPT);
 
+      if (opt->verify_export_cert)
+        {
+           if (tmp_file)
+              delete_file(tmp_file);
+           gc_free(&gc);
+        }
+
       if (system_ok (ret))
        {
          msg (D_HANDSHAKE, "VERIFY SCRIPT OK: depth=%d, %s",
diff --git a/ssl.h b/ssl.h
index 9737f265a3facb93cc82d0f8c8338600887b91b2..a22c8546b0f8c389db83f249070b7d43b9cb0e11 100644 (file)
--- a/ssl.h
+++ b/ssl.h
@@ -441,6 +441,7 @@ struct tls_options
 
   /* cert verification parms */
   const char *verify_command;
+  const char *verify_export_cert;
   const char *verify_x509name;
   const char *crl_file;
   int ns_cert_type;