freebsd-dev/crypto/heimdal/lib/asn1/template.c
Stanislav Sedov ae77177087 - Update FreeBSD Heimdal distribution to version 1.5.1. This also brings
several new kerberos related libraries and applications to FreeBSD:
  o kgetcred(1) allows one to manually get a ticket for a particular service.
  o kf(1) securily forwards ticket to another host through an authenticated
    and encrypted stream.
  o kcc(1) is an umbrella program around klist(1), kswitch(1), kgetcred(1)
    and other user kerberos operations. klist and kswitch are just symlinks
    to kcc(1) now.
  o kswitch(1) allows you to easily switch between kerberos credentials if
    you're running KCM.
  o hxtool(1) is a certificate management tool to use with PKINIT.
  o string2key(1) maps a password into key.
  o kdigest(8) is a userland tool to access the KDC's digest interface.
  o kimpersonate(8) creates a "fake" ticket for a service.

  We also now install manpages for some lirbaries that were not installed
  before, libheimntlm and libhx509.

- The new HEIMDAL version no longer supports Kerberos 4.  All users are
  recommended to switch to Kerberos 5.

- Weak ciphers are now disabled by default.  To enable DES support (used
  by telnet(8)), use "allow_weak_crypto" option in krb5.conf.

- libtelnet, pam_ksu and pam_krb5 are now compiled with error on warnings
  disabled due to the function they use (krb5_get_err_text(3)) being
  deprecated.  I plan to work on this next.

- Heimdal's KDC now require sqlite to operate.  We use the bundled version
  and install it as libheimsqlite.  If some other FreeBSD components will
  require it in the future we can rename it to libbsdsqlite and use for these
  components as well.

- This is not a latest Heimdal version, the new one was released while I was
  working on the update.  I will update it to 1.5.2 soon, as it fixes some
  important bugs and security issues.
2012-03-22 08:48:42 +00:00

1120 lines
24 KiB
C

