]> granicus.if.org Git - neomutt/commitdiff
Port certificate host checking from msmtp to mutt.
authorRocco Rutte <pdmef@gmx.net>
Thu, 21 Aug 2008 05:33:52 +0000 (07:33 +0200)
committerRocco Rutte <pdmef@gmx.net>
Thu, 21 Aug 2008 05:33:52 +0000 (07:33 +0200)
It supports IDN, wildcards and extracting the hostname from
subject alternative field as well as common name which should
be the same gnutls supports. Closes #3087.

ChangeLog
mutt_ssl.c

index 9ff80753ec1becdadadd00d8c9c5a47ea3314a0f..4670d63d9b4e94cd6275163e6c3e1f4a78bce6b0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2008-08-19 13:17 -0700  Brendan Cully  <brendan@kublai.com>  (573d1aab3c89)
+
+       * init.c: Silence an incorrect uninitialized variable warning.
+
+2008-08-19 13:14 -0700  Brendan Cully  <brendan@kublai.com>  (6b4f25cd9dac)
+
+       * ChangeLog, init.h: Better documentation for how quote_regexp
+       determines quote level. Closes #1463.
+
 2008-08-19 09:39 +0200  Rocco Rutte  <pdmef@gmx.net>  (3e850c6e43fd)
 
        * handler.c, mutt.h: Make text/enriched handler multibyte aware.
index 0b3ec10de0ace028fb4edbd558095c49279ebb4a..5aeb815c44278a6a86a485c2bb20429dbaa00892 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <openssl/ssl.h>
 #include <openssl/x509.h>
+#include <openssl/x509v3.h>
 #include <openssl/err.h>
 #include <openssl/rand.h>
 
@@ -34,6 +35,7 @@
 #include "mutt_menu.h"
 #include "mutt_curses.h"
 #include "mutt_ssl.h"
+#include "mutt_idna.h"
 
 #if OPENSSL_VERSION_NUMBER >= 0x00904000L
 #define READ_X509_KEY(fp, key) PEM_read_X509(fp, key, NULL, NULL)
@@ -588,20 +590,131 @@ static int check_certificate_by_digest (X509 *peercert)
   return pass;
 }
 
-static int check_certificate_hostname(X509 *peercert, const char *host)
+/* port to mutt from msmtp's tls.c */
+static int hostname_match (const char *hostname, const char *certname)
 {
-  char cert_CN[STRING];
+  const char *cmp1, *cmp2;
 
-  if (!host || !*host)
+  if (strncmp(certname, "*.", 2) == 0)
+  {
+    cmp1 = certname + 2;
+    cmp2 = strchr(hostname, '.');
+    if (!cmp2)
+    {
+      return 0;
+    }
+    else
+    {
+      cmp2++;
+    }
+  }
+  else
+  {
+    cmp1 = certname;
+    cmp2 = hostname;
+  }
+
+  if (*cmp1 == '\0' || *cmp2 == '\0')
+  {
     return 0;
+  }
+
+  if (strcasecmp(cmp1, cmp2) != 0)
+  {
+    return 0;
+  }
+
+  return 1;
+}
+
+/* port to mutt from msmtp's tls.c */
+static int check_host (X509 *x509cert, const char *hostname, char *err, size_t errlen)
+{
+  int i, rc = 0;
+  /* hostname in ASCII format: */
+  char *hostname_ascii = NULL;
+  /* needed to get the common name: */
+  X509_NAME *x509_subject;
+  char *buf = NULL;
+  int bufsize;
+  /* needed to get the DNS subjectAltNames: */
+  STACK *subj_alt_names;
+  int subj_alt_names_count;
+  GENERAL_NAME *subj_alt_name;
+  /* did we find a name matching hostname? */
+  int match_found;
+
+  /* Check if 'hostname' matches the one of the subjectAltName extensions of
+   * type DNS or the Common Name (CN). */
+
+#ifdef HAVE_LIBIDN
+  if (idna_to_ascii_lz(hostname, &hostname_ascii, 0) != IDNA_SUCCESS)
+  {
+    hostname_ascii = safe_strdup(hostname);
+  }
+#else
+  hostname_ascii = safe_strdup(hostname);
+#endif
 
-  X509_NAME_get_text_by_NID (X509_get_subject_name (peercert),
-                            NID_commonName, cert_CN, sizeof (cert_CN));
+  /* Try the DNS subjectAltNames. */
+  match_found = 0;
+  if ((subj_alt_names = X509_get_ext_d2i(x509cert, NID_subject_alt_name,
+                                        NULL, NULL)))
+  {
+    subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names);
+    for (i = 0; i < subj_alt_names_count; i++)
+    {
+      subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
+      if (subj_alt_name->type == GEN_DNS)
+      {
+       if ((match_found = hostname_match(hostname_ascii,
+                                         (char *)(subj_alt_name->d.ia5->data))))
+       {
+         break;
+       }
+      }
+    }
+  }
 
-  dprint (2, (debugfile, "check_certificate_hostname: cert=[%s] host=[%s]\n",
-             cert_CN, host));
+  if (!match_found)
+  {
+    /* Try the common name */
+    if (!(x509_subject = X509_get_subject_name(x509cert)))
+    {
+      if (err && errlen)
+       strfcpy (err, _("cannot get certificate subject"), errlen);
+      goto out;
+    }
 
-  return strcmp (cert_CN, host) == 0;
+    bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName,
+                                       NULL, 0);
+    bufsize++;
+    buf = safe_malloc((size_t)bufsize);
+    if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName,
+                                 buf, bufsize) == -1)
+    {
+      if (err && errlen)
+       strfcpy (err, _("cannot get certificate common name"), errlen);
+      goto out;
+    }
+    match_found = hostname_match(hostname_ascii, buf);
+  }
+
+  if (!match_found)
+  {
+    if (err && errlen)
+      snprintf (err, errlen, _("certificate owner does not match hostname %s"),
+               hostname);
+    goto out;
+  }
+
+  rc = 1;
+
+out:
+  FREE(&buf);
+  FREE(&hostname_ascii);
+
+  return rc;
 }
 
 static int ssl_check_certificate (CONNECTION *conn, sslsockdata * data)
@@ -622,12 +735,17 @@ static int ssl_check_certificate (CONNECTION *conn, sslsockdata * data)
     return 1;
   }
 
-  if (check_certificate_hostname (data->cert, conn->account.host))
+  buf[0] = 0;
+  if (check_host (data->cert, conn->account.host, buf, sizeof (buf)))
   {
     dprint (1, (debugfile, "ssl_check_certificate: hostname check passed\n"));
   }
   else
+  {
+    mutt_error (_("Certificate host check failed: %s"), buf);
+    mutt_sleep (2);
     certerr_hostname = 1;
+  }
 
   if (!certerr_hostname && check_certificate_by_signer (data->cert))
   {