OCSP observability: variables $tls_{in,out}_ocsp
[exim.git] / src / src / tls-openssl.c
index b273fff75fcccc5551a0a2d80927bcbc96f5a062..fd257f3c6aaacb5e64aea6584778718ecabb0972 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2012 */
+/* 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);
@@ -1137,37 +1159,9 @@ construct_cipher_name(SSL *ssl, uschar *cipherbuf, int bsize, int *bits)
 yet reflect that.  It should be a safe change anyway, even 0.9.8 versions have
 the accessor functions use const in the prototype. */
 const SSL_CIPHER *c;
-uschar *ver;
-
-switch (ssl->session->ssl_version)
-  {
-  case SSL2_VERSION:
-  ver = US"SSLv2";
-  break;
+const uschar *ver;
 
-  case SSL3_VERSION:
-  ver = US"SSLv3";
-  break;
-
-  case TLS1_VERSION:
-  ver = US"TLSv1";
-  break;
-
-#ifdef TLS1_1_VERSION
-  case TLS1_1_VERSION:
-  ver = US"TLSv1.1";
-  break;
-#endif
-
-#ifdef TLS1_2_VERSION
-  case TLS1_2_VERSION:
-  ver = US"TLSv1.2";
-  break;
-#endif
-
-  default:
-  ver = US"UNKNOWN";
-  }
+ver = (const uschar *)SSL_get_version(ssl);
 
 c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
 SSL_CIPHER_get_bits(c, bits);
@@ -1461,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
@@ -1495,15 +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
+  ob               smtp transport options
 
 Returns:           OK on success
                    FAIL otherwise - note that tls_error() will not give DEFER
@@ -1512,27 +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)
+  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;
@@ -1540,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
@@ -1556,17 +1547,34 @@ if (expciphers != NULL)
     return tls_error(US"SSL_CTX_set_cipher_list", host, NULL);
   }
 
-rc = setup_certs(client_ctx, verify_certs, crl, host, FALSE, verify_callback_client);
-if (rc != OK) return rc;
+/* 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))
+  {
+  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(&ob->tls_try_verify_hosts) == OK)
+  {
+  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)
     {
@@ -1590,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);
 
@@ -1608,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;
@@ -1621,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;
 }
@@ -1934,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
 */
@@ -1942,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. */
 }
 
 
@@ -2252,4 +2280,6 @@ for (s=option_spec; *s != '\0'; /**/)
 return TRUE;
 }
 
+/* vi: aw ai sw=2
+*/
 /* End of tls-openssl.c */