/*
* Copyright (c) 2009 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Portions Copyright (c) 2009 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "der_locl.h"
#include <com_err.h>
#if 0
#define ABORT_ON_ERROR() abort()
#else
#define ABORT_ON_ERROR() do { } while(0)
#endif
#define DPOC(data,offset) ((const void *)(((const unsigned char *)data) + offset))
#define DPO(data,offset) ((void *)(((unsigned char *)data) + offset))
static struct asn1_type_func prim[] = {
#define el(name, type) { \
(asn1_type_encode)der_put_##name, \
(asn1_type_decode)der_get_##name, \
(asn1_type_length)der_length_##name, \
(asn1_type_copy)der_copy_##name, \
(asn1_type_release)der_free_##name, \
sizeof(type) \
}
#define elber(name, type) { \
(asn1_type_encode)der_put_##name, \
(asn1_type_decode)der_get_##name##_ber, \
(asn1_type_length)der_length_##name, \
(asn1_type_copy)der_copy_##name, \
(asn1_type_release)der_free_##name, \
sizeof(type) \
}
el(integer, int),
el(heim_integer, heim_integer),
el(integer, int),
el(unsigned, unsigned),
el(general_string, heim_general_string),
el(octet_string, heim_octet_string),
elber(octet_string, heim_octet_string),
el(ia5_string, heim_ia5_string),
el(bmp_string, heim_bmp_string),
el(universal_string, heim_universal_string),
el(printable_string, heim_printable_string),
el(visible_string, heim_visible_string),
el(utf8string, heim_utf8_string),
el(generalized_time, time_t),
el(utctime, time_t),
el(bit_string, heim_bit_string),
{ (asn1_type_encode)der_put_boolean, (asn1_type_decode)der_get_boolean,
(asn1_type_length)der_length_boolean, (asn1_type_copy)der_copy_integer,
(asn1_type_release)der_free_integer, sizeof(int)
},
el(oid, heim_oid),
el(general_string, heim_general_string),
#undef el
#undef elber
};
static size_t
sizeofType(const struct asn1_template *t)
{
return t->offset;
}
/*
* Here is abstraction to not so well evil fact of bit fields in C,
* they are endian dependent, so when getting and setting bits in the
* host local structure we need to know the endianness of the host.
*
* Its not the first time in Heimdal this have bitten us, and some day
* we'll grow up and use #defined constant, but bit fields are still
* so pretty and shiny.
*/
static void
bmember_get_bit(const unsigned char *p, void *data,
unsigned int bit, size_t size)
{
unsigned int localbit = bit % 8;
if ((*p >> (7 - localbit)) & 1) {
#ifdef WORDS_BIGENDIAN
*(unsigned int *)data |= (1 << ((size * 8) - bit - 1));
#else
*(unsigned int *)data |= (1 << bit);
#endif
}
}
static int
bmember_isset_bit(const void *data, unsigned int bit, size_t size)
{
#ifdef WORDS_BIGENDIAN
if ((*(unsigned int *)data) & (1 << ((size * 8) - bit - 1)))
return 1;
return 0;
#else
if ((*(unsigned int *)data) & (1 << bit))
return 1;
return 0;
#endif
}
static void
bmember_put_bit(unsigned char *p, const void *data, unsigned int bit,
size_t size, unsigned int *bitset)
{
unsigned int localbit = bit % 8;
if (bmember_isset_bit(data, bit, size)) {
*p |= (1 << (7 - localbit));
if (*bitset == 0)
*bitset = (7 - localbit) + 1;
}
}
int
_asn1_decode(const struct asn1_template *t, unsigned flags,
const unsigned char *p, size_t len, void *data, size_t *size)
{
size_t elements = A1_HEADER_LEN(t);
size_t oldlen = len;
int ret = 0;
const unsigned char *startp = NULL;
unsigned int template_flags = t->tt;
/* skip over header */
t++;
if (template_flags & A1_HF_PRESERVE)
startp = p;
while (elements) {
switch (t->tt & A1_OP_MASK) {
case A1_OP_TYPE:
case A1_OP_TYPE_EXTERN: {
size_t newsize, size;
void *el = DPO(data, t->offset);
void **pel = (void **)el;
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
size = sizeofType(t->ptr);
} else {
const struct asn1_type_func *f = t->ptr;
size = f->size;
}
if (t->tt & A1_FLAG_OPTIONAL) {
*pel = calloc(1, size);
if (*pel == NULL)
return ENOMEM;
el = *pel;
}
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
ret = _asn1_decode(t->ptr, flags, p, len, el, &newsize);
} else {
const struct asn1_type_func *f = t->ptr;
ret = (f->decode)(p, len, el, &newsize);
}
if (ret) {
if (t->tt & A1_FLAG_OPTIONAL) {
free(*pel);
*pel = NULL;
break;
}
return ret;
}
p += newsize; len -= newsize;
break;
}
case A1_OP_TAG: {
Der_type dertype;
size_t newsize;
size_t datalen, l;
void *olddata = data;
int is_indefinite = 0;
int subflags = flags;
ret = der_match_tag_and_length(p, len, A1_TAG_CLASS(t->tt),
&dertype, A1_TAG_TAG(t->tt),
&datalen, &l);
if (ret) {
if (t->tt & A1_FLAG_OPTIONAL)
break;
return ret;
}
p += l; len -= l;
/*
* Only allow indefinite encoding for OCTET STRING and BER
* for now. Should handle BIT STRING too.
*/
if (dertype != A1_TAG_TYPE(t->tt) && (flags & A1_PF_ALLOW_BER)) {
const struct asn1_template *subtype = t->ptr;
subtype++; /* skip header */
if (((subtype->tt & A1_OP_MASK) == A1_OP_PARSE) &&
A1_PARSE_TYPE(subtype->tt) == A1T_OCTET_STRING)
subflags |= A1_PF_INDEFINTE;
}
if (datalen == ASN1_INDEFINITE) {
if ((flags & A1_PF_ALLOW_BER) == 0)
return ASN1_GOT_BER;
is_indefinite = 1;
datalen = len;
if (datalen < 2)
return ASN1_OVERRUN;
/* hide EndOfContent for sub-decoder, catching it below */
datalen -= 2;
} else if (datalen > len)
return ASN1_OVERRUN;
data = DPO(data, t->offset);
if (t->tt & A1_FLAG_OPTIONAL) {
void **el = (void **)data;
size_t ellen = sizeofType(t->ptr);
*el = calloc(1, ellen);
if (*el == NULL)
return ENOMEM;
data = *el;
}
ret = _asn1_decode(t->ptr, subflags, p, datalen, data, &newsize);
if (ret)
return ret;
if (newsize != datalen)
return ASN1_EXTRA_DATA;
len -= datalen;
p += datalen;
/*
* Indefinite encoding needs a trailing EndOfContent,
* check for that.
*/
if (is_indefinite) {
ret = der_match_tag_and_length(p, len, ASN1_C_UNIV,
&dertype, UT_EndOfContent,
&datalen, &l);
if (ret)
return ret;
if (dertype != PRIM)
return ASN1_BAD_ID;
if (datalen != 0)
return ASN1_INDEF_EXTRA_DATA;
p += l; len -= l;
}
data = olddata;
break;
}
case A1_OP_PARSE: {
unsigned int type = A1_PARSE_TYPE(t->tt);
size_t newsize;
void *el = DPO(data, t->offset);
/*
* INDEFINITE primitive types are one element after the
* same type but non-INDEFINITE version.
*/
if (flags & A1_PF_INDEFINTE)
type++;
if (type >= sizeof(prim)/sizeof(prim[0])) {
ABORT_ON_ERROR();
return ASN1_PARSE_ERROR;
}
ret = (prim[type].decode)(p, len, el, &newsize);
if (ret)
return ret;
p += newsize; len -= newsize;
break;
}
case A1_OP_SETOF:
case A1_OP_SEQOF: {
struct template_of *el = DPO(data, t->offset);
size_t newsize;
size_t ellen = sizeofType(t->ptr);
size_t vallength = 0;
while (len > 0) {
void *tmp;
size_t newlen = vallength + ellen;
if (vallength > newlen)
return ASN1_OVERFLOW;
tmp = realloc(el->val, newlen);
if (tmp == NULL)
return ENOMEM;
memset(DPO(tmp, vallength), 0, ellen);
el->val = tmp;
ret = _asn1_decode(t->ptr, flags & (~A1_PF_INDEFINTE), p, len,
DPO(el->val, vallength), &newsize);
if (ret)
return ret;
vallength = newlen;
el->len++;
p += newsize; len -= newsize;
}
break;
}
case A1_OP_BMEMBER: {
const struct asn1_template *bmember = t->ptr;
size_t size = bmember->offset;
size_t elements = A1_HEADER_LEN(bmember);
size_t pos = 0;
bmember++;
memset(data, 0, size);
if (len < 1)
return ASN1_OVERRUN;
p++; len--;
while (elements && len) {
while (bmember->offset / 8 > pos / 8) {
if (len < 1)
break;
p++; len--;
pos += 8;
}
if (len) {
bmember_get_bit(p, data, bmember->offset, size);
elements--; bmember++;
}
}
len = 0;
break;
}
case A1_OP_CHOICE: {
const struct asn1_template *choice = t->ptr;
unsigned int *element = DPO(data, choice->offset);
size_t datalen;
unsigned int i;
for (i = 1; i < A1_HEADER_LEN(choice) + 1; i++) {
/* should match first tag instead, store it in choice.tt */
ret = _asn1_decode(choice[i].ptr, 0, p, len,
DPO(data, choice[i].offset), &datalen);
if (ret == 0) {
*element = i;
p += datalen; len -= datalen;
break;
} else if (ret != ASN1_BAD_ID && ret != ASN1_MISPLACED_FIELD && ret != ASN1_MISSING_FIELD) {
return ret;
}
}
if (i >= A1_HEADER_LEN(choice) + 1) {
if (choice->tt == 0)
return ASN1_BAD_ID;
*element = 0;
ret = der_get_octet_string(p, len,
DPO(data, choice->tt), &datalen);
if (ret)
return ret;
p += datalen; len -= datalen;
}
break;
}
default:
ABORT_ON_ERROR();
return ASN1_PARSE_ERROR;
}
t++;
elements--;
}
/* if we are using padding, eat up read of context */
if (template_flags & A1_HF_ELLIPSIS)
len = 0;
oldlen -= len;
if (size)
*size = oldlen;
/*
* saved the raw bits if asked for it, useful for signature
* verification.
*/
if (startp) {
heim_octet_string *save = data;
save->data = malloc(oldlen);
if (save->data == NULL)
return ENOMEM;
else {
save->length = oldlen;
memcpy(save->data, startp, oldlen);
}
}
return 0;
}
int
_asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const void *data, size_t *size)
{
size_t elements = A1_HEADER_LEN(t);
int ret = 0;
size_t oldlen = len;
t += A1_HEADER_LEN(t);
while (elements) {
switch (t->tt & A1_OP_MASK) {
case A1_OP_TYPE:
case A1_OP_TYPE_EXTERN: {
size_t newsize;
const void *el = DPOC(data, t->offset);
if (t->tt & A1_FLAG_OPTIONAL) {
void **pel = (void **)el;
if (*pel == NULL)
break;
el = *pel;
}
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
ret = _asn1_encode(t->ptr, p, len, el, &newsize);
} else {
const struct asn1_type_func *f = t->ptr;
ret = (f->encode)(p, len, el, &newsize);
}
if (ret)
return ret;
p -= newsize; len -= newsize;
break;
}
case A1_OP_TAG: {
const void *olddata = data;
size_t l, datalen;
data = DPOC(data, t->offset);
if (t->tt & A1_FLAG_OPTIONAL) {
void **el = (void **)data;
if (*el == NULL) {
data = olddata;
break;
}
data = *el;
}
ret = _asn1_encode(t->ptr, p, len, data, &datalen);
if (ret)
return ret;
len -= datalen; p -= datalen;
ret = der_put_length_and_tag(p, len, datalen,
A1_TAG_CLASS(t->tt),
A1_TAG_TYPE(t->tt),
A1_TAG_TAG(t->tt), &l);
if (ret)
return ret;
p -= l; len -= l;
data = olddata;
break;
}
case A1_OP_PARSE: {
unsigned int type = A1_PARSE_TYPE(t->tt);
size_t newsize;
const void *el = DPOC(data, t->offset);
if (type > sizeof(prim)/sizeof(prim[0])) {
ABORT_ON_ERROR();
return ASN1_PARSE_ERROR;
}
ret = (prim[type].encode)(p, len, el, &newsize);
if (ret)
return ret;
p -= newsize; len -= newsize;
break;
}
case A1_OP_SETOF: {
const struct template_of *el = DPOC(data, t->offset);
size_t ellen = sizeofType(t->ptr);
struct heim_octet_string *val;
unsigned char *elptr = el->val;
size_t i, totallen;
if (el->len == 0)
break;
if (el->len > UINT_MAX/sizeof(val[0]))
return ERANGE;
val = malloc(sizeof(val[0]) * el->len);
if (val == NULL)
return ENOMEM;
for(totallen = 0, i = 0; i < el->len; i++) {
unsigned char *next;
size_t l;
val[i].length = _asn1_length(t->ptr, elptr);
val[i].data = malloc(val[i].length);
ret = _asn1_encode(t->ptr, DPO(val[i].data, val[i].length - 1),
val[i].length, elptr, &l);
if (ret)
break;
next = elptr + ellen;
if (next < elptr) {
ret = ASN1_OVERFLOW;
break;
}
elptr = next;
totallen += val[i].length;
}
if (ret == 0 && totallen > len)
ret = ASN1_OVERFLOW;
if (ret) {
do {
free(val[i].data);
} while(i-- > 0);
free(val);
return ret;
}
len -= totallen;
qsort(val, el->len, sizeof(val[0]), _heim_der_set_sort);
i = el->len - 1;
do {
p -= val[i].length;
memcpy(p + 1, val[i].data, val[i].length);
free(val[i].data);
} while(i-- > 0);
free(val);
break;
}
case A1_OP_SEQOF: {
struct template_of *el = DPO(data, t->offset);
size_t ellen = sizeofType(t->ptr);
size_t newsize;
unsigned int i;
unsigned char *elptr = el->val;
if (el->len == 0)
break;
elptr += ellen * (el->len - 1);
for (i = 0; i < el->len; i++) {
ret = _asn1_encode(t->ptr, p, len,
elptr,
&newsize);
if (ret)
return ret;
p -= newsize; len -= newsize;
elptr -= ellen;
}
break;
}
case A1_OP_BMEMBER: {
const struct asn1_template *bmember = t->ptr;
size_t size = bmember->offset;
size_t elements = A1_HEADER_LEN(bmember);
size_t pos;
unsigned char c = 0;
unsigned int bitset = 0;
int rfc1510 = (bmember->tt & A1_HBF_RFC1510);
bmember += elements;
if (rfc1510)
pos = 31;
else
pos = bmember->offset;
while (elements && len) {
while (bmember->offset / 8 < pos / 8) {
if (rfc1510 || bitset || c) {
if (len < 1)
return ASN1_OVERFLOW;
*p-- = c; len--;
}
c = 0;
pos -= 8;
}
bmember_put_bit(&c, data, bmember->offset, size, &bitset);
elements--; bmember--;
}
if (rfc1510 || bitset) {
if (len < 1)
return ASN1_OVERFLOW;
*p-- = c; len--;
}
if (len < 1)
return ASN1_OVERFLOW;
if (rfc1510 || bitset == 0)
*p-- = 0;
else
*p-- = bitset - 1;
len--;
break;
}
case A1_OP_CHOICE: {
const struct asn1_template *choice = t->ptr;
const unsigned int *element = DPOC(data, choice->offset);
size_t datalen;
const void *el;
if (*element > A1_HEADER_LEN(choice)) {
printf("element: %d\n", *element);
return ASN1_PARSE_ERROR;
}
if (*element == 0) {
ret += der_put_octet_string(p, len,
DPOC(data, choice->tt), &datalen);
} else {
choice += *element;
el = DPOC(data, choice->offset);
ret = _asn1_encode(choice->ptr, p, len, el, &datalen);
if (ret)
return ret;
}
len -= datalen; p -= datalen;
break;
}
default:
ABORT_ON_ERROR();
}
t--;
elements--;
}
if (size)
*size = oldlen - len;
return 0;
}
size_t
_asn1_length(const struct asn1_template *t, const void *data)
{
size_t elements = A1_HEADER_LEN(t);
size_t ret = 0;
t += A1_HEADER_LEN(t);
while (elements) {
switch (t->tt & A1_OP_MASK) {
case A1_OP_TYPE:
case A1_OP_TYPE_EXTERN: {
const void *el = DPOC(data, t->offset);
if (t->tt & A1_FLAG_OPTIONAL) {
void **pel = (void **)el;
if (*pel == NULL)
break;
el = *pel;
}
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
ret += _asn1_length(t->ptr, el);
} else {
const struct asn1_type_func *f = t->ptr;
ret += (f->length)(el);
}
break;
}
case A1_OP_TAG: {
size_t datalen;
const void *olddata = data;
data = DPO(data, t->offset);
if (t->tt & A1_FLAG_OPTIONAL) {
void **el = (void **)data;
if (*el == NULL) {
data = olddata;
break;
}
data = *el;
}
datalen = _asn1_length(t->ptr, data);
ret += der_length_tag(A1_TAG_TAG(t->tt)) + der_length_len(datalen);
ret += datalen;
data = olddata;
break;
}
case A1_OP_PARSE: {
unsigned int type = A1_PARSE_TYPE(t->tt);
const void *el = DPOC(data, t->offset);
if (type > sizeof(prim)/sizeof(prim[0])) {
ABORT_ON_ERROR();
break;
}
ret += (prim[type].length)(el);
break;
}
case A1_OP_SETOF:
case A1_OP_SEQOF: {
const struct template_of *el = DPOC(data, t->offset);
size_t ellen = sizeofType(t->ptr);
const unsigned char *element = el->val;
unsigned int i;
for (i = 0; i < el->len; i++) {
ret += _asn1_length(t->ptr, element);
element += ellen;
}
break;
}
case A1_OP_BMEMBER: {
const struct asn1_template *bmember = t->ptr;
size_t size = bmember->offset;
size_t elements = A1_HEADER_LEN(bmember);
int rfc1510 = (bmember->tt & A1_HBF_RFC1510);
if (rfc1510) {
ret += 5;
} else {
ret += 1;
bmember += elements;
while (elements) {
if (bmember_isset_bit(data, bmember->offset, size)) {
ret += (bmember->offset / 8) + 1;
break;
}
elements--; bmember--;
}
}
break;
}
case A1_OP_CHOICE: {
const struct asn1_template *choice = t->ptr;
const unsigned int *element = DPOC(data, choice->offset);
if (*element > A1_HEADER_LEN(choice))
break;
if (*element == 0) {
ret += der_length_octet_string(DPOC(data, choice->tt));
} else {
choice += *element;
ret += _asn1_length(choice->ptr, DPOC(data, choice->offset));
}
break;
}
default:
ABORT_ON_ERROR();
break;
}
elements--;
t--;
}
return ret;
}
void
_asn1_free(const struct asn1_template *t, void *data)
{
size_t elements = A1_HEADER_LEN(t);
if (t->tt & A1_HF_PRESERVE)
der_free_octet_string(data);
t++;
while (elements) {
switch (t->tt & A1_OP_MASK) {
case A1_OP_TYPE:
case A1_OP_TYPE_EXTERN: {
void *el = DPO(data, t->offset);
if (t->tt & A1_FLAG_OPTIONAL) {
void **pel = (void **)el;
if (*pel == NULL)
break;
el = *pel;
}
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
_asn1_free(t->ptr, el);
} else {
const struct asn1_type_func *f = t->ptr;
(f->release)(el);
}
if (t->tt & A1_FLAG_OPTIONAL)
free(el);
break;
}
case A1_OP_PARSE: {
unsigned int type = A1_PARSE_TYPE(t->tt);
void *el = DPO(data, t->offset);
if (type > sizeof(prim)/sizeof(prim[0])) {
ABORT_ON_ERROR();
break;
}
(prim[type].release)(el);
break;
}
case A1_OP_TAG: {
void *el = DPO(data, t->offset);
if (t->tt & A1_FLAG_OPTIONAL) {
void **pel = (void **)el;
if (*pel == NULL)
break;
el = *pel;
}
_asn1_free(t->ptr, el);
if (t->tt & A1_FLAG_OPTIONAL)
free(el);
break;
}
case A1_OP_SETOF:
case A1_OP_SEQOF: {
struct template_of *el = DPO(data, t->offset);
size_t ellen = sizeofType(t->ptr);
unsigned char *element = el->val;
unsigned int i;
for (i = 0; i < el->len; i++) {
_asn1_free(t->ptr, element);
element += ellen;
}
free(el->val);
el->val = NULL;
el->len = 0;
break;
}
case A1_OP_BMEMBER:
break;
case A1_OP_CHOICE: {
const struct asn1_template *choice = t->ptr;
const unsigned int *element = DPOC(data, choice->offset);
if (*element > A1_HEADER_LEN(choice))
break;
if (*element == 0) {
der_free_octet_string(DPO(data, choice->tt));
} else {
choice += *element;
_asn1_free(choice->ptr, DPO(data, choice->offset));
}
break;
}
default:
ABORT_ON_ERROR();
break;
}
t++;
elements--;
}
}
int
_asn1_copy(const struct asn1_template *t, const void *from, void *to)
{
size_t elements = A1_HEADER_LEN(t);
int ret = 0;
int preserve = (t->tt & A1_HF_PRESERVE);
t++;
if (preserve) {
ret = der_copy_octet_string(from, to);
if (ret)
return ret;
}
while (elements) {
switch (t->tt & A1_OP_MASK) {
case A1_OP_TYPE:
case A1_OP_TYPE_EXTERN: {
const void *fel = DPOC(from, t->offset);
void *tel = DPO(to, t->offset);
void **ptel = (void **)tel;
size_t size;
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
size = sizeofType(t->ptr);
} else {
const struct asn1_type_func *f = t->ptr;
size = f->size;
}
if (t->tt & A1_FLAG_OPTIONAL) {
void **pfel = (void **)fel;
if (*pfel == NULL)
break;
fel = *pfel;
tel = *ptel = calloc(1, size);
if (tel == NULL)
return ENOMEM;
}
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
ret = _asn1_copy(t->ptr, fel, tel);
} else {
const struct asn1_type_func *f = t->ptr;
ret = (f->copy)(fel, tel);
}
if (ret) {
if (t->tt & A1_FLAG_OPTIONAL) {
free(*ptel);
*ptel = NULL;
}
return ret;
}
break;
}
case A1_OP_PARSE: {
unsigned int type = A1_PARSE_TYPE(t->tt);
const void *fel = DPOC(from, t->offset);
void *tel = DPO(to, t->offset);
if (type > sizeof(prim)/sizeof(prim[0])) {
ABORT_ON_ERROR();
return ASN1_PARSE_ERROR;
}
ret = (prim[type].copy)(fel, tel);
if (ret)
return ret;
break;
}
case A1_OP_TAG: {
const void *oldfrom = from;
void *oldto = to;
void **tel = NULL;
from = DPOC(from, t->offset);
to = DPO(to, t->offset);
if (t->tt & A1_FLAG_OPTIONAL) {
void **fel = (void **)from;
tel = (void **)to;
if (*fel == NULL) {
from = oldfrom;
to = oldto;
break;
}
from = *fel;
to = *tel = calloc(1, sizeofType(t->ptr));
if (to == NULL)
return ENOMEM;
}
ret = _asn1_copy(t->ptr, from, to);
if (ret) {
if (t->tt & A1_FLAG_OPTIONAL) {
free(*tel);
*tel = NULL;
}
return ret;
}
from = oldfrom;
to = oldto;
break;
}
case A1_OP_SETOF:
case A1_OP_SEQOF: {
const struct template_of *fel = DPOC(from, t->offset);
struct template_of *tel = DPO(to, t->offset);
size_t ellen = sizeofType(t->ptr);
unsigned int i;
tel->val = calloc(fel->len, ellen);
if (tel->val == NULL)
return ENOMEM;
tel->len = fel->len;
for (i = 0; i < fel->len; i++) {
ret = _asn1_copy(t->ptr,
DPOC(fel->val, (i * ellen)),
DPO(tel->val, (i *ellen)));
if (ret)
return ret;
}
break;
}
case A1_OP_BMEMBER: {
const struct asn1_template *bmember = t->ptr;
size_t size = bmember->offset;
memcpy(to, from, size);
break;
}
case A1_OP_CHOICE: {
const struct asn1_template *choice = t->ptr;
const unsigned int *felement = DPOC(from, choice->offset);
unsigned int *telement = DPO(to, choice->offset);
if (*felement > A1_HEADER_LEN(choice))
return ASN1_PARSE_ERROR;
*telement = *felement;
if (*felement == 0) {
ret = der_copy_octet_string(DPOC(from, choice->tt), DPO(to, choice->tt));
} else {
choice += *felement;
ret = _asn1_copy(choice->ptr,
DPOC(from, choice->offset),
DPO(to, choice->offset));
}
if (ret)
return ret;
break;
}
default:
ABORT_ON_ERROR();
break;
}
t++;
elements--;
}
return 0;
}
int
_asn1_decode_top(const struct asn1_template *t, unsigned flags, const unsigned char *p, size_t len, void *data, size_t *size)
{
int ret;
memset(data, 0, t->offset);
ret = _asn1_decode(t, flags, p, len, data, size);
if (ret) {
_asn1_free(t, data);
memset(data, 0, t->offset);
}
return ret;
}
int
_asn1_copy_top(const struct asn1_template *t, const void *from, void *to)
{
int ret;
memset(to, 0, t->offset);
ret = _asn1_copy(t, from, to);
if (ret) {
_asn1_free(t, to);
memset(to, 0, t->offset);
}
return ret;
}