]> git.llucax.com Git - software/mutt-debian.git/blobdiff - mutt_ssl.c
test upstream patch to fix 533439
[software/mutt-debian.git] / mutt_ssl.c
index 7de574eac60fb6902ab03e364d2484b8332ac8dc..cb1b0cc43fa03632e988f9d54af0b1b9f014485c 100644 (file)
@@ -1,16 +1,16 @@
 /*
  * Copyright (C) 1999-2001 Tommi Komulainen <Tommi.Komulainen@iki.fi>
- * 
+ *
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License as published by
  *     the Free Software Foundation; either version 2 of the License, or
  *     (at your option) any later version.
- * 
+ *
  *     This program is distributed in the hope that it will be useful,
  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *     GNU General Public License for more details.
- * 
+ *
  *     You should have received a copy of the GNU General Public License
  *     along with this program; if not, write to the Free Software
  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
@@ -143,7 +143,7 @@ int mutt_ssl_starttls (CONNECTION* conn)
   return -1;
 }
 
-/* 
+/*
  * OpenSSL library needs to be fed with sufficient entropy. On systems
  * with /dev/urandom, this is done transparently by the library itself,
  * on other systems we need to fill the entropy pool ourselves.
@@ -166,7 +166,7 @@ static int ssl_init (void)
     /* load entropy from files */
     add_entropy (SslEntropyFile);
     add_entropy (RAND_file_name (path, sizeof (path)));
-  
+
     /* load entropy from egd sockets */
 #ifdef HAVE_RAND_EGD
     add_entropy (getenv ("EGDSOCKET"));
@@ -206,9 +206,9 @@ static int add_entropy (const char *file)
 
   mutt_message (_("Filling entropy pool: %s...\n"),
                file);
-  
+
   /* check that the file permissions are secure */
-  if (st.st_uid != getuid () || 
+  if (st.st_uid != getuid () ||
       ((st.st_mode & (S_IWGRP | S_IRGRP)) != 0) ||
       ((st.st_mode & (S_IWOTH | S_IROTH)) != 0))
   {
@@ -280,15 +280,15 @@ static int ssl_socket_open (CONNECTION * conn)
   data->ctx = SSL_CTX_new (SSLv23_client_method ());
 
   /* disable SSL protocols as needed */
-  if (!option(OPTTLSV1)) 
+  if (!option(OPTTLSV1))
   {
     SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1);
   }
-  if (!option(OPTSSLV2)) 
+  if (!option(OPTSSLV2))
   {
     SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv2);
   }
-  if (!option(OPTSSLV3)) 
+  if (!option(OPTSSLV3))
   {
     SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv3);
   }
@@ -303,7 +303,7 @@ static int ssl_socket_open (CONNECTION * conn)
     mutt_socket_close (conn);
     return -1;
   }
-  
+
   conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (data->ssl),
     &maxbits);
 
@@ -336,7 +336,7 @@ static int ssl_negotiate (CONNECTION *conn, sslsockdata* ssldata)
     default:
       errmsg = _("unknown error");
     }
-    
+
     mutt_error (_("SSL failed: %s"), errmsg);
     mutt_sleep (1);
 
@@ -354,7 +354,7 @@ static int ssl_negotiate (CONNECTION *conn, sslsockdata* ssldata)
   if (!ssl_check_certificate (conn, ssldata))
     return -1;
 
