diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/includes/Makefile.am | 2 | ||||
-rw-r--r-- | lib/includes/gnutls/ocsp.h | 254 | ||||
-rw-r--r-- | lib/libgnutls.map | 32 | ||||
-rw-r--r-- | lib/pkix.asn | 109 | ||||
-rw-r--r-- | lib/pkix_asn1_tab.c | 105 | ||||
-rw-r--r-- | lib/x509/Makefile.am | 4 | ||||
-rw-r--r-- | lib/x509/ocsp.c | 2160 | ||||
-rw-r--r-- | lib/x509/ocsp_output.c | 631 |
8 files changed, 3295 insertions, 2 deletions
diff --git a/lib/includes/Makefile.am b/lib/includes/Makefile.am index 5da8bda6dd..31650c1c3e 100644 --- a/lib/includes/Makefile.am +++ b/lib/includes/Makefile.am @@ -20,7 +20,7 @@ nobase_include_HEADERS = gnutls/x509.h gnutls/pkcs12.h gnutls/compat.h \ gnutls/openpgp.h gnutls/crypto.h gnutls/pkcs11.h \ - gnutls/abstract.h gnutls/dtls.h + gnutls/abstract.h gnutls/dtls.h gnutls/ocsp.h if ENABLE_CXX nobase_include_HEADERS += gnutls/gnutlsxx.h diff --git a/lib/includes/gnutls/ocsp.h b/lib/includes/gnutls/ocsp.h new file mode 100644 index 0000000000..f748d75c3f --- /dev/null +++ b/lib/includes/gnutls/ocsp.h @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2011-2012 Free Software Foundation, Inc. + * + * Author: Simon Josefsson + * + * 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 3 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 <http://www.gnu.org/licenses/> + * + */ + +/* Online Certificate Status Protocol - RFC 2560 + */ + +#ifndef GNUTLS_OCSP_H +#define GNUTLS_OCSP_H + +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define GNUTLS_OCSP_NONCE "1.3.6.1.5.5.7.48.1.2" + + /** + * gnutls_ocsp_print_formats_t: + * @GNUTLS_OCSP_PRINT_FULL: Full information about OCSP request/response. + * + * Enumeration of different OCSP printing variants. + */ + typedef enum gnutls_ocsp_print_formats_t + { + GNUTLS_OCSP_PRINT_FULL = 0, + } gnutls_ocsp_print_formats_t; + + /** + * gnutls_ocsp_resp_status_t: + * @GNUTLS_OCSP_RESP_SUCCESSFUL: Response has valid confirmations. + * @GNUTLS_OCSP_RESP_MALFORMEDREQUEST: Illegal confirmation request + * @GNUTLS_OCSP_RESP_INTERNALERROR: Internal error in issuer + * @GNUTLS_OCSP_RESP_TRYLATER: Try again later + * @GNUTLS_OCSP_RESP_SIGREQUIRED: Must sign the request + * @GNUTLS_OCSP_RESP_UNAUTHORIZED: Request unauthorized + * + * Enumeration of different OCSP response status codes. + */ + typedef enum gnutls_ocsp_resp_status_t + { + GNUTLS_OCSP_RESP_SUCCESSFUL = 0, + GNUTLS_OCSP_RESP_MALFORMEDREQUEST = 1, + GNUTLS_OCSP_RESP_INTERNALERROR = 2, + GNUTLS_OCSP_RESP_TRYLATER = 3, + GNUTLS_OCSP_RESP_SIGREQUIRED = 5, + GNUTLS_OCSP_RESP_UNAUTHORIZED = 6 + } gnutls_ocsp_resp_status_t; + + /** + * gnutls_ocsp_cert_status_t: + * @GNUTLS_OCSP_CERT_GOOD: Positive response to status inquiry. + * @GNUTLS_OCSP_CERT_REVOKED: Certificate has been revoked. + * @GNUTLS_OCSP_CERT_UNKNOWN: The responder doesn't know about the + * certificate. + * + * Enumeration of different OCSP response certificate status codes. + */ + typedef enum gnutls_ocsp_cert_status_t + { + GNUTLS_OCSP_CERT_GOOD = 0, + GNUTLS_OCSP_CERT_REVOKED = 1, + GNUTLS_OCSP_CERT_UNKNOWN = 2 + } gnutls_ocsp_cert_status_t; + + /** + * gnutls_x509_crl_reason_t: + * @GNUTLS_X509_CRLREASON_UNSPECIFIED: Unspecified reason. + * @GNUTLS_X509_CRLREASON_KEYCOMPROMISE: Private key compromised. + * @GNUTLS_X509_CRLREASON_CACOMPROMISE: CA compromised. + * @GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED: Affiliation has changed. + * @GNUTLS_X509_CRLREASON_SUPERSEDED: Certificate superseded. + * @GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION: Operation has ceased. + * @GNUTLS_X509_CRLREASON_CERTIFICATEHOLD: Certificate is on hold. + * @GNUTLS_X509_CRLREASON_REMOVEFROMCRL: Will be removed from delta CRL. + * @GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN: Privilege withdrawn. + * @GNUTLS_X509_CRLREASON_AACOMPROMISE: AA compromised. + * + * Enumeration of different reason codes. Note that this + * corresponds to the CRLReason ASN.1 enumeration type, and not the + * ReasonFlags ASN.1 bit string. + */ + typedef enum gnutls_x509_crl_reason_t + { + GNUTLS_X509_CRLREASON_UNSPECIFIED = 0, + GNUTLS_X509_CRLREASON_KEYCOMPROMISE = 1, + GNUTLS_X509_CRLREASON_CACOMPROMISE = 2, + GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED = 3, + GNUTLS_X509_CRLREASON_SUPERSEDED = 4, + GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION = 5, + GNUTLS_X509_CRLREASON_CERTIFICATEHOLD = 6, + /* -- value 7 is not used */ + GNUTLS_X509_CRLREASON_REMOVEFROMCRL = 8, + GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN = 9, + GNUTLS_X509_CRLREASON_AACOMPROMISE = 10 + } gnutls_x509_crl_reason_t; + + /** + * gnutls_ocsp_verify_reason_t: + * @GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND: Signer cert not found. + * @GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR: Signer keyusage bits incorrect. + * @GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER: Signer is not trusted. + * @GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM: Signature using insecure algorithm. + * @GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE: Signature mismatch. + * @GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED: Signer cert is not yet activated. + * @GNUTLS_OCSP_VERIFY_CERT_EXPIRED: Signer cert has expired. + * + * Enumeration of OCSP verify status codes, used by + * gnutls_ocsp_resp_verify() and gnutls_ocsp_resp_verify_direct(). + */ + typedef enum gnutls_ocsp_verify_reason_t + { + GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND = 1, + GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR = 2, + GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER = 4, + GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM = 8, + GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE = 16, + GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED = 32, + GNUTLS_OCSP_VERIFY_CERT_EXPIRED = 64 + } gnutls_ocsp_verify_reason_t; + + struct gnutls_ocsp_req_int; + typedef struct gnutls_ocsp_req_int *gnutls_ocsp_req_t; + + int gnutls_ocsp_req_init (gnutls_ocsp_req_t * req); + void gnutls_ocsp_req_deinit (gnutls_ocsp_req_t req); + + int gnutls_ocsp_req_import (gnutls_ocsp_req_t req, + const gnutls_datum_t * data); + int gnutls_ocsp_req_export (gnutls_ocsp_req_t req, gnutls_datum_t * data); + int gnutls_ocsp_req_print (gnutls_ocsp_req_t req, + gnutls_ocsp_print_formats_t format, + gnutls_datum_t * out); + + int gnutls_ocsp_req_get_version (gnutls_ocsp_req_t req); + + int gnutls_ocsp_req_get_cert_id (gnutls_ocsp_req_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); + 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 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 gnutls_ocsp_req_get_extension (gnutls_ocsp_req_t req, + unsigned indx, + gnutls_datum_t *oid, + unsigned int *critical, + gnutls_datum_t *data); + int gnutls_ocsp_req_set_extension (gnutls_ocsp_req_t req, + const char *oid, + unsigned int critical, + const gnutls_datum_t *data); + + int gnutls_ocsp_req_get_nonce (gnutls_ocsp_req_t req, + unsigned int *critical, + gnutls_datum_t *nonce); + int gnutls_ocsp_req_set_nonce (gnutls_ocsp_req_t req, + unsigned int critical, + const gnutls_datum_t *nonce); + int gnutls_ocsp_req_randomize_nonce (gnutls_ocsp_req_t req); + + struct gnutls_ocsp_resp_int; + typedef struct gnutls_ocsp_resp_int *gnutls_ocsp_resp_t; + + int gnutls_ocsp_resp_init (gnutls_ocsp_resp_t * resp); + void gnutls_ocsp_resp_deinit (gnutls_ocsp_resp_t resp); + + int gnutls_ocsp_resp_import (gnutls_ocsp_resp_t resp, + const gnutls_datum_t * data); + int gnutls_ocsp_resp_export (gnutls_ocsp_resp_t resp, + gnutls_datum_t * data); + int gnutls_ocsp_resp_print (gnutls_ocsp_resp_t resp, + gnutls_ocsp_print_formats_t format, + gnutls_datum_t * out); + + int gnutls_ocsp_resp_get_status (gnutls_ocsp_resp_t resp); + int gnutls_ocsp_resp_get_response (gnutls_ocsp_resp_t resp, + gnutls_datum_t *response_type_oid, + gnutls_datum_t *response); + + int gnutls_ocsp_resp_get_version (gnutls_ocsp_resp_t resp); + int gnutls_ocsp_resp_get_responder (gnutls_ocsp_resp_t resp, + gnutls_datum_t *dn); + time_t gnutls_ocsp_resp_get_produced (gnutls_ocsp_resp_t resp); + int gnutls_ocsp_resp_get_single (gnutls_ocsp_resp_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, + int *cert_status, + time_t *this_update, + time_t *next_update, + time_t *revocation_time, + int *revocation_reason); + int gnutls_ocsp_resp_get_extension (gnutls_ocsp_resp_t resp, + unsigned indx, + gnutls_datum_t *oid, + unsigned int *critical, + gnutls_datum_t *data); + int gnutls_ocsp_resp_get_nonce (gnutls_ocsp_resp_t resp, + unsigned int *critical, + gnutls_datum_t *nonce); + int gnutls_ocsp_resp_get_signature_algorithm (gnutls_ocsp_resp_t resp); + int gnutls_ocsp_resp_get_signature (gnutls_ocsp_resp_t resp, + gnutls_datum_t *sig); + int gnutls_ocsp_resp_get_certs (gnutls_ocsp_resp_t resp, + gnutls_x509_crt_t ** certs, + size_t *ncerts); + + int gnutls_ocsp_resp_verify_direct (gnutls_ocsp_resp_t resp, + gnutls_x509_crt_t signercert, + unsigned *verify, + int flags); + int gnutls_ocsp_resp_verify (gnutls_ocsp_resp_t resp, + gnutls_x509_trust_list_t trustlist, + unsigned *verify, + int flags); + +#ifdef __cplusplus +} +#endif + +#endif /* GNUTLS_OCSP_H */ diff --git a/lib/libgnutls.map b/lib/libgnutls.map index fe9d258a94..0c68c71212 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -718,6 +718,38 @@ GNUTLS_3_0_0 { gnutls_pubkey_import_ecc_raw2; gnutls_record_get_discarded; gnutls_x509_crt_get_authority_info_access; + gnutls_ocsp_req_add_cert; + gnutls_ocsp_req_add_cert_id; + gnutls_ocsp_req_deinit; + gnutls_ocsp_req_export; + gnutls_ocsp_req_get_cert_id; + gnutls_ocsp_req_get_extension; + gnutls_ocsp_req_get_nonce; + gnutls_ocsp_req_get_version; + gnutls_ocsp_req_import; + gnutls_ocsp_req_init; + gnutls_ocsp_req_print; + gnutls_ocsp_req_randomize_nonce; + gnutls_ocsp_req_set_extension; + gnutls_ocsp_req_set_nonce; + gnutls_ocsp_resp_deinit; + gnutls_ocsp_resp_export; + gnutls_ocsp_resp_get_certs; + gnutls_ocsp_resp_get_extension; + gnutls_ocsp_resp_get_nonce; + gnutls_ocsp_resp_get_produced; + gnutls_ocsp_resp_get_responder; + gnutls_ocsp_resp_get_response; + gnutls_ocsp_resp_get_signature; + gnutls_ocsp_resp_get_signature_algorithm; + gnutls_ocsp_resp_get_single; + gnutls_ocsp_resp_get_status; + gnutls_ocsp_resp_get_version; + gnutls_ocsp_resp_import; + gnutls_ocsp_resp_init; + gnutls_ocsp_resp_print; + gnutls_ocsp_resp_verify; + gnutls_ocsp_resp_verify_direct; gnutls_privkey_import_ext; gnutls_certificate_set_key; gnutls_srp_3072_group_generator; diff --git a/lib/pkix.asn b/lib/pkix.asn index ef1937c925..e0121c6d32 100644 --- a/lib/pkix.asn +++ b/lib/pkix.asn @@ -572,4 +572,113 @@ ProxyPolicy ::= SEQUENCE { policyLanguage OBJECT IDENTIFIER, policy OCTET STRING OPTIONAL } +-- rfc2560 + +OCSPRequest ::= SEQUENCE { + tbsRequest TBSRequest, + optionalSignature [0] EXPLICIT Signature OPTIONAL } + +TBSRequest ::= SEQUENCE { + version [0] EXPLICIT Version DEFAULT v1, + requestorName [1] EXPLICIT GeneralName OPTIONAL, + requestList SEQUENCE OF Request, + requestExtensions [2] EXPLICIT Extensions OPTIONAL } + +Signature ::= SEQUENCE { + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING, + certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + +-- comment out, already used above, fortunately with same values and names +-- Version ::= INTEGER { v1(0) } + +Request ::= SEQUENCE { + reqCert CertID, + singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } + +CertID ::= SEQUENCE { + hashAlgorithm AlgorithmIdentifier, + issuerNameHash OCTET STRING, -- Hash of Issuer's DN + issuerKeyHash OCTET STRING, -- Hash of Issuers public key + serialNumber CertificateSerialNumber } + +OCSPResponse ::= SEQUENCE { + responseStatus OCSPResponseStatus, + responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + +OCSPResponseStatus ::= ENUMERATED { + successful (0), --Response has valid confirmations + malformedRequest (1), --Illegal confirmation request + internalError (2), --Internal error in issuer + tryLater (3), --Try again later + --(4) is not used + sigRequired (5), --Must sign the request + unauthorized (6) --Request unauthorized +} + +ResponseBytes ::= SEQUENCE { + responseType OBJECT IDENTIFIER, + response OCTET STRING } + +BasicOCSPResponse ::= SEQUENCE { + tbsResponseData ResponseData, + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING, + certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + +ResponseData ::= SEQUENCE { + version [0] EXPLICIT Version DEFAULT v1, + responderID ResponderID, + producedAt GeneralizedTime, + responses SEQUENCE OF SingleResponse, + responseExtensions [1] EXPLICIT Extensions OPTIONAL } + +ResponderID ::= CHOICE { +-- Changed to work with the libtasn1 parser. + byName [1] EXPLICIT RDNSequence, --Name, + byKey [2] KeyHash } + +KeyHash ::= OCTET STRING --SHA-1 hash of responder's public key + --(excluding the tag and length fields) + +SingleResponse ::= SEQUENCE { + certID CertID, + certStatus CertStatus, + thisUpdate GeneralizedTime, + nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + singleExtensions [1] EXPLICIT Extensions OPTIONAL } + +CertStatus ::= CHOICE { + good [0] IMPLICIT NULL, + revoked [1] IMPLICIT RevokedInfo, + unknown [2] IMPLICIT UnknownInfo } + +RevokedInfo ::= SEQUENCE { + revocationTime GeneralizedTime, + revocationReason [0] EXPLICIT CRLReason OPTIONAL } + +UnknownInfo ::= NULL -- this can be replaced with an enumeration + +ArchiveCutoff ::= GeneralizedTime + +AcceptableResponses ::= SEQUENCE OF OBJECT IDENTIFIER + +ServiceLocator ::= SEQUENCE { + issuer Name, + locator AuthorityInfoAccessSyntax } + +-- rfc5280 + +CRLReason ::= ENUMERATED { + unspecified (0), + keyCompromise (1), + cACompromise (2), + affiliationChanged (3), + superseded (4), + cessationOfOperation (5), + certificateHold (6), + removeFromCRL (8), + privilegeWithdrawn (9), + aACompromise (10) } + END diff --git a/lib/pkix_asn1_tab.c b/lib/pkix_asn1_tab.c index c42b155b4a..159bba5fcd 100644 --- a/lib/pkix_asn1_tab.c +++ b/lib/pkix_asn1_tab.c @@ -407,8 +407,111 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = { { "pCPathLenConstraint", 1611153411, NULL }, { "0", 10, "MAX"}, { "proxyPolicy", 2, "ProxyPolicy"}, - { "ProxyPolicy", 536870917, NULL }, + { "ProxyPolicy", 1610612741, NULL }, { "policyLanguage", 1073741836, NULL }, { "policy", 16391, NULL }, + { "OCSPRequest", 1610612741, NULL }, + { "tbsRequest", 1073741826, "TBSRequest"}, + { "optionalSignature", 536895490, "Signature"}, + { NULL, 2056, "0"}, + { "TBSRequest", 1610612741, NULL }, + { "version", 1610653698, "Version"}, + { NULL, 1073741833, "v1"}, + { NULL, 2056, "0"}, + { "requestorName", 1610637314, "GeneralName"}, + { NULL, 2056, "1"}, + { "requestList", 1610612747, NULL }, + { NULL, 2, "Request"}, + { "requestExtensions", 536895490, "Extensions"}, + { NULL, 2056, "2"}, + { "Signature", 1610612741, NULL }, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 1073741830, NULL }, + { "certs", 536895499, NULL }, + { NULL, 1073743880, "0"}, + { NULL, 2, "Certificate"}, + { "Request", 1610612741, NULL }, + { "reqCert", 1073741826, "CertID"}, + { "singleRequestExtensions", 536895490, "Extensions"}, + { NULL, 2056, "0"}, + { "CertID", 1610612741, NULL }, + { "hashAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "issuerNameHash", 1073741831, NULL }, + { "issuerKeyHash", 1073741831, NULL }, + { "serialNumber", 2, "CertificateSerialNumber"}, + { "OCSPResponse", 1610612741, NULL }, + { "responseStatus", 1073741826, "OCSPResponseStatus"}, + { "responseBytes", 536895490, "ResponseBytes"}, + { NULL, 2056, "0"}, + { "OCSPResponseStatus", 1610874901, NULL }, + { "successful", 1073741825, "0"}, + { "malformedRequest", 1073741825, "1"}, + { "internalError", 1073741825, "2"}, + { "tryLater", 1073741825, "3"}, + { "sigRequired", 1073741825, "5"}, + { "unauthorized", 1, "6"}, + { "ResponseBytes", 1610612741, NULL }, + { "responseType", 1073741836, NULL }, + { "response", 7, NULL }, + { "BasicOCSPResponse", 1610612741, NULL }, + { "tbsResponseData", 1073741826, "ResponseData"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 1073741830, NULL }, + { "certs", 536895499, NULL }, + { NULL, 1073743880, "0"}, + { NULL, 2, "Certificate"}, + { "ResponseData", 1610612741, NULL }, + { "version", 1610653698, "Version"}, + { NULL, 1073741833, "v1"}, + { NULL, 2056, "0"}, + { "responderID", 1073741826, "ResponderID"}, + { "producedAt", 1082130449, NULL }, + { "responses", 1610612747, NULL }, + { NULL, 2, "SingleResponse"}, + { "responseExtensions", 536895490, "Extensions"}, + { NULL, 2056, "1"}, + { "ResponderID", 1610612754, NULL }, + { "byName", 1610620930, "RDNSequence"}, + { NULL, 2056, "1"}, + { "byKey", 536879106, "KeyHash"}, + { NULL, 4104, "2"}, + { "KeyHash", 1073741831, NULL }, + { "SingleResponse", 1610612741, NULL }, + { "certID", 1073741826, "CertID"}, + { "certStatus", 1073741826, "CertStatus"}, + { "thisUpdate", 1082130449, NULL }, + { "nextUpdate", 1619025937, NULL }, + { NULL, 2056, "0"}, + { "singleExtensions", 536895490, "Extensions"}, + { NULL, 2056, "1"}, + { "CertStatus", 1610612754, NULL }, + { "good", 1610620948, NULL }, + { NULL, 4104, "0"}, + { "revoked", 1610620930, "RevokedInfo"}, + { NULL, 4104, "1"}, + { "unknown", 536879106, "UnknownInfo"}, + { NULL, 4104, "2"}, + { "RevokedInfo", 1610612741, NULL }, + { "revocationTime", 1082130449, NULL }, + { "revocationReason", 536895490, "CRLReason"}, + { NULL, 2056, "0"}, + { "UnknownInfo", 1073741844, NULL }, + { "ArchiveCutoff", 1082130449, NULL }, + { "AcceptableResponses", 1610612747, NULL }, + { NULL, 12, NULL }, + { "ServiceLocator", 1610612741, NULL }, + { "issuer", 1073741826, "Name"}, + { "locator", 2, "AuthorityInfoAccessSyntax"}, + { "CRLReason", 537133077, NULL }, + { "unspecified", 1073741825, "0"}, + { "keyCompromise", 1073741825, "1"}, + { "cACompromise", 1073741825, "2"}, + { "affiliationChanged", 1073741825, "3"}, + { "superseded", 1073741825, "4"}, + { "cessationOfOperation", 1073741825, "5"}, + { "certificateHold", 1073741825, "6"}, + { "removeFromCRL", 1073741825, "8"}, + { "privilegeWithdrawn", 1073741825, "9"}, + { "aACompromise", 1, "10"}, { NULL, 0, NULL } }; diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am index 9386a4e630..d16dfc7246 100644 --- a/lib/x509/Makefile.am +++ b/lib/x509/Makefile.am @@ -57,3 +57,7 @@ libgnutls_x509_la_SOURCES = \ x509_write.c \ verify-high.c \ verify-high.h + +if ENABLE_OCSP +libgnutls_x509_la_SOURCES += ocsp.c ocsp_output.c +endif diff --git a/lib/x509/ocsp.c b/lib/x509/ocsp.c new file mode 100644 index 0000000000..ae2316822d --- /dev/null +++ b/lib/x509/ocsp.c @@ -0,0 +1,2160 @@ +/* + * Copyright (C) 2011-2012 Free Software Foundation, Inc. + * Author: Simon Josefsson + * + * 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 3 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 <http://www.gnu.org/licenses/> + * + */ + +/* Online Certificate Status Protocol - RFC 2560 + */ + +#include <gnutls_int.h> +#include <gnutls_global.h> +#include <gnutls_errors.h> +#include <libtasn1.h> +#include <gnutls_pk.h> +#include "common.h" +#include "hash.h" +#include "verify-high.h" + +#include <gnutls/ocsp.h> + +typedef struct gnutls_ocsp_req_int +{ + ASN1_TYPE req; +} gnutls_ocsp_req_int; + +typedef struct gnutls_ocsp_resp_int +{ + ASN1_TYPE resp; + gnutls_datum_t response_type_oid; + ASN1_TYPE basicresp; +} gnutls_ocsp_resp_int; + +#define MAX_TIME 64 + +/** + * gnutls_ocsp_req_init: + * @req: The structure 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 structure 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: The structure 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 structure 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->response_type_oid.data = NULL; + resp->basicresp = NULL; + + gnutls_free (resp); +} + +/** + * gnutls_ocsp_req_import: + * @req: The structure 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->req) + { + /* Any earlier asn1_der_decoding 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); + } + } + + ret = asn1_der_decoding (&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 structure 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) +{ + int ret = 0; + + if (resp == NULL || data == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (resp->resp) + { + /* Any earlier asn1_der_decoding will modify the ASN.1 + structure, so we need to replace it with a fresh + structure. */ + asn1_delete_structure (&resp->resp); + + ret = asn1_create_element (_gnutls_get_pkix (), + "PKIX1.OCSPResponse", &resp->resp); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + } + + ret = asn1_der_decoding (&resp->resp, data->data, data->size, NULL); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + + if (gnutls_ocsp_resp_get_status (resp) != GNUTLS_OCSP_RESP_SUCCESSFUL) + return GNUTLS_E_SUCCESS; + + ret = _gnutls_x509_read_value (resp->resp, "responseBytes.responseType", + &resp->response_type_oid, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + +#define OCSP_BASIC "1.3.6.1.5.5.7.48.1.1" + + if (resp->response_type_oid.size == sizeof (OCSP_BASIC) + && memcmp (resp->response_type_oid.data, OCSP_BASIC, + resp->response_type_oid.size) == 0) + { + gnutls_datum_t d; + + if (resp->basicresp) + { + asn1_delete_structure (&resp->basicresp); + + ret = asn1_create_element (_gnutls_get_pkix (), + "PKIX1.BasicOCSPResponse", &resp->basicresp); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + } + + ret = _gnutls_x509_read_value (resp->resp, "responseBytes.response", + &d, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = asn1_der_decoding (&resp->basicresp, d.data, d.size, NULL); + gnutls_free (d.data); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + } + else + resp->basicresp = NULL; + + return GNUTLS_E_SUCCESS; +} + +static int +export (ASN1_TYPE node, const char *name, gnutls_datum_t * data) +{ + int ret; + int len = 0; + + ret = asn1_der_coding (node, name, NULL, &len, NULL); + if (ret != ASN1_MEM_ERROR) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + data->size = len; + data->data = gnutls_malloc (len); + if (data->data == NULL) + return GNUTLS_E_MEMORY_ERROR; + ret = asn1_der_coding (node, name, data->data, &len, NULL); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + + return GNUTLS_E_SUCCESS; +} + +/** + * 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_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 */ + asn1_write_value (req->req, "tbsRequest.requestorName", NULL, 0); + 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) + asn1_write_value (req->req, "tbsRequest.requestExtensions", NULL, 0); + + return export (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_t resp, gnutls_datum_t * data) +{ + if (resp == NULL || data == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return export (resp->resp, "", data); +} + +/** + * gnutls_ocsp_req_get_version: + * @req: should contain a #gnutls_ocsp_req_t structure + * + * 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_t req) +{ + opaque version[8]; + int len, ret; + + if (req == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + len = sizeof (version); + ret = asn1_read_value (req->req, "tbsRequest.version", version, &len); + if (ret != ASN1_SUCCESS) + { + if (ret == ASN1_ELEMENT_NOT_FOUND) + return 1; /* the DEFAULT version */ + gnutls_assert (); + return _gnutls_asn2err (ret); + } + + return (int) version[0] + 1; +} + +/** + * gnutls_ocsp_req_get_cert_id: + * @req: should contain a #gnutls_ocsp_req_t structure + * @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_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[ASN1_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, 0); + 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_x509_oid2digest_algorithm (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, 0); + 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, 0); + 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, 0); + 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 structure + * @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 (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 structure + * @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; + char inh_buf[MAX_HASH_SIZE]; + char 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, 2); + 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, 0); + 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 structure + * @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. + * + * 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_t req, + unsigned indx, + gnutls_datum_t *oid, + unsigned int *critical, + gnutls_datum_t *data) +{ + int ret; + char str_critical[10]; + char name[ASN1_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, 0); + 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, 0); + 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 structure + * @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 set_extension (req->req, "tbsRequest.requestExtensions", oid, + data, critical); +} + +/** + * gnutls_ocsp_req_get_nonce: + * @req: should contain a #gnutls_ocsp_req_t structure + * @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_t req, + unsigned int *critical, + gnutls_datum_t *nonce) +{ + int ret; + size_t l = 0; + gnutls_datum_t tmp; + + if (req == NULL || nonce == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = 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_octet_string (NULL, tmp.data, (size_t) tmp.size, + NULL, &l); + if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_assert (); + gnutls_free (tmp.data); + return ret; + } + + nonce->data = gnutls_malloc (l); + if (nonce->data == NULL) + { + gnutls_assert (); + gnutls_free (tmp.data); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = _gnutls_x509_decode_octet_string (NULL, tmp.data, (size_t) tmp.size, + nonce->data, &l); + gnutls_free (tmp.data); + if (ret != GNUTLS_E_SUCCESS) + { + gnutls_assert (); + return ret; + } + nonce->size = l; + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_req_set_nonce: + * @req: should contain a #gnutls_ocsp_req_t structure + * @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 = 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 structure + * + * 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; + char 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 structure + * + * 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_t resp) +{ + opaque 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); + } + + 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_E_UNEXPECTED_PACKET; + } + + return (int) str[0]; +} + +/** + * gnutls_ocsp_resp_get_response: + * @resp: should contain a #gnutls_ocsp_resp_t structure + * @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. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_ocsp_resp_get_response (gnutls_ocsp_resp_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, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + + if (response != NULL) + { + ret = _gnutls_x509_read_value (resp->resp, "responseBytes.response", + response, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_resp_get_version: + * @resp: should contain a #gnutls_ocsp_resp_t structure + * + * 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_t resp) +{ + opaque version[8]; + int len, ret; + + if (resp == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + len = sizeof (version); + ret = asn1_read_value (resp->resp, "tbsResponseData.version", version, &len); + if (ret != ASN1_SUCCESS) + { + if (ret == ASN1_ELEMENT_NOT_FOUND) + return 1; /* the DEFAULT version */ + gnutls_assert (); + return _gnutls_asn2err (ret); + } + + return (int) version[0] + 1; +} + +/** + * gnutls_ocsp_resp_get_responder: + * @resp: should contain a #gnutls_ocsp_resp_t structure + * @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. + * + * 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 (gnutls_ocsp_resp_t resp, + gnutls_datum_t *dn) +{ + int ret; + size_t l = 0; + + if (resp == NULL || dn == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_parse_dn + (resp->basicresp, "tbsResponseData.responderID.byName", + NULL, &l); + if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_assert (); + return ret; + } + + dn->data = gnutls_malloc (l); + if (dn->data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = _gnutls_x509_parse_dn + (resp->basicresp, "tbsResponseData.responderID.byName", + dn->data, &l); + if (ret != GNUTLS_E_SUCCESS) + { + gnutls_assert (); + return ret; + } + + dn->size = l; + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_resp_get_produced: + * @resp: should contain a #gnutls_ocsp_resp_t structure + * + * 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_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_get_single: + * @resp: should contain a #gnutls_ocsp_resp_t structure + * @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 + * @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 SingleResponse structure + * except the final singleExtensions, reproduced here for illustration: + * + * <informalexample><programlisting> + * SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + * + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer's DN + * issuerKeyHash OCTET STRING, -- Hash of Issuers public key + * serialNumber CertificateSerialNumber } + * + * CertStatus ::= CHOICE { + * good [0] IMPLICIT NULL, + * revoked [1] IMPLICIT RevokedInfo, + * unknown [2] IMPLICIT UnknownInfo } + * + * RevokedInfo ::= SEQUENCE { + * revocationTime GeneralizedTime, + * revocationReason [0] EXPLICIT CRLReason OPTIONAL } + * </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_resp_get_single (gnutls_ocsp_resp_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, + int *cert_status, + time_t *this_update, + time_t *next_update, + time_t *revocation_time, + int *revocation_reason) +{ + gnutls_datum_t sa; + char name[ASN1_MAX_NAME_SIZE]; + int ret; + + snprintf (name, sizeof (name), + "tbsResponseData.responses.?%u.certID.hashAlgorithm.algorithm", + indx + 1); + ret = _gnutls_x509_read_value (resp->basicresp, name, &sa, 0); + 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_x509_oid2digest_algorithm (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), + "tbsResponseData.responses.?%u.certID.issuerNameHash", + indx + 1); + ret = _gnutls_x509_read_value (resp->basicresp, name, + issuer_name_hash, 0); + if (ret != GNUTLS_E_SUCCESS) + { + 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, 0); + 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), + "tbsResponseData.responses.?%u.certID.serialNumber", + indx + 1); + ret = _gnutls_x509_read_value (resp->basicresp, name, + serial_number, 0); + 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; + } + } + + if (cert_status) + { + snprintf (name, sizeof (name), + "tbsResponseData.responses.?%u.certStatus", + indx + 1); + ret = _gnutls_x509_read_value (resp->basicresp, name, &sa, 0); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (ret < 0) + { + gnutls_assert (); + return ret; + } + if (sa.size == 5 && memcmp (sa.data, "good", sa.size) == 0) + *cert_status = GNUTLS_OCSP_CERT_GOOD; + else if (sa.size == 8 && memcmp (sa.data, "revoked", sa.size) == 0) + *cert_status = GNUTLS_OCSP_CERT_REVOKED; + else if (sa.size == 8 && memcmp (sa.data, "unknown", sa.size) == 0) + *cert_status = GNUTLS_OCSP_CERT_UNKNOWN; + else + { + gnutls_assert (); + gnutls_free (sa.data); + return GNUTLS_E_ASN1_DER_ERROR; + } + gnutls_free (sa.data); + } + + if (this_update) + { + char ttime[MAX_TIME]; + int len; + + snprintf (name, sizeof (name), + "tbsResponseData.responses.?%u.thisUpdate", + indx + 1); + len = sizeof (ttime) - 1; + ret = asn1_read_value (resp->basicresp, name, ttime, &len); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + *this_update = (time_t) (-1); + } + else + *this_update = _gnutls_x509_generalTime2gtime (ttime); + } + + if (next_update) + { + char ttime[MAX_TIME]; + int len; + + snprintf (name, sizeof (name), + "tbsResponseData.responses.?%u.nextUpdate", + indx + 1); + len = sizeof (ttime) - 1; + ret = asn1_read_value (resp->basicresp, name, ttime, &len); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + *next_update = (time_t) (-1); + } + else + *next_update = _gnutls_x509_generalTime2gtime (ttime); + } + + if (revocation_time) + { + char ttime[MAX_TIME]; + int len; + + snprintf (name, sizeof (name), + "tbsResponseData.responses.?%u.certStatus." + "revoked.revocationTime", + indx + 1); + len = sizeof (ttime) - 1; + ret = asn1_read_value (resp->basicresp, name, ttime, &len); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + *revocation_time = (time_t) (-1); + } + else + *revocation_time = _gnutls_x509_generalTime2gtime (ttime); + } + + /* revocation_reason */ + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_resp_get_extension: + * @resp: should contain a #gnutls_ocsp_resp_t structure + * @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. + * + * 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_t resp, + unsigned indx, + gnutls_datum_t *oid, + unsigned int *critical, + gnutls_datum_t *data) +{ + int ret; + char str_critical[10]; + char name[ASN1_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, 0); + 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, 0); + 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 structure + * @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_t resp, + unsigned int *critical, + gnutls_datum_t *nonce) +{ + int ret; + size_t l = 0; + gnutls_datum_t tmp; + + ret = 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_octet_string (NULL, tmp.data, (size_t) tmp.size, + NULL, &l); + if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_assert (); + gnutls_free (tmp.data); + return ret; + } + + nonce->data = gnutls_malloc (l); + if (nonce->data == NULL) + { + gnutls_assert (); + gnutls_free (tmp.data); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = _gnutls_x509_decode_octet_string (NULL, tmp.data, (size_t) tmp.size, + nonce->data, &l); + gnutls_free (tmp.data); + if (ret != GNUTLS_E_SUCCESS) + { + gnutls_assert (); + return ret; + } + nonce->size = l; + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_ocsp_resp_get_signature_algorithm: + * @resp: should contain a #gnutls_ocsp_resp_t structure + * + * 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_t resp) +{ + int ret; + gnutls_datum_t sa; + + ret = _gnutls_x509_read_value (resp->basicresp, + "signatureAlgorithm.algorithm", &sa, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_x509_oid2sign_algorithm (sa.data); + + _gnutls_free_datum (&sa); + + return ret; +} + +/** + * gnutls_ocsp_resp_get_signature: + * @resp: should contain a #gnutls_ocsp_resp_t structure + * @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_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, 2); + 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 structure + * @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_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[ASN1_MAX_NAME_SIZE]; + + snprintf (name, sizeof (name), "certs.?%lu", 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; + } + + tmpcerts2 = gnutls_realloc (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); + c.data = NULL; + } + + 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_t resp) +{ + int rc; + gnutls_x509_crt_t * certs; + size_t ncerts = 0, i; + gnutls_datum_t riddn; + gnutls_x509_crt_t signercert = NULL; + + rc = gnutls_ocsp_resp_get_responder (resp, &riddn); + 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 (); + gnutls_free (riddn.data); + return NULL; + } + + for (i = 0; i < ncerts; i++) + { + char *crtdn; + size_t crtdnsize = 0; + int cmpok; + + rc = gnutls_x509_crt_get_dn (certs[i], NULL, &crtdnsize); + if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_assert (); + goto quit; + } + + crtdn = gnutls_malloc (crtdnsize); + if (crtdn == NULL) + { + gnutls_assert (); + goto quit; + } + + rc = gnutls_x509_crt_get_dn (certs[i], crtdn, &crtdnsize); + if (rc != GNUTLS_E_SUCCESS) + { + gnutls_assert (); + gnutls_free (crtdn); + goto quit; + } + + cmpok = (crtdnsize == riddn.size) + && memcmp (riddn.data, crtdn, crtdnsize); + + gnutls_free (crtdn); + + if (cmpok == 0) + { + signercert = certs[i]; + goto quit; + } + } + + gnutls_assert (); + signercert = NULL; + + quit: + gnutls_free (riddn.data); + for (i = 0; i < ncerts; i++) + if (certs[i] != signercert) + gnutls_x509_crt_deinit (certs[i]); + gnutls_free (certs); + return signercert; +} + +/** + * gnutls_ocsp_resp_verify_direct: + * @resp: should contain a #gnutls_ocsp_resp_t structure + * @signercert: certificate believed to have signed the response + * @verify: output variable with verification status, an #gnutls_ocsp_cert_status_t + * @flags: verification flags, 0 for now. + * + * Verify signature of the Basic OCSP Response against the public key + * in the @signercert 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_t resp, + gnutls_x509_crt_t signercert, + unsigned *verify, + 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; + } + + sigalg = gnutls_ocsp_resp_get_signature_algorithm (resp); + + rc = export (resp->basicresp, "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; + } + + 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, 0, &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 (data.data); + gnutls_free (sig.data); + gnutls_pubkey_deinit (pubkey); + + return rc; +} + +/** + * gnutls_ocsp_resp_verify: + * @resp: should contain a #gnutls_ocsp_resp_t structure + * @trustlist: trust anchors as a #gnutls_x509_trust_list_t structure + * @verify: output variable with verification status, an #gnutls_ocsp_cert_status_t + * @flags: verification flags, 0 for now. + * + * 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_t resp, + gnutls_x509_trust_list_t trustlist, + unsigned *verify, + 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) + { + /* XXX Search in trustlist for certificate matching + responderId as well? */ + gnutls_assert (); + *verify = GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND; + rc = GNUTLS_E_SUCCESS; + goto done; + } + + /* 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) + { + gnutls_assert (); + goto done; + } + if (rc == 1) + { + /* not in trustlist, need to verify signature and bits */ + gnutls_x509_crt_t issuer; + unsigned vtmp; + char oidtmp[sizeof (GNUTLS_KP_OCSP_SIGNING)]; + size_t oidsize; + int indx; + + gnutls_assert (); + + rc = gnutls_x509_trust_list_get_issuer (trustlist, signercert, + &issuer, 0); + if (rc != GNUTLS_E_SUCCESS) + { + gnutls_assert (); + *verify = GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER; + rc = GNUTLS_E_SUCCESS; + goto done; + } + + rc = gnutls_x509_crt_verify (signercert, &issuer, 1, 0, &vtmp); + if (rc != GNUTLS_E_SUCCESS) + { + gnutls_assert (); + goto done; + } + + if (vtmp != 0) + { + gnutls_assert (); + if (vtmp & GNUTLS_CERT_INSECURE_ALGORITHM) + *verify = GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM; + else if (vtmp & GNUTLS_CERT_NOT_ACTIVATED) + *verify = GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED; + else if (vtmp & GNUTLS_CERT_EXPIRED) + *verify = GNUTLS_OCSP_VERIFY_CERT_EXPIRED; + else + *verify = GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER; + rc = GNUTLS_E_SUCCESS; + goto done; + } + + 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 (); + *verify = GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR; + rc = GNUTLS_E_SUCCESS; + goto done; + } + else if (rc == GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_assert (); + continue; + } + else if (rc != GNUTLS_E_SUCCESS) + { + gnutls_assert (); + goto done; + } + + if (memcmp (oidtmp, GNUTLS_KP_OCSP_SIGNING, oidsize) != 0) + { + gnutls_assert (); + continue; + } + + break; + } + } + + rc = gnutls_ocsp_resp_verify_direct (resp, signercert, verify, flags); + + done: + gnutls_x509_crt_deinit (signercert); + + return rc; +} diff --git a/lib/x509/ocsp_output.c b/lib/x509/ocsp_output.c new file mode 100644 index 0000000000..719ce89543 --- /dev/null +++ b/lib/x509/ocsp_output.c @@ -0,0 +1,631 @@ +/* + * Copyright (C) 2011-2012 Free Software Foundation, Inc. + * Author: Simon Josefsson + * + * 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 3 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 <http://www.gnu.org/licenses/> + * + */ + +/* Online Certificate Status Protocol - RFC 2560 + */ + +#include <gnutls_int.h> +#include <gnutls_global.h> +#include <gnutls_errors.h> +#include <libtasn1.h> +#include <gnutls_pk.h> +#include "algorithms.h" + +#include <gnutls/ocsp.h> + +/* I18n of error codes. */ +#include "gettext.h" +#define _(String) dgettext (PACKAGE, String) + +#define addf _gnutls_buffer_append_printf +#define adds _gnutls_buffer_append_str + +static void +print_req (gnutls_buffer_st * str, gnutls_ocsp_req_t req) +{ + int ret; + unsigned indx; + + /* Version. */ + { + int version = gnutls_ocsp_req_get_version (req); + if (version < 0) + addf (str, "error: get_version: %s\n", gnutls_strerror (version)); + else + addf (str, _("\tVersion: %d\n"), version); + } + + /* XXX requestorName */ + + /* requestList */ + addf (str, "\tRequest List:\n"); + for (indx = 0; ; indx++) + { + gnutls_digest_algorithm_t digest; + gnutls_datum_t in, ik, sn; + + ret = gnutls_ocsp_req_get_cert_id (req, indx, &digest, &in, &ik, &sn); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + addf (str, "\t\tCertificate ID:\n"); + if (ret != GNUTLS_E_SUCCESS) + { + addf (str, "error: get_cert_id: %s\n", + gnutls_strerror (ret)); + continue; + } + addf (str, "\t\t\tHash Algorithm: %s\n", + _gnutls_digest_get_name (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); + + /* XXX singleRequestExtensions */ + } + + for (indx = 0; ; indx++) + { + gnutls_datum_t oid; + unsigned int critical; + gnutls_datum_t data; + + ret = gnutls_ocsp_req_get_extension (req, 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 (indx == 0) + adds (str, "\tExtensions:\n"); + + if (memcmp (oid.data, GNUTLS_OCSP_NONCE, oid.size) == 0) + { + gnutls_datum_t nonce; + unsigned int critical; + + ret = gnutls_ocsp_req_get_nonce (req, &critical, &nonce); + if (ret != GNUTLS_E_SUCCESS) + { + addf (str, "error: get_nonce: %s\n", + gnutls_strerror (ret)); + } + else + { + addf (str, "\t\tNonce%s: ", critical ? " (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"); + + addf (str, _("\t\t\tASCII: ")); + _gnutls_buffer_asciiprint (str, data.data, data.size); + addf (str, "\n"); + + addf (str, _("\t\t\tHexdump: ")); + _gnutls_buffer_hexprint (str, data.data, data.size); + adds (str, "\n"); + } + + gnutls_free (oid.data); + gnutls_free (data.data); + } + + /* XXX Signature */ +} + +/** + * gnutls_ocsp_req_print: + * @req: The structure to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with (0) terminated string. + * + * This function will pretty print a OCSP request, suitable for + * display to a human. + * + * If the format is %GNUTLS_PRINT_FULL then all fields of the request + * will be output, on multiple lines. + * + * The output @out->data needs to be deallocate using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_ocsp_req_print (gnutls_ocsp_req_t req, + gnutls_ocsp_print_formats_t format, + gnutls_datum_t * out) +{ + gnutls_buffer_st str; + int rc; + + if (format != GNUTLS_OCSP_PRINT_FULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + _gnutls_buffer_init (&str); + + _gnutls_buffer_append_str (&str, _("OCSP Request Information:\n")); + + print_req (&str, req); + + _gnutls_buffer_append_data (&str, "\0", 1); + + rc = _gnutls_buffer_to_datum (&str, out); + if (rc != GNUTLS_E_SUCCESS) + { + gnutls_assert (); + return rc; + } + + return GNUTLS_E_SUCCESS; +} + +static void +print_resp (gnutls_buffer_st * str, gnutls_ocsp_resp_t resp) +{ + 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; + + /* XXX byKey */ + + ret = gnutls_ocsp_resp_get_responder (resp, &dn); + if (ret < 0) + addf (str, "error: get_dn: %s\n", gnutls_strerror (ret)); + else + { + 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; + int cert_status; + time_t this_update; + time_t next_update; + time_t revocation_time; + 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 (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) + addf (str, "error: next_update\n"); + else 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 (memcmp (oid.data, GNUTLS_OCSP_NONCE, oid.size) == 0) + { + gnutls_datum_t nonce; + unsigned int critical; + + ret = gnutls_ocsp_resp_get_nonce (resp, &critical, &nonce); + if (ret != GNUTLS_E_SUCCESS) + { + addf (str, "error: get_nonce: %s\n", + gnutls_strerror (ret)); + } + else + { + addf (str, "\t\tNonce%s: ", critical ? " (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"); + + addf (str, _("\t\t\tASCII: ")); + _gnutls_buffer_asciiprint (str, data.data, data.size); + addf (str, "\n"); + + addf (str, _("\t\t\tHexdump: ")); + _gnutls_buffer_hexprint (str, data.data, data.size); + adds (str, "\n"); + } + + gnutls_free (oid.data); + gnutls_free (data.data); + } + + /* Signature. */ + { + gnutls_datum_t sig; + + ret = gnutls_ocsp_resp_get_signature_algorithm (resp); + if (ret < 0) + addf (str, "retor: 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_RSA_MD5 || ret == GNUTLS_SIGN_RSA_MD2) + { + 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 */ + { + 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 + { + 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); + out.size = s; + addf (str, "%.*s", out.size, out.data); + gnutls_free (out.data); + } + } + + gnutls_x509_crt_deinit (certs[i]); + } + gnutls_free (certs); + } + } +} + +/** + * gnutls_ocsp_resp_print: + * @resp: The structure to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with (0) terminated string. + * + * This function will pretty print a OCSP response, suitable for + * display to a human. + * + * If the format is %GNUTLS_PRINT_FULL then all fields of the response + * will be output, on multiple lines. + * + * The output @out->data needs to be deallocate using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_ocsp_resp_print (gnutls_ocsp_resp_t resp, + gnutls_ocsp_print_formats_t format, + gnutls_datum_t * out) +{ + gnutls_buffer_st str; + int rc; + + if (format != GNUTLS_OCSP_PRINT_FULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + _gnutls_buffer_init (&str); + + _gnutls_buffer_append_str (&str, _("OCSP Response Information:\n")); + + print_resp (&str, resp); + + _gnutls_buffer_append_data (&str, "\0", 1); + + rc = _gnutls_buffer_to_datum (&str, out); + if (rc != GNUTLS_E_SUCCESS) + { + gnutls_assert (); + return rc; + } + + return GNUTLS_E_SUCCESS; +} |