static void
print_resp(gnutls_buffer_st * str, gnutls_ocsp_resp_t resp,
	   gnutls_ocsp_print_formats_t format)
{
	int ret;
	unsigned indx;

	ret = gnutls_ocsp_resp_get_status(resp);
	if (ret < 0) {
		addf(str, "error: ocsp_resp_get_status: %s\n",
		     gnutls_strerror(ret));
		return;
	}

	adds(str, "\tResponse Status: ");
	switch (ret) {
	case GNUTLS_OCSP_RESP_SUCCESSFUL:
		adds(str, "Successful\n");
		break;

	case GNUTLS_OCSP_RESP_MALFORMEDREQUEST:
		adds(str, "malformedRequest\n");
		return;

	case GNUTLS_OCSP_RESP_INTERNALERROR:
		adds(str, "internalError\n");
		return;

	case GNUTLS_OCSP_RESP_TRYLATER:
		adds(str, "tryLater\n");
		return;

	case GNUTLS_OCSP_RESP_SIGREQUIRED:
		adds(str, "sigRequired\n");
		return;

	case GNUTLS_OCSP_RESP_UNAUTHORIZED:
		adds(str, "unauthorized\n");
		return;

	default:
		adds(str, "unknown\n");
		return;
	}

	{
		gnutls_datum_t oid;

		ret = gnutls_ocsp_resp_get_response(resp, &oid, NULL);
		if (ret < 0) {
			addf(str, "error: get_response: %s\n",
			     gnutls_strerror(ret));
			return;
		}

		adds(str, "\tResponse Type: ");
#define OCSP_BASIC "1.3.6.1.5.5.7.48.1.1"

		if (oid.size == sizeof(OCSP_BASIC)
		    && memcmp(oid.data, OCSP_BASIC, oid.size) == 0) {
			adds(str, "Basic OCSP Response\n");
			gnutls_free(oid.data);
		} else {
			addf(str, "Unknown response type (%.*s)\n",
			     oid.size, oid.data);
			gnutls_free(oid.data);
			return;
		}
	}

	/* Version. */
	{
		int version = gnutls_ocsp_resp_get_version(resp);
		if (version < 0)
			addf(str, "error: get_version: %s\n",
			     gnutls_strerror(version));
		else
			addf(str, _("\tVersion: %d\n"), version);
	}

	/* responderID */
	{
		gnutls_datum_t dn;

		ret = gnutls_ocsp_resp_get_responder(resp, &dn);
		if (ret < 0 || dn.data == NULL) {
			if (dn.data == 0) {
				ret = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_KEY, &dn);

				if (ret >= 0) {
					addf(str, _("\tResponder Key ID: "));
					_gnutls_buffer_hexprint(str, dn.data, dn.size);
					adds(str, "\n");
				}
				gnutls_free(dn.data);
			} else {
				addf(str, "error: get_dn: %s\n",
				     gnutls_strerror(ret));
			}
		} else {
			if (dn.data != NULL) {
				addf(str, _("\tResponder ID: %.*s\n"), dn.size,
				     dn.data);
				gnutls_free(dn.data);
			}
		}
	}

	{
		char s[42];
		size_t max = sizeof(s);
		struct tm t;
		time_t tim = gnutls_ocsp_resp_get_produced(resp);

		if (tim == (time_t) - 1)
			addf(str, "error: ocsp_resp_get_produced\n");
		else if (gmtime_r(&tim, &t) == NULL)
			addf(str, "error: gmtime_r (%ld)\n",
			     (unsigned long) tim);
		else if (strftime(s, max, "%a %b %d %H:%M:%S UTC %Y", &t)
			 == 0)
			addf(str, "error: strftime (%ld)\n",
			     (unsigned long) tim);
		else
			addf(str, _("\tProduced At: %s\n"), s);
	}

	addf(str, "\tResponses:\n");
	for (indx = 0;; indx++) {
		gnutls_digest_algorithm_t digest;
		gnutls_datum_t in, ik, sn;
		unsigned int cert_status;
		time_t this_update;
		time_t next_update;
		time_t revocation_time;
		unsigned int revocation_reason;

		ret = gnutls_ocsp_resp_get_single(resp,
						  indx,
						  &digest, &in, &ik, &sn,
						  &cert_status,
						  &this_update,
						  &next_update,
						  &revocation_time,
						  &revocation_reason);
		if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
			break;
		addf(str, "\t\tCertificate ID:\n");
		if (ret != GNUTLS_E_SUCCESS) {
			addf(str, "error: get_singleresponse: %s\n",
			     gnutls_strerror(ret));
			continue;
		}
		addf(str, "\t\t\tHash Algorithm: %s\n",
		     _gnutls_digest_get_name(hash_to_entry(digest)));

		adds(str, "\t\t\tIssuer Name Hash: ");
		_gnutls_buffer_hexprint(str, in.data, in.size);
		adds(str, "\n");

		adds(str, "\t\t\tIssuer Key Hash: ");
		_gnutls_buffer_hexprint(str, ik.data, ik.size);
		adds(str, "\n");

		adds(str, "\t\t\tSerial Number: ");
		_gnutls_buffer_hexprint(str, sn.data, sn.size);
		adds(str, "\n");

		gnutls_free(in.data);
		gnutls_free(ik.data);
		gnutls_free(sn.data);

		{
			const char *p = NULL;

			switch (cert_status) {
			case GNUTLS_OCSP_CERT_GOOD:
				p = "good";
				break;

			case GNUTLS_OCSP_CERT_REVOKED:
				p = "revoked";
				break;

			case GNUTLS_OCSP_CERT_UNKNOWN:
				p = "unknown";
				break;

			default:
				addf(str,
				     "\t\tCertificate Status: unexpected value %d\n",
				     cert_status);
				break;
			}

			if (p)
				addf(str, "\t\tCertificate Status: %s\n",
				     p);
		}

		/* XXX revocation reason */

		if (cert_status == GNUTLS_OCSP_CERT_REVOKED) {
			char s[42];
			size_t max = sizeof(s);
			struct tm t;

			if (revocation_time == (time_t) - 1)
				addf(str, "error: revocation_time\n");
			else if (gmtime_r(&revocation_time, &t) == NULL)
				addf(str, "error: gmtime_r (%ld)\n",
				     (unsigned long) revocation_time);
			else if (strftime
				 (s, max, "%a %b %d %H:%M:%S UTC %Y",
				  &t) == 0)
				addf(str, "error: strftime (%ld)\n",
				     (unsigned long) revocation_time);
			else
				addf(str, _("\t\tRevocation time: %s\n"),
				     s);
		}

		{
			char s[42];
			size_t max = sizeof(s);
			struct tm t;

			if (this_update == (time_t) - 1)
				addf(str, "error: this_update\n");
			else if (gmtime_r(&this_update, &t) == NULL)
				addf(str, "error: gmtime_r (%ld)\n",
				     (unsigned long) this_update);
			else if (strftime
				 (s, max, "%a %b %d %H:%M:%S UTC %Y",
				  &t) == 0)
				addf(str, "error: strftime (%ld)\n",
				     (unsigned long) this_update);
			else
				addf(str, _("\t\tThis Update: %s\n"), s);
		}

		{
			char s[42];
			size_t max = sizeof(s);
			struct tm t;

			if (next_update != (time_t) - 1) {
				if (gmtime_r(&next_update, &t) == NULL)
					addf(str, "error: gmtime_r (%ld)\n",
					     (unsigned long) next_update);
				else if (strftime
					 (s, max, "%a %b %d %H:%M:%S UTC %Y",
					  &t) == 0)
					addf(str, "error: strftime (%ld)\n",
					     (unsigned long) next_update);
				else
					addf(str, _("\t\tNext Update: %s\n"), s);
			}
		}

		/* XXX singleRequestExtensions */
	}

	adds(str, "\tExtensions:\n");
	for (indx = 0;; indx++) {
		gnutls_datum_t oid;
		unsigned int critical;
		gnutls_datum_t data;

		ret =
		    gnutls_ocsp_resp_get_extension(resp, indx, &oid,
						   &critical, &data);
		if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
			break;
		else if (ret != GNUTLS_E_SUCCESS) {
			addf(str, "error: get_extension: %s\n",
			     gnutls_strerror(ret));
			continue;
		}

		if (oid.size == sizeof(GNUTLS_OCSP_NONCE) &&
		    memcmp(oid.data, GNUTLS_OCSP_NONCE, oid.size) == 0) {
			gnutls_datum_t nonce;
			unsigned int ncrit;

			ret =
			    gnutls_ocsp_resp_get_nonce(resp, &ncrit,
						       &nonce);
			if (ret != GNUTLS_E_SUCCESS) {
				addf(str, "error: get_nonce: %s\n",
				     gnutls_strerror(ret));
			} else {
				addf(str, "\t\tNonce%s: ",
				     ncrit ? " (critical)" : "");
				_gnutls_buffer_hexprint(str, nonce.data,
							nonce.size);
				adds(str, "\n");
				gnutls_free(nonce.data);
			}
		} else {
			addf(str, "\t\tUnknown extension %s (%s):\n",
			     oid.data,
			     critical ? "critical" : "not critical");

			adds(str, _("\t\t\tASCII: "));
			_gnutls_buffer_asciiprint(str, (char *) data.data,
						  data.size);
			addf(str, "\n");

			adds(str, _("\t\t\tHexdump: "));
			_gnutls_buffer_hexprint(str, (char *) data.data,
						data.size);
			adds(str, "\n");
		}

		gnutls_free(oid.data);
		gnutls_free(data.data);
	}

	/* Signature. */
	if (format == GNUTLS_OCSP_PRINT_FULL) {
		gnutls_datum_t sig;

		ret = gnutls_ocsp_resp_get_signature_algorithm(resp);
		if (ret < 0)
			addf(str, "error: get_signature_algorithm: %s\n",
			     gnutls_strerror(ret));
		else {
			const char *name =
			    gnutls_sign_algorithm_get_name(ret);
			if (name == NULL)
				name = _("unknown");
			addf(str, _("\tSignature Algorithm: %s\n"), name);
		}
		if (ret != GNUTLS_SIGN_UNKNOWN && gnutls_sign_is_secure(ret) == 0) {
			adds(str,
			     _("warning: signed using a broken signature "
			       "algorithm that can be forged.\n"));
		}

		ret = gnutls_ocsp_resp_get_signature(resp, &sig);
		if (ret < 0)
			addf(str, "error: get_signature: %s\n",
			     gnutls_strerror(ret));
		else {
			adds(str, _("\tSignature:\n"));
			_gnutls_buffer_hexdump(str, sig.data, sig.size,
					       "\t\t");

			gnutls_free(sig.data);
		}
	}

	/* certs */
	if (format == GNUTLS_OCSP_PRINT_FULL) {
		gnutls_x509_crt_t *certs;
		size_t ncerts, i;
		gnutls_datum_t out;

		ret = gnutls_ocsp_resp_get_certs(resp, &certs, &ncerts);
		if (ret < 0)
			addf(str, "error: get_certs: %s\n",
			     gnutls_strerror(ret));
		else {
			if (ncerts > 0)
				addf(str, "\tAdditional certificates:\n");

			for (i = 0; i < ncerts; i++) {
				size_t s = 0;

				ret =
				    gnutls_x509_crt_print(certs[i],
							  GNUTLS_CRT_PRINT_FULL,
							  &out);
				if (ret < 0)
					addf(str, "error: crt_print: %s\n",
					     gnutls_strerror(ret));
				else {
					addf(str, "%.*s", out.size,
					     out.data);
					gnutls_free(out.data);
				}

				ret =
				    gnutls_x509_crt_export(certs[i],
							   GNUTLS_X509_FMT_PEM,
							   NULL, &s);
				if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
					addf(str,
					     "error: crt_export: %s\n",
					     gnutls_strerror(ret));
				else {
					out.data = gnutls_malloc(s);
					if (out.data == NULL)
						addf(str,
						     "error: malloc: %s\n",
						     gnutls_strerror
						     (GNUTLS_E_MEMORY_ERROR));
					else {
						ret =
						    gnutls_x509_crt_export
						    (certs[i],
						     GNUTLS_X509_FMT_PEM,
						     out.data, &s);
						if (ret < 0)
							addf(str,
							     "error: crt_export: %s\n",
							     gnutls_strerror
							     (ret));
						else {
							out.size = s;
							addf(str, "%.*s",
							     out.size,
							     out.data);
						}
						gnutls_free(out.data);
					}
				}

				gnutls_x509_crt_deinit(certs[i]);
			}
			gnutls_free(certs);
		}
	}
}
Exemple #2
0
/* 
 * Verifies the given certificate against a certificate list of
 * trusted CAs.
 *
 * Returns only 0 or 1. If 1 it means that the certificate 
 * was successfuly verified.
 *
 * 'flags': an OR of the gnutls_certificate_verify_flags enumeration.
 *
 * Output will hold some extra information about the verification
 * procedure.
 */
