Expanded EXPERIMENTAL_TPDA feature
[exim.git] / src / src / tls-openssl.c
index e562a8926d7b43792b892cadbd569403db6f6612..c031b8e4d10f0013387d4df1efc80acef493d76a 100644 (file)
@@ -116,6 +116,9 @@ typedef struct tls_ext_ctx_cb {
 #ifdef EXPERIMENTAL_CERTNAMES
   uschar * verify_cert_hostnames;
 #endif
+#ifdef EXPERIMENTAL_TPDA
+  uschar * event_action;
+#endif
 } tls_ext_ctx_cb;
 
 /* should figure out a cleanup of API to handle state preserved per
@@ -262,6 +265,9 @@ when asked. We get here only if a certificate has been received. Handling of
 optional verification for this case is done when requesting SSL to verify, by
 setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT in the non-optional case.
 
+May be called multiple times for different issues with a certificate, even
+for a given "depth" in the certificate chain.
+
 Arguments:
   state      current yes/no state as 1/0
   x509ctx    certificate information.
@@ -275,6 +281,7 @@ verify_callback(int state, X509_STORE_CTX *x509ctx,
   tls_support *tlsp, BOOL *calledp, BOOL *optionalp)
 {
 X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
+int depth = X509_STORE_CTX_get_error_depth(x509ctx);
 static uschar txt[256];
 
 X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt));
@@ -282,7 +289,7 @@ X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt));
 if (state == 0)
   {
   log_write(0, LOG_MAIN, "SSL verify error: depth=%d error=%s cert=%s",
-    X509_STORE_CTX_get_error_depth(x509ctx),
+    depth,
     X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)),
     txt);
   tlsp->certificate_verified = FALSE;
@@ -296,10 +303,9 @@ if (state == 0)
     "tls_try_verify_hosts)\n");
   }
 
-else if (X509_STORE_CTX_get_error_depth(x509ctx) != 0)
+else if (depth != 0)
   {
-  DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n",
-     X509_STORE_CTX_get_error_depth(x509ctx), txt);
+  DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, txt);
 #ifndef DISABLE_OCSP
   if (tlsp == &tls_out && client_static_cbinfo->u_ocsp.client.verify_store)
     {  /* client, wanting stapling  */
@@ -310,6 +316,23 @@ else if (X509_STORE_CTX_get_error_depth(x509ctx) != 0)
                              cert))
       ERR_clear_error();
     }
+#endif
+#ifdef EXPERIMENTAL_TPDA
+  if (tlsp == &tls_out && client_static_cbinfo->event_action)
+    {
+    tlsp->peercert = X509_dup(cert);
+    if (tpda_raise_event(client_static_cbinfo->event_action,
+                   US"tls:cert", string_sprintf("%d", depth)) == DEFER)
+      {
+      log_write(0, LOG_MAIN, "SSL verify denied by event-action: "
+                             "depth=%d cert=%s", depth, txt);
+      tlsp->certificate_verified = FALSE;
+      *calledp = TRUE;
+      return 0;                            /* reject */
+      }
+    X509_free(tlsp->peercert);
+    tlsp->peercert = NULL;
+    }
 #endif
   }
 else
@@ -361,6 +384,21 @@ else
       return 0;                                /* reject */
       }
 # endif
+#endif /*EXPERIMENTAL_CERTNAMES*/
+
+#ifdef EXPERIMENTAL_TPDA
+  if (tlsp == &tls_out)
+    {
+    if (tpda_raise_event(client_static_cbinfo->event_action,
+                   US"tls:cert", US"0") == DEFER)
+      {
+      log_write(0, LOG_MAIN, "SSL verify denied by event-action: "
+                             "depth=0 cert=%s", txt);
+      tlsp->certificate_verified = FALSE;
+      *calledp = TRUE;
+      return 0;                            /* reject */
+      }
+    }
 #endif
 
   DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n",
@@ -369,7 +407,7 @@ else
   *calledp = TRUE;
   }
 
-return 1;   /* accept */
+return 1;   /* accept, at least for this level */
 }
 
 static int
@@ -1032,7 +1070,7 @@ tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate,
 long init_options;
 int rc;
 BOOL okay;
-tls_ext_ctx_cb *cbinfo;
+tls_ext_ctx_cb * cbinfo;
 
 cbinfo = store_malloc(sizeof(tls_ext_ctx_cb));
 cbinfo->certificate = certificate;
@@ -1050,6 +1088,9 @@ else
 cbinfo->dhparam = dhparam;
 cbinfo->server_cipher_list = NULL;
 cbinfo->host = host;
+#ifdef EXPERIMENTAL_TPDA
+cbinfo->event_action = NULL;
+#endif
 
 SSL_load_error_strings();          /* basic set up */
 OpenSSL_add_ssl_algorithms();
@@ -1546,7 +1587,7 @@ Argument:
   fd               the fd of the connection
   host             connected host (for messages)
   addr             the first address
-  ob               smtp transport options
+  tb               transport (always smtp)
 
 Returns:           OK on success
                    FAIL otherwise - note that tls_error() will not give DEFER
@@ -1555,9 +1596,10 @@ Returns:           OK on success
 
 int
 tls_client_start(int fd, host_item *host, address_item *addr,
-  void *v_ob)
+  transport_instance *tb)
 {
-smtp_transport_options_block * ob = v_ob;
+smtp_transport_options_block * ob =
+  (smtp_transport_options_block *)tb->options_block;
 static uschar txt[256];
 uschar *expciphers;
 X509* server_cert;
@@ -1672,6 +1714,10 @@ if (request_ocsp)
   }
 #endif
 
+#ifdef EXPERIMENTAL_TPDA
+client_static_cbinfo->event_action = tb->tpda_event_action;
+#endif
+
 /* There doesn't seem to be a built-in timeout on connection. */
 
 DEBUG(D_tls) debug_printf("Calling SSL_connect\n");