-  mutt_message (_("SSL connection using %s (%s)"), 
+  mutt_message (_("SSL connection using %s (%s)"),
     SSL_get_cipher_version (ssldata->ssl), SSL_get_cipher_name (ssldata->ssl));
   mutt_sleep (0);
 
@@ -442,7 +442,7 @@ static char *asn1time_to_string (ASN1_UTCTIME *tm)
   BIO *bio;
 
   strfcpy (buf, _("[invalid date]"), sizeof (buf));
-  
+
   bio = BIO_new (BIO_s_mem());
   if (bio)
   {
@@ -496,7 +496,7 @@ static int check_certificate_by_signer (X509 *peercert)
     int err;
 
     err = X509_STORE_CTX_get_error (&xsc);
-    snprintf (buf, sizeof (buf), "%s (%d)", 
+    snprintf (buf, sizeof (buf), "%s (%d)",
        X509_verify_cert_error_string(err), err);
     dprint (2, (debugfile, "X509_verify_cert: %s\n", buf));
     dprint (2, (debugfile, " [%s]\n", peercert->name));
@@ -513,17 +513,17 @@ static int compare_certificates (X509 *cert, X509 *peercert,
 {
   unsigned char md[EVP_MAX_MD_SIZE];
   unsigned int mdlen;
-  
+
   /* Avoid CPU-intensive digest calculation if the certificates are
     * not even remotely equal.
     */
   if (X509_subject_name_cmp (cert, peercert) != 0 ||
       X509_issuer_name_cmp (cert, peercert) != 0)
     return -1;
-  
+
   if (!X509_digest (cert, EVP_sha1(), md, &mdlen) || peermdlen != mdlen)
     return -1;
-  
+
   if (memcmp(peermd, md, mdlen) != 0)
     return -1;
 
@@ -542,7 +542,7 @@ static int check_certificate_cache (X509 *peercert)
   {
     return 0;
   }
-  
+
   for (i = sk_X509_num (SslSessionCerts); i-- > 0;)
   {
     cert = sk_X509_value (SslSessionCerts, i);
@@ -551,7 +551,7 @@ static int check_certificate_cache (X509 *peercert)
       return 1;
     }
   }
-  
+
   return 0;
 }
 
@@ -564,19 +564,22 @@ static int check_certificate_by_digest (X509 *peercert)
   FILE *fp;
 
   /* expiration check */
-  if (X509_cmp_current_time (X509_get_notBefore (peercert)) >= 0)
-  {
-    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)
+  if (option (OPTSSLVERIFYDATES) != M_NO)
   {
-    dprint (2, (debugfile, "Server certificate has expired"));
-    mutt_error (_("Server certificate has expired"));
-    mutt_sleep (2);
-    return 0;
+    if (X509_cmp_current_time (X509_get_notBefore (peercert)) >= 0)
+    {
+      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"));
+      mutt_error (_("Server certificate has expired"));
+      mutt_sleep (2);
+      return 0;
+    }
   }
 
   if ((fp = fopen (SslCertFile, "rt")) == NULL)
@@ -584,19 +587,19 @@ static int check_certificate_by_digest (X509 *peercert)
 
   if (!X509_digest (peercert, EVP_sha1(), peermd, &peermdlen))
   {
-    fclose (fp);
+    safe_fclose (&fp);
     return 0;
   }
 
   while ((cert = READ_X509_KEY (fp, &cert)) != NULL)
   {
     pass = compare_certificates (cert, peercert, peermd, peermdlen) ? 0 : 1;
-    
+
     if (pass)
       break;
   }
   X509_free (cert);
-  fclose (fp);
+  safe_fclose (&fp);
 
   return pass;
 }
@@ -736,8 +739,10 @@ static int ssl_cache_trusted_cert (X509 *c)
   return (sk_X509_push (SslSessionCerts, X509_dup(c)));
 }
 
-/* check whether cert is preauthorized */
-static int ssl_check_preauth (X509 *cert, CONNECTION *conn)
+/* check whether cert is preauthorized. If host is not null, verify that
+ * it matches the certificate.
+ * Return > 0: authorized, < 0: problems, 0: unknown validity */
+static int ssl_check_preauth (X509 *cert, const char* host)
 {
   char buf[SHORT_STRING];
 
@@ -749,13 +754,16 @@ static int ssl_check_preauth (X509 *cert, CONNECTION *conn)
   }
 
   buf[0] = 0;
-  if (!check_host (cert, conn->account.host, buf, sizeof (buf)))
+  if (host && option (OPTSSLVERIFYHOST) != M_NO)
   {
-    mutt_error (_("Certificate host check failed: %s"), buf);
-    mutt_sleep (2);
-    return -1;
+    if (!check_host (cert, host, buf, sizeof (buf)))
+    {
+      mutt_error (_("Certificate host check failed: %s"), buf);
+      mutt_sleep (2);
+      return -1;
+    }
+    dprint (2, (debugfile, "ssl_check_preauth: hostname check passed\n"));
   }
-  dprint (2, (debugfile, "ssl_check_preauth: hostname check passed\n"));
 
   if (check_certificate_by_signer (cert))
   {
@@ -779,44 +787,30 @@ static int ssl_check_certificate (CONNECTION *conn, sslsockdata *data)
   STACK_OF(X509) *chain;
   X509 *cert;
 
-  if ((preauthrc = ssl_check_preauth (data->cert, conn)) > 0)
+  if ((preauthrc = ssl_check_preauth (data->cert, conn->account.host)) > 0)
     return preauthrc;
 
   chain = SSL_get_peer_cert_chain (data->ssl);
   chain_len = sk_X509_num (chain);
-  if (!chain || (chain_len < 1))
+  /* negative preauthrc means the certificate won't be accepted without
+   * manual override. */
+  if (preauthrc < 0 || !chain || (chain_len <= 1))
     return interactive_check_cert (data->cert, 0, 0);
 
-  /* check the chain from root to peer */
+  /* check the chain from root to peer. */
   for (i = chain_len-1; i >= 0; i--)
   {
     cert = sk_X509_value (chain, i);
-    if (check_certificate_cache (cert))
-      dprint (2, (debugfile, "ssl chain: already cached: %s\n", cert->name));
-    else if (i /* 0 is the peer */ || !preauthrc)
+
+    /* if the certificate validates or is manually accepted, then add it to
+     * the trusted set and recheck the peer certificate */
+    if (ssl_check_preauth (cert, NULL)
+       || interactive_check_cert (cert, i, chain_len))
     {
-      if (check_certificate_by_signer (cert))
-      {
-       dprint (2, (debugfile, "ssl chain: checked by signer: %s\n", cert->name));
-       ssl_cache_trusted_cert (cert);
+      ssl_cache_trusted_cert (cert);
+      if (ssl_check_preauth (data->cert, conn->account.host))
        return 1;
-      }
-      else if (SslCertFile && check_certificate_by_digest (cert))
-      {
-       dprint (2, (debugfile, "ssl chain: trusted with file: %s\n", cert->name));
-       ssl_cache_trusted_cert (cert);
-       return 1;
-      }
-      else /* allow users to shoot their foot */
-      {
-       dprint (2, (debugfile, "ssl chain: check failed: %s\n", cert->name));
-       if (interactive_check_cert (cert, i, chain_len))
-         return 1;
-      }
     }
-    else /* highly suspicious because (i==0 && preauthrc < 0) */
-      if (interactive_check_cert (cert, i, chain_len))
-       return 1;
   }
 
   return 0;
@@ -847,7 +841,7 @@ static int interactive_check_cert (X509 *cert, int idx, int len)
   name = X509_NAME_oneline (X509_get_subject_name (cert),
                            buf, sizeof (buf));
   dprint (2, (debugfile, "oneline: %s\n", name));
-  
+
   for (i = 0; i < 5; i++)
   {
     c = x509_get_part (name, part[i]);
@@ -867,9 +861,9 @@ static int interactive_check_cert (X509 *cert, int idx, int len)
 
   row++;
   snprintf (menu->dialog[row++], SHORT_STRING, _("This certificate is valid"));
-  snprintf (menu->dialog[row++], SHORT_STRING, _("   from %s"), 
+  snprintf (menu->dialog[row++], SHORT_STRING, _("   from %s"),
       asn1time_to_string (X509_get_notBefore (cert)));
-  snprintf (menu->dialog[row++], SHORT_STRING, _("     to %s"), 
+  snprintf (menu->dialog[row++], SHORT_STRING, _("     to %s"),
       asn1time_to_string (X509_get_notAfter (cert)));
 
   row++;
@@ -881,8 +875,10 @@ static int interactive_check_cert (X509 *cert, int idx, int len)
            _("SSL Certificate check (certificate %d of %d in chain)"),
            len - idx, len);
   menu->title = title;
-  if (SslCertFile && X509_cmp_current_time (X509_get_notAfter (cert)) >= 0
-      && X509_cmp_current_time (X509_get_notBefore (cert)) < 0)
+  if (SslCertFile
+      && (option (OPTSSLVERIFYDATES) == M_NO
+         || (X509_cmp_current_time (X509_get_notAfter (cert)) >= 0
+             && X509_cmp_current_time (X509_get_notBefore (cert)) < 0)))
   {
     menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
     menu->keys = _("roa");
@@ -892,7 +888,7 @@ static int interactive_check_cert (X509 *cert, int idx, int len)
     menu->prompt = _("(r)eject, accept (o)nce");
     menu->keys = _("ro");
   }
-  
+
   helpstr[0] = '\0';
   mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
   safe_strcat (helpstr, sizeof (helpstr), buf);
@@ -917,7 +913,7 @@ static int interactive_check_cert (X509 *cert, int idx, int len)
        {
          if (PEM_write_X509 (fp, cert))
            done = 1;
-         fclose (fp);
+         safe_fclose (&fp);
        }
        if (!done)
         {
@@ -951,7 +947,7 @@ static void ssl_get_client_cert(sslsockdata *ssldata, CONNECTION *conn)
     SSL_CTX_set_default_passwd_cb(ssldata->ctx, ssl_passwd_cb);
     SSL_CTX_use_certificate_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM);
     SSL_CTX_use_PrivateKey_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM);
-    
+
     /* if we are using a client cert, SASL may expect an external auth name */
     mutt_account_getuser (&conn->account);
   }
@@ -966,7 +962,7 @@ static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
 
   dprint (2, (debugfile, "ssl_passwd_cb: getting password for %s@%s:%u\n",
              account->user, account->host, account->port));
-  
+
   if (mutt_account_getpass (account))
     return 0;