Commit message: Improved the semantics of GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, to allow
only trusted Version 1 CAs and introduced GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT
which has the old semantics.
CVE: CVE-2009-5138
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

static int _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;
    }

    result = ret;

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

    return result;
}

Project: gnutls
File: verify.c

_pkcs1_rsa_verify_sig(const gnutls_datum_t * text,
		      const gnutls_datum_t * signature, mpi_t * params,
		      int params_len)
{
    gnutls_mac_algorithm_t hash;
    int ret;
    opaque digest[MAX_HASH_SIZE], md[MAX_HASH_SIZE];
    int digest_size;
    GNUTLS_HASH_HANDLE 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;
    }

    hd = _gnutls_hash_init(hash);
    if (hd == NULL) {
	gnutls_assert();
	return GNUTLS_E_HASH_FAILED;
    }

    _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

int _gnutls_x509_verify_signature(const gnutls_datum_t * tbs,
				  const gnutls_datum_t * signature,
				  gnutls_x509_crt_t issuer)
{
    mpi_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

int is_crl_issuer(gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer_cert)
{
    gnutls_datum_t dn1 = { NULL, 0 }, dn2 = {
    NULL, 0};
    int ret;

    ret = _gnutls_x509_crl_get_raw_issuer_dn(crl, &dn1);
    if (ret < 0) {
	gnutls_assert();
	goto cleanup;
    }

    ret = _gnutls_x509_crt_get_raw_dn(issuer_cert, &dn2);
    if (ret < 0) {
	gnutls_assert();
	return ret;
    }

    ret = _gnutls_x509_compare_raw_dn(&dn1, &dn2);

  cleanup:
    _gnutls_free_datum(&dn1);
    _gnutls_free_datum(&dn2);

    return ret;
}

Project: gnutls
File: verify.c

int gnutls_x509_crl_check_issuer(gnutls_x509_crl_t cert,
				 gnutls_x509_crt_t issuer)
{
    return is_crl_issuer(cert, issuer);
}

Project: gnutls
File: verify.c

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

    hd = _gnutls_hash_init(GNUTLS_MAC_SHA1);
    if (hd == NULL) {
	gnutls_assert();
	return GNUTLS_E_HASH_FAILED;
    }

    _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

int gnutls_x509_crt_check_issuer(gnutls_x509_crt_t cert,
				 gnutls_x509_crt_t issuer)
{
    return is_issuer(cert, issuer);
}

Project: gnutls
File: verify.c

static int check_if_ca(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer,
		       unsigned int flags)
{
    gnutls_datum_t cert_signed_data = { NULL, 0 };
    gnutls_datum_t issuer_signed_data = { NULL, 0 };
    gnutls_datum_t cert_signature = { NULL, 0 };
    gnutls_datum_t issuer_signature = { NULL, 0 };
    int result;

    /* Check if the issuer is the same with the
     * certificate. This is added in order for trusted
     * certificates to be able to verify themselves.
     */

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

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

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

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

    /* If the subject certificate is the same as the issuer
     * return true.
     */
    if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME))
	if (cert_signed_data.size == issuer_signed_data.size) {
	    if ((memcmp(cert_signed_data.data, issuer_signed_data.data,
			cert_signed_data.size) == 0) &&
		(cert_signature.size == issuer_signature.size) &&
		(memcmp(cert_signature.data, issuer_signature.data,
			cert_signature.size) == 0)) {
		result = 1;
		goto cleanup;
	    }
	}

    if (gnutls_x509_crt_get_ca_status(issuer, NULL) == 1) {
	result = 1;
	goto cleanup;
    } else
	gnutls_assert();

    result = 0;

  cleanup:
    _gnutls_free_datum(&cert_signed_data);
    _gnutls_free_datum(&issuer_signed_data);
    _gnutls_free_datum(&cert_signature);
    _gnutls_free_datum(&issuer_signature);
    return result;
}

Project: gnutls
File: verify.c

int is_issuer(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer_cert)
{
    gnutls_datum_t dn1 = { NULL, 0 }, dn2 = {
    NULL, 0};
    int ret;

    ret = _gnutls_x509_crt_get_raw_issuer_dn(cert, &dn1);
    if (ret < 0) {
	gnutls_assert();
	goto cleanup;
    }

    ret = _gnutls_x509_crt_get_raw_dn(issuer_cert, &dn2);
    if (ret < 0) {
	gnutls_assert();
	goto cleanup;
    }

    ret = _gnutls_x509_compare_raw_dn(&dn1, &dn2);

  cleanup:
    _gnutls_free_datum(&dn1);
    _gnutls_free_datum(&dn2);
    return ret;

}

Project: gnutls
File: verify.c

