On Thu, Feb 09, 2017 at 10:56:36PM +0000, isdtor wrote:
> > What I hear you saying is that *with* the expired imap.google.com
> > certificate, you are getting a prompt for an expired Google G2 cert
> > (the 2nd in the chain).  But without the expired imap.google.com you
> > are getting no prompt.  Is that right?
> 
> That is correct. With only two certs in the local store and no cert
> for imap.google.com present, it proceeds straight to the password
> prompt.  It's like the actual server cert is considered optional
> because the rest of the chain checks out.

Hi isdtor,

So, after some research it seems there is a warning about this issue in
the SSL_CTX_load_verify_locations man page.

The attached patch seems to fix the problem for me: it filters out
expired certs for the initial verification/chain construction.  The
entire $certificate_file is still used in the verification callback if
needed, but is just filtered from OpenSSL's trusted stored.

I'd appreciate if you could give this a try.

-- 
Kevin J. McCarthy
GPG Fingerprint: 8975 A9B3 3AA3 7910 385C  5308 ADEF 7684 8031 6BDA
# HG changeset patch
# User Kevin McCarthy <ke...@8t8.us>
# Date 1486852952 28800
#      Sat Feb 11 14:42:32 2017 -0800
# Node ID 6f8279f5a3aead18d2ace266d493040d03a90c1d
# Parent  7c97a8af8718ae7807e0ed0f1eb2b11d436a2e91
Filter expired local certs for OpenSSL verification.

OpenSSL has trouble establishing the chain and verifying when
duplicate expired certs are loaded in from $certificate_file.  A
warning about this is mentioned in
SSL_CTX_load_verify_locations(3SSL).

Filter out expired certs when loading verify certs.  Note that the
full certicates file is still used for verification in
check_certificate_by_digest().

diff --git a/mutt_ssl.c b/mutt_ssl.c
--- a/mutt_ssl.c
+++ b/mutt_ssl.c
@@ -81,16 +81,57 @@
 static void ssl_dprint_err_stack (void);
 static int ssl_cache_trusted_cert (X509 *cert);
 static int ssl_verify_callback (int preverify_ok, X509_STORE_CTX *ctx);
 static int interactive_check_cert (X509 *cert, int idx, int len);
 static void ssl_get_client_cert(sslsockdata *ssldata, CONNECTION *conn);
 static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
 static int ssl_negotiate (CONNECTION *conn, sslsockdata*);
 
+/* ssl certificate verification can behave strangely if there are expired
+ * certs loaded into the trusted store.  This function filters out expired
+ * certs.
+ * Previously the code used this form:
+ *     SSL_CTX_load_verify_locations (ssldata->ctx, SslCertFile, NULL);
+ */
+static int ssl_load_certificates (SSL_CTX *ctx)
+{
+  FILE *fp;
+  X509 *cert;
+  X509_STORE *store;
+  char buf[STRING];
+
+  dprint (2, (debugfile, "ssl_load_certificates: loading trusted 
certificates\n"));
+  store = SSL_CTX_get_cert_store (ctx);
+  if (!store)
+  {
+    store = X509_STORE_new ();
+    SSL_CTX_set_cert_store (ctx, store);
+  }
+
+  if ((fp = fopen (SslCertFile, "rt")) == NULL)
+    return 0;
+
+  while ((cert = PEM_read_X509 (fp, NULL, NULL, NULL)) != NULL)
+  {
+    if ((X509_cmp_current_time (X509_get_notBefore (cert)) >= 0) ||
+        (X509_cmp_current_time (X509_get_notAfter (cert)) <= 0))
+    {
+      dprint (2, (debugfile, "ssl_load_certificates: filtering expired cert: 
%s\n",
+              X509_NAME_oneline (X509_get_subject_name (cert), buf, sizeof 
(buf))));
+      X509_free (cert);
+    }
+    else
+      X509_STORE_add_cert (store, cert);
+  }
+  safe_fclose (&fp);
+
+  return 1;
+}
+
 /* mutt_ssl_starttls: Negotiate TLS over an already opened connection.
  *   TODO: Merge this code better with ssl_socket_open. */
 int mutt_ssl_starttls (CONNECTION* conn)
 {
   sslsockdata* ssldata;
   int maxbits;
   long ssl_options = 0;
 
@@ -139,17 +180,17 @@
   {
     if (! SSL_CTX_set_default_verify_paths (ssldata->ctx))
     {
       dprint (1, (debugfile, "mutt_ssl_starttls: Error setting default verify 
paths\n"));
       goto bail_ctx;
     }
   }
 
-  if (SslCertFile && ! SSL_CTX_load_verify_locations (ssldata->ctx, 
SslCertFile, NULL))
+  if (SslCertFile && !ssl_load_certificates (ssldata->ctx))
     dprint (1, (debugfile, "mutt_ssl_starttls: Error loading trusted 
certificates\n"));
 
   ssl_get_client_cert(ssldata, conn);
 
   if (SslCiphers) {
     if (!SSL_CTX_set_cipher_list (ssldata->ctx, SslCiphers)) {
       dprint (1, (debugfile, "mutt_ssl_starttls: Could not select preferred 
ciphers\n"));
       goto bail_ctx;
@@ -389,17 +430,17 @@
     if (! SSL_CTX_set_default_verify_paths (data->ctx))
     {
       dprint (1, (debugfile, "ssl_socket_open: Error setting default verify 
paths\n"));
       mutt_socket_close (conn);
       return -1;
     }
   }
 
-  if (SslCertFile && ! SSL_CTX_load_verify_locations (data->ctx, SslCertFile, 
NULL))
+  if (SslCertFile && !ssl_load_certificates (data->ctx))
     dprint (1, (debugfile, "ssl_socket_open: Error loading trusted 
certificates\n"));
 
   ssl_get_client_cert(data, conn);
 
   if (SslCiphers) {
     SSL_CTX_set_cipher_list (data->ctx, SslCiphers);
   }
 
@@ -699,17 +740,17 @@
     {
       dprint (2, (debugfile, "Server certificate is not yet valid\n"));
       mutt_error (_("Server certificate is not yet valid"));
       mutt_sleep (2);
       return 0;
     }
     if (X509_cmp_current_time (X509_get_notAfter (peercert)) <= 0)
     {
-      dprint (2, (debugfile, "Server certificate has expired"));
+      dprint (2, (debugfile, "Server certificate has expired\n"));
       mutt_error (_("Server certificate has expired"));
       mutt_sleep (2);
       return 0;
     }
   }
 
   if ((fp = fopen (SslCertFile, "rt")) == NULL)
     return 0;

Attachment: signature.asc
Description: PGP signature

Reply via email to