Example #1
0
/**
 * tls_check_preauth - Prepare a certificate for authentication
 * @param[in]  certdata  List of GnuTLS certificates
 * @param[in]  certstat  GnuTLS certificate status
 * @param[in]  hostname  Hostname
 * @param[in]  chainidx  Index in the certificate chain
 * @param[out] certerr   Result, e.g. #CERTERR_VALID
 * @param[out] savedcert 1 if certificate has been saved
 * @retval  0 Success
 * @retval -1 Error
 */
static int tls_check_preauth(const gnutls_datum_t *certdata,
                             gnutls_certificate_status_t certstat, const char *hostname,
                             int chainidx, int *certerr, int *savedcert)
{
  gnutls_x509_crt_t cert;

  *certerr = CERTERR_VALID;
  *savedcert = 0;

  if (gnutls_x509_crt_init(&cert) < 0)
  {
    mutt_error(_("Error initialising gnutls certificate data"));
    return -1;
  }

  if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0)
  {
    mutt_error(_("Error processing certificate data"));
    gnutls_x509_crt_deinit(cert);
    return -1;
  }

  /* Note: tls_negotiate() contains a call to
   * gnutls_certificate_set_verify_flags() with a flag disabling
   * GnuTLS checking of the dates.  So certstat shouldn't have the
   * GNUTLS_CERT_EXPIRED and GNUTLS_CERT_NOT_ACTIVATED bits set. */
  if (C_SslVerifyDates != MUTT_NO)
  {
    if (gnutls_x509_crt_get_expiration_time(cert) < time(NULL))
      *certerr |= CERTERR_EXPIRED;
    if (gnutls_x509_crt_get_activation_time(cert) > time(NULL))
      *certerr |= CERTERR_NOTYETVALID;
  }

  if ((chainidx == 0) && (C_SslVerifyHost != MUTT_NO) &&
      !gnutls_x509_crt_check_hostname(cert, hostname) &&
      !tls_check_stored_hostname(certdata, hostname))
  {
    *certerr |= CERTERR_HOSTNAME;
  }

  if (certstat & GNUTLS_CERT_REVOKED)
  {
    *certerr |= CERTERR_REVOKED;
    certstat ^= GNUTLS_CERT_REVOKED;
  }

  /* see whether certificate is in our cache (certificates file) */
  if (tls_compare_certificates(certdata))
  {
    *savedcert = 1;

    /* We check above for certs with bad dates or that are revoked.
     * These must be accepted manually each time.  Otherwise, we
     * accept saved certificates as valid. */
    if (*certerr == CERTERR_VALID)
    {
      gnutls_x509_crt_deinit(cert);
      return 0;
    }
  }

  if (certstat & GNUTLS_CERT_INVALID)
  {
    *certerr |= CERTERR_NOTTRUSTED;
    certstat ^= GNUTLS_CERT_INVALID;
  }

  if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
  {
    /* NB: already cleared if cert in cache */
    *certerr |= CERTERR_NOTTRUSTED;
    certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
  }

  if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
  {
    /* NB: already cleared if cert in cache */
    *certerr |= CERTERR_SIGNERNOTCA;
    certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
  }

  if (certstat & GNUTLS_CERT_INSECURE_ALGORITHM)
  {
    /* NB: already cleared if cert in cache */
    *certerr |= CERTERR_INSECUREALG;
    certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM;
  }

  /* we've been zeroing the interesting bits in certstat -
   * don't return OK if there are any unhandled bits we don't
   * understand */
  if (certstat != 0)
    *certerr |= CERTERR_OTHER;

  gnutls_x509_crt_deinit(cert);

  if (*certerr == CERTERR_VALID)
    return 0;

  return -1;
}
Example #2
0
static int tls_check_preauth (const gnutls_datum_t *certdata,
                              gnutls_certificate_status certstat,
                              const char *hostname, int chainidx, int* certerr,
                              int* savedcert)
{
  gnutls_x509_crt cert;

  *certerr = CERTERR_VALID;
  *savedcert = 0;

  if (gnutls_x509_crt_init (&cert) < 0)
  {
    mutt_error (_("Error initialising gnutls certificate data"));
    mutt_sleep (2);
    return -1;
  }

  if (gnutls_x509_crt_import (cert, certdata, GNUTLS_X509_FMT_DER) < 0)
  {
    mutt_error (_("Error processing certificate data"));
    mutt_sleep (2);
    gnutls_x509_crt_deinit (cert);
    return -1;
  }

  if (option (OPTSSLVERIFYDATES) != M_NO)
  {
    if (gnutls_x509_crt_get_expiration_time (cert) < time(NULL))
      *certerr |= CERTERR_EXPIRED;
    if (gnutls_x509_crt_get_activation_time (cert) > time(NULL))
      *certerr |= CERTERR_NOTYETVALID;
  }

  if (chainidx == 0 && option (OPTSSLVERIFYHOST) != M_NO
      && !gnutls_x509_crt_check_hostname (cert, hostname)
      && !tls_check_stored_hostname (certdata, hostname))
    *certerr |= CERTERR_HOSTNAME;

  /* see whether certificate is in our cache (certificates file) */
  if (tls_compare_certificates (certdata))
  {
    *savedcert = 1;

    if (chainidx == 0 && (certstat & GNUTLS_CERT_INVALID))
    {
      /* doesn't matter - have decided is valid because server
       certificate is in our trusted cache */
      certstat ^= GNUTLS_CERT_INVALID;
    }

    if (chainidx == 0 && (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND))
    {
      /* doesn't matter that we haven't found the signer, since
       certificate is in our trusted cache */
      certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
    }

    if (chainidx <= 1 && (certstat & GNUTLS_CERT_SIGNER_NOT_CA))
    {
      /* Hmm. Not really sure how to handle this, but let's say
       that we don't care if the CA certificate hasn't got the
       correct X.509 basic constraints if server or first signer
       certificate is in our cache. */
      certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
    }

    if (chainidx == 0 && (certstat & GNUTLS_CERT_INSECURE_ALGORITHM))
    {
      /* doesn't matter that it was signed using an insecure
         algorithm, since certificate is in our trusted cache */
      certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM;
    }
  }

  if (certstat & GNUTLS_CERT_REVOKED)
  {
    *certerr |= CERTERR_REVOKED;
    certstat ^= GNUTLS_CERT_REVOKED;
  }

  if (certstat & GNUTLS_CERT_INVALID)
  {
    *certerr |= CERTERR_NOTTRUSTED;
    certstat ^= GNUTLS_CERT_INVALID;
  }

  if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
  {
    /* NB: already cleared if cert in cache */
    *certerr |= CERTERR_NOTTRUSTED;
    certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
  }

  if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
  {
    /* NB: already cleared if cert in cache */
    *certerr |= CERTERR_SIGNERNOTCA;
    certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
  }

  if (certstat & GNUTLS_CERT_INSECURE_ALGORITHM)
  {
    /* NB: already cleared if cert in cache */
    *certerr |= CERTERR_INSECUREALG;
    certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM;
  }

  gnutls_x509_crt_deinit (cert);

  /* we've been zeroing the interesting bits in certstat -
   don't return OK if there are any unhandled bits we don't
   understand */
  if (*certerr == CERTERR_VALID && certstat == 0)
    return 0;

  return -1;
}
Example #3
0
static void
tls_check_certificate(struct connection_state *scs,
					  const char *remote_hostname)
{
	int ret;
	unsigned int certstat;
	const gnutls_datum_t *cert_list;
	unsigned int cert_list_size = 0;
	gnutls_x509_crt_t cert;

	if (gnutls_auth_get_type(scs->tls_state) != GNUTLS_CRD_CERTIFICATE) {
		bad_certificate(scs, "Unable to get certificate from peer.\n");
		return;	/* bad_cert will exit if -skip-certificate-check was not given */
	}
	ret = gnutls_certificate_verify_peers2(scs->tls_state, &certstat);

	if (ret < 0) {
		char errbuf[1024];

		snprintf(errbuf, 1024, "could not verify certificate: %s (%d).\n",
			gnutls_strerror(ret), ret);
		bad_certificate(scs, (ret == GNUTLS_E_NO_CERTIFICATE_FOUND ?
			"server presented no certificate.\n" :
			errbuf));
		return;
#ifdef GNUTLS_CERT_CORRUPTED
	} else if (certstat & GNUTLS_CERT_CORRUPTED) {
		bad_certificate(scs, "server's certificate is corrupt.\n");
#endif
	} else if (certstat & GNUTLS_CERT_REVOKED) {
		bad_certificate(scs, "server's certificate has been revoked.\n");
	} else if (certstat & GNUTLS_CERT_EXPIRED) {
		bad_certificate(scs, "server's certificate is expired.\n");
	} else if (certstat & GNUTLS_CERT_INSECURE_ALGORITHM) {
		warn_certificate(scs, "server's certificate use an insecure algorithm.\n");
	} else if (certstat & GNUTLS_CERT_INVALID) {
		if (gnutls_certificate_type_get(scs->tls_state) == GNUTLS_CRT_X509) {
			/* bad_certificate(scs, "server's certificate is not trusted.\n"
			   "there may be a problem with the certificate stored in your certfile\n"); */
		} else {
			bad_certificate(scs,
				"server's certificate is invalid or not X.509.\n"
				"there may be a problem with the certificate stored in your certfile\n");
		}
#if defined(GNUTLS_CERT_SIGNER_NOT_FOUND)
	} else if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
		TDM(DEBUG_INFO, "server's certificate is not signed.\n");
		TDM(DEBUG_INFO,
			"to verify that a certificate is trusted, use the certfile option.\n");
#endif

#if defined(GNUTLS_CERT_NOT_TRUSTED)
	} else if (certstat & GNUTLS_CERT_NOT_TRUSTED) {
		TDM(DEBUG_INFO, "server's certificate is not trusted.\n");
		TDM(DEBUG_INFO,
			"to verify that a certificate is trusted, use the certfile option.\n");
#endif
	}

	if (gnutls_x509_crt_init(&cert) < 0) {
		bad_certificate(scs,
			"Unable to initialize certificate data structure");
	}


	/* not checking for not-yet-valid certs... this would make sense
	   if we weren't just comparing to stored ones */
	cert_list =
		gnutls_certificate_get_peers(scs->tls_state, &cert_list_size);

	if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) <
		0) {
		bad_certificate(scs, "Error processing certificate data");
	}

	if (gnutls_x509_crt_get_expiration_time(cert) < time(NULL)) {
		bad_certificate(scs, "server's certificate has expired.\n");
	} else if (gnutls_x509_crt_get_activation_time(cert)
			   > time(NULL)) {
		bad_certificate(scs, "server's certificate is not yet valid.\n");
	} else {
		TDM(DEBUG_INFO, "certificate passed time check.\n");
	}

	if (gnutls_x509_crt_check_hostname(cert, remote_hostname) == 0) {
		char certificate_hostname[256];
		size_t buflen = 255;
		gnutls_x509_crt_get_dn(cert, certificate_hostname, &buflen);
		/* gnutls_x509_extract_certificate_dn(&cert_list[0], &dn); */
		TDM(DEBUG_INFO,
			"server's certificate (%s) does not match its hostname (%s).\n",
			certificate_hostname, remote_hostname);
		bad_certificate(scs,
						"server's certificate does not match its hostname.\n");
	} else {
		if ((scs->pc)->debug >= DEBUG_INFO) {
			char certificate_hostname[256];
			size_t buflen = 255;
			gnutls_x509_crt_get_dn(cert, certificate_hostname, &buflen);
			/* gnutls_x509_extract_certificate_dn(&cert_list[0], &dn); */
			TDM(DEBUG_INFO,
				"server's certificate (%s) matched its hostname (%s).\n",
				certificate_hostname, remote_hostname);
		}
	}

	if (certificate_filename != NULL &&
		tls_compare_certificates(&cert_list[0]) == 0) {
		bad_certificate(scs,
			"server's certificate was not found in the certificate file.\n");
	}

	gnutls_x509_crt_deinit(cert);

	TDM(DEBUG_INFO, "certificate check ok.\n");
	return;
}