Browse code

enhance tls-verify possibility

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>

Mathieu GIANNECCHINI authored on 2010/03/02 08:26:57
Showing 6 changed files
... ...
@@ -2024,6 +2024,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
2024 2024
 #endif
2025 2025
 
2026 2026
   to.verify_command = options->tls_verify;
2027
+  to.verify_export_cert = options->tls_export_cert;
2027 2028
   to.verify_x509name = options->tls_remote;
2028 2029
   to.crl_file = options->crl_file;
2029 2030
   to.ns_cert_type = options->ns_cert_type;
... ...
@@ -4288,6 +4288,14 @@ to
4288 4288
 to build a command line which will be passed to the script.
4289 4289
 .\"*********************************************************
4290 4290
 .TP
4291
+.B \-\-tls-export-cert directory
4292
+Store the certificates the clients uses upon connection to this
4293
+directory. This will be done before --tls-verify is called.  The
4294
+certificates will use a temporary name and will be deleted when
4295
+the tls-verify script returns.  The file name used for the certificate
4296
+is available via the peer_cert environment variable.
4297
+.\"*********************************************************
4298
+.TP
4291 4299
 .B \-\-tls-remote name
4292 4300
 Accept connections only from a host with X509 name
4293 4301
 or common name equal to
... ...
@@ -5286,6 +5294,11 @@ than their names as denoted on the command line
5286 5286
 or configuration file.
5287 5287
 .\"*********************************************************
5288 5288
 .TP
5289
+.B peer_cert
5290
+Temporary file name containing the client certificate upon
5291
+connection.  Useful in conjunction with --tls-verify
5292
+.\"*********************************************************
5293
+.TP
5289 5294
 .B script_context
5290 5295
 Set to "init" or "restart" prior to up/down script execution.
5291 5296
 For more information, see
... ...
@@ -540,6 +540,9 @@ static const char usage_message[] =
540 540
   "                  tests of certification.  cmd should return 0 to allow\n"
541 541
   "                  TLS handshake to proceed, or 1 to fail.  (cmd is\n"
542 542
   "                  executed as 'cmd certificate_depth X509_NAME_oneline')\n"
543
+  "--tls-export-cert [directory] : Get peer cert in PEM format and store it \n"
544
+  "                  in an openvpn temporary file in [directory]. Peer cert is \n"
545
+  "                  stored before tls-verify script execution and deleted after.\n"
543 546
   "--tls-remote x509name: Accept connections only from a host with X509 name\n"
544 547
   "                  x509name. The remote host must also pass all other tests\n"
545 548
   "                  of verification.\n"
... ...
@@ -1341,6 +1344,7 @@ show_settings (const struct options *o)
1341 1341
 #endif
1342 1342
   SHOW_STR (cipher_list);
1343 1343
   SHOW_STR (tls_verify);
1344
+  SHOW_STR (tls_export_cert);
1344 1345
   SHOW_STR (tls_remote);
1345 1346
   SHOW_STR (crl_file);
1346 1347
   SHOW_INT (ns_cert_type);
... ...
@@ -2069,6 +2073,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
2069 2069
       MUST_BE_UNDEF (pkcs12_file);
2070 2070
       MUST_BE_UNDEF (cipher_list);
2071 2071
       MUST_BE_UNDEF (tls_verify);
2072
+      MUST_BE_UNDEF (tls_export_cert);
2072 2073
       MUST_BE_UNDEF (tls_remote);
2073 2074
       MUST_BE_UNDEF (tls_timeout);
2074 2075
       MUST_BE_UNDEF (renegotiate_bytes);
... ...
@@ -5779,6 +5784,11 @@ add_option (struct options *options,
5779 5779
       warn_multiple_script (options->tls_verify, "tls-verify");
5780 5780
       options->tls_verify = string_substitute (p[1], ',', ' ', &options->gc);
5781 5781
     }
5782
+  else if (streq (p[0], "tls-export-cert") && p[1])
5783
+    {
5784
+      VERIFY_PERMISSION (OPT_P_GENERAL);
5785
+      options->tls_export_cert = p[1];
5786
+    }
5782 5787
   else if (streq (p[0], "tls-remote") && p[1])