static unsigned
verify_crt(gnutls_x509_crt_t cert,
			    const gnutls_x509_crt_t * trusted_cas,
			    int tcas_size, unsigned int flags,
			    unsigned int *output,
			    verify_state_st *vparams,
			    unsigned end_cert)
{
	gnutls_datum_t cert_signed_data = { NULL, 0 };
	gnutls_datum_t cert_signature = { NULL, 0 };
	gnutls_x509_crt_t issuer = NULL;
	int issuer_version, hash_algo;
	unsigned result = 1;
	const mac_entry_st * me;
	unsigned int out = 0, usage;
	int sigalg, ret;

	if (output)
		*output = 0;

	if (vparams->max_path == 0) {
		MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
		/* bail immediately, to avoid inconistency  */
		goto cleanup;
	}
	vparams->max_path--;

	if (tcas_size >= 1)
		issuer = find_issuer(cert, trusted_cas, tcas_size);

	ret =
	    _gnutls_x509_get_signed_data(cert->cert, &cert->der, "tbsCertificate",
					 &cert_signed_data);
	if (ret < 0) {
		MARK_INVALID(0);
		cert_signed_data.data = NULL;
	}

	ret =
	    _gnutls_x509_get_signature(cert->cert, "signature",
				       &cert_signature);
	if (ret < 0) {
		MARK_INVALID(0);
		cert_signature.data = NULL;
	}

	ret =
	    _gnutls_x509_get_signature_algorithm(cert->cert,
						 "signatureAlgorithm.algorithm");
	if (ret < 0) {
		MARK_INVALID(0);
	}
	sigalg = ret;

	/* issuer is not in trusted certificate
	 * authorities.
	 */
	if (issuer == NULL) {
		MARK_INVALID(GNUTLS_CERT_SIGNER_NOT_FOUND);
	} else {
		if (vparams->nc != NULL) {
			/* append the issuer's constraints */
			ret = gnutls_x509_crt_get_name_constraints(issuer, vparams->nc, 
				GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, NULL);
			if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
				MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
				goto nc_done;
			}

			/* only check name constraints in server certificates, not CAs */
			if (end_cert != 0) {
				ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_DNSNAME, cert);
				if (ret == 0) {
					MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
					goto nc_done;
				}

				ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_RFC822NAME, cert);
				if (ret == 0) {
					MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
					goto nc_done;
				}

				ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_DN, cert);
				if (ret == 0) {
					MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
					goto nc_done;
				}

				ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_URI, cert);
				if (ret == 0) {
					MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
					goto nc_done;
				}

				ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_IPADDRESS, cert);
				if (ret == 0) {
					MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
					goto nc_done;
				}
			}
		}

 nc_done:
		if (vparams->tls_feat != NULL) {
			/* append the issuer's constraints */
			ret = gnutls_x509_crt_get_tlsfeatures(issuer, vparams->tls_feat, GNUTLS_EXT_FLAG_APPEND, NULL);
			if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
				MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
				goto feat_done;
			}

			ret = gnutls_x509_tlsfeatures_check_crt(vparams->tls_feat, cert);
			if (ret == 0) {
				MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
				goto feat_done;
			}
		}

 feat_done:
		issuer_version = gnutls_x509_crt_get_version(issuer);

		if (issuer_version < 0) {
			MARK_INVALID(0);
		} else if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN) &&
			   ((flags & GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT)
			    || issuer_version != 1)) {
			if (check_if_ca(cert, issuer, &vparams->max_path, flags) != 1) {
				MARK_INVALID(GNUTLS_CERT_SIGNER_NOT_CA);
			}

			ret =
			    gnutls_x509_crt_get_key_usage(issuer, &usage, NULL);
			if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
				if (ret < 0) {
					MARK_INVALID(0);
				} else if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) {
					MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
				}
			}
		}

		if (sigalg >= 0) {
			hash_algo = gnutls_sign_get_hash_algorithm(sigalg);
			me = mac_to_entry(hash_algo);
		} else {
			me = NULL;
		}

		if (me == NULL) {
			MARK_INVALID(0);
		} else if (cert_signed_data.data != NULL &&
			   cert_signature.data != NULL) {
			ret =
			    _gnutls_x509_verify_data(me,
						     &cert_signed_data,
						     &cert_signature,
						     issuer);
			if (ret == GNUTLS_E_PK_SIG_VERIFY_FAILED) {
				MARK_INVALID(GNUTLS_CERT_SIGNATURE_FAILURE);
			} else if (ret < 0) {
				MARK_INVALID(0);
			}
		}
	}

	/* we always check the issuer for unsupported critical extensions */
	if (issuer && check_for_unknown_exts(issuer) != 0) {
		if (!(flags & GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS)) {
			MARK_INVALID(GNUTLS_CERT_UNKNOWN_CRIT_EXTENSIONS);
		}
	}

	/* we only check the end-certificate for critical extensions; that
	 * way do not perform this check twice on the certificates when
	 * verifying a large list */
	if (end_cert && check_for_unknown_exts(cert) != 0) {
		if (!(flags & GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS)) {
			MARK_INVALID(GNUTLS_CERT_UNKNOWN_CRIT_EXTENSIONS);
		}
	}

	if (sigalg >= 0) {
		if (is_level_acceptable(cert, issuer, sigalg, flags) == 0) {
			MARK_INVALID(GNUTLS_CERT_INSECURE_ALGORITHM);
		}

		/* 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 (gnutls_sign_is_secure(sigalg) == 0 &&
		    _gnutls_is_broken_sig_allowed(sigalg, flags) == 0 &&
		    is_issuer(cert, cert) == 0) {
			MARK_INVALID(GNUTLS_CERT_INSECURE_ALGORITHM);
		}
	}

	/* Check activation/expiration times
	 */
	if (!(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) {
		/* check the time of the issuer first */
		if (issuer != NULL &&
		    !(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS)) {
			out |= check_time_status(issuer, vparams->now);
			if (out != 0) {
				gnutls_assert();
				result = 0;
			}
		}

		out |= check_time_status(cert, vparams->now);
		if (out != 0) {
			gnutls_assert();
			result = 0;
		}
	}

      cleanup:
	if (output)
		*output |= out;

	if (vparams->func) {
		if (result == 0) {
			out |= GNUTLS_CERT_INVALID;
		}
		vparams->func(cert, issuer, NULL, out);
	}
	_gnutls_free_datum(&cert_signed_data);
	_gnutls_free_datum(&cert_signature);

	return result;
}