For connects and certificate-verifies denied by event actions, log
[exim.git] / src / src / tls-gnu.c
index b7eae17938710a88e925c97c9e351adc9b6ff041..04de02d7431d1f59d64a34719d1033fc6df1c58f 100644 (file)
@@ -47,9 +47,14 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
 # warning "GnuTLS library version too old; define DISABLE_OCSP in Makefile"
 # define DISABLE_OCSP
 #endif
-#if GNUTLS_VERSION_NUMBER < 0x020a00 && defined(EXPERIMENTAL_TPDA)
-# warning "GnuTLS library version too old; TPDA tls:cert event unsupported"
-# undef EXPERIMENTAL_TPDA
+#if GNUTLS_VERSION_NUMBER < 0x020a00 && defined(EXPERIMENTAL_EVENT)
+# warning "GnuTLS library version too old; tls:cert event unsupported"
+# undef EXPERIMENTAL_EVENT
+#endif
+#if GNUTLS_VERSION_NUMBER >= 0x030306
+# define SUPPORT_CA_DIR
+#else
+# undef  SUPPORT_CA_DIR
 #endif
 
 #ifndef DISABLE_OCSP
@@ -119,7 +124,7 @@ typedef struct exim_gnutls_state {
 #ifdef EXPERIMENTAL_CERTNAMES
   uschar *exp_tls_verify_cert_hostnames;
 #endif
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
   uschar *event_action;
 #endif
 
@@ -140,7 +145,7 @@ static const exim_gnutls_state_st exim_gnutls_state_init = {
 #ifdef EXPERIMENTAL_CERTNAMES
                                             NULL,
 #endif
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
                                             NULL,
 #endif
   NULL,
@@ -268,7 +273,7 @@ tls_error(const uschar *prefix, const char *msg, const host_item *host)
 {
 if (host)
   {
-  log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s)%s%s",
+  log_write(0, LOG_MAIN, "H=%s [%s] TLS error on connection (%s)%s%s",
       host->name, host->address, prefix, msg ? ": " : "", msg ? msg : "");
   return FAIL;
   }
@@ -277,6 +282,7 @@ else
   uschar *conn_info = smtp_get_connection_info();
   if (Ustrncmp(conn_info, US"SMTP ", 5) == 0)
     conn_info += 5;
+  /* I'd like to get separated H= here, but too hard for now */
   log_write(0, LOG_MAIN, "TLS error on %s (%s)%s%s",
       conn_info, prefix, msg ? ": " : "", msg ? msg : "");
   return DEFER;
@@ -884,6 +890,7 @@ if (Ustat(state->exp_tls_verify_certificates, &statbuf) < 0)
   return DEFER;
   }
 
+#ifndef SUPPORT_CA_DIR
 /* The test suite passes in /dev/null; we could check for that path explicitly,
 but who knows if someone has some weird FIFO which always dumps some certs, or
 other weirdness.  The thing we really want to check is that it's not a
@@ -899,6 +906,7 @@ if (S_ISDIR(statbuf.st_mode))
       state->exp_tls_verify_certificates);
   return DEFER;
   }
+#endif
 
 DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n",
         state->exp_tls_verify_certificates, statbuf.st_size);
@@ -910,8 +918,18 @@ if (statbuf.st_size == 0)
   return OK;
   }
 
-cert_count = gnutls_certificate_set_x509_trust_file(state->x509_cred,
+cert_count =
+
+#ifdef SUPPORT_CA_DIR
+  (statbuf.st_mode & S_IFMT) == S_IFDIR
+  ?
+  gnutls_certificate_set_x509_trust_dir(state->x509_cred,
+    CS state->exp_tls_verify_certificates, GNUTLS_X509_FMT_PEM)
+  :
+#endif
+  gnutls_certificate_set_x509_trust_file(state->x509_cred,
     CS state->exp_tls_verify_certificates, GNUTLS_X509_FMT_PEM);
+
 if (cert_count < 0)
   {
   rc = cert_count;
@@ -1524,23 +1542,24 @@ return 0;
 #endif
 
 
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
 /*
 We use this callback to get observability and detail-level control
-for an exim client TLS connection, raising a TPDA tls:cert event
-for each cert in the chain presented by the server.  Any event
+for an exim TLS connection (either direction), raising a tls:cert event
+for each cert in the chain presented by the peer.  Any event
 can deny verification.
 
 Return 0 for the handshake to continue or non-zero to terminate.
 */
 
 static int
-client_verify_cb(gnutls_session_t session)
+verify_cb(gnutls_session_t session)
 {
 const gnutls_datum * cert_list;
 unsigned int cert_list_size = 0;
 gnutls_x509_crt_t crt;
 int rc;
+uschar * yield;
 exim_gnutls_state_st * state = gnutls_session_get_ptr(session);
 
 cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
@@ -1556,11 +1575,12 @@ if (cert_list)
     }
 
   state->tlsp->peercert = crt;
-  if (tpda_raise_event(state->event_action,
-             US"tls:cert", string_sprintf("%d", cert_list_size)) == DEFER)
+  if ((yield = event_raise(state->event_action,
+             US"tls:cert", string_sprintf("%d", cert_list_size))))
     {
     log_write(0, LOG_MAIN,
-             "SSL verify denied by event-action: depth=%d", cert_list_size);
+             "SSL verify denied by event-action: depth=%d: %s",
+             cert_list_size, yield);
     return 1;                     /* reject */
     }
   state->tlsp->peercert = NULL;
@@ -1646,6 +1666,15 @@ else
   gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_IGNORE);
   }
 
+#ifdef EXPERIMENTAL_EVENT
+if (event_action)
+  {
+  state->event_action = event_action;
+  gnutls_session_set_ptr(state->session, state);
+  gnutls_certificate_set_verify_function(state->x509_cred, verify_cb);
+  }
+#endif
+
 /* Register SNI handling; always, even if not in tls_certificate, so that the
 expansion variable $tls_sni is always available. */
 
@@ -1761,7 +1790,11 @@ Returns:            OK/DEFER/FAIL (because using common functions),
 int
 tls_client_start(int fd, host_item *host,
     address_item *addr ARG_UNUSED,
-    transport_instance *tb)
+    transport_instance *tb
+#ifdef EXPERIMENTAL_DANE
+    , dne_answer * unused_tlsa_dnsa
+#endif
+    )
 {
 smtp_transport_options_block *ob =
   (smtp_transport_options_block *)tb->options_block;
@@ -1863,12 +1896,12 @@ if (request_ocsp)
   }
 #endif
 
-#ifdef EXPERIMENTAL_TPDA
-if (tb->tpda_event_action)
+#ifdef EXPERIMENTAL_EVENT
+if (tb->event_action)
   {
-  state->event_action = tb->tpda_event_action;
+  state->event_action = tb->event_action;
   gnutls_session_set_ptr(state->session, state);
-  gnutls_certificate_set_verify_function(state->x509_cred, client_verify_cb);
+  gnutls_certificate_set_verify_function(state->x509_cred, verify_cb);
   }
 #endif