diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/src/functions.h | 8 | ||||
-rw-r--r-- | src/src/readconf.c | 5 | ||||
-rw-r--r-- | src/src/smtp_in.c | 24 | ||||
-rw-r--r-- | src/src/tls-gnu.c | 211 | ||||
-rw-r--r-- | src/src/tls-openssl.c | 191 | ||||
-rw-r--r-- | src/src/tls.c | 19 | ||||
-rw-r--r-- | src/src/transports/smtp.c | 18 | ||||
-rw-r--r-- | src/src/verify.c | 9 |
8 files changed, 253 insertions, 232 deletions
diff --git a/src/src/functions.h b/src/src/functions.h index 0fc69e4eb..a0ab39e17 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -45,11 +45,11 @@ extern uschar * tls_cert_fprt_sha1(void *); extern uschar * tls_cert_fprt_sha256(void *); extern int tls_client_start(int, host_item *, address_item *, - transport_instance * + transport_instance *, # ifdef EXPERIMENTAL_DANE - , dns_answer * + dns_answer *, # endif - ); + uschar **); extern void tls_close(BOOL, BOOL); extern int tls_export_cert(uschar *, size_t, void *); extern int tls_feof(void); @@ -59,7 +59,7 @@ extern int tls_getc(unsigned); extern void tls_get_cache(void); extern int tls_import_cert(const uschar *, void **); extern int tls_read(BOOL, uschar *, size_t); -extern int tls_server_start(const uschar *); +extern int tls_server_start(const uschar *, uschar **); extern BOOL tls_smtp_buffered(void); extern int tls_ungetc(int); extern int tls_write(BOOL, const uschar *, size_t); diff --git a/src/src/readconf.c b/src/src/readconf.c index 55bba4c05..7751b3607 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -3251,12 +3251,9 @@ if (pid == 0) exim_setugid(exim_uid, exim_gid, FALSE, US"calling tls_validate_require_cipher"); - errmsg = tls_validate_require_cipher(); - if (errmsg) - { + if ((errmsg = tls_validate_require_cipher())) log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "tls_require_ciphers invalid: %s", errmsg); - } fflush(NULL); _exit(0); } diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index bd83de045..6963e7da2 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2218,6 +2218,19 @@ return done - 2; /* Convert yield values */ +static BOOL +smtp_log_tls_fail(uschar * errstr) +{ +uschar * conn_info = smtp_get_connection_info(); + +if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5; +/* I'd like to get separated H= here, but too hard for now */ + +log_write(0, LOG_MAIN, "TLS error on %s %s", conn_info, errstr); +return FALSE; +} + + /************************************************* * Start an SMTP session * *************************************************/ @@ -2706,8 +2719,8 @@ if (check_proxy_protocol_host()) smtps port for use with older style SSL MTAs. */ #ifdef SUPPORT_TLS - if (tls_in.on_connect && tls_server_start(tls_require_ciphers) != OK) - return FALSE; + if (tls_in.on_connect && tls_server_start(tls_require_ciphers, &user_msg) != OK) + return smtp_log_tls_fail(user_msg); #endif /* Run the connect ACL if it exists */ @@ -5181,7 +5194,8 @@ while (done <= 0) We must allow for an extra EHLO command and an extra AUTH command after STARTTLS that don't add to the nonmail command count. */ - if ((rc = tls_server_start(tls_require_ciphers)) == OK) + s = NULL; + if ((rc = tls_server_start(tls_require_ciphers, &s)) == OK) { if (!tls_remember_esmtp) helo_seen = esmtp = auth_advertised = pipelining_advertised = FALSE; @@ -5210,11 +5224,13 @@ while (done <= 0) DEBUG(D_tls) debug_printf("TLS active\n"); break; /* Successful STARTTLS */ } + else + (void) smtp_log_tls_fail(s); /* Some local configuration problem was discovered before actually trying to do a TLS handshake; give a temporary error. */ - else if (rc == DEFER) + if (rc == DEFER) { smtp_printf("454 TLS currently unavailable\r\n"); break; diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 7b16fc811..c3f7241de 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -206,9 +206,12 @@ before, for now. */ #endif #define exim_gnutls_err_check(Label) do { \ - if (rc != GNUTLS_E_SUCCESS) { return tls_error((Label), gnutls_strerror(rc), host); } } while (0) + if (rc != GNUTLS_E_SUCCESS) \ + return tls_error((Label), gnutls_strerror(rc), host, errstr); \ + } while (0) -#define expand_check_tlsvar(Varname) expand_check(state->Varname, US #Varname, &state->exp_##Varname) +#define expand_check_tlsvar(Varname, errstr) \ + expand_check(state->Varname, US #Varname, &state->exp_##Varname, errstr) #if GNUTLS_VERSION_NUMBER >= 0x020c00 # define HAVE_GNUTLS_SESSION_CHANNEL_BINDING @@ -264,29 +267,18 @@ Argument: usually obtained from gnutls_strerror() host NULL if setting up a server; the connected host if setting up a client + errstr pointer to returned error string Returns: OK/DEFER/FAIL */ static int -tls_error(const uschar *prefix, const char *msg, const host_item *host) +tls_error(const uschar *prefix, const char *msg, const host_item *host, + uschar ** errstr) { -if (host) - { - log_write(0, LOG_MAIN, "H=%s [%s] TLS error on connection (%s)%s%s", - host->name, host->address, prefix, msg ? ": " : "", msg ? msg : ""); - return FAIL; - } -else - { - uschar *conn_info = smtp_get_connection_info(); - if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) - conn_info += 5; - /* I'd like to get separated H= here, but too hard for now */ - log_write(0, LOG_MAIN, "TLS error on %s (%s)%s%s", - conn_info, prefix, msg ? ": " : "", msg ? msg : ""); - return DEFER; - } +if (errstr) + *errstr = string_sprintf("(%s)%s%s", prefix, msg ? ": " : "", msg ? msg : ""); +return host ? FAIL : DEFER; } @@ -310,7 +302,8 @@ Returns: nothing static void record_io_error(exim_gnutls_state_st *state, int rc, uschar *when, uschar *text) { -const char *msg; +const char * msg; +uschar * errstr; if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) msg = CS string_sprintf("%s: %s", US gnutls_strerror(rc), @@ -318,7 +311,18 @@ if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) else msg = gnutls_strerror(rc); -tls_error(when, msg, state->host); +(void) tls_error(when, msg, state->host, &errstr); + +if (state->host) + log_write(0, LOG_MAIN, "H=%s [%s] TLS error on connection %s", + state->host->name, state->host->address, errstr); +else + { + uschar * conn_info = smtp_get_connection_info(); + if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5; + /* I'd like to get separated H= here, but too hard for now */ + log_write(0, LOG_MAIN, "TLS error on %s %s", conn_info, errstr); + } } @@ -454,7 +458,7 @@ Returns: OK/DEFER/FAIL */ static int -init_server_dh(void) +init_server_dh(uschar ** errstr) { int fd, rc; unsigned int dh_bits; @@ -475,7 +479,7 @@ exim_gnutls_err_check(US"gnutls_dh_params_init"); m.data = NULL; m.size = 0; -if (!expand_check(tls_dhparam, US"tls_dhparam", &exp_tls_dhparam)) +if (!expand_check(tls_dhparam, US"tls_dhparam", &exp_tls_dhparam, errstr)) return DEFER; if (!exp_tls_dhparam) @@ -494,7 +498,7 @@ else if (Ustrcmp(exp_tls_dhparam, "none") == 0) else if (exp_tls_dhparam[0] != '/') { if (!(m.data = US std_dh_prime_named(exp_tls_dhparam))) - return tls_error(US"No standard prime named", CS exp_tls_dhparam, NULL); + return tls_error(US"No standard prime named", CS exp_tls_dhparam, NULL, errstr); m.size = Ustrlen(m.data); } else @@ -516,7 +520,7 @@ if (m.data) different filename and ensure we have sufficient bits. */ dh_bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_NORMAL); if (!dh_bits) - return tls_error(US"gnutls_sec_param_to_pk_bits() failed", NULL, NULL); + return tls_error(US"gnutls_sec_param_to_pk_bits() failed", NULL, NULL, errstr); DEBUG(D_tls) debug_printf("GnuTLS tells us that for D-H PK, NORMAL is %d bits.\n", dh_bits); @@ -540,7 +544,7 @@ if (use_file_in_spool) { if (!string_format(filename_buf, sizeof(filename_buf), "%s/gnutls-params-%d", spool_directory, dh_bits)) - return tls_error(US"overlong filename", NULL, NULL); + return tls_error(US"overlong filename", NULL, NULL, errstr); filename = filename_buf; } @@ -557,33 +561,33 @@ if ((fd = Uopen(filename, O_RDONLY, 0)) >= 0) { saved_errno = errno; (void)close(fd); - return tls_error(US"TLS cache stat failed", strerror(saved_errno), NULL); + return tls_error(US"TLS cache stat failed", strerror(saved_errno), NULL, errstr); } if (!S_ISREG(statbuf.st_mode)) { (void)close(fd); - return tls_error(US"TLS cache not a file", NULL, NULL); + return tls_error(US"TLS cache not a file", NULL, NULL, errstr); } if (!(fp = fdopen(fd, "rb"))) { saved_errno = errno; (void)close(fd); return tls_error(US"fdopen(TLS cache stat fd) failed", - strerror(saved_errno), NULL); + strerror(saved_errno), NULL, errstr); } m.size = statbuf.st_size; if (!(m.data = malloc(m.size))) { fclose(fp); - return tls_error(US"malloc failed", strerror(errno), NULL); + return tls_error(US"malloc failed", strerror(errno), NULL, errstr); } if (!(sz = fread(m.data, m.size, 1, fp))) { saved_errno = errno; fclose(fp); free(m.data); - return tls_error(US"fread failed", strerror(saved_errno), NULL); + return tls_error(US"fread failed", strerror(saved_errno), NULL, errstr); } fclose(fp); @@ -604,7 +608,7 @@ else if (errno == ENOENT) } else return tls_error(string_open_failed(errno, "\"%s\" for reading", filename), - NULL, NULL); + NULL, NULL, errstr); /* If ret < 0, either the cache file does not exist, or the data it contains is not useful. One particular case of this is when upgrading from an older @@ -619,11 +623,11 @@ if (rc < 0) if ((PATH_MAX - Ustrlen(filename)) < 10) return tls_error(US"Filename too long to generate replacement", - CS filename, NULL); + CS filename, NULL, errstr); temp_fn = string_copy(US "%s.XXXXXXX"); if ((fd = mkstemp(CS temp_fn)) < 0) /* modifies temp_fn */ - return tls_error(US"Unable to open temp file", strerror(errno), NULL); + return tls_error(US"Unable to open temp file", strerror(errno), NULL, errstr); (void)fchown(fd, exim_uid, exim_gid); /* Probably not necessary */ /* GnuTLS overshoots! @@ -660,7 +664,7 @@ if (rc < 0) exim_gnutls_err_check(US"gnutls_dh_params_export_pkcs3(NULL) sizing"); m.size = sz; if (!(m.data = malloc(m.size))) - return tls_error(US"memory allocation failed", strerror(errno), NULL); + return tls_error(US"memory allocation failed", strerror(errno), NULL, errstr); /* this will return a size 1 less than the allocation size above */ rc = gnutls_dh_params_export_pkcs3(dh_server_params, GNUTLS_X509_FMT_PEM, @@ -676,19 +680,19 @@ if (rc < 0) { free(m.data); return tls_error(US"TLS cache write D-H params failed", - strerror(errno), NULL); + strerror(errno), NULL, errstr); } free(m.data); if ((sz = write_to_fd_buf(fd, US"\n", 1)) != 1) return tls_error(US"TLS cache write D-H params final newline failed", - strerror(errno), NULL); + strerror(errno), NULL, errstr); if ((rc = close(fd))) - return tls_error(US"TLS cache write close() failed", strerror(errno), NULL); + return tls_error(US"TLS cache write close() failed", strerror(errno), NULL, errstr); if (Urename(temp_fn, filename) < 0) return tls_error(string_sprintf("failed to rename \"%s\" as \"%s\"", - temp_fn, filename), strerror(errno), NULL); + temp_fn, filename), strerror(errno), NULL, errstr); DEBUG(D_tls) debug_printf("wrote D-H parameters to file \"%s\"\n", filename); } @@ -703,7 +707,7 @@ return OK; /* Create and install a selfsigned certificate, for use in server mode */ static int -tls_install_selfsign(exim_gnutls_state_st * state) +tls_install_selfsign(exim_gnutls_state_st * state, uschar ** errstr) { gnutls_x509_crt_t cert = NULL; time_t now; @@ -761,7 +765,7 @@ out: return rc; err: - rc = tls_error(where, gnutls_strerror(rc), NULL); + rc = tls_error(where, gnutls_strerror(rc), NULL, errstr); goto out; } @@ -782,12 +786,13 @@ which we are responsible for setting on the first pass through. Arguments: state exim_gnutls_state_st * + errstr error string pointer Returns: OK/DEFER/FAIL */ static int -tls_expand_session_files(exim_gnutls_state_st *state) +tls_expand_session_files(exim_gnutls_state_st *state, uschar ** errstr) { struct stat statbuf; int rc; @@ -831,7 +836,7 @@ false if expansion failed, unless expansion was forced to fail. */ /* check if we at least have a certificate, before doing expensive D-H generation. */ -if (!expand_check_tlsvar(tls_certificate)) +if (!expand_check_tlsvar(tls_certificate, errstr)) return DEFER; /* certificate is mandatory in server, optional in client */ @@ -840,11 +845,11 @@ if ( !state->exp_tls_certificate || !*state->exp_tls_certificate ) if (!host) - return tls_install_selfsign(state); + return tls_install_selfsign(state, errstr); else DEBUG(D_tls) debug_printf("TLS: no client certificate specified; okay\n"); -if (state->tls_privatekey && !expand_check_tlsvar(tls_privatekey)) +if (state->tls_privatekey && !expand_check_tlsvar(tls_privatekey, errstr)) return DEFER; /* tls_privatekey is optional, defaulting to same file as certificate */ @@ -897,7 +902,7 @@ if ( !host /* server */ else { if (!expand_check(tls_ocsp_file, US"tls_ocsp_file", - &state->exp_tls_ocsp_file)) + &state->exp_tls_ocsp_file, errstr)) return DEFER; /* Use the full callback method for stapling just to get observability. @@ -921,14 +926,14 @@ behaviour. */ if (state->tls_verify_certificates && *state->tls_verify_certificates) { - if (!expand_check_tlsvar(tls_verify_certificates)) + if (!expand_check_tlsvar(tls_verify_certificates, errstr)) return DEFER; #ifndef SUPPORT_SYSDEFAULT_CABUNDLE if (Ustrcmp(state->exp_tls_verify_certificates, "system") == 0) state->exp_tls_verify_certificates = NULL; #endif if (state->tls_crl && *state->tls_crl) - if (!expand_check_tlsvar(tls_crl)) + if (!expand_check_tlsvar(tls_crl, errstr)) return DEFER; if (!(state->exp_tls_verify_certificates && @@ -1041,12 +1046,13 @@ out to this. Arguments: state exim_gnutls_state_st * + errstr error string pointer Returns: OK/DEFER/FAIL */ static int -tls_set_remaining_x509(exim_gnutls_state_st *state) +tls_set_remaining_x509(exim_gnutls_state_st *state, uschar ** errstr) { int rc; const host_item *host = state->host; /* macro should be reconsidered? */ @@ -1059,7 +1065,7 @@ if (!state->host) { if (!dh_server_params) { - rc = init_server_dh(); + rc = init_server_dh(errstr); if (rc != OK) return rc; } gnutls_certificate_set_dh_params(state->x509_cred, dh_server_params); @@ -1121,6 +1127,7 @@ Arguments: crl CRL file require_ciphers tls_require_ciphers setting caller_state returned state-info structure + errstr error string pointer Returns: OK/DEFER/FAIL */ @@ -1134,7 +1141,8 @@ tls_init( const uschar *cas, const uschar *crl, const uschar *require_ciphers, - exim_gnutls_state_st **caller_state) + exim_gnutls_state_st **caller_state, + uschar ** errstr) { exim_gnutls_state_st *state; int rc; @@ -1212,19 +1220,17 @@ that's tls_certificate, tls_privatekey, tls_verify_certificates, tls_crl */ DEBUG(D_tls) debug_printf("Expanding various TLS configuration options for session credentials.\n"); -rc = tls_expand_session_files(state); -if (rc != OK) return rc; +if ((rc = tls_expand_session_files(state, errstr)) != OK) return rc; /* These are all other parts of the x509_cred handling, since SNI in GnuTLS requires a new structure afterwards. */ -rc = tls_set_remaining_x509(state); -if (rc != OK) return rc; +if ((rc = tls_set_remaining_x509(state, errstr)) != OK) return rc; /* set SNI in client, only */ if (host) { - if (!expand_check(sni, US"tls_out_sni", &state->tlsp->sni)) + if (!expand_check(sni, US"tls_out_sni", &state->tlsp->sni, errstr)) return DEFER; if (state->tlsp->sni && *state->tlsp->sni) { @@ -1250,7 +1256,7 @@ want_default_priorities = TRUE; if (state->tls_require_ciphers && *state->tls_require_ciphers) { - if (!expand_check_tlsvar(tls_require_ciphers)) + if (!expand_check_tlsvar(tls_require_ciphers, errstr)) return DEFER; if (state->exp_tls_require_ciphers && *state->exp_tls_require_ciphers) { @@ -1324,12 +1330,13 @@ don't apply. Arguments: state exim_gnutls_state_st * + errstr pointer to error string Returns: OK/DEFER/FAIL */ static int -peer_status(exim_gnutls_state_st *state) +peer_status(exim_gnutls_state_st *state, uschar ** errstr) { uschar cipherbuf[256]; const gnutls_datum_t *cert_list; @@ -1383,7 +1390,7 @@ if (cert_list == NULL || cert_list_size == 0) cert_list, cert_list_size); if (state->verify_requirement >= VERIFY_REQUIRED) return tls_error(US"certificate verification failed", - "no certificate received from peer", state->host); + "no certificate received from peer", state->host, errstr); return OK; } @@ -1395,7 +1402,7 @@ if (ct != GNUTLS_CRT_X509) debug_printf("TLS: peer cert not X.509 but instead \"%s\"\n", ctn); if (state->verify_requirement >= VERIFY_REQUIRED) return tls_error(US"certificate verification not possible, unhandled type", - ctn, state->host); + ctn, state->host, errstr); return OK; } @@ -1406,7 +1413,7 @@ if (ct != GNUTLS_CRT_X509) DEBUG(D_tls) debug_printf("TLS: peer cert problem: %s: %s\n", \ (Label), gnutls_strerror(rc)); \ if (state->verify_requirement >= VERIFY_REQUIRED) \ - return tls_error((Label), gnutls_strerror(rc), state->host); \ + return tls_error((Label), gnutls_strerror(rc), state->host, errstr); \ return OK; \ } \ } while (0) @@ -1447,7 +1454,7 @@ the peer information, but that's too new for some OSes. Arguments: state exim_gnutls_state_st * - error where to put an error message + errstr where to put an error message Returns: FALSE if the session should be rejected @@ -1455,17 +1462,17 @@ Returns: */ static BOOL -verify_certificate(exim_gnutls_state_st *state, const char **error) +verify_certificate(exim_gnutls_state_st *state, uschar ** errstr) { int rc; unsigned int verify; -*error = NULL; +*errstr = NULL; -if ((rc = peer_status(state)) != OK) +if ((rc = peer_status(state, errstr)) != OK) { verify = GNUTLS_CERT_INVALID; - *error = "certificate not supplied"; + *errstr = US"certificate not supplied"; } else rc = gnutls_certificate_verify_peers2(state->session, &verify); @@ -1478,13 +1485,13 @@ if (rc < 0 || ) { state->peer_cert_verified = FALSE; - if (!*error) - *error = verify & GNUTLS_CERT_REVOKED - ? "certificate revoked" : "certificate invalid"; + if (!*errstr) + *errstr = verify & GNUTLS_CERT_REVOKED + ? US"certificate revoked" : US"certificate invalid"; DEBUG(D_tls) debug_printf("TLS certificate verification failed (%s): peerdn=\"%s\"\n", - *error, state->peerdn ? state->peerdn : US"<unset>"); + *errstr, state->peerdn ? state->peerdn : US"<unset>"); if (state->verify_requirement >= VERIFY_REQUIRED) { @@ -1579,6 +1586,7 @@ size_t data_len = MAX_HOST_LEN; exim_gnutls_state_st *state = &state_server; unsigned int sni_type; int rc, old_pool; +uschar * dummy_errstr; rc = gnutls_server_name_get(session, sni_name, &data_len, &sni_type, 0); if (rc != GNUTLS_E_SUCCESS) @@ -1589,7 +1597,7 @@ if (rc != GNUTLS_E_SUCCESS) else debug_printf("TLS failure: gnutls_server_name_get(): %s [%d]\n", gnutls_strerror(rc), rc); - }; + } return 0; } @@ -1614,15 +1622,14 @@ DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", sni_name, if (!state->trigger_sni_changes) return 0; -rc = tls_expand_session_files(state); -if (rc != OK) +if ((rc = tls_expand_session_files(state, &dummy_errstr)) != OK) { /* If the setup of certs/etc failed before handshake, TLS would not have been offered. The best we can do now is abort. */ return GNUTLS_E_APPLICATION_ERROR_MIN; } -rc = tls_set_remaining_x509(state); +rc = tls_set_remaining_x509(state, &dummy_errstr); if (rc != OK) return GNUTLS_E_APPLICATION_ERROR_MIN; return 0; @@ -1720,6 +1727,7 @@ a TLS session. Arguments: require_ciphers list of allowed ciphers or NULL + errstr pointer to error string Returns: OK on success DEFER for errors before the start of the negotiation @@ -1728,16 +1736,15 @@ Returns: OK on success */ int -tls_server_start(const uschar *require_ciphers) +tls_server_start(const uschar * require_ciphers, uschar ** errstr) { int rc; -const char *error; -exim_gnutls_state_st *state = NULL; +exim_gnutls_state_st * state = NULL; /* Check for previous activation */ if (tls_in.active >= 0) { - tls_error(US"STARTTLS received after TLS started", "", NULL); + tls_error(US"STARTTLS received after TLS started", "", NULL, errstr); smtp_printf("554 Already in TLS\r\n"); return FAIL; } @@ -1747,10 +1754,9 @@ and sent an SMTP response. */ DEBUG(D_tls) debug_printf("initialising GnuTLS as a server\n"); -rc = tls_init(NULL, tls_certificate, tls_privatekey, +if ((rc = tls_init(NULL, tls_certificate, tls_privatekey, NULL, tls_verify_certificates, tls_crl, - require_ciphers, &state); -if (rc != OK) return rc; + require_ciphers, &state, errstr)) != OK) return rc; /* If this is a host for which certificate verification is mandatory or optional, set up appropriately. */ @@ -1828,12 +1834,12 @@ if (rc != GNUTLS_E_SUCCESS) if (sigalrm_seen) { - tls_error(US"gnutls_handshake", "timed out", NULL); + tls_error(US"gnutls_handshake", "timed out", NULL, errstr); gnutls_db_remove_session(state->session); } else { - tls_error(US"gnutls_handshake", gnutls_strerror(rc), NULL); + tls_error(US"gnutls_handshake", gnutls_strerror(rc), NULL, errstr); (void) gnutls_alert_send_appropriate(state->session, rc); gnutls_deinit(state->session); gnutls_certificate_free_credentials(state->x509_cred); @@ -1853,21 +1859,21 @@ DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n"); /* Verify after the fact */ if ( state->verify_requirement != VERIFY_NONE - && !verify_certificate(state, &error)) + && !verify_certificate(state, errstr)) { if (state->verify_requirement != VERIFY_OPTIONAL) { - tls_error(US"certificate verification failed", error, NULL); + (void) tls_error(US"certificate verification failed", *errstr, NULL, errstr); return FAIL; } DEBUG(D_tls) debug_printf("TLS: continuing on only because verification was optional, after: %s\n", - error); + *errstr); } /* Figure out peer DN, and if authenticated, etc. */ -if ((rc = peer_status(state)) != OK) return rc; +if ((rc = peer_status(state, NULL)) != OK) return rc; /* Sets various Exim expansion variables; always safe within server */ @@ -1922,6 +1928,8 @@ Arguments: addr the first address (not used) tb transport (always smtp) + errstr error string pointer + Returns: OK/DEFER/FAIL (because using common functions), but for a client, DEFER and FAIL have the same meaning */ @@ -1929,16 +1937,15 @@ Returns: OK/DEFER/FAIL (because using common functions), int tls_client_start(int fd, host_item *host, address_item *addr ARG_UNUSED, - transport_instance *tb + transport_instance * tb, #ifdef EXPERIMENTAL_DANE - , dns_answer * unused_tlsa_dnsa + dns_answer * tlsa_dnsa ARG_UNUSED, #endif - ) + uschar ** errstr) { smtp_transport_options_block *ob = (smtp_transport_options_block *)tb->options_block; int rc; -const char *error; exim_gnutls_state_st *state = NULL; #ifndef DISABLE_OCSP BOOL require_ocsp = @@ -1951,7 +1958,7 @@ DEBUG(D_tls) debug_printf("initialising GnuTLS as a client on fd %d\n", fd); 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) + ob->tls_require_ciphers, &state, errstr)) != OK) return rc; { @@ -2012,7 +2019,7 @@ if (request_ocsp) 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); + gnutls_strerror(rc), state->host, errstr); tls_out.ocsp = OCSP_NOT_RESP; } #endif @@ -2046,18 +2053,18 @@ if (rc != GNUTLS_E_SUCCESS) if (sigalrm_seen) { gnutls_alert_send(state->session, GNUTLS_AL_FATAL, GNUTLS_A_USER_CANCELED); - return tls_error(US"gnutls_handshake", "timed out", state->host); + return tls_error(US"gnutls_handshake", "timed out", state->host, errstr); } else - return tls_error(US"gnutls_handshake", gnutls_strerror(rc), state->host); + return tls_error(US"gnutls_handshake", gnutls_strerror(rc), state->host, errstr); DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n"); /* Verify late */ if (state->verify_requirement != VERIFY_NONE && - !verify_certificate(state, &error)) - return tls_error(US"certificate verification failed", error, state->host); + !verify_certificate(state, errstr)) + return tls_error(US"certificate verification failed", *errstr, state->host, errstr); #ifndef DISABLE_OCSP if (require_ocsp) @@ -2077,13 +2084,13 @@ if (require_ocsp) gnutls_free(printed.data); } else - (void) tls_error(US"ocsp decode", gnutls_strerror(rc), state->host); + (void) tls_error(US"ocsp decode", gnutls_strerror(rc), state->host, errstr); } 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); + return tls_error(US"certificate status check failed", NULL, state->host, errstr); } DEBUG(D_tls) debug_printf("Passed OCSP checking\n"); tls_out.ocsp = OCSP_VFIED; @@ -2092,7 +2099,7 @@ if (require_ocsp) /* Figure out peer DN, and if authenticated, etc. */ -if ((rc = peer_status(state)) != OK) +if ((rc = peer_status(state, errstr)) != OK) return rc; /* Sets various Exim expansion variables; may need to adjust for ACL callouts */ @@ -2437,6 +2444,7 @@ int rc; uschar *expciphers = NULL; gnutls_priority_t priority_cache; const char *errpos; +uschar * dummy_errstr; #define validate_check_rc(Label) do { \ if (rc != GNUTLS_E_SUCCESS) { if (exim_gnutls_base_init_done) gnutls_global_deinit(); \ @@ -2461,7 +2469,8 @@ exim_gnutls_base_init_done = TRUE; if (!(tls_require_ciphers && *tls_require_ciphers)) return_deinit(NULL); -if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers)) +if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers, + &dummy_errstr)) return_deinit(US"failed to expand tls_require_ciphers"); if (!(expciphers && *expciphers)) diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 392d59dd5..dd9affb65 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -180,7 +180,7 @@ tls_ext_ctx_cb *server_static_cbinfo = NULL; static int setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional, - int (*cert_vfy_cb)(int, X509_STORE_CTX *) ); + int (*cert_vfy_cb)(int, X509_STORE_CTX *), uschar ** errstr ); /* Callbacks */ #ifdef EXIM_HAVE_OPENSSL_TLSEXT @@ -207,35 +207,22 @@ Argument: host NULL if setting up a server; the connected host if setting up a client msg error message or NULL if we should ask OpenSSL + errstr pointer to output error message Returns: OK/DEFER/FAIL */ static int -tls_error(uschar * prefix, const host_item * host, uschar * msg) +tls_error(uschar * prefix, const host_item * host, uschar * msg, uschar ** errstr) { if (!msg) { ERR_error_string(ERR_get_error(), ssl_errstring); - msg = (uschar *)ssl_errstring; + msg = US ssl_errstring; } -if (host) - { - log_write(0, LOG_MAIN, "H=%s [%s] TLS error on connection (%s): %s", - host->name, host->address, prefix, msg); - return FAIL; - } -else - { - uschar *conn_info = smtp_get_connection_info(); - if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) - conn_info += 5; - /* I'd like to get separated H= here, but too hard for now */ - log_write(0, LOG_MAIN, "TLS error on %s (%s): %s", - conn_info, prefix, msg); - return DEFER; - } +if (errstr) *errstr = string_sprintf("(%s): %s", prefix, msg); +return host ? FAIL : DEFER; } @@ -596,19 +583,20 @@ Arguments: sctx The current SSL CTX (inbound or outbound) dhparam DH parameter file or fixed parameter identity string host connected host, if client; NULL if server + errstr error string pointer Returns: TRUE if OK (nothing to set up, or setup worked) */ static BOOL -init_dh(SSL_CTX *sctx, uschar *dhparam, const host_item *host) +init_dh(SSL_CTX *sctx, uschar *dhparam, const host_item *host, uschar ** errstr) { BIO *bio; DH *dh; uschar *dhexpanded; const char *pem; -if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded)) +if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded, errstr)) return FALSE; if (!dhexpanded || !*dhexpanded) @@ -618,7 +606,7 @@ else if (dhexpanded[0] == '/') if (!(bio = BIO_new_file(CS dhexpanded, "r"))) { tls_error(string_sprintf("could not read dhparams file %s", dhexpanded), - host, US strerror(errno)); + host, US strerror(errno), errstr); return FALSE; } } @@ -633,7 +621,7 @@ else if (!(pem = std_dh_prime_named(dhexpanded))) { tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded), - host, US strerror(errno)); + host, US strerror(errno), errstr); return FALSE; } bio = BIO_new_mem_buf(CS pem, -1); @@ -643,7 +631,7 @@ if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL))) { BIO_free(bio); tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded), - host, NULL); + host, NULL, errstr); return FALSE; } @@ -696,12 +684,13 @@ Patches welcome. Arguments: sctx The current SSL CTX (inbound or outbound) host connected host, if client; NULL if server + errstr error string pointer Returns: TRUE if OK (nothing to set up, or setup worked) */ static BOOL -init_ecdh(SSL_CTX * sctx, host_item * host) +init_ecdh(SSL_CTX * sctx, host_item * host, uschar ** errstr) { #ifdef OPENSSL_NO_ECDH return TRUE; @@ -721,7 +710,7 @@ DEBUG(D_tls) return TRUE; # else -if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve)) +if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve, errstr)) return FALSE; if (!exp_curve || !*exp_curve) return TRUE; @@ -760,15 +749,14 @@ if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef # endif ) { - tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", - exp_curve), - host, NULL); + tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve), + host, NULL, errstr); return FALSE; } if (!(ecdh = EC_KEY_new_by_curve_name(nid))) { - tls_error(US"Unable to create ec curve", host, NULL); + tls_error(US"Unable to create ec curve", host, NULL, errstr); return FALSE; } @@ -776,7 +764,7 @@ if (!(ecdh = EC_KEY_new_by_curve_name(nid))) not to the stability of the interface. */ if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0)) - tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), host, NULL); + tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), host, NULL, errstr); else DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve); @@ -951,7 +939,7 @@ return; /* Create and install a selfsigned certificate, for use in server mode */ static int -tls_install_selfsign(SSL_CTX * sctx) +tls_install_selfsign(SSL_CTX * sctx, uschar ** errstr) { X509 * x509 = NULL; EVP_PKEY * pkey; @@ -1006,7 +994,7 @@ if (!SSL_CTX_use_PrivateKey(sctx, pkey)) return OK; err: - (void) tls_error(where, NULL, NULL); + (void) tls_error(where, NULL, NULL, errstr); if (x509) X509_free(x509); if (pkey) EVP_PKEY_free(pkey); return DEFER; @@ -1026,12 +1014,14 @@ the certificate string. Arguments: sctx the SSL_CTX* to update cbinfo various parts of session state + errstr error string pointer Returns: OK/DEFER/FAIL */ static int -tls_expand_session_files(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo) +tls_expand_session_files(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo, + uschar ** errstr) { uschar *expanded; @@ -1040,7 +1030,7 @@ if (!cbinfo->certificate) if (cbinfo->host) /* client */ return OK; /* server */ - if (tls_install_selfsign(sctx) != OK) + if (tls_install_selfsign(sctx, errstr) != OK) return DEFER; } else @@ -1051,7 +1041,7 @@ else ) reexpand_tls_files_for_sni = TRUE; - if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded)) + if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded, errstr)) return DEFER; if (expanded != NULL) @@ -1060,11 +1050,11 @@ else if (!SSL_CTX_use_certificate_chain_file(sctx, CS expanded)) return tls_error(string_sprintf( "SSL_CTX_use_certificate_chain_file file=%s", expanded), - cbinfo->host, NULL); + cbinfo->host, NULL, errstr); } if (cbinfo->privatekey != NULL && - !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded)) + !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded, errstr)) return DEFER; /* If expansion was forced to fail, key_expanded will be NULL. If the result @@ -1076,14 +1066,14 @@ else DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded); if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM)) return tls_error(string_sprintf( - "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL); + "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL, errstr); } } #ifndef DISABLE_OCSP if (cbinfo->is_server && cbinfo->u_ocsp.server.file) { - if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded)) + if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded, errstr)) return DEFER; if (expanded && *expanded) @@ -1131,6 +1121,7 @@ const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg; int rc; int old_pool = store_pool; +uschar * dummy_errstr; if (!servername) return SSL_TLSEXT_ERR_OK; @@ -1167,8 +1158,8 @@ SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(server_ctx)); SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb); SSL_CTX_set_tlsext_servername_arg(server_sni, cbinfo); -if ( !init_dh(server_sni, cbinfo->dhparam, NULL) - || !init_ecdh(server_sni, NULL) +if ( !init_dh(server_sni, cbinfo->dhparam, NULL, &dummy_errstr) + || !init_ecdh(server_sni, NULL, &dummy_errstr) ) return SSL_TLSEXT_ERR_NOACK; @@ -1183,12 +1174,12 @@ if (cbinfo->u_ocsp.server.file) #endif if ((rc = setup_certs(server_sni, tls_verify_certificates, tls_crl, NULL, FALSE, - verify_callback_server)) != OK) + verify_callback_server, &dummy_errstr)) != OK) return SSL_TLSEXT_ERR_NOACK; /* do this after setup_certs, because this can require the certs for verifying OCSP information. */ -if ((rc = tls_expand_session_files(server_sni, cbinfo)) != OK) +if ((rc = tls_expand_session_files(server_sni, cbinfo, &dummy_errstr)) != OK) return SSL_TLSEXT_ERR_NOACK; DEBUG(D_tls) debug_printf("Switching SSL context.\n"); @@ -1414,6 +1405,7 @@ Arguments: ocsp_file file of stapling info (server); flag for require ocsp (client) addr address if client; NULL if server (for some randomness) cbp place to put allocated callback context + errstr error string pointer Returns: OK/DEFER/FAIL */ @@ -1424,7 +1416,7 @@ tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate, #ifndef DISABLE_OCSP uschar *ocsp_file, #endif - address_item *addr, tls_ext_ctx_cb ** cbp) + address_item *addr, tls_ext_ctx_cb ** cbp, uschar ** errstr) { long init_options; int rc; @@ -1471,7 +1463,7 @@ existing knob. */ *ctxp = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method()); -if (!*ctxp) return tls_error(US"SSL_CTX_new", host, NULL); +if (!*ctxp) return tls_error(US"SSL_CTX_new", host, NULL, errstr); /* It turns out that we need to seed the random number generator this early in order to get the full complement of ciphers to work. It took me roughly a day @@ -1493,7 +1485,7 @@ if (!RAND_status()) if (!RAND_status()) return tls_error(US"RAND_status", host, - US"unable to seed random number generator"); + US"unable to seed random number generator", errstr); } /* Set up the information callback, which outputs if debugging is at a suitable @@ -1515,14 +1507,14 @@ availability of the option value macros from OpenSSL. */ okay = tls_openssl_options_parse(openssl_options, &init_options); if (!okay) - return tls_error(US"openssl_options parsing failed", host, NULL); + return tls_error(US"openssl_options parsing failed", host, NULL, errstr); if (init_options) { DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options); if (!(SSL_CTX_set_options(*ctxp, init_options))) return tls_error(string_sprintf( - "SSL_CTX_set_option(%#lx)", init_options), host, NULL); + "SSL_CTX_set_option(%#lx)", init_options), host, NULL, errstr); } else DEBUG(D_tls) debug_printf("no SSL CTX options to set\n"); @@ -1530,14 +1522,14 @@ else /* Initialize with DH parameters if supplied */ /* Initialize ECDH temp key parameter selection */ -if ( !init_dh(*ctxp, dhparam, host) - || !init_ecdh(*ctxp, host) +if ( !init_dh(*ctxp, dhparam, host, errstr) + || !init_ecdh(*ctxp, host, errstr) ) return DEFER; /* Set up certificate and key (and perhaps OCSP info) */ -if ((rc = tls_expand_session_files(*ctxp, cbinfo)) != OK) +if ((rc = tls_expand_session_files(*ctxp, cbinfo, errstr)) != OK) return rc; /* If we need to handle SNI or OCSP, do so */ @@ -1694,17 +1686,18 @@ Arguments: optional TRUE if called from a server for a host in tls_try_verify_hosts; otherwise passed as FALSE cert_vfy_cb Callback function for certificate verification + errstr error string pointer Returns: OK/DEFER/FAIL */ static int setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional, - int (*cert_vfy_cb)(int, X509_STORE_CTX *) ) + int (*cert_vfy_cb)(int, X509_STORE_CTX *), uschar ** errstr) { uschar *expcerts, *expcrl; -if (!expand_check(certs, US"tls_verify_certificates", &expcerts)) +if (!expand_check(certs, US"tls_verify_certificates", &expcerts, errstr)) return DEFER; if (expcerts && *expcerts) @@ -1713,7 +1706,7 @@ if (expcerts && *expcerts) CA bundle. Then add the ones specified in the config, if any. */ if (!SSL_CTX_set_default_verify_paths(sctx)) - return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); + return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL, errstr); if (Ustrcmp(expcerts, "system") != 0) { @@ -1757,7 +1750,7 @@ if (expcerts && *expcerts) if ( (!file || statbuf.st_size > 0) && !SSL_CTX_load_verify_locations(sctx, CS file, CS dir)) - return tls_error(US"SSL_CTX_load_verify_locations", host, NULL); + return tls_error(US"SSL_CTX_load_verify_locations", host, NULL, errstr); /* Load the list of CAs for which we will accept certs, for sending to the client. This is only for the one-file tls_verify_certificates @@ -1795,7 +1788,7 @@ if (expcerts && *expcerts) OpenSSL will then handle the verify against CA certs and CRLs by itself in the verify callback." */ - if (!expand_check(crl, US"tls_crl", &expcrl)) return DEFER; + if (!expand_check(crl, US"tls_crl", &expcrl, errstr)) return DEFER; if (expcrl && *expcrl) { struct stat statbufcrl; @@ -1823,7 +1816,7 @@ if (expcerts && *expcerts) DEBUG(D_tls) debug_printf("SSL CRL value is a file %s\n", file); } if (X509_STORE_load_locations(cvstore, CS file, CS dir) == 0) - return tls_error(US"X509_STORE_load_locations", host, NULL); + return tls_error(US"X509_STORE_load_locations", host, NULL, errstr); /* setting the flags to check against the complete crl chain */ @@ -1856,6 +1849,7 @@ a TLS session. Arguments: require_ciphers allowed ciphers + errstr pointer to error message Returns: OK on success DEFER for errors before the start of the negotiation @@ -1864,11 +1858,11 @@ Returns: OK on success */ int -tls_server_start(const uschar *require_ciphers) +tls_server_start(const uschar * require_ciphers, uschar ** errstr) { int rc; -uschar *expciphers; -tls_ext_ctx_cb *cbinfo; +uschar * expciphers; +tls_ext_ctx_cb * cbinfo; static uschar peerdn[256]; static uschar cipherbuf[256]; @@ -1876,7 +1870,7 @@ static uschar cipherbuf[256]; if (tls_in.active >= 0) { - tls_error(US"STARTTLS received after TLS started", NULL, US""); + tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr); smtp_printf("554 Already in TLS\r\n"); return FAIL; } @@ -1888,11 +1882,11 @@ rc = tls_init(&server_ctx, NULL, tls_dhparam, tls_certificate, tls_privatekey, #ifndef DISABLE_OCSP tls_ocsp_file, #endif - NULL, &server_static_cbinfo); + NULL, &server_static_cbinfo, errstr); if (rc != OK) return rc; cbinfo = server_static_cbinfo; -if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers)) +if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers, errstr)) return FAIL; /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they @@ -1906,7 +1900,7 @@ if (expciphers) while (*s != 0) { if (*s == '_') *s = '-'; s++; } DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers); if (!SSL_CTX_set_cipher_list(server_ctx, CS expciphers)) - return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL); + return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL, errstr); cbinfo->server_cipher_list = expciphers; } @@ -1922,21 +1916,22 @@ server_verify_callback_called = FALSE; if (verify_check_host(&tls_verify_hosts) == OK) { rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL, - FALSE, verify_callback_server); + FALSE, verify_callback_server, errstr); if (rc != OK) return rc; server_verify_optional = FALSE; } else if (verify_check_host(&tls_try_verify_hosts) == OK) { rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL, - TRUE, verify_callback_server); + TRUE, verify_callback_server, errstr); if (rc != OK) return rc; server_verify_optional = TRUE; } /* Prepare for new connection */ -if (!(server_ssl = SSL_new(server_ctx))) return tls_error(US"SSL_new", NULL, NULL); +if (!(server_ssl = SSL_new(server_ctx))) + return tls_error(US"SSL_new", NULL, NULL, errstr); /* Warning: we used to SSL_clear(ssl) here, it was removed. * @@ -1980,10 +1975,7 @@ alarm(0); if (rc <= 0) { - tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL); - if (ERR_get_error() == 0) - log_write(0, LOG_MAIN, - "TLS client disconnected cleanly (rejected our certificate?)"); + (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr); return FAIL; } @@ -2035,8 +2027,8 @@ return OK; static int tls_client_basic_ctx_init(SSL_CTX * ctx, - host_item * host, smtp_transport_options_block * ob, tls_ext_ctx_cb * cbinfo - ) + host_item * host, smtp_transport_options_block * ob, tls_ext_ctx_cb * cbinfo, + uschar ** errstr) { int rc; /* stick to the old behaviour for compatibility if tls_verify_certificates is @@ -2055,7 +2047,8 @@ else return OK; if ((rc = setup_certs(ctx, ob->tls_verify_certificates, - ob->tls_crl, host, client_verify_optional, verify_callback_client)) != OK) + ob->tls_crl, host, client_verify_optional, verify_callback_client, + errstr)) != OK) return rc; if (verify_check_given_host(&ob->tls_verify_cert_hostnames, host) == OK) @@ -2075,7 +2068,7 @@ return OK; #ifdef EXPERIMENTAL_DANE static int -dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa) +dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa, uschar ** errstr) { dns_record * rr; dns_scan dnss; @@ -2083,7 +2076,7 @@ const char * hostnames[2] = { CS host->name, NULL }; int found = 0; if (DANESSL_init(ssl, NULL, hostnames) != 1) - return tls_error(US"hostnames load", host, NULL); + return tls_error(US"hostnames load", host, NULL, errstr); for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; @@ -2114,7 +2107,7 @@ for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); switch (DANESSL_add_tlsa(ssl, usage, selector, mdname, p, rr->size - 3)) { default: - return tls_error(US"tlsa load", host, NULL); + return tls_error(US"tlsa load", host, NULL, errstr); case 0: /* action not taken */ case 1: break; } @@ -2144,6 +2137,7 @@ Argument: addr the first address tb transport (always smtp) tlsa_dnsa tlsa lookup, if DANE, else null + errstr error string pointer Returns: OK on success FAIL otherwise - note that tls_error() will not give DEFER @@ -2152,11 +2146,11 @@ Returns: OK on success int tls_client_start(int fd, host_item *host, address_item *addr, - transport_instance *tb + transport_instance * tb, #ifdef EXPERIMENTAL_DANE - , dns_answer * tlsa_dnsa + dns_answer * tlsa_dnsa, #endif - ) + uschar ** errstr) { smtp_transport_options_block * ob = (smtp_transport_options_block *)tb->options_block; @@ -2207,27 +2201,27 @@ rc = tls_init(&client_ctx, host, NULL, #ifndef DISABLE_OCSP (void *)(long)request_ocsp, #endif - addr, &client_static_cbinfo); + addr, &client_static_cbinfo, errstr); if (rc != OK) return rc; tls_out.certificate_verified = FALSE; client_verify_callback_called = FALSE; if (!expand_check(ob->tls_require_ciphers, US"tls_require_ciphers", - &expciphers)) + &expciphers, errstr)) return FAIL; /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they are separated by underscores. So that I can use either form in my tests, and also for general convenience, we turn underscores into hyphens here. */ -if (expciphers != NULL) +if (expciphers) { uschar *s = expciphers; - while (*s != 0) { if (*s == '_') *s = '-'; s++; } + while (*s) { if (*s == '_') *s = '-'; s++; } DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers); if (!SSL_CTX_set_cipher_list(client_ctx, CS expciphers)) - return tls_error(US"SSL_CTX_set_cipher_list", host, NULL); + return tls_error(US"SSL_CTX_set_cipher_list", host, NULL, errstr); } #ifdef EXPERIMENTAL_DANE @@ -2238,29 +2232,29 @@ if (tlsa_dnsa) verify_callback_client_dane); if (!DANESSL_library_init()) - return tls_error(US"library init", host, NULL); + return tls_error(US"library init", host, NULL, errstr); if (DANESSL_CTX_init(client_ctx) <= 0) - return tls_error(US"context init", host, NULL); + return tls_error(US"context init", host, NULL, errstr); } else #endif - if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob, client_static_cbinfo)) - != OK) + if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob, + client_static_cbinfo, errstr)) != OK) return rc; if ((client_ssl = SSL_new(client_ctx)) == NULL) - return tls_error(US"SSL_new", host, NULL); + return tls_error(US"SSL_new", host, NULL, errstr); SSL_set_session_id_context(client_ssl, sid_ctx, Ustrlen(sid_ctx)); SSL_set_fd(client_ssl, fd); SSL_set_connect_state(client_ssl); if (ob->tls_sni) { - if (!expand_check(ob->tls_sni, US"tls_sni", &tls_out.sni)) + if (!expand_check(ob->tls_sni, US"tls_sni", &tls_out.sni, errstr)) return FAIL; - if (tls_out.sni == NULL) + if (!tls_out.sni) { DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n"); } @@ -2280,7 +2274,7 @@ if (ob->tls_sni) #ifdef EXPERIMENTAL_DANE if (tlsa_dnsa) - if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa)) != OK) + if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa, errstr)) != OK) return rc; #endif @@ -2330,7 +2324,8 @@ if (tlsa_dnsa) #endif if (rc <= 0) - return tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL); + return tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, + errstr); DEBUG(D_tls) debug_printf("SSL_connect succeeded\n"); @@ -2625,7 +2620,8 @@ EVP_add_digest(EVP_sha256()); if (!(tls_require_ciphers && *tls_require_ciphers)) return NULL; -if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers)) +if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers, + &err)) return US"failed to expand tls_require_ciphers"; if (!(expciphers && *expciphers)) @@ -2650,7 +2646,8 @@ DEBUG(D_tls) if (!SSL_CTX_set_cipher_list(ctx, CS expciphers)) { ERR_error_string(ERR_get_error(), ssl_errstring); - err = string_sprintf("SSL_CTX_set_cipher_list(%s) failed", expciphers); + err = string_sprintf("SSL_CTX_set_cipher_list(%s) failed: %s", + expciphers, ssl_errstring); } SSL_CTX_free(ctx); diff --git a/src/src/tls.c b/src/src/tls.c index f34cf6321..a5cb35bd9 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -64,17 +64,18 @@ Returns: TRUE if OK; result may still be NULL after forced failure */ static BOOL -expand_check(const uschar *s, const uschar *name, uschar **result) +expand_check(const uschar *s, const uschar *name, uschar **result, uschar ** errstr) { -if (s == NULL) *result = NULL; else +if (!s) + *result = NULL; +else if ( !(*result = expand_string(US s)) /* need to clean up const more */ + && !expand_string_forcedfail + ) { - *result = expand_string(US s); /* need to clean up const some more */ - if (*result == NULL && !expand_string_forcedfail) - { - log_write(0, LOG_MAIN|LOG_PANIC, "expansion of %s failed: %s", name, - expand_string_message); - return FALSE; - } + *errstr = US"Internal error"; + log_write(0, LOG_MAIN|LOG_PANIC, "expansion of %s failed: %s", name, + expand_string_message); + return FALSE; } return TRUE; } diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index c1f5ad45a..a4c036642 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1863,11 +1863,12 @@ if ( smtp_peer_options & PEER_OFFERED_TLS TLS_NEGOTIATE: { address_item * addr; - int rc = tls_client_start(sx->inblock.sock, sx->host, sx->addrlist, sx->tblock + uschar * errstr; + int rc = tls_client_start(sx->inblock.sock, sx->host, sx->addrlist, sx->tblock, # ifdef EXPERIMENTAL_DANE - , sx->dane ? &tlsa_dnsa : NULL + sx->dane ? &tlsa_dnsa : NULL, # endif - ); + &errstr); /* TLS negotiation failed; give an error. From outside, this function may be called again to try in clear on a new connection, if the options permit @@ -1877,12 +1878,12 @@ if ( smtp_peer_options & PEER_OFFERED_TLS { # ifdef EXPERIMENTAL_DANE if (sx->dane) log_write(0, LOG_MAIN, - "DANE attempt failed; no TLS connection to %s [%s]", - sx->host->name, sx->host->address); + "DANE attempt failed; TLS connection to %s [%s]: %s", + sx->host->name, sx->host->address, errstr); # endif errno = ERRNO_TLSFAILURE; - message = US"failure while setting up TLS session"; + message = string_sprintf("TLS session: %s", errstr); sx->send_quit = FALSE; goto TLS_FAILED; } @@ -4053,8 +4054,9 @@ for (cutoff_retry = 0; && verify_check_given_host(&ob->hosts_require_tls, host) != OK ) { - log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted " - "to %s [%s] (not in hosts_require_tls)", host->name, host->address); + log_write(0, LOG_MAIN, + "%s: delivering unencrypted to H=%s [%s] (not in hosts_require_tls)", + first_addr->message, host->name, host->address); first_addr = prepare_addresses(addrlist, host); rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock, &message_defer, TRUE); diff --git a/src/src/verify.c b/src/src/verify.c index 9c4776b31..a152a8f24 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -690,9 +690,9 @@ tls_retry_connection: && verify_check_given_host(&ob->hosts_require_tls, host) != OK ) { - log_write(0, LOG_MAIN, "TLS session failure:" - " callout unencrypted to %s [%s] (not in hosts_require_tls)", - host->name, host->address); + log_write(0, LOG_MAIN, + "%s: callout unencrypted to %s [%s] (not in hosts_require_tls)", + addr->message, host->name, host->address); addr->transport_return = PENDING_DEFER; yield = smtp_setup_conn(&sx, TRUE); } @@ -730,8 +730,7 @@ tls_retry_connection: sx.send_rset = TRUE; sx.completed_addr = FALSE; - new_domain_record.result = - old_domain_cache_result == ccache_reject_mfnull + new_domain_record.result = old_domain_cache_result == ccache_reject_mfnull ? ccache_reject_mfnull : ccache_accept; /* Do the random local part check first. Temporarily replace the recipient |