Ejemplo n.º 1
0
/**
 * gnutls_x509_name_constraints_check_crt:
 * @nc: the extracted name constraints
 * @type: the type of the constraint to check (of type gnutls_x509_subject_alt_name_t)
 * @cert: the certificate to be checked
 *
 * This function will check the provided certificate names against the constraints in
 * @nc using the RFC5280 rules. It will traverse all the certificate's names and
 * alternative names.
 *
 * Currently this function is limited to DNS
 * names and emails (of type %GNUTLS_SAN_DNSNAME and %GNUTLS_SAN_RFC822NAME).
 *
 * Returns: zero if the provided name is not acceptable, and non-zero otherwise.
 *
 * Since: 3.3.0
 **/
unsigned gnutls_x509_name_constraints_check_crt(gnutls_x509_name_constraints_t nc,
				       gnutls_x509_subject_alt_name_t type,
				       gnutls_x509_crt_t cert)
{
char name[MAX_CN];
size_t name_size;
int ret;
unsigned idx, t, san_type;
gnutls_datum_t n;
unsigned found_one;

	if (is_nc_empty(nc, type) != 0)
		return 1; /* shortcut; no constraints to check */

	if (type == GNUTLS_SAN_RFC822NAME) {
		idx = found_one = 0;
		do {
			name_size = sizeof(name);
			ret = gnutls_x509_crt_get_subject_alt_name2(cert,
				idx++, name, &name_size, &san_type, NULL);
			if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
				break;
			else if (ret < 0)
				return gnutls_assert_val(0);

			if (san_type != GNUTLS_SAN_RFC822NAME)
				continue;

			found_one = 1;
			n.data = (void*)name;
			n.size = name_size;
			t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_RFC822NAME,
				&n);
			if (t == 0)
				return gnutls_assert_val(t);
		} while(ret >= 0);

		/* there is at least a single e-mail. That means that the EMAIL field will
		 * not be used for verifying the identity of the holder. */
		if (found_one != 0)
			return 1;

		do {
			/* ensure there is only a single EMAIL, similarly to CN handling (rfc6125) */
			name_size = sizeof(name);
			ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL,
							    1, 0, name, &name_size);
			if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
				return gnutls_assert_val(0);

			name_size = sizeof(name);
			ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL,
							    0, 0, name, &name_size);
			if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
				break;
			else if (ret < 0)
				return gnutls_assert_val(0);

			found_one = 1;
			n.data = (void*)name;
			n.size = name_size;
			t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_RFC822NAME, &n);
			if (t == 0)
				return gnutls_assert_val(t);
		} while(0);

		/* passed */
		if (found_one != 0)
			return 1;
		else {
			/* no name was found. According to RFC5280: 
			 * If no name of the type is in the certificate, the certificate is acceptable.
			 */
			return gnutls_assert_val(1);
		}
	} else if (type == GNUTLS_SAN_DNSNAME) {
		idx = found_one = 0;
		do {
			name_size = sizeof(name);
			ret = gnutls_x509_crt_get_subject_alt_name2(cert,
				idx++, name, &name_size, &san_type, NULL);
			if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
				break;
			else if (ret < 0)
				return gnutls_assert_val(0);

			if (san_type != GNUTLS_SAN_DNSNAME)
				continue;

			found_one = 1;
			n.data = (void*)name;
			n.size = name_size;
			t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_DNSNAME,
				&n);
			if (t == 0)
				return gnutls_assert_val(t);
		} while(ret >= 0);

		/* there is at least a single DNS name. That means that the CN will
		 * not be used for verifying the identity of the holder. */
		if (found_one != 0)
			return 1;

		/* verify the name constraints against the CN, if the certificate is
		 * not a CA. We do this check only on certificates marked as WWW server,
		 * because that's where the CN check is only performed. */
		if (_gnutls_check_key_purpose(cert, GNUTLS_KP_TLS_WWW_SERVER, 0) != 0)
		do {
			/* ensure there is only a single CN, according to rfc6125 */
			name_size = sizeof(name);
			ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME,
				 			    1, 0, name, &name_size);
			if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
				return gnutls_assert_val(0);

			name_size = sizeof(name);
			ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME,
							    0, 0, name, &name_size);
			if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
				break;
			else if (ret < 0)
				return gnutls_assert_val(0);

			found_one = 1;
			n.data = (void*)name;
			n.size = name_size;
			t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_DNSNAME,
				&n);
			if (t == 0)
				return gnutls_assert_val(t);
		} while(0);

		/* passed */
		if (found_one != 0)
			return 1;
		else {
			/* no name was found. According to RFC5280: 
			 * If no name of the type is in the certificate, the certificate is acceptable.
			 */
			return gnutls_assert_val(1);
		}
	} else if (type == GNUTLS_SAN_IPADDRESS || type == GNUTLS_SAN_URI) {
		return check_unsupported_constraint2(cert, nc, type);
	} else
		return check_unsupported_constraint(nc, type);
}
Ejemplo n.º 2
0
/* Verify X.509 certificate chain using a PKCS #11 token.
 *
 * Note that the return value is an OR of GNUTLS_CERT_* elements.
 *
 * Unlike the non-PKCS#11 version, this function accepts a key purpose
 * (from GNUTLS_KP_...). That is because in the p11-kit trust modules
 * anchors are mixed and get assigned a purpose.
 *
 * This function verifies a X.509 certificate list. The certificate
 * list should lead to a trusted certificate in order to be trusted.
 */
