X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Ftls-openssl.c;h=fd257f3c6aaacb5e64aea6584778718ecabb0972;hb=4466248715466b6f251454283642b74de65e9d9a;hp=a64f85dea25afe91caf16515b9e0350ba0db4fba;hpb=d9784128d573baed950d9b0c6aef1c864c33b4b4;p=exim.git diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index a64f85dea..fd257f3c6 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2,7 +2,7 @@ * 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 */ @@ -97,7 +97,8 @@ typedef struct tls_ext_ctx_cb { 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 @@ -276,7 +277,11 @@ if (state == 0) 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 */ @@ -303,6 +308,7 @@ else 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 @@ -792,15 +798,18 @@ else 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; } @@ -827,12 +836,15 @@ DEBUG(D_tls) debug_printf("Received TLS status response (OCSP stapling):"); 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; } +tls_out.ocsp = OCSP_NOT_VFY; if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) { if (log_extra_selector & LX_tls_cipher) @@ -873,11 +885,12 @@ if(!(bs = OCSP_response_get1_basic(rsp))) /* 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) { 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; } @@ -889,39 +902,48 @@ if(!(bs = OCSP_response_get1_basic(rsp))) if (sk_OCSP_SINGLERESP_num(sresp) != 1) { - log_write(0, LOG_MAIN, "OCSP stapling with multiple responses not handled"); + 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)) { 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: + i = 1; + tls_out.ocsp = OCSP_VFIED; + 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 = cbinfo->u_ocsp.client.verify_required ? 0 : 1; + break; + default: + 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); @@ -1433,6 +1455,11 @@ DEBUG(D_tls) 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 @@ -1467,17 +1494,7 @@ Argument: 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 @@ -1486,28 +1503,26 @@ Returns: OK on success 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; @@ -1515,7 +1530,8 @@ 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 @@ -1532,30 +1548,33 @@ if (expciphers != NULL) } /* 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) { @@ -1579,15 +1598,19 @@ if (sni) #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); @@ -1597,12 +1620,13 @@ if (rc <= 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; @@ -1610,6 +1634,12 @@ else 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; } @@ -1923,6 +1953,11 @@ one version of OpenSSL but the run-time linker picks up another version, 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 */ @@ -1931,9 +1966,13 @@ void 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. */ } @@ -2241,4 +2280,6 @@ for (s=option_spec; *s != '\0'; /**/) return TRUE; } +/* vi: aw ai sw=2 +*/ /* End of tls-openssl.c */