Support use of system default CA bundle
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 23 Nov 2014 16:10:30 +0000 (16:10 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 23 Nov 2014 17:39:22 +0000 (17:39 +0000)
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
src/src/tls-gnu.c
src/src/tls-openssl.c

index 823b86dbd9b96b4678d7c34cdf75edd8ecbc828f..89fb5841e9cf392238b53a545f019dc75a61f416 100644 (file)
@@ -16497,21 +16497,31 @@ See &%tls_verify_hosts%& below.
 .option tls_verify_certificates main string&!! unset
 .cindex "TLS" "client certificate verification"
 .cindex "certificate" "verification of client"
-The value of this option is expanded, and must then be the absolute path to
-a file containing permitted certificates for clients that
-match &%tls_verify_hosts%& or &%tls_try_verify_hosts%&. Alternatively, if you
-are using either GnuTLS version 3.3.6 (or later) or OpenSSL,
-you can set &%tls_verify_certificates%& to the name of a
-directory containing certificate files.
-For earlier versions of GnuTLS
-the option must be set to the name of a single file.
+The value of this option is expanded, and must then be either the
+word "system"
+or the absolute path to
+a file or directory containing permitted certificates for clients that
+match &%tls_verify_hosts%& or &%tls_try_verify_hosts%&.
+
+The "system" value for the option will use a
+system default location compiled into the SSL library.
+This is not available for GnuTLS versions preceding 3.0.20 and an explicit location
+must be specified.
+
+The use of a directory for the option value is not avilable for GnuTLS versions
+preceding 3.3.6 and a single file must be used.
+
+With OpenSSL the certificates specified
+explicitly
+either by file or directory
+are added to those given by the system default location.
 
 These certificates should be for the certificate authorities trusted, rather
 than the public cert of individual clients.  With both OpenSSL and GnuTLS, if
 the value is a file then the certificates are sent by Exim as a server to
 connecting clients, defining the list of accepted certificate authorities.
 Thus the values defined should be considered public data.  To avoid this,
-use OpenSSL with a directory.
+use the explicit directory version.
 
 See &<<SECTtlssni>>& for discussion of when this option might be re-expanded.
 
@@ -23443,7 +23453,7 @@ certificate verification will be tried but need not succeed.
 The &%tls_verify_certificates%& option must also be set.
 Note that unless the host is in this list
 TLS connections will be denied to hosts using self-signed certificates
-when &%tls_verify_certificates%& is set.
+when &%tls_verify_certificates%& is matched.
 The &$tls_out_certificate_verified$& variable is set when
 certificate verification succeeds.
 
@@ -23466,15 +23476,24 @@ There is no equivalent checking on client certificates.
 .cindex "certificate" "verification of server"
 .vindex "&$host$&"
 .vindex "&$host_address$&"
-The value of this option must be the absolute path to a file containing
-permitted server certificates, for use when setting up an encrypted connection.
-Alternatively,
-if you are using either GnuTLS version 3.3.6 (or later) or OpenSSL,
-you can set
-&%tls_verify_certificates%& to the name of a directory containing certificate
-files.
-For earlier versions of GnuTLS the option must be set to the name of a
-single file.
+The value of this option must be either the
+word "system"
+or the absolute path to
+a file or directory containing permitted certificates for servers,
+for use when setting up an encrypted connection.
+
+The "system" value for the option will use a location compiled into the SSL library.
+This is not available for GnuTLS versions preceding 3.0.20 and an explicit location
+must be specified.
+
+The use of a directory for the option value is not avilable for GnuTLS versions
+preceding 3.3.6 and a single file must be used.
+
+With OpenSSL the certificates specified
+explicitly
+either by file or directory
+are added to those given by the system default location.
+
 The values of &$host$& and
 &$host_address$& are set to the name and address of the server during the
 expansion of this option. See chapter &<<CHAPTLS>>& for details of TLS.
@@ -25956,8 +25975,9 @@ include files and libraries for GnuTLS can be found.
 There are some differences in usage when using GnuTLS instead of OpenSSL:
 
 .ilist
-The &%tls_verify_certificates%& option must contain the name of a file, not the
-name of a directory for GnuTLS versions before 3.3.6
+The &%tls_verify_certificates%& option
+cannot be the path of a directory
+for GnuTLS versions before 3.3.6
 (for later versions, or OpenSSL, it can be either).
 .next
 The default value for &%tls_dhparam%& differs for historical reasons.
@@ -26309,8 +26329,10 @@ session with a client, you must set either &%tls_verify_hosts%& or
 apply to all TLS connections. For any host that matches one of these options,
 Exim requests a certificate as part of the setup of the TLS session. The
 contents of the certificate are verified by comparing it with a list of
-expected certificates. These must be available in a file or,
-for OpenSSL only (not GnuTLS), a directory, identified by
+expected certificates.
+These may be the system default set (depending on library version),
+an explicit file or,
+depending on library version, a directory, identified by
 &%tls_verify_certificates%&.
 
 A file can contain multiple certificates, concatenated end to end. If a
@@ -26470,9 +26492,13 @@ if it requests it. If the server is Exim, it will request a certificate only if
 &%tls_verify_hosts%& or &%tls_try_verify_hosts%& matches the client.
 
 If the &%tls_verify_certificates%& option is set on the &(smtp)& transport, it
+specified a collection of expected server certificates.
+These may be the system default set (depeding on library version),
+a file or,
+depnding on liibrary version, a directory,
 must name a file or,
-for OpenSSL only (not GnuTLS), a directory, that contains a collection of
-expected server certificates. The client verifies the server's certificate
+for OpenSSL only (not GnuTLS), a directory.
+The client verifies the server's certificate
 against this collection, taking into account any revoked certificates that are
 in the list defined by &%tls_crl%&.
 Failure to verify fails the TLS connection unless either of the
index abac469c6ca8d3baaa7959865030e88b37de1e54..0e9b7ddca56da85b5be4923c4fa5d0b3d8a2a7e9 100644 (file)
@@ -17,6 +17,10 @@ JH/04 Certificate name checking on server certificates, when exim is a client,
       can be used to disable this per-host.  The build option
       EXPERIMENTAL_CERTNAMES is withdrawn.
 
+JH/05 The value of the tls_verify_certificates smtp transport and main options
+      can now be the word "system" to access the system default CA bundle.
+      For GnuTLS, only version 3.0.20 or later.
+
 
 Exim version 4.85
 -----------------
index b520ebfd84edcad4c49f2e8bca1831c7398aae09..4943f48b7d2e3318bb46c683d32c44e65b629185 100644 (file)
@@ -56,6 +56,9 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
 #else
 # undef  SUPPORT_CA_DIR
 #endif
+#if GNUTLS_VERSION_NUMBER >= 0x030314
+# define SUPPORT_SYSDEFAULT_CABUNDLE
+#endif
 
 #ifndef DISABLE_OCSP
 # include <gnutls/ocsp.h>
@@ -874,58 +877,65 @@ else
   return OK;
   }
 
