diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/src/expand.c | 2 | ||||
-rw-r--r-- | src/src/globals.c | 6 | ||||
-rw-r--r-- | src/src/globals.h | 6 | ||||
-rw-r--r-- | src/src/smtp_in.c | 1 | ||||
-rw-r--r-- | src/src/spool_in.c | 3 | ||||
-rw-r--r-- | src/src/spool_out.c | 1 | ||||
-rw-r--r-- | src/src/tls-gnu.c | 63 | ||||
-rw-r--r-- | src/src/tls-openssl.c | 84 | ||||
-rw-r--r-- | src/src/transports/smtp.c | 5 | ||||
-rw-r--r-- | src/src/transports/smtp.h | 1 |
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; |