* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2013 */
+/* Copyright (c) University of Cambridge 1995 - 2014 */
/* See the file NOTICE for conditions of use and distribution. */
/* Portions Copyright (c) The OpenSSL Project 1999 */
OCSP_RESPONSE *response;
} server;
struct {
- X509_STORE *verify_store;
+ X509_STORE *verify_store; /* non-null if status requested */
+ BOOL verify_required;
} client;
} u_ocsp;
#endif
txt);
tlsp->certificate_verified = FALSE;
*calledp = TRUE;
- if (!*optionalp) return 0; /* reject */
+ if (!*optionalp)
+ {
+ tlsp->peercert = X509_dup(x509ctx->current_cert);
+ return 0; /* reject */
+ }
DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
"tls_try_verify_hosts)\n");
return 1; /* accept */
DEBUG(D_tls) debug_printf("SSL%s peer: %s\n",
*calledp ? "" : " authenticated", txt);
tlsp->peerdn = txt;
+ tlsp->peercert = X509_dup(x509ctx->current_cert);
}
/*XXX JGH: this looks bogus - we set "verified" first time through, which
}
supply_response:
-cbinfo->u_ocsp.server.response = resp;
+ cbinfo->u_ocsp.server.response = resp;
return;
bad:
-if (running_in_test_harness)
- {
- extern char ** environ;
- uschar ** p;
- 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");
- goto supply_response;
- }
- }
+ if (running_in_test_harness)
+ {
+ extern char ** environ;
+ uschar ** p;
+ 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");
+ goto supply_response;
+ }
+ }
return;
}
#endif /*EXPERIMENTAL_OCSP*/
DEBUG(D_tls) debug_printf("Received TLS status request (OCSP stapling); %s response.",
cbinfo->u_ocsp.server.response ? "have" : "lack");
+tls_in.ocsp = OCSP_NOT_RESP;
if (!cbinfo->u_ocsp.server.response)
return SSL_TLSEXT_ERR_NOACK;
response_der = NULL;
-response_der_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response, &response_der);
+response_der_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response,
+ &response_der);
if (response_der_len <= 0)
return SSL_TLSEXT_ERR_NOACK;
SSL_set_tlsext_status_ocsp_resp(server_ssl, response_der, response_der_len);
+tls_in.ocsp = OCSP_VFIED;
return SSL_TLSEXT_ERR_OK;
}
len = SSL_get_tlsext_status_ocsp_resp(s, &p);
if(!p)
{
- if (log_extra_selector & LX_tls_cipher)
- log_write(0, LOG_MAIN, "Received TLS status response, null content");
+ /* Expect this when we requested ocsp but got none */
+ if ( cbinfo->u_ocsp.client.verify_required
+ && log_extra_selector & LX_tls_cipher)
+ log_write(0, LOG_MAIN, "Received TLS status callback, null content");
else
DEBUG(D_tls) debug_printf(" null\n");
- return 0; /* This is the fail case for require-ocsp; none from server */
+ return cbinfo->u_ocsp.client.verify_required ? 0 : 1;
}
+
if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
{
+ tls_out.ocsp = OCSP_FAILED;
if (log_extra_selector & LX_tls_cipher)
log_write(0, LOG_MAIN, "Received TLS status response, parse error");
else
if(!(bs = OCSP_response_get1_basic(rsp)))
{
+ tls_out.ocsp = OCSP_FAILED;
if (log_extra_selector & LX_tls_cipher)
log_write(0, LOG_MAIN, "Received TLS status response, error parsing response");
else
/* We'd check the nonce here if we'd put one in the request. */
/* However that would defeat cacheability on the server so we don't. */
-
/* This section of code reworked from OpenSSL apps source;
The OpenSSL Project retains copyright:
Copyright (c) 1999 The OpenSSL Project. All rights reserved.
*/
{
BIO * bp = NULL;
- OCSP_CERTID *id;
int status, reason;
ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
/* Use the chain that verified the server cert to verify the stapled info */
/* DEBUG(D_tls) x509_store_dump_cert_s_names(cbinfo->u_ocsp.client.verify_store); */
- if ((i = OCSP_basic_verify(bs, NULL, cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
+ if ((i = OCSP_basic_verify(bs, NULL,
+ cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
{
+ tls_out.ocsp = OCSP_FAILED;
BIO_printf(bp, "OCSP response verify failure\n");
ERR_print_errors(bp);
- i = 0;
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
goto out;
}
if (sk_OCSP_SINGLERESP_num(sresp) != 1)
{
- log_write(0, LOG_MAIN, "OCSP stapling with multiple responses not handled");
+ 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;
}
single = OCSP_resp_get0(bs, 0);
- status = OCSP_single_get0_status(single, &reason, &rev, &thisupd, &nextupd);
+ status = OCSP_single_get0_status(single, &reason, &rev,
+ &thisupd, &nextupd);
}
- i = 0;
DEBUG(D_tls) time_print(bp, "This OCSP Update", thisupd);
DEBUG(D_tls) if(nextupd) time_print(bp, "Next OCSP Update", nextupd);
- if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
+ 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");
- goto out;
+ i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
}
-
- DEBUG(D_tls) BIO_printf(bp, "Certificate status: %s\n", OCSP_cert_status_str(status));
- switch(status)
+ else
{
- case V_OCSP_CERTSTATUS_GOOD:
- i = 1;
- break;
- case V_OCSP_CERTSTATUS_REVOKED:
- 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 = 0;
- break;
- default:
- log_write(0, LOG_MAIN, "Server certificate status unknown, in OCSP stapling");
- i = 0;
- break;
+ DEBUG(D_tls) BIO_printf(bp, "Certificate status: %s\n",
+ OCSP_cert_status_str(status));
+ switch(status)
+ {
+ case V_OCSP_CERTSTATUS_GOOD:
+ tls_out.ocsp = OCSP_VFIED;
+ i = 1;
+ break;
+ 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:
BIO_free(bp);
debug_printf("Shared ciphers: %s\n", buf);
}
+/* Record the certificate we presented */
+ {
+ X509 * crt = SSL_get_certificate(server_ssl);
+ tls_in.ourcert = crt ? X509_dup(crt) : NULL;
+ }
/* Only used by the server-side tls (tls_in), including tls_getc.
Client-side (tls_out) reads (seem to?) go via
fd the fd of the connection
host connected host (for messages)
addr the first address
- certificate certificate file
- privatekey private key file
- sni TLS SNI to send to remote host
- 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
- verify_hosts mandatory client verification
- try_verify_hosts optional client verification
+ ob smtp transport options
Returns: OK on success
FAIL otherwise - note that tls_error() will not give DEFER
int
tls_client_start(int fd, host_item *host, address_item *addr,
- uschar *certificate, uschar *privatekey, uschar *sni,
- uschar *verify_certs, uschar *crl,
- uschar *require_ciphers,
-#ifdef EXPERIMENTAL_OCSP
- uschar *hosts_require_ocsp,
-#endif
- int dh_min_bits ARG_UNUSED, int timeout,
- uschar *verify_hosts, uschar *try_verify_hosts)
+ void *v_ob)
{
+smtp_transport_options_block * ob = v_ob;
static uschar txt[256];
uschar *expciphers;
X509* server_cert;
int rc;
static uschar cipherbuf[256];
#ifdef EXPERIMENTAL_OCSP
-BOOL require_ocsp = verify_check_this_host(&hosts_require_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
+ : verify_check_this_host(&ob->hosts_request_ocsp,
+ NULL, host->name, host->address, NULL) == OK;
#endif
-rc = tls_init(&client_ctx, host, NULL, certificate, privatekey,
+rc = tls_init(&client_ctx, host, NULL,
+ ob->tls_certificate, ob->tls_privatekey,
#ifdef EXPERIMENTAL_OCSP
- require_ocsp ? US"" : NULL,
+ (void *)(long)request_ocsp,
#endif
addr, &client_static_cbinfo);
if (rc != OK) return rc;
tls_out.certificate_verified = FALSE;
client_verify_callback_called = FALSE;
-if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers))
+if (!expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
+ &expciphers))
return FAIL;
/* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
}
/* 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
+ 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 (((verify_hosts == NULL) && (try_verify_hosts == NULL)) ||
- (verify_check_host(&verify_hosts) == OK))
+if ((!ob->tls_verify_hosts && !ob->tls_try_verify_hosts) ||
+ (verify_check_host(&ob->tls_verify_hosts) == OK))
{
- rc = setup_certs(client_ctx, verify_certs, crl, host, FALSE, verify_callback_client);
- if (rc != OK) return rc;
+ if ((rc = setup_certs(client_ctx, ob->tls_verify_certificates,
+ ob->tls_crl, host, FALSE, verify_callback_client)) != OK)
+ return rc;
client_verify_optional = FALSE;
}
-else if (verify_check_host(&try_verify_hosts) == OK)
+else if (verify_check_host(&ob->tls_try_verify_hosts) == OK)
{
- rc = setup_certs(client_ctx, verify_certs, crl, host, TRUE, verify_callback_client);
- if (rc != OK) return rc;
+ if ((rc = setup_certs(client_ctx, ob->tls_verify_certificates,
+ ob->tls_crl, host, TRUE, verify_callback_client)) != OK)
+ return rc;
client_verify_optional = TRUE;
}
-if ((client_ssl = SSL_new(client_ctx)) == NULL) return tls_error(US"SSL_new", host, NULL);
+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 (ob->tls_sni)
{
- if (!expand_check(sni, US"tls_sni", &tls_out.sni))
+ if (!expand_check(ob->tls_sni, US"tls_sni", &tls_out.sni))
return FAIL;
if (tls_out.sni == NULL)
{
#ifdef EXPERIMENTAL_OCSP
/* Request certificate status at connection-time. If the server
does OCSP stapling we will get the callback (set in tls_init()) */
-if (require_ocsp)
+if (request_ocsp)
+ {
SSL_set_tlsext_status_type(client_ssl, TLSEXT_STATUSTYPE_ocsp);
+ client_static_cbinfo->u_ocsp.client.verify_required = require_ocsp;
+ tls_out.ocsp = OCSP_NOT_RESP;
+ }
#endif
/* There doesn't seem to be a built-in timeout on connection. */
DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
sigalrm_seen = FALSE;
-alarm(timeout);
+alarm(ob->command_timeout);
rc = SSL_connect(client_ssl);
alarm(0);
DEBUG(D_tls) debug_printf("SSL_connect succeeded\n");
/* Beware anonymous ciphers which lead to server_cert being NULL */
+/*XXX server_cert is never freed... use X509_free() */
server_cert = SSL_get_peer_certificate (client_ssl);
if (server_cert)
{
tls_out.peerdn = US X509_NAME_oneline(X509_get_subject_name(server_cert),
CS txt, sizeof(txt));
- tls_out.peerdn = txt;
+ tls_out.peerdn = txt; /*XXX a static buffer... */
}
else
tls_out.peerdn = NULL;
construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits);
tls_out.cipher = cipherbuf;
+/* Record the certificate we presented */
+ {
+ X509 * crt = SSL_get_certificate(client_ssl);
+ tls_out.ourcert = crt ? X509_dup(crt) : NULL;
+ }
+
tls_out.active = fd;
return OK;
}
it can result in serious failures, including crashing with a SIGSEGV. So
report the version found by the compiler and the run-time version.
+Note: some OS vendors backport security fixes without changing the version
+number/string, and the version date remains unchanged. The _build_ date
+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
*/
tls_version_report(FILE *f)
{
fprintf(f, "Library version: OpenSSL: Compile: %s\n"
- " Runtime: %s\n",
+ " Runtime: %s\n"
+ " : %s\n",
OPENSSL_VERSION_TEXT,
- SSLeay_version(SSLEAY_VERSION));
+ 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 TRUE;
}
+/* vi: aw ai sw=2
+*/
/* End of tls-openssl.c */