/* Local static variables */
-static BOOL verify_callback_called = FALSE;
+static BOOL client_verify_callback_called = FALSE;
+static BOOL server_verify_callback_called = FALSE;
static const uschar *sid_ctx = US"exim";
-static SSL_CTX *ctx = NULL;
+static SSL_CTX *client_ctx = NULL;
+static SSL_CTX *server_ctx = NULL;
+static SSL *client_ssl = NULL;
+static SSL *server_ssl = NULL;
+
#ifdef EXIM_HAVE_OPENSSL_TLSEXT
-static SSL_CTX *ctx_sni = NULL;
+static SSL_CTX *client_sni = NULL;
+static SSL_CTX *server_sni = NULL;
#endif
-static SSL *ssl = NULL;
static char ssl_errstring[256];
static int ssl_session_timeout = 200;
-static BOOL verify_optional = FALSE;
+static BOOL client_verify_optional = FALSE;
+static BOOL server_verify_optional = FALSE;
static BOOL reexpand_tls_files_for_sni = FALSE;
/* should figure out a cleanup of API to handle state preserved per
implementation, for various reasons, which can be void * in the APIs.
For now, we hack around it. */
-tls_ext_ctx_cb *static_cbinfo = NULL;
+tls_ext_ctx_cb *client_static_cbinfo = NULL;
+tls_ext_ctx_cb *server_static_cbinfo = NULL;
static int
-setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional);
+setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional, BOOL client);
/* Callbacks */
#ifdef EXIM_HAVE_OPENSSL_TLSEXT
Arguments:
state current yes/no state as 1/0
x509ctx certificate information.
+ client TRUE for client startup, FALSE for server startup
Returns: 1 if verified, 0 if not
*/
static int
-verify_callback(int state, X509_STORE_CTX *x509ctx)
+verify_callback(int state, X509_STORE_CTX *x509ctx, BOOL client)
{
static uschar txt[256];
+tls_support * tlsp;
+BOOL * calledp;
+BOOL * optionalp;
+
+if (client)
+ {
+ tlsp= &tls_out;
+ calledp= &client_verify_callback_called;
+ optionalp= &client_verify_optional;
+ }
+else
+ {
+ tlsp= &tls_in;
+ calledp= &server_verify_callback_called;
+ optionalp= &server_verify_optional;
+ }
X509_NAME_oneline(X509_get_subject_name(x509ctx->current_cert),
CS txt, sizeof(txt));
x509ctx->error_depth,
X509_verify_cert_error_string(x509ctx->error),
txt);
- tls_certificate_verified = FALSE;
- verify_callback_called = TRUE;
- if (!verify_optional) return 0; /* reject */
+ tlsp->certificate_verified = FALSE;
+ *calledp = TRUE;
+ if (!*optionalp) return 0; /* reject */
DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
"tls_try_verify_hosts)\n");
return 1; /* accept */
else
{
DEBUG(D_tls) debug_printf("SSL%s peer: %s\n",
- verify_callback_called? "" : " authenticated", txt);
- tls_peerdn = txt;
+ *calledp ? "" : " authenticated", txt);
+ tlsp->peerdn = txt;
}
-if (!verify_callback_called) tls_certificate_verified = TRUE;
-verify_callback_called = TRUE;
+if (!*calledp) tlsp->certificate_verified = TRUE;
+*calledp = TRUE;
return 1; /* accept */
}
+static int
+verify_callback_client(int state, X509_STORE_CTX *x509ctx)
+{
+return verify_callback(state, x509ctx, TRUE);
+}
+
+static int
+verify_callback_server(int state, X509_STORE_CTX *x509ctx)
+{
+return verify_callback(state, x509ctx, FALSE);
+}
+
/*************************************************
if (cbinfo->certificate == NULL)
return OK;
-if (Ustrstr(cbinfo->certificate, US"tls_sni"))
+if (Ustrstr(cbinfo->certificate, US"tls_sni") ||
+ Ustrstr(cbinfo->certificate, US"tls_in_sni") ||
+ Ustrstr(cbinfo->certificate, US"tls_out_sni")
+ )
reexpand_tls_files_for_sni = TRUE;
if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded))
/* Make the extension value available for expansion */
store_pool = POOL_PERM;
-tls_sni = string_copy(US servername);
+tls_in.sni = string_copy(US servername);
store_pool = old_pool;
if (!reexpand_tls_files_for_sni)
not confident that memcpy wouldn't break some internal reference counting.
Especially since there's a references struct member, which would be off. */
-ctx_sni = SSL_CTX_new(SSLv23_server_method());
-if (!ctx_sni)
+server_sni = SSL_CTX_new(SSLv23_server_method());
+if (!server_sni)
{
ERR_error_string(ERR_get_error(), ssl_errstring);
DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring);
/* Not sure how many of these are actually needed, since SSL object
already exists. Might even need this selfsame callback, for reneg? */
-SSL_CTX_set_info_callback(ctx_sni, SSL_CTX_get_info_callback(ctx));
-SSL_CTX_set_mode(ctx_sni, SSL_CTX_get_mode(ctx));
-SSL_CTX_set_options(ctx_sni, SSL_CTX_get_options(ctx));
-SSL_CTX_set_timeout(ctx_sni, SSL_CTX_get_timeout(ctx));
-SSL_CTX_set_tlsext_servername_callback(ctx_sni, tls_servername_cb);
-SSL_CTX_set_tlsext_servername_arg(ctx_sni, cbinfo);
+SSL_CTX_set_info_callback(server_sni, SSL_CTX_get_info_callback(server_ctx));
+SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(server_ctx));
+SSL_CTX_set_options(server_sni, SSL_CTX_get_options(server_ctx));
+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 (cbinfo->server_cipher_list)
- SSL_CTX_set_cipher_list(ctx_sni, CS cbinfo->server_cipher_list);
+ SSL_CTX_set_cipher_list(server_sni, CS cbinfo->server_cipher_list);
#ifdef EXPERIMENTAL_OCSP
if (cbinfo->ocsp_file)
{
- SSL_CTX_set_tlsext_status_cb(ctx_sni, tls_stapling_cb);
+ SSL_CTX_set_tlsext_status_cb(server_sni, tls_stapling_cb);
SSL_CTX_set_tlsext_status_arg(ctx, cbinfo);
}
#endif
-rc = setup_certs(ctx_sni, tls_verify_certificates, tls_crl, NULL, FALSE);
+rc = setup_certs(server_sni, tls_verify_certificates, tls_crl, NULL, FALSE, FALSE);
if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
/* do this after setup_certs, because this can require the certs for verifying
OCSP information. */
-rc = tls_expand_session_files(ctx_sni, cbinfo);
+rc = tls_expand_session_files(server_sni, cbinfo);
if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
-rc = init_dh(ctx_sni, cbinfo->dhparam, NULL);
+rc = init_dh(server_sni, cbinfo->dhparam, NULL);
if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
DEBUG(D_tls) debug_printf("Switching SSL context.\n");
-SSL_set_SSL_CTX(s, ctx_sni);
+SSL_set_SSL_CTX(s, server_sni);
return SSL_TLSEXT_ERR_OK;
}
*/
static int
-tls_init(host_item *host, uschar *dhparam, uschar *certificate,
+tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate,
uschar *privatekey,
#ifdef EXPERIMENTAL_OCSP
uschar *ocsp_file,
#endif
- address_item *addr)
+ address_item *addr, tls_ext_ctx_cb ** cbp)
{
long init_options;
int rc;
By disabling with openssl_options, we can let admins re-enable with the
existing knob. */
-ctx = SSL_CTX_new((host == NULL)?
+*ctxp = SSL_CTX_new((host == NULL)?
SSLv23_server_method() : SSLv23_client_method());
-if (ctx == NULL) return tls_error(US"SSL_CTX_new", host, NULL);
+if (*ctxp == NULL) return tls_error(US"SSL_CTX_new", host, NULL);
/* 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
/* Set up the information callback, which outputs if debugging is at a suitable
level. */
-SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
+SSL_CTX_set_info_callback(*ctxp, (void (*)())info_callback);
/* Automatically re-try reads/writes after renegotiation. */
-(void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
+(void) SSL_CTX_set_mode(*ctxp, SSL_MODE_AUTO_RETRY);
/* Apply administrator-supplied work-arounds.
Historically we applied just one requested option,
if (init_options)
{
DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options);
- if (!(SSL_CTX_set_options(ctx, init_options)))
+ if (!(SSL_CTX_set_options(*ctxp, init_options)))
return tls_error(string_sprintf(
"SSL_CTX_set_option(%#lx)", init_options), host, NULL);
}
/* Initialize with DH parameters if supplied */
-if (!init_dh(ctx, dhparam, host)) return DEFER;
+if (!init_dh(*ctxp, dhparam, host)) return DEFER;
/* Set up certificate and key (and perhaps OCSP info) */
-rc = tls_expand_session_files(ctx, cbinfo);
+rc = tls_expand_session_files(*ctxp, cbinfo);
if (rc != OK) return rc;
/* If we need to handle SNI, do so */
#endif
/* We always do this, so that $tls_sni is available even if not used in
tls_certificate */
- SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb);
- SSL_CTX_set_tlsext_servername_arg(ctx, cbinfo);
+ SSL_CTX_set_tlsext_servername_callback(*ctxp, tls_servername_cb);
+ SSL_CTX_set_tlsext_servername_arg(*ctxp, cbinfo);
}
#endif
/* Set up the RSA callback */
-SSL_CTX_set_tmp_rsa_callback(ctx, rsa_callback);
+SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback);
/* Finally, set the timeout, and we are done */
-SSL_CTX_set_timeout(ctx, ssl_session_timeout);
+SSL_CTX_set_timeout(*ctxp, ssl_session_timeout);
DEBUG(D_tls) debug_printf("Initialized TLS\n");
-static_cbinfo = cbinfo;
+*cbp = cbinfo;
return OK;
}
* Get name of cipher in use *
*************************************************/
-/* The answer is left in a static buffer, and tls_cipher is set to point
-to it.
-
+/*
Argument: pointer to an SSL structure for the connection
+ buffer to use for answer
+ size of buffer
+ pointer to number of bits for cipher
Returns: nothing
*/
static void
-construct_cipher_name(SSL *ssl)
+construct_cipher_name(SSL *ssl, uschar *cipherbuf, int bsize, int *bits)
{
-static uschar cipherbuf[256];
/* With OpenSSL 1.0.0a, this needs to be const but the documentation doesn't
yet reflect that. It should be a safe change anyway, even 0.9.8 versions have
the accessor functions use const in the prototype. */
}
c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
-SSL_CIPHER_get_bits(c, &tls_bits);
+SSL_CIPHER_get_bits(c, bits);
-string_format(cipherbuf, sizeof(cipherbuf), "%s:%s:%u", ver,
- SSL_CIPHER_get_name(c), tls_bits);
-tls_cipher = cipherbuf;
+string_format(cipherbuf, bsize, "%s:%s:%u", ver,
+ SSL_CIPHER_get_name(c), *bits);
DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf);
}
host NULL in a server; the remote host in a client
optional TRUE if called from a server for a host in tls_try_verify_hosts;
otherwise passed as FALSE
+ client TRUE if called for client startup, FALSE for server startup
Returns: OK/DEFER/FAIL
*/
static int
-setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional)
+setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional, BOOL client)
{
uschar *expcerts, *expcrl;
SSL_CTX_set_verify(sctx,
SSL_VERIFY_PEER | (optional? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
- verify_callback);
+ client ? verify_callback_client : verify_callback_server);
}
return OK;
int rc;
uschar *expciphers;
tls_ext_ctx_cb *cbinfo;
+static uschar cipherbuf[256];
/* Check for previous activation */
-if (tls_active >= 0)
+if (tls_in.active >= 0)
{
tls_error(US"STARTTLS received after TLS started", NULL, US"");
smtp_printf("554 Already in TLS\r\n");
/* Initialize the SSL library. If it fails, it will already have logged
the error. */
-rc = tls_init(NULL, tls_dhparam, tls_certificate, tls_privatekey,
+rc = tls_init(&server_ctx, NULL, tls_dhparam, tls_certificate, tls_privatekey,
#ifdef EXPERIMENTAL_OCSP
tls_ocsp_file,
#endif
- NULL);
+ NULL, &server_static_cbinfo);
if (rc != OK) return rc;
-cbinfo = static_cbinfo;
+cbinfo = server_static_cbinfo;
if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers))
return FAIL;
uschar *s = expciphers;
while (*s != 0) { if (*s == '_') *s = '-'; s++; }
DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
- if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
+ if (!SSL_CTX_set_cipher_list(server_ctx, CS expciphers))
return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL);
cbinfo->server_cipher_list = expciphers;
}
/* If this is a host for which certificate verification is mandatory or
optional, set up appropriately. */
-tls_certificate_verified = FALSE;
-verify_callback_called = FALSE;
+tls_in.certificate_verified = FALSE;
+server_verify_callback_called = FALSE;
if (verify_check_host(&tls_verify_hosts) == OK)
{
- rc = setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, FALSE);
+ rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL, FALSE, FALSE);
if (rc != OK) return rc;
- verify_optional = FALSE;
+ server_verify_optional = FALSE;
}
else if (verify_check_host(&tls_try_verify_hosts) == OK)
{
- rc = setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, TRUE);
+ rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL, TRUE, FALSE);
if (rc != OK) return rc;
- verify_optional = TRUE;
+ server_verify_optional = TRUE;
}
/* Prepare for new connection */
-if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", NULL, NULL);
+if ((server_ssl = SSL_new(server_ctx)) == NULL) return tls_error(US"SSL_new", NULL, NULL);
/* Warning: we used to SSL_clear(ssl) here, it was removed.
*
the response. Other smtp_printf() calls do not need it, because in non-TLS
mode, the fflush() happens when smtp_getc() is called. */
-SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx));
-if (!tls_on_connect)
+SSL_set_session_id_context(server_ssl, sid_ctx, Ustrlen(sid_ctx));
+if (!tls_in.on_connect)
{
smtp_printf("220 TLS go ahead\r\n");
fflush(smtp_out);
/* Now negotiate the TLS session. We put our own timer on it, since it seems
that the OpenSSL library doesn't. */
-SSL_set_wfd(ssl, fileno(smtp_out));
-SSL_set_rfd(ssl, fileno(smtp_in));
-SSL_set_accept_state(ssl);
+SSL_set_wfd(server_ssl, fileno(smtp_out));
+SSL_set_rfd(server_ssl, fileno(smtp_in));
+SSL_set_accept_state(server_ssl);
DEBUG(D_tls) debug_printf("Calling SSL_accept\n");
sigalrm_seen = FALSE;
if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
-rc = SSL_accept(ssl);
+rc = SSL_accept(server_ssl);
alarm(0);
if (rc <= 0)
/* TLS has been set up. Adjust the input functions to read via TLS,
and initialize things. */
-construct_cipher_name(ssl);
+construct_cipher_name(server_ssl, cipherbuf, sizeof(cipherbuf), &tls_in.bits);
+tls_in.cipher = cipherbuf;
DEBUG(D_tls)
{
uschar buf[2048];
- if (SSL_get_shared_ciphers(ssl, CS buf, sizeof(buf)) != NULL)
+ if (SSL_get_shared_ciphers(server_ssl, CS buf, sizeof(buf)) != NULL)
debug_printf("Shared ciphers: %s\n", buf);
}
+/* Only used by the server-side tls (tls_in), including tls_getc.
+ Client-side (tls_out) reads (seem to?) go via
+ smtp_read_response()/ip_recv().
+ Hence no need to duplicate for _in and _out.
+ */
ssl_xfer_buffer = store_malloc(ssl_xfer_buffer_size);
ssl_xfer_buffer_lwm = ssl_xfer_buffer_hwm = 0;
ssl_xfer_eof = ssl_xfer_error = 0;
receive_ferror = tls_ferror;
receive_smtp_buffered = tls_smtp_buffered;
-tls_active = fileno(smtp_out);
+tls_in.active = fileno(smtp_out);
return OK;
}
verify_certs file for certificate verify
crl file containing CRL
require_ciphers list of allowed ciphers
+ dh_min_bits minimum number of bits acceptable in server's DH prime
+ (unused in OpenSSL)
timeout startup timeout
Returns: OK on success
tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
uschar *certificate, uschar *privatekey, uschar *sni,
uschar *verify_certs, uschar *crl,
- uschar *require_ciphers, int timeout)
+ uschar *require_ciphers, int dh_min_bits ARG_UNUSED, int timeout)
{
static uschar txt[256];
uschar *expciphers;
X509* server_cert;
int rc;
+static uschar cipherbuf[256];
-rc = tls_init(host, dhparam, certificate, privatekey,
+rc = tls_init(&client_ctx, host, dhparam, certificate, privatekey,
#ifdef EXPERIMENTAL_OCSP
NULL,
#endif
- addr);
+ addr, &client_static_cbinfo);
if (rc != OK) return rc;
-tls_certificate_verified = FALSE;
-verify_callback_called = FALSE;
+tls_out.certificate_verified = FALSE;
+client_verify_callback_called = FALSE;
if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers))
return FAIL;
uschar *s = expciphers;
while (*s != 0) { if (*s == '_') *s = '-'; s++; }
DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
- if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
+ if (!SSL_CTX_set_cipher_list(client_ctx, CS expciphers))
return tls_error(US"SSL_CTX_set_cipher_list", host, NULL);
}
-rc = setup_certs(ctx, verify_certs, crl, host, FALSE);
+rc = setup_certs(client_ctx, verify_certs, crl, host, FALSE, TRUE);
if (rc != OK) return rc;
-if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", host, NULL);
-SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx));
-SSL_set_fd(ssl, fd);
-SSL_set_connect_state(ssl);
+if ((client_ssl = SSL_new(client_ctx)) == NULL) return tls_error(US"SSL_new", host, NULL);
+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 (sni)
{
- if (!expand_check(sni, US"tls_sni", &tls_sni))
+ if (!expand_check(sni, US"tls_sni", &tls_out.sni))
return FAIL;
- if (!Ustrlen(tls_out.sni))
- if (tls_sni == NULL)
++ if (tls_out.sni == NULL)
+ {
+ DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n");
+ }
- else if (!Ustrlen(tls_sni))
- tls_sni = NULL;
++ else if (!Ustrlen(tls_out.sni))
+ tls_out.sni = NULL;
else
{
#ifdef EXIM_HAVE_OPENSSL_TLSEXT
- DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_sni);
- SSL_set_tlsext_host_name(ssl, tls_sni);
+ DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_out.sni);
+ SSL_set_tlsext_host_name(client_ssl, tls_out.sni);
#else
DEBUG(D_tls)
debug_printf("OpenSSL at build-time lacked SNI support, ignoring \"%s\"\n",
DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
sigalrm_seen = FALSE;
alarm(timeout);
-rc = SSL_connect(ssl);
+rc = SSL_connect(client_ssl);
alarm(0);
if (rc <= 0)
DEBUG(D_tls) debug_printf("SSL_connect succeeded\n");
/* Beware anonymous ciphers which lead to server_cert being NULL */
-server_cert = SSL_get_peer_certificate (ssl);
+server_cert = SSL_get_peer_certificate (client_ssl);
if (server_cert)
{
- tls_peerdn = US X509_NAME_oneline(X509_get_subject_name(server_cert),
+ tls_out.peerdn = US X509_NAME_oneline(X509_get_subject_name(server_cert),
CS txt, sizeof(txt));
- tls_peerdn = txt;
+ tls_out.peerdn = txt;
}
else
- tls_peerdn = NULL;
+ tls_out.peerdn = NULL;
-construct_cipher_name(ssl); /* Sets tls_cipher */
+construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits);
+tls_out.cipher = cipherbuf;
-tls_active = fd;
+tls_out.active = fd;
return OK;
}
Arguments: none
Returns: the next character or EOF
+
+Only used by the server-side TLS.
*/
int
int error;
int inbytes;
- DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", ssl,
+ DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", server_ssl,
ssl_xfer_buffer, ssl_xfer_buffer_size);
if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
- inbytes = SSL_read(ssl, CS ssl_xfer_buffer, ssl_xfer_buffer_size);
- error = SSL_get_error(ssl, inbytes);
+ inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer, ssl_xfer_buffer_size);
+ error = SSL_get_error(server_ssl, inbytes);
alarm(0);
/* SSL_ERROR_ZERO_RETURN appears to mean that the SSL session has been
receive_ferror = smtp_ferror;
receive_smtp_buffered = smtp_buffered;
- SSL_free(ssl);
- ssl = NULL;
- tls_active = -1;
- tls_bits = 0;
- tls_cipher = NULL;
- tls_peerdn = NULL;
- tls_sni = NULL;
+ SSL_free(server_ssl);
+ server_ssl = NULL;
+ tls_in.active = -1;
+ tls_in.bits = 0;
+ tls_in.cipher = NULL;
+ tls_in.peerdn = NULL;
+ tls_in.sni = NULL;
return smtp_getc();
}
Returns: the number of bytes read
-1 after a failed read
+
+Only used by the client-side TLS.
*/
int
-tls_read(uschar *buff, size_t len)
+tls_read(BOOL is_server, uschar *buff, size_t len)
{
+SSL *ssl = is_server ? server_ssl : client_ssl;
int inbytes;
int error;
/*
Arguments:
+ is_server channel specifier
buff buffer of data
len number of bytes
Returns: the number of bytes after a successful write,
-1 after a failed write
+
+Used by both server-side and client-side TLS.
*/
int
-tls_write(const uschar *buff, size_t len)
+tls_write(BOOL is_server, const uschar *buff, size_t len)
{
int outbytes;
int error;
int left = len;
+SSL *ssl = is_server ? server_ssl : client_ssl;
DEBUG(D_tls) debug_printf("tls_do_write(%p, %d)\n", buff, left);
while (left > 0)
log_write(0, LOG_MAIN, "SSL channel closed on write");
return -1;
+ case SSL_ERROR_SYSCALL:
+ log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s",
+ sender_fullhost ? sender_fullhost : US"<unknown>",
+ strerror(errno));
+
default:
log_write(0, LOG_MAIN, "SSL_write error %d", error);
return -1;
Arguments: TRUE if SSL_shutdown is to be called
Returns: nothing
+
+Used by both server-side and client-side TLS.
*/
void
-tls_close(BOOL shutdown)
+tls_close(BOOL is_server, BOOL shutdown)
{
-if (tls_active < 0) return; /* TLS was not active */
+SSL **sslp = is_server ? &server_ssl : &client_ssl;
+int *fdp = is_server ? &tls_in.active : &tls_out.active;
+
+if (*fdp < 0) return; /* TLS was not active */
if (shutdown)
{
DEBUG(D_tls) debug_printf("tls_close(): shutting down SSL\n");
- SSL_shutdown(ssl);
+ SSL_shutdown(*sslp);
}
-SSL_free(ssl);
-ssl = NULL;
+SSL_free(*sslp);
+*sslp = NULL;
-tls_active = -1;
+*fdp = -1;
}