TLS: Whine to log on client config of SNI under too-old OpenSSL version
[exim.git] / src / src / tls-openssl.c
index 3430e4eacef18d80736f7bab2d8c5d35e8e66bdb..98762fd624999ab482a72eb8592aa8ed320589e8 100644 (file)
@@ -294,6 +294,7 @@ X509 * old_cert;
 ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action;
 if (ev)
   {
+  DEBUG(D_tls) debug_printf("verify_event: %s %d\n", what, depth);
   old_cert = tlsp->peercert;
   tlsp->peercert = X509_dup(cert);
   /* NB we do not bother setting peerdn */
@@ -344,15 +345,17 @@ 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.
-  client     TRUE for client startup, FALSE for server startup
+  preverify_ok current yes/no state as 1/0
+  x509ctx      certificate information.
+  tlsp         per-direction (client vs. server) support data
+  calledp      has-been-called flag
+  optionalp    verification-is-optional flag
 
-Returns:     1 if verified, 0 if not
+Returns:     0 if verification should fail, otherwise 1
 */
 
 static int
-verify_callback(int state, X509_STORE_CTX *x509ctx,
+verify_callback(int preverify_ok, X509_STORE_CTX *x509ctx,
   tls_support *tlsp, BOOL *calledp, BOOL *optionalp)
 {
 X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
@@ -362,7 +365,7 @@ uschar dn[256];
 X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn));
 dn[sizeof(dn)-1] = '\0';
 
-if (state == 0)
+if (preverify_ok == 0)
   {
   log_write(0, LOG_MAIN, "[%s] SSL verify error: depth=%d error=%s cert=%s",
        tlsp == &tls_out ? deliver_host_address : sender_host_address,
@@ -420,7 +423,7 @@ else
     uschar * name;
     int rc;
     while ((name = string_nextinlist(&list, &sep, NULL, 0)))
-      if ((rc = X509_check_host(cert, name, 0,
+      if ((rc = X509_check_host(cert, CCS name, 0,
                  X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
                  | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS,
                  NULL)))
@@ -469,15 +472,17 @@ return 1;   /* accept, at least for this level */
 }
 
 static int
-verify_callback_client(int state, X509_STORE_CTX *x509ctx)
+verify_callback_client(int preverify_ok, X509_STORE_CTX *x509ctx)
 {
-return verify_callback(state, x509ctx, &tls_out, &client_verify_callback_called, &client_verify_optional);
+return verify_callback(preverify_ok, x509ctx, &tls_out,
+  &client_verify_callback_called, &client_verify_optional);
 }
 
 static int
-verify_callback_server(int state, X509_STORE_CTX *x509ctx)
+verify_callback_server(int preverify_ok, X509_STORE_CTX *x509ctx)
 {
-return verify_callback(state, x509ctx, &tls_in, &server_verify_callback_called, &server_verify_optional);
+return verify_callback(preverify_ok, x509ctx, &tls_in,
+  &server_verify_callback_called, &server_verify_optional);
 }
 
 
@@ -487,7 +492,7 @@ return verify_callback(state, x509ctx, &tls_in, &server_verify_callback_called,
 itself.
 */
 static int
-verify_callback_client_dane(int state, X509_STORE_CTX * x509ctx)
+verify_callback_client_dane(int preverify_ok, X509_STORE_CTX * x509ctx)
 {
 X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
 uschar dn[256];
@@ -499,7 +504,8 @@ BOOL dummy_called, optional = FALSE;
 X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn));
 dn[sizeof(dn)-1] = '\0';
 
-DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", dn);
+DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n",
+  preverify_ok ? "ok":"BAD", depth, dn);
 
 #ifndef DISABLE_EVENT
   if (verify_event(&tls_out, cert, depth, dn,
@@ -507,10 +513,18 @@ DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", dn);
     return 0;                          /* reject, with peercert set */
 #endif
 
-if (state == 1)
+if (preverify_ok == 1)
   tls_out.dane_verified =
   tls_out.certificate_verified = TRUE;
-return 1;
+else
+  {
+  int err = X509_STORE_CTX_get_error(x509ctx);
+  DEBUG(D_tls)
+    debug_printf(" - err %d '%s'\n", err, X509_verify_cert_error_string(err));
+  if (err == X509_V_ERR_APPLICATION_VERIFICATION)
+    preverify_ok = 1;
+  }
+return preverify_ok;
 }
 
 #endif /*EXPERIMENTAL_DANE*/
@@ -918,7 +932,7 @@ if (cbinfo->privatekey != NULL &&
 of the expansion is an empty string, ignore it also, and assume the private
 key is in the same file as the certificate. */
 
-if (expanded != NULL && *expanded != 0)
+if (expanded && *expanded)
   {
   DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded);
   if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM))
@@ -927,21 +941,22 @@ if (expanded != NULL && *expanded != 0)
   }
 
 #ifndef DISABLE_OCSP
-if (cbinfo->is_server &&  cbinfo->u_ocsp.server.file != NULL)
+if (cbinfo->is_server && cbinfo->u_ocsp.server.file)
   {
   if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded))
     return DEFER;
 
-  if (expanded != NULL && *expanded != 0)
+  if (expanded && *expanded)
     {
     DEBUG(D_tls) debug_printf("tls_ocsp_file %s\n", expanded);
-    if (cbinfo->u_ocsp.server.file_expanded &&
-        (Ustrcmp(expanded, cbinfo->u_ocsp.server.file_expanded) == 0))
+    if (  cbinfo->u_ocsp.server.file_expanded
+       && (Ustrcmp(expanded, cbinfo->u_ocsp.server.file_expanded) == 0))
       {
-      DEBUG(D_tls)
-        debug_printf("tls_ocsp_file value unchanged, using existing values.\n");
-      } else {
-        ocsp_load_response(sctx, cbinfo, expanded);
+      DEBUG(D_tls) debug_printf(" - value unchanged, using existing values\n");
+      }
+    else
+      {
+      ocsp_load_response(sctx, cbinfo, expanded);
       }
     }
   }
@@ -1517,26 +1532,18 @@ uschar *expcerts, *expcrl;
 if (!expand_check(certs, US"tls_verify_certificates", &expcerts))
   return DEFER;
 
-if (expcerts != NULL && *expcerts != '\0')
+if (expcerts && *expcerts)
   {
-  if (Ustrcmp(expcerts, "system") == 0)
-    {
-    /* Tell the library to use its compiled-in location for the system default
-    CA bundle, only */
+  /* Tell the library to use its compiled-in location for the system default
+  CA bundle. Then add the ones specified in the config, if any. */
 
-    if (!SSL_CTX_set_default_verify_paths(sctx))
-      return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL);
-    }
-  else
+  if (!SSL_CTX_set_default_verify_paths(sctx))
+    return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL);
+
+  if (Ustrcmp(expcerts, "system") != 0)
     {
     struct stat statbuf;
 
-    /* Tell the library to use its compiled-in location for the system default
-    CA bundle. Those given by the exim config are additional to these */
-
-    if (!SSL_CTX_set_default_verify_paths(sctx))
-      return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL);
-
     if (Ustat(expcerts, &statbuf) < 0)
       {
       log_write(0, LOG_MAIN|LOG_PANIC,
@@ -1556,8 +1563,8 @@ if (expcerts != NULL && *expcerts != '\0')
       certificates are recognized, but the error message is still misleading (it
       says no certificate was supplied.) But this is better. */
 
-      if ((file == NULL || statbuf.st_size > 0) &&
-           !SSL_CTX_load_verify_locations(sctx, CS file, CS dir))
+      if (  (!file || statbuf.st_size > 0)
+         && !SSL_CTX_load_verify_locations(sctx, CS file, CS dir))
        return tls_error(US"SSL_CTX_load_verify_locations", host, NULL);
 
       /* Load the list of CAs for which we will accept certs, for sending
@@ -1566,15 +1573,16 @@ if (expcerts != NULL && *expcerts != '\0')
       If a list isn't loaded into the server, but
       some verify locations are set, the server end appears to make
       a wildcard reqest for client certs.
-      Meanwhile, the client library as deafult behaviour *ignores* the list
+      Meanwhile, the client library as default behaviour *ignores* the list
       we send over the wire - see man SSL_CTX_set_client_cert_cb.
       Because of this, and that the dir variant is likely only used for
       the public-CA bundle (not for a private CA), not worth fixing.
       */
-      if (file != NULL)
+      if (file)
        {
        STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
-  DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
+
+       DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
                                    sk_X509_NAME_num(names));
        SSL_CTX_set_client_CA_list(sctx, names);
        }
@@ -1583,20 +1591,20 @@ if (expcerts != NULL && *expcerts != '\0')
 
   /* Handle a certificate revocation list. */
 
-  #if OPENSSL_VERSION_NUMBER > 0x00907000L
+#if OPENSSL_VERSION_NUMBER > 0x00907000L
 
   /* This bit of code is now the version supplied by Lars Mainka. (I have
-   * merely reformatted it into the Exim code style.)
+  merely reformatted it into the Exim code style.)
 
-   * "From here I changed the code to add support for multiple crl's
-   * in pem format in one file or to support hashed directory entries in
-   * pem format instead of a file. This method now uses the library function
-   * X509_STORE_load_locations to add the CRL location to the SSL context.
-   * OpenSSL will then handle the verify against CA certs and CRLs by
-   * itself in the verify callback." */
+  "From here I changed the code to add support for multiple crl's
+  in pem format in one file or to support hashed directory entries in
+  pem format instead of a file. This method now uses the library function
+  X509_STORE_load_locations to add the CRL location to the SSL context.
+  OpenSSL will then handle the verify against CA certs and CRLs by
+  itself in the verify callback." */
 
   if (!expand_check(crl, US"tls_crl", &expcrl)) return DEFER;
-  if (expcrl != NULL && *expcrl != 0)
+  if (expcrl && *expcrl)
     {
     struct stat statbufcrl;
     if (Ustat(expcrl, &statbufcrl) < 0)
@@ -1632,7 +1640,7 @@ if (expcerts != NULL && *expcerts != '\0')
       }
     }
 
-  #endif  /* OPENSSL_VERSION_NUMBER > 0x00907000L */
+#endif  /* OPENSSL_VERSION_NUMBER > 0x00907000L */
 
   /* If verification is optional, don't fail if no certificate */
 
@@ -2071,8 +2079,7 @@ if (ob->tls_sni)
     DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_out.sni);
     SSL_set_tlsext_host_name(client_ssl, tls_out.sni);
 #else
-    DEBUG(D_tls)
-      debug_printf("OpenSSL at build-time lacked SNI support, ignoring \"%s\"\n",
+    log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
           tls_out.sni);
 #endif
     }
@@ -2743,6 +2750,9 @@ result = 0L;
 #ifdef SSL_OP_NO_SSLv2
 result |= SSL_OP_NO_SSLv2;
 #endif
+#ifdef SSL_OP_SINGLE_DH_USE
+result |= SSL_OP_SINGLE_DH_USE;
+#endif
 
 if (option_spec == NULL)
   {