static int decode_ber_digest_info(const gnutls_datum_t * info,
				  gnutls_mac_algorithm_t * hash,
				  opaque * digest, int *digest_size)
{
    ASN1_TYPE dinfo = ASN1_TYPE_EMPTY;
    int result;
    char str[1024];
    int len;

    if ((result = asn1_create_element(_gnutls_get_gnutls_asn(),
				      "GNUTLS.DigestInfo",
				      &dinfo)) != ASN1_SUCCESS) {
	gnutls_assert();
	return _gnutls_asn2err(result);
    }

    result = asn1_der_decoding(&dinfo, info->data, info->size, NULL);
    if (result != ASN1_SUCCESS) {
	gnutls_assert();
	asn1_delete_structure(&dinfo);
	return _gnutls_asn2err(result);
    }

    len = sizeof(str) - 1;
    result =
	asn1_read_value(dinfo, "digestAlgorithm.algorithm", str, &len);
    if (result != ASN1_SUCCESS) {
	gnutls_assert();
	asn1_delete_structure(&dinfo);
	return _gnutls_asn2err(result);
    }

    *hash = _gnutls_x509_oid2mac_algorithm(str);

    if (*hash == GNUTLS_MAC_UNKNOWN) {

	_gnutls_x509_log("verify.c: HASH OID: %s\n", str);

	gnutls_assert();
	asn1_delete_structure(&dinfo);
	return GNUTLS_E_UNKNOWN_HASH_ALGORITHM;
    }

    result = asn1_read_value(dinfo, "digest", digest, digest_size);
    if (result != ASN1_SUCCESS) {
	gnutls_assert();
	asn1_delete_structure(&dinfo);
	return _gnutls_asn2err(result);
    }

    asn1_delete_structure(&dinfo);

    return 0;
}

Project: gnutls
File: verify.c

    gnutls_x509_crt_t find_crl_issuer(gnutls_x509_crl_t crl,
				      const gnutls_x509_crt_t *
				      trusted_cas, int tcas_size)
{
    int i;

    /* this is serial search. 
     */

    for (i = 0; i < tcas_size; i++) {
	if (is_crl_issuer(crl, trusted_cas[i]) == 1)
	    return trusted_cas[i];
    }

    gnutls_assert();
    return NULL;
}

Project: gnutls
File: verify.c

int _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

int gnutls_x509_crt_verify(gnutls_x509_crt_t cert,
     const gnutls_x509_crt_t * CA_list,
     int CA_list_length, unsigned int flags, unsigned int *verify)
{
    int ret;
    /* Verify certificate 
     */
    ret =
	_gnutls_verify_certificate2(cert, CA_list, CA_list_length, flags,
				    verify);
    if (ret < 0) {
	gnutls_assert();
	return ret;
    }

    return 0;
}

Project: gnutls
File: verify.c

unsigned int _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;


    /* Verify the last certificate in the certificate path
     * against the trusted CA certificate list.
     *
     * If no CAs are present returns NOT_TRUSTED. 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.
         */
        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

static int verify_sig(const gnutls_datum_t * tbs,
		      const gnutls_datum_t * signature,
		      gnutls_pk_algorithm_t pk, mpi_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

int gnutls_x509_crt_list_verify(const gnutls_x509_crt_t * cert_list,
    int cert_list_length, const gnutls_x509_crt_t * CA_list,
    int CA_list_length, const gnutls_x509_crl_t * CRL_list,
    int CRL_list_length, unsigned int flags, unsigned int *verify)
{
    if (cert_list == NULL || cert_list_length == 0)
	return GNUTLS_E_NO_CERTIFICATE_FOUND;

    /* Verify certificate 
     */
    *verify =
	_gnutls_x509_verify_certificate(cert_list, cert_list_length,
					CA_list, CA_list_length, CRL_list,
					CRL_list_length, flags);

    return 0;
}

Project: gnutls
File: verify.c

    gnutls_x509_crt_t find_issuer(gnutls_x509_crt_t cert,
				  const gnutls_x509_crt_t * trusted_cas,
				  int tcas_size)
{
    int i;

    /* this is serial search. 
     */

    for (i = 0; i < tcas_size; i++) {
	if (is_issuer(cert, trusted_cas[i]) == 1)
	    return trusted_cas[i];
    }

    gnutls_assert();
    return NULL;
}

Project: gnutls
File: verify.c

int gnutls_x509_crl_verify(gnutls_x509_crl_t crl,
			   const gnutls_x509_crt_t * CA_list,
			   int CA_list_length, unsigned int flags,
			   unsigned int *verify)
{
    int ret;
    /* Verify crl 
     */
    ret = _gnutls_verify_crl2(crl, CA_list, CA_list_length, flags, verify);
    if (ret < 0) {
	gnutls_assert();
	return ret;
    }

    return 0;
}

Project: gnutls
File: verify.c

static int _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;
    }

    result = ret;

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

    return result;
}