* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
/* See the file NOTICE for conditions of use and distribution. */
/* Portions Copyright (c) The OpenSSL Project 1999 */
#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
# define EXIM_HAVE_OPENSSL_TLSEXT
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L
+# define EXIM_HAVE_RSA_GENKEY_EX
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+# define EXIM_HAVE_OCSP_RESP_COUNT
+#else
+# define EXIM_HAVE_EPHEM_RSA_KEX
+# define EXIM_HAVE_RAND_PSEUDO
+#endif
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+# define EXIM_HAVE_SHA256
+#endif
/*
* X509_check_host provides sane certificate hostname checking, but was added
&& (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L
# define EXIM_HAVE_OPENSSL_CHECKHOST
# endif
+#endif
+#if !defined(LIBRESSL_VERSION_NUMBER) \
+ || LIBRESSL_VERSION_NUMBER >= 0x20010000L
# if !defined(OPENSSL_NO_ECDH)
# if OPENSSL_VERSION_NUMBER >= 0x0090800fL
# define EXIM_HAVE_ECDH
# endif
# if OPENSSL_VERSION_NUMBER >= 0x10002000L
-# define EXIM_HAVE_OPENSSL_ECDH_AUTO
+# if OPENSSL_VERSION_NUMBER < 0x10100000L
+# define EXIM_HAVE_OPENSSL_ECDH_AUTO
+# endif
# define EXIM_HAVE_OPENSSL_EC_NIST2NID
# endif
# endif
*/
static int
-tls_error(uschar * prefix, const host_item * host, uschar * msg)
+tls_error(uschar * prefix, const host_item * host, uschar * msg)
{
if (!msg)
{
+#ifdef EXIM_HAVE_EPHEM_RSA_KEX
/*************************************************
* Callback to generate RSA key *
*************************************************/
rsa_callback(SSL *s, int export, int keylength)
{
RSA *rsa_key;
+#ifdef EXIM_HAVE_RSA_GENKEY_EX
+BIGNUM *bn = BN_new();
+#endif
+
export = export; /* Shut picky compilers up */
DEBUG(D_tls) debug_printf("Generating %d bit RSA key...\n", keylength);
-rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL);
-if (rsa_key == NULL)
+
+#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
+if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL)))
+#endif
+
{
ERR_error_string(ERR_get_error(), ssl_errstring);
log_write(0, LOG_MAIN|LOG_PANIC, "TLS error (RSA_generate_key): %s",
}
return rsa_key;
}
+#endif
{
extern char ** environ;
uschar ** p;
- for (p = USS environ; *p != NULL; p++)
+ if (environ) for (p = USS environ; *p != NULL; p++)
if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0)
{
DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n");
+/* Create and install a selfsigned certificate, for use in server mode */
+
+static int
+tls_install_selfsign(SSL_CTX * sctx)
+{
+X509 * x509 = NULL;
+EVP_PKEY * pkey;
+RSA * rsa;
+X509_NAME * name;
+uschar * where;
+
+where = US"allocating pkey";
+if (!(pkey = EVP_PKEY_new()))
+ goto err;
+
+where = US"allocating cert";
+if (!(x509 = X509_new()))
+ goto err;
+
+where = US"generating pkey";
+ /* deprecated, use RSA_generate_key_ex() */
+if (!(rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL)))
+ goto err;
+
+where = US"assiging pkey";
+if (!EVP_PKEY_assign_RSA(pkey, rsa))
+ goto err;
+
+X509_set_version(x509, 2); /* N+1 - version 3 */
+ASN1_INTEGER_set(X509_get_serialNumber(x509), 0);
+X509_gmtime_adj(X509_get_notBefore(x509), 0);
+X509_gmtime_adj(X509_get_notAfter(x509), (long)60 * 60); /* 1 hour */
+X509_set_pubkey(x509, pkey);
+
+name = X509_get_subject_name(x509);
+X509_NAME_add_entry_by_txt(name, "C",
+ MBSTRING_ASC, "UK", -1, -1, 0);
+X509_NAME_add_entry_by_txt(name, "O",
+ MBSTRING_ASC, "Exim Developers", -1, -1, 0);
+X509_NAME_add_entry_by_txt(name, "CN",
+ MBSTRING_ASC, CS smtp_active_hostname, -1, -1, 0);
+X509_set_issuer_name(x509, name);
+
+where = US"signing cert";
+if (!X509_sign(x509, pkey, EVP_md5()))
+ goto err;
+
+where = US"installing selfsign cert";
+if (!SSL_CTX_use_certificate(sctx, x509))
+ goto err;
+
+where = US"installing selfsign key";
+if (!SSL_CTX_use_PrivateKey(sctx, pkey))
+ goto err;
+
+return OK;
+
+err:
+ (void) tls_error(where, NULL, NULL);
+ if (x509) X509_free(x509);
+ if (pkey) EVP_PKEY_free(pkey);
+ return DEFER;
+}
+
+
+
+
/*************************************************
* Expand key and cert file specs *
*************************************************/
{
uschar *expanded;
-if (cbinfo->certificate == NULL)
- return OK;
-
-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))
- return DEFER;
-
-if (expanded != NULL)
+if (!cbinfo->certificate)
{
- DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded);
- if (!SSL_CTX_use_certificate_chain_file(sctx, CS expanded))
- return tls_error(string_sprintf(
- "SSL_CTX_use_certificate_chain_file file=%s", expanded),
- cbinfo->host, NULL);
+ if (cbinfo->host) /* client */
+ return OK;
+ /* server */
+ if (tls_install_selfsign(sctx) != OK)
+ return DEFER;
}
+else
+ {
+ 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 (cbinfo->privatekey != NULL &&
- !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded))
- return DEFER;
+ if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded))
+ return DEFER;
-/* If expansion was forced to fail, key_expanded will be NULL. If the result
-of the expansion is an empty string, ignore it also, and assume the private
-key is in the same file as the certificate. */
+ if (expanded != NULL)
+ {
+ DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded);
+ if (!SSL_CTX_use_certificate_chain_file(sctx, CS expanded))
+ return tls_error(string_sprintf(
+ "SSL_CTX_use_certificate_chain_file file=%s", expanded),
+ cbinfo->host, NULL);
+ }
-if (expanded && *expanded)
- {
- DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded);
- if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM))
- return tls_error(string_sprintf(
- "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL);
+ if (cbinfo->privatekey != NULL &&
+ !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded))
+ return DEFER;
+
+ /* If expansion was forced to fail, key_expanded will be NULL. If the result
+ of the expansion is an empty string, ignore it also, and assume the private
+ key is in the same file as the certificate. */
+
+ if (expanded && *expanded)
+ {
+ DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded);
+ if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM))
+ return tls_error(string_sprintf(
+ "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL);
+ }
}
#ifndef DISABLE_OCSP
log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable");
BIO_printf(bp, "OCSP response verify failure\n");
ERR_print_errors(bp);
- i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
- goto out;
+ goto failed;
}
BIO_printf(bp, "OCSP response well-formed and signed OK\n");
+ /*XXX So we have a good stapled OCSP status. How do we know
+ it is for the cert of interest? OpenSSL 1.1.0 has a routine
+ OCSP_resp_find_status() which matches on a cert id, which presumably
+ we should use. Making an id needs OCSP_cert_id_new(), which takes
+ issuerName, issuerKey, serialNumber. Are they all in the cert?
+
+ For now, carry on blindly accepting the resp. */
+
{
- STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
OCSP_SINGLERESP * single;
+#ifdef EXIM_HAVE_OCSP_RESP_COUNT
+ if (OCSP_resp_count(bs) != 1)
+#else
+ STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
if (sk_OCSP_SINGLERESP_num(sresp) != 1)
+#endif
{
tls_out.ocsp = OCSP_FAILED;
log_write(0, LOG_MAIN, "OCSP stapling "
"with multiple responses not handled");
- i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
- goto out;
+ goto failed;
}
single = OCSP_resp_get0(bs, 0);
status = OCSP_single_get0_status(single, &reason, &rev,
tls_out.ocsp = OCSP_FAILED;
DEBUG(D_tls) ERR_print_errors(bp);
log_write(0, LOG_MAIN, "Server OSCP dates invalid");
- i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
}
else
{
case V_OCSP_CERTSTATUS_GOOD:
tls_out.ocsp = OCSP_VFIED;
i = 1;
- break;
+ goto good;
case V_OCSP_CERTSTATUS_REVOKED:
tls_out.ocsp = OCSP_FAILED;
log_write(0, LOG_MAIN, "Server certificate revoked%s%s",
reason != -1 ? "; reason: " : "",
reason != -1 ? OCSP_crl_reason_str(reason) : "");
DEBUG(D_tls) time_print(bp, "Revocation Time", rev);
- i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
break;
default:
tls_out.ocsp = OCSP_FAILED;
log_write(0, LOG_MAIN,
"Server certificate status unknown, in OCSP stapling");
- i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
break;
}
}
- out:
+ failed:
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+ good:
BIO_free(bp);
}
SSL_load_error_strings(); /* basic set up */
OpenSSL_add_ssl_algorithms();
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+#ifdef EXIM_HAVE_SHA256
/* SHA256 is becoming ever more popular. This makes sure it gets added to the
list of available digests. */
EVP_add_digest(EVP_sha256());
By disabling with openssl_options, we can let admins re-enable with the
existing knob. */
-*ctxp = SSL_CTX_new((host == NULL)?
- SSLv23_server_method() : SSLv23_client_method());
+*ctxp = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method());
-if (*ctxp == NULL) return tls_error(US"SSL_CTX_new", host, NULL);
+if (!*ctxp) 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 certificate and key (and perhaps OCSP info) */
-rc = tls_expand_session_files(*ctxp, cbinfo);
-if (rc != OK) return rc;
+if ((rc = tls_expand_session_files(*ctxp, cbinfo)) != OK)
+ return rc;
/* If we need to handle SNI, do so */
#ifdef EXIM_HAVE_OPENSSL_TLSEXT
cbinfo->verify_cert_hostnames = NULL;
+#ifdef EXIM_HAVE_EPHEM_RSA_KEX
/* Set up the RSA callback */
-
SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback);
+#endif
/* Finally, set the timeout, and we are done */
switch (DANESSL_add_tlsa(ssl, usage, selector, mdname, p, rr->size - 3))
{
default:
- case 0: /* action not taken */
return tls_error(US"tlsa load", host, NULL);
+ case 0: /* action not taken */
case 1: break;
}
switch (error)
{
case SSL_ERROR_SSL:
- ERR_error_string(ERR_get_error(), ssl_errstring);
- log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
- return -1;
+ ERR_error_string(ERR_get_error(), 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;
+ left -= outbytes;
+ buff += outbytes;
+ break;
case SSL_ERROR_ZERO_RETURN:
- log_write(0, LOG_MAIN, "SSL channel closed on write");
- return -1;
+ 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));
+ log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s",
+ sender_fullhost ? sender_fullhost : US"<unknown>",
+ strerror(errno));
+ return -1;
default:
- log_write(0, LOG_MAIN, "SSL_write error %d", error);
- return -1;
+ log_write(0, LOG_MAIN, "SSL_write error %d", error);
+ return -1;
}
}
return len;
if (i < needed_len)
needed_len = i;
+#ifdef EXIM_HAVE_RAND_PSEUDO
/* We do not care if crypto-strong */
i = RAND_pseudo_bytes(smallbuf, needed_len);
+#else
+i = RAND_bytes(smallbuf, needed_len);
+#endif
+
if (i < 0)
{
DEBUG(D_all)
keep_c = *end;
*end = '\0';
item_parsed = tls_openssl_one_option_parse(s, &item);
+ *end = keep_c;
if (!item_parsed)
{
DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"\n", s);
result |= item;
else
result &= ~item;
- *end = keep_c;
s = end;
}