summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2014-04-24 23:28:24 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2014-04-24 23:28:24 +0100
commit2b4a568dfa3d79a9a968984cf5b23829c084a951 (patch)
treecebc0c6b8f1aac79863f3b39160027eaed4df5aa /src
parent4e0983dcef8dd8630fc77aad39f7606e2ed32199 (diff)
Support OCSP Stapling under GnuTLS. Bug 1459
Requires GnuTLS version 3.1.3 or later. Under EXPERIMENTAL_OCSP
Diffstat (limited to 'src')
-rw-r--r--src/src/EDITME2
-rw-r--r--src/src/globals.c2
-rw-r--r--src/src/globals.h2
-rw-r--r--src/src/readconf.c2
-rw-r--r--src/src/tls-gnu.c105
5 files changed, 94 insertions, 19 deletions
diff --git a/src/src/EDITME b/src/src/EDITME
index 7377af844..d13b1b13a 100644
--- a/src/src/EDITME
+++ b/src/src/EDITME
@@ -456,7 +456,7 @@ EXIM_MONITOR=eximon.bin
# LDFLAGS += -lxml2_single -lbmiclient_single -L/opt/brightmail/bsdk-6.0/lib
# Uncomment the following line to add OCSP stapling support in TLS, if Exim
-# was built using OpenSSL.
+# was built using OpenSSL, or with GnuTLS 3.1.3 or later.
# EXPERIMENTAL_OCSP=yes
diff --git a/src/src/globals.c b/src/src/globals.c
index cb014fbe8..da81b8db5 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -137,7 +137,7 @@ that's the interop problem which has been observed: GnuTLS suggesting a higher
bit-count as "NORMAL" (2432) and Thunderbird dropping connection. */
int tls_dh_max_bits = 2236;
uschar *tls_dhparam = NULL;
-#if defined(EXPERIMENTAL_OCSP) && !defined(USE_GNUTLS)
+#if defined(EXPERIMENTAL_OCSP)
uschar *tls_ocsp_file = NULL;
#endif
BOOL tls_offered = FALSE;
diff --git a/src/src/globals.h b/src/src/globals.h
index cfa6d2bff..79bf38caa 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -105,7 +105,7 @@ extern uschar *tls_channelbinding_b64; /* string of base64 channel binding */
extern uschar *tls_crl; /* CRL File */
extern int tls_dh_max_bits; /* don't accept higher lib suggestions */
extern uschar *tls_dhparam; /* DH param file */
-#if defined(EXPERIMENTAL_OCSP) && !defined(USE_GNUTLS)
+#if defined(EXPERIMENTAL_OCSP)
extern uschar *tls_ocsp_file; /* OCSP stapling proof file */
#endif
extern BOOL tls_offered; /* Server offered TLS */
diff --git a/src/src/readconf.c b/src/src/readconf.c
index a0238d25f..f213b2c57 100644
--- a/src/src/readconf.c
+++ b/src/src/readconf.c
@@ -436,7 +436,7 @@ static optionlist optionlist_config[] = {
{ "tls_crl", opt_stringptr, &tls_crl },
{ "tls_dh_max_bits", opt_int, &tls_dh_max_bits },
{ "tls_dhparam", opt_stringptr, &tls_dhparam },
-# if defined(EXPERIMENTAL_OCSP) && !defined(USE_GNUTLS)
+# if defined(EXPERIMENTAL_OCSP)
{ "tls_ocsp_file", opt_stringptr, &tls_ocsp_file },
# endif
{ "tls_on_connect_ports", opt_stringptr, &tls_in.on_connect_ports },
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index cbd44d6f2..ace59633a 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -43,6 +43,9 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
#if GNUTLS_VERSION_NUMBER >= 0x020c00
# include <gnutls/pkcs11.h>
#endif
+#ifdef EXPERIMENTAL_OCSP
+# include <gnutls/ocsp.h>
+#endif
/* GnuTLS 2 vs 3
@@ -658,7 +661,7 @@ uschar *saved_tls_crl = NULL;
int cert_count;
/* We check for tls_sni *before* expansion. */
-if (!state->host)
+if (!host) /* server */
{
if (!state->received_sni)
{
@@ -700,7 +703,7 @@ if (!expand_check_tlsvar(tls_certificate))
if ((state->exp_tls_certificate == NULL) ||
(*state->exp_tls_certificate == '\0'))
{
- if (state->host == NULL)
+ if (!host)
return tls_error(US"no TLS server certificate is specified", NULL, NULL);
else
DEBUG(D_tls) debug_printf("TLS: no client certificate specified; okay\n");
@@ -745,6 +748,30 @@ if (state->exp_tls_certificate && *state->exp_tls_certificate)
DEBUG(D_tls) debug_printf("TLS: cert/key registered\n");
} /* tls_certificate */
+
+/* Set the OCSP stapling server info */
+
+#ifdef EXPERIMENTAL_OCSP
+if ( !host /* server */
+ && tls_ocsp_file
+ )
+ {
+ uschar * expanded;
+ int rc;
+
+ if (!expand_check(tls_ocsp_file, US"tls_ocsp_file", &expanded))
+ return DEFER;
+
+ /* Lazy way; would like callback to emit debug on actual response */
+
+ rc = gnutls_certificate_set_ocsp_status_request_file(state->x509_cred,
+ expanded, 0);
+ exim_gnutls_err_check(US"gnutls_certificate_set_ocsp_status_request_file");
+ DEBUG(D_tls) debug_printf("Set OCSP response file %s\n", expanded);
+ }
+#endif
+
+
/* Set the trusted CAs file if one is provided, and then add the CRL if one is
provided. Experiment shows that, if the certificate file is empty, an unhelpful
error message is provided. However, if we just refrain from setting anything up
@@ -1559,10 +1586,11 @@ Arguments:
verify_certs file for certificate verify
verify_crl CRL for verify
require_ciphers list of allowed ciphers or NULL
+ hosts_require_ocsp hosts for which to request certificate-status (OCSP)
dh_min_bits minimum number of bits acceptable in server's DH prime
timeout startup timeout
- verify_hosts mandatory client verification
- try_verify_hosts optional client verification
+ verify_hosts mandatory client verification
+ try_verify_hosts optional client verification
Returns: OK/DEFER/FAIL (because using common functions),
but for a client, DEFER and FAIL have the same meaning
@@ -1575,7 +1603,7 @@ tls_client_start(int fd, host_item *host,
uschar *verify_certs, uschar *verify_crl,
uschar *require_ciphers,
#ifdef EXPERIMENTAL_OCSP
- uschar *require_ocsp ARG_UNUSED,
+ uschar *hosts_require_ocsp,
#endif
int dh_min_bits, int timeout,
uschar *verify_hosts, uschar *try_verify_hosts)
@@ -1583,12 +1611,16 @@ tls_client_start(int fd, host_item *host,
int rc;
const char *error;
exim_gnutls_state_st *state = NULL;
+#ifdef EXPERIMENTAL_OCSP
+BOOL require_ocsp = verify_check_this_host(&hosts_require_ocsp,
+ NULL, host->name, host->address, NULL) == OK;
+#endif
DEBUG(D_tls) debug_printf("initialising GnuTLS as a client on fd %d\n", fd);
-rc = tls_init(host, certificate, privatekey,
- sni, verify_certs, verify_crl, require_ciphers, &state);
-if (rc != OK) return rc;
+if ((rc = tls_init(host, certificate, privatekey,
+ sni, verify_certs, verify_crl, require_ciphers, &state)) != OK)
+ return rc;
if (dh_min_bits < EXIM_CLIENT_DH_MIN_MIN_BITS)
{
@@ -1602,11 +1634,17 @@ DEBUG(D_tls) debug_printf("Setting D-H prime minimum acceptable bits to %d\n",
dh_min_bits);
gnutls_dh_set_prime_bits(state->session, dh_min_bits);
-/* stick to the old behaviour for compatibility if tls_verify_certificates is
- set but both tls_verify_hosts and tls_try_verify_hosts is not set. Check only
- the specified host patterns if one of them is defined */
-if (((state->exp_tls_verify_certificates != NULL) && (verify_hosts == NULL) && (try_verify_hosts == NULL)) ||
- (verify_check_host(&verify_hosts) == OK))
+/* Stick to the old behaviour for compatibility if tls_verify_certificates is
+set but both tls_verify_hosts and tls_try_verify_hosts are unset. Check only
+the specified host patterns if one of them is defined */
+
+if (( state->exp_tls_verify_certificates
+ && !verify_hosts
+ && !try_verify_hosts
+ )
+ ||
+ verify_check_host(&verify_hosts) == OK
+ )
{
DEBUG(D_tls) debug_printf("TLS: server certificate verification required.\n");
state->verify_requirement = VERIFY_REQUIRED;
@@ -1625,6 +1663,13 @@ else
gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_IGNORE);
}
+#ifdef EXPERIMENTAL_OCSP /* since GnuTLS 3.1.3 */
+if (require_ocsp &&
+ (rc = gnutls_ocsp_status_request_enable_client(state->session, NULL, 0, NULL))
+ != OK)
+ return tls_error(US"cert-status-req", gnutls_strerror(rc), state->host);
+#endif
+
gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr)fd);
state->fd_in = fd;
state->fd_out = fd;
@@ -1652,10 +1697,38 @@ if (state->verify_requirement != VERIFY_NONE &&
!verify_certificate(state, &error))
return tls_error(US"certificate verification failed", error, state->host);
+#ifdef EXPERIMENTAL_OCSP
+if (require_ocsp)
+ {
+ DEBUG(D_tls)
+ {
+ gnutls_datum_t stapling;
+ gnutls_ocsp_resp_t resp;
+ gnutls_datum_t printed;
+ if ( (rc= gnutls_ocsp_status_request_get(state->session, &stapling)) == 0
+ && (rc= gnutls_ocsp_resp_init(&resp)) == 0
+ && (rc= gnutls_ocsp_resp_import(resp, &stapling)) == 0
+ && (rc= gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &printed)) == 0
+ )
+ {
+ fprintf(stderr, "%.4096s", printed.data);
+ gnutls_free(printed.data);
+ }
+ else
+ (void) tls_error(US"ocsp decode", gnutls_strerror(rc), state->host);
+ }
+
+ fprintf(stderr, "%s: checking ocsp\n", __FUNCTION__);
+ if (gnutls_ocsp_status_request_is_checked(state->session, 0) == 0)
+ return tls_error(US"certificate status check failed", NULL, state->host);
+ DEBUG(D_tls) debug_printf("Passed OCSP checking\n");
+ }
+#endif
+
/* Figure out peer DN, and if authenticated, etc. */
-rc = peer_status(state);
-if (rc != OK) return rc;
+if ((rc = peer_status(state)) != OK)
+ return rc;
/* Sets various Exim expansion variables; may need to adjust for ACL callouts */
@@ -2041,4 +2114,6 @@ fprintf(f, "Library version: GnuTLS: Compile: %s\n"
gnutls_check_version(NULL));
}
+/* vi: aw ai sw=2
+*/
/* End of tls-gnu.c */