# ifndef EXIM_HAVE_OPENSSL_CIPHER_GET_ID
# define SSL_CIPHER_get_id(c) (c->id)
# endif
-# include "tls-cipher-stdname.c"
+# ifndef MACRO_PREDEF
+# include "tls-cipher-stdname.c"
+# endif
#endif
/*************************************************
typedef struct {
SSL_CTX * ctx;
SSL * ssl;
+ gstring * corked;
} exim_openssl_client_tls_ctx;
static SSL_CTX *server_ctx = NULL;
#ifdef EXIM_HAVE_OPENSSL_KEYLOG
{
- BIO * bp = BIO_new(BIO_s_mem());
- uschar * s;
- int len;
+ BIO * bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
SSL_SESSION_print_keylog(bp, SSL_get_session(server_ssl));
- len = (int) BIO_get_mem_data(bp, CSS &s);
- debug_printf("%.*s", len, s);
BIO_free(bp);
}
#endif
/* 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;
rc = store_pool;
store_pool = POOL_PERM;
exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx));
+exim_client_ctx->corked = NULL;
store_pool = rc;
#ifdef SUPPORT_DANE
#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)
debug_printf("SSL_connect succeeded\n");
#ifdef EXIM_HAVE_OPENSSL_KEYLOG
{
- BIO * bp = BIO_new(BIO_s_mem());
- uschar * s;
- int len;
- SSL_SESSION_print_keylog(bp, SSL_get_session(server_ssl));
- len = (int) BIO_get_mem_data(bp, CSS &s);
- debug_printf("%.*s", len, s);
+ BIO * bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
+ SSL_SESSION_print_keylog(bp, SSL_get_session(exim_client_ctx->ssl));
BIO_free(bp);
}
#endif
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;
}
{
size_t olen = len;
int outbytes, error;
-SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
-static gstring * corked = NULL;
+SSL * ssl = ct_ctx
+ ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
+static gstring * server_corked = NULL;
+gstring ** corkedp = ct_ctx
+ ? &((exim_openssl_client_tls_ctx *)ct_ctx)->corked : &server_corked;
+gstring * corked = *corkedp;
DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
buff, (unsigned long)len, more ? ", more" : "");
/* Lacking a CORK or MSG_MORE facility (such as GnuTLS has) we copy data when
"more" is notified. This hack is only ok if small amounts are involved AND only
one stream does it, in one context (i.e. no store reset). Currently it is used
-for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */
+for the responses to the received SMTP MAIL , RCPT, DATA sequence, only.
+We support callouts done by the server process by using a separate client
+context for the stashed information. */
/* + if PIPE_COMMAND, banner & ehlo-resp for smmtp-on-connect. Suspect there's
a store reset there, so use POOL_PERM. */
/* + if CHUNKING, cmds EHLO,MAIL,RCPT(s),BDAT */
#endif
if (more)
+ {
+ *corkedp = corked;
return len;
+ }
buff = CUS corked->s;
len = corked->ptr;
- corked = NULL;
+ *corkedp = NULL;
}
for (int left = len; left > 0;)
DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"\n", s);
return FALSE;
}
- DEBUG(D_tls) debug_printf("openssl option, %s from %lx: %lx (%s)\n",
- adding ? "adding" : "removing", result, item, s);
+ DEBUG(D_tls) debug_printf("openssl option, %s %8lx: %lx (%s)\n",
+ adding ? "adding to " : "removing from", result, item, s);
if (adding)
result |= item;
else