-if (Ustat(state->exp_tls_verify_certificates, &statbuf) < 0)
+#ifdef SUPPORT_SYSDEFAULT_CABUNDLE
+if (Ustrcmp(state->exp_tls_verify_certificates, "system") == 0)
+  cert_count = gnutls_certificate_set_x509_system_trust(state->x509_cred);
+else
+#endif
   {
-  log_write(0, LOG_MAIN|LOG_PANIC, "could not stat %s "
-      "(tls_verify_certificates): %s", state->exp_tls_verify_certificates,
-      strerror(errno));
-  return DEFER;
-  }
+  if (Ustat(state->exp_tls_verify_certificates, &statbuf) < 0)
+    {
+    log_write(0, LOG_MAIN|LOG_PANIC, "could not stat %s "
+       "(tls_verify_certificates): %s", state->exp_tls_verify_certificates,
+       strerror(errno));
+    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
-directory, since while OpenSSL supports that, GnuTLS does not.
-So s/!S_ISREG/S_ISDIR/ and change some messsaging ... */
-if (S_ISDIR(statbuf.st_mode))
-  {
-  DEBUG(D_tls)
-    debug_printf("verify certificates path is a dir: \"%s\"\n",
-        state->exp_tls_verify_certificates);
-  log_write(0, LOG_MAIN|LOG_PANIC,
-      "tls_verify_certificates \"%s\" is a directory",
-      state->exp_tls_verify_certificates);
-  return DEFER;
-  }
+  /* 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
+  directory, since while OpenSSL supports that, GnuTLS does not.
+  So s/!S_ISREG/S_ISDIR/ and change some messsaging ... */
+  if (S_ISDIR(statbuf.st_mode))
+    {
+    DEBUG(D_tls)
+      debug_printf("verify certificates path is a dir: \"%s\"\n",
+         state->exp_tls_verify_certificates);
+    log_write(0, LOG_MAIN|LOG_PANIC,
+       "tls_verify_certificates \"%s\" is a directory",
+       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);
+  DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n",
+         state->exp_tls_verify_certificates, statbuf.st_size);
 
-if (statbuf.st_size == 0)
-  {
-  DEBUG(D_tls)
-    debug_printf("cert file empty, no certs, no verification, ignoring any CRL\n");
-  return OK;
-  }
+  if (statbuf.st_size == 0)
+    {
+    DEBUG(D_tls)
+      debug_printf("cert file empty, no certs, no verification, ignoring any CRL\n");
+    return OK;
+    }
 
-cert_count =
+  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)
-  :
+    (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);
+    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;
-  exim_gnutls_err_check(US"gnutls_certificate_set_x509_trust_file");
+  exim_gnutls_err_check(US"setting certificate trust");
   }
 DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", cert_count);
 
