#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
-#ifdef EXPERIMENTAL_OCSP
-#include <openssl/ocsp.h>
+#ifndef DISABLE_OCSP
+# include <openssl/ocsp.h>
#endif
-#ifdef EXPERIMENTAL_OCSP
-#define EXIM_OCSP_SKEW_SECONDS (300L)
-#define EXIM_OCSP_MAX_AGE (-1L)
+#ifndef DISABLE_OCSP
+# define EXIM_OCSP_SKEW_SECONDS (300L)
+# define EXIM_OCSP_MAX_AGE (-1L)
#endif
#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
-#define EXIM_HAVE_OPENSSL_TLSEXT
+# define EXIM_HAVE_OPENSSL_TLSEXT
+#endif
+
+#if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP)
+# warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
+# define DISABLE_OCSP
#endif
/* Structure for collecting random data for seeding. */
typedef struct tls_ext_ctx_cb {
uschar *certificate;
uschar *privatekey;
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
BOOL is_server;
union {
struct {
uschar *server_cipher_list;
/* only passed down to tls_error: */
host_item *host;
+
+#ifdef EXPERIMENTAL_CERTNAMES
+ uschar * verify_cert_hostnames;
+#endif
} tls_ext_ctx_cb;
/* should figure out a cleanup of API to handle state preserved per
#ifdef EXIM_HAVE_OPENSSL_TLSEXT
static int tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg);
#endif
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
static int tls_server_stapling_cb(SSL *s, void *arg);
#endif
/* Extreme debug
-#if defined(EXPERIMENTAL_OCSP)
+#ifndef DISABLE_OCSP
void
x509_store_dump_cert_s_names(X509_STORE * store)
{
X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
static uschar txt[256];
-X509_NAME_oneline(X509_get_subject_name(cert),
- CS txt, sizeof(txt));
+X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt));
if (state == 0)
{
{
DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n",
X509_STORE_CTX_get_error_depth(x509ctx), txt);
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
if (tlsp == &tls_out && client_static_cbinfo->u_ocsp.client.verify_store)
{ /* client, wanting stapling */
/* Add the server cert's signing chain as the one
}
else
{
+#ifdef EXPERIMENTAL_CERTNAMES
+ uschar * verify_cert_hostnames;
+#endif
+
tlsp->peerdn = txt;
tlsp->peercert = X509_dup(cert);
+
+#ifdef EXPERIMENTAL_CERTNAMES
+ if ( tlsp == &tls_out
+ && ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames)))
+ /* client, wanting hostname check */
+
+# if OPENSSL_VERSION_NUMBER >= 0x010100000L || OPENSSL_VERSION_NUMBER >= 0x010002000L
+# ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
+# define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0
+# endif
+ {
+ int sep = 0;
+ uschar * list = verify_cert_hostnames;
+ uschar * name;
+ int rc;
+ while ((name = string_nextinlist(&list, &sep, NULL, 0)))
+ if ((rc = X509_check_host(cert, name, 0,
+ X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS)))
+ {
+ if (rc < 0)
+ {
+ log_write(0, LOG_MAIN, "SSL verify error: internal error\n");
+ name = NULL;
+ }
+ break;
+ }
+ if (!name)
+ {
+ log_write(0, LOG_MAIN,
+ "SSL verify error: certificate name mismatch: \"%s\"\n", txt);
+ return 0; /* reject */
+ }
+ }
+# else
+ if (!tls_is_name_for_cert(verify_cert_hostnames, cert))
+ {
+ log_write(0, LOG_MAIN,
+ "SSL verify error: certificate name mismatch: \"%s\"\n", txt);
+ return 0; /* reject */
+ }
+# endif
+#endif
+
DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n",
*calledp ? "" : " authenticated", txt);
if (!*calledp) tlsp->certificate_verified = TRUE;
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
/*************************************************
* Load OCSP information into state *
*************************************************/
}
return;
}
-#endif /*EXPERIMENTAL_OCSP*/
+#endif /*!DISABLE_OCSP*/
"SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL);
}
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
if (cbinfo->is_server && cbinfo->u_ocsp.server.file != NULL)
{
if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded))
SSL_CTX_set_tlsext_servername_arg(server_sni, cbinfo);
if (cbinfo->server_cipher_list)
SSL_CTX_set_cipher_list(server_sni, CS cbinfo->server_cipher_list);
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
if (cbinfo->u_ocsp.server.file)
{
SSL_CTX_set_tlsext_status_cb(server_sni, tls_server_stapling_cb);
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
/*************************************************
* Callback to handle OCSP Stapling *
OCSP_RESPONSE_free(rsp);
return i;
}
-#endif /*EXPERIMENTAL_OCSP*/
+#endif /*!DISABLE_OCSP*/
* Initialize for TLS *
*************************************************/
-/* Called from both server and client code, to do preliminary initialization of
-the library.
+/* Called from both server and client code, to do preliminary initialization
+of the library. We allocate and return a context structure.
Arguments:
host connected host, if client; NULL if server
privatekey private key
ocsp_file file of stapling info (server); flag for require ocsp (client)
addr address if client; NULL if server (for some randomness)
+ cbp place to put allocated context
Returns: OK/DEFER/FAIL
*/
static int
tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate,
uschar *privatekey,
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
uschar *ocsp_file,
#endif
address_item *addr, tls_ext_ctx_cb ** cbp)
cbinfo = store_malloc(sizeof(tls_ext_ctx_cb));
cbinfo->certificate = certificate;
cbinfo->privatekey = privatekey;
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
if ((cbinfo->is_server = host==NULL))
{
cbinfo->u_ocsp.server.file = ocsp_file;
#ifdef EXIM_HAVE_OPENSSL_TLSEXT
if (host == NULL) /* server */
{
-# ifdef EXPERIMENTAL_OCSP
+# ifndef DISABLE_OCSP
/* We check u_ocsp.server.file, not server.response, because we care about if
the option exists, not what the current expansion might be, as SNI might
change the certificate and OCSP file in use between now and the time the
SSL_CTX_set_tlsext_servername_callback(*ctxp, tls_servername_cb);
SSL_CTX_set_tlsext_servername_arg(*ctxp, cbinfo);
}
-# ifdef EXPERIMENTAL_OCSP
+# ifndef DISABLE_OCSP
else /* client */
if(ocsp_file) /* wanting stapling */
{
# endif
#endif
+#ifdef EXPERIMENTAL_CERTNAMES
+cbinfo->verify_cert_hostnames = NULL;
+#endif
+
/* Set up the RSA callback */
SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback);
the error. */
rc = tls_init(&server_ctx, NULL, tls_dhparam, tls_certificate, tls_privatekey,
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
tls_ocsp_file,
#endif
NULL, &server_static_cbinfo);
X509* server_cert;
int rc;
static uschar cipherbuf[256];
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
BOOL require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp,
NULL, host->name, host->address, NULL) == OK;
BOOL request_ocsp = require_ocsp ? TRUE
rc = tls_init(&client_ctx, host, NULL,
ob->tls_certificate, ob->tls_privatekey,
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
(void *)(long)request_ocsp,
#endif
addr, &client_static_cbinfo);
/* stick to the old behaviour for compatibility if tls_verify_certificates is
set but both tls_verify_hosts and tls_try_verify_hosts is not set. Check only
the specified host patterns if one of them is defined */
+
if ((!ob->tls_verify_hosts && !ob->tls_try_verify_hosts) ||
(verify_check_host(&ob->tls_verify_hosts) == OK))
{
ob->tls_crl, host, FALSE, verify_callback_client)) != OK)
return rc;
client_verify_optional = FALSE;
+
+#ifdef EXPERIMENTAL_CERTNAMES
+ if (ob->tls_verify_cert_hostnames)
+ {
+ if (!expand_check(ob->tls_verify_cert_hostnames,
+ US"tls_verify_cert_hostnames",
+ &client_static_cbinfo->verify_cert_hostnames))
+ return FAIL;
+ if (client_static_cbinfo->verify_cert_hostnames)
+ DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n",
+ client_static_cbinfo->verify_cert_hostnames);
+ }
+#endif
}
else if (verify_check_host(&ob->tls_try_verify_hosts) == OK)
{
}
}
-#ifdef EXPERIMENTAL_OCSP
+#ifndef DISABLE_OCSP
/* Request certificate status at connection-time. If the server
does OCSP stapling we will get the callback (set in tls_init()) */
if (request_ocsp)