unsigned int
_gnutls_pkcs11_verify_crt_status(const char* url,
				const gnutls_x509_crt_t * certificate_list,
				unsigned clist_size,
				const char *purpose,
				unsigned int flags,
				gnutls_verify_output_function func)
{
	int ret;
	unsigned int status = 0, i;
	gnutls_x509_crt_t issuer = NULL;
	gnutls_datum_t raw_issuer = {NULL, 0};
	time_t now = gnutls_time(0);

	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. */
	if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME))
		i = 0;		/* also replace the first one */
	else
		i = 1;		/* do not replace the first one */

	for (; i < clist_size; i++) {
		unsigned vflags;

		if (i == 0) /* in the end certificate do full comparison */
			vflags = GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|
				GNUTLS_PKCS11_OBJ_FLAG_COMPARE|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED;
		else
			vflags = GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|
				GNUTLS_PKCS11_OBJ_FLAG_COMPARE_KEY|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED;

		if (gnutls_pkcs11_crt_is_known (url, certificate_list[i], vflags) != 0) {

			if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS) &&
				!(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) {
				status |=
				    check_time_status(certificate_list[i], now);
				if (status != 0) {
					if (func)
						func(certificate_list[i], certificate_list[i], NULL, status);
					return status;
				}
			}
			if (func)
				func(certificate_list[i],
				     certificate_list[i], NULL, status);

			clist_size = i;
			break;
		}
		/* clist_size may have been changed which gets out of loop */
	}

	if (clist_size == 0) {
		/* The certificate is already present in the trusted certificate list.
		 * Nothing to verify. */
		return status;
	}

	/* check for blacklists */
	for (i = 0; i < clist_size; i++) {
		if (gnutls_pkcs11_crt_is_known (url, certificate_list[i], 
			GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|
			GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) != 0) {
			status |= GNUTLS_CERT_INVALID;
			status |= GNUTLS_CERT_REVOKED;
			if (func)
				func(certificate_list[i], certificate_list[i], NULL, status);
			goto cleanup;
		}
	}

	/* check against issuer */
	ret = gnutls_pkcs11_get_raw_issuer(url, certificate_list[clist_size - 1],
					   &raw_issuer, GNUTLS_X509_FMT_DER,
					   GNUTLS_PKCS11_OBJ_FLAG_OVERWRITE_TRUSTMOD_EXT|GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE);
	if (ret < 0) {
		gnutls_assert();
		if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE && clist_size > 2) {

			/* check if the last certificate in the chain is present
			 * in our trusted list, and if yes, verify against it. */
			ret = gnutls_pkcs11_crt_is_known(url, certificate_list[clist_size - 1],
				GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED|GNUTLS_PKCS11_OBJ_FLAG_COMPARE);
			if (ret != 0) {
				return _gnutls_verify_crt_status(certificate_list, clist_size,
					&certificate_list[clist_size - 1], 1, flags,
					purpose, func);
			}
		}

		status |= GNUTLS_CERT_INVALID;
		status |= GNUTLS_CERT_SIGNER_NOT_FOUND;
		/* verify the certificate list against 0 trusted CAs in order
		 * to get, any additional flags from the certificate list (e.g.,
		 * insecure algorithms or expired */
		status |= _gnutls_verify_crt_status(certificate_list, clist_size,
						    NULL, 0, flags, purpose, func);
		goto cleanup;
	}

	ret = gnutls_x509_crt_init(&issuer);
	if (ret < 0) {
		gnutls_assert();
		status |= GNUTLS_CERT_INVALID;
		status |= GNUTLS_CERT_SIGNER_NOT_FOUND;
		goto cleanup;
	}

	ret = gnutls_x509_crt_import(issuer, &raw_issuer, GNUTLS_X509_FMT_DER);
	if (ret < 0) {
		gnutls_assert();
		status |= GNUTLS_CERT_INVALID;
		status |= GNUTLS_CERT_SIGNER_NOT_FOUND;
		goto cleanup;
	}

	/* check if the raw issuer is blacklisted (it can happen if
	 * the issuer is both in the trusted list and the blacklisted)
	 */
	if (gnutls_pkcs11_crt_is_known (url, issuer,
		GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|
		GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) != 0) {
		status |= GNUTLS_CERT_INVALID;
		status |= GNUTLS_CERT_SIGNER_NOT_FOUND; /* if the signer is revoked - it is as if it doesn't exist */
		goto cleanup;
	}

	/* security modules that provide trust, bundle all certificates (of all purposes)
	 * together. In software that doesn't specify any purpose assume the default to
	 * be www-server. */
	ret = _gnutls_check_key_purpose(issuer, purpose==NULL?GNUTLS_KP_TLS_WWW_SERVER:purpose, 0);
	if (ret != 1) {
		gnutls_assert();
		status |= GNUTLS_CERT_INVALID;
		status |= GNUTLS_CERT_SIGNER_NOT_FOUND;
		goto cleanup;
	}

	status = _gnutls_verify_crt_status(certificate_list, clist_size,
				&issuer, 1, flags, purpose, func);

