Ejemplo n.º 1
0
/* 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];

/* check session cache first */
        if (check_certificate_cache (cert)) {
                dprint (2, (debugfile, "ssl_check_preauth: using cached certificate\n"));
                return 1;
        }

        buf[0] = 0;
        if (host && option (OPTSSLVERIFYHOST) != M_NO) {
                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"));
        }

        if (check_certificate_by_signer (cert)) {
                dprint (2, (debugfile, "ssl_check_preauth: signer check passed\n"));
                return 1;
        }

/* automatic check from user's database */
        if (SslCertFile && check_certificate_by_digest (cert)) {
                dprint (2, (debugfile, "ssl_check_preauth: digest check passed\n"));
                return 1;
        }

        return 0;
}
Ejemplo n.º 2
0
Archivo: ssl.c Proyecto: darnir/neomutt
/**
 * 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;
}