Commit message: Corrected bit disable (was flipping instead).
Initialy reported by Daniel Kahn Gillmor on 9/1/2008. Many thanks to
moog@sysdev.oucs.ox.ac.uk for bringing this into my attention.
CVE: CVE-2009-5138
CWE: CWE-264
Vulnerability description: GnuTLS before 2.7.6, when the GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT flag is not enabled, treats version 1 X.509 certificates as intermediate CAs, which allows remote attackers to bypass intended restrictions by leveraging a X.509 V1 certificate from a trusted CA to issue new certificates, a different vulnerability than CVE-2014-1959.

Project: gnutls
File: verify.c

dsa_verify_sig (const gnutls_datum_t * text,
		const gnutls_datum_t * signature, bigint_t * params,
		int params_len)
{
  int ret;
  opaque _digest[MAX_HASH_SIZE];
  gnutls_datum_t digest;
  digest_hd_st hd;

  ret = _gnutls_hash_init (&hd, GNUTLS_MAC_SHA1);
  if (ret < 0)
    {
      gnutls_assert ();
      return ret;
    }

  _gnutls_hash (&hd, text->data, text->size);
  _gnutls_hash_deinit (&hd, _digest);

  digest.data = _digest;
  digest.size = 20;

  ret = _gnutls_dsa_verify (&digest, signature, params, params_len);

  return ret;
}

Project: gnutls
File: verify.c

_gnutls_x509_verify_signature (const gnutls_datum_t * tbs,
			       const gnutls_datum_t * signature,
			       gnutls_x509_crt_t issuer)
{
  bigint_t issuer_params[MAX_PUBLIC_PARAMS_SIZE];
  int ret, issuer_params_size, i;

  /* Read the MPI parameters from the issuer's certificate.
   */
  issuer_params_size = MAX_PUBLIC_PARAMS_SIZE;
  ret =
    _gnutls_x509_crt_get_mpis (issuer, issuer_params, &issuer_params_size);
  if (ret < 0)
    {
      gnutls_assert ();
      return ret;
    }

  ret =
    verify_sig (tbs, signature,
		gnutls_x509_crt_get_pk_algorithm (issuer, NULL),
		issuer_params, issuer_params_size);
  if (ret < 0)
    {
      gnutls_assert ();
    }

  /* release all allocated MPIs
   */
  for (i = 0; i < issuer_params_size; i++)
    {
      _gnutls_mpi_release (&issuer_params[i]);
    }

  return ret;
}

Project: gnutls
File: verify.c

_gnutls_x509_privkey_verify_signature (const gnutls_datum_t * tbs,
				       const gnutls_datum_t * signature,
				       gnutls_x509_privkey_t issuer)
{
  int ret;

  ret = verify_sig (tbs, signature, issuer->pk_algorithm,
		    issuer->params, issuer->params_size);
  if (ret < 0)
    {
      gnutls_assert ();
    }

  return ret;
}

Project: gnutls
File: verify.c

_gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list,
				 int clist_size,
				 const gnutls_x509_crt_t * trusted_cas,
				 int tcas_size,
				 const gnutls_x509_crl_t * CRLs,
				 int crls_size, unsigned int flags)
{
  int i = 0, ret;
  unsigned int status = 0, output;

  if (clist_size > 1)
    {
      /* Check if the last certificate in the path is self signed.
       * In that case ignore it (a certificate is trusted only if it
       * leads to a trusted party by us, not the server's).
       *
       * This prevents from verifying self signed certificates against
       * themselves. This (although not bad) caused verification
       * failures on some root self signed certificates that use the
       * MD2 algorithm.
       */
      if (gnutls_x509_crt_check_issuer (certificate_list[clist_size - 1],
				    certificate_list[clist_size - 1]) > 0)
	{
	  clist_size--;
	}
    }

  /* We want to shorten the chain by removing the cert that matches
   * one of the certs we trust and all the certs after that i.e. if
   * cert chain is A signed-by B signed-by C signed-by D (signed-by
   * self-signed E but already removed above), and we trust B, remove
   * B, C and D.  We must leave the first cert on chain. */
  if (clist_size > 1 && !(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME))
    {
      for (i = 1; i < clist_size; i++)
	{
	  int j;

	  for (j = 0; j < tcas_size; j++)
	    {
	      if (check_if_same_cert (certificate_list[i],
				      trusted_cas[j]) == 0)
		{
		  clist_size = i;
		  break;
		}
	    }
	  /* clist_size may have been changed which gets out of loop */
	}
    }

  /* Verify the last certificate in the certificate path
   * against the trusted CA certificate list.
   *
   * If no CAs are present returns CERT_INVALID. Thus works
   * in self signed etc certificates.
   */
  ret = _gnutls_verify_certificate2 (certificate_list[clist_size - 1],
				     trusted_cas, tcas_size, flags, &output);
  if (ret == 0)
    {
      /* if the last certificate in the certificate
       * list is invalid, then the certificate is not
       * trusted.
       */
      gnutls_assert ();
      status |= output;
      status |= GNUTLS_CERT_INVALID;
      return status;
    }

  /* Check for revoked certificates in the chain
   */
#ifdef ENABLE_PKI
  for (i = 0; i < clist_size; i++)
    {
      ret = gnutls_x509_crt_check_revocation (certificate_list[i],
					      CRLs, crls_size);
      if (ret == 1)
	{			/* revoked */
	  status |= GNUTLS_CERT_REVOKED;
	  status |= GNUTLS_CERT_INVALID;
	  return status;
	}
    }
#endif

  /* Verify the certificate path (chain)
   */
  for (i = clist_size - 1; i > 0; i--)
    {
      if (i - 1 < 0)
	break;

      /* note that here we disable this V1 CA flag. So that no version 1
       * certificates can exist in a supplied chain.
       */
      if (!(flags & GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT))
	flags &= ~(GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
      if ((ret =
	   _gnutls_verify_certificate2 (certificate_list[i - 1],
					&certificate_list[i], 1, flags,
					NULL)) == 0)
	{
	  status |= GNUTLS_CERT_INVALID;
	  return status;
	}
    }

  return 0;
}

Project: gnutls
File: verify.c

check_if_same_cert (gnutls_x509_crt_t cert1, gnutls_x509_crt_t cert2)
{
  gnutls_datum_t cert1bin = { NULL, 0 }, cert2bin = { NULL, 0 };
  int result;

  result = _gnutls_x509_der_encode (cert1->cert, "", &cert1bin, 0);
  if (result < 0)
    {
      gnutls_assert ();
      goto cleanup;
    }

  result = _gnutls_x509_der_encode (cert2->cert, "", &cert2bin, 0);
  if (result < 0)
    {
      gnutls_assert ();
      goto cleanup;
    }

  if ((cert1bin.size == cert2bin.size) &&
      (memcmp (cert1bin.data, cert2bin.data, cert1bin.size) == 0))
    result = 0;
  else
    result = 1;

 cleanup:
  _gnutls_free_datum (&cert1bin);
  _gnutls_free_datum (&cert2bin);
  return result;
}

Project: gnutls
File: verify.c

_gnutls_verify_crl2 (gnutls_x509_crl_t crl,
		     const gnutls_x509_crt_t * trusted_cas,
		     int tcas_size, unsigned int flags, unsigned int *output)
{
/* CRL is ignored for now */
  gnutls_datum_t crl_signed_data = { NULL, 0 };
  gnutls_datum_t crl_signature = { NULL, 0 };
  gnutls_x509_crt_t issuer;
  int ret, result;

  if (output)
    *output = 0;

  if (tcas_size >= 1)
    issuer = find_crl_issuer (crl, trusted_cas, tcas_size);
  else
    {
      gnutls_assert ();
      if (output)
	*output |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID;
      return 0;
    }

  /* issuer is not in trusted certificate
   * authorities.
   */
  if (issuer == NULL)
    {
      gnutls_assert ();
      if (output)
	*output |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID;
      return 0;
    }

  if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN))
    {
      if (gnutls_x509_crt_get_ca_status (issuer, NULL) != 1)
	{
	  gnutls_assert ();
	  if (output)
	    *output |= GNUTLS_CERT_SIGNER_NOT_CA | GNUTLS_CERT_INVALID;
	  return 0;
	}
    }

  result =
    _gnutls_x509_get_signed_data (crl->crl, "tbsCertList", &crl_signed_data);
  if (result < 0)
    {
      gnutls_assert ();
      goto cleanup;
    }

  result = _gnutls_x509_get_signature (crl->crl, "signature", &crl_signature);
  if (result < 0)
    {
      gnutls_assert ();
      goto cleanup;
    }

  ret =
    _gnutls_x509_verify_signature (&crl_signed_data, &crl_signature, issuer);
  if (ret < 0)
    {
      gnutls_assert ();
    }
  else if (ret == 0)
    {
      gnutls_assert ();
      /* error. ignore it */
      if (output)
	*output |= GNUTLS_CERT_INVALID;
      ret = 0;
    }

  {
    int sigalg;

    sigalg = gnutls_x509_crl_get_signature_algorithm (crl);

    if (((sigalg == GNUTLS_SIGN_RSA_MD2) &&
	 !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) ||
	((sigalg == GNUTLS_SIGN_RSA_MD5) &&
	 !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5)))
      {
	if (output)
	  *output |= GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID;
	ret = 0;
      }
  }

  result = ret;

cleanup:
  _gnutls_free_datum (&crl_signed_data);
  _gnutls_free_datum (&crl_signature);

  return result;
}

Project: gnutls
File: verify.c

