diff --git a/src/ber-help.c b/src/ber-help.c index f6a6692..87109f3 100644 --- a/src/ber-help.c +++ b/src/ber-help.c @@ -1,471 +1,477 @@ /* ber-help.c - BER herlper functions * Copyright (C) 2001, 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 . */ #include #include #include #include #include #include "util.h" #include "asn1-func.h" /* need some constants */ #include "ber-help.h" /* Fixme: The parser functions should check that primitive types don't have the constructed bit set (which is not allowed). This saves us some work when using these parsers */ static int read_byte (ksba_reader_t reader) { unsigned char buf; size_t nread; int rc; do rc = ksba_reader_read (reader, &buf, 1, &nread); while (!rc && !nread); return rc? -1: buf; } static int premature_eof (struct tag_info *ti) { /* Note: We do an strcmp on this string at othyer places. */ ti->err_string = "premature EOF"; return gpg_error (GPG_ERR_BAD_BER); } static gpg_error_t eof_or_error (ksba_reader_t reader, struct tag_info *ti, int premature) { gpg_error_t err; err = ksba_reader_error (reader); if (err) { ti->err_string = "read error"; return err; } if (premature) return premature_eof (ti); return gpg_error (GPG_ERR_EOF); } /* Read the tag and the length part from the TLV triplet. */ gpg_error_t _ksba_ber_read_tl (ksba_reader_t reader, struct tag_info *ti) { int c; unsigned long tag; ti->length = 0; ti->ndef = 0; ti->nhdr = 0; ti->err_string = NULL; ti->non_der = 0; /* Get the tag */ c = read_byte (reader); if (c==-1) return eof_or_error (reader, ti, 0); ti->buf[ti->nhdr++] = c; ti->class = (c & 0xc0) >> 6; ti->is_constructed = !!(c & 0x20); tag = c & 0x1f; if (tag == 0x1f) { tag = 0; do { /* We silently ignore an overflow in the tag value. It is not worth checking for it. */ tag <<= 7; c = read_byte (reader); if (c == -1) return eof_or_error (reader, ti, 1); if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; tag |= c & 0x7f; } while (c & 0x80); } ti->tag = tag; /* Get the length */ c = read_byte (reader); if (c == -1) return eof_or_error (reader, ti, 1); if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; if ( !(c & 0x80) ) ti->length = c; else if (c == 0x80) { ti->ndef = 1; ti->non_der = 1; } else if (c == 0xff) { ti->err_string = "forbidden length value"; return gpg_error (GPG_ERR_BAD_BER); } else { unsigned long len = 0; int count = c & 0x7f; if (count > sizeof (len) || count > sizeof (size_t)) return gpg_error (GPG_ERR_BAD_BER); for (; count; count--) { len <<= 8; c = read_byte (reader); if (c == -1) return eof_or_error (reader, ti, 1); if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; len |= c & 0xff; } ti->length = len; } /* Without this kludge some example certs can't be parsed */ if (ti->class == CLASS_UNIVERSAL && !ti->tag) ti->length = 0; return 0; } /* Parse the buffer at the address BUFFER which of SIZE and return the tag and the length part from the TLV triplet. Update BUFFER and SIZE on success. */ gpg_error_t _ksba_ber_parse_tl (unsigned char const **buffer, size_t *size, struct tag_info *ti) { int c; unsigned long tag; const unsigned char *buf = *buffer; size_t length = *size; ti->length = 0; ti->ndef = 0; ti->nhdr = 0; ti->err_string = NULL; ti->non_der = 0; /* Get the tag */ if (!length) return premature_eof (ti); c = *buf++; length--; ti->buf[ti->nhdr++] = c; ti->class = (c & 0xc0) >> 6; ti->is_constructed = !!(c & 0x20); tag = c & 0x1f; if (tag == 0x1f) { tag = 0; do { /* We silently ignore an overflow in the tag value. It is not worth checking for it. */ tag <<= 7; if (!length) return premature_eof (ti); c = *buf++; length--; if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; tag |= c & 0x7f; } while (c & 0x80); } ti->tag = tag; /* Get the length */ if (!length) return premature_eof (ti); c = *buf++; length--; if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; if ( !(c & 0x80) ) ti->length = c; else if (c == 0x80) { ti->ndef = 1; ti->non_der = 1; } else if (c == 0xff) { ti->err_string = "forbidden length value"; return gpg_error (GPG_ERR_BAD_BER); } else { unsigned long len = 0; int count = c & 0x7f; if (count > sizeof (len) || count > sizeof (size_t)) return gpg_error (GPG_ERR_BAD_BER); for (; count; count--) { len <<= 8; if (!length) return premature_eof (ti); c = *buf++; length--; if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; len |= c & 0xff; } + /* Sanity check for the length: This is done so that we can take + * the value for malloc plus some additional bytes without + * risking an overflow. */ + if (len > (1 << 30)) + return gpg_error (GPG_ERR_BAD_BER); ti->length = len; } + /* Without this kludge some example certs can't be parsed */ if (ti->class == CLASS_UNIVERSAL && !ti->tag) ti->length = 0; *buffer = buf; *size = length; return 0; } /* Write TAG of CLASS to WRITER. constructed is a flag telling whether the value is a constructed one. length gives the length of the value, if it is 0 undefinite length is assumed. length is ignored for the NULL tag. */ gpg_error_t _ksba_ber_write_tl (ksba_writer_t writer, unsigned long tag, enum tag_class class, int constructed, unsigned long length) { unsigned char buf[50]; int buflen = 0; if (tag < 0x1f) { *buf = (class << 6) | tag; if (constructed) *buf |= 0x20; buflen++; } else { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } if (!tag && !class) buf[buflen++] = 0; /* end tag */ else if (tag == TYPE_NULL && !class) buf[buflen++] = 0; /* NULL tag */ else if (!length) buf[buflen++] = 0x80; /* indefinite length */ else if (length < 128) buf[buflen++] = length; else { int i; /* fixme: if we know the sizeof an ulong we could support larger objects - however this is pretty ridiculous */ i = (length <= 0xff ? 1: length <= 0xffff ? 2: length <= 0xffffff ? 3: 4); buf[buflen++] = (0x80 | i); if (i > 3) buf[buflen++] = length >> 24; if (i > 2) buf[buflen++] = length >> 16; if (i > 1) buf[buflen++] = length >> 8; buf[buflen++] = length; } return ksba_writer_write (writer, buf, buflen); } /* Encode TAG of CLASS in BUFFER. CONSTRUCTED is a flag telling whether the value is a constructed one. LENGTH gives the length of the value, if it is 0 undefinite length is assumed. LENGTH is ignored for the NULL tag. It is assumed that the provide buffer is large enough for storing the result - this is usually achieved by using _ksba_ber_count_tl() in advance. Returns 0 in case of an error or the length of the encoding.*/ size_t _ksba_ber_encode_tl (unsigned char *buffer, unsigned long tag, enum tag_class class, int constructed, unsigned long length) { unsigned char *buf = buffer; if (tag < 0x1f) { *buf = (class << 6) | tag; if (constructed) *buf |= 0x20; buf++; } else { return 0; /*Not implemented*/ } if (!tag && !class) *buf++ = 0; /* end tag */ else if (tag == TYPE_NULL && !class) *buf++ = 0; /* NULL tag */ else if (!length) *buf++ = 0x80; /* indefinite length */ else if (length < 128) *buf++ = length; else { int i; /* fixme: if we know the sizeof an ulong we could support larger objetcs - however this is pretty ridiculous */ i = (length <= 0xff ? 1: length <= 0xffff ? 2: length <= 0xffffff ? 3: 4); *buf++ = (0x80 | i); if (i > 3) *buf++ = length >> 24; if (i > 2) *buf++ = length >> 16; if (i > 1) *buf++ = length >> 8; *buf++ = length; } return buf - buffer; } /* Calculate the length of the TL needed to encode a TAG of CLASS. CONSTRUCTED is a flag telling whether the value is a constructed one. LENGTH gives the length of the value; if it is 0 an indefinite length is assumed. LENGTH is ignored for the NULL tag. */ size_t _ksba_ber_count_tl (unsigned long tag, enum tag_class class, int constructed, unsigned long length) { int buflen = 0; (void)constructed; /* Not used, but passed for uniformity of such calls. */ if (tag < 0x1f) { buflen++; } else { buflen++; /* assume one and let the actual write function bail out */ } if (!tag && !class) buflen++; /* end tag */ else if (tag == TYPE_NULL && !class) buflen++; /* NULL tag */ else if (!length) buflen++; /* indefinite length */ else if (length < 128) buflen++; else { int i; /* fixme: if we know the sizeof an ulong we could support larger objetcs - however this is pretty ridiculous */ i = (length <= 0xff ? 1: length <= 0xffff ? 2: length <= 0xffffff ? 3: 4); buflen++; if (i > 3) buflen++; if (i > 2) buflen++; if (i > 1) buflen++; buflen++; } return buflen; } diff --git a/src/cert.c b/src/cert.c index 7f19dc1..f3ff6a1 100644 --- a/src/cert.c +++ b/src/cert.c @@ -1,2143 +1,2164 @@ /* cert.c - main function for the certificate handling * Copyright (C) 2001, 2002, 2003, 2004, 2005, 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 . */ #include #include #include #include #include #include #include "util.h" #include "ber-decoder.h" #include "ber-help.h" #include "convert.h" #include "keyinfo.h" #include "sexp-parse.h" #include "cert.h" static const char oidstr_subjectKeyIdentifier[] = "2.5.29.14"; static const char oidstr_keyUsage[] = "2.5.29.15"; static const char oidstr_subjectAltName[] = "2.5.29.17"; static const char oidstr_issuerAltName[] = "2.5.29.18"; static const char oidstr_basicConstraints[] = "2.5.29.19"; static const char oidstr_crlDistributionPoints[] = "2.5.29.31"; static const char oidstr_certificatePolicies[] = "2.5.29.32"; static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35"; static const char oidstr_extKeyUsage[] = "2.5.29.37"; static const char oidstr_authorityInfoAccess[] = "1.3.6.1.5.5.7.1.1"; static const char oidstr_subjectInfoAccess[] = "1.3.6.1.5.5.7.1.11"; /** * ksba_cert_new: * * Create a new and empty certificate object * * Return value: 0 on success or error code. For a successful * operation, ACERT is set to the new certifixate obbject, otherwise * it is set to NULL. **/ gpg_error_t ksba_cert_new (ksba_cert_t *acert) { *acert = xtrycalloc (1, sizeof **acert); if (!*acert) return gpg_error_from_errno (errno); (*acert)->ref_count++; return 0; } void ksba_cert_ref (ksba_cert_t cert) { if (!cert) fprintf (stderr, "BUG: ksba_cert_ref for NULL\n"); else ++cert->ref_count; } /** * ksba_cert_release: * @cert: A certificate object * * Release a certificate object. **/ void ksba_cert_release (ksba_cert_t cert) { int i; if (!cert) return; if (cert->ref_count < 1) { fprintf (stderr, "BUG: trying to release an already released cert\n"); return; } if (--cert->ref_count) return; if (cert->udata) { struct cert_user_data *ud = cert->udata; cert->udata = NULL; do { struct cert_user_data *ud2 = ud->next; if (ud->data && ud->data != ud->databuf) xfree (ud->data); xfree (ud); ud = ud2; } while (ud); } xfree (cert->cache.digest_algo); if (cert->cache.extns_valid) { for (i=0; i < cert->cache.n_extns; i++) xfree (cert->cache.extns[i].oid); xfree (cert->cache.extns); } _ksba_asn_release_nodes (cert->root); ksba_asn_tree_release (cert->asn_tree); xfree (cert->image); xfree (cert); } /* Store arbitrary data along with a certificate. The DATA of length DATALEN will be stored under the string KEY. If some data is already stored under this key it will be replaced by the new data. Using NULL for DATA will effectivly delete the data. On error (i.e. out or memory) an already existing data object stored under KEY may get deleted. This function is not thread safe because we don't employ any locking. */ gpg_error_t ksba_cert_set_user_data (ksba_cert_t cert, const char *key, const void *data, size_t datalen) { struct cert_user_data *ud; if (!cert || !key || !*key) return gpg_error (GPG_ERR_INV_VALUE); for (ud=cert->udata; ud; ud = ud->next) if (!strcmp (ud->key, key)) break; if (ud) /* Update the data stored under this key or reuse this item. */ { if (ud->data && ud->data != ud->databuf) xfree (ud->data); ud->data = NULL; if (data && datalen <= sizeof ud->databuf) { memcpy (ud->databuf, data, datalen); ud->data = ud->databuf; ud->datalen = datalen; } else if (data) { ud->data = xtrymalloc (datalen); if (!ud->data) return gpg_error_from_errno (errno); memcpy (ud->data, data, datalen); ud->datalen = datalen; } } else if (data) /* Insert as a new item. */ { ud = xtrycalloc (1, sizeof *ud + strlen (key)); if (!ud) return gpg_error_from_errno (errno); strcpy (ud->key, key); if (datalen <= sizeof ud->databuf) { memcpy (ud->databuf, data, datalen); ud->data = ud->databuf; ud->datalen = datalen; } else { ud->data = xtrymalloc (datalen); if (!ud->data) { xfree (ud); return gpg_error_from_errno (errno); } memcpy (ud->data, data, datalen); ud->datalen = datalen; } ud->next = cert->udata; cert->udata = ud; } return 0; } /* Return user data for certificate CERT stored under the string KEY. The caller needs to provide a suitable large BUFFER and pass the usable length of the buffer in BUFFERLEN. If DATALEN is not NULL, the length of the data stored at BUFFER will be stored there. If BUFFER is NULL, BUFFERLEN will be ignored and the required length of the buffer will be returned at DATALEN. On success 0 is returned. If no data is stored under KEY GPG_ERR_NOT_FOUND is returned. If the provided buffer is too short, GPG_ERR_BUFFER_TOO_SHORT will be returned (note, that this is not the case if BUFFER is NULL). */ gpg_error_t ksba_cert_get_user_data (ksba_cert_t cert, const char *key, void *buffer, size_t bufferlen, size_t *datalen) { struct cert_user_data *ud; if (!cert || !key || !*key) return gpg_error (GPG_ERR_INV_VALUE); for (ud=cert->udata; ud; ud = ud->next) if (!strcmp (ud->key, key)) break; if (!ud || !ud->data) return gpg_error (GPG_ERR_NOT_FOUND); if (datalen) *datalen = ud->datalen; if (buffer) { if (ud->datalen > bufferlen) return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); memcpy (buffer, ud->data, ud->datalen); } return 0; } /** * ksba_cert_read_der: * @cert: An unitialized certificate object * @reader: A KSBA Reader object * * Read the next certificate from the reader and store it in the * certificate object for future access. The certificate is parsed * and rejected if it has any syntactical or semantical error * (i.e. does not match the ASN.1 description). * * Return value: 0 on success or an error value **/ gpg_error_t ksba_cert_read_der (ksba_cert_t cert, ksba_reader_t reader) { gpg_error_t err = 0; BerDecoder decoder = NULL; if (!cert || !reader) return gpg_error (GPG_ERR_INV_VALUE); if (cert->initialized) return gpg_error (GPG_ERR_CONFLICT); /* Fixme: should remove the old one */ _ksba_asn_release_nodes (cert->root); ksba_asn_tree_release (cert->asn_tree); cert->root = NULL; cert->asn_tree = NULL; err = ksba_asn_create_tree ("tmttv2", &cert->asn_tree); if (err) goto leave; decoder = _ksba_ber_decoder_new (); if (!decoder) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } err = _ksba_ber_decoder_set_reader (decoder, reader); if (err) goto leave; err = _ksba_ber_decoder_set_module (decoder, cert->asn_tree); if (err) goto leave; err = _ksba_ber_decoder_decode (decoder, "TMTTv2.Certificate", 0, &cert->root, &cert->image, &cert->imagelen); if (!err) cert->initialized = 1; leave: _ksba_ber_decoder_release (decoder); return err; } gpg_error_t ksba_cert_init_from_mem (ksba_cert_t cert, const void *buffer, size_t length) { gpg_error_t err; ksba_reader_t reader; err = ksba_reader_new (&reader); if (err) return err; err = ksba_reader_set_mem (reader, buffer, length); if (err) { ksba_reader_release (reader); return err; } err = ksba_cert_read_der (cert, reader); ksba_reader_release (reader); return err; } const unsigned char * ksba_cert_get_image (ksba_cert_t cert, size_t *r_length ) { AsnNode n; if (!cert) return NULL; if (!cert->initialized) return NULL; n = _ksba_asn_find_node (cert->root, "Certificate"); if (!n) return NULL; if (n->off == -1) { /* fputs ("ksba_cert_get_image problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return NULL; } /* Due to minor problems in our parser we might hit the assertion below. Thus we better return a error, proper. */ if ( !(n->nhdr + n->len + n->off <= cert->imagelen) ) { fprintf (stderr,"\nOops, ksba_cert_get_image failed: " "imagelen=%lu hdr=%d len=%d off=%d\n", (unsigned long)cert->imagelen, n->nhdr, (int)n->len, n->off); return NULL; } /*assert (n->nhdr + n->len + n->off <= cert->imagelen);*/ if (r_length) *r_length = n->nhdr + n->len; return cert->image + n->off; } /* Check whether certificates A and B are identical and return o in this case. */ int _ksba_cert_cmp (ksba_cert_t a, ksba_cert_t b) { const unsigned char *img_a, *img_b; size_t len_a, len_b; img_a = ksba_cert_get_image (a, &len_a); if (!img_a) return 1; img_b = ksba_cert_get_image (b, &len_b); if (!img_b) return 1; return !(len_a == len_b && !memcmp (img_a, img_b, len_a)); } gpg_error_t ksba_cert_hash (ksba_cert_t cert, int what, void (*hasher)(void *, const void *, size_t length), void *hasher_arg) { AsnNode n; if (!cert /*|| !hasher*/) return gpg_error (GPG_ERR_INV_VALUE); if (!cert->initialized) return gpg_error (GPG_ERR_NO_DATA); n = _ksba_asn_find_node (cert->root, what == 1? "Certificate.tbsCertificate" : "Certificate"); if (!n) return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */ if (n->off == -1) { /* fputs ("ksba_cert_hash problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return gpg_error (GPG_ERR_NO_VALUE); } hasher (hasher_arg, cert->image + n->off, n->nhdr + n->len); return 0; } /** * ksba_cert_get_digest_algo: * @cert: Initialized certificate object * * Figure out the the digest algorithm used for the signature and * return its OID * * This function is intended as a helper for the ksba_cert_hash(). * * Return value: NULL for error otherwise a constant string with the OID. * This string is valid as long the certificate object is valid. **/ const char * ksba_cert_get_digest_algo (ksba_cert_t cert) { gpg_error_t err; AsnNode n; char *algo; size_t nread; if (!cert) return NULL; /* Ooops (can't set cert->last_error :-(). */ if (!cert->initialized) { cert->last_error = gpg_error (GPG_ERR_NO_DATA); return NULL; } if (cert->cache.digest_algo) return cert->cache.digest_algo; /* n = _ksba_asn_find_node (cert->root, */ /* "Certificate.signatureAlgorithm.algorithm"); */ /* algo = _ksba_oid_node_to_str (cert->image, n); */ /* if (!algo) */ /* cert->last_error = gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); */ /* else */ /* cert->cache.digest_algo = algo; */ n = _ksba_asn_find_node (cert->root, "Certificate.signatureAlgorithm"); if (!n || n->off == -1) err = gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); else err = _ksba_parse_algorithm_identifier (cert->image + n->off, n->nhdr + n->len, &nread, &algo); if (err) cert->last_error = err; else cert->cache.digest_algo = algo; return algo; } /** * ksba_cert_get_serial: * @cert: certificate object * * This function returnes the serial number of the certificate. The * serial number is an integer returned as an canonical encoded * S-expression with just one element. * * Return value: An allocated S-Exp or NULL for no value. **/ ksba_sexp_t ksba_cert_get_serial (ksba_cert_t cert) { AsnNode n; char *p; char numbuf[22]; int numbuflen; if (!cert || !cert->initialized) return NULL; n = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.serialNumber"); if (!n) return NULL; /* oops - should be there */ if (n->off == -1) { /* fputs ("get_serial problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return NULL; } sprintf (numbuf,"(%u:", (unsigned int)n->len); numbuflen = strlen (numbuf); p = xtrymalloc (numbuflen + n->len + 2); if (!p) return NULL; strcpy (p, numbuf); memcpy (p+numbuflen, cert->image + n->off + n->nhdr, n->len); p[numbuflen + n->len] = ')'; p[numbuflen + n->len + 1] = 0; return p; } /* Return a pointer to the DER encoding of the serial number in CERT in PTR and the length of that field in LENGTH. */ gpg_error_t _ksba_cert_get_serial_ptr (ksba_cert_t cert, unsigned char const **ptr, size_t *length) { asn_node_t n; if (!cert || !cert->initialized || !ptr || !length) return gpg_error (GPG_ERR_INV_VALUE); n = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.serialNumber"); if (!n || n->off == -1) return gpg_error (GPG_ERR_NO_VALUE); *ptr = cert->image + n->off + n->nhdr; *length = n->len; return 0; } /* Return a pointer to the DER encoding of the subject's DN in CERT in PTR and the length of that object in LENGTH. */ gpg_error_t _ksba_cert_get_subject_dn_ptr (ksba_cert_t cert, unsigned char const **ptr, size_t *length) { asn_node_t n; if (!cert || !cert->initialized || !ptr || !length) return gpg_error (GPG_ERR_INV_VALUE); n = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.subject"); if (!n || !n->down) return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */ n = n->down; /* dereference the choice node */ if (n->off == -1) return gpg_error (GPG_ERR_NO_VALUE); *ptr = cert->image + n->off; *length = n->nhdr + n->len; return 0; } /* Worker function for get_isssuer and get_subject. */ static gpg_error_t get_name (ksba_cert_t cert, int idx, int use_subject, char **result) { gpg_error_t err; char *p; int i; const char *oid; struct tag_info ti; const unsigned char *der; size_t off, derlen, seqlen; if (!cert || !cert->initialized || !result) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); *result = NULL; if (!idx) { /* Get the required DN */ AsnNode n; n = _ksba_asn_find_node (cert->root, (use_subject? "Certificate.tbsCertificate.subject": "Certificate.tbsCertificate.issuer") ); if (!n || !n->down) return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */ n = n->down; /* dereference the choice node */ if (n->off == -1) return gpg_error (GPG_ERR_NO_VALUE); err = _ksba_dn_to_str (cert->image, n, &p); if (err) return err; *result = p; return 0; } /* get {issuer,subject}AltName */ for (i=0; !(err=ksba_cert_get_extension (cert, i, &oid, NULL, &off, &derlen)); i++) { if (!strcmp (oid, (use_subject? oidstr_subjectAltName:oidstr_issuerAltName))) break; } if (err) return err; /* no alt name or error*/ der = cert->image + off; /* FIXME: We should use _ksba_name_new_from_der and ksba_name_enum here */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); seqlen = ti.length; if (seqlen > derlen) return gpg_error (GPG_ERR_BAD_BER); if (!seqlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* empty sequence is not allowed */ while (seqlen) { err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (seqlen < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.nhdr; if (seqlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.length; if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (!(ti.tag == 1 || ti.tag == 2 || ti.tag == 6)) ; /* Not a supported tag: Do not change IDX. */ else if (--idx) ; /* not yet at the desired index */ else if (ti.tag == 1) { /* rfc822Name - this is an implicit IA5_STRING */ p = xtrymalloc (ti.length+3); if (!p) return gpg_error (GPG_ERR_ENOMEM); *p = '<'; memcpy (p+1, der, ti.length); p[ti.length+1] = '>'; p[ti.length+2] = 0; *result = p; return 0; } else if (ti.tag == 2 || ti.tag == 6) { /* dNSName or URI - this are implicit IA5_STRINGs */ char numbuf[20], *numbufp; size_t numbuflen; numbufp = smklen (numbuf, DIM(numbuf), ti.length, &numbuflen); p = xtrymalloc (11 + numbuflen + ti.length + 3); if (!p) return gpg_error (GPG_ERR_ENOMEM); *result = p; p = stpcpy (p, ti.tag == 2? "(8:dns-name" : "(3:uri"); p = stpcpy (p, numbufp); memcpy (p, der, ti.length); p += ti.length; *p++ = ')'; *p = 0; return 0; } /* advance pointer */ der += ti.length; derlen -= ti.length; } return gpg_error (GPG_ERR_EOF); } /** * ksba_cert_get_issuer: * @cert: certificate object * * With @idx == 0 this function returns the Distinguished Name (DN) of * the certificate issuer which in most cases is a CA. The format of * the returned string is in accordance with RFC-2253. NULL is * returned if the DN is not available which is an error and should * have been catched by the certificate reading function. * * With @idx > 0 the function may be used to enumerate alternate * issuer names. The function returns NULL if there are no more * alternate names. The function does only return alternate names * which are recognized by libksba and ignores others. The format of * the returned name is either a RFC-2253 formated one which can be * detected by checking whether the first character is letter or * digit. rfc-2822 conform email addresses are returned enclosed in * angle brackets, the opening angle bracket should be used to * indicate this. Other formats are returned as an S-Expression in * canonical format, so a opening parenthesis may be used to detect * this encoding, the name may include binary null characters, so * strlen may return a length shorther than actually used, the real * length is implictly given by the structure of the S-Exp, an extra * null is appended for safety reasons. * * The caller must free the returned string using ksba_free() or the * function he has registered as a replacement. * * Return value: An allocated string or NULL for error. **/ char * ksba_cert_get_issuer (ksba_cert_t cert, int idx) { gpg_error_t err; char *name; err = get_name (cert, idx, 0, &name); if (err) { cert->last_error = err; return NULL; } return name; } /* See ..get_issuer */ char * ksba_cert_get_subject (ksba_cert_t cert, int idx) { gpg_error_t err; char *name; err = get_name (cert, idx, 1, &name); if (err) { cert->last_error = err; return NULL; } return name; } /** * ksba_cert_get_valididy: * @cert: certificate object * @what: 0 for notBefore, 1 for notAfter * @timebuf: Returns the time. * * Return the validity object from the certificate. If no value is * available 0 is returned because we can safely assume that this is * not a valid date. * * Return value: The time value an 0 or an error code. **/ gpg_error_t ksba_cert_get_validity (ksba_cert_t cert, int what, ksba_isotime_t timebuf) { AsnNode n, n2; if (!cert || what < 0 || what > 1) return gpg_error (GPG_ERR_INV_VALUE); *timebuf = 0; if (!cert->initialized) return gpg_error (GPG_ERR_NO_DATA); n = _ksba_asn_find_node (cert->root, what == 0? "Certificate.tbsCertificate.validity.notBefore" : "Certificate.tbsCertificate.validity.notAfter"); if (!n) return 0; /* no value available */ /* Fixme: We should remove the choice node and don't use this ugly hack */ for (n2=n->down; n2; n2 = n2->right) { if ((n2->type == TYPE_UTC_TIME || n2->type == TYPE_GENERALIZED_TIME) && n2->off != -1) break; } n = n2; if (!n) return 0; /* no value available */ return_val_if_fail (n->off != -1, gpg_error (GPG_ERR_BUG)); return _ksba_asntime_to_iso (cert->image + n->off + n->nhdr, n->len, n->type == TYPE_UTC_TIME, timebuf); } ksba_sexp_t ksba_cert_get_public_key (ksba_cert_t cert) { AsnNode n; gpg_error_t err; ksba_sexp_t string; if (!cert) return NULL; if (!cert->initialized) return NULL; n = _ksba_asn_find_node (cert->root, "Certificate" ".tbsCertificate.subjectPublicKeyInfo"); if (!n) { cert->last_error = gpg_error (GPG_ERR_NO_VALUE); return NULL; } err = _ksba_keyinfo_to_sexp (cert->image + n->off, n->nhdr + n->len, &string); if (err) { cert->last_error = err; return NULL; } return string; } /* Return a pointer to the DER encoding of the actual public key (i.e. the bit string) in PTR and the length of that object in LENGTH. */ gpg_error_t _ksba_cert_get_public_key_ptr (ksba_cert_t cert, unsigned char const **ptr, size_t *length) { asn_node_t n; if (!cert || !cert->initialized || !ptr || !length) return gpg_error (GPG_ERR_INV_VALUE); n = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.subjectPublicKeyInfo"); if (!n || !n->down || !n->down->right) return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */ n = n->down->right; if (n->off == -1) return gpg_error (GPG_ERR_NO_VALUE); *ptr = cert->image + n->off + n->nhdr; *length = n->len; /* Somehow we end up at the preceding NULL value, and not at a sequence, we hack it way here. */ if (*length && !**ptr) { (*length)--; (*ptr)++; } return 0; } ksba_sexp_t ksba_cert_get_sig_val (ksba_cert_t cert) { AsnNode n, n2; gpg_error_t err; ksba_sexp_t string; if (!cert) return NULL; if (!cert->initialized) return NULL; n = _ksba_asn_find_node (cert->root, "Certificate.signatureAlgorithm"); if (!n) { cert->last_error = gpg_error (GPG_ERR_NO_VALUE); return NULL; } if (n->off == -1) { /* fputs ("ksba_cert_get_sig_val problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ cert->last_error = gpg_error (GPG_ERR_NO_VALUE); return NULL; } n2 = n->right; err = _ksba_sigval_to_sexp (cert->image + n->off, n->nhdr + n->len + ((!n2||n2->off == -1)? 0:(n2->nhdr+n2->len)), &string); if (err) { cert->last_error = err; return NULL; } return string; } /* Read all extensions into the cache */ static gpg_error_t read_extensions (ksba_cert_t cert) { AsnNode start, n; int count; assert (!cert->cache.extns_valid); assert (!cert->cache.extns); start = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.extensions.."); for (count=0, n=start; n; n = n->right) count++; if (!count) { cert->cache.n_extns = 0; cert->cache.extns_valid = 1; return 0; /* no extensions at all */ } cert->cache.extns = xtrycalloc (count, sizeof *cert->cache.extns); if (!cert->cache.extns) return gpg_error (GPG_ERR_ENOMEM); cert->cache.n_extns = count; { for (count=0; start; start = start->right, count++) { n = start->down; if (!n || n->type != TYPE_OBJECT_ID) goto no_value; cert->cache.extns[count].oid = _ksba_oid_node_to_str (cert->image, n); if (!cert->cache.extns[count].oid) goto no_value; n = n->right; if (n && n->type == TYPE_BOOLEAN) { if (n->off != -1 && n->len && cert->image[n->off + n->nhdr]) cert->cache.extns[count].crit = 1; n = n->right; } if (!n || n->type != TYPE_OCTET_STRING || n->off == -1) goto no_value; cert->cache.extns[count].off = n->off + n->nhdr; cert->cache.extns[count].len = n->len; } assert (count == cert->cache.n_extns); cert->cache.extns_valid = 1; return 0; no_value: for (count=0; count < cert->cache.n_extns; count++) xfree (cert->cache.extns[count].oid); xfree (cert->cache.extns); cert->cache.extns = NULL; return gpg_error (GPG_ERR_NO_VALUE); } } /* Return information about the IDX nth extension */ gpg_error_t ksba_cert_get_extension (ksba_cert_t cert, int idx, char const **r_oid, int *r_crit, size_t *r_deroff, size_t *r_derlen) { gpg_error_t err; if (!cert) return gpg_error (GPG_ERR_INV_VALUE); if (!cert->initialized) return gpg_error (GPG_ERR_NO_DATA); if (!cert->cache.extns_valid) { err = read_extensions (cert); if (err) return err; assert (cert->cache.extns_valid); } if (idx == cert->cache.n_extns) return gpg_error (GPG_ERR_EOF); /* No more extensions. */ if (idx < 0 || idx >= cert->cache.n_extns) return gpg_error (GPG_ERR_INV_INDEX); if (r_oid) *r_oid = cert->cache.extns[idx].oid; if (r_crit) *r_crit = cert->cache.extns[idx].crit; if (r_deroff) *r_deroff = cert->cache.extns[idx].off; if (r_derlen) *r_derlen = cert->cache.extns[idx].len; return 0; } /* Return information on the basicConstraint (2.5.19.19) of CERT. R_CA receives true if this is a CA and only in that case R_PATHLEN is set to the maximim certification path length or -1 if there is nosuch limitation */ gpg_error_t ksba_cert_is_ca (ksba_cert_t cert, int *r_ca, int *r_pathlen) { gpg_error_t err; const char *oid; int idx, crit; size_t off, derlen, seqlen; const unsigned char *der; struct tag_info ti; unsigned long value; /* set default values */ if (r_ca) *r_ca = 0; if (r_pathlen) *r_pathlen = -1; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit, &off, &derlen)); idx++) { if (!strcmp (oid, oidstr_basicConstraints)) break; } if (gpg_err_code (err) == GPG_ERR_EOF) return 0; /* no such constraint */ if (err) return err; /* check that there is only one */ for (idx++; !(err=ksba_cert_get_extension (cert, idx, &oid, NULL, NULL, NULL)); idx++) { if (!strcmp (oid, oidstr_basicConstraints)) return gpg_error (GPG_ERR_DUP_VALUE); } der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); seqlen = ti.length; if (seqlen > derlen) return gpg_error (GPG_ERR_BAD_BER); if (!seqlen) return 0; /* an empty sequence is allowed because both elements are optional */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (seqlen < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.nhdr; if (seqlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.length; if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BOOLEAN) { if (ti.length != 1) return gpg_error (GPG_ERR_ENCODING_PROBLEM); if (r_ca) *r_ca = !!*der; der++; derlen--; if (!seqlen) return 0; /* ready (no pathlength) */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (seqlen < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.nhdr; if (seqlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.length; } if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER)) return gpg_error (GPG_ERR_INV_CERT_OBJ); for (value=0; ti.length; ti.length--) { value <<= 8; value |= (*der++) & 0xff; derlen--; } if (r_pathlen) *r_pathlen = value; /* if the extension is marked as critical and any stuff is still left we better return an error */ if (crit && seqlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); return 0; } /* Get the key usage flags. The function returns Ksba_No_Data if no key usage is specified. */ gpg_error_t ksba_cert_get_key_usage (ksba_cert_t cert, unsigned int *r_flags) { gpg_error_t err; const char *oid; int idx, crit; size_t off, derlen; const unsigned char *der; struct tag_info ti; unsigned int bits, mask; int i, unused, full; if (!r_flags) return gpg_error (GPG_ERR_INV_VALUE); *r_flags = 0; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit, &off, &derlen)); idx++) { if (!strcmp (oid, oidstr_keyUsage)) break; } if (gpg_err_code (err) == GPG_ERR_EOF || gpg_err_code (err) == GPG_ERR_NO_VALUE) return gpg_error (GPG_ERR_NO_DATA); /* no key usage */ if (err) return err; /* check that there is only one */ for (idx++; !(err=ksba_cert_get_extension (cert, idx, &oid, NULL, NULL, NULL)); idx++) { if (!strcmp (oid, oidstr_keyUsage)) return gpg_error (GPG_ERR_DUP_VALUE); } der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BIT_STRING && !ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (!ti.length || ti.length > derlen) return gpg_error (GPG_ERR_ENCODING_PROBLEM); /* number of unused bits missing */ unused = *der++; derlen--; ti.length--; if ((!ti.length && unused) || unused/8 > ti.length) return gpg_error (GPG_ERR_ENCODING_PROBLEM); full = ti.length - (unused+7)/8; unused %= 8; mask = 0; for (i=1; unused; i <<= 1, unused--) mask |= i; /* the first octet */ if (!ti.length) return 0; /* no bits set */ bits = *der++; derlen--; ti.length--; if (full) full--; else { bits &= ~mask; mask = 0; } if (bits & 0x80) *r_flags |= KSBA_KEYUSAGE_DIGITAL_SIGNATURE; if (bits & 0x40) *r_flags |= KSBA_KEYUSAGE_NON_REPUDIATION; if (bits & 0x20) *r_flags |= KSBA_KEYUSAGE_KEY_ENCIPHERMENT; if (bits & 0x10) *r_flags |= KSBA_KEYUSAGE_DATA_ENCIPHERMENT; if (bits & 0x08) *r_flags |= KSBA_KEYUSAGE_KEY_AGREEMENT; if (bits & 0x04) *r_flags |= KSBA_KEYUSAGE_KEY_CERT_SIGN; if (bits & 0x02) *r_flags |= KSBA_KEYUSAGE_CRL_SIGN; if (bits & 0x01) *r_flags |= KSBA_KEYUSAGE_ENCIPHER_ONLY; /* the second octet */ if (!ti.length) return 0; /* no bits set */ bits = *der++; derlen--; ti.length--; if (full) full--; else { bits &= mask; mask = ~0; } if (bits & 0x80) *r_flags |= KSBA_KEYUSAGE_DECIPHER_ONLY; return 0; } /* Note, that this helper is also used for ext_key_usage. */ static gpg_error_t append_cert_policy (char **policies, const char *oid, int crit) { char *p; if (!*policies) { *policies = xtrymalloc (strlen (oid) + 4); if (!*policies) return gpg_error (GPG_ERR_ENOMEM); p = *policies; } else { char *tmp = xtryrealloc (*policies, strlen(*policies) + 1 + strlen (oid) + 4); if (!tmp) return gpg_error (GPG_ERR_ENOMEM); *policies = tmp; p = stpcpy (tmp+strlen (tmp), "\n");; } strcpy (stpcpy (p, oid), crit? ":C:": ":N:"); return 0; } /* Return a string with the certificatePolicies delimited by linefeeds. The return values may be extended to carry more information er line, so the caller should only use the first white-space delimited token per line. The function returns GPG_ERR_NO_DATA when this extension is not used. Caller must free the returned value. */ gpg_error_t ksba_cert_get_cert_policies (ksba_cert_t cert, char **r_policies) { gpg_error_t err; const char *oid; int idx, crit; size_t off, derlen, seqlen; const unsigned char *der; struct tag_info ti; if (!cert || !r_policies) return gpg_error (GPG_ERR_INV_VALUE); *r_policies = NULL; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit, &off, &derlen)); idx++) { if (!strcmp (oid, oidstr_certificatePolicies)) { char *suboid; der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) goto leave; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) { err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } if (ti.ndef) { err = gpg_error (GPG_ERR_NOT_DER_ENCODED); goto leave; } seqlen = ti.length; if (seqlen > derlen) { err = gpg_error (GPG_ERR_BAD_BER); goto leave; } while (seqlen) { size_t seqseqlen; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) goto leave; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) { err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } if (ti.ndef) { err = gpg_error (GPG_ERR_NOT_DER_ENCODED); goto leave; } + if (ti.length > derlen) + { + err = gpg_error (GPG_ERR_BAD_BER); + goto leave; + } if (!ti.length) { - err = gpg_error (GPG_ERR_INV_CERT_OBJ); /* no empty inner SEQ */ + /* We do not accept an empty inner SEQ */ + err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } if (ti.nhdr+ti.length > seqlen) { err = gpg_error (GPG_ERR_BAD_BER); goto leave; } seqlen -= ti.nhdr + ti.length; seqseqlen = ti.length; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) goto leave; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID)) { err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } + if (ti.length > derlen) + { + err = gpg_error (GPG_ERR_BAD_BER); + goto leave; + } if (ti.nhdr+ti.length > seqseqlen) { err = gpg_error (GPG_ERR_BAD_BER); goto leave; } seqseqlen -= ti.nhdr; suboid = ksba_oid_to_str (der, ti.length); if (!suboid) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } der += ti.length; derlen -= ti.length; seqseqlen -= ti.length; err = append_cert_policy (r_policies, suboid, crit); xfree (suboid); if (err) goto leave; /* skip the rest of the seq which is more or less optional */ der += seqseqlen; derlen -= seqseqlen; } } } if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; if (!*r_policies || gpg_err_code (err) == GPG_ERR_NO_VALUE) err = gpg_error (GPG_ERR_NO_DATA); leave: if (err) { xfree (*r_policies); *r_policies = NULL; } return err; } /* Return a string with the extendedKeyUsageOIDs delimited by linefeeds. The return values may be extended to carry more information per line, so the caller should only use the first white-space delimited token per line. The function returns GPG_ERR_NO_DATA when this extension is not used. Caller must free the returned value. */ gpg_error_t ksba_cert_get_ext_key_usages (ksba_cert_t cert, char **result) { gpg_error_t err; const char *oid; int idx, crit; size_t off, derlen; const unsigned char *der; struct tag_info ti; if (!cert || !result) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit, &off, &derlen)); idx++) { if (!strcmp (oid, oidstr_extKeyUsage)) { char *suboid; der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) goto leave; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) { err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } if (ti.ndef) { err = gpg_error (GPG_ERR_NOT_DER_ENCODED); goto leave; } if (ti.length > derlen) { err = gpg_error (GPG_ERR_BAD_BER); goto leave; } while (derlen) { err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) goto leave; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID)) { err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } + if (ti.ndef) + { + err = gpg_error (GPG_ERR_NOT_DER_ENCODED); + goto leave; + } + if (ti.length > derlen) + { + err = gpg_error (GPG_ERR_BAD_BER); + goto leave; + } suboid = ksba_oid_to_str (der, ti.length); if (!suboid) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } der += ti.length; derlen -= ti.length; err = append_cert_policy (result, suboid, crit); xfree (suboid); if (err) goto leave; } } } if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; if (!*result || gpg_err_code (err) == GPG_ERR_NO_VALUE) err = gpg_error (GPG_ERR_NO_DATA); leave: if (err) { xfree (*result); *result = NULL; } return err; } /* Helper function for ksba_cert_get_crl_dist_point */ static gpg_error_t parse_distribution_point (const unsigned char *der, size_t derlen, ksba_name_t *distpoint, ksba_name_t *issuer, ksba_crl_reason_t *reason) { gpg_error_t err; struct tag_info ti; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (ti.tag == 0 && derlen) { /* distributionPointName */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (ti.tag == 0) { if (distpoint) { err = _ksba_name_new_from_der (distpoint, der, ti.length); if (err) return err; } } else { /* We don't support nameRelativeToCRLIssuer yet*/ } der += ti.length; derlen -= ti.length; if (!derlen) return 0; /* read the next optional element */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); } if (ti.tag == 1 && derlen) { /* reasonFlags */ unsigned int bits, mask; int i, unused, full; unused = *der++; derlen--; ti.length--; if ((!ti.length && unused) || unused/8 > ti.length) return gpg_error (GPG_ERR_ENCODING_PROBLEM); full = ti.length - (unused+7)/8; unused %= 8; mask = 0; for (i=1; unused; i <<= 1, unused--) mask |= i; /* we are only required to look at the first octect */ if (ti.length && reason) { bits = *der; if (full) full--; else { bits &= ~mask; mask = 0; } if (bits & 0x80) *reason |= KSBA_CRLREASON_UNSPECIFIED; if (bits & 0x40) *reason |= KSBA_CRLREASON_KEY_COMPROMISE; if (bits & 0x20) *reason |= KSBA_CRLREASON_CA_COMPROMISE; if (bits & 0x10) *reason |= KSBA_CRLREASON_AFFILIATION_CHANGED; if (bits & 0x08) *reason |= KSBA_CRLREASON_SUPERSEDED; if (bits & 0x04) *reason |= KSBA_CRLREASON_CESSATION_OF_OPERATION; if (bits & 0x02) *reason |= KSBA_CRLREASON_CERTIFICATE_HOLD; } der += ti.length; derlen -= ti.length; if (!derlen) return 0; /* read the next optional element */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); } if (ti.tag == 2 && derlen) { /* crlIssuer */ if (issuer) { err = _ksba_name_new_from_der (issuer, der, ti.length); if (err) return err; } der += ti.length; derlen -= ti.length; } if (derlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); return 0; } /* Return the CRLDistPoints given in the cert extension. IDX should be iterated started from 0 until the function returns -1. R_DISTPOINT returns a ksba_name_t object with the distribution point name(s) the return value may be NULL to indicate that this name is not available. R_ISSUER returns the CRL issuer; if the returned value is NULL the caller should assume that the CRL issuer is the same as the certificate issuer. R_REASON returns the reason for the CRL. This is a bit encoded value with no bit set if this has not been specified in the cert. The caller may pass NULL to any of the pointer arguments if he is not interested in this value. The return values for R_DISTPOINT and R_ISSUER must be released by the caller using ksba_name_release(). */ gpg_error_t ksba_cert_get_crl_dist_point (ksba_cert_t cert, int idx, ksba_name_t *r_distpoint, ksba_name_t *r_issuer, ksba_crl_reason_t *r_reason) { gpg_error_t err; const char *oid; size_t off, derlen; int myidx, crit; if (r_distpoint) *r_distpoint = NULL; if (r_issuer) *r_issuer = NULL; if (r_reason) *r_reason = 0; for (myidx=0; !(err=ksba_cert_get_extension (cert, myidx, &oid, &crit, &off, &derlen)); myidx++) { if (!strcmp (oid, oidstr_crlDistributionPoints)) { const unsigned char *der; struct tag_info ti; size_t seqlen; der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); seqlen = ti.length; if (seqlen > derlen) return gpg_error (GPG_ERR_BAD_BER); /* Note: an empty sequence is actually not allowed but we better don't care */ while (seqlen) { err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (seqlen < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.nhdr; if (seqlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (idx) { /* skip because we are not yet at the desired index */ der += ti.length; derlen -= ti.length; seqlen -= ti.length; idx--; continue; } if (!ti.length) return 0; err = parse_distribution_point (der, ti.length, r_distpoint, r_issuer, r_reason); if (err && r_distpoint) { ksba_name_release (*r_distpoint); *r_distpoint = NULL; } if (err && r_issuer) { ksba_name_release (*r_issuer); *r_issuer = NULL; } if (err && r_reason) *r_reason = 0; return err; } } } return err; } /* Return the authorityKeyIdentifier in R_NAME and R_SERIAL or/and in R_KEYID. GPG_ERR_NO_DATA is returned if no authorityKeyIdentifier or only one using the keyIdentifier method is available and R_KEYID is NULL. */ gpg_error_t ksba_cert_get_auth_key_id (ksba_cert_t cert, ksba_sexp_t *r_keyid, ksba_name_t *r_name, ksba_sexp_t *r_serial) { gpg_error_t err; const char *oid; size_t off, derlen; const unsigned char *der; const unsigned char *keyid_der = NULL; size_t keyid_derlen = 0; int idx, crit; struct tag_info ti; char numbuf[30]; size_t numbuflen; if (r_keyid) *r_keyid = NULL; if (!r_name || !r_serial) return gpg_error (GPG_ERR_INV_VALUE); *r_name = NULL; *r_serial = NULL; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit, &off, &derlen)); idx++) { if (!strcmp (oid, oidstr_authorityKeyIdentifier)) break; } if (gpg_err_code (err) == GPG_ERR_EOF || gpg_err_code (err) == GPG_ERR_NO_VALUE) return gpg_error (GPG_ERR_NO_DATA); /* not available */ if (err) return err; /* check that there is only one */ for (idx++; !(err=ksba_cert_get_extension (cert, idx, &oid, NULL, NULL, NULL)); idx++) { if (!strcmp (oid, oidstr_authorityKeyIdentifier)) return gpg_error (GPG_ERR_DUP_VALUE); } der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (ti.length > derlen) return gpg_error (GPG_ERR_BAD_BER); err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (ti.tag == 0) { /* keyIdentifier: Save it away and skip over it. */ keyid_der = der; keyid_derlen = ti.length; der += ti.length; derlen -= ti.length; /* If the keyid has been requested but no other data follows, we directly jump to the end. */ if (r_keyid && !derlen) goto build_keyid; if (!derlen) return gpg_error (GPG_ERR_NO_DATA); /* not available */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); } if (ti.tag != 1 || !derlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); err = _ksba_name_new_from_der (r_name, der, ti.length); if (err) return err; der += ti.length; derlen -= ti.length; /* fixme: we should release r_name before returning on error */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (ti.tag != 2 || !derlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); sprintf (numbuf,"(%u:", (unsigned int)ti.length); numbuflen = strlen (numbuf); *r_serial = xtrymalloc (numbuflen + ti.length + 2); if (!*r_serial) return gpg_error (GPG_ERR_ENOMEM); strcpy (*r_serial, numbuf); memcpy (*r_serial+numbuflen, der, ti.length); (*r_serial)[numbuflen + ti.length] = ')'; (*r_serial)[numbuflen + ti.length + 1] = 0; build_keyid: if (r_keyid && keyid_der && keyid_derlen) { sprintf (numbuf,"(%u:", (unsigned int)keyid_derlen); numbuflen = strlen (numbuf); *r_keyid = xtrymalloc (numbuflen + keyid_derlen + 2); if (!*r_keyid) return gpg_error (GPG_ERR_ENOMEM); strcpy (*r_keyid, numbuf); memcpy (*r_keyid+numbuflen, keyid_der, keyid_derlen); (*r_keyid)[numbuflen + keyid_derlen] = ')'; (*r_keyid)[numbuflen + keyid_derlen + 1] = 0; } return 0; } /* Return a simple octet string extension at the object identifier OID from certificate CERT. The data is return as a simple S-expression and stored at R_DATA. Returns 0 on success or an error code. common error codes are: GPG_ERR_NO_DATA if no such extension is available, GPG_ERR_DUP_VALUE if more than one is available. If R_CRIT is not NULL, the critical extension flag will be stored at that address. */ static gpg_error_t get_simple_octet_string_ext (ksba_cert_t cert, const char *oid, int *r_crit, ksba_sexp_t *r_data) { gpg_error_t err; const char *tmpoid; size_t off, derlen; const unsigned char *der; int idx, crit; struct tag_info ti; char numbuf[30]; size_t numbuflen; if (!r_data) return gpg_error (GPG_ERR_INV_VALUE); *r_data = NULL; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &tmpoid, &crit, &off, &derlen)); idx++) { if (!strcmp (tmpoid, oid)) break; } if (err) { if (gpg_err_code (err) == GPG_ERR_EOF || gpg_err_code (err) == GPG_ERR_NO_VALUE) return gpg_error (GPG_ERR_NO_DATA); return err; } /* Check that there is only one */ for (idx++; !(err=ksba_cert_get_extension (cert, idx, &tmpoid, NULL, NULL, NULL)); idx++) { if (!strcmp (tmpoid, oid)) return gpg_error (GPG_ERR_DUP_VALUE); } der = cert->image + off; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && !ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (ti.length > derlen) return gpg_error (GPG_ERR_BAD_BER); if (ti.length != derlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* Garbage follows. */ sprintf (numbuf,"(%u:", (unsigned int)ti.length); numbuflen = strlen (numbuf); *r_data = xtrymalloc (numbuflen + ti.length + 2); if (!*r_data) return gpg_error (GPG_ERR_ENOMEM); strcpy (*r_data, numbuf); memcpy (*r_data+numbuflen, der, ti.length); (*r_data)[numbuflen + ti.length] = ')'; (*r_data)[numbuflen + ti.length + 1] = 0; if (r_crit) *r_crit = crit; return 0; } /* Return the subjectKeyIdentifier extension as a simple allocated S-expression at the address of R_KEYID. 0 is returned on success, GPG_ERR_NO_DATA if no such extension is available or any other error code. If R_CRIT is not passed as NULL, the criticla flag of this is extension is stored there. */ gpg_error_t ksba_cert_get_subj_key_id (ksba_cert_t cert, int *r_crit, ksba_sexp_t *r_keyid) { return get_simple_octet_string_ext (cert, oidstr_subjectKeyIdentifier, r_crit, r_keyid); } /* MODE 0 := authorityInfoAccess 1 := subjectInfoAccess Caller must release METHOD and LOCATION if the function returned with success; on error both variables will point to NULL. */ static gpg_error_t get_info_access (ksba_cert_t cert, int idx, int mode, char **method, ksba_name_t *location) { gpg_error_t err; const char *oid; size_t off, derlen; int myidx, crit; *method = NULL; *location = NULL; if (!cert || !cert->initialized) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (myidx=0; !(err=ksba_cert_get_extension (cert, myidx, &oid, &crit, &off, &derlen)); myidx++) { if (!strcmp (oid,(mode == 0)? oidstr_authorityInfoAccess : oidstr_subjectInfoAccess) ) { const unsigned char *der; struct tag_info ti; size_t seqlen; der = cert->image + off; /* What we are going to parse is: * * AuthorityInfoAccessSyntax ::= * SEQUENCE SIZE (1..MAX) OF AccessDescription * * AccessDescription ::= SEQUENCE { * accessMethod OBJECT IDENTIFIER, * accessLocation GeneralName } */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); seqlen = ti.length; if (seqlen > derlen) return gpg_error (GPG_ERR_BAD_BER); /* Note: an empty sequence is actually not allowed but we better don't care. */ while (seqlen) { err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (seqlen < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); seqlen -= ti.nhdr; if (seqlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (idx) { /* Skip because we are not yet at the desired index. */ der += ti.length; derlen -= ti.length; seqlen -= ti.length; idx--; continue; } /* We only need the next object, thus we can (and actually need to) limit the DERLEN to the length of the current sequence. */ derlen = ti.length; if (!derlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID && !ti.is_constructed)) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); *method = ksba_oid_to_str (der, ti.length); if (!*method) return gpg_error (GPG_ERR_ENOMEM); der += ti.length; derlen -= ti.length; err = _ksba_name_new_from_der (location, der, derlen); if (err) { ksba_free (*method); *method = NULL; return err; } return 0; } } } return err; } /* Return the authorityInfoAccess attributes. IDX should be iterated starting from 0 until the function returns GPG_ERR_EOF. R_METHOD returns an allocated string with the OID of one item and R_LOCATION return the GeneralName for that OID. The return values for R_METHOD and R_LOCATION must be released by the caller unless the function returned an error; the function will however make sure that R_METHOD and R_LOCATION will point to NULL if the function returns an error. See RFC 2459, section 4.2.2.1 */ gpg_error_t ksba_cert_get_authority_info_access (ksba_cert_t cert, int idx, char **r_method, ksba_name_t *r_location) { if (!r_method || !r_location) return gpg_error (GPG_ERR_INV_VALUE); return get_info_access (cert, idx, 0, r_method, r_location); } /* Return the subjectInfoAccess attributes. IDX should be iterated starting from 0 until the function returns GPG_ERR_EOF. R_METHOD returns an allocated string with the OID of one item and R_LOCATION return the GeneralName for that OID. The return values for R_METHOD and R_LOCATION must be released by the caller unless the function returned an error; the function will however make sure that R_METHOD and R_LOCATION will point to NULL if the function returns an error. See RFC 2459, section 4.2.2.2 */ gpg_error_t ksba_cert_get_subject_info_access (ksba_cert_t cert, int idx, char **r_method, ksba_name_t *r_location) { if (!r_method || !r_location) return gpg_error (GPG_ERR_INV_VALUE); return get_info_access (cert, idx, 1, r_method, r_location); } diff --git a/src/name.c b/src/name.c index c734199..371fc41 100644 --- a/src/name.c +++ b/src/name.c @@ -1,275 +1,275 @@ /* name.c - Object to access GeneralNames etc. * Copyright (C) 2002, 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 . */ #include #include #include #include #include #include #include "util.h" #include "asn1-func.h" #include "convert.h" #include "ber-help.h" struct ksba_name_s { int ref_count; int n_names; /* number of names */ char **names; /* array with the parsed names */ }; /* Also this is a public function it is not yet usable becuase we don't have a way to set real information. */ gpg_error_t ksba_name_new (ksba_name_t *r_name) { *r_name = xtrycalloc (1, sizeof **r_name); if (!*r_name) return gpg_error_from_errno (errno); (*r_name)->ref_count++; return 0; } void ksba_name_ref (ksba_name_t name) { if (!name) fprintf (stderr, "BUG: ksba_name_ref for NULL\n"); else ++name->ref_count; } void ksba_name_release (ksba_name_t name) { int i; if (!name) return; if (name->ref_count < 1) { fprintf (stderr, "BUG: trying to release an already released name\n"); return; } if (--name->ref_count) return; for (i=0; i < name->n_names; i++) xfree (name->names[i]); xfree (name->names); name->n_names = 0; xfree (name); } /* This is an internal function to create an ksba_name_t object from an DER encoded image which must point to an GeneralNames object */ gpg_error_t _ksba_name_new_from_der (ksba_name_t *r_name, const unsigned char *image, size_t imagelen) { gpg_error_t err; ksba_name_t name; struct tag_info ti; const unsigned char *der; size_t derlen; int n; char *p; if (!r_name || !image) return gpg_error (GPG_ERR_INV_VALUE); *r_name = NULL; - /* count and check for encoding errors - we won;t do this again + /* Count and check for encoding errors - we won't do this again during the second pass */ der = image; derlen = imagelen; n = 0; while (derlen) { err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); switch (ti.tag) { case 1: /* rfc822Name - this is an imlicit IA5_STRING */ case 4: /* Name */ case 6: /* URI */ n++; break; default: break; } /* advance pointer */ der += ti.length; derlen -= ti.length; } /* allocate array and set all slots to NULL for easier error recovery */ err = ksba_name_new (&name); if (err) return err; if (!n) return 0; /* empty GeneralNames */ name->names = xtrycalloc (n, sizeof *name->names); if (!name->names) { ksba_name_release (name); return gpg_error (GPG_ERR_ENOMEM); } name->n_names = n; /* start the second pass */ der = image; derlen = imagelen; n = 0; while (derlen) { char numbuf[21]; err = _ksba_ber_parse_tl (&der, &derlen, &ti); assert (!err); switch (ti.tag) { case 1: /* rfc822Name - this is an imlicit IA5_STRING */ p = name->names[n] = xtrymalloc (ti.length+3); if (!p) { ksba_name_release (name); return gpg_error (GPG_ERR_ENOMEM); } *p++ = '<'; memcpy (p, der, ti.length); p += ti.length; *p++ = '>'; *p = 0; n++; break; case 4: /* Name */ err = _ksba_derdn_to_str (der, ti.length, &p); if (err) return err; /* FIXME: we need to release some of the memory */ name->names[n++] = p; break; case 6: /* URI */ sprintf (numbuf, "%u:", (unsigned int)ti.length); p = name->names[n] = xtrymalloc (1+5+strlen (numbuf) + ti.length +1+1); if (!p) { ksba_name_release (name); return gpg_error (GPG_ERR_ENOMEM); } p = stpcpy (p, "(3:uri"); p = stpcpy (p, numbuf); memcpy (p, der, ti.length); p += ti.length; *p++ = ')'; *p = 0; /* extra safeguard null */ n++; break; default: break; } /* advance pointer */ der += ti.length; derlen -= ti.length; } *r_name = name; return 0; } /* By iterating IDX up starting with 0, this function returns the all General Names stored in NAME. The format of the returned name is either a RFC-2253 formated one which can be detected by checking whether the first character is letter or a digit. RFC 2822 conform email addresses are returned enclosed in angle brackets, the opening angle bracket should be used to detect this. Other formats are returned as an S-Expression in canonical format, so a opening parenthesis may be used to detect this encoding, in this case the name may include binary null characters, so strlen might return a length shorter than actually used, the real length is implictly given by the structure of the S-Exp, an extra null is appended for safety reasons. One common format return is probably an Universal Resource Identifier which has the S-expression: "(uri )". The return string has the same lifetime as NAME. */ const char * ksba_name_enum (ksba_name_t name, int idx) { if (!name || idx < 0) return NULL; if (idx >= name->n_names) return NULL; /* end of list */ return name->names[idx]; } /* Convenience function to return names representing an URI. Caller must free the return value. Note that this function should not be used for enumeration */ char * ksba_name_get_uri (ksba_name_t name, int idx) { const char *s = ksba_name_enum (name, idx); int n; char *buf; if (!s || strncmp (s, "(3:uri", 6)) return NULL; /* we do only return URIs */ s += 6; for (n=0; *s && *s != ':' && digitp (s); s++) n = n*10 + atoi_1 (s); if (!n || *s != ':') return NULL; /* oops */ s++; buf = xtrymalloc (n+1); if (buf) { memcpy (buf, s, n); buf[n] = 0; } return buf; } diff --git a/src/ocsp.c b/src/ocsp.c index 85679bb..c053b18 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -1,1903 +1,1905 @@ /* 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 . */ #include #include #include #include #include #include #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 (ti.length > *len) + err = gpg_error (GPG_ERR_INV_BER); 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; }