Esempio n. 1
0
static int ssl_check_certificate (CONNECTION *conn, sslsockdata *data)
{
        int i, preauthrc, chain_len;
        STACK_OF(X509) *chain;
        X509 *cert;

        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);
/* 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. */
        for (i = chain_len-1; i >= 0; i--) {
                cert = sk_X509_value (chain, i);

/* 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)) {
                        ssl_cache_trusted_cert (cert);
                        if (ssl_check_preauth (data->cert, conn->account.host))
                                return 1;
                }
        }

        return 0;
}
Esempio n. 2
0
File: ssl.c Progetto: 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;
}