summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2019-10-17 21:45:32 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2019-10-17 21:45:32 +0100
commit86ede124f0ce622b4f73e05504abc11fece021e3 (patch)
tree1ec851a5ca9a6ec6986bbe1aeb1f5f00a7933b09 /src
parent6f47da8d2d526953e8e6403f448d1598c9140df1 (diff)
OpenSSL: full-chain OCSP stapling. Bug 1466
Diffstat (limited to 'src')
-rw-r--r--src/src/environment.c1
-rw-r--r--src/src/tls-gnu.c11
-rw-r--r--src/src/tls-openssl.c150
-rw-r--r--src/src/tls.c2
4 files changed, 105 insertions, 59 deletions
diff --git a/src/src/environment.c b/src/src/environment.c
index cef82dfb1..9cb90c86f 100644
--- a/src/src/environment.c
+++ b/src/src/environment.c
@@ -67,7 +67,6 @@ if (add_environment)
uschar * p;
int sep = 0;
const uschar * envlist = add_environment;
- int old_pool = store_pool;
while ((p = string_nextinlist(&envlist, &sep, NULL, 0)))
{
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index deeb04253..03e704e39 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -2258,17 +2258,17 @@ post_handshake_debug(exim_gnutls_state_st * state)
#ifdef SUPPORT_GNUTLS_SESS_DESC
debug_printf("%s\n", gnutls_session_get_desc(state->session));
#endif
-#ifdef SUPPORT_GNUTLS_KEYLOG
+#ifdef SUPPORT_GNUTLS_KEYLOG
# ifdef EXIM_HAVE_TLS1_3
if (gnutls_protocol_get_version(state->session) < GNUTLS_TLS1_3)
-#else
+# else
if (TRUE)
-#endif
+# endif
{
gnutls_datum_t c, s;
gstring * gc, * gs;
- /* we only want the client random and the master secret */
+ /* For TLS1.2 we only want the client random and the master secret */
gnutls_session_get_random(state->session, &c, &s);
gnutls_session_get_master_secret(state->session, &s);
gc = ddump(&c);
@@ -2281,7 +2281,8 @@ else
" add SSLKEYLOGFILE to keep_environment in the exim config\n"
" run exim as root\n"
" if using sudo, add SSLKEYLOGFILE to env_keep in /etc/sudoers\n"
- " (works for TLS1.2 also, and saves cut-paste into file)\n");
+ " (works for TLS1.2 also, and saves cut-paste into file)"
+ " Trying to use add_environment for this will not work\n");
#endif
}
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 67a35d489..7a625a8ba 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -1206,12 +1206,13 @@ Arguments:
sctx the SSL_CTX* to update
cbinfo various parts of session state
filename the filename putatively holding an OCSP response
+ is_pem file is PEM format; otherwise is DER
*/
static void
ocsp_load_response(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo,
- const uschar * filename)
+ const uschar * filename, BOOL is_pem)
{
BIO * bio;
OCSP_RESPONSE * resp;
@@ -1222,7 +1223,8 @@ STACK_OF(X509) * sk;
unsigned long verify_flags;
int status, reason, i;
-DEBUG(D_tls) debug_printf("tls_ocsp_file '%s'\n", filename);
+DEBUG(D_tls)
+ debug_printf("tls_ocsp_file (%s) '%s'\n", is_pem ? "PEM" : "DER", filename);
if (!(bio = BIO_new_file(CS filename, "rb")))
{
@@ -1231,8 +1233,26 @@ if (!(bio = BIO_new_file(CS filename, "rb")))
return;
}
-resp = d2i_OCSP_RESPONSE_bio(bio, NULL);
+if (is_pem)
+ {
+ uschar * data, * freep;
+ char * dummy;
+ long len;
+ if (!PEM_read_bio(bio, &dummy, &dummy, &data, &len))
+ {
+ DEBUG(D_tls) debug_printf("Failed to read PEM file \"%s\"\n",
+ filename);
+ return;
+ }
+debug_printf("read pem file\n");
+ freep = data;
+ resp = d2i_OCSP_RESPONSE(NULL, CUSS &data, len);
+ OPENSSL_free(freep);
+ }
+else
+ resp = d2i_OCSP_RESPONSE_bio(bio, NULL);
BIO_free(bio);
+
if (!resp)
{
DEBUG(D_tls) debug_printf("Error reading OCSP response.\n");
@@ -1518,6 +1538,7 @@ else
const uschar * olist = cbinfo->u_ocsp.server.file;
int osep = 0;
uschar * ofile;
+ BOOL fmt_pem = FALSE;
if (olist)
if (!expand_check(olist, US"tls_ocsp_file", USS &olist, errstr))
@@ -1546,7 +1567,19 @@ else
#ifndef DISABLE_OCSP
if (olist)
if ((ofile = string_nextinlist(&olist, &osep, NULL, 0)))
- ocsp_load_response(sctx, cbinfo, ofile);
+ {
+ if (Ustrncmp(ofile, US"PEM ", 4) == 0)
+ {
+ fmt_pem = TRUE;
+ ofile += 4;
+ }
+ else if (Ustrncmp(ofile, US"DER ", 4) == 0)
+ {
+ fmt_pem = FALSE;
+ ofile += 4;
+ }
+ ocsp_load_response(sctx, cbinfo, ofile, fmt_pem);
+ }
else
DEBUG(D_tls) debug_printf("ran out of ocsp file list\n");
#endif
@@ -1808,7 +1841,7 @@ OCSP_RESPONSE * rsp;
OCSP_BASICRESP * bs;
int i;
-DEBUG(D_tls) debug_printf("Received TLS status response (OCSP stapling):");
+DEBUG(D_tls) debug_printf("Received TLS status response (OCSP stapling):\n");
len = SSL_get_tlsext_status_ocsp_resp(s, &p);
if(!p)
{
@@ -1850,8 +1883,9 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
*/
{
BIO * bp = NULL;
- int status, reason;
- ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+#ifndef EXIM_HAVE_OCSP_RESP_COUNT
+ STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
+#endif
DEBUG(D_tls) bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
@@ -1861,19 +1895,23 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
/* DEBUG(D_tls) x509_store_dump_cert_s_names(cbinfo->u_ocsp.client.verify_store); */
if ((i = OCSP_basic_verify(bs, cbinfo->verify_stack,
- cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
- {
- tls_out.ocsp = OCSP_FAILED;
- if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN,
- "Received TLS cert status response, itself unverifiable: %s",
- ERR_reason_error_string(ERR_peek_error()));
- BIO_printf(bp, "OCSP response verify failure\n");
- ERR_print_errors(bp);
- OCSP_RESPONSE_print(bp, rsp, 0);
- goto failed;
- }
+ cbinfo->u_ocsp.client.verify_store, OCSP_NOEXPLICIT)) <= 0)
+ if (ERR_peek_error())
+ {
+ tls_out.ocsp = OCSP_FAILED;
+ if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN,
+ "Received TLS cert status response, itself unverifiable: %s",
+ ERR_reason_error_string(ERR_peek_error()));
+ BIO_printf(bp, "OCSP response verify failure\n");
+ ERR_print_errors(bp);
+ OCSP_RESPONSE_print(bp, rsp, 0);
+ goto failed;
+ }
+ else
+ DEBUG(D_tls) debug_printf("no explicit trust for OCSP signing"
+ " in the root CA certificate; ignoring\n");
- BIO_printf(bp, "OCSP response well-formed and signed OK\n");
+ DEBUG(D_tls) debug_printf("OCSP response well-formed and signed OK\n");
/*XXX So we have a good stapled OCSP status. How do we know
it is for the cert of interest? OpenSSL 1.1.0 has a routine
@@ -1883,60 +1921,65 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
For now, carry on blindly accepting the resp. */
- {
- OCSP_SINGLERESP * single;
-
+ for (int idx =
#ifdef EXIM_HAVE_OCSP_RESP_COUNT
- if (OCSP_resp_count(bs) != 1)
+ OCSP_resp_count(bs) - 1;
#else
- STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
- if (sk_OCSP_SINGLERESP_num(sresp) != 1)
+ sk_OCSP_SINGLERESP_num(sresp) - 1;
#endif
- {
- tls_out.ocsp = OCSP_FAILED;
- log_write(0, LOG_MAIN, "OCSP stapling "
- "with multiple responses not handled");
- goto failed;
- }
- single = OCSP_resp_get0(bs, 0);
+ idx >= 0; idx--)
+ {
+ OCSP_SINGLERESP * single = OCSP_resp_get0(bs, idx);
+ int status, reason;
+ ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd;
+
+ /*XXX so I can see putting a loop in here to handle a rsp with >1 singleresp
+ - but what happens with a GnuTLS-style input?
+
+ we could do with a debug label for each singleresp
+ - it has a certID with a serialNumber, but I see no API to get that
+ */
status = OCSP_single_get0_status(single, &reason, &rev,
&thisupd, &nextupd);
- }
- 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))
- {
- tls_out.ocsp = OCSP_FAILED;
- DEBUG(D_tls) ERR_print_errors(bp);
- log_write(0, LOG_MAIN, "Server OSCP dates invalid");
- }
- else
- {
+ 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))
+ {
+ tls_out.ocsp = OCSP_FAILED;
+ DEBUG(D_tls) ERR_print_errors(bp);
+ log_write(0, LOG_MAIN, "Server OSCP dates invalid");
+ goto failed;
+ }
+
DEBUG(D_tls) BIO_printf(bp, "Certificate status: %s\n",
OCSP_cert_status_str(status));
switch(status)
{
case V_OCSP_CERTSTATUS_GOOD:
- tls_out.ocsp = OCSP_VFIED;
- i = 1;
- goto good;
+ continue; /* the idx loop */
case V_OCSP_CERTSTATUS_REVOKED:
- tls_out.ocsp = OCSP_FAILED;
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);
break;
default:
- tls_out.ocsp = OCSP_FAILED;
log_write(0, LOG_MAIN,
"Server certificate status unknown, in OCSP stapling");
break;
}
+
+ goto failed;
}
+
+ i = 1;
+ tls_out.ocsp = OCSP_VFIED;
+ goto good;
+
failed:
+ tls_out.ocsp = OCSP_FAILED;
i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
good:
BIO_free(bp);
@@ -3904,7 +3947,7 @@ BOOL
tls_openssl_options_parse(uschar *option_spec, long *results)
{
long result, item;
-uschar *end;
+uschar * exp, * end;
uschar keep_c;
BOOL adding, item_parsed;
@@ -3912,7 +3955,7 @@ BOOL adding, item_parsed;
result = SSL_OP_NO_TICKET;
/* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
- * from default because it increases BEAST susceptibility. */
+from default because it increases BEAST susceptibility. */
#ifdef SSL_OP_NO_SSLv2
result |= SSL_OP_NO_SSLv2;
#endif
@@ -3929,7 +3972,10 @@ if (!option_spec)
return TRUE;
}
-for (uschar * s = option_spec; *s; /**/)
+if (!expand_check(option_spec, US"openssl_options", &exp, &end))
+ return FALSE;
+
+for (uschar * s = exp; *s; /**/)
{
while (isspace(*s)) ++s;
if (*s == '\0')
diff --git a/src/src/tls.c b/src/src/tls.c
index 9c587e55d..a541a3c7a 100644
--- a/src/src/tls.c
+++ b/src/src/tls.c
@@ -399,7 +399,7 @@ if (path)
else if (Ustrncmp(path, spool_directory, Ustrlen(spool_directory)) != 0)
{
DEBUG(D_tls)
- debug_printf("removing env SSLKEYLOGFILE: not under spooldir\n");
+ debug_printf("removing env SSLKEYLOGFILE=%s: not under spooldir\n", path);
unsetenv("SSLKEYLOGFILE");
}
}