# define EXIM_HAVE_SESSION_TICKET
# define EXIM_HAVE_OPESSL_TRACE
# define EXIM_HAVE_OPESSL_GET0_SERIAL
+# ifndef DISABLE_OCSP
+# define EXIM_HAVE_OCSP
+# endif
# else
# define EXIM_NEED_OPENSSL_INIT
# endif
# define OPENSSL_HAVE_KEYLOG_CB
# define OPENSSL_HAVE_NUM_TICKETS
# define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME
+# else
+# define OPENSSL_BAD_SRVR_OURCERT
# endif
#endif
to apply.
This list is current as of:
- ==> 1.0.1b <==
-Plus SSL_OP_SAFARI_ECDHE_ECDSA_BUG from 2013-June patch/discussion on openssl-dev
-Plus SSL_OP_NO_TLSv1_3 for 1.1.2-dev
+ ==> 1.1.1c <==
+
+XXX could we autobuild this list, as with predefined-macros?
+Seems just parsing ssl.h for SSL_OP_.* would be enough (except to exclude DTLS).
+Also allow a numeric literal?
*/
static exim_openssl_option exim_openssl_options[] = {
/* KEEP SORTED ALPHABETICALLY! */
#ifdef SSL_OP_ALL
{ US"all", (long) SSL_OP_ALL },
#endif
+#ifdef SSL_OP_ALLOW_NO_DHE_KEX
+ { US"allow_no_dhe_kex", SSL_OP_ALLOW_NO_DHE_KEX },
+#endif
#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
{ US"allow_unsafe_legacy_renegotiation", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION },
#endif
#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
{ US"cipher_server_preference", SSL_OP_CIPHER_SERVER_PREFERENCE },
#endif
+#ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG
+ { US"cryptopro_tlsext_bug", SSL_OP_CRYPTOPRO_TLSEXT_BUG },
+#endif
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
{ US"dont_insert_empty_fragments", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS },
#endif
+#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
+ { US"enable_middlebox_compat", SSL_OP_ENABLE_MIDDLEBOX_COMPAT },
+#endif
#ifdef SSL_OP_EPHEMERAL_RSA
{ US"ephemeral_rsa", SSL_OP_EPHEMERAL_RSA },
#endif
#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
{ US"netscape_reuse_cipher_change_bug", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG },
#endif
+#ifdef SSL_OP_NO_ANTI_REPLAY
+ { US"no_anti_replay", SSL_OP_NO_ANTI_REPLAY },
+#endif
#ifdef SSL_OP_NO_COMPRESSION
{ US"no_compression", SSL_OP_NO_COMPRESSION },
#endif
+#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC
+ { US"no_encrypt_then_mac", SSL_OP_NO_ENCRYPT_THEN_MAC },
+#endif
+#ifdef SSL_OP_NO_RENEGOTIATION
+ { US"no_renegotiation", SSL_OP_NO_RENEGOTIATION },
+#endif
#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
{ US"no_session_resumption_on_renegotiation", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION },
#endif
#ifdef SSL_OP_NO_TLSv1_3
{ US"no_tlsv1_3", SSL_OP_NO_TLSv1_3 },
#endif
+#ifdef SSL_OP_PRIORITIZE_CHACHA
+ { US"prioritize_chacha", SSL_OP_PRIORITIZE_CHACHA },
+#endif
#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG
{ US"safari_ecdhe_ecdsa_bug", SSL_OP_SAFARI_ECDHE_ECDSA_BUG },
#endif
#ifdef SSL_OP_TLS_ROLLBACK_BUG
{ US"tls_rollback_bug", SSL_OP_TLS_ROLLBACK_BUG },
#endif
+#ifdef SSL_OP_TLSEXT_PADDING
+ { US"tlsext_padding", SSL_OP_TLSEXT_PADDING },
+#endif
};
#ifndef MACRO_PREDEF
# ifdef SSL_OP_NO_TLSv1_3
builtin_macro_create(US"_HAVE_TLS1_3");
# endif
+# ifdef OPENSSL_BAD_SRVR_OURCERT
+builtin_macro_create(US"_TLS_BAD_MULTICERT_IN_OURCERT");
+# endif
+# ifdef EXIM_HAVE_OCSP
+builtin_macro_create(US"_HAVE_TLS_OCSP");
+builtin_macro_create(US"_HAVE_TLS_OCSP_LIST");
+# endif
}
#else
OCSP_BASICRESP * bs;
int i;
-DEBUG(D_tls) debug_printf("Received TLS status response (OCSP stapling):\n");
+DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
len = SSL_get_tlsext_status_ocsp_resp(s, &p);
if(!p)
{
/* Expect this when we requested ocsp but got none */
if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
- log_write(0, LOG_MAIN, "Received TLS status callback, null content");
+ log_write(0, LOG_MAIN, "Required TLS certificate status not received");
else
DEBUG(D_tls) debug_printf(" null\n");
return cbinfo->u_ocsp.client.verify_required ? 0 : 1;
*/
static uschar *
-construct_cipher_name(SSL * ssl, int * bits)
+construct_cipher_name(SSL * ssl, const uschar * ver, int * bits)
{
int pool = store_pool;
/* With OpenSSL 1.0.0a, 'c' 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. */
-const uschar * ver = CUS SSL_get_version(ssl);
const SSL_CIPHER * c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
uschar * s;
}
+static const uschar *
+tlsver_name(SSL * ssl)
+{
+uschar * s, * p;
+int pool = store_pool;
+
+store_pool = POOL_PERM;
+s = string_copy(US SSL_get_version(ssl));
+store_pool = pool;
+if ((p = Ustrchr(s, 'v'))) /* TLSv1.2 -> TLS1.2 */
+ for (;; p++) if (!(*p = p[1])) break;
+return CUS s;
+}
+
+
static void
peer_cert(SSL * ssl, tls_support * tlsp, uschar * peerdn, unsigned siz)
{
for resumption next to the TLS session, and used here. */
if (!tlsp->verify_override)
- tlsp->certificate_verified = SSL_get_verify_result(ssl) == X509_V_OK;
+ tlsp->certificate_verified =
+#ifdef SUPPORT_DANE
+ tlsp->dane_verified ||
+#endif
+ SSL_get_verify_result(ssl) == X509_V_OK;
}
}
case SSL_ERROR_SSL:
{
uschar * s = US"SSL_accept";
- ulong e = ERR_peek_error();
+ unsigned long e = ERR_peek_error();
if (ERR_GET_REASON(e) == SSL_R_WRONG_VERSION_NUMBER)
s = string_sprintf("%s (%s)", s, SSL_get_version(server_ssl));
(void) tls_error(s, NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
}
#endif
-/* TLS has been set up. Adjust the input functions to read via TLS,
-and initialize things. */
+/* TLS has been set up. Record data for the connection,
+adjust the input functions to read via TLS, and initialize things. */
+#ifdef SSL_get_extms_support
+tls_in.ext_master_secret = SSL_get_extms_support(server_ssl) == 1;
+#endif
peer_cert(server_ssl, &tls_in, peerdn, sizeof(peerdn));
-tls_in.cipher = construct_cipher_name(server_ssl, &tls_in.bits);
+tls_in.ver = tlsver_name(server_ssl);
+tls_in.cipher = construct_cipher_name(server_ssl, tls_in.ver, &tls_in.bits);
tls_in.cipher_stdname = cipher_stdname_ssl(server_ssl);
DEBUG(D_tls)
tls_in.ourcert = crt ? X509_dup(crt) : NULL;
}
+/* Channel-binding info for authenticators
+See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */
+ {
+ uschar c, * s;
+ size_t len = SSL_get_peer_finished(server_ssl, &c, 0);
+ int old_pool = store_pool;
+
+ SSL_get_peer_finished(server_ssl, s = store_get((int)len, FALSE), len);
+ store_pool = POOL_PERM;
+ tls_in.channelbinding = b64encode_taint(CUS s, (int)len, FALSE);
+ store_pool = old_pool;
+ DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p\n", tls_in.channelbinding);
+ }
+
/* 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().
tls_client_resume_posthandshake(exim_client_ctx, tlsp);
#endif
+#ifdef SSL_get_extms_support
+tlsp->ext_master_secret = SSL_get_extms_support(exim_client_ctx->ssl) == 1;
+#endif
peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn));
-tlsp->cipher = construct_cipher_name(exim_client_ctx->ssl, &tlsp->bits);
+tlsp->ver = tlsver_name(exim_client_ctx->ssl);
+tlsp->cipher = construct_cipher_name(exim_client_ctx->ssl, tlsp->ver, &tlsp->bits);
tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
/* Record the certificate we presented */
tlsp->ourcert = crt ? X509_dup(crt) : NULL;
}
+/*XXX will this work with continued-TLS? */
+/* Channel-binding info for authenticators */
+ {
+ uschar c, * s;
+ size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0);
+ int old_pool = store_pool;
+
+ SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, TRUE), len);
+ store_pool = POOL_PERM;
+ tlsp->channelbinding = b64encode_taint(CUS s, (int)len, TRUE);
+ store_pool = old_pool;
+ DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
+ }
+
tlsp->active.sock = cctx->sock;
tlsp->active.tls_ctx = exim_client_ctx;
cctx->tls_ctx = exim_client_ctx;
Returns: the number of bytes after a successful write,
-1 after a failed write
-Used by both server-side and client-side TLS.
+Used by both server-side and client-side TLS. Calling with len zero and more unset
+will flush buffered writes; buff can be null for this case.
*/
int
-tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
+tls_write(void * ct_ctx, const uschar * buff, size_t len, BOOL more)
{
size_t olen = len;
int outbytes, error;
if ((more || corked))
{
-#ifdef SUPPORT_PIPE_CONNECT
+ if (!len) buff = US &error; /* dummy just so that string_catn is ok */
+
+#ifndef DISABLE_PIPE_CONNECT
int save_pool = store_pool;
store_pool = POOL_PERM;
#endif
corked = string_catn(corked, buff, len);
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
store_pool = save_pool;
#endif
DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
switch (error)
{
+ case SSL_ERROR_NONE: /* the usual case */
+ left -= outbytes;
+ buff += outbytes;
+ break;
+
case SSL_ERROR_SSL:
ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
return -1;
- case SSL_ERROR_NONE:
- left -= outbytes;
- buff += outbytes;
- break;
-
case SSL_ERROR_ZERO_RETURN:
log_write(0, LOG_MAIN, "SSL channel closed on write");
return -1;
#ifdef SSL_OP_SINGLE_DH_USE
result |= SSL_OP_SINGLE_DH_USE;
#endif
+#ifdef SSL_OP_NO_RENEGOTIATION
+result |= SSL_OP_NO_RENEGOTIATION;
+#endif
if (!option_spec)
{
DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"\n", s);
return FALSE;
}
- DEBUG(D_tls) debug_printf("openssl option, %s %8lx: %lx (%s)\n",
+ DEBUG(D_tls) debug_printf("openssl option, %s %08lx: %08lx (%s)\n",
adding ? "adding to " : "removing from", result, item, s);
if (adding)
result |= item;