5783 5788
     {
5784 5789
       VERIFY_PERMISSION (OPT_P_GENERAL);
... ...
@@ -467,6 +467,7 @@ struct options
467 467
   const char *pkcs12_file;
468 468
   const char *cipher_list;
469 469
   const char *tls_verify;
470
+  const char *tls_export_cert;
470 471
   const char *tls_remote;
471 472
   const char *crl_file;
472 473
 
... ...
@@ -691,6 +691,49 @@ string_mod_sslname (char *str, const unsigned int restrictive_flags, const unsig
691 691
     string_mod (str, restrictive_flags, 0, '_');
692 692
 }
693 693
 
694
+/* Get peer cert and store it in pem format in a temporary file
695
+ * in tmp_dir
696
+ */
697
+
698
+const char *
699
+get_peer_cert(X509_STORE_CTX *ctx, const char *tmp_dir, struct gc_arena *gc)
700
+{
701
+  X509 *peercert;
702
+  FILE *peercert_file;
703
+  const char *peercert_filename="";
704
+
705
+  if(!tmp_dir)
706
+      return NULL;
707
+
708
+  /* get peer cert */
709
+  peercert = X509_STORE_CTX_get_current_cert(ctx);
710
+  if(!peercert)
711
+    {
712
+      msg (M_ERR, "Unable to get peer certificate from current context");
713
+      return NULL;
714
+    }
715
+
716
+  /* create tmp file to store peer cert */
717
+  peercert_filename = create_temp_filename (tmp_dir, "pcf", gc);
718
+
719
+  /* write peer-cert in tmp-file */
720
+  peercert_file = fopen(peercert_filename, "w+");
721
+  if(!peercert_file)
722
+    {
723
+      msg (M_ERR, "Failed to open temporary file : %s", peercert_filename);
724
+      return NULL;
725
+    }
726
+  if(PEM_write_X509(peercert_file,peercert)<0)
727
+    {
728
+      msg (M_ERR, "Failed to write peer certificate in PEM format");
729
+      fclose(peercert_file);
730
+      return NULL;
731
+    }
732
+
733
+  fclose(peercert_file);
734
+  return peercert_filename;
735
+}
736
+
694 737
 /*
695 738
  * Our verify callback function -- check
696 739
  * that an incoming peer certificate is good.
... ...
@@ -920,10 +963,21 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
920 920
   /* run --tls-verify script */
921 921
   if (opt->verify_command)
922 922
     {
923
+      const char *tmp_file;
924
+      struct gc_arena gc;
923 925
       int ret;
924 926
 
925 927
       setenv_str (opt->es, "script_type", "tls-verify");
926 928
 
929
+      if (opt->verify_export_cert)
930
+        {
931
+          gc = gc_new();
932
+          if (tmp_file=get_peer_cert(ctx, opt->verify_export_cert,&gc))
933
+           {
934
+             setenv_str(opt->es, "peer_cert", tmp_file);
935
+           }
936
+        }
937
+
927 938
       argv_printf (&argv, "%sc %d %s",
928 939
 		   opt->verify_command,
929 940
 		   ctx->error_depth,
... ...
@@ -931,6 +985,13 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
931 931
       argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command");
932 932
       ret = openvpn_execve (&argv, opt->es, S_SCRIPT);
933 933
 
934
+      if (opt->verify_export_cert)
935
+        {
936
+           if (tmp_file)
937
+              delete_file(tmp_file);
938
+           gc_free(&gc);
939
+        }
940
+
934 941
       if (system_ok (ret))
935 942
 	{
936 943
 	  msg (D_HANDSHAKE, "VERIFY SCRIPT OK: depth=%d, %s",
... ...
@@ -446,6 +446,7 @@ struct tls_options
446 446
 
447 447
   /* cert verification parms */
448 448
   const char *verify_command;
449
+  const char *verify_export_cert;
449 450
   const char *verify_x509name;
450 451
   const char *crl_file;
451 452
   int ns_cert_type;