Files
ipxe/src/crypto/asn1.c
T
Michael Brown 309ac8fd21 [crypto] Do not return an error when skipping the final ASN.1 object
Successfully reaching the end of a well-formed ASN.1 object list is
arguably not an error, but the current code (dating back to the
original ASN.1 commit in 2007) will explicitly check for and report
this as an error condition.

Remove the explicit check for reaching the end of a well-formed ASN.1
object list, and instead return success along with a zero-length (and
hence implicitly invalidated) cursor.

Almost every existing caller of asn1_skip() or asn1_skip_if_exists()
currently ignores the return value anyway.  Skipped objects are (by
definition) not of interest to the caller, and the invalidation
behaviour of asn1_skip() ensures that any errors will be safely caught
on a subsequent attempt to actually use the ASN.1 object content.
Since these existing callers ignore the return value, they cannot be
affected by this change.

There is one existing caller of asn1_skip_if_exists() that does check
the return value: in asn1_skip() itself, an error returned from
asn1_skip_if_exists() will cause the cursor to be invalidated.  In the
case of an error indicating only that the cursor length is already
zero, invalidation is a no-op, and so this change affects only the
return value propagated from asn1_skip().

This leaves only a single call site within ocsp_request() where the
return value from asn1_skip() is currently checked.  The return status
here is moot since there is no way for the code in question to fail
(absent a bug in the ASN.1 construction or parsing code).

There are therefore no callers of asn1_skip() or asn1_skip_if_exists()
that rely on an error being returned for successfully reaching the end
of a well-formed ASN.1 object list.  Simplify the code by redefining
this as a successful outcome.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-07 13:06:23 +01:00

904 lines
23 KiB
C