cleanup:
	gnutls_free(raw_issuer.data);
	if (issuer != NULL)
		gnutls_x509_crt_deinit(issuer);

	return status;
}
Ejemplo n.º 3
0
/* Verify X.509 certificate chain.
 *
 * Note that the return value is an OR of GNUTLS_CERT_* elements.
 *
 * This function verifies a X.509 certificate list. The certificate
 * list should lead to a trusted certificate in order to be trusted.
 */
unsigned int
_gnutls_verify_crt_status(const gnutls_x509_crt_t * certificate_list,
				int clist_size,
				const gnutls_x509_crt_t * trusted_cas,
				int tcas_size,
				unsigned int flags,
				const char *purpose,
				gnutls_verify_output_function func)
{
	int i = 0, ret;
	unsigned int status = 0, output;
	time_t now = gnutls_time(0);
	verify_state_st vparams;

	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. */
	if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME))
		i = 0;		/* also replace the first one */
	else
		i = 1;		/* do not replace the first one */

	for (; i < clist_size; i++) {
		int j;

		for (j = 0; j < tcas_size; j++) {
			/* we check for a certificate that may not be identical with the one
			 * sent by the client, but will have the same name and key. That is
			 * because it can happen that a CA certificate is upgraded from intermediate
			 * CA to self-signed CA at some point. */
			if (_gnutls_check_if_same_key
			    (certificate_list[i], trusted_cas[j], i) != 0) {
				/* explicit time check for trusted CA that we remove from
				 * list. GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS
				 */

				if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS) &&
					!(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) {
					status |=
					    check_time_status(trusted_cas[j],
						       now);
					if (status != 0) {
						if (func)
							func(certificate_list[i], trusted_cas[j], NULL, status);
						return status;
					}
				}

				if (func)
					func(certificate_list[i],
					     trusted_cas[j], NULL, status);
				clist_size = i;
				break;
			}
		}
		/* clist_size may have been changed which gets out of loop */
	}

	if (clist_size == 0) {
		/* The certificate is already present in the trusted certificate list.
		 * Nothing to verify. */
		return status;
	}

	memset(&vparams, 0, sizeof(vparams));
	vparams.now = now;
	vparams.max_path = MAX_VERIFY_DEPTH;
	vparams.func = func;

	ret = gnutls_x509_name_constraints_init(&vparams.nc);
	if (ret < 0) {
		gnutls_assert();
		status |= GNUTLS_CERT_INVALID;
		return status;
	}

	ret = gnutls_x509_tlsfeatures_init(&vparams.tls_feat);
	if (ret < 0) {
		gnutls_assert();
		status |= GNUTLS_CERT_INVALID;
		goto cleanup;
	}

	/* 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.
	 */
	output = 0;

	ret = verify_crt(certificate_list[clist_size - 1],
					  trusted_cas, tcas_size, flags,
					  &output,
					  &vparams,
					  clist_size==1?1:0);
	if (ret != 1) {
		/* if the last certificate in the certificate
		 * list is invalid, then the certificate is not
		 * trusted.
		 */
		gnutls_assert();
		status |= output;
		status |= GNUTLS_CERT_INVALID;
		goto cleanup;
	}

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

		if (purpose != NULL) {
			ret = _gnutls_check_key_purpose(certificate_list[i], purpose, 1);
			if (ret != 1) {
				gnutls_assert();
				status |= GNUTLS_CERT_INVALID;
				status |= GNUTLS_CERT_PURPOSE_MISMATCH;

				if (func)
					func(certificate_list[i-1],
					     certificate_list[i], NULL, status);
				goto cleanup;
			}
		}

		/* 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_DO_NOT_ALLOW_X509_V1_CA_CRT;
		}

		if ((ret =
		     verify_crt(certificate_list[i - 1],
						 &certificate_list[i], 1,
						 flags, &output,
						 &vparams,
						 i==1?1:0)) != 1) {
			gnutls_assert();
			status |= output;
			status |= GNUTLS_CERT_INVALID;
			goto cleanup;
		}
	}

cleanup:
	gnutls_x509_name_constraints_deinit(vparams.nc);
	gnutls_x509_tlsfeatures_deinit(vparams.tls_feat);
	return status;
}
Ejemplo n.º 4
0
/**
 * gnutls_x509_trust_list_verify_crt2:
 * @list: The structure of the list
 * @cert_list: is the certificate list to be verified
 * @cert_list_size: is the certificate list size
 * @data: an array of typed data
 * @elements: the number of data elements
 * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations.
 * @voutput: will hold the certificate verification output.
 * @func: If non-null will be called on each chain element verification with the output.
 *
 * This function will attempt to verify the given certificate and return
 * its status. The @voutput parameter will hold an OR'ed sequence of
 * %gnutls_certificate_status_t flags. When a chain of @cert_list_size with 
 * more than one certificates is provided, the verification status will apply
 * to the first certificate in the chain that failed verification. The
 * verification process starts from the end of the chain (from CA to end
 * certificate).
 *
 * Additionally a certificate verification profile can be specified
 * from the ones in %gnutls_certificate_verification_profiles_t by
 * ORing the result of GNUTLS_PROFILE_TO_VFLAGS() to the verification
 * flags.
 *
 * The acceptable @data types are %GNUTLS_DT_DNS_HOSTNAME and %GNUTLS_DT_KEY_PURPOSE_OID.
 * The former accepts as data a null-terminated hostname, and the latter a null-terminated
 * object identifier (e.g., %GNUTLS_KP_TLS_WWW_SERVER).
 * If a DNS hostname is provided then this function will compare
 * the hostname in the certificate against the given. If names do not match the 
 * %GNUTLS_CERT_UNEXPECTED_OWNER status flag will be set. In addition it
 * will consider certificates provided with gnutls_x509_trust_list_add_named_crt().
 *
 * If a key purpose OID is provided and the end-certificate contains the extended key
 * usage PKIX extension, it will be required to match the provided OID
 * or be marked for any purpose, otherwise verification will fail with 
 * %GNUTLS_CERT_PURPOSE_MISMATCH status.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value. Note that verification failure will not result to an
 *   error code, only @voutput will be updated.
 *
 * Since: 3.3.8
 **/
