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>
... | ... |
@@ -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); |
... | ... |
@@ -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", |