Capture the knowlege that verification succeeded
[exim.git] / src / src / tls-openssl.c
index fa29f4ed9eaa21922ab7ea47335dfc8fd2a9d4f9..e37b1add5001ad6ba8b6a2f02f07dee08f8b34df 100644 (file)
@@ -361,7 +361,7 @@ else
       return 0;                                /* reject */
       }
 # endif
-#endif
+#endif /*EXPERIMENTAL_CERTNAMES*/
 
   DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n",
     *calledp ? "" : " authenticated", txt);
@@ -385,6 +385,28 @@ return verify_callback(state, x509ctx, &tls_in, &server_verify_callback_called,
 }
 
 
+#ifdef EXPERIMENTAL_DANE
+/* This gets called *by* the dane library verify callback, which interposes
+itself.
+*/
+static int
+verify_callback_client_dane(int state, X509_STORE_CTX * x509ctx)
+{
+X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
+static uschar txt[256];
+
+X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt));
+
+DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", txt);
+tls_out.peerdn = txt;
+tls_out.peercert = X509_dup(cert);
+
+if (state == 1)
+  tls_out.certificate_verified = TRUE;
+return 1;
+}
+#endif
+
 
 /*************************************************
 *           Information callback                 *
@@ -999,7 +1021,6 @@ return i;
 #endif /*!DISABLE_OCSP*/
 
 
-
 /*************************************************
 *            Initialize for TLS                  *
 *************************************************/
@@ -1602,8 +1623,8 @@ tls_client_start(int fd, host_item *host, address_item *addr,
 {
 smtp_transport_options_block * ob = v_ob;
 static uschar txt[256];
-uschar *expciphers;
-X509* server_cert;
+uschar * expciphers;
+X509 * server_cert;
 int rc;
 static uschar cipherbuf[256];
 
@@ -1612,14 +1633,64 @@ BOOL request_ocsp = FALSE;
 BOOL require_ocsp = FALSE;
 #endif
 #ifdef EXPERIMENTAL_DANE
-BOOL dane_in_use = FALSE;
+dns_answer tlsa_dnsa;
+BOOL dane = FALSE;
+BOOL dane_required;
 #endif
 
 #ifdef EXPERIMENTAL_DANE
-/*XXX TBD: test for transport options, and for TLSA records */
-/*dane_in_use = TRUE;*/
+dane_required = verify_check_this_host(&ob->hosts_require_dane, NULL,
+                         host->name, host->address, NULL) == OK;
+
+if (host->dnssec == DS_YES)
+  {
+  if(  dane_required
+    || verify_check_this_host(&ob->hosts_try_dane, NULL,
+                         host->name, host->address, NULL) == OK
+    )
+    {
+    /* move this out to host.c given the similarity to dns_lookup() ? */
+    uschar buffer[300];
+    uschar * fullname = buffer;
+
+    /* TLSA lookup string */
+    (void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port,
+      host->name);
+
+    switch (rc = dns_lookup(&tlsa_dnsa, buffer, T_TLSA, &fullname))
+      {
+      case DNS_AGAIN:
+       return DEFER; /* just defer this TLS'd conn */
 
-if (!dane_in_use)
+      default:
+      case DNS_FAIL:
+       if (dane_required)
+         {
+         log_write(0, LOG_MAIN, "DANE error: TLSA lookup failed");
+         return FAIL;
+         }
+       break;
+
+      case DNS_SUCCEED:
+       if (!dns_is_secure(&tlsa_dnsa))
+         {
+         log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC");
+         return DEFER;
+         }
+       dane = TRUE;
+       break;
+      }
+    }
+  }
+else if (dane_required)
+  {
+  /*XXX a shame we only find this after making tcp & smtp connection */
+  /* move the test earlier? */
+  log_write(0, LOG_MAIN, "DANE error: previous lookup not DNSSEC");
+  return FAIL;
+  }
+
+if (!dane)     /*XXX todo: enable ocsp with dane */
 #endif
 
 #ifndef DISABLE_OCSP
@@ -1661,12 +1732,14 @@ if (expciphers != NULL)
   }
 
 #ifdef EXPERIMENTAL_DANE
-if (dane_in_use)
+if (dane)
   {
+  SSL_CTX_set_verify(client_ctx, SSL_VERIFY_PEER, verify_callback_client_dane);
+
   if (!DANESSL_library_init())
-    return tls_error(US"library init", host, US"DANE library error");
+    return tls_error(US"library init", host, NULL);
   if (DANESSL_CTX_init(client_ctx) <= 0)
-    return tls_error(US"context init", host, US"DANE library error");
+    return tls_error(US"context init", host, NULL);
   }
 else
 
@@ -1720,18 +1793,46 @@ if (request_ocsp)
 #endif
 
 #ifdef EXPERIMENTAL_DANE
-if (dane_in_use)
+if (dane)
   {
-  if (DANESSL_init(client_ssl, NULL, NULL /*??? hostnames*/) != 1)
-    return tls_error(US"hostnames load", host, US"DANE library error");
+  dns_record * rr;
+  dns_scan dnss;
+  uschar * hostnames[2] = { host->name, NULL };
+
+  if (DANESSL_init(client_ssl, NULL, hostnames) != 1)
+    return tls_error(US"hostnames load", host, NULL);
+
+  for (rr = dns_next_rr(&tlsa_dnsa, &dnss, RESET_ANSWERS);
+       rr;
+       rr = dns_next_rr(&tlsa_dnsa, &dnss, RESET_NEXT)
+      ) if (rr->type == T_TLSA)
+    {
+    uschar * p = rr->data;
+    int usage, selector, mtype;
+    const char * mdname;
+
+    GETSHORT(usage, p);
+    GETSHORT(selector, p);
+    GETSHORT(mtype, p);
 
-  /*
-  foreach TLSA record
-    
-    DANESSL_add_tlsa(client_ssl, uint8_t usage, uint8_t selector,
-       const char *mdname,
-        unsigned const char *data, size_t dlen)
-  */
+    switch (mtype)
+      {
+      default: /* log bad */ return FAIL;
+      case 0:  mdname = NULL; break;
+      case 1:  mdname = "sha256"; break;
+      case 2:  mdname = "sha512"; break;
+      }
+
+    switch (DANESSL_add_tlsa(client_ssl,
+               (uint8_t) usage, (uint8_t) selector,
+               mdname, p, rr->size - (p - rr->data)))
+      {
+      default:
+      case 0:  /* action not taken */
+       return tls_error(US"tlsa load", host, NULL);
+      case 1:  break;
+      }
+    }
   }
 #endif
 
@@ -1745,7 +1846,8 @@ rc = SSL_connect(client_ssl);
 alarm(0);
 
 #ifdef EXPERIMENTAL_DANE
-DANESSL_cleanup(client_ssl);   /*XXX earliest possible callpoint. Too early? */
+if (dane)
+  DANESSL_cleanup(client_ssl); /*XXX earliest possible callpoint. Too early? */
 #endif
 
 if (rc <= 0)