decode.c revision e65e5c2d2f32a99e8c5f740cabae9075dab03ce7
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* Copyright (c) 1990 Regents of the University of Michigan.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that this notice is preserved and that due credit is given
* to the University of Michigan at Ann Arbor. The name of the University
* may not be used to endorse or promote products derived from this
* software without specific prior written permission. This software
* is provided ``as is'' without express or implied warranty.
*/
/* decode.c - ber input decoding routines */
#include <strings.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <inttypes.h>
#include <ber_der.h>
#include "kmfber_int.h"
static void
ber_svecfree(char **vals)
{
int i;
if (vals == NULL)
return;
for (i = 0; vals[i] != NULL; i++)
free(vals[i]);
free((char *)vals);
}
/*
* Note: kmfber_get_tag() only uses the ber_end and ber_ptr elements of ber.
* If that changes, the kmfber_peek_tag() and/or
* kmfkmfber_skip_tag() implementations will need to be changed.
*/
/* return the tag - KMFBER_DEFAULT returned means trouble */
static ber_tag_t
kmfber_get_tag(BerElement *ber)
{
unsigned char xbyte;
ber_tag_t tag;
char *tagp;
int i;
if (kmfber_read(ber, (char *)&xbyte, 1) != 1)
return (KMFBER_DEFAULT);
if ((xbyte & KMFBER_BIG_TAG_MASK) != KMFBER_BIG_TAG_MASK)
return ((ber_uint_t)xbyte);
tagp = (char *)&tag;
tagp[0] = xbyte;
for (i = 1; i < sizeof (ber_int_t); i++) {
if (kmfber_read(ber, (char *)&xbyte, 1) != 1)
return (KMFBER_DEFAULT);
tagp[i] = xbyte;
if (! (xbyte & KMFBER_MORE_TAG_MASK))
break;
}
/* tag too big! */
if (i == sizeof (ber_int_t))
return (KMFBER_DEFAULT);
/* want leading, not trailing 0's */
return (tag >> (sizeof (ber_int_t)- i - 1));
}
/*
* Note: kmfber_skip_tag() only uses the ber_end and ber_ptr elements of ber.
* If that changes, the implementation of kmfber_peek_tag() will need to
* be changed.
*/
ber_tag_t
kmfber_skip_tag(BerElement *ber, ber_len_t *len)
{
ber_tag_t tag;
unsigned char lc;
int noctets, diff;
uint32_t netlen;
/*
* Any ber element looks like this: tag length contents.
* Assuming everything's ok, we return the tag byte (we
* can assume a single byte), and return the length in len.
*
* Assumptions:
* 1) definite lengths
* 2) primitive encodings used whenever possible
*/
/*
* First, we read the tag.
*/
if ((tag = kmfber_get_tag(ber)) == KMFBER_DEFAULT)
return (KMFBER_DEFAULT);
/*
* Next, read the length. The first byte contains the length of
* the length. If bit 8 is set, the length is the long form,
* otherwise it's the short form. We don't allow a length that's
* greater than what we can hold in an unsigned long.
*/
*len = 0;
netlen = 0;
if (kmfber_read(ber, (char *)&lc, 1) != 1)
return (KMFBER_DEFAULT);
if (lc & 0x80) {
noctets = (lc & 0x7f);
if (noctets > sizeof (ber_uint_t))
return (KMFBER_DEFAULT);
diff = sizeof (ber_int_t) - noctets;
if (kmfber_read(ber, (char *)&netlen + diff, noctets)
!= noctets)
return (KMFBER_DEFAULT);
*len = ntohl(netlen);
} else {
*len = lc;
}
return (tag);
}
/*
* Note: Previously, we passed the "ber" parameter directly to
* kmfber_skip_tag(), saving and restoring the ber_ptr element only.
* We now take advantage of the fact that the only ber structure
* elements touched by kmfber_skip_tag() are ber_end and ber_ptr.
* If that changes, this code must change too.
*/
static ber_tag_t
kmfber_peek_tag(BerElement *ber, ber_len_t *len)
{
BerElement bercopy;
bercopy.ber_end = ber->ber_end;
bercopy.ber_ptr = ber->ber_ptr;
return (kmfber_skip_tag(&bercopy, len));
}
static int
ber_getnint(BerElement *ber, ber_int_t *num, ber_slen_t len)
{
int i;
ber_int_t value;
unsigned char buffer[sizeof (ber_int_t)];
/*
* The tag and length have already been stripped off. We should
* be sitting right before len bytes of 2's complement integer,
* ready to be read straight into an int. We may have to sign
* extend after we read it in.
*/
if (len > sizeof (buffer))
return (-1);
/* read into the low-order bytes of netnum */
if (kmfber_read(ber, (char *)buffer, len) != len)
return (-1);
/* This sets the required sign extension */
if (len != 0) {
value = 0x80 & buffer[0] ? (-1) : 0;
} else {
value = 0;
}
for (i = 0; i < len; i++)
value = (value << 8) | buffer[i];
*num = value;
return (len);
}
static ber_tag_t
kmfber_get_int(BerElement *ber, ber_int_t *num)
{
ber_tag_t tag;
ber_len_t len;
if ((tag = kmfber_skip_tag(ber, &len)) == KMFBER_DEFAULT)
return (KMFBER_DEFAULT);
/*
* len is being demoted to a long here -- possible conversion error
*/
if (ber_getnint(ber, num, (int)len) != (ber_slen_t)len)
return (KMFBER_DEFAULT);
else
return (tag);
}
static ber_tag_t
kmfber_get_stringb(BerElement *ber, char *buf, ber_len_t *len)
{
ber_len_t datalen;
ber_tag_t tag;
#ifdef STR_TRANSLATION
char *transbuf;
#endif /* STR_TRANSLATION */
if ((tag = kmfber_skip_tag(ber, &datalen)) == KMFBER_DEFAULT)
return (KMFBER_DEFAULT);
if (datalen > (*len - 1))
return (KMFBER_DEFAULT);
/*
* datalen is being demoted to a long here -- possible conversion error
*/
if (kmfber_read(ber, buf, datalen) != (ber_slen_t)datalen)
return (KMFBER_DEFAULT);
buf[datalen] = '\0';
#ifdef STR_TRANSLATION
if (datalen > 0 && (ber->ber_options & KMFBER_OPT_TRANSLATE_STRINGS)
!= 0 && ber->ber_decode_translate_proc != NULL) {
transbuf = buf;
++datalen;
if ((*(ber->ber_decode_translate_proc))(&transbuf, &datalen,
0) != 0) {
return (KMFBER_DEFAULT);
}
if (datalen > *len) {
free(transbuf);
return (KMFBER_DEFAULT);
}
(void) memmove(buf, transbuf, datalen);
free(transbuf);
--datalen;
}
#endif /* STR_TRANSLATION */
*len = datalen;
return (tag);
}
static ber_tag_t
kmfber_get_stringa(BerElement *ber, char **buf)
{
ber_len_t datalen;
ber_tag_t tag;
if ((tag = kmfber_skip_tag(ber, &datalen)) == KMFBER_DEFAULT)
return (KMFBER_DEFAULT);
if ((*buf = (char *)malloc((size_t)datalen + 1)) == NULL)
return (KMFBER_DEFAULT);
/*
* datalen is being demoted to a long here -- possible conversion error
*/
if (kmfber_read(ber, *buf, datalen) != (ber_slen_t)datalen)
return (KMFBER_DEFAULT);
(*buf)[datalen] = '\0';
return (tag);
}
ber_tag_t
ber_get_oid(BerElement *ber, struct berval *oid)
{
ber_len_t len;
ber_tag_t tag;
if ((tag = kmfber_skip_tag(ber, &len)) != 0x06) {
return (KMFBER_DEFAULT);
}
if ((oid->bv_val = (char *)malloc((size_t)len + 1)) == NULL) {
return (KMFBER_DEFAULT);
}
oid->bv_len = len;
if (kmfber_read(ber, oid->bv_val, oid->bv_len) !=
(ber_slen_t)oid->bv_len)
return (KMFBER_DEFAULT);
return (tag);
}
ber_tag_t
ber_get_bigint(BerElement *ber, struct berval **bv)
{
ber_len_t len;
ber_tag_t tag;
if ((*bv = (struct berval *)malloc(sizeof (struct berval)))
== NULL) {
return (KMFBER_DEFAULT);
}
(*bv)->bv_len = 0;
(*bv)->bv_val = NULL;
if ((tag = kmfber_skip_tag(ber, &len)) != BER_INTEGER) {
return (KMFBER_DEFAULT);
}
if (((*bv)->bv_val = (char *)malloc((size_t)len + 1))
== NULL) {
return (KMFBER_DEFAULT);
}
/*
* len is being demoted to a long here -- possible conversion error
*/
if (kmfber_read(ber, (*bv)->bv_val, len) != (ber_slen_t)len)
return (KMFBER_DEFAULT);
(*bv)->bv_len = len;
/* If DER encoding, strip leading 0's if high-order bit is set */
if (ber->ber_options & KMFBER_OPT_USE_DER) {
char *p = (*bv)->bv_val;
while ((*p == 0x00) && ((*bv)->bv_len > 0) && (p[1] & 0x80)) {
p++;
(*bv)->bv_len--;
}
/*
* Shift the buffer to the beginning of the allocated space
* so it can be properly freed later.
*/
if ((p > (*bv)->bv_val) && ((*bv)->bv_len > 0))
(void) bcopy(p, (*bv)->bv_val, (*bv)->bv_len);
}
return (tag);
}
static ber_tag_t
kmfber_get_stringal(BerElement *ber, struct berval **bv)
{
ber_len_t len;
ber_tag_t tag;
if ((*bv = (struct berval *)malloc(sizeof (struct berval)))
== NULL) {
return (KMFBER_DEFAULT);
}
if ((tag = kmfber_skip_tag(ber, &len)) == KMFBER_DEFAULT) {
return (KMFBER_DEFAULT);
}
if (((*bv)->bv_val = (char *)malloc((size_t)len + 1))
== NULL) {
return (KMFBER_DEFAULT);
}
/*
* len is being demoted to a long here -- possible conversion error
*/
if (kmfber_read(ber, (*bv)->bv_val, len) != (ber_slen_t)len)
return (KMFBER_DEFAULT);
((*bv)->bv_val)[len] = '\0';
(*bv)->bv_len = len;
return (tag);
}
static ber_tag_t
kmfber_get_bitstringa(BerElement *ber, char **buf, ber_len_t *blen)
{
ber_len_t datalen;
ber_tag_t tag;
unsigned char unusedbits;
if ((tag = kmfber_skip_tag(ber, &datalen)) == KMFBER_DEFAULT)
return (KMFBER_DEFAULT);
if ((*buf = (char *)malloc((size_t)datalen - 1)) == NULL)
return (KMFBER_DEFAULT);
if (kmfber_read(ber, (char *)&unusedbits, 1) != 1)
return (KMFBER_DEFAULT);
/* Subtract 1 for the unused bits */
datalen--;
/*
* datalen is being demoted to a long here -- possible conversion error
*/
if (kmfber_read(ber, *buf, datalen) != (ber_slen_t)datalen)
return (KMFBER_DEFAULT);
*blen = datalen * 8 - unusedbits;
return (tag);
}
static ber_tag_t
kmfber_get_null(BerElement *ber)
{
ber_len_t len;
ber_tag_t tag;
if ((tag = kmfber_skip_tag(ber, &len)) == KMFBER_DEFAULT)
return (KMFBER_DEFAULT);
if (len != 0)
return (KMFBER_DEFAULT);
return (tag);
}
static ber_tag_t
kmfber_get_boolean(BerElement *ber, int *boolval)
{
ber_int_t longbool;
int rc;
rc = kmfber_get_int(ber, &longbool);
*boolval = longbool;
return (rc);
}
ber_tag_t
kmfber_first_element(BerElement *ber, ber_len_t *len, char **last)
{
/* skip the sequence header, use the len to mark where to stop */
if (kmfber_skip_tag(ber, len) == KMFBER_DEFAULT) {
return (KMFBER_ERROR);
}
*last = ber->ber_ptr + *len;
if (*last == ber->ber_ptr) {
return (KMFBER_END_OF_SEQORSET);
}
return (kmfber_peek_tag(ber, len));
}
ber_tag_t
kmfber_next_element(BerElement *ber, ber_len_t *len, char *last)
{
if (ber->ber_ptr == last) {
return (KMFBER_END_OF_SEQORSET);
}
return (kmfber_peek_tag(ber, len));
}
void
kmfber_bvfree(struct berval *bv)
{
if (bv != NULL) {
if (bv->bv_val != NULL) {
free(bv->bv_val);
}
free((char *)bv);
}
}
void
kmfber_bvecfree(struct berval **bv)
{
int i;
if (bv != NULL) {
for (i = 0; bv[i] != NULL; i++) {
kmfber_bvfree(bv[i]);
}
free((char *)bv);
}
}
/* VARARGS */
ber_tag_t
kmfber_scanf(BerElement *ber, const char *fmt, ...)
{
va_list ap;
char *last, *p;
char *s, **ss, ***sss;
struct berval ***bv, **bvp, *bval;
int *i, j;
ber_slen_t *l;
ber_int_t rc, tag, *b_int;
ber_tag_t *t;
ber_len_t len;
size_t array_size;
va_start(ap, fmt);
for (rc = 0, p = (char *)fmt; *p && rc != KMFBER_DEFAULT; p++) {
switch (*p) {
case 'a': /* octet string - allocate storage as needed */
ss = va_arg(ap, char **);
rc = kmfber_get_stringa(ber, ss);
break;
case 'b': /* boolean */
i = va_arg(ap, int *);
rc = kmfber_get_boolean(ber, i);
break;
case 'D': /* Object ID */
bval = va_arg(ap, struct berval *);
rc = ber_get_oid(ber, bval);
break;
case 'e': /* enumerated */
case 'i': /* int */
b_int = va_arg(ap, ber_int_t *);
rc = kmfber_get_int(ber, b_int);
break;
case 'l': /* length of next item */
l = va_arg(ap, ber_slen_t *);
rc = kmfber_peek_tag(ber, (ber_len_t *)l);
break;
case 'n': /* null */
rc = kmfber_get_null(ber);
break;
case 's': /* octet string - in a buffer */
s = va_arg(ap, char *);
l = va_arg(ap, ber_slen_t *);
rc = kmfber_get_stringb(ber, s, (ber_len_t *)l);
break;
case 'o': /* octet string in a supplied berval */
bval = va_arg(ap, struct berval *);
(void) kmfber_peek_tag(ber, &bval->bv_len);
rc = kmfber_get_stringa(ber, &bval->bv_val);
break;
case 'I': /* variable length Integer */
/* Treat INTEGER same as an OCTET string, but ignore the tag */
bvp = va_arg(ap, struct berval **);
rc = ber_get_bigint(ber, bvp);
break;
case 'O': /* octet string - allocate & include length */
bvp = va_arg(ap, struct berval **);
rc = kmfber_get_stringal(ber, bvp);
break;
case 'B': /* bit string - allocate storage as needed */
ss = va_arg(ap, char **);
l = va_arg(ap, ber_slen_t *); /* for length, in bits */
rc = kmfber_get_bitstringa(ber, ss, (ber_len_t *)l);
break;
case 't': /* tag of next item */
t = va_arg(ap, ber_tag_t *);
*t = kmfber_peek_tag(ber, &len);
rc = (ber_int_t)(*t);
break;
case 'T': /* skip tag of next item */
t = va_arg(ap, ber_tag_t *);
*t = kmfber_skip_tag(ber, &len);
rc = (ber_int_t)(*t);
break;
case 'v': /* sequence of strings */
sss = va_arg(ap, char ***);
if (sss == NULL)
break;
*sss = NULL;
j = 0;
array_size = 0;
for (tag = kmfber_first_element(ber, &len, &last);
(tag != KMFBER_DEFAULT &&
tag != KMFBER_END_OF_SEQORSET &&
rc != KMFBER_DEFAULT);
tag = kmfber_next_element(ber, &len, last)) {
if (*sss == NULL) {
/* Make room for at least 15 strings */
*sss = (char **)malloc(16 * sizeof (char *));
array_size = 16;
} else {
if ((size_t)(j+2) > array_size) {
/* We'v overflowed our buffer */
*sss = (char **)realloc(*sss,
(array_size * 2) * sizeof (char *));
array_size = array_size * 2;
}
}
rc = kmfber_get_stringa(ber, &((*sss)[j]));
j++;
}
if (rc != KMFBER_DEFAULT && tag != KMFBER_END_OF_SEQORSET) {
rc = KMFBER_DEFAULT;
}
if (j > 0)
(*sss)[j] = NULL;
break;
case 'V': /* sequence of strings + lengths */
bv = va_arg(ap, struct berval ***);
*bv = NULL;
j = 0;
for (tag = kmfber_first_element(ber, &len, &last);
(tag != KMFBER_DEFAULT &&
tag != KMFBER_END_OF_SEQORSET &&
rc != KMFBER_DEFAULT);
tag = kmfber_next_element(ber, &len, last)) {
if (*bv == NULL) {
*bv = (struct berval **)malloc(
2 * sizeof (struct berval *));
} else {
*bv = (struct berval **)realloc(*bv,
(j + 2) * sizeof (struct berval *));
}
rc = kmfber_get_stringal(ber, &((*bv)[j]));
j++;
}
if (rc != KMFBER_DEFAULT &&
tag != KMFBER_END_OF_SEQORSET) {
rc = KMFBER_DEFAULT;
}
if (j > 0)
(*bv)[j] = NULL;
break;
case 'x': /* skip the next element - whatever it is */
if ((rc = kmfber_skip_tag(ber, &len)) == KMFBER_DEFAULT)
break;
ber->ber_ptr += len;
break;
case '{': /* begin sequence */
case '[': /* begin set */
if (*(p + 1) != 'v' && *(p + 1) != 'V')
rc = kmfber_skip_tag(ber, &len);
break;
case '}': /* end sequence */
case ']': /* end set */
break;
default:
rc = KMFBER_DEFAULT;
break;
}
}
va_end(ap);
if (rc == KMFBER_DEFAULT) {
va_start(ap, fmt);
for (p--; fmt < p && *fmt; fmt++) {
switch (*fmt) {
case 'a': /* octet string - allocate storage as needed */
ss = va_arg(ap, char **);
if (ss != NULL && *ss != NULL) {
free(*ss);
*ss = NULL;
}
break;
case 'b': /* boolean */
i = va_arg(ap, int *);
break;
case 'e': /* enumerated */
case 'i': /* int */
l = va_arg(ap, ber_slen_t *);
break;
case 'l': /* length of next item */
l = va_arg(ap, ber_slen_t *);
break;
case 'n': /* null */
break;
case 's': /* octet string - in a buffer */
s = va_arg(ap, char *);
l = va_arg(ap, ber_slen_t *);
break;
case 'o': /* octet string in a supplied berval */
bval = va_arg(ap, struct berval *);
if (bval->bv_val) free(bval->bv_val);
(void) memset(bval, 0, sizeof (struct berval));
break;
case 'O': /* octet string - allocate & include length */
bvp = va_arg(ap, struct berval **);
kmfber_bvfree(*bvp);
bvp = NULL;
break;
case 'B': /* bit string - allocate storage as needed */
ss = va_arg(ap, char **);
l = va_arg(ap, ber_slen_t *); /* for length, in bits */
if (ss != NULL && *ss != NULL) {
free(*ss);
*ss = NULL;
}
break;
case 't': /* tag of next item */
t = va_arg(ap, ber_tag_t *);
break;
case 'T': /* skip tag of next item */
t = va_arg(ap, ber_tag_t *);
break;
case 'v': /* sequence of strings */
sss = va_arg(ap, char ***);
if (sss != NULL && *sss != NULL) {
ber_svecfree(*sss);
*sss = NULL;
}
break;
case 'V': /* sequence of strings + lengths */
bv = va_arg(ap, struct berval ***);
kmfber_bvecfree(*bv);
*bv = NULL;
break;
case 'x': /* skip the next element - whatever it is */
break;
case '{': /* begin sequence */
case '[': /* begin set */
break;
case '}': /* end sequence */
case ']': /* end set */
break;
default:
break;
}
} /* for */
va_end(ap);
} /* if */
return (rc);
}
struct berval *
kmfber_bvdup(const struct berval *bv)
{
struct berval *new;
if ((new = (struct berval *)malloc(sizeof (struct berval)))
== NULL) {
return (NULL);
}
if (bv->bv_val == NULL) {
new->bv_val = NULL;
new->bv_len = 0;
} else {
if ((new->bv_val = (char *)malloc(bv->bv_len + 1))
== NULL) {
return (NULL);
}
(void) memmove(new->bv_val, bv->bv_val, (size_t)bv->bv_len);
new->bv_val[bv->bv_len] = '\0';
new->bv_len = bv->bv_len;
}
return (new);
}