diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-11-07 13:44:34 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-11-07 13:44:34 -0800 |
commit | 5c9df53f92d8e35bacdff3dcc572f26d4157f663 (patch) | |
tree | 11c3117af96a2720d7ef04604a952377aeb16cb0 /src/ocsp.c | |
download | libksba-5c9df53f92d8e35bacdff3dcc572f26d4157f663.tar.gz libksba-5c9df53f92d8e35bacdff3dcc572f26d4157f663.tar.bz2 libksba-5c9df53f92d8e35bacdff3dcc572f26d4157f663.zip |
Imported Upstream version 1.3.0upstream/1.3.0
Diffstat (limited to 'src/ocsp.c')
-rw-r--r-- | src/ocsp.c | 1903 |
1 files changed, 1903 insertions, 0 deletions
diff --git a/src/ocsp.c b/src/ocsp.c new file mode 100644 index 0000000..85679bb --- /dev/null +++ b/src/ocsp.c @@ -0,0 +1,1903 @@ +/* ocsp.c - OCSP (rfc2560) + * Copyright (C) 2003, 2004, 2005, 2006, 2012 g10 Code GmbH + * + * This file is part of KSBA. + * + * KSBA is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - 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. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * KSBA 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 General Public + * License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include "util.h" + +#include "cert.h" +#include "convert.h" +#include "keyinfo.h" +#include "der-encoder.h" +#include "ber-help.h" +#include "ocsp.h" + + +static const char oidstr_sha1[] = "1.3.14.3.2.26"; +static const char oidstr_ocsp_basic[] = "1.3.6.1.5.5.7.48.1.1"; +static const char oidstr_ocsp_nonce[] = "1.3.6.1.5.5.7.48.1.2"; + + +#if 0 +static void +dump_hex (const unsigned char *p, size_t n) +{ + if (!p) + fputs ("none", stderr); + else + { + for (; n; n--, p++) + fprintf (stderr, " %02X", *p); + } +} +#endif + + +static void +parse_skip (unsigned char const **buf, size_t *len, struct tag_info *ti) +{ + if (ti->length) + { + assert (ti->length <= *len); + *len -= ti->length; + *buf += ti->length; + } +} + +static gpg_error_t +parse_sequence (unsigned char const **buf, size_t *len, struct tag_info *ti) +{ + gpg_error_t err; + + err = _ksba_ber_parse_tl (buf, len, ti); + if (err) + ; + else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_SEQUENCE + && ti->is_constructed) ) + err = gpg_error (GPG_ERR_INV_OBJ); + else if (ti->length > *len) + err = gpg_error (GPG_ERR_BAD_BER); + return err; +} + +static gpg_error_t +parse_enumerated (unsigned char const **buf, size_t *len, struct tag_info *ti, + size_t maxlen) +{ + gpg_error_t err; + + err = _ksba_ber_parse_tl (buf, len, ti); + if (err) + ; + else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_ENUMERATED + && !ti->is_constructed) ) + err = gpg_error (GPG_ERR_INV_OBJ); + else if (!ti->length) + err = gpg_error (GPG_ERR_TOO_SHORT); + else if (maxlen && ti->length > maxlen) + err = gpg_error (GPG_ERR_TOO_LARGE); + else if (ti->length > *len) + err = gpg_error (GPG_ERR_BAD_BER); + + return err; +} + +static gpg_error_t +parse_integer (unsigned char const **buf, size_t *len, struct tag_info *ti) +{ + gpg_error_t err; + + err = _ksba_ber_parse_tl (buf, len, ti); + if (err) + ; + else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_INTEGER + && !ti->is_constructed) ) + err = gpg_error (GPG_ERR_INV_OBJ); + else if (!ti->length) + err = gpg_error (GPG_ERR_TOO_SHORT); + else if (ti->length > *len) + err = gpg_error (GPG_ERR_BAD_BER); + + return err; +} + +static gpg_error_t +parse_octet_string (unsigned char const **buf, size_t *len, struct tag_info *ti) +{ + gpg_error_t err; + + err= _ksba_ber_parse_tl (buf, len, ti); + if (err) + ; + else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_OCTET_STRING + && !ti->is_constructed) ) + err = gpg_error (GPG_ERR_INV_OBJ); + else if (!ti->length) + err = gpg_error (GPG_ERR_TOO_SHORT); + else if (ti->length > *len) + err = gpg_error (GPG_ERR_BAD_BER); + + return err; +} + + +/* Note that R_BOOL will only be set if a value has been given. Thus + the caller should set it to the default value prior to calling this + function. Obviously no call to parse_skip is required after + calling this function. */ +static gpg_error_t +parse_optional_boolean (unsigned char const **buf, size_t *len, int *r_bool) +{ + gpg_error_t err; + struct tag_info ti; + + err = _ksba_ber_parse_tl (buf, len, &ti); + if (err) + ; + else if (!ti.length) + err = gpg_error (GPG_ERR_TOO_SHORT); + else if (ti.length > *len) + err = gpg_error (GPG_ERR_BAD_BER); + else if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BOOLEAN + && !ti.is_constructed) + { + if (ti.length != 1) + err = gpg_error (GPG_ERR_BAD_BER); + *r_bool = !!**buf; + parse_skip (buf, len, &ti); + } + else + { /* Undo the read. */ + *buf -= ti.nhdr; + *len += ti.nhdr; + } + + return err; +} + + + +static gpg_error_t +parse_object_id_into_str (unsigned char const **buf, size_t *len, char **oid) +{ + struct tag_info ti; + gpg_error_t err; + + *oid = NULL; + err = _ksba_ber_parse_tl (buf, len, &ti); + if (err) + ; + else if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID + && !ti.is_constructed) ) + err = gpg_error (GPG_ERR_INV_OBJ); + else if (!ti.length) + err = gpg_error (GPG_ERR_TOO_SHORT); + else if (ti.length > *len) + err = gpg_error (GPG_ERR_BAD_BER); + else if (!(*oid = ksba_oid_to_str (*buf, ti.length))) + err = gpg_error_from_errno (errno); + else + { + *buf += ti.length; + *len -= ti.length; + } + return err; +} + + +static gpg_error_t +parse_asntime_into_isotime (unsigned char const **buf, size_t *len, + ksba_isotime_t isotime) +{ + struct tag_info ti; + gpg_error_t err; + + err = _ksba_ber_parse_tl (buf, len, &ti); + if (err) + ; + else if ( !(ti.class == CLASS_UNIVERSAL + && (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME) + && !ti.is_constructed) ) + err = gpg_error (GPG_ERR_INV_OBJ); + else if (!(err = _ksba_asntime_to_iso (*buf, ti.length, + ti.tag == TYPE_UTC_TIME, isotime))) + parse_skip (buf, len, &ti); + + return err; +} + + +static gpg_error_t +parse_context_tag (unsigned char const **buf, size_t *len, struct tag_info *ti, + int tag) +{ + gpg_error_t err; + + err = _ksba_ber_parse_tl (buf, len, ti); + if (err) + ; + else if (!(ti->class == CLASS_CONTEXT && ti->tag == tag + && ti->is_constructed) ) + err = gpg_error (GPG_ERR_INV_OBJ); + else if (ti->length > *len) + err = gpg_error (GPG_ERR_BAD_BER); + + return err; +} + + + +/* Create a new OCSP object and retrun it in R_OCSP. Return 0 on + success or an error code. + */ +gpg_error_t +ksba_ocsp_new (ksba_ocsp_t *r_ocsp) +{ + *r_ocsp = xtrycalloc (1, sizeof **r_ocsp); + if (!*r_ocsp) + return gpg_error_from_errno (errno); + return 0; +} + + +static void +release_ocsp_certlist (struct ocsp_certlist_s *cl) +{ + while (cl) + { + struct ocsp_certlist_s *tmp = cl->next; + ksba_cert_release (cl->cert); + xfree (cl); + cl = tmp; + } +} + + +static void +release_ocsp_extensions (struct ocsp_extension_s *ex) +{ + while (ex) + { + struct ocsp_extension_s *tmp = ex->next; + xfree (ex); + ex = tmp; + } +} + + +/* Release the OCSP object and all its resources. Passing NULL for + OCSP is a valid nop. */ +void +ksba_ocsp_release (ksba_ocsp_t ocsp) +{ + struct ocsp_reqitem_s *ri; + + if (!ocsp) + return; + xfree (ocsp->digest_oid); + xfree (ocsp->request_buffer); + for (; (ri=ocsp->requestlist); ri = ocsp->requestlist ) + { + ocsp->requestlist = ri->next; + ksba_cert_release (ri->cert); + ksba_cert_release (ri->issuer_cert); + release_ocsp_extensions (ri->single_extensions); + xfree (ri->serialno); + } + xfree (ocsp->sigval); + xfree (ocsp->responder_id.name); + xfree (ocsp->responder_id.keyid); + release_ocsp_certlist (ocsp->received_certs); + release_ocsp_extensions (ocsp->response_extensions); + xfree (ocsp); +} + + + +/* Set the hash algorithm to be used for signing the request to OID. + Using this function will force the creation of a signed + request. */ +gpg_error_t +ksba_ocsp_set_digest_algo (ksba_ocsp_t ocsp, const char *oid) +{ + if (!ocsp || !oid || !*oid) + return gpg_error (GPG_ERR_INV_VALUE); + if (ocsp->digest_oid) + xfree (ocsp->digest_oid); + ocsp->digest_oid = xtrystrdup (oid); + if (!ocsp->digest_oid) + return gpg_error_from_errno (errno); + return 0; +} + + +gpg_error_t +ksba_ocsp_set_requestor (ksba_ocsp_t ocsp, ksba_cert_t cert) +{ + (void)ocsp; + (void)cert; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +} + + +/* Add the certificate CERT for which the status is to be requested + and it's issuer certificate ISSUER_CERT to the context. This + function may be called multiple time to create a list of targets to + get combined into one actual request. */ +gpg_error_t +ksba_ocsp_add_target (ksba_ocsp_t ocsp, + ksba_cert_t cert, ksba_cert_t issuer_cert) +{ + struct ocsp_reqitem_s *ri; + + if (!ocsp || !cert || !issuer_cert) + return gpg_error (GPG_ERR_INV_VALUE); + + ri = xtrycalloc (1, sizeof *ri); + if (!ri) + return gpg_error_from_errno (errno); + ksba_cert_ref (cert); + ri->cert = cert; + ksba_cert_ref (issuer_cert); + ri->issuer_cert = issuer_cert; + + ri->next = ocsp->requestlist; + ocsp->requestlist = ri; + + return 0; +} + + +/* Set the nonce to be used for the request to the content of the + buffer NONCE of size NONCELEN. Libksba may have an upper limit of + the allowed size of the nonce; if the supplied nonce is larger it + will be truncated and the actual used length of the nonce returned. + To detect the implementation limit (which should be considered as a + good suggestion), the function may be called with NULL for NONCE, + in which case the maximal usable noncelength is returned. The + function returns the length of the nonce which will be used. */ +size_t +ksba_ocsp_set_nonce (ksba_ocsp_t ocsp, unsigned char *nonce, size_t noncelen) +{ + if (!ocsp) + return 0; + if (!nonce) + return sizeof ocsp->nonce; + if (noncelen > sizeof ocsp->nonce) + noncelen = sizeof ocsp->nonce; + if (noncelen) + { + memcpy (ocsp->nonce, nonce, noncelen); + /* Reset the high bit. We do this to make sure that we have a + positive integer and thus we don't need to prepend a leading + zero which would be needed then. */ + ocsp->nonce[0] &= 0x7f; + } + ocsp->noncelen = noncelen; + return noncelen; +} + + +/* Compute the SHA-1 nameHash for the certificate CERT and put it in + the buffer SHA1_BUFFER which must have been allocated to at least + 20 bytes. */ +static gpg_error_t +issuer_name_hash (ksba_cert_t cert, unsigned char *sha1_buffer) +{ + gpg_error_t err; + const unsigned char *ptr; + size_t length, dummy; + + err = _ksba_cert_get_subject_dn_ptr (cert, &ptr, &length); + if (!err) + { + err = _ksba_hash_buffer (NULL, ptr, length, 20, sha1_buffer, &dummy); + if (!err && dummy != 20) + err = gpg_error (GPG_ERR_BUG); + } + return err; +} + +/* Compute the SHA-1 hash of the public key of CERT and put it in teh + buffer SHA1_BUFFER which must have been allocated with at least 20 + bytes. */ +static gpg_error_t +issuer_key_hash (ksba_cert_t cert, unsigned char *sha1_buffer) +{ + gpg_error_t err; + const unsigned char *ptr; + size_t length, dummy; + + err = _ksba_cert_get_public_key_ptr (cert, &ptr, &length); + if (!err) + { + err = _ksba_hash_buffer (NULL, ptr, length, 20, sha1_buffer, &dummy); + if (!err && dummy != 20) + err = gpg_error (GPG_ERR_BUG); + } + return err; +} + + +/* Write the extensions for a request to WOUT. */ +static gpg_error_t +write_request_extensions (ksba_ocsp_t ocsp, ksba_writer_t wout) +{ + gpg_error_t err; + unsigned char *buf; + size_t buflen; + unsigned char *p; + size_t derlen; + ksba_writer_t w1 = NULL; + ksba_writer_t w2 = NULL; + + if (!ocsp->noncelen) + return 0; /* We do only support the nonce extension. */ + + /* Create writer objects for construction of the extension. */ + err = ksba_writer_new (&w2); + if (!err) + err = ksba_writer_set_mem (w2, 256); + if (!err) + err = ksba_writer_new (&w1); + if (!err) + err = ksba_writer_set_mem (w1, 256); + if (err) + goto leave; + + /* Write OID and and nonce. */ + err = ksba_oid_from_str (oidstr_ocsp_nonce, &buf, &buflen); + if (err) + goto leave; + err = _ksba_ber_write_tl (w1, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, buflen); + if (!err) + err = ksba_writer_write (w1, buf, buflen); + xfree (buf); buf = NULL; + /* We known that the nonce is short enough to put the tag into 2 bytes, thus + we write the encapsulating octet string directly with a fixed length. */ + if (!err) + err = _ksba_ber_write_tl (w1, TYPE_OCTET_STRING, CLASS_UNIVERSAL, 0, + 2+ocsp->noncelen); + if (!err) + err = _ksba_ber_write_tl (w1, TYPE_INTEGER, CLASS_UNIVERSAL, 0, + ocsp->noncelen); + if (!err) + err = ksba_writer_write (w1, ocsp->nonce, ocsp->noncelen); + + /* Put a sequence around. */ + p = ksba_writer_snatch_mem (w1, &derlen); + if (!p) + { + err = ksba_writer_error (w1); + goto leave; + } + err = _ksba_ber_write_tl (w2, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, derlen); + if (!err) + err = ksba_writer_write (w2, p, derlen); + xfree (p); p = NULL; + + /* Put the sequence around all extensions. */ + err = ksba_writer_set_mem (w1, 256); + if (err) + goto leave; + p = ksba_writer_snatch_mem (w2, &derlen); + if (!p) + { + err = ksba_writer_error (w2); + goto leave; + } + err = _ksba_ber_write_tl (w1, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, derlen); + if (!err) + err = ksba_writer_write (w1, p, derlen); + xfree (p); p = NULL; + + /* And put a context tag around everything. */ + p = ksba_writer_snatch_mem (w1, &derlen); + if (!p) + { + err = ksba_writer_error (w1); + goto leave; + } + err = _ksba_ber_write_tl (wout, 2, CLASS_CONTEXT, 1, derlen); + if (!err) + err = ksba_writer_write (wout, p, derlen); + xfree (p); p = NULL; + + + leave: + ksba_writer_release (w2); + ksba_writer_release (w1); + return err; +} + + +/* Build a request from the current context. The function checks that + all necessary information have been set and stores the prepared + request in the context. A subsequent ksba_ocsp_build_request may + then be used to retrieve this request. Optional the requestmay be + signed beofre calling ksba_ocsp_build_request. + */ +gpg_error_t +ksba_ocsp_prepare_request (ksba_ocsp_t ocsp) +{ + gpg_error_t err; + struct ocsp_reqitem_s *ri; + unsigned char *p; + const unsigned char *der; + size_t derlen; + ksba_writer_t w1 = NULL; + ksba_writer_t w2 = NULL; + ksba_writer_t w3 = NULL; + ksba_writer_t w4, w5, w6, w7; /* Used as aliases. */ + + if (!ocsp) + return gpg_error (GPG_ERR_INV_VALUE); + + xfree (ocsp->request_buffer); + ocsp->request_buffer = NULL; + ocsp->request_buflen = 0; + + if (!ocsp->requestlist) + return gpg_error (GPG_ERR_MISSING_ACTION); + + /* Create three writer objects for construction of the request. */ + err = ksba_writer_new (&w3); + if (!err) + err = ksba_writer_set_mem (w3, 2048); + if (!err) + err = ksba_writer_new (&w2); + if (!err) + err = ksba_writer_new (&w1); + if (err) + goto leave; + + + /* Loop over all single requests. */ + for (ri=ocsp->requestlist; ri; ri = ri->next) + { + err = ksba_writer_set_mem (w2, 256); + if (!err) + err = ksba_writer_set_mem (w1, 256); + if (err) + goto leave; + + /* Write the AlgorithmIdentifier. */ + err = _ksba_der_write_algorithm_identifier (w1, oidstr_sha1, NULL, 0); + if (err) + goto leave; + + /* Compute the issuerNameHash and write it into the CertID object. */ + err = issuer_name_hash (ri->issuer_cert, ri->issuer_name_hash); + if (!err) + err = _ksba_ber_write_tl (w1, TYPE_OCTET_STRING, CLASS_UNIVERSAL, 0,20); + if (!err) + err = ksba_writer_write (w1, ri->issuer_name_hash, 20); + if(err) + goto leave; + + /* Compute the issuerKeyHash and write it. */ + err = issuer_key_hash (ri->issuer_cert, ri->issuer_key_hash); + if (!err) + err = _ksba_ber_write_tl (w1, TYPE_OCTET_STRING, CLASS_UNIVERSAL, 0,20); + if (!err) + err = ksba_writer_write (w1, ri->issuer_key_hash, 20); + if (err) + goto leave; + + /* Write the serialNumber of the certificate to be checked. */ + err = _ksba_cert_get_serial_ptr (ri->cert, &der, &derlen); + if (!err) + err = _ksba_ber_write_tl (w1, TYPE_INTEGER, CLASS_UNIVERSAL, 0, derlen); + if (!err) + err = ksba_writer_write (w1, der, derlen); + if (err) + goto leave; + xfree (ri->serialno); + ri->serialno = xtrymalloc (derlen); + if (!ri->serialno) + err = gpg_error_from_errno (errno); + if (err) + goto leave; + memcpy (ri->serialno, der, derlen); + ri->serialnolen = derlen; + + + /* Now write it out as a sequence to the outer certID object. */ + p = ksba_writer_snatch_mem (w1, &derlen); + if (!p) + { + err = ksba_writer_error (w1); + goto leave; + } + err = _ksba_ber_write_tl (w2, TYPE_SEQUENCE, CLASS_UNIVERSAL, + 1, derlen); + if (!err) + err = ksba_writer_write (w2, p, derlen); + xfree (p); p = NULL; + if (err) + goto leave; + + /* Here we would write singleRequestExtensions. */ + + /* Now write it out as a sequence to the outer Request object. */ + p = ksba_writer_snatch_mem (w2, &derlen); + if (!p) + { + err = ksba_writer_error (w2); + goto leave; + } + err = _ksba_ber_write_tl (w3, TYPE_SEQUENCE, CLASS_UNIVERSAL, + 1, derlen); + if (!err) + err = ksba_writer_write (w3, p, derlen); + xfree (p); p = NULL; + if (err) + goto leave; + + } /* End of looping over single requests. */ + + /* Reuse writers; for clarity, use new names. */ + w4 = w1; + w5 = w2; + err = ksba_writer_set_mem (w4, 2048); + if (!err) + err = ksba_writer_set_mem (w5, 2048); + if (err) + goto leave; + + /* Put a sequence tag before the requestList. */ + p = ksba_writer_snatch_mem (w3, &derlen); + if (!p) + { + err = ksba_writer_error (w3); + goto leave; + } + err = _ksba_ber_write_tl (w4, TYPE_SEQUENCE, CLASS_UNIVERSAL, + 1, derlen); + if (!err) + err = ksba_writer_write (w4, p, derlen); + xfree (p); p = NULL; + if (err) + goto leave; + + /* The requestExtensions go here. */ + err = write_request_extensions (ocsp, w4); + + /* Write the tbsRequest. */ + + /* The version is default, thus we don't write it. */ + + /* The requesterName would go here. */ + + /* Write the requestList. */ + p = ksba_writer_snatch_mem (w4, &derlen); + if (!p) + { + err = ksba_writer_error (w4); + goto leave; + } + err = _ksba_ber_write_tl (w5, TYPE_SEQUENCE, CLASS_UNIVERSAL, + 1, derlen); + if (!err) + err = ksba_writer_write (w5, p, derlen); + xfree (p); p = NULL; + if (err) + goto leave; + + /* Reuse writers; for clarity, use new names. */ + w6 = w3; + w7 = w4; + err = ksba_writer_set_mem (w6, 2048); + if (!err) + err = ksba_writer_set_mem (w7, 2048); + if (err) + goto leave; + + /* Prepend a sequence tag. */ + p = ksba_writer_snatch_mem (w5, &derlen); + if (!p) + { + err = ksba_writer_error (w5); + goto leave; + } + err = _ksba_ber_write_tl (w6, TYPE_SEQUENCE, CLASS_UNIVERSAL, + 1, derlen); + if (!err) + err = ksba_writer_write (w6, p, derlen); + xfree (p); p = NULL; + if (err) + goto leave; + + /* Write the ocspRequest. */ + + /* Note that we do not support the optional signature, because this + saves us one writer object. */ + + /* Prepend a sequence tag. */ +/* p = ksba_writer_snatch_mem (w6, &derlen); */ +/* if (!p) */ +/* { */ +/* err = ksba_writer_error (w6); */ +/* goto leave; */ +/* } */ +/* err = _ksba_ber_write_tl (w7, TYPE_SEQUENCE, CLASS_UNIVERSAL, */ +/* 1, derlen); */ +/* if (!err) */ +/* err = ksba_writer_write (w7, p, derlen); */ +/* xfree (p); p = NULL; */ +/* if (err) */ +/* goto leave; */ + + + /* Read out the entire request. */ + p = ksba_writer_snatch_mem (w6, &derlen); + if (!p) + { + err = ksba_writer_error (w6); + goto leave; + } + ocsp->request_buffer = p; + ocsp->request_buflen = derlen; + /* Ready. */ + + leave: + ksba_writer_release (w3); + ksba_writer_release (w2); + ksba_writer_release (w1); + return err; +} + + +gpg_error_t +ksba_ocsp_hash_request (ksba_ocsp_t ocsp, + void (*hasher)(void *, const void *, + size_t length), + void *hasher_arg) +{ + (void)ocsp; + (void)hasher; + (void)hasher_arg; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +} + + +gpg_error_t +ksba_ocsp_set_sig_val (ksba_ocsp_t ocsp, + ksba_const_sexp_t sigval) +{ + (void)ocsp; + (void)sigval; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +} + + +gpg_error_t +ksba_ocsp_add_cert (ksba_ocsp_t ocsp, ksba_cert_t cert) +{ + (void)ocsp; + (void)cert; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +} + + + +/* Build a request from the current context. The function checks that + all necessary information have been set and then returns an + allocated buffer with the resulting request. + */ +gpg_error_t +ksba_ocsp_build_request (ksba_ocsp_t ocsp, + unsigned char **r_buffer, size_t *r_buflen) +{ + gpg_error_t err; + + if (!ocsp || !r_buffer || !r_buflen) + return gpg_error (GPG_ERR_INV_VALUE); + *r_buffer = NULL; + *r_buflen = 0; + + if (!ocsp->requestlist) + return gpg_error (GPG_ERR_MISSING_ACTION); + if (!ocsp->request_buffer) + { + /* No prepare done, do it now. */ + err = ksba_ocsp_prepare_request (ocsp); + if (err) + return err; + assert (ocsp->request_buffer); + } + *r_buffer = ocsp->request_buffer; + *r_buflen = ocsp->request_buflen; + ocsp->request_buffer = NULL; + ocsp->request_buflen = 0; + return 0; +} + + + +/* + Parse the response extensions and store them aways. While doing + this we also check the nonce extension. A typical data ASN.1 blob + with only the nonce extension as passed to this function is: + + SEQUENCE { + SEQUENCE { + OBJECT IDENTIFIER ocspNonce (1 3 6 1 5 5 7 48 1 2) + OCTET STRING, encapsulates { + INTEGER + 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 + } + } + } +*/ + +static int +parse_response_extensions (ksba_ocsp_t ocsp, + const unsigned char *data, size_t datalen) +{ + gpg_error_t err; + struct tag_info ti; + size_t length; + char *oid = NULL; + + assert (!ocsp->response_extensions); + err = parse_sequence (&data, &datalen, &ti); + if (err) + goto leave; + length = ti.length; + while (length) + { + struct ocsp_extension_s *ex; + int is_crit; + + err = parse_sequence (&data, &datalen, &ti); + if (err) + goto leave; + if (length < ti.nhdr + ti.length) + { + err = gpg_error (GPG_ERR_BAD_BER); + goto leave; + } + length -= ti.nhdr + ti.length; + + xfree (oid); + err = parse_object_id_into_str (&data, &datalen, &oid); + if (err) + goto leave; + is_crit = 0; + err = parse_optional_boolean (&data, &datalen, &is_crit); + if (err) + goto leave; + err = parse_octet_string (&data, &datalen, &ti); + if (err) + goto leave; + if (!strcmp (oid, oidstr_ocsp_nonce)) + { + err = parse_integer (&data, &datalen, &ti); + if (err) + goto leave; + if (ocsp->noncelen != ti.length + || memcmp (ocsp->nonce, data, ti.length)) + ocsp->bad_nonce = 1; + else + ocsp->good_nonce = 1; + } + ex = xtrymalloc (sizeof *ex + strlen (oid) + ti.length); + if (!ex) + { + err = gpg_error_from_errno (errno); + goto leave; + } + ex->crit = is_crit; + strcpy (ex->data, oid); + ex->data[strlen (oid)] = 0; + ex->off = strlen (oid) + 1; + ex->len = ti.length; + memcpy (ex->data + ex->off, data, ti.length); + ex->next = ocsp->response_extensions; + ocsp->response_extensions = ex; + + parse_skip (&data, &datalen, &ti); /* Skip the octet string / integer. */ + } + + leave: + xfree (oid); + return err; +} + + +/* + Parse single extensions and store them away. +*/ +static int +parse_single_extensions (struct ocsp_reqitem_s *ri, + const unsigned char *data, size_t datalen) +{ + gpg_error_t err; + struct tag_info ti; + size_t length; + char *oid = NULL; + + assert (ri && !ri->single_extensions); + err = parse_sequence (&data, &datalen, &ti); + if (err) + goto leave; + length = ti.length; + while (length) + { + struct ocsp_extension_s *ex; + int is_crit; + + err = parse_sequence (&data, &datalen, &ti); + if (err) + goto leave; + if (length < ti.nhdr + ti.length) + { + err = gpg_error (GPG_ERR_BAD_BER); + goto leave; + } + length -= ti.nhdr + ti.length; + + xfree (oid); + err = parse_object_id_into_str (&data, &datalen, &oid); + if (err) + goto leave; + is_crit = 0; + err = parse_optional_boolean (&data, &datalen, &is_crit); + if (err) + goto leave; + err = parse_octet_string (&data, &datalen, &ti); + if (err) + goto leave; + ex = xtrymalloc (sizeof *ex + strlen (oid) + ti.length); + if (!ex) + { + err = gpg_error_from_errno (errno); + goto leave; + } + ex->crit = is_crit; + strcpy (ex->data, oid); + ex->data[strlen (oid)] = 0; + ex->off = strlen (oid) + 1; + ex->len = ti.length; + memcpy (ex->data + ex->off, data, ti.length); + ex->next = ri->single_extensions; + ri->single_extensions = ex; + + parse_skip (&data, &datalen, &ti); /* Skip the octet string / integer. */ + } + + leave: + xfree (oid); + return err; +} + + +/* Parse the first part of a response: + + 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 } + + On success the RESPONSE_STATUS field of OCSP will be set to the + response status and DATA will now point to the first byte in the + octet string of the response; RLEN will be set to the length of + this octet string. Note thate DATALEN is also updated but might + point to a value larger than RLEN points to, if the provided data + is a part of a larger image. */ +static gpg_error_t +parse_response_status (ksba_ocsp_t ocsp, + unsigned char const **data, size_t *datalen, + size_t *rlength) +{ + gpg_error_t err; + struct tag_info ti; + char *oid; + + *rlength = 0; + /* Parse the OCSPResponse sequence. */ + err = parse_sequence (data, datalen, &ti); + if (err) + return err; + /* Parse the OCSPResponseStatus. */ + err = parse_enumerated (data, datalen, &ti, 1); + if (err) + return err; + switch (**data) + { + case 0: ocsp->response_status = KSBA_OCSP_RSPSTATUS_SUCCESS; break; + case 1: ocsp->response_status = KSBA_OCSP_RSPSTATUS_MALFORMED; break; + case 2: ocsp->response_status = KSBA_OCSP_RSPSTATUS_INTERNAL; break; + case 3: ocsp->response_status = KSBA_OCSP_RSPSTATUS_TRYLATER; break; + case 5: ocsp->response_status = KSBA_OCSP_RSPSTATUS_SIGREQUIRED; break; + case 6: ocsp->response_status = KSBA_OCSP_RSPSTATUS_UNAUTHORIZED; break; + default: ocsp->response_status = KSBA_OCSP_RSPSTATUS_OTHER; break; + } + parse_skip (data, datalen, &ti); + + if (ocsp->response_status) + return 0; /* This is an error reponse; we have to stop here. */ + + /* We have a successful reponse status, thus we check that + ResponseBytes are actually available. */ + err = parse_context_tag (data, datalen, &ti, 0); + if (err) + return err; + err = parse_sequence (data, datalen, &ti); + if (err) + return err; + err = parse_object_id_into_str (data, datalen, &oid); + if (err) + return err; + if (strcmp (oid, oidstr_ocsp_basic)) + { + xfree (oid); + return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + } + xfree (oid); + + /* Check that the next field is an octet string. */ + err = parse_octet_string (data, datalen, &ti); + if (err) + return err; + *rlength = ti.length; + return 0; +} + +/* Parse the object: + + 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 + +*/ + +static gpg_error_t +parse_single_response (ksba_ocsp_t ocsp, + unsigned char const **data, size_t *datalen) +{ + gpg_error_t err; + struct tag_info ti; + const unsigned char *savedata; + const unsigned char *endptr; + size_t savedatalen; + size_t n; + char *oid; + ksba_isotime_t this_update, next_update, revocation_time; + int look_for_request; + const unsigned char *name_hash; + const unsigned char *key_hash; + const unsigned char *serialno; + size_t serialnolen; + struct ocsp_reqitem_s *request_item = NULL; + + /* The SingeResponse sequence. */ + err = parse_sequence (data, datalen, &ti); + if (err) + return err; + endptr = *data + ti.length; + + /* The CertID is + SEQUENCE { + hashAlgorithm AlgorithmIdentifier, + issuerNameHash OCTET STRING, -- Hash of Issuer's DN + issuerKeyHash OCTET STRING, -- Hash of Issuers public key + serialNumber CertificateSerialNumber } + */ + err = parse_sequence (data, datalen, &ti); + if (err) + return err; + err = _ksba_parse_algorithm_identifier (*data, *datalen, &n, &oid); + if (err) + return err; + assert (n <= *datalen); + *data += n; + *datalen -= n; + /* fprintf (stderr, "algorithmIdentifier is `%s'\n", oid); */ + look_for_request = !strcmp (oid, oidstr_sha1); + xfree (oid); + + err = parse_octet_string (data, datalen, &ti); + if (err) + return err; + name_hash = *data; +/* fprintf (stderr, "issuerNameHash="); */ +/* dump_hex (*data, ti.length); */ +/* putc ('\n', stderr); */ + if (ti.length != 20) + look_for_request = 0; /* Can't be a SHA-1 digest. */ + parse_skip (data, datalen, &ti); + + err = parse_octet_string (data, datalen, &ti); + if (err) + return err; + key_hash = *data; +/* fprintf (stderr, "issuerKeyHash="); */ +/* dump_hex (*data, ti.length); */ +/* putc ('\n', stderr); */ + if (ti.length != 20) + look_for_request = 0; /* Can't be a SHA-1 digest. */ + parse_skip (data, datalen, &ti); + + err= parse_integer (data, datalen, &ti); + if (err) + return err; + serialno = *data; + serialnolen = ti.length; +/* fprintf (stderr, "serialNumber="); */ +/* dump_hex (*data, ti.length); */ +/* putc ('\n', stderr); */ + parse_skip (data, datalen, &ti); + + if (look_for_request) + { + for (request_item = ocsp->requestlist; + request_item; request_item = request_item->next) + if (!memcmp (request_item->issuer_name_hash, name_hash, 20) + && !memcmp (request_item->issuer_key_hash, key_hash, 20) + && request_item->serialnolen == serialnolen + && !memcmp (request_item->serialno, serialno, serialnolen)) + break; /* Got it. */ + } + + + /* + CertStatus ::= CHOICE { + good [0] IMPLICIT NULL, + revoked [1] IMPLICIT RevokedInfo, + unknown [2] IMPLICIT UnknownInfo } + */ + *revocation_time = 0; + err = _ksba_ber_parse_tl (data, datalen, &ti); + if (err) + return err; + if (ti.length > *datalen) + return gpg_error (GPG_ERR_BAD_BER); + else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && !ti.is_constructed) + { /* good */ + if (!ti.length) + ; /* Cope with zero length objects. */ + else if (*datalen && !**data) + { /* Skip the NULL. */ + (*datalen)--; + (*data)++; + } + else + return gpg_error (GPG_ERR_INV_OBJ); + + if (request_item) + request_item->status = KSBA_STATUS_GOOD; + } + else if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed) + { /* revoked */ + ksba_crl_reason_t reason = KSBA_CRLREASON_UNSPECIFIED; + + err = parse_asntime_into_isotime (data, datalen, revocation_time); + if (err) + return err; +/* fprintf (stderr, "revocationTime=%s\n", revocation_time); */ + savedata = *data; + savedatalen = *datalen; + err = parse_context_tag (data, datalen, &ti, 0); + if (err) + { + *data = savedata; + *datalen = savedatalen; + } + else + { /* Got a revocationReason. */ + err = parse_enumerated (data, datalen, &ti, 1); + if (err) + return err; + switch (**data) + { + case 0: reason = KSBA_CRLREASON_UNSPECIFIED; break; + case 1: reason = KSBA_CRLREASON_KEY_COMPROMISE; break; + case 2: reason = KSBA_CRLREASON_CA_COMPROMISE; break; + case 3: reason = KSBA_CRLREASON_AFFILIATION_CHANGED; break; + case 4: reason = KSBA_CRLREASON_SUPERSEDED; break; + case 5: reason = KSBA_CRLREASON_CESSATION_OF_OPERATION; break; + case 6: reason = KSBA_CRLREASON_CERTIFICATE_HOLD; break; + case 8: reason = KSBA_CRLREASON_REMOVE_FROM_CRL; break; + case 9: reason = KSBA_CRLREASON_PRIVILEGE_WITHDRAWN; break; + case 10: reason = KSBA_CRLREASON_AA_COMPROMISE; break; + default: reason = KSBA_CRLREASON_OTHER; break; + } + parse_skip (data, datalen, &ti); + } +/* fprintf (stderr, "revocationReason=%04x\n", reason); */ + if (request_item) + { + request_item->status = KSBA_STATUS_REVOKED; + _ksba_copy_time (request_item->revocation_time, revocation_time); + request_item->revocation_reason = reason; + } + } + else if (ti.class == CLASS_CONTEXT && ti.tag == 2 && !ti.is_constructed + && *datalen) + { /* unknown */ + if (!ti.length) + ; /* Cope with zero length objects. */ + else if (!**data) + { /* Skip the NULL. */ + (*datalen)--; + (*data)++; + } + else /* The comment indicates that an enumeration may come here. */ + { + err = parse_enumerated (data, datalen, &ti, 0); + if (err) + return err; + fprintf (stderr, "libksba: unknownReason with an enum of " + "length %u detected\n", + (unsigned int)ti.length); + parse_skip (data, datalen, &ti); + } + if (request_item) + request_item->status = KSBA_STATUS_UNKNOWN; + } + else + err = gpg_error (GPG_ERR_INV_OBJ); + + /* thisUpdate. */ + err = parse_asntime_into_isotime (data, datalen, this_update); + if (err) + return err; +/* fprintf (stderr, "thisUpdate=%s\n", this_update); */ + if (request_item) + _ksba_copy_time (request_item->this_update, this_update); + + /* nextUpdate is optional. */ + if (*data >= endptr) + return 0; + *next_update = 0; + err = _ksba_ber_parse_tl (data, datalen, &ti); + if (err) + return err; + if (ti.length > *datalen) + return gpg_error (GPG_ERR_BAD_BER); + else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed) + { /* have nextUpdate */ + err = parse_asntime_into_isotime (data, datalen, next_update); + if (err) + return err; +/* fprintf (stderr, "nextUpdate=%s\n", next_update); */ + if (request_item) + _ksba_copy_time (request_item->next_update, next_update); + } + else if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed) + { /* Undo that read. */ + *data -= ti.nhdr; + *datalen += ti.nhdr; + } + else + err = gpg_error (GPG_ERR_INV_OBJ); + + /* singleExtensions is optional */ + if (*data >= endptr) + return 0; + err = _ksba_ber_parse_tl (data, datalen, &ti); + if (err) + return err; + if (ti.length > *datalen) + return gpg_error (GPG_ERR_BAD_BER); + if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed) + { + if (request_item) + { + err = parse_single_extensions (request_item, *data, ti.length); + if (err) + return err; + } + parse_skip (data, datalen, &ti); + } + else + err = gpg_error (GPG_ERR_INV_OBJ); + + return 0; +} + +/* Parse the object: + + ResponseData ::= SEQUENCE { + version [0] EXPLICIT Version DEFAULT v1, + responderID ResponderID, + producedAt GeneralizedTime, + responses SEQUENCE OF SingleResponse, + responseExtensions [1] EXPLICIT Extensions OPTIONAL } + + ResponderID ::= CHOICE { + byName [1] Name, + byKey [2] KeyHash } + + +*/ +static gpg_error_t +parse_response_data (ksba_ocsp_t ocsp, + unsigned char const **data, size_t *datalen) +{ + gpg_error_t err; + struct tag_info ti; + const unsigned char *savedata; + size_t savedatalen; + size_t responses_length; + + /* The out er sequence. */ + err = parse_sequence (data, datalen, &ti); + if (err) + return err; + + /* The optional version field. */ + savedata = *data; + savedatalen = *datalen; + err = parse_context_tag (data, datalen, &ti, 0); + if (err) + { + *data = savedata; + *datalen = savedatalen; + } + else + { + /* FIXME: check that the version matches. */ + parse_skip (data, datalen, &ti); + } + + /* The responderID field. */ + assert (!ocsp->responder_id.name); + assert (!ocsp->responder_id.keyid); + err = _ksba_ber_parse_tl (data, datalen, &ti); + if (err) + return err; + if (ti.length > *datalen) + return gpg_error (GPG_ERR_BAD_BER); + else if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed) + { /* byName. */ + err = _ksba_derdn_to_str (*data, ti.length, &ocsp->responder_id.name); + if (err) + return err; + parse_skip (data, datalen, &ti); + } + else if (ti.class == CLASS_CONTEXT && ti.tag == 2 && ti.is_constructed) + { /* byKey. */ + err = parse_octet_string (data, datalen, &ti); + if (err) + return err; + if (!ti.length) + return gpg_error (GPG_ERR_INV_OBJ); /* Zero length key id. */ + ocsp->responder_id.keyid = xtrymalloc (ti.length); + if (!ocsp->responder_id.keyid) + return gpg_error_from_errno (errno); + memcpy (ocsp->responder_id.keyid, *data, ti.length); + ocsp->responder_id.keyidlen = ti.length; + parse_skip (data, datalen, &ti); + } + else + err = gpg_error (GPG_ERR_INV_OBJ); + + /* The producedAt field. */ + err = parse_asntime_into_isotime (data, datalen, ocsp->produced_at); + if (err) + return err; + + /* The responses field set. */ + err = parse_sequence (data, datalen, &ti); + if (err ) + return err; + responses_length = ti.length; + while (responses_length) + { + savedatalen = *datalen; + err = parse_single_response (ocsp, data, datalen); + if (err) + return err; + assert (responses_length >= savedatalen - *datalen); + responses_length -= savedatalen - *datalen; + } + + /* The optional responseExtensions set. */ + savedata = *data; + savedatalen = *datalen; + err = parse_context_tag (data, datalen, &ti, 1); + if (!err) + { + err = parse_response_extensions (ocsp, *data, ti.length); + if (err) + return err; + parse_skip (data, datalen, &ti); + } + else if (gpg_err_code (err) == GPG_ERR_INV_OBJ) + { + *data = savedata; + *datalen = savedatalen; + } + else + return err; + + return 0; +} + + +/* Parse the entire response message pointed to by MSG of length + MSGLEN. */ +static gpg_error_t +parse_response (ksba_ocsp_t ocsp, const unsigned char *msg, size_t msglen) +{ + gpg_error_t err; + struct tag_info ti; + const unsigned char *msgstart; + const unsigned char *endptr; + const char *s; + size_t len; + + + msgstart = msg; + err = parse_response_status (ocsp, &msg, &msglen, &len); + if (err) + return err; + msglen = len; /* We don't care about any extra bytes provided to us. */ + if (ocsp->response_status) + { +/* fprintf (stderr,"response status found to be %d - stop\n", */ +/* ocsp->response_status); */ + return 0; + } + + /* Now that we are sure that it is a BasicOCSPResponse, we can parse + the really important things: + + BasicOCSPResponse ::= SEQUENCE { + tbsResponseData ResponseData, + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING, + certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ + err = parse_sequence (&msg, &msglen, &ti); + if (err) + return err; + endptr = msg + ti.length; + + ocsp->hash_offset = msg - msgstart; + err = parse_response_data (ocsp, &msg, &msglen); + if (err) + return err; + ocsp->hash_length = msg - msgstart - ocsp->hash_offset; + + /* The signatureAlgorithm and the signature. We only need to get the + length of both objects and let a specialized function do the + actual parsing. */ + s = msg; + len = msglen; + err = parse_sequence (&msg, &msglen, &ti); + if (err) + return err; + parse_skip (&msg, &msglen, &ti); + err= _ksba_ber_parse_tl (&msg, &msglen, &ti); + if (err) + return err; + if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BIT_STRING + && !ti.is_constructed) ) + err = gpg_error (GPG_ERR_INV_OBJ); + else if (!ti.length) + err = gpg_error (GPG_ERR_TOO_SHORT); + else if (ti.length > msglen) + err = gpg_error (GPG_ERR_BAD_BER); + parse_skip (&msg, &msglen, &ti); + len = len - msglen; + xfree (ocsp->sigval); ocsp->sigval = NULL; + err = _ksba_sigval_to_sexp (s, len, &ocsp->sigval); + if (err) + return err; + + /* Parse the optional sequence of certificates. */ + if (msg >= endptr) + return 0; /* It's optional, so stop now. */ + err = parse_context_tag (&msg, &msglen, &ti, 0); + if (gpg_err_code (err) == GPG_ERR_INV_OBJ) + return 0; /* Not the right tag. Stop here. */ + if (err) + return err; + err = parse_sequence (&msg, &msglen, &ti); + if (err) + return err; + if (ti.ndef) + return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); + + { + ksba_cert_t cert; + struct ocsp_certlist_s *cl, **cl_tail; + + assert (!ocsp->received_certs); + cl_tail = &ocsp->received_certs; + endptr = msg + ti.length; + while (msg < endptr) + { + /* Find the length of the certificate. */ + s = msg; + err = parse_sequence (&msg, &msglen, &ti); + if (err) + return err; + err = ksba_cert_new (&cert); + if (err) + return err; + err = ksba_cert_init_from_mem (cert, msg - ti.nhdr, + ti.nhdr + ti.length); + if (err) + { + ksba_cert_release (cert); + return err; + } + parse_skip (&msg, &msglen, &ti); + cl = xtrycalloc (1, sizeof *cl); + if (!cl) + err = gpg_error_from_errno (errno); + if (err) + { + ksba_cert_release (cert); + return gpg_error (GPG_ERR_ENOMEM); + } + cl->cert = cert; + + *cl_tail = cl; + cl_tail = &cl->next; + } + } + + return 0; +} + + +/* Given the OCSP context and a binary reponse message of MSGLEN bytes + in MSG, this fucntion parses the response and prepares it for + signature verification. The status from the server is returned in + RESPONSE_STATUS and must be checked even if the function returns + without an error. */ +gpg_error_t +ksba_ocsp_parse_response (ksba_ocsp_t ocsp, + const unsigned char *msg, size_t msglen, + ksba_ocsp_response_status_t *response_status) +{ + gpg_error_t err; + struct ocsp_reqitem_s *ri; + + if (!ocsp || !msg || !msglen || !response_status) + return gpg_error (GPG_ERR_INV_VALUE); + + if (!ocsp->requestlist) + return gpg_error (GPG_ERR_MISSING_ACTION); + + /* Reset the fields used to track the response. This is so that we + can use the parse function a second time for the same + request. This is useful in case of a TryLater response status. */ + ocsp->response_status = KSBA_OCSP_RSPSTATUS_NONE; + release_ocsp_certlist (ocsp->received_certs); + release_ocsp_extensions (ocsp->response_extensions); + ocsp->received_certs = NULL; + ocsp->hash_length = 0; + ocsp->bad_nonce = 0; + ocsp->good_nonce = 0; + xfree (ocsp->responder_id.name); + ocsp->responder_id.name = NULL; + xfree (ocsp->responder_id.keyid); + ocsp->responder_id.keyid = NULL; + for (ri=ocsp->requestlist; ri; ri = ri->next) + { + ri->status = KSBA_STATUS_NONE; + *ri->this_update = 0; + *ri->next_update = 0; + *ri->revocation_time = 0; + ri->revocation_reason = 0; + release_ocsp_extensions (ri->single_extensions); + } + + /* Run the actual parser. */ + err = parse_response (ocsp, msg, msglen); + *response_status = ocsp->response_status; + + /* FIXME: find duplicates in the request list and set them to the + same status. */ + + if (*response_status == KSBA_OCSP_RSPSTATUS_SUCCESS) + if (ocsp->bad_nonce || (ocsp->noncelen && !ocsp->good_nonce)) + *response_status = KSBA_OCSP_RSPSTATUS_REPLAYED; + + return err; +} + + +/* Return the digest algorithm to be used for the signature or NULL in + case of an error. The returned pointer is valid as long as the + context is valid and no other ksba_ocsp_parse_response or + ksba_ocsp_build_request has been used. */ +const char * +ksba_ocsp_get_digest_algo (ksba_ocsp_t ocsp) +{ + return ocsp? ocsp->digest_oid : NULL; +} + + +/* Hash the data of the response using the hash function HASHER which + will be passed HASHER_ARG as its first argument and a pointer and a + length of the data to be hashed. This hash function might be called + several times and should update the hash context. The algorithm to + be used for the hashing can be retrieved using + ksba_ocsp_get_digest_algo. Note that MSG and MSGLEN should be + indentical to the values passed to ksba_ocsp_parse_response. */ +gpg_error_t +ksba_ocsp_hash_response (ksba_ocsp_t ocsp, + const unsigned char *msg, size_t msglen, + void (*hasher)(void *, const void *, size_t length), + void *hasher_arg) + +{ + if (!ocsp || !msg || !hasher) + return gpg_error (GPG_ERR_INV_VALUE); + if (!ocsp->hash_length) + return gpg_error (GPG_ERR_MISSING_ACTION); + if (ocsp->hash_offset + ocsp->hash_length >= msglen) + return gpg_error (GPG_ERR_CONFLICT); + + hasher (hasher_arg, msg + ocsp->hash_offset, ocsp->hash_length); + return 0; +} + + +/* Return the actual signature in a format suitable to be used as + input to Libgcrypt's verification function. The caller must free + the returned string and that function may be called only once after + a successful ksba_ocsp_parse_response. Returns NULL for an invalid + handle or if no signature is available. If PRODUCED_AT is not NULL, + it will receive the time the response was signed. */ +ksba_sexp_t +ksba_ocsp_get_sig_val (ksba_ocsp_t ocsp, ksba_isotime_t produced_at) +{ + ksba_sexp_t p; + + if (produced_at) + *produced_at = 0; + if (!ocsp || !ocsp->sigval ) + return NULL; + + if (produced_at) + _ksba_copy_time (produced_at, ocsp->produced_at); + + p = ocsp->sigval; + ocsp->sigval = NULL; + return p; +} + + +/* Return the responder ID for the current response into R_NAME or + into R_KEYID. On sucess either R_NAME or R_KEYID will receive an + allocated object. If R_NAME or R_KEYID has been passed as NULL but + a value is available the errorcode GPG_ERR_NO_DATA is returned. + Caller must release the values stored at R_NAME or R_KEYID; the + function stores NULL tehre in case of an error. */ +gpg_error_t +ksba_ocsp_get_responder_id (ksba_ocsp_t ocsp, + char **r_name, ksba_sexp_t *r_keyid) +{ + if (r_name) + *r_name = NULL; + if (r_keyid) + *r_keyid = NULL; + + if (!ocsp) + return gpg_error (GPG_ERR_INV_VALUE); + + if (ocsp->responder_id.name && r_name) + { + *r_name = xtrystrdup (ocsp->responder_id.name); + if (!*r_name) + return gpg_error_from_errno (errno); + } + else if (ocsp->responder_id.keyid && r_keyid) + { + char numbuf[50]; + size_t numbuflen; + + sprintf (numbuf,"(%lu:", (unsigned long)ocsp->responder_id.keyidlen); + numbuflen = strlen (numbuf); + *r_keyid = xtrymalloc (numbuflen + ocsp->responder_id.keyidlen + 2); + if (!*r_keyid) + return gpg_error_from_errno (errno); + strcpy (*r_keyid, numbuf); + memcpy (*r_keyid+numbuflen, + ocsp->responder_id.keyid, ocsp->responder_id.keyidlen); + (*r_keyid)[numbuflen + ocsp->responder_id.keyidlen] = ')'; + (*r_keyid)[numbuflen + ocsp->responder_id.keyidlen + 1] = 0; + } + else + gpg_error (GPG_ERR_NO_DATA); + + return 0; +} + + +/* Get optional certificates out of a response. The caller may use + * this in a loop to get all certificates. The returned certificate + * is a shallow copy of the original one; the caller must still use + * ksba_cert_release() to free it. Returns: A certificate object or + * NULL for end of list or error. */ +ksba_cert_t +ksba_ocsp_get_cert (ksba_ocsp_t ocsp, int idx) +{ + struct ocsp_certlist_s *cl; + + if (!ocsp || idx < 0) + return NULL; + + for (cl=ocsp->received_certs; cl && idx; cl = cl->next, idx--) + ; + if (!cl) + return NULL; + ksba_cert_ref (cl->cert); + return cl->cert; +} + + + + +/* Return the status of the certificate CERT for the last response + done on the context OCSP. CERT must be the same certificate as + used for the request; only a shallow compare is done (i.e. the + pointers are compared). R_STATUS returns the status value, + R_THIS_UPDATE and R_NEXT_UPDATE are the corresponding OCSP response + values, R_REVOCATION_TIME is only set to the revocation time if the + indicated status is revoked, R_REASON will be set to the reason + given for a revocation. All the R_* arguments may be given as NULL + if the value is not required. The function return 0 on success, + GPG_ERR_NOT_FOUND if CERT was not used in the request or any other + error code. Note that the caller should have checked the signature + of the entire reponse to be good before using the stati retruned by + this function. */ +gpg_error_t +ksba_ocsp_get_status (ksba_ocsp_t ocsp, ksba_cert_t cert, + ksba_status_t *r_status, + ksba_isotime_t r_this_update, + ksba_isotime_t r_next_update, + ksba_isotime_t r_revocation_time, + ksba_crl_reason_t *r_reason) +{ + struct ocsp_reqitem_s *ri; + + if (!ocsp || !cert || !r_status) + return gpg_error (GPG_ERR_INV_VALUE); + if (!ocsp->requestlist) + return gpg_error (GPG_ERR_MISSING_ACTION); + + /* Find the certificate. We don't care about the issuer certificate + and stop at the first match. The implementation may be optimized + by keeping track of the last certificate found to start with the + next one then. Given that a usual request consists only of a few + certificates, this does not make much sense in reality. */ + for (ri=ocsp->requestlist; ri; ri = ri->next) + if (ri->cert == cert) + break; + if (!ri) + return gpg_error (GPG_ERR_NOT_FOUND); + if (r_status) + *r_status = ri->status; + if (r_this_update) + _ksba_copy_time (r_this_update, ri->this_update); + if (r_next_update) + _ksba_copy_time (r_next_update, ri->next_update); + if (r_revocation_time) + _ksba_copy_time (r_revocation_time, ri->revocation_time); + if (r_reason) + *r_reason = ri->revocation_reason; + return 0; +} + + +/* WARNING: The returned values ares only valid as long as no other + ocsp function is called on the same context. */ +gpg_error_t +ksba_ocsp_get_extension (ksba_ocsp_t ocsp, ksba_cert_t cert, int idx, + char const **r_oid, int *r_crit, + unsigned char const **r_der, size_t *r_derlen) +{ + struct ocsp_extension_s *ex; + + if (!ocsp) + return gpg_error (GPG_ERR_INV_VALUE); + if (!ocsp->requestlist) + return gpg_error (GPG_ERR_MISSING_ACTION); + if (idx < 0) + return gpg_error (GPG_ERR_INV_INDEX); + + if (cert) + { + /* Return extensions for the certificate (singleExtensions). */ + struct ocsp_reqitem_s *ri; + + for (ri=ocsp->requestlist; ri; ri = ri->next) + if (ri->cert == cert) + break; + if (!ri) + return gpg_error (GPG_ERR_NOT_FOUND); + + for (ex=ri->single_extensions; ex && idx; ex = ex->next, idx--) + ; + if (!ex) + return gpg_error (GPG_ERR_EOF); /* No more extensions. */ + } + else + { + /* Return extensions for the response (responseExtensions). */ + for (ex=ocsp->response_extensions; ex && idx; ex = ex->next, idx--) + ; + if (!ex) + return gpg_error (GPG_ERR_EOF); /* No more extensions. */ + } + + if (r_oid) + *r_oid = ex->data; + if (r_crit) + *r_crit = ex->crit; + if (r_der) + *r_der = ex->data + ex->off; + if (r_derlen) + *r_derlen = ex->len; + + return 0; +} |