int
gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list,
				  gnutls_x509_crt_t * cert_list,
				  unsigned int cert_list_size,
				  gnutls_typed_vdata_st *data,
				  unsigned int elements,
				  unsigned int flags,
				  unsigned int *voutput,
				  gnutls_verify_output_function func)
{
	int ret;
	unsigned int i;
	uint32_t hash;
	gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH];
	const char *hostname = NULL, *purpose = NULL;
	unsigned hostname_size = 0;

	if (cert_list == NULL || cert_list_size < 1)
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);

	for (i=0;i<elements;i++) {
		if (data[i].type == GNUTLS_DT_DNS_HOSTNAME) {
			hostname = (void*)data[i].data;
			if (data[i].size > 0) {
				hostname_size = data[i].size;
			}
		} else if (data[i].type == GNUTLS_DT_KEY_PURPOSE_OID) {
			purpose = (void*)data[i].data;
		}
	}

	if (hostname) { /* shortcut using the named certs - if any */
		unsigned vtmp = 0;
		if (hostname_size == 0)
			hostname_size = strlen(hostname);

		ret = gnutls_x509_trust_list_verify_named_crt(list,
					cert_list[0], hostname, hostname_size,
					flags, &vtmp, func);
		if (ret == 0 && vtmp == 0) {
			*voutput = vtmp;
			return 0;
		}
	}

	if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_UNSORTED_CHAIN))
		cert_list = _gnutls_sort_clist(sorted, cert_list, &cert_list_size, NULL);

	cert_list_size = shorten_clist(list, cert_list, cert_list_size);
	if (cert_list_size <= 0)
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

	hash =
	    hash_pjw_bare(cert_list[cert_list_size - 1]->raw_issuer_dn.
			  data,
			  cert_list[cert_list_size -
				    1]->raw_issuer_dn.size);
	hash %= list->size;

	ret = check_if_in_blacklist(cert_list, cert_list_size,
		list->blacklisted, list->blacklisted_size);
	if (ret != 0) {
		*voutput = 0;
		*voutput |= GNUTLS_CERT_REVOKED;
		*voutput |= GNUTLS_CERT_INVALID;
		return 0;
	}

	*voutput =
	    _gnutls_verify_crt_status(cert_list, cert_list_size,
					    list->node[hash].trusted_cas,
					    list->
					    node[hash].trusted_ca_size,
					    flags, purpose, func);

