X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/9d1c15ef45fcc8809349378922de20ae9a774c75..9aa512a1898155484e00ee089057d28f2432b30e:/src/src/tls-gnu.c diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 880aaeb14..3c926c0d4 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. */ - 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); + gnutls_certificate_set_ocsp_status_request_function(state->x509_cred, + server_ocsp_stapling_cb, state->exp_tls_ocsp_file); + + 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; + +if ((ret = gnutls_load_file(ptr, ocsp_response)) < 0) + { + DEBUG(D_tls) debug_printf("Failed to load ocsp stapling file %s\n", + (char *)ptr); + tls_in.ocsp = OCSP_NOT_RESP; + return GNUTLS_E_NO_CERTIFICATE_STATUS; + } + +tls_in.ocsp = OCSP_VFY_NOT_TRIED; +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); @@ -1610,17 +1640,7 @@ Arguments: fd the fd of the connection host connected host (for messages) addr the first address (not used) - certificate certificate file - privatekey private key file - sni TLS SNI to send to remote host - 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 + ob smtp transport options Returns: OK/DEFER/FAIL (because using common functions), but for a client, DEFER and FAIL have the same meaning @@ -1629,58 +1649,61 @@ Returns: OK/DEFER/FAIL (because using common functions), int tls_client_start(int fd, host_item *host, address_item *addr ARG_UNUSED, - uschar *certificate, uschar *privatekey, uschar *sni, - uschar *verify_certs, uschar *verify_crl, - uschar *require_ciphers, -#ifdef EXPERIMENTAL_OCSP - uschar *hosts_require_ocsp, -#endif - int dh_min_bits, int timeout, - uschar *verify_hosts, uschar *try_verify_hosts) + void *v_ob) { +smtp_transport_options_block *ob = v_ob; int rc; const char *error; exim_gnutls_state_st *state = NULL; #ifdef EXPERIMENTAL_OCSP -BOOL require_ocsp = verify_check_this_host(&hosts_require_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); -if ((rc = tls_init(host, certificate, privatekey, - sni, verify_certs, verify_crl, require_ciphers, &state)) != OK) +if ((rc = tls_init(host, ob->tls_certificate, ob->tls_privatekey, + ob->tls_sni, ob->tls_verify_certificates, ob->tls_crl, + ob->tls_require_ciphers, &state)) != OK) return rc; -if (dh_min_bits < EXIM_CLIENT_DH_MIN_MIN_BITS) { - DEBUG(D_tls) - debug_printf("WARNING: tls_dh_min_bits far too low, clamping %d up to %d\n", - dh_min_bits, EXIM_CLIENT_DH_MIN_MIN_BITS); - dh_min_bits = EXIM_CLIENT_DH_MIN_MIN_BITS; - } + int dh_min_bits = ob->tls_dh_min_bits; + if (dh_min_bits < EXIM_CLIENT_DH_MIN_MIN_BITS) + { + DEBUG(D_tls) + debug_printf("WARNING: tls_dh_min_bits far too low," + " clamping %d up to %d\n", + dh_min_bits, EXIM_CLIENT_DH_MIN_MIN_BITS); + dh_min_bits = EXIM_CLIENT_DH_MIN_MIN_BITS; + } -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); + 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 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 + && !ob->tls_verify_hosts + && !ob->tls_try_verify_hosts ) || - verify_check_host(&verify_hosts) == OK + verify_check_host(&ob->tls_verify_hosts) == OK ) { DEBUG(D_tls) debug_printf("TLS: server certificate verification required.\n"); state->verify_requirement = VERIFY_REQUIRED; gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_REQUIRE); } -else if (verify_check_host(&try_verify_hosts) == OK) +else if (verify_check_host(&ob->tls_try_verify_hosts) == OK) { DEBUG(D_tls) debug_printf("TLS: server certificate verification optional.\n"); state->verify_requirement = VERIFY_OPTIONAL; @@ -1694,18 +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"); - rc = gnutls_ocsp_status_request_enable_client(state->session, - NULL, 0, NULL); - if (rc != OK) + 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; @@ -1713,7 +1736,7 @@ DEBUG(D_tls) debug_printf("about to gnutls_handshake\n"); /* There doesn't seem to be a built-in timeout on connection. */ sigalrm_seen = FALSE; -alarm(timeout); +alarm(ob->command_timeout); do { rc = gnutls_handshake(state->session); @@ -1747,17 +1770,20 @@ if (require_ocsp) && (rc= gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &printed)) == 0 ) { - fprintf(stderr, "%.4096s", printed.data); + debug_printf("%.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) + { + tls_out.ocsp = OCSP_FAILED; 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