Page MenuHome GnuPG

crl.c
No OneTemporary

/* crl.c - CRL parser
* Copyright (C) 2002, 2004 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 the GNU General Public License as published by
* the Free Software Fountion; either version 2 of the License, or
* (at your option) any later version.
*
* 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 copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "util.h"
#include "convert.h"
#include "keyinfo.h"
#include "der-encoder.h"
#include "ber-help.h"
#include "ber-decoder.h"
#include "crl.h"
static const char oidstr_crlNumber[] = "2.5.29.20";
static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35";
/* We better buffer the hashing. */
static inline void
do_hash (ksba_crl_t crl, const void *buffer, size_t length)
{
while (length)
{
size_t n = length;
if (crl->hashbuf.used + n > sizeof crl->hashbuf.buffer)
n = sizeof crl->hashbuf.buffer - crl->hashbuf.used;
memcpy (crl->hashbuf.buffer+crl->hashbuf.used, buffer, n);
crl->hashbuf.used += n;
if (crl->hashbuf.used == sizeof crl->hashbuf.buffer)
{
if (crl->hash_fnc)
crl->hash_fnc (crl->hash_fnc_arg,
crl->hashbuf.buffer, crl->hashbuf.used);
crl->hashbuf.used = 0;
}
buffer += n;
length -= n;
}
}
#define HASH(a,b) do_hash (crl, (a), (b))
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_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;
}
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;
}
/**
* ksba_crl_new:
*
* Create a new and empty CRL object
*
* Return value: A CRL object or an error code.
**/
gpg_error_t
ksba_crl_new (ksba_crl_t *r_crl)
{
*r_crl = xtrycalloc (1, sizeof **r_crl);
if (!*r_crl)
return gpg_error_from_errno (errno);
return 0;
}
/**
* ksba_crl_release:
* @crl: A CRL object
*
* Release a CRL object.
**/
void
ksba_crl_release (ksba_crl_t crl)
{
if (!crl)
return;
xfree (crl->algo.oid);
xfree (crl->algo.parm);
_ksba_asn_release_nodes (crl->issuer.root);
xfree (crl->issuer.image);
xfree (crl->item.serial);
xfree (crl->sigval);
while (crl->extension_list)
{
crl_extn_t tmp = crl->extension_list->next;
xfree (crl->extension_list->oid);
xfree (crl->extension_list);
crl->extension_list = tmp;
}
xfree (crl);
}
gpg_error_t
ksba_crl_set_reader (ksba_crl_t crl, ksba_reader_t r)
{
if (!crl || !r)
return gpg_error (GPG_ERR_INV_VALUE);
crl->reader = r;
return 0;
}
/* Provide a hash function so that we are able to hash the data */
void
ksba_crl_set_hash_function (ksba_crl_t crl,
void (*hash_fnc)(void *, const void *, size_t),
void *hash_fnc_arg)
{
if (crl)
{
crl->hash_fnc = hash_fnc;
crl->hash_fnc_arg = hash_fnc_arg;
}
}
/*
access functions
*/
/**
* ksba_crl_get_digest_algo:
* @cms: CMS object
*
* Figure out the the digest algorithm used for the signature and return
* its OID.
*
* Return value: NULL if the signature algorithm is not yet available
* or there is a mismatched between "tbsCertList.signature" and
* "signatureAlgorithm"; on success the OID is returned which is valid
* as long as the CRL object is valid.
**/
const char *
ksba_crl_get_digest_algo (ksba_crl_t crl)
{
if (!crl)
return NULL;
/* fixme: implement the described check */
return crl->algo.oid;
}
/**
* ksba_crl_get_issuer:
* @cms: CMS object
* @r_issuer: returns the issuer
*
* This functions returns the issuer of the CRL. The caller must
* release the returned object.
*
* Return value: 0 on success or an error code
**/
gpg_error_t
ksba_crl_get_issuer (ksba_crl_t crl, char **r_issuer)
{
gpg_error_t err;
AsnNode n;
const unsigned char *image;
if (!crl || !r_issuer)
return gpg_error (GPG_ERR_INV_VALUE);
if (!crl->issuer.root)
return gpg_error (GPG_ERR_NO_DATA);
n = crl->issuer.root;
image = crl->issuer.image;
if (!n || !n->down)
return gpg_error (GPG_ERR_NO_VALUE);
n = n->down; /* dereference the choice node */
if (n->off == -1)
{
/* fputs ("get_issuer problem at node:\n", stderr); */
/* _ksba_asn_node_dump_all (n, stderr); */
return gpg_error (GPG_ERR_GENERAL);
}
err = _ksba_dn_to_str (image, n, r_issuer);
return err;
}
/* Return the CRL extension in OID, CRITICAL, DER and DERLEN. The
caller should iterate IDX from 0 upwards until GPG_ERR_EOF is
returned. Note, that the returned values are valid as long as the
context is valid and no new parsing has been started. */
gpg_error_t
ksba_crl_get_extension (ksba_crl_t crl, int idx,
char const **oid, int *critical,
unsigned char const **der, size_t *derlen)
{
crl_extn_t e;
if (!crl)
return gpg_error (GPG_ERR_INV_VALUE);
if (idx < 0)
return gpg_error (GPG_ERR_INV_INDEX);
for (e=crl->extension_list; e && idx; e = e->next, idx-- )
;
if (!e)
return gpg_error (GPG_ERR_EOF);
if (oid)
*oid = e->oid;
if (critical)
*critical = e->critical;
if (der)
*der = e->der;
if (derlen)
*derlen = e->derlen;
return 0;
}
/* Return the authorityKeyIdentifier in r_name and r_serial or in
r_keyID. Note that r_keyID is not yet supported and must be passed
as NULL. GPG_ERR_NO_DATA is returned if no authorityKeyIdentifier
or only one using the keyIdentifier method is available.
FIXME: This function shares a lot of code with the one in cert.c
*/
gpg_error_t
ksba_crl_get_auth_key_id (ksba_crl_t crl,
ksba_sexp_t *r_keyid,
ksba_name_t *r_name,
ksba_sexp_t *r_serial)
{
gpg_error_t err;
size_t derlen;
const unsigned char *der;
struct tag_info ti;
char numbuf[30];
size_t numbuflen;
crl_extn_t e;
if (r_keyid)
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
if (!crl || !r_name || !r_serial)
return gpg_error (GPG_ERR_INV_VALUE);
*r_name = NULL;
*r_serial = NULL;
for (e=crl->extension_list; e; e = e->next)
if (!strcmp (e->oid, oidstr_authorityKeyIdentifier))
break;
if (!e)
return gpg_error (GPG_ERR_NO_DATA); /* not available */
/* Check that there is only one */
{
crl_extn_t e2;
for (e2 = e->next; e2; e2 = e2->next)
if (!strcmp (e2->oid, oidstr_authorityKeyIdentifier))
return gpg_error (GPG_ERR_DUP_VALUE);
}
der = e->der;
derlen = e->derlen;
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_CRL_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_CRL_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)
{ /* We do not support the keyIdentifier method yet, but we need
to skip it. */
der += ti.length;
derlen -= ti.length;
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_CRL_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_CRL_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_CRL_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_CRL_OBJ);
sprintf (numbuf,"(%u:", (unsigned int)ti.length);
numbuflen = strlen (numbuf);
*r_serial = xtrymalloc (numbuflen + ti.length + 2);
if (!*r_serial)
return gpg_error_from_errno (errno);
strcpy (*r_serial, numbuf);
memcpy (*r_serial+numbuflen, der, ti.length);
(*r_serial)[numbuflen + ti.length] = ')';
(*r_serial)[numbuflen + ti.length + 1] = 0;
return 0;
}
/* Return the optional crlNumber in NUMBER or GPG_ERR_NO_DATA if it is
not available. Caller must release NUMBER if the fuction retruned
with success. */
gpg_error_t
ksba_crl_get_crl_number (ksba_crl_t crl, ksba_sexp_t *number)
{
gpg_error_t err;
size_t derlen;
const unsigned char *der;
struct tag_info ti;
char numbuf[30];
size_t numbuflen;
crl_extn_t e;
if (!crl || !number)
return gpg_error (GPG_ERR_INV_VALUE);
*number = NULL;
for (e=crl->extension_list; e; e = e->next)
if (!strcmp (e->oid, oidstr_crlNumber))
break;
if (!e)
return gpg_error (GPG_ERR_NO_DATA); /* not available */
/* Check that there is only one. */
{
crl_extn_t e2;
for (e2 = e->next; e2; e2 = e2->next)
if (!strcmp (e2->oid, oidstr_crlNumber))
return gpg_error (GPG_ERR_DUP_VALUE);
}
der = e->der;
derlen = e->derlen;
err = parse_integer (&der, &derlen, &ti);
if (err)
return err;
sprintf (numbuf,"(%u:", (unsigned int)ti.length);
numbuflen = strlen (numbuf);
*number = xtrymalloc (numbuflen + ti.length + 2);
if (!*number)
return gpg_error_from_errno (errno);
strcpy (*number, numbuf);
memcpy (*number+numbuflen, der, ti.length);
(*number)[numbuflen + ti.length] = ')';
(*number)[numbuflen + ti.length + 1] = 0;
return 0;
}
/**
* ksba_crl_get_update_times:
* @crl: CRL object
* @this: Returns the thisUpdate value
* @next: Returns the nextUpdate value.
*
* THIS and NEXT may be given as NULL if the value is not required.
* Return value: 0 on success or an error code
**/
gpg_error_t
ksba_crl_get_update_times (ksba_crl_t crl,
ksba_isotime_t this,
ksba_isotime_t next)
{
if (this)
*this = 0;
if (next)
*next = 0;
if (!crl)
return gpg_error (GPG_ERR_INV_VALUE);
if (!*crl->this_update || !*crl->next_update)
return gpg_error (GPG_ERR_INV_TIME);
if (this)
_ksba_copy_time (this, crl->this_update);
if (next)
_ksba_copy_time (next, crl->next_update);
return 0;
}
/**
* ksba_crl_get_item:
* @crl: CRL object
* @r_serial: Returns a S-exp with the serial number; caller must free.
* @r_revocation_date: Returns the recocation date
* @r_reason: Return the reason for revocation
*
* Return the serial number, revocation time and reason of the current
* item. Any of these arguments may be passed as %NULL if the value
* is not of interest. This function should be used after the parse
* function came back with %KSBA_SR_GOT_ITEM. For efficiency reasons
* the function should be called only once, the implementation may
* return an error for the second call.
*
* Return value: 0 in success or an error code.
**/
gpg_error_t
ksba_crl_get_item (ksba_crl_t crl, ksba_sexp_t *r_serial,
ksba_isotime_t r_revocation_date,
ksba_crl_reason_t *r_reason)
{
if (r_revocation_date)
*r_revocation_date = 0;
if (!crl)
return gpg_error (GPG_ERR_INV_VALUE);
if (r_serial)
{
if (!crl->item.serial)
return gpg_error (GPG_ERR_NO_DATA);
*r_serial = crl->item.serial;
crl->item.serial = NULL;
}
if (r_revocation_date)
_ksba_copy_time (r_revocation_date, crl->item.revocation_date);
if (r_reason)
*r_reason = crl->item.reason;
return 0;
}
/**
* ksba_crl_get_sig_val:
* @crl: CRL object
*
* 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.
*
* Return value: NULL or a string with an S-Exp.
**/
ksba_sexp_t
ksba_crl_get_sig_val (ksba_crl_t crl)
{
ksba_sexp_t p;
if (!crl)
return NULL;
if (!crl->sigval)
return NULL;
p = crl->sigval;
crl->sigval = NULL;
return p;
}
/*
Parser functions
*/
/* read one byte */
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;
}
/* read COUNT bytes into buffer. Return 0 on success */
static int
read_buffer (ksba_reader_t reader, char *buffer, size_t count)
{
size_t nread;
while (count)
{
if (ksba_reader_read (reader, buffer, count, &nread))
return -1;
buffer += nread;
count -= nread;
}
return 0;
}
/* Create a new decoder and run it for the given element */
/* Fixme: this code is duplicated from cms-parser.c */
static gpg_error_t
create_and_run_decoder (ksba_reader_t reader, const char *elem_name,
AsnNode *r_root,
unsigned char **r_image, size_t *r_imagelen)
{
gpg_error_t err;
ksba_asn_tree_t crl_tree;
BerDecoder decoder;
err = ksba_asn_create_tree ("tmttv2", &crl_tree);
if (err)
return err;
decoder = _ksba_ber_decoder_new ();
if (!decoder)
{
ksba_asn_tree_release (crl_tree);
return gpg_error (GPG_ERR_ENOMEM);
}
err = _ksba_ber_decoder_set_reader (decoder, reader);
if (err)
{
ksba_asn_tree_release (crl_tree);
_ksba_ber_decoder_release (decoder);
return err;
}
err = _ksba_ber_decoder_set_module (decoder, crl_tree);
if (err)
{
ksba_asn_tree_release (crl_tree);
_ksba_ber_decoder_release (decoder);
return err;
}
err = _ksba_ber_decoder_decode (decoder, elem_name,
r_root, r_image, r_imagelen);
_ksba_ber_decoder_release (decoder);
ksba_asn_tree_release (crl_tree);
return err;
}
/* Parse the extension in the buffer DER or length DERLEN and return
the result in OID, CRITICAL, OFF and LEN. */
static gpg_error_t
parse_one_extension (const unsigned char *der, size_t derlen,
char **oid, int *critical, size_t *off, size_t *len)
{
gpg_error_t err;
struct tag_info ti;
const unsigned char *start = der;
*oid = NULL;
*critical = 0;
*off = *len = 0;
/*
Extension ::= SEQUENCE {
extnID OBJECT IDENTIFIER,
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING }
*/
err = parse_sequence (&der, &derlen, &ti);
if (err)
goto failure;
err = parse_object_id_into_str (&der, &derlen, oid);
if (err)
goto failure;
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
goto failure;
if (ti.length > derlen)
return gpg_error (GPG_ERR_BAD_BER);
if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BOOLEAN
&& !ti.is_constructed)
{
if (ti.length != 1)
goto bad_ber;
*critical = !!*der;
parse_skip (&der, &derlen, &ti);
}
else
{ /* Undo that read. */
der -= ti.nhdr;
derlen += ti.nhdr;
}
err = parse_octet_string (&der, &derlen, &ti);
if (err)
goto failure;
*off = der - start;
*len = ti.length;
return 0;
bad_ber:
err = gpg_error (GPG_ERR_BAD_BER);
failure:
xfree (*oid);
*oid = NULL;
return err;
}
/* Store an extension into the context. */
static gpg_error_t
store_one_extension (ksba_crl_t crl, const unsigned char *der, size_t derlen)
{
gpg_error_t err;
char *oid;
int critical;
size_t off, len;
crl_extn_t e;
err = parse_one_extension (der, derlen, &oid, &critical, &off, &len);
if (err)
return err;
e = xtrymalloc (sizeof *e + len - 1);
if (!e)
{
err = gpg_error_from_errno (errno);
xfree (oid);
return err;
}
e->oid = oid;
e->critical = critical;
e->derlen = len;
memcpy (e->der, der + off, len);
e->next = crl->extension_list;
crl->extension_list = e;
return 0;
}
/* Parse the fixed block at the beginning. We use a custom parser
here because our BER decoder is not yet able to stop at certain
points */
static gpg_error_t
parse_to_next_update (ksba_crl_t crl)
{
gpg_error_t err;
struct tag_info ti;
unsigned long outer_len, tbs_len;
int outer_ndef, tbs_ndef;
int c;
unsigned char tmpbuf[500]; /* for OID or algorithmIdentifier */
size_t nread;
/* read the outer sequence */
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
outer_len = ti.length;
outer_ndef = ti.ndef;
if (!outer_ndef && outer_len < 10)
return gpg_error (GPG_ERR_TOO_SHORT);
/* read the tbs sequence */
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
HASH (ti.buf, ti.nhdr);
if (!outer_ndef)
{
if (outer_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* Triplet header larger
than outer sequence */
outer_len -= ti.nhdr;
if (!ti.ndef && outer_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* Triplet larger than
outer sequence */
outer_len -= ti.length;
}
tbs_len = ti.length;
tbs_ndef = ti.ndef;
if (!tbs_ndef && tbs_len < 10)
return gpg_error (GPG_ERR_TOO_SHORT);
/* read the optional version integer */
crl->crl_version = -1;
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
if ( ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER)
{
if ( ti.is_constructed || !ti.length )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
HASH (ti.buf, ti.nhdr);
if (!tbs_ndef)
{
if (tbs_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
tbs_len -= ti.nhdr;
if (tbs_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
tbs_len -= ti.length;
}
/* fixme: we should also check the outer data length here and in
the follwing code. It might however be easier to to thsi at
the end of this sequence */
if (ti.length != 1)
return gpg_error (GPG_ERR_UNSUPPORTED_CRL_VERSION);
if ( (c=read_byte (crl->reader)) == -1)
{
err = ksba_reader_error (crl->reader);
return err? err : gpg_error (GPG_ERR_GENERAL);
}
if ( !(c == 0 || c == 1) )
return gpg_error (GPG_ERR_UNSUPPORTED_CRL_VERSION);
{
unsigned char tmp = c;
HASH (&tmp, 1);
}
crl->crl_version = c;
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
}
/* read the algorithm identifier */
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
if (!tbs_ndef)
{
if (tbs_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
tbs_len -= ti.nhdr;
if (!ti.ndef && tbs_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
tbs_len -= ti.length;
}
if (ti.nhdr + ti.length >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
memcpy (tmpbuf, ti.buf, ti.nhdr);
err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length);
if (err)
return err;
HASH (tmpbuf, ti.nhdr+ti.length);
xfree (crl->algo.oid); crl->algo.oid = NULL;
xfree (crl->algo.parm); crl->algo.parm = NULL;
err = _ksba_parse_algorithm_identifier2 (tmpbuf, ti.nhdr+ti.length, &nread,
&crl->algo.oid,
&crl->algo.parm,
&crl->algo.parmlen);
if (err)
return err;
assert (nread <= ti.nhdr + ti.length);
if (nread < ti.nhdr + ti.length)
return gpg_error (GPG_ERR_TOO_SHORT);
/* read the name */
{
unsigned long n = ksba_reader_tell (crl->reader);
err = create_and_run_decoder (crl->reader,
"TMTTv2.CertificateList.tbsCertList.issuer",
&crl->issuer.root,
&crl->issuer.image,
&crl->issuer.imagelen);
if (err)
return err;
/* imagelen might be larger than the valid data (due to read ahead).
So we need to get the count from the reader */
n = ksba_reader_tell (crl->reader) - n;
if (n > crl->issuer.imagelen)
return gpg_error (GPG_ERR_BUG);
HASH (crl->issuer.image, n);
if (!tbs_ndef)
{
if (tbs_len < n)
return gpg_error (GPG_ERR_BAD_BER);
tbs_len -= n;
}
}
/* read the thisUpdate time */
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL
&& (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME)
&& !ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
if (!tbs_ndef)
{
if (tbs_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
tbs_len -= ti.nhdr;
if (!ti.ndef && tbs_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
tbs_len -= ti.length;
}
if (ti.nhdr + ti.length >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
memcpy (tmpbuf, ti.buf, ti.nhdr);
err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length);
if (err)
return err;
HASH (tmpbuf, ti.nhdr+ti.length);
_ksba_asntime_to_iso (tmpbuf+ti.nhdr, ti.length, crl->this_update);
/* read the optional nextUpdate time */
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
if ( ti.class == CLASS_UNIVERSAL
&& (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME)
&& !ti.is_constructed )
{
if (!tbs_ndef)
{
if (tbs_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
tbs_len -= ti.nhdr;
if (!ti.ndef && tbs_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
tbs_len -= ti.length;
}
if (ti.nhdr + ti.length >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
memcpy (tmpbuf, ti.buf, ti.nhdr);
err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length);
if (err)
return err;
HASH (tmpbuf, ti.nhdr+ti.length);
_ksba_asntime_to_iso (tmpbuf+ti.nhdr, ti.length, crl->next_update);
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
}
/* read the first sequence tag of the optional SEQ of SEQ */
if (tbs_ndef || tbs_len)
{
if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed )
{ /* yes, there is one */
HASH (ti.buf, ti.nhdr);
if (!tbs_ndef)
{
if (tbs_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
tbs_len -= ti.nhdr;
if (!ti.ndef && tbs_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
tbs_len -= ti.length;
}
crl->state.have_seqseq = 1;
crl->state.seqseq_ndef = ti.ndef;
crl->state.seqseq_len = ti.length;
/* and read the next */
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
}
}
/* we need to save some stuff for the next round */
crl->state.ti = ti;
crl->state.outer_ndef = outer_ndef;
crl->state.outer_len = outer_len;
crl->state.tbs_ndef = tbs_ndef;
crl->state.tbs_len = tbs_len;
return 0;
}
/* Parse the revokedCertificates SEQUENCE of SEQUENCE using a custom
parser for efficiency and return after each entry */
static gpg_error_t
parse_crl_entry (ksba_crl_t crl, int *got_entry)
{
gpg_error_t err;
struct tag_info ti = crl->state.ti;
unsigned long seqseq_len= crl->state.seqseq_len;
int seqseq_ndef = crl->state.seqseq_ndef;
unsigned long len;
int ndef;
unsigned char tmpbuf[4096]; /* for time, serial number and extensions */
char numbuf[22];
int numbuflen;
/* Check the length to see whether we are at the end of the seq but do
this only when we know that we have this optional seq of seq. */
if (!crl->state.have_seqseq)
return 0; /* ready (no entries at all) */
if (!seqseq_ndef && !seqseq_len)
return 0; /* ready */
/* if this is not a SEQUENCE the CRL is invalid */
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
HASH (ti.buf, ti.nhdr);
if (!seqseq_ndef)
{
if (seqseq_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
seqseq_len -= ti.nhdr;
if (!ti.ndef && seqseq_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
seqseq_len -= ti.length;
}
ndef = ti.ndef;
len = ti.length;
/* get the serial number */
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER
&& !ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
if (!ndef)
{
if (len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
len -= ti.nhdr;
if (!ti.ndef && len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
len -= ti.length;
}
if (ti.nhdr + ti.length >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
memcpy (tmpbuf, ti.buf, ti.nhdr);
err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length);
if (err)
return err;
HASH (tmpbuf, ti.nhdr+ti.length);
xfree (crl->item.serial);
sprintf (numbuf,"(%u:", (unsigned int)ti.length);
numbuflen = strlen (numbuf);
crl->item.serial = xtrymalloc (numbuflen + ti.length + 2);
if (!crl->item.serial)
return gpg_error (GPG_ERR_ENOMEM);
strcpy (crl->item.serial, numbuf);
memcpy (crl->item.serial+numbuflen, tmpbuf+ti.nhdr, ti.length);
crl->item.serial[numbuflen + ti.length] = ')';
crl->item.serial[numbuflen + ti.length + 1] = 0;
/* get the revocation time */
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL
&& (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME)
&& !ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
if (!ndef)
{
if (len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
len -= ti.nhdr;
if (!ti.ndef && len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
len -= ti.length;
}
if (ti.nhdr + ti.length >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
memcpy (tmpbuf, ti.buf, ti.nhdr);
err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length);
if (err)
return err;
HASH (tmpbuf, ti.nhdr+ti.length);
_ksba_asntime_to_iso (tmpbuf+ti.nhdr, ti.length,
crl->item.revocation_date);
/* if there is still space we must parse the optional entryExtensions */
if (ndef)
return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
else if (len)
{
/* read the outer sequence */
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL
&& ti.tag == TYPE_SEQUENCE && ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
HASH (ti.buf, ti.nhdr);
if (len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
len -= ti.nhdr;
if (len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
/* now loop over the extensions */
while (len)
{
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL
&& ti.tag == TYPE_SEQUENCE && ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
if (len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
len -= ti.nhdr;
if (len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
len -= ti.length;
if (ti.nhdr + ti.length >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
memcpy (tmpbuf, ti.buf, ti.nhdr);
err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length);
if (err)
return err;
HASH (tmpbuf, ti.nhdr+ti.length);
/* fixme: handle extension */
}
}
/* read ahead */
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
*got_entry = 1;
/* Fixme: the seqseq length is not correct if any element was ndef'd */
crl->state.ti = ti;
crl->state.seqseq_ndef = seqseq_ndef;
crl->state.seqseq_len = seqseq_len;
return 0;
}
/* This function is used when a [0] tag was encountered to read the
crlExtensions */
static gpg_error_t
parse_crl_extensions (ksba_crl_t crl)
{
gpg_error_t err;
struct tag_info ti = crl->state.ti;
unsigned long ext_len, len;
unsigned char tmpbuf[4096]; /* for extensions */
/* if we do not have a tag [0] we are done with this */
if (!(ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed))
return 0;
if (ti.ndef)
return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
HASH (ti.buf, ti.nhdr);
ext_len = ti.length;
/* read the outer sequence */
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL
&& ti.tag == TYPE_SEQUENCE && ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
HASH (ti.buf, ti.nhdr);
if (ext_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
ext_len -= ti.nhdr;
if (ext_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
len = ti.length;
/* now loop over the extensions */
while (len)
{
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL
&& ti.tag == TYPE_SEQUENCE && ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
if (len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
len -= ti.nhdr;
if (len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
len -= ti.length;
if (ti.nhdr + ti.length >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
/* fixme use a larger buffer if the extension does not fit into tmpbuf */
memcpy (tmpbuf, ti.buf, ti.nhdr);
err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length);
if (err)
return err;
HASH (tmpbuf, ti.nhdr+ti.length);
err = store_one_extension (crl, tmpbuf, ti.nhdr+ti.length);
if (err)
return err;
}
/* read ahead */
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
crl->state.ti = ti;
return 0;
}
/* Parse the signatureAlgorithm and the signature */
static gpg_error_t
parse_signature (ksba_crl_t crl)
{
gpg_error_t err;
struct tag_info ti = crl->state.ti;
unsigned char tmpbuf[2048]; /* for the sig algo and bitstr */
size_t n, n2;
/* We do read the stuff into a temporary buffer so that we can apply
our parsing function for this structure */
/* read the algorithmIdentifier sequence */
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
n = ti.nhdr + ti.length;
if (n >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
memcpy (tmpbuf, ti.buf, ti.nhdr);
err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length);
if (err)
return err;
/* and append the bit string */
err = _ksba_ber_read_tl (crl->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BIT_STRING
&& !ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CRL_OBJ);
n2 = ti.nhdr + ti.length;
if (n + n2 >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
memcpy (tmpbuf+n, ti.buf, ti.nhdr);
err = read_buffer (crl->reader, tmpbuf+n+ti.nhdr, ti.length);
if (err)
return err;
/* now parse it */
xfree (crl->sigval); crl->sigval = NULL;
return _ksba_sigval_to_sexp (tmpbuf, n + n2, &crl->sigval);
}
/* The actual parser which should be used with a new CRL object and
run in a loop until the the KSBA_SR_READY is encountered */
gpg_error_t
ksba_crl_parse (ksba_crl_t crl, ksba_stop_reason_t *r_stopreason)
{
enum {
sSTART,
sCRLENTRY,
sCRLEXT,
sERROR
} state = sERROR;
ksba_stop_reason_t stop_reason;
gpg_error_t err = 0;
int got_entry = 0;
if (!crl || !r_stopreason)
return gpg_error (GPG_ERR_INV_VALUE);
if (!crl->any_parse_done)
{ /* first time initialization of the stop reason */
*r_stopreason = 0;
crl->any_parse_done = 1;
}
/* Calculate state from last reason */
stop_reason = *r_stopreason;
*r_stopreason = KSBA_SR_RUNNING;
switch (stop_reason)
{
case 0:
state = sSTART;
break;
case KSBA_SR_BEGIN_ITEMS:
case KSBA_SR_GOT_ITEM:
state = sCRLENTRY;
break;
case KSBA_SR_END_ITEMS:
state = sCRLEXT;
break;
case KSBA_SR_RUNNING:
err = gpg_error (GPG_ERR_INV_STATE);
break;
default:
err = gpg_error (GPG_ERR_BUG);
break;
}
if (err)
return err;
/* Do the action */
switch (state)
{
case sSTART:
err = parse_to_next_update (crl);
break;
case sCRLENTRY:
err = parse_crl_entry (crl, &got_entry);
break;
case sCRLEXT:
err = parse_crl_extensions (crl);
if (!err)
{
if (crl->hash_fnc && crl->hashbuf.used)
crl->hash_fnc (crl->hash_fnc_arg,
crl->hashbuf.buffer, crl->hashbuf.used);
crl->hashbuf.used = 0;
err = parse_signature (crl);
}
break;
default:
err = gpg_error (GPG_ERR_INV_STATE);
break;
}
if (err)
return err;
/* Calculate new stop reason */
switch (state)
{
case sSTART:
stop_reason = KSBA_SR_BEGIN_ITEMS;
break;
case sCRLENTRY:
stop_reason = got_entry? KSBA_SR_GOT_ITEM : KSBA_SR_END_ITEMS;
break;
case sCRLEXT:
stop_reason = KSBA_SR_READY;
break;
default:
break;
}
*r_stopreason = stop_reason;
return 0;
}

File Metadata

Mime Type
text/x-c
Expires
Mon, Dec 23, 1:14 PM (9 m, 28 s)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
da/dc/20be3b9640b315be24e7949a5eb4

Event Timeline