_pkcs1_rsa_verify_sig (const gnutls_datum_t * text,
		       const gnutls_datum_t * signature, bigint_t * params,
		       int params_len)
{
  gnutls_mac_algorithm_t hash = GNUTLS_MAC_UNKNOWN;
  int ret;
  opaque digest[MAX_HASH_SIZE], md[MAX_HASH_SIZE];
  int digest_size;
  digest_hd_st hd;
  gnutls_datum_t decrypted;

  ret =
    _gnutls_pkcs1_rsa_decrypt (&decrypted, signature, params, params_len, 1);
  if (ret < 0)
    {
      gnutls_assert ();
      return ret;
    }

  /* decrypted is a BER encoded data of type DigestInfo
   */

  digest_size = sizeof (digest);
  if ((ret =
       decode_ber_digest_info (&decrypted, &hash, digest, &digest_size)) != 0)
    {
      gnutls_assert ();
      _gnutls_free_datum (&decrypted);
      return ret;
    }

  _gnutls_free_datum (&decrypted);

  if (digest_size != _gnutls_hash_get_algo_len (hash))
    {
      gnutls_assert ();
      return GNUTLS_E_ASN1_GENERIC_ERROR;
    }

  ret = _gnutls_hash_init (&hd, hash);
  if (ret < 0)
    {
      gnutls_assert ();
      return ret;
    }

  _gnutls_hash (&hd, text->data, text->size);
  _gnutls_hash_deinit (&hd, md);

  if (memcmp (md, digest, digest_size) != 0)
    {
      gnutls_assert ();
      return GNUTLS_E_PK_SIG_VERIFY_FAILED;
    }

  return 0;
}

Project: gnutls
File: verify.c

verify_sig (const gnutls_datum_t * tbs,
	    const gnutls_datum_t * signature,
	    gnutls_pk_algorithm_t pk, bigint_t * issuer_params,
	    int issuer_params_size)
{

  switch (pk)
    {
    case GNUTLS_PK_RSA:

      if (_pkcs1_rsa_verify_sig
	  (tbs, signature, issuer_params, issuer_params_size) != 0)
	{
	  gnutls_assert ();
	  return 0;
	}

      return 1;
      break;

    case GNUTLS_PK_DSA:
      if (dsa_verify_sig
	  (tbs, signature, issuer_params, issuer_params_size) != 0)
	{
	  gnutls_assert ();
	  return 0;
	}

      return 1;
      break;
    default:
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;

    }
}

Project: gnutls
File: verify.c

_gnutls_verify_certificate2 (gnutls_x509_crt_t cert,
			     const gnutls_x509_crt_t * trusted_cas,
			     int tcas_size, unsigned int flags,
			     unsigned int *output)
{
  gnutls_datum_t cert_signed_data = { NULL, 0 };
  gnutls_datum_t cert_signature = { NULL, 0 };
  gnutls_x509_crt_t issuer;
  int ret, issuer_version, result;

  if (output)
    *output = 0;

  if (tcas_size >= 1)
    issuer = find_issuer (cert, trusted_cas, tcas_size);
  else
    {
      gnutls_assert ();
      if (output)
	*output |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID;
      return 0;
    }

  /* issuer is not in trusted certificate
   * authorities.
   */
  if (issuer == NULL)
    {
      if (output)
	*output |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID;
      gnutls_assert ();
      return 0;
    }

  issuer_version = gnutls_x509_crt_get_version (issuer);
  if (issuer_version < 0)
    {
      gnutls_assert ();
      return issuer_version;
    }

  if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN) &&
      !((flags & GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT) && issuer_version == 1))
    {
      if (check_if_ca (cert, issuer, flags) == 0)
	{
	  gnutls_assert ();
	  if (output)
	    *output |= GNUTLS_CERT_SIGNER_NOT_CA | GNUTLS_CERT_INVALID;
	  return 0;
	}
    }

  result =
    _gnutls_x509_get_signed_data (cert->cert, "tbsCertificate",
				  &cert_signed_data);
  if (result < 0)
    {
      gnutls_assert ();
      goto cleanup;
    }

  result =
    _gnutls_x509_get_signature (cert->cert, "signature", &cert_signature);
  if (result < 0)
    {
      gnutls_assert ();
      goto cleanup;
    }

  ret =
    _gnutls_x509_verify_signature (&cert_signed_data, &cert_signature,
				   issuer);
  if (ret < 0)
    {
      gnutls_assert ();
    }
  else if (ret == 0)
    {
      gnutls_assert ();
      /* error. ignore it */
      if (output)
	*output |= GNUTLS_CERT_INVALID;
      ret = 0;
    }

  /* If the certificate is not self signed check if the algorithms
   * used are secure. If the certificate is self signed it doesn't
   * really matter.
   */
  if (is_issuer (cert, cert) == 0)
    {
      int sigalg;

      sigalg = gnutls_x509_crt_get_signature_algorithm (cert);

      if (((sigalg == GNUTLS_SIGN_RSA_MD2) &&
	   !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) ||
	  ((sigalg == GNUTLS_SIGN_RSA_MD5) &&
	   !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5)))
	{
	  if (output)
	    *output |= GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID;
	  ret = 0;
	}
    }

  result = ret;

cleanup:
  _gnutls_free_datum (&cert_signed_data);
  _gnutls_free_datum (&cert_signature);

  return result;
}