Beispiel #1
0
static int check_certificate_by_digest (X509 *peercert)
{
  unsigned char peermd[EVP_MAX_MD_SIZE];
  unsigned int peermdlen;
  X509 *cert = NULL;
  int pass = 0;
  FILE *fp;

  /* expiration check */
  if (option (OPTSSLVERIFYDATES) != M_NO)
  {
    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)
    return 0;

  if (!X509_digest (peercert, EVP_sha1(), peermd, &peermdlen))
  {
    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);
  safe_fclose (&fp);

  return pass;
}
Beispiel #2
0
static int check_certificate_cache (X509 *peercert)
{
        unsigned char peermd[EVP_MAX_MD_SIZE];
        unsigned int peermdlen;
        X509 *cert;
        int i;

        if (!X509_digest (peercert, EVP_sha1(), peermd, &peermdlen)
        || !SslSessionCerts) {
                return 0;
        }

        for (i = sk_X509_num (SslSessionCerts); i-- > 0;) {
                cert = sk_X509_value (SslSessionCerts, i);
                if (!compare_certificates (cert, peercert, peermd, peermdlen)) {
                        return 1;
                }
        }

        return 0;
}
Beispiel #3
0
/**
 * check_certificate_cache - Is the X509 Certificate in the cache?
 * @param peercert Certificate
 * @retval true Certificate is in the cache
 */
static bool check_certificate_cache(X509 *peercert)
{
  unsigned char peermd[EVP_MAX_MD_SIZE];
  unsigned int peermdlen;
  X509 *cert = NULL;

  if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen) || !SslSessionCerts)
  {
    return false;
  }

  for (int i = sk_X509_num(SslSessionCerts); i-- > 0;)
  {
    cert = sk_X509_value(SslSessionCerts, i);
    if (compare_certificates(cert, peercert, peermd, peermdlen))
    {
      return true;
    }
  }

  return false;
}
Beispiel #4
0
/**
 * check_certificate_file - Read and check a certificate file
 * @param peercert Certificate
 * @retval true  Certificate is valid
 * @retval false Error, or certificate is invalid
 */
static bool check_certificate_file(X509 *peercert)
{
  unsigned char peermd[EVP_MAX_MD_SIZE];
  unsigned int peermdlen;
  X509 *cert = NULL;
  int pass = false;
  FILE *fp = NULL;

  if (!C_CertificateFile)
    return false;

  fp = fopen(C_CertificateFile, "rt");
  if (!fp)
    return false;

  if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen))
  {
    mutt_file_fclose(&fp);
    return false;
  }

  while (PEM_read_X509(fp, &cert, NULL, NULL))
  {
    if (compare_certificates(cert, peercert, peermd, peermdlen) &&
        check_certificate_expiration(cert, true))
    {
      pass = true;
      break;
    }
  }
  /* PEM_read_X509 sets an error on eof */
  if (!pass)
    ERR_clear_error();
  X509_free(cert);
  mutt_file_fclose(&fp);

  return pass;
}
Beispiel #5
0
/**
 * ssl_verify_callback - Certificate verification callback
 * @param preverify_ok If true, don't question the user if they skipped verification
 * @param ctx          X509 store context
 * @retval true  Certificate is valid
 * @retval false Error, or Certificate is invalid
 *
 * Called for each certificate in the chain sent by the peer, starting from the
 * root; returning true means that the given certificate is trusted, returning
 * false immediately aborts the SSL connection
 */
static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
  char buf[256];
  const char *host = NULL;
  size_t len;
  int pos;
  X509 *cert = NULL;
  SSL *ssl = NULL;
  bool skip_mode;

  ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
  if (!ssl)
  {
    mutt_debug(LL_DEBUG1,
               "failed to retrieve SSL structure from X509_STORE_CTX\n");
    return false;
  }
  host = SSL_get_ex_data(ssl, HostExDataIndex);
  if (!host)
  {
    mutt_debug(LL_DEBUG1, "failed to retrieve hostname from SSL structure\n");
    return false;
  }

  /* This is true when a previous entry in the certificate chain did
   * not verify and the user manually chose to skip it via the
   * $ssl_verify_partial_chains option.
   * In this case, all following certificates need to be treated as non-verified
   * until one is actually verified.  */
  skip_mode = (SSL_get_ex_data(ssl, SkipModeExDataIndex));

  cert = X509_STORE_CTX_get_current_cert(ctx);
  pos = X509_STORE_CTX_get_error_depth(ctx);
  len = sk_X509_num(X509_STORE_CTX_get0_chain(ctx));

  mutt_debug(LL_DEBUG1, "checking cert chain entry %s (preverify: %d skipmode: %d)\n",
             X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)),
             preverify_ok, skip_mode);

#ifdef HAVE_SSL_PARTIAL_CHAIN
  /* Sometimes, when a certificate is (s)kipped, OpenSSL will pass it
   * a second time with preverify_ok = 1.  Don't show it or the user
   * will think their "s" key is broken.  */
  if (C_SslVerifyPartialChains)
  {
    static int last_pos = 0;
    static X509 *last_cert = NULL;
    if (skip_mode && preverify_ok && (pos == last_pos) && last_cert)
    {
      unsigned char last_cert_md[EVP_MAX_MD_SIZE];
      unsigned int last_cert_mdlen;
      if (X509_digest(last_cert, EVP_sha256(), last_cert_md, &last_cert_mdlen) &&
          compare_certificates(cert, last_cert, last_cert_md, last_cert_mdlen))
      {
        mutt_debug(LL_DEBUG2, "ignoring duplicate skipped certificate.\n");
        return true;
      }
    }

    last_pos = pos;
    if (last_cert)
      X509_free(last_cert);
    last_cert = X509_dup(cert);
  }
#endif

  /* check session cache first */
  if (check_certificate_cache(cert))
  {
    mutt_debug(LL_DEBUG2, "using cached certificate\n");
    SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
    return true;
  }

  /* check hostname only for the leaf certificate */
  buf[0] = '\0';
  if ((pos == 0) && (C_SslVerifyHost != MUTT_NO))
  {
    if (check_host(cert, host, buf, sizeof(buf)) == 0)
    {
      mutt_error(_("Certificate host check failed: %s"), buf);
      /* we disallow (a)ccept always in the prompt, because it will have no effect
       * for hostname mismatches. */
      return interactive_check_cert(cert, pos, len, ssl, false);
    }
    mutt_debug(LL_DEBUG2, "hostname check passed\n");
  }

  if (!preverify_ok || skip_mode)
  {
    /* automatic check from user's database */
    if (C_CertificateFile && check_certificate_by_digest(cert))
    {
      mutt_debug(LL_DEBUG2, "digest check passed\n");
      SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
      return true;
    }

    /* log verification error */
    int err = X509_STORE_CTX_get_error(ctx);
    snprintf(buf, sizeof(buf), "%s (%d)", X509_verify_cert_error_string(err), err);
    mutt_debug(LL_DEBUG2, "X509_verify_cert: %s\n", buf);

    /* prompt user */
    return interactive_check_cert(cert, pos, len, ssl, true);
  }

  return true;
}