Test development
[exim.git] / src / src / tls-openssl.c
index 18994eaa93d053894eddc5c9bd0668deeed910b2..0bd23ac6335b0b15464d59f6715feef408b55b6a 100644 (file)
@@ -1008,13 +1008,14 @@ return i;
 of the library.  We allocate and return a context structure.
 
 Arguments:
+  ctxp            returned SSL context
   host            connected host, if client; NULL if server
   dhparam         DH parameter file
   certificate     certificate file
   privatekey      private key
   ocsp_file       file of stapling info (server); flag for require ocsp (client)
   addr            address if client; NULL if server (for some randomness)
-  cbp             place to put allocated context
+  cbp             place to put allocated callback context
 
 Returns:          OK/DEFER/FAIL
 */
@@ -1533,6 +1534,50 @@ return OK;
 
 
 
+static int
+tls_client_basic_ctx_init(SSL_CTX * ctx,
+    host_item * host, smtp_transport_options_block * ob
+#ifdef EXPERIMENTAL_CERTNAMES
+    , tls_ext_ctx_cb * cbinfo
+#endif
+                         )
+{
+int 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(ctx, ob->tls_verify_certificates,
+       ob->tls_crl, host, FALSE, verify_callback_client)) != OK)
+    return rc;
+  client_verify_optional = FALSE;
+
+#ifdef EXPERIMENTAL_CERTNAMES
+  if (ob->tls_verify_cert_hostnames)
+    {
+    if (!expand_check(ob->tls_verify_cert_hostnames,
+                     US"tls_verify_cert_hostnames",
+                     &cbinfo->verify_cert_hostnames))
+      return FAIL;
+    if (cbinfo->verify_cert_hostnames)
+      DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n",
+                     cbinfo->verify_cert_hostnames);
+    }
+#endif
+  }
+else if (verify_check_host(&ob->tls_try_verify_hosts) == OK)
+  {
+  if ((rc = setup_certs(ctx, ob->tls_verify_certificates,
+       ob->tls_crl, host, TRUE, verify_callback_client)) != OK)
+    return rc;
+  client_verify_optional = TRUE;
+  }
+
+return OK;
+}
 
 /*************************************************
 *    Start a TLS session in a client             *
@@ -1557,16 +1602,83 @@ 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];
+
+#ifndef DISABLE_OCSP
+BOOL request_ocsp = FALSE;
+BOOL require_ocsp = FALSE;
+#endif
+#ifdef EXPERIMENTAL_DANE
+dns_answer tlsa_dnsa;
+BOOL dane = FALSE;
+BOOL dane_required;
+#endif
+
+#ifdef EXPERIMENTAL_DANE
+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 */
+
+      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)
+  {
+  /* Hmm - what lookup, precisely? */
+  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
-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;
+  {
+  require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp,
+    NULL, host->name, host->address, NULL) == OK;
+  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,
@@ -1597,38 +1709,24 @@ if (expciphers != NULL)
     return tls_error(US"SSL_CTX_set_cipher_list", host, 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
-   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))
+#ifdef EXPERIMENTAL_DANE
+if (dane)
   {
-  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;
+  if (!DANESSL_library_init())
+    return tls_error(US"library init", host, US"DANE library error");
+  if (DANESSL_CTX_init(client_ctx) <= 0)
+    return tls_error(US"context init", host, US"DANE library error");
+  }
+else
+
+#endif
 
+  if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob
 #ifdef EXPERIMENTAL_CERTNAMES
-  if (ob->tls_verify_cert_hostnames)
-    {
-    if (!expand_check(ob->tls_verify_cert_hostnames,
-                     US"tls_verify_cert_hostnames",
-                     &client_static_cbinfo->verify_cert_hostnames))
-      return FAIL;
-    if (client_static_cbinfo->verify_cert_hostnames)
-      DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n",
-                     client_static_cbinfo->verify_cert_hostnames);
-    }
+                               , client_static_cbinfo
 #endif
-  }
-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)
+                               )) != OK)
     return rc;
-  client_verify_optional = TRUE;
-  }
 
 if ((client_ssl = SSL_new(client_ctx)) == NULL)
   return tls_error(US"SSL_new", host, NULL);
@@ -1670,6 +1768,51 @@ if (request_ocsp)
   }
 #endif
 
+#ifdef EXPERIMENTAL_DANE
+if (dane)
+  {
+  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, US"DANE library error");
+
+  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);
+
+    switch (mtype)
+      {
+      default: /* log bad */ return FAIL;
+      case 0:  mdname = NULL; break;
+      case 1:  mdname = "SHA2-256"; break;
+      case 2:  mdname = "SHA2-512"; 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; log error */
+        return FAIL;
+      case 1:  break;
+      }
+    }
+  }
+#endif
+
+
 /* There doesn't seem to be a built-in timeout on connection. */
 
 DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
@@ -1678,6 +1821,11 @@ alarm(ob->command_timeout);
 rc = SSL_connect(client_ssl);
 alarm(0);
 
+#ifdef EXPERIMENTAL_DANE
+if (dane)
+  DANESSL_cleanup(client_ssl); /*XXX earliest possible callpoint. Too early? */
+#endif
+
 if (rc <= 0)
   return tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL);