Hi! I needed support in OpenVPN to let an external script
verify peer certificates in multi-client mode. I need this
to be able to accept individual client certificates signed
by different CAs or even self signed certificates, so that
I can control exatly who may connect when I do not operate
a CA.

I attached a patch for this against OpenVPN 2.0. It adds a
new configuration option --tls-verify-cert. When it is
turned on, the normal OpenSSL certificate preverification
is ignored and an environment variable named peer_cert is
exported for the tls-verify script, containing the peer's
certificate in base64 encoded form.

This is not PEM format because of the missing header, foo-
ter and line breaks. Instead if you un-base64 it, you'll
get the DER-form certificate.

I hope this patch can be useful to someone, or someday be
included in OpenVPN.

regards, Tom
diff -ur openvpn-2.0/init.c openvpn-2.0-new/init.c
--- openvpn-2.0/init.c  Mon Apr 11 05:43:56 2005
+++ openvpn-2.0-new/init.c      Mon May  9 18:57:45 2005
@@ -1305,6 +1305,7 @@
 #endif

   to.verify_command = options->tls_verify;
+  to.verify_export_cert = options->tls_verify_cert;
   to.verify_x509name = options->tls_remote;
   to.crl_file = options->crl_file;
   to.ns_cert_type = options->ns_cert_type;
diff -ur openvpn-2.0/options.c openvpn-2.0-new/options.c
--- openvpn-2.0/options.c       Sun Apr 17 00:03:15 2005
+++ openvpn-2.0-new/options.c   Mon May  9 18:19:02 2005
@@ -426,6 +426,9 @@
   "                  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-verify-cert: Let --tls-verify script verify the peer certificate\n"
+  "                  and disable openssl's built in verification (possibly\n"
+  "                  dangerous!)\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"
@@ -1105,6 +1108,7 @@
 #endif
   SHOW_STR (cipher_list);
   SHOW_STR (tls_verify);
+  SHOW_BOOL (tls_verify_cert);
   SHOW_STR (tls_remote);
   SHOW_STR (crl_file);
   SHOW_INT (ns_cert_type);
@@ -1625,6 +1629,7 @@
       MUST_BE_UNDEF (pkcs12_file);
       MUST_BE_UNDEF (cipher_list);
       MUST_BE_UNDEF (tls_verify);
+      MUST_BE_UNDEF (tls_verify_cert);
       MUST_BE_UNDEF (tls_remote);
       MUST_BE_UNDEF (tls_timeout);
       MUST_BE_UNDEF (renegotiate_bytes);
@@ -4355,6 +4360,11 @@
       if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
        goto err;
       options->tls_verify = string_substitute (p[1], ',', ' ', &options->gc);
+    }
+  else if (streq (p[0], "tls-verify-cert"))
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      options->tls_verify_cert = true;
     }
   else if (streq (p[0], "tls-remote") && p[1])
     {
diff -ur openvpn-2.0/options.h openvpn-2.0-new/options.h
--- openvpn-2.0/options.h       Mon Apr 11 05:43:57 2005
+++ openvpn-2.0-new/options.h   Mon May  9 18:17:35 2005
@@ -362,6 +362,7 @@
   const char *pkcs12_file;
   const char *cipher_list;
   const char *tls_verify;
+  bool tls_verify_cert; /* Verify peer cert in tls-verify script _only_ */
   const char *tls_remote;
   const char *crl_file;
   int ns_cert_type; /* set to 0, NS_SSL_SERVER, or NS_SSL_CLIENT */
diff -ur openvpn-2.0/ssl.c openvpn-2.0-new/ssl.c
--- openvpn-2.0/ssl.c   Mon Apr 11 05:43:55 2005
+++ openvpn-2.0-new/ssl.c       Mon May  9 19:03:19 2005
@@ -52,6 +52,7 @@
 #include "perf.h"
 #include "status.h"
 #include "gremlin.h"
+#include "base64.h"

 #ifdef WIN32
 #include "cryptoapi.h"
@@ -398,6 +399,31 @@
     }
 }

+static int get_peer_cert(X509_STORE_CTX *ctx, char **out)
+{
+  X509 *peercert;
+  int len, b64len;
+  char *buf, *bufp, *b64buf;
+
+  peercert = X509_STORE_CTX_get_current_cert(ctx);
+  len = i2d_X509(peercert, NULL);
+  buf = malloc(len);
+  if (!buf) {
+    *out = NULL;
+    return false;
+  }
+  bufp = buf;
+  i2d_X509(peercert, (unsigned char **) &bufp);
+  b64len = base64_encode(buf, len, &b64buf);
+  free(buf);
+  if (b64len <= 0) {
+    *out = NULL;
+    return false;
+  }
+  *out = b64buf;
+  return true;
+}
+
 /*
  * Our verify callback function -- check
  * that an incoming peer certificate is good.
@@ -413,6 +439,7 @@
   struct tls_session *session;
   const struct tls_options *opt;
   const int max_depth = 8;
+  char *peer_cert;

   /* get the tls_session pointer */
   ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
@@ -445,9 +472,18 @@
   if (!preverify_ok)
     {
       /* Remote site specified a certificate, but it's not correct */
-      msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, error=%s: %s",
-          ctx->error_depth, X509_verify_cert_error_string (ctx->error), 
subject);
-      goto err;                        /* Reject connection */
+      if (opt->verify_export_cert)
+        {
+          msg (D_TLS_ERRORS, "IGNORED VERIFY ERROR: depth=%d, error=%s: %s",
+            ctx->error_depth, X509_verify_cert_error_string (ctx->error), 
subject);
+          /* continue checking */
+        }
+      else
+        {
+          msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, error=%s: %s",
+            ctx->error_depth, X509_verify_cert_error_string (ctx->error), 
subject);
+          goto err;                    /* Reject connection */
+        }
     }

   /* warn if cert chain is too deep */
@@ -544,6 +580,14 @@
       int ret;

       setenv_str (opt->es, "script_type", "tls-verify");
+      if (opt->verify_export_cert)
+        {
+          if (get_peer_cert(ctx, &peer_cert))
+            {
+              setenv_str(opt->es, "peer_cert", peer_cert);
+              free(peer_cert);
+            }
+        }

       buf_set_write (&out, (uint8_t*)command, sizeof (command));
       buf_printf (&out, "%s %d %s",
diff -ur openvpn-2.0/ssl.h openvpn-2.0-new/ssl.h
--- openvpn-2.0/ssl.h   Mon Apr 11 05:43:56 2005
+++ openvpn-2.0-new/ssl.h       Mon May  9 19:05:41 2005
@@ -407,6 +407,7 @@

   /* cert verification parms */
   const char *verify_command;
+  bool verify_export_cert;
   const char *verify_x509name;
   const char *crl_file;
   int ns_cert_type;

Reply via email to