summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/src/expand.c2
-rw-r--r--src/src/globals.c6
-rw-r--r--src/src/globals.h6
-rw-r--r--src/src/smtp_in.c1
-rw-r--r--src/src/spool_in.c3
-rw-r--r--src/src/spool_out.c1
-rw-r--r--src/src/tls-gnu.c63
-rw-r--r--src/src/tls-openssl.c84
-rw-r--r--src/src/transports/smtp.c5
-rw-r--r--src/src/transports/smtp.h1
10 files changed, 126 insertions, 46 deletions
diff --git a/src/src/expand.c b/src/src/expand.c
index 05b714a7f..de911db90 100644
--- a/src/src/expand.c
+++ b/src/src/expand.c
@@ -671,6 +671,7 @@ static var_entry var_table[] = {
{ "tls_in_bits", vtype_int, &tls_in.bits },
{ "tls_in_certificate_verified", vtype_int, &tls_in.certificate_verified },
{ "tls_in_cipher", vtype_stringptr, &tls_in.cipher },
+ { "tls_in_ocsp", vtype_int, &tls_in.ocsp },
{ "tls_in_ourcert", vtype_cert, &tls_in.ourcert },
{ "tls_in_peercert", vtype_cert, &tls_in.peercert },
{ "tls_in_peerdn", vtype_stringptr, &tls_in.peerdn },
@@ -680,6 +681,7 @@ static var_entry var_table[] = {
{ "tls_out_bits", vtype_int, &tls_out.bits },
{ "tls_out_certificate_verified", vtype_int,&tls_out.certificate_verified },
{ "tls_out_cipher", vtype_stringptr, &tls_out.cipher },
+ { "tls_out_ocsp", vtype_int, &tls_out.ocsp },
{ "tls_out_ourcert", vtype_cert, &tls_out.ourcert },
{ "tls_out_peercert", vtype_cert, &tls_out.peercert },
{ "tls_out_peerdn", vtype_stringptr, &tls_out.peerdn },
diff --git a/src/src/globals.c b/src/src/globals.c
index 7b591e42a..af2903525 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -109,7 +109,8 @@ tls_support tls_in = {
NULL, /* tls_ourcert */
NULL, /* tls_peercert */
NULL, /* tls_peerdn */
- NULL /* tls_sni */
+ NULL, /* tls_sni */
+ 0 /* tls_ocsp */
};
tls_support tls_out = {
-1, /* tls_active */
@@ -121,7 +122,8 @@ tls_support tls_out = {
NULL, /* tls_ourcert */
NULL, /* tls_peercert */
NULL, /* tls_peerdn */
- NULL /* tls_sni */
+ NULL, /* tls_sni */
+ 0 /* tls_ocsp */
};
diff --git a/src/src/globals.h b/src/src/globals.h
index 584d1bd09..9a42fe27e 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -89,6 +89,12 @@ typedef struct {
void *peercert; /* Certificate of peer, binary */
uschar *peerdn; /* DN from peer */
uschar *sni; /* Server Name Indication */
+ enum {
+ OCSP_NOT_REQ=0, /* not requested */
+ OCSP_NOT_RESP, /* no response to request */
+ OCSP_NOT_VFY, /* response not verified */
+ OCSP_VFIED /* verified */
+ } ocsp; /* Stapled OCSP status */
} tls_support;
extern tls_support tls_in;
extern tls_support tls_out;
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index 6810d25e3..82a805a21 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -1820,6 +1820,7 @@ authenticated_by = NULL;
tls_in.cipher = tls_in.peerdn = NULL;
tls_in.ourcert = tls_in.peercert = NULL;
tls_in.sni = NULL;
+tls_in.ocsp = OCSP_NOT_REQ;
tls_advertised = FALSE;
#endif
diff --git a/src/src/spool_in.c b/src/src/spool_in.c
index 2006e1b02..ba775bbce 100644
--- a/src/src/spool_in.c
+++ b/src/src/spool_in.c
@@ -289,6 +289,7 @@ tls_in.ourcert = NULL;
tls_in.peercert = NULL;
tls_in.peerdn = NULL;
tls_in.sni = NULL;
+tls_in.ocsp = OCSP_NOT_REQ;
#endif
#ifdef WITH_CONTENT_SCAN
@@ -560,6 +561,8 @@ for (;;)
tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12));
else if (Ustrncmp(p, "ls_sni", 6) == 0)
tls_in.sni = string_unprinting(string_copy(big_buffer + 9));
+ else if (Ustrncmp(p, "ls_ocsp", 7) == 0)
+ tls_in.ocsp = big_buffer[10] - '0';
break;
#endif
diff --git a/src/src/spool_out.c b/src/src/spool_out.c
index 7bbd42df0..de81786b3 100644
--- a/src/src/spool_out.c
+++ b/src/src/spool_out.c
@@ -242,6 +242,7 @@ if (tls_in.ourcert)
(void) tls_export_cert(big_buffer, big_buffer_size, tls_in.ourcert);
fprintf(f, "-tls_ourcert %s\n", CS big_buffer);
}
+if (tls_in.ocsp) fprintf(f, "-tls_ocsp %d\n", tls_in.ocsp);
#endif
/* To complete the envelope, write out the tree of non-recipients, followed by
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index d0e1c35d7..b0b67d820 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -101,6 +101,7 @@ typedef struct exim_gnutls_state {
uschar *exp_tls_verify_certificates;
uschar *exp_tls_crl;
uschar *exp_tls_require_ciphers;
+ uschar *exp_tls_ocsp_file;
tls_support *tlsp; /* set in tls_init() */
@@ -115,7 +116,7 @@ static const exim_gnutls_state_st exim_gnutls_state_init = {
NULL, NULL, NULL, VERIFY_NONE, -1, -1, FALSE, FALSE, FALSE,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL,
NULL, 0, 0, 0, 0,
};
@@ -203,6 +204,10 @@ static void exim_gnutls_logger_cb(int level, const char *message);
static int exim_sni_handling_cb(gnutls_session_t session);
+#ifdef EXPERIMENTAL_OCSP
+static int server_ocsp_stapling_cb(gnutls_session_t session, void * ptr,
+ gnutls_datum_t * ocsp_response);
+#endif
@@ -791,18 +796,18 @@ if ( !host /* server */
&& tls_ocsp_file
)
{
- uschar * expanded;
- int rc;
-
- if (!expand_check(tls_ocsp_file, US"tls_ocsp_file", &expanded))
+ if (!expand_check(tls_ocsp_file, US"tls_ocsp_file",
+ &state->exp_tls_ocsp_file))
return DEFER;
- /* Lazy way; would like callback to emit debug on actual response */
+ /* Use the full callback method for stapling just to get observability.
+ More efficient would be to read the file once only, if it never changed
+ (due to SNI). Would need restart on file update, or watch datestamp. */
+
+ gnutls_certificate_set_ocsp_status_request_function(state->x509_cred,
+ server_ocsp_stapling_cb, state->exp_tls_ocsp_file);
- 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);
+ DEBUG(D_tls) debug_printf("Set OCSP response file %s\n", &state->exp_tls_ocsp_file);
}
#endif
@@ -1433,6 +1438,31 @@ return 0;
+#ifdef EXPERIMENTAL_OCSP
+
+static int
+server_ocsp_stapling_cb(gnutls_session_t session, void * ptr,
+ gnutls_datum_t * ocsp_response)
+{
+int ret;
+
+tls_in.ocsp = OCSP_NOT_RESP;
+if ((ret = gnutls_load_file(ptr, ocsp_response)) < 0)
+ {
+ DEBUG(D_tls) debug_printf("Failed to load ocsp stapling file %s\n",
+ (char *)ptr);
+ return GNUTLS_E_NO_CERTIFICATE_STATUS;
+ }
+
+tls_in.ocsp = OCSP_NOT_VFY;
+return 0;
+}
+
+#endif
+
+
+
+
/* ------------------------------------------------------------------------ */
/* Exported functions */
@@ -1526,8 +1556,8 @@ if (!state->tlsp->on_connect)
that the GnuTLS library doesn't. */
gnutls_transport_set_ptr2(state->session,
- (gnutls_transport_ptr)fileno(smtp_in),
- (gnutls_transport_ptr)fileno(smtp_out));
+ (gnutls_transport_ptr)(long) fileno(smtp_in),
+ (gnutls_transport_ptr)(long) fileno(smtp_out));
state->fd_in = fileno(smtp_in);
state->fd_out = fileno(smtp_out);
@@ -1628,6 +1658,9 @@ exim_gnutls_state_st *state = NULL;
#ifdef EXPERIMENTAL_OCSP
BOOL require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp,
NULL, host->name, host->address, NULL) == OK;
+BOOL request_ocsp = require_ocsp ? TRUE
+ : verify_check_this_host(&ob->hosts_request_ocsp,
+ NULL, host->name, host->address, NULL) == OK;
#endif
DEBUG(D_tls) debug_printf("initialising GnuTLS as a client on fd %d\n", fd);
@@ -1684,17 +1717,18 @@ else
}
#ifdef EXPERIMENTAL_OCSP /* since GnuTLS 3.1.3 */
-if (require_ocsp)
+if (request_ocsp)
{
DEBUG(D_tls) debug_printf("TLS: will request OCSP stapling\n");
if ((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);
+ tls_out.ocsp = OCSP_NOT_RESP;
}
#endif
-gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr)fd);
+gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr)(long) fd);
state->fd_in = fd;
state->fd_out = fd;
@@ -1746,6 +1780,7 @@ if (require_ocsp)
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");
+ tls_out.ocsp = OCSP_VFIED;
}
#endif
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 66fca7dbb..fd257f3c6 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -97,7 +97,8 @@ typedef struct tls_ext_ctx_cb {
OCSP_RESPONSE *response;
} server;
struct {
- X509_STORE *verify_store;
+ X509_STORE *verify_store; /* non-null if status requested */
+ BOOL verify_required;
} client;
} u_ocsp;
#endif
@@ -797,15 +798,18 @@ else
DEBUG(D_tls) debug_printf("Received TLS status request (OCSP stapling); %s response.",
cbinfo->u_ocsp.server.response ? "have" : "lack");
+tls_in.ocsp = OCSP_NOT_RESP;
if (!cbinfo->u_ocsp.server.response)
return SSL_TLSEXT_ERR_NOACK;
response_der = NULL;
-response_der_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response, &response_der);
+response_der_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response,
+ &response_der);
if (response_der_len <= 0)
return SSL_TLSEXT_ERR_NOACK;
SSL_set_tlsext_status_ocsp_resp(server_ssl, response_der, response_der_len);
+tls_in.ocsp = OCSP_VFIED;
return SSL_TLSEXT_ERR_OK;
}
@@ -832,12 +836,15 @@ DEBUG(D_tls) debug_printf("Received TLS status response (OCSP stapling):");
len = SSL_get_tlsext_status_ocsp_resp(s, &p);
if(!p)
{
- if (log_extra_selector & LX_tls_cipher)
- log_write(0, LOG_MAIN, "Received TLS status response, null content");
+ /* Expect this when we requested ocsp but got none */
+ if ( cbinfo->u_ocsp.client.verify_required
+ && log_extra_selector & LX_tls_cipher)
+ log_write(0, LOG_MAIN, "Received TLS status callback, null content");
else
DEBUG(D_tls) debug_printf(" null\n");
- return 0; /* This is the fail case for require-ocsp; none from server */
+ return cbinfo->u_ocsp.client.verify_required ? 0 : 1;
}
+tls_out.ocsp = OCSP_NOT_VFY;
if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
{
if (log_extra_selector & LX_tls_cipher)
@@ -878,11 +885,12 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
/* Use the chain that verified the server cert to verify the stapled info */
/* DEBUG(D_tls) x509_store_dump_cert_s_names(cbinfo->u_ocsp.client.verify_store); */
- if ((i = OCSP_basic_verify(bs, NULL, cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
+ if ((i = OCSP_basic_verify(bs, NULL,
+ cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
{
BIO_printf(bp, "OCSP response verify failure\n");
ERR_print_errors(bp);
- i = 0;
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
goto out;
}
@@ -894,39 +902,48 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
if (sk_OCSP_SINGLERESP_num(sresp) != 1)
{
- log_write(0, LOG_MAIN, "OCSP stapling with multiple responses not handled");
+ log_write(0, LOG_MAIN, "OCSP stapling "
+ "with multiple responses not handled");
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
goto out;
}
single = OCSP_resp_get0(bs, 0);
- status = OCSP_single_get0_status(single, &reason, &rev, &thisupd, &nextupd);
+ status = OCSP_single_get0_status(single, &reason, &rev,
+ &thisupd, &nextupd);
}
- i = 0;
DEBUG(D_tls) time_print(bp, "This OCSP Update", thisupd);
DEBUG(D_tls) if(nextupd) time_print(bp, "Next OCSP Update", nextupd);
- if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
+ if (!OCSP_check_validity(thisupd, nextupd,
+ EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
{
DEBUG(D_tls) ERR_print_errors(bp);
log_write(0, LOG_MAIN, "Server OSCP dates invalid");
- goto out;
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
}
-
- DEBUG(D_tls) BIO_printf(bp, "Certificate status: %s\n", OCSP_cert_status_str(status));
- switch(status)
+ else
{
- case V_OCSP_CERTSTATUS_GOOD:
- i = 1;
- break;
- case V_OCSP_CERTSTATUS_REVOKED:
- log_write(0, LOG_MAIN, "Server certificate revoked%s%s",
- reason != -1 ? "; reason: " : "", reason != -1 ? OCSP_crl_reason_str(reason) : "");
- DEBUG(D_tls) time_print(bp, "Revocation Time", rev);
- i = 0;
- break;
- default:
- log_write(0, LOG_MAIN, "Server certificate status unknown, in OCSP stapling");
- i = 0;
- break;
+ DEBUG(D_tls) BIO_printf(bp, "Certificate status: %s\n",
+ OCSP_cert_status_str(status));
+ switch(status)
+ {
+ case V_OCSP_CERTSTATUS_GOOD:
+ i = 1;
+ tls_out.ocsp = OCSP_VFIED;
+ break;
+ case V_OCSP_CERTSTATUS_REVOKED:
+ log_write(0, LOG_MAIN, "Server certificate revoked%s%s",
+ reason != -1 ? "; reason: " : "",
+ reason != -1 ? OCSP_crl_reason_str(reason) : "");
+ DEBUG(D_tls) time_print(bp, "Revocation Time", rev);
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+ break;
+ default:
+ log_write(0, LOG_MAIN,
+ "Server certificate status unknown, in OCSP stapling");
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+ break;
+ }
}
out:
BIO_free(bp);
@@ -1497,12 +1514,15 @@ static uschar cipherbuf[256];
#ifdef EXPERIMENTAL_OCSP
BOOL require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp,
NULL, host->name, host->address, NULL) == OK;
+BOOL request_ocsp = require_ocsp ? TRUE
+ : verify_check_this_host(&ob->hosts_request_ocsp,
+ NULL, host->name, host->address, NULL) == OK;
#endif
rc = tls_init(&client_ctx, host, NULL,
ob->tls_certificate, ob->tls_privatekey,
#ifdef EXPERIMENTAL_OCSP
- require_ocsp ? US"" : NULL,
+ (void *)(long)request_ocsp,
#endif
addr, &client_static_cbinfo);
if (rc != OK) return rc;
@@ -1578,8 +1598,12 @@ if (ob->tls_sni)
#ifdef EXPERIMENTAL_OCSP
/* Request certificate status at connection-time. If the server
does OCSP stapling we will get the callback (set in tls_init()) */
-if (require_ocsp)
+if (request_ocsp)
+ {
SSL_set_tlsext_status_type(client_ssl, TLSEXT_STATUSTYPE_ocsp);
+ client_static_cbinfo->u_ocsp.client.verify_required = require_ocsp;
+ tls_out.ocsp = OCSP_NOT_RESP;
+ }
#endif
/* There doesn't seem to be a built-in timeout on connection. */
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index 7223f9c89..9089d90c1 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -102,6 +102,10 @@ optionlist smtp_transport_options[] = {
(void *)offsetof(smtp_transport_options_block, hosts_override) },
{ "hosts_randomize", opt_bool,
(void *)offsetof(smtp_transport_options_block, hosts_randomize) },
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_OCSP)
+ { "hosts_request_ocsp", opt_stringptr,
+ (void *)offsetof(smtp_transport_options_block, hosts_request_ocsp) },
+#endif
{ "hosts_require_auth", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_require_auth) },
#ifdef SUPPORT_TLS
@@ -196,6 +200,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
NULL, /* hosts_try_prdr */
#endif
#ifdef EXPERIMENTAL_OCSP
+ US"*", /* hosts_request_ocsp */
NULL, /* hosts_require_ocsp */
#endif
NULL, /* hosts_require_tls */
diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h
index 6912ad83e..900542564 100644
--- a/src/src/transports/smtp.h
+++ b/src/src/transports/smtp.h
@@ -25,6 +25,7 @@ typedef struct {
uschar *hosts_try_prdr;
#endif
#ifdef EXPERIMENTAL_OCSP
+ uschar *hosts_request_ocsp;
uschar *hosts_require_ocsp;
#endif
uschar *hosts_require_tls;