Changeset 5483:9373afa9278f

Show
Ignore:
Timestamp:
2008-08-20 22:33:52 (4 months ago)
Author:
Rocco Rutte <pdmef@…>
Branch:
HEAD
Message:

Port certificate host checking from msmtp to mutt.
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.

Files:
2 modified

Legend:

Unmodified
Added
Removed
  • ChangeLog

    r5481 r5483  
     12008-08-19 13:17 -0700  Brendan Cully  <brendan@kublai.com>  (573d1aab3c89) 
     2 
     3        * init.c: Silence an incorrect uninitialized variable warning. 
     4 
     52008-08-19 13:14 -0700  Brendan Cully  <brendan@kublai.com>  (6b4f25cd9dac) 
     6 
     7        * ChangeLog, init.h: Better documentation for how quote_regexp 
     8        determines quote level. Closes #1463. 
     9 
    1102008-08-19 09:39 +0200  Rocco Rutte  <pdmef@gmx.net>  (3e850c6e43fd) 
    211 
  • mutt_ssl.c

    r5452 r5483  
    2323#include <openssl/ssl.h> 
    2424#include <openssl/x509.h> 
     25#include <openssl/x509v3.h> 
    2526#include <openssl/err.h> 
    2627#include <openssl/rand.h> 
     
    3536#include "mutt_curses.h" 
    3637#include "mutt_ssl.h" 
     38#include "mutt_idna.h" 
    3739 
    3840#if OPENSSL_VERSION_NUMBER >= 0x00904000L 
     
    589591} 
    590592 
    591 static int check_certificate_hostname(X509 *peercert, const char *host) 
    592 { 
    593   char cert_CN[STRING]; 
    594  
    595   if (!host || !*host) 
    596     return 0; 
    597  
    598   X509_NAME_get_text_by_NID (X509_get_subject_name (peercert), 
    599                              NID_commonName, cert_CN, sizeof (cert_CN)); 
    600  
    601   dprint (2, (debugfile, "check_certificate_hostname: cert=[%s] host=[%s]\n", 
    602               cert_CN, host)); 
    603  
    604   return strcmp (cert_CN, host) == 0; 
     593/* port to mutt from msmtp's tls.c */ 
     594static int hostname_match (const char *hostname, const char *certname) 
     595{ 
     596  const char *cmp1, *cmp2; 
     597 
     598  if (strncmp(certname, "*.", 2) == 0) 
     599  { 
     600    cmp1 = certname + 2; 
     601    cmp2 = strchr(hostname, '.'); 
     602    if (!cmp2) 
     603    { 
     604      return 0; 
     605    } 
     606    else 
     607    { 
     608      cmp2++; 
     609    } 
     610  } 
     611  else 
     612  { 
     613    cmp1 = certname; 
     614    cmp2 = hostname; 
     615  } 
     616 
     617  if (*cmp1 == '\0' || *cmp2 == '\0') 
     618  { 
     619    return 0; 
     620  } 
     621 
     622  if (strcasecmp(cmp1, cmp2) != 0) 
     623  { 
     624    return 0; 
     625  } 
     626 
     627  return 1; 
     628} 
     629 
     630/* port to mutt from msmtp's tls.c */ 
     631static int check_host (X509 *x509cert, const char *hostname, char *err, size_t errlen) 
     632{ 
     633  int i, rc = 0; 
     634  /* hostname in ASCII format: */ 
     635  char *hostname_ascii = NULL; 
     636  /* needed to get the common name: */ 
     637  X509_NAME *x509_subject; 
     638  char *buf = NULL; 
     639  int bufsize; 
     640  /* needed to get the DNS subjectAltNames: */ 
     641  STACK *subj_alt_names; 
     642  int subj_alt_names_count; 
     643  GENERAL_NAME *subj_alt_name; 
     644  /* did we find a name matching hostname? */ 
     645  int match_found; 
     646 
     647  /* Check if 'hostname' matches the one of the subjectAltName extensions of 
     648   * type DNS or the Common Name (CN). */ 
     649 
     650#ifdef HAVE_LIBIDN 
     651  if (idna_to_ascii_lz(hostname, &hostname_ascii, 0) != IDNA_SUCCESS) 
     652  { 
     653    hostname_ascii = safe_strdup(hostname); 
     654  } 
     655#else 
     656  hostname_ascii = safe_strdup(hostname); 
     657#endif 
     658 
     659  /* Try the DNS subjectAltNames. */ 
     660  match_found = 0; 
     661  if ((subj_alt_names = X509_get_ext_d2i(x509cert, NID_subject_alt_name, 
     662                                         NULL, NULL))) 
     663  { 
     664    subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names); 
     665    for (i = 0; i < subj_alt_names_count; i++) 
     666    { 
     667      subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i); 
     668      if (subj_alt_name->type == GEN_DNS) 
     669      { 
     670        if ((match_found = hostname_match(hostname_ascii, 
     671                                          (char *)(subj_alt_name->d.ia5->data)))) 
     672        { 
     673          break; 
     674        } 
     675      } 
     676    } 
     677  } 
     678 
     679  if (!match_found) 
     680  { 
     681    /* Try the common name */ 
     682    if (!(x509_subject = X509_get_subject_name(x509cert))) 
     683    { 
     684      if (err && errlen) 
     685        strfcpy (err, _("cannot get certificate subject"), errlen); 
     686      goto out; 
     687    } 
     688 
     689    bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName, 
     690                                        NULL, 0); 
     691    bufsize++; 
     692    buf = safe_malloc((size_t)bufsize); 
     693    if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName, 
     694                                  buf, bufsize) == -1) 
     695    { 
     696      if (err && errlen) 
     697        strfcpy (err, _("cannot get certificate common name"), errlen); 
     698      goto out; 
     699    } 
     700    match_found = hostname_match(hostname_ascii, buf); 
     701  } 
     702 
     703  if (!match_found) 
     704  { 
     705    if (err && errlen) 
     706      snprintf (err, errlen, _("certificate owner does not match hostname %s"), 
     707                hostname); 
     708    goto out; 
     709  } 
     710 
     711  rc = 1; 
     712 
     713out: 
     714  FREE(&buf); 
     715  FREE(&hostname_ascii); 
     716 
     717  return rc; 
    605718} 
    606719 
     
    623736  } 
    624737 
    625   if (check_certificate_hostname (data->cert, conn->account.host)) 
     738  buf[0] = 0; 
     739  if (check_host (data->cert, conn->account.host, buf, sizeof (buf))) 
    626740  { 
    627741    dprint (1, (debugfile, "ssl_check_certificate: hostname check passed\n")); 
    628742  } 
    629743  else 
     744  { 
     745    mutt_error (_("Certificate host check failed: %s"), buf); 
     746    mutt_sleep (2); 
    630747    certerr_hostname = 1; 
     748  } 
    631749 
    632750  if (!certerr_hostname && check_certificate_by_signer (data->cert))