#define LAST_DN cert_list[cert_list_size-1]->raw_dn
#define LAST_IDN cert_list[cert_list_size-1]->raw_issuer_dn

	if ((*voutput) & GNUTLS_CERT_SIGNER_NOT_FOUND &&
		(LAST_DN.size != LAST_IDN.size ||
		 memcmp(LAST_DN.data, LAST_IDN.data, LAST_IDN.size) != 0)) {

		/* if we couldn't find the issuer, try to see if the last
		 * certificate is in the trusted list and try to verify against
		 * (if it is not self signed) */
		hash =
		    hash_pjw_bare(cert_list[cert_list_size - 1]->raw_dn.
			  data, cert_list[cert_list_size - 1]->raw_dn.size);
		hash %= list->size;

		*voutput =
		    _gnutls_verify_crt_status(cert_list, cert_list_size,
					    list->node[hash].trusted_cas,
					    list->
					    node[hash].trusted_ca_size,
					    flags, purpose, func);
	}

#ifdef ENABLE_PKCS11
	if ((*voutput & GNUTLS_CERT_SIGNER_NOT_FOUND) && list->pkcs11_token) {
		/* use the token for verification */

		*voutput = _gnutls_pkcs11_verify_crt_status(list->pkcs11_token,
								cert_list, cert_list_size,
								purpose,
								flags, func);
		if (*voutput != 0) {
			gnutls_assert();
		}
	}
#endif

	/* End-certificate, key purpose and hostname checks. */
	if (purpose) {
		ret = _gnutls_check_key_purpose(cert_list[0], purpose, 0);
		if (ret != 1) {
			gnutls_assert();
			*voutput |= GNUTLS_CERT_PURPOSE_MISMATCH|GNUTLS_CERT_INVALID;
		}
	}

	if (hostname) {
		ret =
		    gnutls_x509_crt_check_hostname2(cert_list[0], hostname, flags);
		if (ret == 0)
			*voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID;
	}

	/* CRL checks follow */

	if (*voutput != 0 || (flags & GNUTLS_VERIFY_DISABLE_CRL_CHECKS))
		return 0;

	/* Check revocation of individual certificates.
	 * start with the last one that we already have its hash
	 */
	ret =
	    _gnutls_x509_crt_check_revocation(cert_list
					      [cert_list_size - 1],
					      list->node[hash].crls,
					      list->node[hash].crl_size,
					      func);
	if (ret == 1) {		/* revoked */
		*voutput |= GNUTLS_CERT_REVOKED;
		*voutput |= GNUTLS_CERT_INVALID;
		return 0;
	}

	for (i = 0; i < cert_list_size - 1; i++) {
		hash =
		    hash_pjw_bare(cert_list[i]->raw_issuer_dn.data,
				  cert_list[i]->raw_issuer_dn.size);
		hash %= list->size;

		ret = _gnutls_x509_crt_check_revocation(cert_list[i],
							list->node[hash].
							crls,
							list->node[hash].
							crl_size, func);
		if (ret < 0) {
			gnutls_assert();
		} else if (ret == 1) {	/* revoked */
			*voutput |= GNUTLS_CERT_REVOKED;
			*voutput |= GNUTLS_CERT_INVALID;
			return 0;
		}
	}

	return 0;
}