TLS connection attempt, so that the normal retry-in-clear can work (if
suitably configured).
-JB/01 Big 2375: fix expansions of 822 addresses having comments in local-part
+JB/01 Bug 2375: fix expansions of 822 addresses having comments in local-part
and/or domain. Found and fixed by Jason Betts.
case EITEM_READSOCK:
{
- int fd;
+ client_conn_ctx cctx;
int timeout = 5;
int save_ptr = yield->ptr;
FILE * fp;
host_item host;
BOOL do_shutdown = TRUE;
BOOL do_tls = FALSE; /* Only set under SUPPORT_TLS */
- void * tls_ctx = NULL; /* ditto */
blob reqstr;
if (expand_forbid & RDO_READSOCK)
}
/*XXX we trust that the request is idempotent. Hmm. */
- fd = ip_connectedsocket(SOCK_STREAM, server_name, port, port,
+ cctx.sock = ip_connectedsocket(SOCK_STREAM, server_name, port, port,
timeout, &host, &expand_string_message,
do_tls ? NULL : &reqstr);
callout_address = NULL;
- if (fd < 0)
+ if (cctx.sock < 0)
goto SOCK_FAIL;
if (!do_tls)
reqstr.len = 0;
struct sockaddr_un sockun; /* don't call this "sun" ! */
int rc;
- if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
+ if ((cctx.sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
{
expand_string_message = string_sprintf("failed to create socket: %s",
strerror(errno));
sigalrm_seen = FALSE;
ALARM(timeout);
- rc = connect(fd, (struct sockaddr *)(&sockun), sizeof(sockun));
+ rc = connect(cctx.sock, (struct sockaddr *)(&sockun), sizeof(sockun));
ALARM_CLR(0);
if (sigalrm_seen)
{
#ifdef SUPPORT_TLS
if (do_tls)
{
+ smtp_connect_args conn_args = {.host = &host };
tls_support tls_dummy = {.sni=NULL};
uschar * errstr;
- if (!(tls_ctx = tls_client_start(fd, &host, NULL, NULL,
-# ifdef SUPPORT_DANE
- NULL,
-# endif
- &tls_dummy, &errstr)))
+ if (!tls_client_start(&cctx, &conn_args, NULL, &tls_dummy, &errstr))
{
expand_string_message = string_sprintf("TLS connect failed: %s", errstr);
goto SOCK_FAIL;
reqstr.data);
if ( (
#ifdef SUPPORT_TLS
- tls_ctx ? tls_write(tls_ctx, reqstr.data, reqstr.len, FALSE) :
+ cctx.tls_ctx ? tls_write(cctx.tls_ctx, reqstr.data, reqstr.len, FALSE) :
#endif
- write(fd, reqstr.data, reqstr.len)) != reqstr.len)
+ write(cctx.sock, reqstr.data, reqstr.len)) != reqstr.len)
{
expand_string_message = string_sprintf("request write to socket "
"failed: %s", strerror(errno));
system doesn't have this function, make it conditional. */
#ifdef SHUT_WR
- if (!tls_ctx && do_shutdown) shutdown(fd, SHUT_WR);
+ if (!cctx.tls_ctx && do_shutdown) shutdown(cctx.sock, SHUT_WR);
#endif
if (f.running_in_test_harness) millisleep(100);
/* Now we need to read from the socket, under a timeout. The function
that reads a file can be used. */
- if (!tls_ctx)
- fp = fdopen(fd, "rb");
+ if (!cctx.tls_ctx)
+ fp = fdopen(cctx.sock, "rb");
sigalrm_seen = FALSE;
ALARM(timeout);
yield =
#ifdef SUPPORT_TLS
- tls_ctx ? cat_file_tls(tls_ctx, yield, sub_arg[3]) :
+ cctx.tls_ctx ? cat_file_tls(cctx.tls_ctx, yield, sub_arg[3]) :
#endif
cat_file(fp, yield, sub_arg[3]);
ALARM_CLR(0);
#ifdef SUPPORT_TLS
- if (tls_ctx)
+ if (cctx.tls_ctx)
{
- tls_close(tls_ctx, TRUE);
- close(fd);
+ tls_close(cctx.tls_ctx, TRUE);
+ close(cctx.sock);
}
else
#endif
extern uschar * tls_cert_fprt_sha1(void *);
extern uschar * tls_cert_fprt_sha256(void *);
-extern void * tls_client_start(int, host_item *, address_item *,
- transport_instance *,
-# ifdef SUPPORT_DANE
- dns_answer *,
-# endif
- tls_support *, uschar **);
+extern BOOL tls_client_start(client_conn_ctx *, smtp_connect_args *,
+ void *, tls_support *, uschar **);
+
extern void tls_close(void *, int);
extern BOOL tls_could_read(void);
extern int tls_export_cert(uschar *, size_t, void *);
host_item * host;
int host_af;
uschar * interface;
+
+#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
+ BOOL dane:1; /* connection must do dane */
+ dns_answer tlsa_dnsa;
+#endif
} smtp_connect_args;
/* A client-initiated connection. If TLS, the second element is non-NULL */
/* Called from the smtp transport after STARTTLS has been accepted.
Arguments:
- fd the fd of the connection
- host connected host (for messages and option-tests)
- addr the first address (not used)
- tb transport (always smtp)
- tlsa_dnsa non-NULL, either request or require dane for this host, and
- a TLSA record found. Therefore, dane verify required.
- Which implies cert must be requested and supplied, dane
- verify must pass, and cert verify irrelevant (incl.
- hostnames), and (caller handled) require_tls
- tlsp record details of channel configuration
- errstr error string pointer
-
-Returns: Pointer to TLS session context, or NULL on error
+ cctx connection context
+ conn_args connection details
+ cookie datum for randomness (not used)
+ tlsp record details of channel configuration here; must be non-NULL
+ errstr error string pointer
+
+Returns: TRUE for success with TLS session context set in smtp context,
+ FALSE on error
*/
-void *
-tls_client_start(int fd, host_item *host,
- address_item *addr ARG_UNUSED,
- transport_instance * tb,
-#ifdef SUPPORT_DANE
- dns_answer * tlsa_dnsa,
-#endif
- tls_support * tlsp, uschar ** errstr)
+BOOL
+tls_client_start(client_conn_ctx * cctx, smtp_connect_args * conn_args,
+ void * cookie ARG_UNUSED,
+ tls_support * tlsp, uschar ** errstr)
{
-smtp_transport_options_block *ob = tb
+host_item * host = conn_args->host; /* for msgs and option-tests */
+transport_instance * tb = conn_args->tblock; /* always smtp or NULL */
+smtp_transport_options_block * ob = tb
? (smtp_transport_options_block *)tb->options_block
: &smtp_transport_option_defaults;
int rc;
exim_gnutls_state_st * state = NULL;
-uschar *cipher_list = NULL;
+uschar * cipher_list = NULL;
#ifndef DISABLE_OCSP
BOOL require_ocsp =
: verify_check_given_host(CUSS &ob->hosts_request_ocsp, host) == OK;
#endif
-DEBUG(D_tls) debug_printf("initialising GnuTLS as a client on fd %d\n", fd);
+DEBUG(D_tls) debug_printf("initialising GnuTLS as a client on fd %d\n", cctx->sock);
#ifdef SUPPORT_DANE
-if (tlsa_dnsa && ob->dane_require_tls_ciphers)
+/* If dane is flagged, have either request or require dane for this host, and
+a TLSA record found. Therefore, dane verify required. Which implies cert must
+be requested and supplied, dane verify must pass, and cert verify irrelevant
+(incl. hostnames), and (caller handled) require_tls */
+
+if (conn_args->dane && ob->dane_require_tls_ciphers)
{
/* not using expand_check_tlsvar because not yet in state */
if (!expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers",
&cipher_list, errstr))
- return NULL;
+ return FALSE;
cipher_list = cipher_list && *cipher_list
? ob->dane_require_tls_ciphers : ob->tls_require_ciphers;
}
if (tls_init(host, ob->tls_certificate, ob->tls_privatekey,
ob->tls_sni, ob->tls_verify_certificates, ob->tls_crl,
cipher_list, &state, tlsp, errstr) != OK)
- return NULL;
+ return FALSE;
{
int dh_min_bits = ob->tls_dh_min_bits;
the specified host patterns if one of them is defined */
#ifdef SUPPORT_DANE
-if (tlsa_dnsa && dane_tlsa_load(state, tlsa_dnsa))
+if (conn_args->dane && dane_tlsa_load(state, &conn_args->tlsa_dnsa))
{
DEBUG(D_tls)
debug_printf("TLS: server certificate DANE required.\n");
NULL, 0, NULL)) != OK)
{
tls_error(US"cert-status-req", US gnutls_strerror(rc), state->host, errstr);
- return NULL;
+ return FALSE;
}
tlsp->ocsp = OCSP_NOT_RESP;
}
}
#endif
-gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr_t)(long) fd);
-state->fd_in = fd;
-state->fd_out = fd;
+gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr_t)(long) cctx->sock);
+state->fd_in = cctx->sock;
+state->fd_out = cctx->sock;
DEBUG(D_tls) debug_printf("about to gnutls_handshake\n");
/* There doesn't seem to be a built-in timeout on connection. */
}
else
tls_error(US"gnutls_handshake", US gnutls_strerror(rc), state->host, errstr);
- return NULL;
+ return FALSE;
}
DEBUG(D_tls)
if (!verify_certificate(state, errstr))
{
tls_error(US"certificate verification failed", *errstr, state->host, errstr);
- return NULL;
+ return FALSE;
}
#ifndef DISABLE_OCSP
{
tlsp->ocsp = OCSP_FAILED;
tls_error(US"certificate status check failed", NULL, state->host, errstr);
- return NULL;
+ return FALSE;
}
DEBUG(D_tls) debug_printf("Passed OCSP checking\n");
tlsp->ocsp = OCSP_VFIED;
/* Figure out peer DN, and if authenticated, etc. */
if (peer_status(state, errstr) != OK)
- return NULL;
+ return FALSE;
/* Sets various Exim expansion variables; may need to adjust for ACL callouts */
extract_exim_vars_from_tls_state(state);
-return state;
+cctx->tls_ctx = state;
+return TRUE;
}
/* Called from the smtp transport after STARTTLS has been accepted.
-Argument:
- fd the fd of the connection
- host connected host (for messages and option-tests)
- addr the first address (for some randomness; can be NULL)
- tb transport (always smtp)
- tlsa_dnsa tlsa lookup, if DANE, else null
- tlsp record details of channel configuration here; must be non-NULL
- errstr error string pointer
-
-Returns: Pointer to TLS session context, or NULL on error
+Arguments:
+ cctx connection context
+ conn_args connection details
+ cookie datum for randomness; can be NULL
+ tlsp record details of TLS channel configuration here; must be non-NULL
+ errstr error string pointer
+
+Returns: TRUE for success with TLS session context set in connection context,
+ FALSE on error
*/
-void *
-tls_client_start(int fd, host_item *host, address_item *addr,
- transport_instance * tb,
-#ifdef SUPPORT_DANE
- dns_answer * tlsa_dnsa,
-#endif
- tls_support * tlsp, uschar ** errstr)
+BOOL
+tls_client_start(client_conn_ctx * cctx, smtp_connect_args * conn_args,
+ void * cookie, tls_support * tlsp, uschar ** errstr)
{
+host_item * host = conn_args->host; /* for msgs and option-tests */
+transport_instance * tb = conn_args->tblock; /* always smtp or NULL */
smtp_transport_options_block * ob = tb
? (smtp_transport_options_block *)tb->options_block
: &smtp_transport_option_defaults;
exim_openssl_client_tls_ctx * exim_client_ctx;
-static uschar peerdn[256];
uschar * expciphers;
int rc;
+static uschar peerdn[256];
#ifndef DISABLE_OCSP
BOOL request_ocsp = FALSE;
#ifndef DISABLE_OCSP
{
# ifdef SUPPORT_DANE
- if ( tlsa_dnsa
+ if ( conn_args->dane
&& ob->hosts_request_ocsp[0] == '*'
&& ob->hosts_request_ocsp[1] == '\0'
)
#ifndef DISABLE_OCSP
(void *)(long)request_ocsp,
#endif
- addr, &client_static_cbinfo, errstr);
-if (rc != OK) return NULL;
+ cookie, &client_static_cbinfo, errstr);
+if (rc != OK) return FALSE;
tlsp->certificate_verified = FALSE;
client_verify_callback_called = FALSE;
expciphers = NULL;
#ifdef SUPPORT_DANE
-if (tlsa_dnsa)
+if (conn_args->dane)
{
/* We fall back to tls_require_ciphers if unset, empty or forced failure, but
other failures should be treated as problems. */
if (ob->dane_require_tls_ciphers &&
!expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers",
&expciphers, errstr))
- return NULL;
+ return FALSE;
if (expciphers && *expciphers == '\0')
expciphers = NULL;
}
if (!expciphers &&
!expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
&expciphers, errstr))
- return NULL;
+ return FALSE;
/* 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
if (!SSL_CTX_set_cipher_list(exim_client_ctx->ctx, CS expciphers))
{
tls_error(US"SSL_CTX_set_cipher_list", host, NULL, errstr);
- return NULL;
+ return FALSE;
}
}
#ifdef SUPPORT_DANE
-if (tlsa_dnsa)
+if (conn_args->dane)
{
SSL_CTX_set_verify(exim_client_ctx->ctx,
SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
if (!DANESSL_library_init())
{
tls_error(US"library init", host, NULL, errstr);
- return NULL;
+ return FALSE;
}
if (DANESSL_CTX_init(exim_client_ctx->ctx) <= 0)
{
tls_error(US"context init", host, NULL, errstr);
- return NULL;
+ return FALSE;
}
}
else
if (tls_client_basic_ctx_init(exim_client_ctx->ctx, host, ob,
client_static_cbinfo, errstr) != OK)
- return NULL;
+ return FALSE;
if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
{
tls_error(US"SSL_new", host, NULL, errstr);
- return NULL;
+ return FALSE;
}
SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx));
-SSL_set_fd(exim_client_ctx->ssl, fd);
+SSL_set_fd(exim_client_ctx->ssl, cctx->sock);
SSL_set_connect_state(exim_client_ctx->ssl);
if (ob->tls_sni)
{
if (!expand_check(ob->tls_sni, US"tls_sni", &tlsp->sni, errstr))
- return NULL;
+ return FALSE;
if (!tlsp->sni)
{
DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n");
}
#ifdef SUPPORT_DANE
-if (tlsa_dnsa)
- if (dane_tlsa_load(exim_client_ctx->ssl, host, tlsa_dnsa, errstr) != OK)
- return NULL;
+if (conn_args->dane)
+ if (dane_tlsa_load(exim_client_ctx->ssl, host, &conn_args->tlsa_dnsa, errstr) != OK)
+ return FALSE;
#endif
#ifndef DISABLE_OCSP
ALARM_CLR(0);
#ifdef SUPPORT_DANE
-if (tlsa_dnsa)
+if (conn_args->dane)
DANESSL_cleanup(exim_client_ctx->ssl);
#endif
if (rc <= 0)
{
tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr);
- return NULL;
+ return FALSE;
}
DEBUG(D_tls)
tlsp->ourcert = crt ? X509_dup(crt) : NULL;
}
-tlsp->active.sock = fd;
+tlsp->active.sock = cctx->sock;
tlsp->active.tls_ctx = exim_client_ctx;
-return exim_client_ctx;
+cctx->tls_ctx = exim_client_ctx;
+return TRUE;
}
int
smtp_setup_conn(smtp_context * sx, BOOL suppress_tls)
{
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
-dns_answer tlsa_dnsa;
-#endif
smtp_transport_options_block * ob = sx->conn_args.tblock->options_block;
BOOL pass_message = FALSE;
uschar * message = NULL;
#endif
sx->dsn_all_lasthop = TRUE;
#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
-sx->dane = FALSE;
+sx->conn_args.dane = FALSE;
sx->dane_required =
verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK;
#endif
if( sx->dane_required
|| verify_check_given_host(CUSS &ob->hosts_try_dane, sx->conn_args.host) == OK
)
- switch (rc = tlsa_lookup(sx->conn_args.host, &tlsa_dnsa, sx->dane_required))
+ switch (rc = tlsa_lookup(sx->conn_args.host, &sx->conn_args.tlsa_dnsa, sx->dane_required))
{
- case OK: sx->dane = TRUE;
+ case OK: sx->conn_args.dane = TRUE;
ob->tls_tempfail_tryclear = FALSE;
break;
case FAIL_FORCED: break;
else
TLS_NEGOTIATE:
{
- sx->cctx.tls_ctx = tls_client_start(sx->cctx.sock, sx->conn_args.host,
- sx->addrlist, sx->conn_args.tblock,
-# ifdef SUPPORT_DANE
- sx->dane ? &tlsa_dnsa : NULL,
-# endif
- &tls_out, &tls_errstr);
-
- if (!sx->cctx.tls_ctx)
+ if (!tls_client_start(&sx->cctx, &sx->conn_args, sx->addrlist, &tls_out, &tls_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
DEBUG(D_tls) debug_printf("TLS session fail: %s\n", tls_errstr);
# ifdef SUPPORT_DANE
- if (sx->dane)
+ if (sx->conn_args.dane)
{
log_write(0, LOG_MAIN,
"DANE attempt failed; TLS connection to %s [%s]: %s",
else if ( sx->smtps
# ifdef SUPPORT_DANE
- || sx->dane
+ || sx->conn_args.dane
# endif
# ifdef EXPERIMENTAL_REQUIRETLS
|| tls_requiretls & REQUIRETLS_MSG
smtp_peer_options & OPTION_TLS
? "an attempt to start TLS failed" : "the server did not offer TLS support");
# if defined(SUPPORT_DANE) && !defined(DISABLE_EVENT)
- if (sx->dane)
+ if (sx->conn_args.dane)
(void) event_raise(sx->conn_args.tblock->event_action, US"dane:fail",
smtp_peer_options & OPTION_TLS
? US"validation-failure" /* could do with better detail */
#endif
BOOL dsn_all_lasthop:1;
#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
- BOOL dane:1;
BOOL dane_required:1;
#endif
#ifdef EXPERIMENTAL_PIPE_CONNECT