* Expand key and cert file specs *
*************************************************/
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/*
Arguments:
s SSL connection (not used)
DEBUG(D_tls) debug_printf("Generating %d bit RSA key...\n", keylength);
-#ifdef EXIM_HAVE_RSA_GENKEY_EX
+# ifdef EXIM_HAVE_RSA_GENKEY_EX
if ( !BN_set_word(bn, (unsigned long)RSA_F4)
|| !(rsa_key = RSA_new())
|| !RSA_generate_key_ex(rsa_key, keylength, bn, NULL)
)
-#else
+# else
if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL)))
-#endif
+# endif
{
ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
}
return rsa_key;
}
+#endif /* pre-3.0.0 */
{
X509 * x509 = NULL;
EVP_PKEY * pkey;
-RSA * rsa;
X509_NAME * name;
uschar * where;
goto err;
where = US"generating pkey";
-if (!(rsa = rsa_callback(NULL, 0, 2048)))
- goto err;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ {
+ RSA * rsa;
+ if (!(rsa = rsa_callback(NULL, 0, 2048)))
+ goto err;
-where = US"assigning pkey";
-if (!EVP_PKEY_assign_RSA(pkey, rsa))
- goto err;
+ where = US"assigning pkey";
+ if (!EVP_PKEY_assign_RSA(pkey, rsa))
+ goto err;
+ }
+#else
+pkey = EVP_RSA_gen(2048);
+#endif
X509_set_version(x509, 2); /* N+1 - version 3 */
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
old_cert = tlsp->peercert;
tlsp->peercert = X509_dup(cert);
/* NB we do not bother setting peerdn */
- if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth))))
+ if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth), &errno)))
{
log_write(0, LOG_MAIN, "[%s] %s verify denied by event-action: "
"depth=%d cert=%s: %s",
* One-time init credentials for server and client *
**************************************************/
+static void
+normalise_ciphers(uschar ** ciphers, const uschar * pre_expansion_ciphers)
+{
+uschar * s = *ciphers;
+
+if (!s || !Ustrchr(s, '_')) return; /* no change needed */
+
+if (s == pre_expansion_ciphers)
+ s = string_copy(s); /* get writable copy */
+
+for (uschar * t = s; *t; t++) if (*t == '_') *t = '-';
+*ciphers = s;
+}
+
static int
server_load_ciphers(SSL_CTX * ctx, exim_openssl_state_st * state,
uschar * ciphers, uschar ** errstr)
{
-for (uschar * s = ciphers; *s; s++ ) if (*s == '_') *s = '-';
DEBUG(D_tls) debug_printf("required ciphers: %s\n", ciphers);
if (!SSL_CTX_set_cipher_list(ctx, CS ciphers))
return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL, errstr);
if (opt_set_and_noexpand(tls_require_ciphers))
{
DEBUG(D_tls) debug_printf("TLS: preloading cipher list for server\n");
+ normalise_ciphers(&tls_require_ciphers, tls_require_ciphers);
if (server_load_ciphers(ctx, &state_server, tls_require_ciphers,
&dummy_errstr) == OK)
state_server.lib_state.pri_string = TRUE;
const EVP_CIPHER * aes_cipher;
uschar aes_key[32]; /* size needed depends on cipher. aes_128 implies 128/8 = 16? */
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
const EVP_MD * hmac_hash;
+# else
+ const uschar * hmac_hashname;
+# endif
uschar hmac_key[16];
time_t renew;
time_t expire;
exim_tk.name[0] = 'E';
exim_tk.aes_cipher = EVP_aes_256_cbc();
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
exim_tk.hmac_hash = EVP_sha256();
+# else
+exim_tk.hmac_hashname = US "sha256";
+# endif
exim_tk.expire = t + ssl_session_timeout;
exim_tk.renew = t + ssl_session_timeout/2;
}
: NULL;
}
+
+static int
+tk_hmac_init(
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
+ HMAC_CTX * hctx,
+#else
+ EVP_MAC_CTX * hctx,
+#endif
+ exim_stek * key
+ )
+{
+/*XXX will want these dependent on the ssl session strength */
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
+ HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
+ key->hmac_hash, NULL);
+#else
+ {
+ OSSL_PARAM params[3];
+ uschar * hk = string_copy(key->hmac_hashname); /* need nonconst */
+ params[0] = OSSL_PARAM_construct_octet_string("key", key->hmac_key, sizeof(key->hmac_key));
+ params[1] = OSSL_PARAM_construct_utf8_string("digest", CS hk, 0);
+ params[2] = OSSL_PARAM_construct_end();
+ if (EVP_MAC_CTX_set_params(hctx, params) == 0)
+ {
+ DEBUG(D_tls) debug_printf("EVP_MAC_CTX_set_params: %s\n",
+ ERR_reason_error_string(ERR_get_error()));
+ return 0; /* error in mac initialisation */
+ }
+}
+#endif
+return 1;
+}
+
/* Callback for session tickets, on server */
static int
ticket_key_callback(SSL * ssl, uschar key_name[16],
- uschar * iv, EVP_CIPHER_CTX * c_ctx, HMAC_CTX * hctx, int enc)
+ uschar * iv, EVP_CIPHER_CTX * c_ctx,
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
+ HMAC_CTX * hctx,
+#else
+ EVP_MAC_CTX * hctx,
+#endif
+ int enc)
{
tls_support * tlsp = state_server.tlsp;
exim_stek * key;
memcpy(key_name, key->name, 16);
DEBUG(D_tls) debug_printf("STEK expire " TIME_T_FMT "\n", key->expire - time(NULL));
- /*XXX will want these dependent on the ssl session strength */
- HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
- key->hmac_hash, NULL);
+ if (tk_hmac_init(hctx, key) == 0) return 0;
EVP_EncryptInit_ex(c_ctx, key->aes_cipher, NULL, key->aes_key, iv);
DEBUG(D_tls) debug_printf("ticket created\n");
return 0;
}
- HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
- key->hmac_hash, NULL);
+ if (tk_hmac_init(hctx, key) == 0) return 0;
EVP_DecryptInit_ex(c_ctx, key->aes_cipher, NULL, key->aes_key, iv);
DEBUG(D_tls) debug_printf("ticket usable, STEK expire " TIME_T_FMT "\n", key->expire - now);
return key->renew < now ? 2 : 1;
}
}
-#endif
+#endif /* !DISABLE_TLS_RESUME */
STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
#endif
- DEBUG(D_tls) bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
+ DEBUG(D_tls) bp = BIO_new(BIO_s_mem());
/*OCSP_RESPONSE_print(bp, rsp, 0); extreme debug: stapling content */
if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN,
"Received TLS cert status response, itself unverifiable: %s",
ERR_reason_error_string(ERR_peek_error()));
- BIO_printf(bp, "OCSP response verify failure\n");
- ERR_print_errors(bp);
- OCSP_RESPONSE_print(bp, rsp, 0);
+ DEBUG(D_tls)
+ {
+ BIO_printf(bp, "OCSP response verify failure\n");
+ ERR_print_errors(bp);
+ OCSP_RESPONSE_print(bp, rsp, 0);
+ }
goto failed;
}
else
status = OCSP_single_get0_status(single, &reason, &rev,
&thisupd, &nextupd);
- DEBUG(D_tls) time_print(bp, "This OCSP Update", thisupd);
- DEBUG(D_tls) if(nextupd) time_print(bp, "Next OCSP Update", nextupd);
+ DEBUG(D_tls)
+ {
+ time_print(bp, "This OCSP Update", thisupd);
+ if (nextupd) time_print(bp, "Next OCSP Update", nextupd);
+ }
if (!OCSP_check_validity(thisupd, nextupd,
EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
{
tls_out.ocsp = OCSP_FAILED;
DEBUG(D_tls) ERR_print_errors(bp);
- log_write(0, LOG_MAIN, "Server OSCP dates invalid");
+ log_write(0, LOG_MAIN, "OCSP dates invalid");
goto failed;
}
tls_out.ocsp = OCSP_FAILED;
i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
good:
+ {
+ uschar * s = NULL;
+ int len = (int) BIO_get_mem_data(bp, CSS &s);
+ if (len > 0) debug_printf("%.*s", len, s);
+ }
BIO_free(bp);
}
+static void
+tls_dump_keylog(SSL * ssl)
+{
+#ifdef EXIM_HAVE_OPENSSL_KEYLOG
+ BIO * bp = BIO_new(BIO_s_mem());
+ uschar * s = NULL;
+ int len;
+ SSL_SESSION_print_keylog(bp, SSL_get_session(ssl));
+ len = (int) BIO_get_mem_data(bp, CSS &s);
+ if (len > 0) debug_printf("%.*s", len, s);
+ BIO_free(bp);
+#endif
+}
+
+
/*************************************************
* Start a TLS session in a server *
*************************************************/
if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers, errstr))
return FAIL;
- if ( expciphers
- && (rc = server_load_ciphers(ctx, &state_server, expciphers, errstr)) != OK)
- return rc;
+ if (expciphers)
+ {
+ normalise_ciphers(&expciphers, tls_require_ciphers);
+ if ((rc = server_load_ciphers(ctx, &state_server, expciphers, errstr)) != OK)
+ return rc;
+ }
}
/* If this is a host for which certificate verification is mandatory or
skip_certs: ;
#ifndef DISABLE_TLS_RESUME
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
SSL_CTX_set_tlsext_ticket_key_cb(ctx, ticket_key_callback);
/* despite working, appears to always return failure, so ignoring */
+# else
+SSL_CTX_set_tlsext_ticket_key_evp_cb(ctx, ticket_key_callback);
+/* despite working, appears to always return failure, so ignoring */
+# endif
#endif
+
#ifdef OPENSSL_HAVE_NUM_TICKETS
# ifndef DISABLE_TLS_RESUME
SSL_CTX_set_num_tickets(ctx, tls_in.host_resumable ? 1 : 0);
case SSL_ERROR_ZERO_RETURN:
DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
(void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
+ (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
if (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN)
- SSL_shutdown(ssl);
+ SSL_shutdown(ssl);
tls_close(NULL, TLS_NO_SHUTDOWN);
return FAIL;
|| r == SSL_R_VERSION_TOO_LOW
#endif
|| r == SSL_R_UNKNOWN_PROTOCOL || r == SSL_R_UNSUPPORTED_PROTOCOL)
- s = string_sprintf("%s (%s)", s, SSL_get_version(ssl));
+ s = string_sprintf("(%s)", SSL_get_version(ssl));
(void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : s, errstr);
+ (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
return FAIL;
}
if (!errno)
{
*errstr = US"SSL_accept: TCP connection closed by peer";
+ (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
return FAIL;
}
DEBUG(D_tls) debug_printf(" - syscall %s\n", strerror(errno));
sigalrm_seen ? US"timed out"
: ERR_peek_error() ? NULL : string_sprintf("ret %d", error),
errstr);
+ (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
return FAIL;
}
}
if (SSL_get_shared_ciphers(ssl, CS buf, sizeof(buf)))
debug_printf("Shared ciphers: %s\n", buf);
-#ifdef EXIM_HAVE_OPENSSL_KEYLOG
- {
- BIO * bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
- SSL_SESSION_print_keylog(bp, SSL_get_session(ssl));
- BIO_free(bp);
- }
-#endif
+ tls_dump_keylog(ssl);
#ifdef EXIM_HAVE_SESSION_TICKET
{
return FALSE;
if (expciphers && *expciphers == '\0')
expciphers = NULL;
+
+ normalise_ciphers(&expciphers, ob->dane_require_tls_ciphers);
}
#endif
-if (!expciphers &&
- !expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
+if (!expciphers)
+ {
+ if (!expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
&expciphers, errstr))
- return FALSE;
+ 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
-also for general convenience, we turn underscores into hyphens here. */
+ /* 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. */
+
+ normalise_ciphers(&expciphers, ob->tls_require_ciphers);
+ }
if (expciphers)
{
- uschar *s = expciphers;
- while (*s) { if (*s == '_') *s = '-'; s++; }
DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
if (!SSL_CTX_set_cipher_list(exim_client_ctx->ctx, CS expciphers))
{
DEBUG(D_tls)
{
debug_printf("SSL_connect succeeded\n");
-#ifdef EXIM_HAVE_OPENSSL_KEYLOG
- {
- 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
+ tls_dump_keylog(exim_client_ctx->ssl);
}
#ifndef DISABLE_TLS_RESUME
if ( (rc = SSL_shutdown(*sslp)) == 0 /* send "close notify" alert */
&& do_shutdown > 1)
{
+#ifdef EXIM_TCP_CORK
+ (void) setsockopt(*fdp, IPPROTO_TCP, EXIM_TCP_CORK, US &off, sizeof(off));
+#endif
ALARM(2);
rc = SSL_shutdown(*sslp); /* wait for response */
ALARM_CLR(0);
if (!(expciphers && *expciphers))
return NULL;
-/* normalisation ripped from above */
-s = expciphers;
-while (*s != 0) { if (*s == '_') *s = '-'; s++; }
+normalise_ciphers(&expciphers, tls_require_ciphers);
err = NULL;
-
if (lib_ctx_new(&ctx, NULL, &err) == OK)
{
DEBUG(D_tls)
will change, so we can more usefully assist with version diagnosis by also
reporting the build date.
-Arguments: a FILE* to print the results to
-Returns: nothing
+Arguments: string to append to
+Returns: string
*/
-void
-tls_version_report(FILE *f)
+gstring *
+tls_version_report(gstring * g)
{
-fprintf(f, "Library version: OpenSSL: Compile: %s\n"
- " Runtime: %s\n"
- " : %s\n",
- OPENSSL_VERSION_TEXT,
- SSLeay_version(SSLEAY_VERSION),
- SSLeay_version(SSLEAY_BUILT_ON));
-/* third line is 38 characters for the %s and the line is 73 chars long;
-the OpenSSL output includes a "built on: " prefix already. */
+return string_fmt_append(g,
+ "Library version: OpenSSL: Compile: %s\n"
+ " Runtime: %s\n"
+ " : %s\n",
+ OPENSSL_VERSION_TEXT,
+ SSLeay_version(SSLEAY_VERSION),
+ SSLeay_version(SSLEAY_BUILT_ON));
+ /* third line is 38 characters for the %s and the line is 73 chars long;
+ the OpenSSL output includes a "built on: " prefix already. */
}