/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program 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 Foundation; either version 2 of the
* License, or any later version.
*
* This program 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <time.h>
#include <ipxe/tables.h>
#include <ipxe/image.h>
#include <ipxe/asn1.h>
/** @file
*
* ASN.1 encoding
*
*/
/* Disambiguate the various error causes */
#define EINVAL_ASN1_EMPTY \
__einfo_error ( EINFO_EINVAL_ASN1_EMPTY )
#define EINFO_EINVAL_ASN1_EMPTY \
__einfo_uniqify ( EINFO_EINVAL, 0x01, "Empty or underlength cursor" )
#define EINVAL_ASN1_LEN_LEN \
__einfo_error ( EINFO_EINVAL_ASN1_LEN_LEN )
#define EINFO_EINVAL_ASN1_LEN_LEN \
__einfo_uniqify ( EINFO_EINVAL, 0x02, "Length field overruns cursor" )
#define EINVAL_ASN1_LEN \
__einfo_error ( EINFO_EINVAL_ASN1_LEN )
#define EINFO_EINVAL_ASN1_LEN \
__einfo_uniqify ( EINFO_EINVAL, 0x03, "Field overruns cursor" )
#define EINVAL_ASN1_BOOLEAN \
__einfo_error ( EINFO_EINVAL_ASN1_BOOLEAN )
#define EINFO_EINVAL_ASN1_BOOLEAN \
__einfo_uniqify ( EINFO_EINVAL, 0x04, "Invalid boolean" )
#define EINVAL_ASN1_INTEGER \
__einfo_error ( EINFO_EINVAL_ASN1_INTEGER )
#define EINFO_EINVAL_ASN1_INTEGER \
__einfo_uniqify ( EINFO_EINVAL, 0x04, "Invalid integer" )
#define EINVAL_ASN1_TIME \
__einfo_error ( EINFO_EINVAL_ASN1_TIME )
#define EINFO_EINVAL_ASN1_TIME \
__einfo_uniqify ( EINFO_EINVAL, 0x05, "Invalid time" )
#define EINVAL_ASN1_ALGORITHM \
__einfo_error ( EINFO_EINVAL_ASN1_ALGORITHM )
#define EINFO_EINVAL_ASN1_ALGORITHM \
__einfo_uniqify ( EINFO_EINVAL, 0x06, "Invalid algorithm" )
#define EINVAL_BIT_STRING \
__einfo_error ( EINFO_EINVAL_BIT_STRING )
#define EINFO_EINVAL_BIT_STRING \
__einfo_uniqify ( EINFO_EINVAL, 0x07, "Invalid bit string" )
#define ENOTSUP_ALGORITHM \
__einfo_error ( EINFO_ENOTSUP_ALGORITHM )
#define EINFO_ENOTSUP_ALGORITHM \
__einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unsupported algorithm" )
#define ENOTTY_ALGORITHM \
__einfo_error ( EINFO_ENOTTY_ALGORITHM )
#define EINFO_ENOTTY_ALGORITHM \
__einfo_uniqify ( EINFO_ENOTTY, 0x01, "Inappropriate algorithm" )
/**
* Start parsing ASN.1 object
*
* @v cursor ASN.1 object cursor
* @v type Expected type, or ASN1_ANY
* @v extra Additional length not present within partial cursor
* @ret len Length of object body, or negative error
*
* The object cursor will be updated to point to the start of the
* object body (i.e. the first byte following the length byte(s)), and
* the length of the object body (i.e. the number of bytes until the
* following object tag, if any) is returned.
*/
int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) {
unsigned int len_len;
unsigned int len;
/* Sanity check */
if ( cursor->len < 2 /* Tag byte and first length byte */ ) {
if ( cursor->len )
DBGC ( cursor, "ASN1 %p too short\n", cursor );
return -EINVAL_ASN1_EMPTY;
}
/* Check the tag byte */
if ( ( type != ASN1_ANY ) && ( type != asn1_type ( cursor ) ) ) {
DBGC ( cursor, "ASN1 %p type mismatch (expected %d, got %d)\n",
cursor, type, *( ( uint8_t * ) cursor->data ) );
return -ENXIO;
}
cursor->data++;
cursor->len--;
/* Extract length of the length field and sanity check */
len_len = *( ( uint8_t * ) cursor->data );
if ( len_len & 0x80 ) {
len_len = ( len_len & 0x7f );
cursor->data++;
cursor->len--;
} else {
len_len = 1;
}
if ( cursor->len < len_len ) {
DBGC ( cursor, "ASN1 %p bad length field length %d (max "
"%zd)\n", cursor, len_len, cursor->len );
return -EINVAL_ASN1_LEN_LEN;
}
/* Extract the length and sanity check */
for ( len = 0 ; len_len ; len_len-- ) {
len <<= 8;
len |= *( ( uint8_t * ) cursor->data );
cursor->data++;
cursor->len--;
}
if ( ( cursor->len + extra ) < len ) {
DBGC ( cursor, "ASN1 %p bad length %d (max %zd)\n",
cursor, len, ( cursor->len + extra ) );
return -EINVAL_ASN1_LEN;
}
return len;
}
/**
* Enter ASN.1 object
*
* @v cursor ASN.1 object cursor
* @v type Expected type, or ASN1_ANY
* @ret rc Return status code
*
* The object cursor will be updated to point to the body of the
* current ASN.1 object. If any error occurs, the object cursor will
* be invalidated.
*/
int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) {
int len;
len = asn1_start ( cursor, type, 0 );
if ( len < 0 ) {
asn1_invalidate_cursor ( cursor );
return len;
}
cursor->len = len;
DBGC ( cursor, "ASN1 %p entered object type %02x (len %x)\n",
cursor, type, len );
return 0;
}
/**
* Skip ASN.1 object if present
*
* @v cursor ASN.1 object cursor
* @v type Expected type, or ASN1_ANY
* @ret rc Return status code
*
* The object cursor will be updated to point to the next ASN.1
* object. If any error occurs, the object cursor will not be
* modified.
*/
int asn1_skip_if_exists ( struct asn1_cursor *cursor, unsigned int type ) {
int len;
len = asn1_start ( cursor, type, 0 );
if ( len < 0 )
return len;
cursor->data += len;
cursor->len -= len;
DBGC ( cursor, "ASN1 %p skipped object type %02x (len %x)\n",
cursor, type, len );
return 0;
}
/**
* Skip ASN.1 object
*
* @v cursor ASN.1 object cursor
* @v type Expected type, or ASN1_ANY
* @ret rc Return status code
*
* The object cursor will be updated to point to the next ASN.1
* object. If any error occurs, the object cursor will be
* invalidated.
*/
int asn1_skip ( struct asn1_cursor *cursor, unsigned int type ) {
int rc;
if ( ( rc = asn1_skip_if_exists ( cursor, type ) ) != 0 ) {
asn1_invalidate_cursor ( cursor );
return rc;
}
return 0;
}
/**
* Shrink ASN.1 cursor to fit object
*
* @v cursor ASN.1 object cursor
* @v type Expected type, or ASN1_ANY
* @ret rc Return status code
*
* The object cursor will be shrunk to contain only the current ASN.1
* object. If any error occurs, the object cursor will be
* invalidated.
*/
int asn1_shrink ( struct asn1_cursor *cursor, unsigned int type ) {
struct asn1_cursor temp;
const void *end;
int len;
/* Find end of object */
memcpy ( &temp, cursor, sizeof ( temp ) );
len = asn1_start ( &temp, type, 0 );
if ( len < 0 ) {
asn1_invalidate_cursor ( cursor );
return len;
}
end = ( temp.data + len );
/* Shrink original cursor to contain only its first object */
cursor->len = ( end - cursor->data );
return 0;
}
/**
* Enter ASN.1 object of any type
*
* @v cursor ASN.1 object cursor
* @ret rc Return status code
*/
int asn1_enter_any ( struct asn1_cursor *cursor ) {
return asn1_enter ( cursor, ASN1_ANY );
}
/**
* Skip ASN.1 object of any type
*
* @v cursor ASN.1 object cursor
* @ret rc Return status code
*/
int asn1_skip_any ( struct asn1_cursor *cursor ) {
return asn1_skip ( cursor, ASN1_ANY );
}
/**
* Shrink ASN.1 object of any type
*
* @v cursor ASN.1 object cursor
* @ret rc Return status code
*/
int asn1_shrink_any ( struct asn1_cursor *cursor ) {
return asn1_shrink ( cursor, ASN1_ANY );
}
/**
* Parse value of ASN.1 boolean
*
* @v cursor ASN.1 object cursor
* @ret value Value, or negative error
*/
int asn1_boolean ( const struct asn1_cursor *cursor ) {
struct asn1_cursor contents;
const struct {
uint8_t value;
} __attribute__ (( packed )) *boolean;
/* Enter boolean */
memcpy ( &contents, cursor, sizeof ( contents ) );
asn1_enter ( &contents, ASN1_BOOLEAN );
if ( contents.len != sizeof ( *boolean ) )
return -EINVAL_ASN1_BOOLEAN;
/* Extract value */
boolean = contents.data;
return boolean->value;
}
/**
* Parse value of ASN.1 integer
*
* @v cursor ASN.1 object cursor
* @v value Value to fill in
* @ret rc Return status code
*/
int asn1_integer ( const struct asn1_cursor *cursor, int *value ) {
struct asn1_cursor contents;
uint8_t high_byte;
int rc;
/* Enter integer */
memcpy ( &contents, cursor, sizeof ( contents ) );
if ( ( rc = asn1_enter ( &contents, ASN1_INTEGER ) ) != 0 )
return rc;
if ( contents.len < 1 )
return -EINVAL_ASN1_INTEGER;
/* Initialise value according to sign byte */
*value = *( ( int8_t * ) contents.data );
contents.data++;
contents.len--;
/* Process value */
while ( contents.len ) {
high_byte = ( (*value) >> ( 8 * ( sizeof ( *value ) - 1 ) ) );
if ( ( high_byte != 0x00 ) && ( high_byte != 0xff ) ) {
DBGC ( cursor, "ASN1 %p integer overflow\n", cursor );
return -EINVAL_ASN1_INTEGER;
}
*value = ( ( *value << 8 ) | *( ( uint8_t * ) contents.data ) );
contents.data++;
contents.len--;
}
return 0;
}
/**
* Parse ASN.1 bit string
*
* @v cursor ASN.1 cursor
* @v bits Bit string to fill in
* @ret rc Return status code
*/
int asn1_bit_string ( const struct asn1_cursor *cursor,
struct asn1_bit_string *bits ) {
struct asn1_cursor contents;
const struct {
uint8_t unused;
uint8_t data[0];
} __attribute__ (( packed )) *bit_string;
size_t len;
unsigned int unused;
uint8_t unused_mask;
const uint8_t *last;
int rc;
/* Enter bit string */
memcpy ( &contents, cursor, sizeof ( contents ) );
if ( ( rc = asn1_enter ( &contents, ASN1_BIT_STRING ) ) != 0 ) {
DBGC ( cursor, "ASN1 %p cannot locate bit string:\n", cursor );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return rc;
}
/* Validity checks */
if ( contents.len < sizeof ( *bit_string ) ) {
DBGC ( cursor, "ASN1 %p invalid bit string:\n", cursor );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -EINVAL_BIT_STRING;
}
bit_string = contents.data;
len = ( contents.len - offsetof ( typeof ( *bit_string ), data ) );
unused = bit_string->unused;
unused_mask = ( 0xff >> ( 8 - unused ) );
last = ( bit_string->data + len - 1 );
if ( ( unused >= 8 ) ||
( ( unused > 0 ) && ( len == 0 ) ) ||
( ( *last & unused_mask ) != 0 ) ) {
DBGC ( cursor, "ASN1 %p invalid bit string:\n", cursor );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -EINVAL_BIT_STRING;
}
/* Populate bit string */
bits->data = &bit_string->data;
bits->len = len;
bits->unused = unused;
return 0;
}
/**
* Parse ASN.1 bit string that must be an integral number of bytes
*
* @v cursor ASN.1 cursor
* @v bits Bit string to fill in
* @ret rc Return status code
*/
int asn1_integral_bit_string ( const struct asn1_cursor *cursor,
struct asn1_bit_string *bits ) {
int rc;
/* Parse bit string */
if ( ( rc = asn1_bit_string ( cursor, bits ) ) != 0 )
return rc;
/* Check that there are no unused bits at end of string */
if ( bits->unused ) {
DBGC ( cursor, "ASN1 %p invalid integral bit string:\n",
cursor );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -EINVAL_BIT_STRING;
}
return 0;
}
/**
* Compare two ASN.1 objects
*
* @v cursor1 ASN.1 object cursor
* @v cursor2 ASN.1 object cursor
* @ret difference Difference as returned by memcmp()
*
* Note that invalid and empty cursors will compare as equal with each
* other.
*/
int asn1_compare ( const struct asn1_cursor *cursor1,
const struct asn1_cursor *cursor2 ) {
int difference;
difference = ( cursor2->len - cursor1->len );
return ( difference ? difference :
memcmp ( cursor1->data, cursor2->data, cursor1->len ) );
}
/**
* Identify ASN.1 algorithm by OID
*
* @v cursor ASN.1 object cursor
* @ret algorithm Algorithm, or NULL
*/
static struct asn1_algorithm *
asn1_find_algorithm ( const struct asn1_cursor *cursor ) {
struct asn1_algorithm *algorithm;
for_each_table_entry ( algorithm, ASN1_ALGORITHMS ) {
if ( asn1_compare ( &algorithm->oid, cursor ) == 0 )
return algorithm;
}
return NULL;
}
/**
* Parse ASN.1 OID-identified algorithm
*
* @v cursor ASN.1 object cursor
* @ret algorithm Algorithm
* @ret rc Return status code
*/
int asn1_algorithm ( const struct asn1_cursor *cursor,
struct asn1_algorithm **algorithm ) {
struct asn1_cursor contents;
int rc;
/* Enter signatureAlgorithm */
memcpy ( &contents, cursor, sizeof ( contents ) );
asn1_enter ( &contents, ASN1_SEQUENCE );
/* Enter algorithm */
if ( ( rc = asn1_enter ( &contents, ASN1_OID ) ) != 0 ) {
DBGC ( cursor, "ASN1 %p cannot locate algorithm OID:\n",
cursor );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -EINVAL_ASN1_ALGORITHM;
}
/* Identify algorithm */
*algorithm = asn1_find_algorithm ( &contents );
if ( ! *algorithm ) {
DBGC ( cursor, "ASN1 %p unrecognised algorithm:\n", cursor );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -ENOTSUP_ALGORITHM;
}
return 0;
}
/**
* Parse ASN.1 OID-identified public-key algorithm
*
* @v cursor ASN.1 object cursor
* @ret algorithm Algorithm
* @ret rc Return status code
*/
int asn1_pubkey_algorithm ( const struct asn1_cursor *cursor,
struct asn1_algorithm **algorithm ) {
int rc;
/* Parse algorithm */
if ( ( rc = asn1_algorithm ( cursor, algorithm ) ) != 0 )
return rc;
/* Check algorithm has a public key */
if ( ! (*algorithm)->pubkey ) {
DBGC ( cursor, "ASN1 %p algorithm %s is not a public-key "
"algorithm:\n", cursor, (*algorithm)->name );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -ENOTTY_ALGORITHM;
}
return 0;
}
/**
* Parse ASN.1 OID-identified digest algorithm
*
* @v cursor ASN.1 object cursor
* @ret algorithm Algorithm
* @ret rc Return status code
*/
int asn1_digest_algorithm ( const struct asn1_cursor *cursor,
struct asn1_algorithm **algorithm ) {
int rc;
/* Parse algorithm */
if ( ( rc = asn1_algorithm ( cursor, algorithm ) ) != 0 )
return rc;
/* Check algorithm has a digest */
if ( ! (*algorithm)->digest ) {
DBGC ( cursor, "ASN1 %p algorithm %s is not a digest "
"algorithm:\n", cursor, (*algorithm)->name );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -ENOTTY_ALGORITHM;
}
return 0;
}
/**
* Parse ASN.1 OID-identified signature algorithm
*
* @v cursor ASN.1 object cursor
* @ret algorithm Algorithm
* @ret rc Return status code
*/
int asn1_signature_algorithm ( const struct asn1_cursor *cursor,
struct asn1_algorithm **algorithm ) {
int rc;
/* Parse algorithm */
if ( ( rc = asn1_algorithm ( cursor, algorithm ) ) != 0 )
return rc;
/* Check algorithm has a public key */
if ( ! (*algorithm)->pubkey ) {
DBGC ( cursor, "ASN1 %p algorithm %s is not a signature "
"algorithm:\n", cursor, (*algorithm)->name );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -ENOTTY_ALGORITHM;
}
/* Check algorithm has a digest */
if ( ! (*algorithm)->digest ) {
DBGC ( cursor, "ASN1 %p algorithm %s is not a signature "
"algorithm:\n", cursor, (*algorithm)->name );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -ENOTTY_ALGORITHM;
}
return 0;
}
/**
* Check ASN.1 OID-identified algorithm
*
* @v cursor ASN.1 object cursor
* @v expected Expected algorithm
* @ret rc Return status code
*/
int asn1_check_algorithm ( const struct asn1_cursor *cursor,
struct asn1_algorithm *expected ) {
struct asn1_algorithm *actual;
int rc;
/* Parse algorithm */
if ( ( rc = asn1_algorithm ( cursor, &actual ) ) != 0 )
return rc;
/* Check algorithm matches */
if ( actual != expected ) {
DBGC ( cursor, "ASN1 %p algorithm %s does not match %s\n",
cursor, actual->name, expected->name );
return -ENOTTY_ALGORITHM;
}
return 0;
}
/**
* Parse ASN.1 GeneralizedTime
*
* @v cursor ASN.1 cursor
* @v time Time to fill in
* @ret rc Return status code
*
* RFC 5280 section 4.1.2.5 places several restrictions on the allowed
* formats for UTCTime and GeneralizedTime, and mandates the
* interpretation of centuryless year values.
*/
int asn1_generalized_time ( const struct asn1_cursor *cursor, time_t *time ) {
struct asn1_cursor contents;
unsigned int have_century;
unsigned int type;
union {
struct {
uint8_t century;
uint8_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
} __attribute__ (( packed )) named;
uint8_t raw[7];
} pairs;
struct tm tm;
const uint8_t *data;
size_t remaining;
unsigned int tens;
unsigned int units;
unsigned int i;
int rc;
/* Determine time format utcTime/generalizedTime */
memcpy ( &contents, cursor, sizeof ( contents ) );
type = asn1_type ( &contents );
switch ( type ) {
case ASN1_UTC_TIME:
have_century = 0;
break;
case ASN1_GENERALIZED_TIME:
have_century = 1;
break;
default:
DBGC ( cursor, "ASN1 %p invalid time type %02x\n",
cursor, type );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -EINVAL_ASN1_TIME;
}
/* Enter utcTime/generalizedTime */
if ( ( rc = asn1_enter ( &contents, type ) ) != 0 ) {
DBGC ( cursor, "ASN1 %p cannot locate %s time:\n", cursor,
( ( type == ASN1_UTC_TIME ) ? "UTC" : "generalized" ) );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return rc;
}
/* Parse digit string a pair at a time */
memset ( &pairs, 0, sizeof ( pairs ) );
data = contents.data;
remaining = contents.len;
for ( i = ( have_century ? 0 : 1 ) ; i < sizeof ( pairs.raw ) ; i++ ) {
if ( remaining < 2 ) {
/* Some certificates violate the X.509 RFC by
* omitting the "seconds" value.
*/
if ( i == ( sizeof ( pairs.raw ) - 1 ) )
break;
DBGC ( cursor, "ASN1 %p invalid time:\n", cursor );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -EINVAL_ASN1_TIME;
}
tens = data[0];
units = data[1];
if ( ! ( isdigit ( tens ) && isdigit ( units ) ) ) {
DBGC ( cursor, "ASN1 %p invalid time:\n", cursor );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -EINVAL_ASN1_TIME;
}
pairs.raw[i] = ( ( 10 * ( tens - '0' ) ) + ( units - '0' ) );
data += 2;
remaining -= 2;
}
/* Determine century if applicable */
if ( ! have_century )
pairs.named.century = ( ( pairs.named.year >= 50 ) ? 19 : 20 );
/* Check for trailing "Z" */
if ( ( remaining != 1 ) || ( data[0] != 'Z' ) ) {
DBGC ( cursor, "ASN1 %p invalid time:\n", cursor );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -EINVAL_ASN1_TIME;
}
/* Fill in time */
tm.tm_year = ( ( ( pairs.named.century - 19 ) * 100 ) +
pairs.named.year );
tm.tm_mon = ( pairs.named.month - 1 );
tm.tm_mday = pairs.named.day;
tm.tm_hour = pairs.named.hour;
tm.tm_min = pairs.named.minute;
tm.tm_sec = pairs.named.second;
/* Convert to seconds since the Epoch */
*time = mktime ( &tm );
return 0;
}
/**
* Construct ASN.1 header
*
* @v header ASN.1 builder header
* @v type Type
* @v len Content length
* @ret header_len Header length
*/
static size_t asn1_header ( struct asn1_builder_header *header,
unsigned int type, size_t len ) {
unsigned int header_len = 2;
unsigned int len_len = 0;
size_t temp;
/* Construct header */
header->type = type;
if ( len < 0x80 ) {
header->length[0] = len;
} else {
for ( temp = len ; temp ; temp >>= 8 )
len_len++;
header->length[0] = ( 0x80 | len_len );
header_len += len_len;
for ( temp = len ; temp ; temp >>= 8 )
header->length[len_len--] = ( temp & 0xff );
}
return header_len;
}
/**
* Grow ASN.1 builder
*
* @v builder ASN.1 builder
* @v extra Extra space to prepend
* @ret rc Return status code
*/
int asn1_grow ( struct asn1_builder *builder, size_t extra ) {
size_t new_len;
void *new;
/* As with the ASN1 parsing functions, make errors permanent */
if ( builder->len && ! builder->data )
return -ENOMEM;
/* Reallocate data buffer */
new_len = ( builder->len + extra );
new = realloc ( builder->data, new_len );
if ( ! new ) {
free ( builder->data );
builder->data = NULL;
return -ENOMEM;
}
builder->data = new;
/* Move existing data to end of buffer */
memmove ( ( builder->data + extra ), builder->data, builder->len );
builder->len = new_len;
return 0;
}
/**
* Prepend raw data to ASN.1 builder
*
* @v builder ASN.1 builder
* @v data Data to prepend
* @v len Length of data to prepend
* @ret rc Return status code
*/
int asn1_prepend_raw ( struct asn1_builder *builder, const void *data,
size_t len ) {
int rc;
/* Grow buffer */
if ( ( rc = asn1_grow ( builder, len ) ) != 0 )
return rc;
/* Populate data buffer */
memcpy ( builder->data, data, len );
return 0;
}
/**
* Prepend data to ASN.1 builder
*
* @v builder ASN.1 builder
* @v type Type
* @v data Data to prepend
* @v len Length of data to prepend
* @ret rc Return status code
*/
int asn1_prepend ( struct asn1_builder *builder, unsigned int type,
const void *data, size_t len ) {
struct asn1_builder_header header;
size_t header_len;
int rc;
/* Construct header */
header_len = asn1_header ( &header, type, len );
/* Grow buffer */
if ( ( rc = asn1_grow ( builder, header_len + len ) ) != 0 )
return rc;
/* Populate data buffer */
memcpy ( builder->data, &header, header_len );
memcpy ( ( builder->data + header_len ), data, len );
return 0;
}
/**
* Wrap ASN.1 builder
*
* @v builder ASN.1 builder
* @v type Type
* @ret rc Return status code
*/
int asn1_wrap ( struct asn1_builder *builder, unsigned int type ) {
struct asn1_builder_header header;
size_t header_len;
int rc;
/* Construct header */
header_len = asn1_header ( &header, type, builder->len );
/* Grow buffer */
if ( ( rc = asn1_grow ( builder, header_len ) ) != 0 )
return rc;
/* Populate data buffer */
memcpy ( builder->data, &header, header_len );
return 0;
}
/**
* Extract ASN.1 object from image
*
* @v image Image
* @v offset Offset within image
* @v cursor ASN.1 cursor to fill in
* @ret next Offset to next image, or negative error
*
* The caller is responsible for eventually calling free() on the
* allocated ASN.1 cursor.
*/
int image_asn1 ( struct image *image, size_t offset,
struct asn1_cursor **cursor ) {
int next;
int rc;
/* Sanity check */
assert ( offset <= image->len );
/* Check that this image can be used to extract an ASN.1 object */
if ( ! ( image->type && image->type->asn1 ) )
return -ENOTSUP;
/* Try creating ASN.1 cursor */
next = image->type->asn1 ( image, offset, cursor );
if ( next < 0 ) {
rc = next;
DBGC ( image, "IMAGE %s could not extract ASN.1 object: %s\n",
image->name, strerror ( rc ) );
return rc;
}
return next;
}
/* Drag in objects via image_asn1() */
REQUIRING_SYMBOL ( image_asn1 );
/* Drag in ASN.1 image formats */
REQUIRE_OBJECT ( config_asn1 );