index 303f79dc0243a83397bec39bea5665535b7dba5f..957f927e753f6303d1f53338714772158164acd3 100644 (file)
@@ -1366,50 +1366,65 @@ if (!expand_check(certs, US"tls_verify_certificates", &expcerts))
 
 if (expcerts != NULL && *expcerts != '\0')
   {
-  struct stat statbuf;
-  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)
+  if (Ustrcmp(expcerts, "system") == 0)
     {
-    log_write(0, LOG_MAIN|LOG_PANIC,
-      "failed to stat %s for certificates", expcerts);
-    return DEFER;
+    /* Tell the library to use its compiled-in location for the system default
+    CA bundle, only */
+
+    if (!SSL_CTX_set_default_verify_paths(sctx))
+      return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL);
     }
   else
     {
-    uschar *file, *dir;
-    if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
-      { file = NULL; dir = expcerts; }
+    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,
+       "failed to stat %s for certificates", expcerts);
+      return DEFER;
+      }
     else
-      { file = expcerts; dir = NULL; }
-
-    /* If a certificate file is empty, the next function fails with an
-    unhelpful error message. If we skip it, we get the correct behaviour (no
-    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))
-      return tls_error(US"SSL_CTX_load_verify_locations", host, NULL);
-
-    /* Load the list of CAs for which we will accept certs, for sending
-    to the client.  This is only for the one-file tls_verify_certificates
-    variant.
-    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
-    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)
       {
-      STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
-DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
-                                 sk_X509_NAME_num(names));
-      SSL_CTX_set_client_CA_list(sctx, names);
+      uschar *file, *dir;
+      if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
+       { file = NULL; dir = expcerts; }
+      else
+       { file = expcerts; dir = NULL; }
+
+      /* If a certificate file is empty, the next function fails with an
+      unhelpful error message. If we skip it, we get the correct behaviour (no
+      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))
+       return tls_error(US"SSL_CTX_load_verify_locations", host, NULL);
+
+      /* Load the list of CAs for which we will accept certs, for sending
+      to the client.  This is only for the one-file tls_verify_certificates
+      variant.
+      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
+      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)
+       {
+       STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
+  DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
+                                   sk_X509_NAME_num(names));
+       SSL_CTX_set_client_CA_list(sctx, names);
+       }
       }
     }