summaryrefslogtreecommitdiffstats
path: root/lib/x509/ocsp.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/x509/ocsp.c2642
1 files changed, 2642 insertions, 0 deletions
diff --git a/lib/x509/ocsp.c b/lib/x509/ocsp.c
new file mode 100644
index 0000000..81f3d7e
--- /dev/null
+++ b/lib/x509/ocsp.c
@@ -0,0 +1,2642 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2016-2017 Red Hat, Inc.
+ *
+ * Author: Simon Josefsson, Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Online Certificate Status Protocol - RFC 2560
+ */
+
+#include "gnutls_int.h"
+#include <global.h>
+#include "errors.h"
+#include <libtasn1.h>
+#include <pk.h>
+#include "common.h"
+#include "verify-high.h"
+#include "x509.h"
+#include "ocsp.h"
+
+#include <gnutls/ocsp.h>
+#include <auth/cert.h>
+
+#include <assert.h>
+#include "intprops.h"
+
+typedef struct gnutls_ocsp_req_int {
+ asn1_node req;
+ unsigned init;
+} gnutls_ocsp_req_int;
+
+typedef struct gnutls_ocsp_resp_int {
+ asn1_node resp;
+ gnutls_datum_t response_type_oid;
+ asn1_node basicresp;
+ gnutls_datum_t der;
+ unsigned init;
+} gnutls_ocsp_resp_int;
+
+#define MAX_TIME 64
+
+/**
+ * gnutls_ocsp_req_init:
+ * @req: A pointer to the type to be initialized
+ *
+ * This function will initialize an OCSP request structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_ocsp_req_init(gnutls_ocsp_req_t * req)
+{
+ gnutls_ocsp_req_t tmp =
+ gnutls_calloc(1, sizeof(gnutls_ocsp_req_int));
+ int ret;
+
+ if (!tmp)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ ret = asn1_create_element(_gnutls_get_pkix(), "PKIX1.OCSPRequest",
+ &tmp->req);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(tmp);
+ return _gnutls_asn2err(ret);
+ }
+
+ *req = tmp;
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_deinit:
+ * @req: The data to be deinitialized
+ *
+ * This function will deinitialize a OCSP request structure.
+ **/
+void gnutls_ocsp_req_deinit(gnutls_ocsp_req_t req)
+{
+ if (!req)
+ return;
+
+ if (req->req)
+ asn1_delete_structure(&req->req);
+
+ req->req = NULL;
+ gnutls_free(req);
+}
+
+/**
+ * gnutls_ocsp_resp_init:
+ * @resp: A pointer to the type to be initialized
+ *
+ * This function will initialize an OCSP response structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_ocsp_resp_init(gnutls_ocsp_resp_t * resp)
+{
+ gnutls_ocsp_resp_t tmp =
+ gnutls_calloc(1, sizeof(gnutls_ocsp_resp_int));
+ int ret;
+
+ if (!tmp)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.OCSPResponse", &tmp->resp);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(tmp);
+ return _gnutls_asn2err(ret);
+ }
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.BasicOCSPResponse",
+ &tmp->basicresp);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&tmp->resp);
+ gnutls_free(tmp);
+ return _gnutls_asn2err(ret);
+ }
+
+ *resp = tmp;
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_deinit:
+ * @resp: The data to be deinitialized
+ *
+ * This function will deinitialize a OCSP response structure.
+ **/
+void gnutls_ocsp_resp_deinit(gnutls_ocsp_resp_t resp)
+{
+ if (!resp)
+ return;
+
+ if (resp->resp)
+ asn1_delete_structure(&resp->resp);
+ gnutls_free(resp->response_type_oid.data);
+ if (resp->basicresp)
+ asn1_delete_structure(&resp->basicresp);
+
+ resp->resp = NULL;
+ resp->basicresp = NULL;
+
+ gnutls_free(resp->der.data);
+ gnutls_free(resp);
+}
+
+/**
+ * gnutls_ocsp_req_import:
+ * @req: The data to store the parsed request.
+ * @data: DER encoded OCSP request.
+ *
+ * This function will convert the given DER encoded OCSP request to
+ * the native #gnutls_ocsp_req_t format. The output will be stored in
+ * @req.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_req_import(gnutls_ocsp_req_t req, const gnutls_datum_t * data)
+{
+ int ret = 0;
+
+ if (req == NULL || data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (req->init) {
+ /* Any earlier _asn1_strict_der_decode will modify the ASN.1
+ structure, so we need to replace it with a fresh
+ structure. */
+ asn1_delete_structure(&req->req);
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.OCSPRequest", &req->req);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+ }
+ req->init = 1;
+
+ ret = _asn1_strict_der_decode(&req->req, data->data, data->size, NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_import:
+ * @resp: The data to store the parsed response.
+ * @data: DER encoded OCSP response.
+ *
+ * This function will convert the given DER encoded OCSP response to
+ * the native #gnutls_ocsp_resp_t format. It also decodes the Basic
+ * OCSP Response part, if any. The output will be stored in @resp.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_import(gnutls_ocsp_resp_t resp,
+ const gnutls_datum_t * data)
+{
+ return gnutls_ocsp_resp_import2(resp, data, GNUTLS_X509_FMT_DER);
+}
+
+/**
+ * gnutls_ocsp_resp_import2:
+ * @resp: The data to store the parsed response.
+ * @data: DER or PEM encoded OCSP response.
+ * @fmt: DER or PEM
+ *
+ * This function will convert the given OCSP response to
+ * the native #gnutls_ocsp_resp_t format. It also decodes the Basic
+ * OCSP Response part, if any. The output will be stored in @resp.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.3
+ **/
+int
+gnutls_ocsp_resp_import2(gnutls_ocsp_resp_t resp,
+ const gnutls_datum_t *data,
+ gnutls_x509_crt_fmt_t fmt)
+{
+ int ret = 0;
+ gnutls_datum_t der;
+
+ if (resp == NULL || data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ der.data = data->data;
+ der.size = data->size;
+
+ if (fmt == GNUTLS_X509_FMT_PEM) {
+ ret = gnutls_pem_base64_decode2(BARE_PEM_OCSP_RESPONSE, data, &der);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+ }
+
+ if (resp->init != 0) {
+ /* Any earlier _asn1_strict_der_decode will modify the ASN.1
+ structure, so we need to replace it with a fresh
+ structure. */
+ asn1_delete_structure(&resp->resp);
+ if (resp->basicresp)
+ asn1_delete_structure(&resp->basicresp);
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.OCSPResponse",
+ &resp->resp);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.BasicOCSPResponse",
+ &resp->basicresp);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ gnutls_free(resp->der.data);
+ }
+
+ resp->init = 1;
+ ret = _asn1_strict_der_decode(&resp->resp, der.data, der.size, NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ if (gnutls_ocsp_resp_get_status(resp) !=
+ GNUTLS_OCSP_RESP_SUCCESSFUL) {
+ ret = GNUTLS_E_SUCCESS;
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_read_value(resp->resp,
+ "responseBytes.responseType",
+ &resp->response_type_oid);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+#define OCSP_BASIC "1.3.6.1.5.5.7.48.1.1"
+
+ if (resp->response_type_oid.size == sizeof(OCSP_BASIC) - 1
+ && memcmp(resp->response_type_oid.data, OCSP_BASIC,
+ resp->response_type_oid.size) == 0) {
+
+ ret =
+ _gnutls_x509_read_value(resp->resp,
+ "responseBytes.response", &resp->der);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _asn1_strict_der_decode(&resp->basicresp, resp->der.data, resp->der.size,
+ NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+ } else {
+ asn1_delete_structure(&resp->basicresp);
+ resp->basicresp = NULL;
+ }
+
+ ret = GNUTLS_E_SUCCESS;
+cleanup:
+ if (der.data != data->data)
+ gnutls_free(der.data);
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_req_export:
+ * @req: Holds the OCSP request
+ * @data: newly allocate buffer holding DER encoded OCSP request
+ *
+ * This function will export the OCSP request to DER format.
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ **/
+int gnutls_ocsp_req_export(gnutls_ocsp_req_const_t req, gnutls_datum_t * data)
+{
+ int ret;
+
+ if (req == NULL || data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* XXX remove when we support these fields */
+ (void)asn1_write_value(req->req, "tbsRequest.requestorName", NULL, 0);
+ (void)asn1_write_value(req->req, "optionalSignature", NULL, 0);
+
+ /* prune extension field if we don't have any extension */
+ ret = gnutls_ocsp_req_get_extension(req, 0, NULL, NULL, NULL);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ (void)asn1_write_value(req->req, "tbsRequest.requestExtensions",
+ NULL, 0);
+
+ return _gnutls_x509_get_raw_field(req->req, "", data);
+}
+
+/**
+ * gnutls_ocsp_resp_export:
+ * @resp: Holds the OCSP response
+ * @data: newly allocate buffer holding DER encoded OCSP response
+ *
+ * This function will export the OCSP response to DER format.
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ **/
+int gnutls_ocsp_resp_export(gnutls_ocsp_resp_const_t resp, gnutls_datum_t * data)
+{
+ return gnutls_ocsp_resp_export2(resp, data, GNUTLS_X509_FMT_DER);
+}
+
+/**
+ * gnutls_ocsp_resp_export2:
+ * @resp: Holds the OCSP response
+ * @data: newly allocate buffer holding DER or PEM encoded OCSP response
+ * @fmt: DER or PEM
+ *
+ * This function will export the OCSP response to DER or PEM format.
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ *
+ * Since: 3.6.3
+ **/
+int gnutls_ocsp_resp_export2(gnutls_ocsp_resp_const_t resp, gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t fmt)
+{
+ int ret;
+ gnutls_datum_t der;
+
+ if (resp == NULL || data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_get_raw_field(resp->resp, "", &der);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (fmt == GNUTLS_X509_FMT_DER) {
+ data->data = der.data;
+ data->size = der.size;
+ return ret;
+ } else {
+ ret = gnutls_pem_base64_encode2("OCSP RESPONSE", &der, data);
+ gnutls_free(der.data);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+ }
+}
+
+/**
+ * gnutls_ocsp_req_get_version:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ *
+ * This function will return the version of the OCSP request.
+ * Typically this is always 1 indicating version 1.
+ *
+ * Returns: version of OCSP request, or a negative error code on error.
+ **/
+int gnutls_ocsp_req_get_version(gnutls_ocsp_req_const_t req)
+{
+ if (req == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_version(req->req, "tbsRequest.version");
+}
+
+/**
+ * gnutls_ocsp_req_get_cert_id:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @indx: Specifies which extension OID to get. Use (0) to get the first one.
+ * @digest: output variable with #gnutls_digest_algorithm_t hash algorithm
+ * @issuer_name_hash: output buffer with hash of issuer's DN
+ * @issuer_key_hash: output buffer with hash of issuer's public key
+ * @serial_number: output buffer with serial number of certificate to check
+ *
+ * This function will return the certificate information of the
+ * @indx'ed request in the OCSP request. The information returned
+ * corresponds to the CertID structure:
+ *
+ * <informalexample><programlisting>
+ * CertID ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * issuerNameHash OCTET STRING, -- Hash of Issuer's DN
+ * issuerKeyHash OCTET STRING, -- Hash of Issuers public key
+ * serialNumber CertificateSerialNumber }
+ * </programlisting></informalexample>
+ *
+ * Each of the pointers to output variables may be NULL to indicate
+ * that the caller is not interested in that value.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned. If you have reached the last
+ * CertID available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
+ * returned.
+ **/
+int
+gnutls_ocsp_req_get_cert_id(gnutls_ocsp_req_const_t req,
+ unsigned indx,
+ gnutls_digest_algorithm_t * digest,
+ gnutls_datum_t * issuer_name_hash,
+ gnutls_datum_t * issuer_key_hash,
+ gnutls_datum_t * serial_number)
+{
+ gnutls_datum_t sa;
+ char name[MAX_NAME_SIZE];
+ int ret;
+
+ if (req == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestList.?%u.reqCert.hashAlgorithm.algorithm",
+ indx + 1);
+ ret = _gnutls_x509_read_value(req->req, name, &sa);
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ else if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_oid_to_digest((char *) sa.data);
+ _gnutls_free_datum(&sa);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (digest)
+ *digest = ret;
+
+ if (issuer_name_hash) {
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestList.?%u.reqCert.issuerNameHash",
+ indx + 1);
+ ret =
+ _gnutls_x509_read_value(req->req, name,
+ issuer_name_hash);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ if (issuer_key_hash) {
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestList.?%u.reqCert.issuerKeyHash",
+ indx + 1);
+ ret =
+ _gnutls_x509_read_value(req->req, name,
+ issuer_key_hash);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ if (issuer_name_hash)
+ gnutls_free(issuer_name_hash->data);
+ return ret;
+ }
+ }
+
+ if (serial_number) {
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestList.?%u.reqCert.serialNumber",
+ indx + 1);
+ ret =
+ _gnutls_x509_read_value(req->req, name, serial_number);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ if (issuer_name_hash)
+ gnutls_free(issuer_name_hash->data);
+ if (issuer_key_hash)
+ gnutls_free(issuer_key_hash->data);
+ return ret;
+ }
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_add_cert_id:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @digest: hash algorithm, a #gnutls_digest_algorithm_t value
+ * @issuer_name_hash: hash of issuer's DN
+ * @issuer_key_hash: hash of issuer's public key
+ * @serial_number: serial number of certificate to check
+ *
+ * This function will add another request to the OCSP request for a
+ * particular certificate having the issuer name hash of
+ * @issuer_name_hash and issuer key hash of @issuer_key_hash (both
+ * hashed using @digest) and serial number @serial_number.
+ *
+ * The information needed corresponds to the CertID structure:
+ *
+ * <informalexample><programlisting>
+ * CertID ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * issuerNameHash OCTET STRING, -- Hash of Issuer's DN
+ * issuerKeyHash OCTET STRING, -- Hash of Issuers public key
+ * serialNumber CertificateSerialNumber }
+ * </programlisting></informalexample>
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_add_cert_id(gnutls_ocsp_req_t req,
+ gnutls_digest_algorithm_t digest,
+ const gnutls_datum_t * issuer_name_hash,
+ const gnutls_datum_t * issuer_key_hash,
+ const gnutls_datum_t * serial_number)
+{
+ int result;
+ const char *oid;
+
+ if (req == NULL || issuer_name_hash == NULL
+ || issuer_key_hash == NULL || serial_number == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ oid = _gnutls_x509_digest_to_oid(hash_to_entry(digest));
+ if (oid == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result =
+ asn1_write_value(req->req, "tbsRequest.requestList", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_write_value
+ (req->req,
+ "tbsRequest.requestList.?LAST.reqCert.hashAlgorithm.algorithm",
+ oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* XXX we don't support any algorithm with parameters */
+ result = asn1_write_value
+ (req->req,
+ "tbsRequest.requestList.?LAST.reqCert.hashAlgorithm.parameters",
+ ASN1_NULL, ASN1_NULL_SIZE);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_write_value
+ (req->req,
+ "tbsRequest.requestList.?LAST.reqCert.issuerNameHash",
+ issuer_name_hash->data, issuer_name_hash->size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_write_value
+ (req->req,
+ "tbsRequest.requestList.?LAST.reqCert.issuerKeyHash",
+ issuer_key_hash->data, issuer_key_hash->size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_write_value
+ (req->req, "tbsRequest.requestList.?LAST.reqCert.serialNumber",
+ serial_number->data, serial_number->size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* XXX add separate function that can add extensions too */
+ result = asn1_write_value
+ (req->req,
+ "tbsRequest.requestList.?LAST.singleRequestExtensions", NULL,
+ 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_add_cert:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @digest: hash algorithm, a #gnutls_digest_algorithm_t value
+ * @issuer: issuer of @subject certificate
+ * @cert: certificate to request status for
+ *
+ * This function will add another request to the OCSP request for a
+ * particular certificate. The issuer name hash, issuer key hash, and
+ * serial number fields is populated as follows. The issuer name and
+ * the serial number is taken from @cert. The issuer key is taken
+ * from @issuer. The hashed values will be hashed using the @digest
+ * algorithm, normally %GNUTLS_DIG_SHA1.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_add_cert(gnutls_ocsp_req_t req,
+ gnutls_digest_algorithm_t digest,
+ gnutls_x509_crt_t issuer, gnutls_x509_crt_t cert)
+{
+ int ret;
+ gnutls_datum_t sn, tmp, inh, ikh;
+ uint8_t inh_buf[MAX_HASH_SIZE];
+ uint8_t ikh_buf[MAX_HASH_SIZE];
+ size_t inhlen = MAX_HASH_SIZE;
+ size_t ikhlen = MAX_HASH_SIZE;
+
+ if (req == NULL || issuer == NULL || cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_der_encode(cert->cert,
+ "tbsCertificate.issuer.rdnSequence",
+ &tmp, 0);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_fingerprint(digest, &tmp, inh_buf, &inhlen);
+ gnutls_free(tmp.data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+ inh.size = inhlen;
+ inh.data = inh_buf;
+
+ ret = _gnutls_x509_read_value
+ (issuer->cert,
+ "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey", &tmp);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_fingerprint(digest, &tmp, ikh_buf, &ikhlen);
+ gnutls_free(tmp.data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+ ikh.size = ikhlen;
+ ikh.data = ikh_buf;
+
+ ret =
+ _gnutls_x509_read_value(cert->cert,
+ "tbsCertificate.serialNumber", &sn);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_ocsp_req_add_cert_id(req, digest, &inh, &ikh, &sn);
+ gnutls_free(sn.data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_get_extension:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @indx: Specifies which extension OID to get. Use (0) to get the first one.
+ * @oid: will hold newly allocated buffer with OID of extension, may be NULL
+ * @critical: output variable with critical flag, may be NULL.
+ * @data: will hold newly allocated buffer with extension data, may be NULL
+ *
+ * This function will return all information about the requested
+ * extension in the OCSP request. The information returned is the
+ * OID, the critical flag, and the data itself. The extension OID
+ * will be stored as a string. Any of @oid, @critical, and @data may
+ * be NULL which means that the caller is not interested in getting
+ * that information back.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @oid->data and @data->data.
+ *
+ * Since 3.7.0 @oid->size does not account for the terminating null byte.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned. If you have reached the last
+ * extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will
+ * be returned.
+ **/
+int
+gnutls_ocsp_req_get_extension(gnutls_ocsp_req_const_t req,
+ unsigned indx,
+ gnutls_datum_t * oid,
+ unsigned int *critical,
+ gnutls_datum_t * data)
+{
+ int ret;
+ char str_critical[10];
+ char name[MAX_NAME_SIZE];
+ int len;
+
+ if (!req) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestExtensions.?%u.critical", indx + 1);
+ len = sizeof(str_critical);
+ ret = asn1_read_value(req->req, name, str_critical, &len);
+ if (ret == ASN1_ELEMENT_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ else if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ if (critical) {
+ if (str_critical[0] == 'T')
+ *critical = 1;
+ else
+ *critical = 0;
+ }
+
+ if (oid) {
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestExtensions.?%u.extnID",
+ indx + 1);
+ ret = _gnutls_x509_read_value(req->req, name, oid);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ if (data) {
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestExtensions.?%u.extnValue",
+ indx + 1);
+ ret = _gnutls_x509_read_value(req->req, name, data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ if (oid)
+ gnutls_free(oid->data);
+ return ret;
+ }
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_set_extension:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @oid: buffer with OID of extension as a string.
+ * @critical: critical flag, normally false.
+ * @data: the extension data
+ *
+ * This function will add an extension to the OCSP request. Calling
+ * this function multiple times for the same OID will overwrite values
+ * from earlier calls.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_set_extension(gnutls_ocsp_req_t req,
+ const char *oid,
+ unsigned int critical,
+ const gnutls_datum_t * data)
+{
+ if (req == NULL || oid == NULL || data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_set_extension(req->req, "tbsRequest.requestExtensions", oid,
+ data, critical);
+}
+
+/**
+ * gnutls_ocsp_req_get_nonce:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @critical: whether nonce extension is marked critical, or NULL
+ * @nonce: will hold newly allocated buffer with nonce data
+ *
+ * This function will return the OCSP request nonce extension data.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @nonce->data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_get_nonce(gnutls_ocsp_req_const_t req,
+ unsigned int *critical, gnutls_datum_t * nonce)
+{
+ int ret;
+ gnutls_datum_t tmp;
+
+ if (req == NULL || nonce == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_get_extension(req->req, "tbsRequest.requestExtensions",
+ GNUTLS_OCSP_NONCE, 0, &tmp, critical);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, tmp.data,
+ (size_t) tmp.size, nonce, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_free(tmp.data);
+ return ret;
+ }
+
+ gnutls_free(tmp.data);
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_set_nonce:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @critical: critical flag, normally false.
+ * @nonce: the nonce data
+ *
+ * This function will add an nonce extension to the OCSP request.
+ * Calling this function multiple times will overwrite values from
+ * earlier calls.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_set_nonce(gnutls_ocsp_req_t req,
+ unsigned int critical,
+ const gnutls_datum_t * nonce)
+{
+ int ret;
+ gnutls_datum_t dernonce;
+ unsigned char temp[SIZEOF_UNSIGNED_LONG_INT + 1];
+ int len;
+
+ if (req == NULL || nonce == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ asn1_length_der(nonce->size, temp, &len);
+
+ dernonce.size = 1 + len + nonce->size;
+ dernonce.data = gnutls_malloc(dernonce.size);
+ if (dernonce.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ dernonce.data[0] = '\x04';
+ memcpy(dernonce.data + 1, temp, len);
+ memcpy(dernonce.data + 1 + len, nonce->data, nonce->size);
+
+ ret = _gnutls_set_extension(req->req, "tbsRequest.requestExtensions",
+ GNUTLS_OCSP_NONCE, &dernonce, critical);
+ gnutls_free(dernonce.data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_req_randomize_nonce:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ *
+ * This function will add or update an nonce extension to the OCSP
+ * request with a newly generated random value.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int gnutls_ocsp_req_randomize_nonce(gnutls_ocsp_req_t req)
+{
+ int ret;
+ uint8_t rndbuf[23];
+ gnutls_datum_t nonce = { rndbuf, sizeof(rndbuf) };
+
+ if (req == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_rnd(GNUTLS_RND_NONCE, rndbuf, sizeof(rndbuf));
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_ocsp_req_set_nonce(req, 0, &nonce);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_status:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ *
+ * This function will return the status of a OCSP response, an
+ * #gnutls_ocsp_resp_status_t enumeration.
+ *
+ * Returns: status of OCSP request as a #gnutls_ocsp_resp_status_t, or
+ * a negative error code on error.
+ **/
+int gnutls_ocsp_resp_get_status(gnutls_ocsp_resp_const_t resp)
+{
+ uint8_t str[1];
+ int len, ret;
+
+ if (resp == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ len = sizeof(str);
+ ret = asn1_read_value(resp->resp, "responseStatus", str, &len);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ if (len != 1)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+
+ switch (str[0]) {
+ case GNUTLS_OCSP_RESP_SUCCESSFUL:
+ case GNUTLS_OCSP_RESP_MALFORMEDREQUEST:
+ case GNUTLS_OCSP_RESP_INTERNALERROR:
+ case GNUTLS_OCSP_RESP_TRYLATER:
+ case GNUTLS_OCSP_RESP_SIGREQUIRED:
+ case GNUTLS_OCSP_RESP_UNAUTHORIZED:
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+ }
+
+ return (int) str[0];
+}
+
+/**
+ * gnutls_ocsp_resp_get_response:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @response_type_oid: newly allocated output buffer with response type OID
+ * @response: newly allocated output buffer with DER encoded response
+ *
+ * This function will extract the response type OID in and the
+ * response data from an OCSP response. Normally the
+ * @response_type_oid is always "1.3.6.1.5.5.7.48.1.1" which means the
+ * @response should be decoded as a Basic OCSP Response, but
+ * technically other response types could be used.
+ *
+ * This function is typically only useful when you want to extract the
+ * response type OID of an response for diagnostic purposes.
+ * Otherwise gnutls_ocsp_resp_import() will decode the basic OCSP
+ * response part and the caller need not worry about that aspect.
+ *
+ * Since 3.7.0 @response_type_oid->size does not account for the terminating
+ * null byte.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_get_response(gnutls_ocsp_resp_const_t resp,
+ gnutls_datum_t * response_type_oid,
+ gnutls_datum_t * response)
+{
+ int ret;
+
+ if (resp == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (response_type_oid != NULL) {
+ ret =
+ _gnutls_x509_read_value(resp->resp,
+ "responseBytes.responseType",
+ response_type_oid);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ if (response != NULL) {
+ ret =
+ _gnutls_x509_read_value(resp->resp,
+ "responseBytes.response",
+ response);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_version:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ *
+ * This function will return the version of the Basic OCSP Response.
+ * Typically this is always 1 indicating version 1.
+ *
+ * Returns: version of Basic OCSP response, or a negative error code
+ * on error.
+ **/
+int gnutls_ocsp_resp_get_version(gnutls_ocsp_resp_const_t resp)
+{
+ if (resp == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_version(resp->resp, "tbsResponseData.version");
+}
+
+/**
+ * gnutls_ocsp_resp_get_responder:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @dn: newly allocated buffer with name
+ *
+ * This function will extract the name of the Basic OCSP Response in
+ * the provided buffer. The name will be in the form
+ * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string
+ * will be ASCII or UTF-8 encoded, depending on the certificate data.
+ *
+ * If the responder ID is not a name but a hash, this function
+ * will return zero and the @dn elements will be set to %NULL.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @dn->data.
+ *
+ * This function does not output a fully RFC4514 compliant string, if
+ * that is required see gnutls_ocsp_resp_get_responder2().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned. When no data exist it will
+ * return success and set @dn elements to zero.
+ **/
+int
+gnutls_ocsp_resp_get_responder(gnutls_ocsp_resp_const_t resp,
+ gnutls_datum_t * dn)
+{
+ int ret;
+
+ ret = gnutls_ocsp_resp_get_responder2(resp, dn, GNUTLS_X509_DN_FLAG_COMPAT);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ dn->data = NULL;
+ dn->size = 0;
+ return 0; /* for backwards compatibility */
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_resp_get_responder2:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @dn: newly allocated buffer with name
+ * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT
+ *
+ * This function will extract the name of the Basic OCSP Response in
+ * the provided buffer. The name will be in the form
+ * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string
+ * will be ASCII or UTF-8 encoded, depending on the certificate data.
+ *
+ * If the responder ID is not a name but a hash, this function
+ * will return zero and the @dn elements will be set to %NULL.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @dn->data.
+ *
+ * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output
+ * format will match the format output by previous to 3.5.6 versions of GnuTLS
+ * which was not not fully RFC4514-compliant.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned. When no data exist it will return
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE.
+ **/
+int
+gnutls_ocsp_resp_get_responder2(gnutls_ocsp_resp_const_t resp,
+ gnutls_datum_t * dn, unsigned flags)
+{
+ if (resp == NULL || dn == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ dn->data = NULL;
+ dn->size = 0;
+
+ return _gnutls_x509_get_dn(resp->basicresp,
+ "tbsResponseData.responderID.byName",
+ dn, flags);
+}
+
+/**
+ * gnutls_ocsp_resp_get_responder_by_key:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @type: should be %GNUTLS_OCSP_RESP_ID_KEY or %GNUTLS_OCSP_RESP_ID_DN
+ * @raw: newly allocated buffer with the raw ID
+ *
+ * This function will extract the raw key (or DN) ID of the Basic OCSP Response in
+ * the provided buffer. If the responder ID is not a key ID then
+ * this function will return %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @dn->data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_resp_get_responder_raw_id(gnutls_ocsp_resp_const_t resp,
+ unsigned type,
+ gnutls_datum_t * raw)
+{
+ int ret;
+
+ if (resp == NULL || raw == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (type == GNUTLS_OCSP_RESP_ID_KEY)
+ ret = _gnutls_x509_read_value(resp->basicresp, "tbsResponseData.responderID.byKey", raw);
+ else {
+ gnutls_datum_t tmp;
+
+ /* simply reading a CHOICE of CHOICE value doesn't work in libtasn1 */
+ ret = _gnutls_x509_get_raw_field2(resp->basicresp, &resp->der,
+ "tbsResponseData.responderID.byName",
+ &tmp);
+ if (ret >= 0) {
+ int real;
+ /* skip the tag */
+ if (tmp.size < 2) {
+ gnutls_assert();
+ ret = GNUTLS_E_ASN1_GENERIC_ERROR;
+ goto fail;
+ }
+
+ tmp.data++;
+ tmp.size--;
+
+ ret = asn1_get_length_der(tmp.data, tmp.size, &real);
+ if (ret < 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_ASN1_GENERIC_ERROR;
+ goto fail;
+ }
+
+ if (tmp.size < (unsigned)real) {
+ gnutls_assert();
+ ret = GNUTLS_E_ASN1_GENERIC_ERROR;
+ goto fail;
+ }
+
+ tmp.data+=real;
+ tmp.size-=real;
+
+ ret = _gnutls_set_datum(raw, tmp.data, tmp.size);
+ }
+ }
+
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || ret == GNUTLS_E_ASN1_VALUE_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ fail:
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_resp_get_produced:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ *
+ * This function will return the time when the OCSP response was
+ * signed.
+ *
+ * Returns: signing time, or (time_t)-1 on error.
+ **/
+time_t gnutls_ocsp_resp_get_produced(gnutls_ocsp_resp_const_t resp)
+{
+ char ttime[MAX_TIME];
+ int len, ret;
+ time_t c_time;
+
+ if (resp == NULL || resp->basicresp == NULL) {
+ gnutls_assert();
+ return (time_t) (-1);
+ }
+
+ len = sizeof(ttime) - 1;
+ ret =
+ asn1_read_value(resp->basicresp, "tbsResponseData.producedAt",
+ ttime, &len);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return (time_t) (-1);
+ }
+
+ c_time = _gnutls_x509_generalTime2gtime(ttime);
+
+ return c_time;
+}
+
+/**
+ * gnutls_ocsp_resp_check_crt:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @indx: Specifies response number to get. Use (0) to get the first one.
+ * @crt: The certificate to check
+ *
+ * This function will check whether the OCSP response
+ * is about the provided certificate.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ *
+ * Since: 3.1.3
+ **/
+int
+gnutls_ocsp_resp_check_crt(gnutls_ocsp_resp_const_t resp,
+ unsigned int indx, gnutls_x509_crt_t crt)
+{
+ int ret;
+ gnutls_digest_algorithm_t digest;
+ gnutls_datum_t rdn_hash = { NULL, 0 }, rserial = {
+ NULL, 0};
+ gnutls_datum_t cserial = { NULL, 0 };
+ gnutls_datum_t dn = { NULL, 0 };
+ uint8_t cdn_hash[MAX_HASH_SIZE];
+ size_t t, hash_len;
+
+ if (resp == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ ret =
+ gnutls_ocsp_resp_get_single(resp, indx, &digest, &rdn_hash,
+ NULL, &rserial, NULL, NULL, NULL,
+ NULL, NULL);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (rserial.size == 0 || digest == GNUTLS_DIG_UNKNOWN) {
+ ret = gnutls_assert_val(GNUTLS_E_OCSP_RESPONSE_ERROR);
+ goto cleanup;
+ }
+
+ hash_len = _gnutls_hash_get_algo_len(hash_to_entry(digest));
+ if (hash_len != rdn_hash.size) {
+ ret = gnutls_assert_val(GNUTLS_E_OCSP_RESPONSE_ERROR);
+ goto cleanup;
+ }
+
+ cserial.size = rserial.size;
+ cserial.data = gnutls_malloc(cserial.size);
+ if (cserial.data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ t = cserial.size;
+ ret = gnutls_x509_crt_get_serial(crt, cserial.data, &t);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ cserial.size = t;
+
+ if (rserial.size != cserial.size
+ || memcmp(cserial.data, rserial.data, rserial.size) != 0) {
+ ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_crt_get_raw_issuer_dn(crt, &dn);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_hash_fast(digest, dn.data, dn.size, cdn_hash);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (memcmp(cdn_hash, rdn_hash.data, hash_len) != 0) {
+ ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ gnutls_free(rdn_hash.data);
+ gnutls_free(rserial.data);
+ gnutls_free(cserial.data);
+ gnutls_free(dn.data);
+
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_resp_get_single:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @indx: Specifies response number to get. Use (0) to get the first one.
+ * @digest: output variable with #gnutls_digest_algorithm_t hash algorithm
+ * @issuer_name_hash: output buffer with hash of issuer's DN
+ * @issuer_key_hash: output buffer with hash of issuer's public key
+ * @serial_number: output buffer with serial number of certificate to check
+ * @cert_status: a certificate status, a #gnutls_ocsp_cert_status_t enum.
+ * @this_update: time at which the status is known to be correct.
+ * @next_update: when newer information will be available, or (time_t)-1 if unspecified
+ * @revocation_time: when @cert_status is %GNUTLS_OCSP_CERT_REVOKED, holds time of revocation.
+ * @revocation_reason: revocation reason, a #gnutls_x509_crl_reason_t enum.
+ *
+ * This function will return the certificate information of the
+ * @indx'ed response in the Basic OCSP Response @resp. The
+ * information returned corresponds to the OCSP SingleResponse structure
+ * except the final singleExtensions.
+ *
+ * Each of the pointers to output variables may be NULL to indicate
+ * that the caller is not interested in that value.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned. If you have reached the last
+ * CertID available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
+ * returned.
+ **/
+int
+gnutls_ocsp_resp_get_single(gnutls_ocsp_resp_const_t resp,
+ unsigned indx,
+ gnutls_digest_algorithm_t * digest,
+ gnutls_datum_t * issuer_name_hash,
+ gnutls_datum_t * issuer_key_hash,
+ gnutls_datum_t * serial_number,
+ unsigned int *cert_status,
+ time_t * this_update,
+ time_t * next_update,
+ time_t * revocation_time,
+ unsigned int *revocation_reason)
+{
+ char name[MAX_NAME_SIZE];
+ int ret, result;
+ char oidtmp[MAX_OID_SIZE];
+ int len;
+ char ttime[MAX_TIME];
+
+ /* initialize any allocated values to NULL, to allow deallocation
+ * on error. */
+ if (issuer_name_hash)
+ issuer_name_hash->data = NULL;
+ if (issuer_key_hash)
+ issuer_key_hash->data = NULL;
+ if (serial_number)
+ serial_number->data = NULL;
+
+ if (digest) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certID.hashAlgorithm.algorithm",
+ indx + 1);
+ len = sizeof(oidtmp);
+ result = asn1_read_value(resp->basicresp, name, oidtmp, &len);
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ } else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ ret = gnutls_oid_to_digest(oidtmp);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ *digest = ret;
+ }
+
+ if (issuer_name_hash) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certID.issuerNameHash",
+ indx + 1);
+ ret = _gnutls_x509_read_value(resp->basicresp, name,
+ issuer_name_hash);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ if (issuer_key_hash) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certID.issuerKeyHash",
+ indx + 1);
+ ret = _gnutls_x509_read_value(resp->basicresp, name,
+ issuer_key_hash);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ }
+
+ if (serial_number) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certID.serialNumber",
+ indx + 1);
+ ret = _gnutls_x509_read_value(resp->basicresp, name,
+ serial_number);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ }
+
+ if (cert_status) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certStatus",
+ indx + 1);
+
+ len = sizeof(oidtmp);
+ result = asn1_read_value(resp->basicresp, name, oidtmp, &len);
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto fail;
+ } else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto fail;
+ }
+
+ if (len == 5 && memcmp(oidtmp, "good", len) == 0)
+ *cert_status = GNUTLS_OCSP_CERT_GOOD;
+ else if (len == 8
+ && memcmp(oidtmp, "revoked", len) == 0)
+ *cert_status = GNUTLS_OCSP_CERT_REVOKED;
+ else if (len == 8
+ && memcmp(oidtmp, "unknown", len) == 0)
+ *cert_status = GNUTLS_OCSP_CERT_UNKNOWN;
+ else {
+ gnutls_assert();
+ ret = GNUTLS_E_ASN1_DER_ERROR;
+ goto fail;
+ }
+ }
+
+ if (this_update) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.thisUpdate",
+ indx + 1);
+ len = sizeof(ttime) - 1;
+ result = asn1_read_value(resp->basicresp, name, ttime, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = GNUTLS_E_ASN1_DER_ERROR;
+ goto fail;
+ } else {
+ *this_update =
+ _gnutls_x509_generalTime2gtime(ttime);
+ }
+ }
+
+ if (next_update) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.nextUpdate",
+ indx + 1);
+ len = sizeof(ttime) - 1;
+ result = asn1_read_value(resp->basicresp, name, ttime, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ *next_update = (time_t) (-1);
+ } else
+ *next_update =
+ _gnutls_x509_generalTime2gtime(ttime);
+ }
+
+ if (revocation_time) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certStatus."
+ "revoked.revocationTime", indx + 1);
+ len = sizeof(ttime) - 1;
+ result = asn1_read_value(resp->basicresp, name, ttime, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ *revocation_time = (time_t) (-1);
+ } else
+ *revocation_time =
+ _gnutls_x509_generalTime2gtime(ttime);
+ }
+
+ /* revocation_reason */
+ if (revocation_reason) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certStatus."
+ "revoked.revocationReason", indx + 1);
+
+ ret = _gnutls_x509_read_uint(resp->basicresp, name,
+ revocation_reason);
+ if (ret < 0)
+ *revocation_reason =
+ GNUTLS_X509_CRLREASON_UNSPECIFIED;
+ }
+
+ return GNUTLS_E_SUCCESS;
+ fail:
+ if (issuer_name_hash)
+ gnutls_free(issuer_name_hash->data);
+ if (issuer_key_hash)
+ gnutls_free(issuer_key_hash->data);
+ if (serial_number)
+ gnutls_free(serial_number->data);
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_resp_get_extension:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @indx: Specifies which extension OID to get. Use (0) to get the first one.
+ * @oid: will hold newly allocated buffer with OID of extension, may be NULL
+ * @critical: output variable with critical flag, may be NULL.
+ * @data: will hold newly allocated buffer with extension data, may be NULL
+ *
+ * This function will return all information about the requested
+ * extension in the OCSP response. The information returned is the
+ * OID, the critical flag, and the data itself. The extension OID
+ * will be stored as a string. Any of @oid, @critical, and @data may
+ * be NULL which means that the caller is not interested in getting
+ * that information back.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @oid->data and @data->data.
+ *
+ * Since 3.7.0 @oid->size does not account for the terminating null byte.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned. If you have reached the last
+ * extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will
+ * be returned.
+ **/
+int
+gnutls_ocsp_resp_get_extension(gnutls_ocsp_resp_const_t resp,
+ unsigned indx,
+ gnutls_datum_t * oid,
+ unsigned int *critical,
+ gnutls_datum_t * data)
+{
+ int ret;
+ char str_critical[10];
+ char name[MAX_NAME_SIZE];
+ int len;
+
+ if (!resp) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responseExtensions.?%u.critical",
+ indx + 1);
+ len = sizeof(str_critical);
+ ret = asn1_read_value(resp->basicresp, name, str_critical, &len);
+ if (ret == ASN1_ELEMENT_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ else if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ if (critical) {
+ if (str_critical[0] == 'T')
+ *critical = 1;
+ else
+ *critical = 0;
+ }
+
+ if (oid) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responseExtensions.?%u.extnID",
+ indx + 1);
+ ret = _gnutls_x509_read_value(resp->basicresp, name, oid);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ if (data) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responseExtensions.?%u.extnValue",
+ indx + 1);
+ ret = _gnutls_x509_read_value(resp->basicresp, name, data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ if (oid)
+ gnutls_free(oid->data);
+ return ret;
+ }
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_nonce:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @critical: whether nonce extension is marked critical
+ * @nonce: will hold newly allocated buffer with nonce data
+ *
+ * This function will return the Basic OCSP Response nonce extension
+ * data.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @nonce->data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_resp_get_nonce(gnutls_ocsp_resp_const_t resp,
+ unsigned int *critical, gnutls_datum_t * nonce)
+{
+ int ret;
+ gnutls_datum_t tmp;
+
+ ret =
+ _gnutls_get_extension(resp->basicresp,
+ "tbsResponseData.responseExtensions",
+ GNUTLS_OCSP_NONCE, 0, &tmp, critical);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, tmp.data,
+ (size_t) tmp.size, nonce, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_free(tmp.data);
+ return ret;
+ }
+
+ gnutls_free(tmp.data);
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_signature_algorithm:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ *
+ * This function will return a value of the #gnutls_sign_algorithm_t
+ * enumeration that is the signature algorithm that has been used to
+ * sign the OCSP response.
+ *
+ * Returns: a #gnutls_sign_algorithm_t value, or a negative error code
+ * on error.
+ **/
+int gnutls_ocsp_resp_get_signature_algorithm(gnutls_ocsp_resp_const_t resp)
+{
+ int ret;
+ gnutls_datum_t sa;
+
+ ret = _gnutls_x509_read_value(resp->basicresp,
+ "signatureAlgorithm.algorithm", &sa);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_oid_to_sign((char *) sa.data);
+
+ _gnutls_free_datum(&sa);
+
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_resp_get_signature:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @sig: newly allocated output buffer with signature data
+ *
+ * This function will extract the signature field of a OCSP response.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_get_signature(gnutls_ocsp_resp_const_t resp,
+ gnutls_datum_t * sig)
+{
+ int ret;
+
+ if (resp == NULL || sig == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_read_value(resp->basicresp, "signature", sig);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_certs:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @certs: newly allocated array with #gnutls_x509_crt_t certificates
+ * @ncerts: output variable with number of allocated certs.
+ *
+ * This function will extract the X.509 certificates found in the
+ * Basic OCSP Response. The @certs output variable will hold a newly
+ * allocated zero-terminated array with X.509 certificates.
+ *
+ * Every certificate in the array needs to be de-allocated with
+ * gnutls_x509_crt_deinit() and the array itself must be freed using
+ * gnutls_free().
+ *
+ * Both the @certs and @ncerts variables may be NULL. Then the
+ * function will work as normal but will not return the NULL:d
+ * information. This can be used to get the number of certificates
+ * only, or to just get the certificate array without its size.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_get_certs(gnutls_ocsp_resp_const_t resp,
+ gnutls_x509_crt_t ** certs, size_t * ncerts)
+{
+ int ret;
+ size_t ctr = 0, i;
+ gnutls_x509_crt_t *tmpcerts = NULL, *tmpcerts2;
+ gnutls_datum_t c = { NULL, 0 };
+
+ if (resp == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ tmpcerts = gnutls_malloc(sizeof(*tmpcerts));
+ if (tmpcerts == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ for (;;) {
+ char name[MAX_NAME_SIZE];
+
+ snprintf(name, sizeof(name), "certs.?%u",
+ (unsigned int) (ctr + 1));
+ ret =
+ _gnutls_x509_der_encode(resp->basicresp, name, &c, 0);
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+ break;
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (unlikely(INT_ADD_OVERFLOW(ctr, 2))) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto error;
+ }
+
+ tmpcerts2 = _gnutls_reallocarray_fast(tmpcerts, ctr + 2,
+ sizeof(*tmpcerts));
+ if (tmpcerts2 == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto error;
+ }
+ tmpcerts = tmpcerts2;
+
+ ret = gnutls_x509_crt_init(&tmpcerts[ctr]);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto error;
+ }
+ ctr++;
+
+ ret = gnutls_x509_crt_import(tmpcerts[ctr - 1], &c,
+ GNUTLS_X509_FMT_DER);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto error;
+ }
+
+ gnutls_free(c.data);
+ }
+
+ tmpcerts[ctr] = NULL;
+
+ if (ncerts)
+ *ncerts = ctr;
+ if (certs)
+ *certs = tmpcerts;
+ else {
+ /* clean up memory */
+ ret = GNUTLS_E_SUCCESS;
+ goto error;
+ }
+
+ return GNUTLS_E_SUCCESS;
+
+ error:
+ gnutls_free(c.data);
+ for (i = 0; i < ctr; i++)
+ gnutls_x509_crt_deinit(tmpcerts[i]);
+ gnutls_free(tmpcerts);
+ return ret;
+}
+
+/* Search the OCSP response for a certificate matching the responderId
+ mentioned in the OCSP response. */
+static gnutls_x509_crt_t find_signercert(gnutls_ocsp_resp_const_t resp)
+{
+ int rc;
+ gnutls_x509_crt_t *certs = NULL;
+ size_t ncerts = 0, i;
+ gnutls_datum_t riddn = {NULL, 0};
+ gnutls_datum_t keyid = {NULL, 0};
+ gnutls_x509_crt_t signercert = NULL;
+
+ rc = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_DN, &riddn);
+ if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ rc = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_KEY, &keyid);
+ }
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ rc = gnutls_ocsp_resp_get_certs(resp, &certs, &ncerts);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ signercert = NULL;
+ goto quit;
+ }
+
+ for (i = 0; i < ncerts; i++) {
+ assert(certs[i] != NULL);
+ _gnutls_cert_log("checking whether signed against", certs[i]);
+ if (keyid.data != NULL) {
+ uint8_t digest[64]; /* to support longer key IDs */
+ gnutls_datum_t spki;
+ size_t digest_size = sizeof(digest);
+ int len;
+
+ _gnutls_debug_log("checking key ID against SPK identifier\n");
+
+ /* check subject key identifier as well, some certificates
+ * match that, but not the hash */
+ rc = gnutls_x509_crt_get_subject_key_id(certs[i], digest, &digest_size, NULL);
+ if (rc >= 0 && digest_size == keyid.size &&
+ memcmp(keyid.data, digest, digest_size) == 0) {
+ signercert = certs[i];
+ goto quit;
+ }
+
+ _gnutls_debug_log("checking key ID against SPKI hash\n");
+
+ /* continue with checking the hash */
+ rc = _gnutls_x509_get_raw_field2(certs[i]->cert, &certs[i]->der,
+ "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey",
+ &spki);
+ if (rc < 0 || spki.size < 6) {
+ gnutls_assert();
+ signercert = NULL;
+ continue;
+ }
+
+ /* For some reason the protocol requires we skip the
+ * tag, length and number of unused bits.
+ */
+ if (spki.data[0] != 0x03) { /* bit string */
+ gnutls_assert();
+ signercert = NULL;
+ continue;
+ }
+
+ rc = asn1_get_length_der(spki.data+1, spki.size-1, &len);
+ if (rc <= 0) {
+ gnutls_assert();
+ signercert = NULL;
+ continue;
+ }
+ len += 1+1; /* skip unused bits as well */
+ if (len >= (int)spki.size) {
+ gnutls_assert();
+ signercert = NULL;
+ continue;
+ }
+
+ rc = gnutls_hash_fast(GNUTLS_DIG_SHA1, spki.data+len, spki.size-len, digest);
+ if (rc < 0) {
+ gnutls_assert();
+ signercert = NULL;
+ continue;
+ }
+
+ if ((20 == keyid.size) &&
+ memcmp(keyid.data, digest, 20) == 0) {
+ signercert = certs[i];
+ goto quit;
+ }
+ gnutls_assert();
+ } else {
+ _gnutls_debug_log("checking issuer DN\n");
+
+ assert(riddn.data != NULL);
+ if ((certs[i]->raw_dn.size == riddn.size)
+ && memcmp(riddn.data, certs[i]->raw_dn.data, riddn.size) == 0) {
+ signercert = certs[i];
+ goto quit;
+ }
+ gnutls_assert();
+ }
+ }
+
+ gnutls_assert();
+ signercert = NULL;
+
+ quit:
+ gnutls_free(riddn.data);
+ gnutls_free(keyid.data);
+ for (i = 0; i < ncerts; i++)
+ if (certs[i] != signercert)
+ gnutls_x509_crt_deinit(certs[i]);
+ gnutls_free(certs);
+ return signercert;
+}
+
+static int
+_ocsp_resp_verify_direct(gnutls_ocsp_resp_const_t resp,
+ gnutls_x509_crt_t signercert,
+ unsigned int *verify, unsigned int flags)
+{
+ gnutls_datum_t sig = { NULL };
+ gnutls_datum_t data = { NULL };
+ gnutls_pubkey_t pubkey = NULL;
+ int sigalg;
+ int rc;
+
+ if (resp == NULL || signercert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ rc = gnutls_ocsp_resp_get_signature_algorithm(resp);
+ if (rc < 0) {
+ gnutls_assert();
+ goto done;
+ }
+ sigalg = rc;
+
+ rc = _gnutls_x509_get_raw_field2(resp->basicresp, &resp->der, "tbsResponseData", &data);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto done;
+ }
+
+ rc = gnutls_pubkey_init(&pubkey);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto done;
+ }
+
+ _gnutls_cert_log("ocsp signer", signercert);
+
+ rc = gnutls_pubkey_import_x509(pubkey, signercert, 0);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto done;
+ }
+
+ rc = gnutls_ocsp_resp_get_signature(resp, &sig);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto done;
+ }
+
+ rc = gnutls_pubkey_verify_data2(pubkey, sigalg, flags, &data, &sig);
+ if (rc == GNUTLS_E_PK_SIG_VERIFY_FAILED) {
+ gnutls_assert();
+ *verify = GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE;
+ } else if (rc < 0) {
+ gnutls_assert();
+ goto done;
+ } else
+ *verify = 0;
+
+ rc = GNUTLS_E_SUCCESS;
+
+ done:
+ gnutls_free(sig.data);
+ gnutls_pubkey_deinit(pubkey);
+
+ return rc;
+}
+
+static inline unsigned int vstatus_to_ocsp_status(unsigned int status)
+{
+ unsigned int ostatus;
+
+ if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+ ostatus = GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM;
+ else if (status & GNUTLS_CERT_NOT_ACTIVATED)
+ ostatus = GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED;
+ else if (status & GNUTLS_CERT_EXPIRED)
+ ostatus = GNUTLS_OCSP_VERIFY_CERT_EXPIRED;
+ else
+ ostatus = GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER;
+
+ return ostatus;
+}
+
+static int check_ocsp_purpose(gnutls_x509_crt_t signercert)
+{
+ char oidtmp[MAX_OID_SIZE];
+ size_t oidsize;
+ int indx, rc;
+
+ for (indx = 0;; indx++) {
+ oidsize = sizeof(oidtmp);
+ rc = gnutls_x509_crt_get_key_purpose_oid(signercert, indx,
+ oidtmp, &oidsize,
+ NULL);
+
+ if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ return rc;
+ } else if (rc == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ gnutls_assert();
+ continue;
+ } else if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_assert_val(rc);
+ }
+
+ if (memcmp(oidtmp, GNUTLS_KP_OCSP_SIGNING, oidsize) != 0) {
+ gnutls_assert();
+ continue;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_ocsp_resp_verify_direct:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @issuer: certificate believed to have signed the response
+ * @verify: output variable with verification status, an #gnutls_ocsp_verify_reason_t
+ * @flags: verification flags from #gnutls_certificate_verify_flags
+ *
+ * Verify signature of the Basic OCSP Response against the public key
+ * in the @issuer certificate.
+ *
+ * The output @verify variable will hold verification status codes
+ * (e.g., %GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND,
+ * %GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) which are only valid if the
+ * function returned %GNUTLS_E_SUCCESS.
+ *
+ * Note that the function returns %GNUTLS_E_SUCCESS even when
+ * verification failed. The caller must always inspect the @verify
+ * variable to find out the verification status.
+ *
+ * The @flags variable should be 0 for now.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_verify_direct(gnutls_ocsp_resp_const_t resp,
+ gnutls_x509_crt_t issuer,
+ unsigned int *verify, unsigned int flags)
+{
+ gnutls_x509_crt_t signercert;
+ int rc;
+
+ if (resp == NULL || issuer == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ signercert = find_signercert(resp);
+ if (!signercert) {
+ signercert = issuer;
+ } else if (!gnutls_x509_crt_equals(signercert, issuer)) {
+
+ /* response contains a signer. Verify him */
+
+ unsigned int vtmp;
+
+ rc = gnutls_x509_crt_verify(signercert, &issuer, 1, flags,
+ &vtmp);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto done;
+ }
+
+ if (vtmp != 0) {
+ _gnutls_reason_log("cert verification", vtmp);
+ *verify = vstatus_to_ocsp_status(vtmp);
+ gnutls_assert();
+ rc = GNUTLS_E_SUCCESS;
+ goto done;
+ }
+
+ rc = check_ocsp_purpose(signercert);
+ if (rc < 0) {
+ gnutls_assert();
+ *verify = GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR;
+ rc = GNUTLS_E_SUCCESS;
+ goto done;
+ }
+ }
+
+ rc = _ocsp_resp_verify_direct(resp, signercert, verify, flags);
+
+ done:
+ if (signercert != issuer)
+ gnutls_x509_crt_deinit(signercert);
+
+ return rc;
+}
+
+/**
+ * gnutls_ocsp_resp_verify:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @trustlist: trust anchors as a #gnutls_x509_trust_list_t type
+ * @verify: output variable with verification status, an #gnutls_ocsp_verify_reason_t
+ * @flags: verification flags from #gnutls_certificate_verify_flags
+ *
+ * Verify signature of the Basic OCSP Response against the public key
+ * in the certificate of a trusted signer. The @trustlist should be
+ * populated with trust anchors. The function will extract the signer
+ * certificate from the Basic OCSP Response and will verify it against
+ * the @trustlist. A trusted signer is a certificate that is either
+ * in @trustlist, or it is signed directly by a certificate in
+ * @trustlist and has the id-ad-ocspSigning Extended Key Usage bit
+ * set.
+ *
+ * The output @verify variable will hold verification status codes
+ * (e.g., %GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND,
+ * %GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) which are only valid if the
+ * function returned %GNUTLS_E_SUCCESS.
+ *
+ * Note that the function returns %GNUTLS_E_SUCCESS even when
+ * verification failed. The caller must always inspect the @verify
+ * variable to find out the verification status.
+ *
+ * The @flags variable should be 0 for now.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_verify(gnutls_ocsp_resp_const_t resp,
+ gnutls_x509_trust_list_t trustlist,
+ unsigned int *verify, unsigned int flags)
+{
+ gnutls_x509_crt_t signercert = NULL;
+ int rc;
+
+ /* Algorithm:
+ 1. Find signer cert.
+ 1a. Search in OCSP response Certificate field for responderID.
+ 1b. Verify that signer cert is trusted.
+ 2a. It is in trustlist?
+ 2b. It has OCSP key usage and directly signed by a CA in trustlist?
+ 3. Verify signature of Basic Response using public key from signer cert.
+ */
+
+ signercert = find_signercert(resp);
+ if (!signercert) {
+ gnutls_datum_t dn;
+
+ rc = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_DN, &dn);
+ if (rc < 0) {
+ gnutls_assert();
+ *verify = GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND;
+ rc = GNUTLS_E_SUCCESS;
+ goto done;
+ }
+
+ rc = gnutls_x509_trust_list_get_issuer_by_dn(trustlist, &dn, &signercert, 0);
+ gnutls_free(dn.data);
+
+ if (rc < 0) {
+ gnutls_assert();
+ *verify = GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND;
+ rc = GNUTLS_E_SUCCESS;
+ goto done;
+ }
+ } else {
+ /* Either the signer is directly trusted (i.e., in trustlist) or it
+ is directly signed by something in trustlist and has proper OCSP
+ extkeyusage. */
+ rc = _gnutls_trustlist_inlist(trustlist, signercert);
+ if (rc == 0) {
+ /* not in trustlist, need to verify signature and bits */
+ unsigned vtmp;
+ gnutls_typed_vdata_st vdata;
+
+ vdata.type = GNUTLS_DT_KEY_PURPOSE_OID;
+ vdata.data = (void*)GNUTLS_KP_OCSP_SIGNING;
+ vdata.size = 0;
+
+ gnutls_assert();
+
+ rc = gnutls_x509_trust_list_verify_crt2(trustlist,
+ &signercert, 1,
+ &vdata, 1,
+ flags, &vtmp, NULL);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto done;
+ }
+
+ if (vtmp != 0) {
+ *verify = vstatus_to_ocsp_status(vtmp);
+ gnutls_assert();
+ rc = GNUTLS_E_SUCCESS;
+ goto done;
+ }
+
+ rc = check_ocsp_purpose(signercert);
+ if (rc < 0) {
+ gnutls_assert();
+ *verify = GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR;
+ rc = GNUTLS_E_SUCCESS;
+ goto done;
+ }
+ }
+ }
+
+ rc = _ocsp_resp_verify_direct(resp, signercert, verify, flags);
+
+ done:
+ gnutls_x509_crt_deinit(signercert);
+
+ return rc;
+}
+
+/**
+ * gnutls_x509_ocsp_resp_list_import2:
+ * @ocsps: Will hold the parsed OCSP response list.
+ * @size: It will contain the size of the list.
+ * @resp_data: The PEM encoded OCSP list.
+ * @format: One of %GNUTLS_X509_FMT_PEM or %GNUTLS_X509_FMT_DER
+ * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags.
+ *
+ * This function will convert the given PEM encoded OCSP response list
+ * to the native gnutls_ocsp_resp_t format. The output will be stored
+ * in @ocsps which will be allocated and initialized.
+ *
+ * The OCSP responses should have a header of "OCSP RESPONSE".
+ *
+ * To deinitialize responses, you need to deinitialize each %gnutls_ocsp_resp_t
+ * structure independently, and use gnutls_free() at @ocsps.
+ *
+ * In PEM files, when no OCSP responses are detected
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ *
+ * Returns: the number of responses read or a negative error value.
+ *
+ * Since: 3.6.3
+ **/
+int
+gnutls_ocsp_resp_list_import2(gnutls_ocsp_resp_t **ocsps,
+ unsigned int *size,
+ const gnutls_datum_t *resp_data,
+ gnutls_x509_crt_fmt_t format,
+ unsigned int flags)
+{
+ gnutls_ocsp_resp_t resp = NULL;
+ gnutls_ocsp_resp_t *new_ocsps;
+ int ret;
+ unsigned i;
+
+
+ if (format == GNUTLS_X509_FMT_PEM) {
+ /* load multiple responses */
+ gnutls_datum_t p = {resp_data->data, resp_data->size};
+
+ *size = 0;
+ *ocsps = NULL;
+
+ p.data = memmem(p.data, p.size, PEM_OCSP_RESPONSE,
+ sizeof(PEM_OCSP_RESPONSE)-1);
+ if (p.data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ goto cleanup;
+ }
+
+ p.size -= p.data - resp_data->data;
+ if (p.size <= 0) {
+ ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ goto cleanup;
+ }
+
+ do {
+ ret = gnutls_ocsp_resp_init(&resp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = gnutls_ocsp_resp_import2(resp, &p, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (unlikely(INT_ADD_OVERFLOW(*size, 1))) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto fail;
+ }
+
+ new_ocsps = _gnutls_reallocarray(*ocsps,
+ *size + 1,
+ sizeof(gnutls_ocsp_resp_t));
+ if (new_ocsps == NULL) {
+ resp = NULL;
+ gnutls_assert();
+ goto fail;
+ }
+
+ new_ocsps[*size] = resp;
+ resp = NULL;
+ (*size)++;
+ *ocsps = new_ocsps;
+
+ p.data++;
+ p.size--;
+
+ p.data = memmem(p.data, p.size, PEM_OCSP_RESPONSE,
+ sizeof(PEM_OCSP_RESPONSE)-1);
+ if (p.data == NULL)
+ break;
+ p.size = resp_data->size - (p.data - resp_data->data);
+ } while(p.size > 0);
+ } else {
+ /* DER: load a single response */
+ ret = gnutls_ocsp_resp_init(&resp);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ ret = gnutls_ocsp_resp_import2(resp, resp_data, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ *ocsps = gnutls_malloc(sizeof(gnutls_ocsp_resp_t));
+ if (*ocsps == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ (*ocsps)[0] = resp;
+ resp = NULL;
+ *size = 1;
+ }
+
+ ret = 0;
+ goto cleanup;
+
+ fail:
+ for (i=0;i<*size;i++) {
+ gnutls_ocsp_resp_deinit((*ocsps)[i]);
+ }
+ gnutls_free(*ocsps);
+
+ cleanup:
+ if (resp)
+ gnutls_ocsp_resp_deinit(resp);
+ return ret;
+}
+
+/* This returns -1 if the OCSP response is invalid (revoked) or its
+ * data are too old. It returns -2 if it cannot determine the expiration
+ * time, and would otherwise treat it as too old.
+ * Otherwise it returns the time after which that data is invalid.
+ */
+time_t _gnutls_ocsp_get_validity(gnutls_ocsp_resp_const_t resp)
+{
+ unsigned int cert_status;
+ time_t rtime, vtime, ntime, now;
+ int ret;
+
+ ret = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, NULL,
+ &cert_status, &vtime, &ntime,
+ &rtime, NULL);
+ if (ret < 0) {
+ _gnutls_debug_log("There was an error parsing the OCSP response: %s\n",
+ gnutls_strerror(ret));
+ return gnutls_assert_val(-1);
+ }
+
+ if (cert_status != GNUTLS_OCSP_CERT_GOOD &&
+ cert_status != GNUTLS_OCSP_CERT_UNKNOWN) {
+ _gnutls_debug_log("The OCSP response status (%d) is invalid\n",
+ cert_status);
+ return gnutls_assert_val(-1);
+ }
+
+ now = gnutls_time(0);
+
+ if (ntime == -1) {
+ /* This is a problematic case, and there is no consensus on how
+ * to treat these responses. It doesn't contain the time after which
+ * the response is invalid, thus it is an OCSP response effectively
+ * valid forever defeating the purpose of OCSP. We set here the same
+ * limit we apply when verifying responses. */
+ if (now - vtime > MAX_OCSP_VALIDITY_SECS) {
+ _gnutls_debug_log("The OCSP response is old\n");
+ return gnutls_assert_val(-2);
+ }
+
+ return now + MAX_OCSP_VALIDITY_SECS;
+ } else {
+ /* there is a newer OCSP answer, don't trust this one */
+ if (ntime < now) {
+ _gnutls_debug_log("There is a newer OCSP response\n");
+ return gnutls_assert_val(-1);
+ }
+
+ return ntime;
+ }
+}
+
+const char *_gnutls_ocsp_verify_status_to_str(gnutls_ocsp_verify_reason_t r, char out[MAX_OCSP_MSG_SIZE])
+{
+ gnutls_buffer_st str;
+ gnutls_datum_t buf;
+ int ret;
+
+ _gnutls_buffer_init(&str);
+
+ if (r == 0)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response is trusted. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response's signer could not be found. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("Error in the signer's key usageflags. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response's signer is not trusted. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response depends on insecure algorithms. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response's signature cannot be validated. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response's signer's certificate is not activated. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_CERT_EXPIRED)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response's signer's certificate is expired. "));
+
+ ret = _gnutls_buffer_to_datum(&str, &buf, 1);
+ if (ret < 0)
+ return _("Memory error");
+
+ snprintf(out, MAX_OCSP_MSG_SIZE, "%s", buf.data);
+ gnutls_free(buf.data);
+
+ return out;
+}