#include <sys/types.h>
#include <netinet/in.h>
#include <inttypes.h>
#include <ber_der.h>
#include "kmfber_int.h"
#define LENMASK1 0xFF
#define LENMASK2 0xFFFF
#define LENMASK3 0xFFFFFF
#define LENMASK4 0xFFFFFFFF
#define _MASK 0x80
int
kmfber_calc_taglen(ber_tag_t tag)
{
int i;
ber_int_t mask;
for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
mask = (LENMASK3 << (i * 8));
if (tag & mask)
break;
}
return (i + 1);
}
static int
ber_put_tag(BerElement *ber, ber_tag_t tag, int nosos)
{
ber_int_t taglen;
ber_tag_t ntag;
taglen = kmfber_calc_taglen(tag);
ntag = htonl(tag);
return (kmfber_write(ber,
((char *) &ntag) + sizeof (ber_int_t) - taglen,
taglen, nosos));
}
int
kmfber_calc_lenlen(ber_int_t len)
{
if (len <= 0x7F)
return (1);
if (len <= LENMASK1)
return (2);
if (len <= LENMASK2)
return (3);
if (len <= LENMASK3)
return (4);
return (5);
}
int
kmfber_put_len(BerElement *ber, ber_int_t len, int nosos)
{
int i;
char lenlen;
ber_int_t mask, netlen;
if (len <= 127) {
netlen = htonl(len);
return (kmfber_write(ber,
(char *)&netlen + sizeof (ber_int_t) - 1,
1, nosos));
}
for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
mask = (LENMASK1 << (i * 8));
if (len & mask)
break;
}
lenlen = ++i;
if (lenlen > 4)
return (-1);
lenlen |= 0x80;
if (kmfber_write(ber, &lenlen, 1, nosos) != 1)
return (-1);
netlen = htonl(len);
if (kmfber_write(ber,
(char *) &netlen + (sizeof (ber_int_t) - i), i, nosos) != i)
return (-1);
return (i + 1);
}
static int
ber_put_int_or_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
{
int i, sign;
ber_int_t len, lenlen, taglen, netnum, mask;
sign = (num < 0);
for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
mask = (LENMASK1 << (i * 8));
if (sign) {
if ((num & mask) != mask)
break;
} else {
if (num & mask)
break;
}
}
mask = (num & (_MASK << (i * 8)));
if ((mask && !sign) || (sign && !mask))
i++;
len = i + 1;
if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
return (-1);
if ((lenlen = kmfber_put_len(ber, len, 0)) == -1)
return (-1);
i++;
netnum = htonl(num);
if (kmfber_write(ber,
(char *) &netnum + (sizeof (ber_int_t) - i), i, 0) == i)
return (taglen + lenlen + i);
return (-1);
}
static int
kmfber_put_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
{
if (tag == KMFBER_DEFAULT)
tag = BER_ENUMERATED;
return (ber_put_int_or_enum(ber, num, tag));
}
int
ber_put_int(BerElement *ber, ber_int_t num, ber_tag_t tag)
{
if (tag == KMFBER_DEFAULT)
tag = BER_INTEGER;
return (ber_put_int_or_enum(ber, num, tag));
}
int
ber_put_oid(BerElement *ber, struct berval *oid, ber_tag_t tag)
{
ber_int_t taglen, lenlen, rc, len;
if (tag == KMFBER_DEFAULT)
tag = 0x06;
if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
return (-1);
len = (ber_int_t)oid->bv_len;
if ((lenlen = kmfber_put_len(ber, len, 0)) == -1 ||
kmfber_write(ber, oid->bv_val, oid->bv_len, 0) !=
(ber_int_t)oid->bv_len) {
rc = -1;
} else {
rc = taglen + lenlen + oid->bv_len;
}
return (rc);
}
int
ber_put_big_int(BerElement *ber, ber_tag_t tag, char *data,
ber_len_t len)
{
ber_int_t taglen, lenlen, ilen, rc;
char zero = 0x00;
if (tag == KMFBER_DEFAULT)
tag = BER_INTEGER;
if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
return (-1);
if (data[0] & 0x80)
len++;
ilen = (ber_int_t)len;
if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1)
return (-1);
if ((data[0] & 0x80) && kmfber_write(ber, &zero, 1, 0) != 1)
return (-1);
if (data[0] & 0x80)
ilen = len - 1;
if (kmfber_write(ber, data, ilen, 0) != (ber_int_t)ilen) {
return (-1);
} else {
rc = taglen + lenlen + len;
}
return (rc);
}
static int
kmfber_put_ostring(BerElement *ber, char *str, ber_len_t len,
ber_tag_t tag)
{
ber_int_t taglen, lenlen, ilen, rc;
#ifdef STR_TRANSLATION
int free_str;
#endif
if (tag == KMFBER_DEFAULT)
tag = BER_OCTET_STRING;
if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
return (-1);
#ifdef STR_TRANSLATION
if (len > 0 && (ber->ber_options & KMFBER_OPT_TRANSLATE_STRINGS) != 0 &&
ber->ber_encode_translate_proc != NULL) {
if ((*(ber->ber_encode_translate_proc))(&str, &len, 0)
!= 0) {
return (-1);
}
free_str = 1;
} else {
free_str = 0;
}
#endif
ilen = (ber_int_t)len;
if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 ||
kmfber_write(ber, str, len, 0) != (ber_int_t)len) {
rc = -1;
} else {
rc = taglen + lenlen + len;
}
#ifdef STR_TRANSLATION
if (free_str) {
free(str);
}
#endif
return (rc);
}
static int
kmfber_put_string(BerElement *ber, char *str, ber_tag_t tag)
{
return (kmfber_put_ostring(ber, str, (ber_len_t)strlen(str), tag));
}
static int
kmfber_put_bitstring(BerElement *ber, char *str,
ber_len_t blen , ber_tag_t tag)
{
ber_int_t taglen, lenlen, len;
unsigned char unusedbits;
if (tag == KMFBER_DEFAULT)
tag = BER_BIT_STRING;
if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
return (-1);
len = (blen + 7) / 8;
unusedbits = (unsigned char) (len * 8 - blen);
if ((lenlen = kmfber_put_len(ber, len + 1, 0)) == -1)
return (-1);
if (kmfber_write(ber, (char *)&unusedbits, 1, 0) != 1)
return (-1);
if (kmfber_write(ber, str, len, 0) != len)
return (-1);
return (taglen + 1 + lenlen + len);
}
static int
kmfber_put_null(BerElement *ber, ber_tag_t tag)
{
int taglen;
if (tag == KMFBER_DEFAULT)
tag = BER_NULL;
if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
return (-1);
if (kmfber_put_len(ber, 0, 0) != 1)
return (-1);
return (taglen + 1);
}
static int
kmfber_put_boolean(BerElement *ber, int boolval, ber_tag_t tag)
{
int taglen;
unsigned char trueval = 0xff;
unsigned char falseval = 0x00;
if (tag == KMFBER_DEFAULT)
tag = BER_BOOLEAN;
if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
return (-1);
if (kmfber_put_len(ber, 1, 0) != 1)
return (-1);
if (kmfber_write(ber, (char *)(boolval ? &trueval : &falseval), 1, 0)
!= 1)
return (-1);
return (taglen + 2);
}
#define FOUR_BYTE_LEN 5
static int
ber_start_seqorset(BerElement *ber, ber_tag_t tag)
{
Seqorset *new_sos;
if (ber->ber_sos_stack_posn < SOS_STACK_SIZE) {
new_sos = &ber->ber_sos_stack[ber->ber_sos_stack_posn];
} else {
if ((new_sos = (Seqorset *)malloc(sizeof (Seqorset)))
== NULLSEQORSET) {
return (-1);
}
}
ber->ber_sos_stack_posn++;
if (ber->ber_sos == NULLSEQORSET)
new_sos->sos_first = ber->ber_ptr;
else
new_sos->sos_first = ber->ber_sos->sos_ptr;
new_sos->sos_ptr = new_sos->sos_first + kmfber_calc_taglen(tag) +
FOUR_BYTE_LEN;
new_sos->sos_tag = tag;
new_sos->sos_next = ber->ber_sos;
new_sos->sos_clen = 0;
ber->ber_sos = new_sos;
if (ber->ber_sos->sos_ptr > ber->ber_end) {
if (kmfber_realloc(ber, ber->ber_sos->sos_ptr -
ber->ber_end) != 0)
return (-1);
}
return (0);
}
static int
kmfber_start_seq(BerElement *ber, ber_tag_t tag)
{
if (tag == KMFBER_DEFAULT)
tag = BER_CONSTRUCTED_SEQUENCE;
return (ber_start_seqorset(ber, tag));
}
static int
kmfber_start_set(BerElement *ber, ber_tag_t tag)
{
if (tag == KMFBER_DEFAULT)
tag = BER_CONSTRUCTED_SET;
return (ber_start_seqorset(ber, tag));
}
static int
ber_put_seqorset(BerElement *ber)
{
ber_int_t netlen, len, taglen, lenlen;
unsigned char ltag = 0x80 + FOUR_BYTE_LEN - 1;
Seqorset *next;
Seqorset **sos = &ber->ber_sos;
len = (*sos)->sos_clen;
netlen = (ber_len_t)htonl(len);
if (ber->ber_options & KMFBER_OPT_USE_DER) {
lenlen = kmfber_calc_lenlen(len);
} else {
lenlen = FOUR_BYTE_LEN;
}
if ((next = (*sos)->sos_next) == NULLSEQORSET) {
if ((taglen = ber_put_tag(ber, (*sos)->sos_tag, 1)) == -1)
return (-1);
if (ber->ber_options & KMFBER_OPT_USE_DER) {
if (kmfber_put_len(ber, len, 1) == -1)
return (-1);
if (lenlen != FOUR_BYTE_LEN) {
(void) memmove((*sos)->sos_first + taglen +
lenlen, (*sos)->sos_first + taglen +
FOUR_BYTE_LEN, len);
}
} else {
if (kmfber_write(ber, (char *)<ag, 1, 1) != 1)
return (-1);
if (kmfber_write(ber,
(char *)&netlen + sizeof (ber_int_t)
- (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1, 1) !=
FOUR_BYTE_LEN - 1)
return (-1);
}
ber->ber_ptr += len;
} else {
ber_tag_t ntag;
taglen = kmfber_calc_taglen((*sos)->sos_tag);
ntag = htonl((*sos)->sos_tag);
(void) memmove((*sos)->sos_first, (char *)&ntag +
sizeof (ber_int_t) - taglen, taglen);
if (ber->ber_options & KMFBER_OPT_USE_DER) {
ltag = (lenlen == 1) ? (unsigned char)len :
(unsigned char) (0x80 + (lenlen - 1));
}
(void) memmove((*sos)->sos_first + 1, <ag, 1);
if (ber->ber_options & KMFBER_OPT_USE_DER) {
if (lenlen > 1) {
(void) memmove((*sos)->sos_first + 2,
(char *)&netlen + sizeof (ber_uint_t) -
(lenlen - 1),
lenlen - 1);
}
if (lenlen != FOUR_BYTE_LEN) {
(void) memmove((*sos)->sos_first + taglen +
lenlen, (*sos)->sos_first + taglen +
FOUR_BYTE_LEN, len);
}
} else {
(void) memmove((*sos)->sos_first + taglen + 1,
(char *) &netlen + sizeof (ber_int_t) -
(FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1);
}
next->sos_clen += (taglen + lenlen + len);
next->sos_ptr += (taglen + lenlen + len);
}
if (ber->ber_sos_stack_posn > SOS_STACK_SIZE) {
free((char *)(*sos));
}
ber->ber_sos_stack_posn--;
*sos = next;
return (taglen + lenlen + len);
}
int
kmfber_printf(BerElement *ber, const char *fmt, ...)
{
va_list ap;
char *s, **ss;
struct berval **bv, *oid;
int rc, i, t;
ber_int_t len;
va_start(ap, fmt);
#ifdef KMFBER_DEBUG
if (lber_debug & 64) {
char msg[80];
sprintf(msg, "kmfber_printf fmt (%s)\n", fmt);
ber_err_print(msg);
}
#endif
for (rc = 0; *fmt && rc != -1; fmt++) {
switch (*fmt) {
case 'b':
i = va_arg(ap, int);
rc = kmfber_put_boolean(ber, i, ber->ber_tag);
break;
case 'i':
i = va_arg(ap, int);
rc = ber_put_int(ber, (ber_int_t)i, ber->ber_tag);
break;
case 'D':
if ((oid = va_arg(ap, struct berval *)) == NULL)
break;
rc = ber_put_oid(ber, oid, ber->ber_tag);
break;
case 'I':
s = va_arg(ap, char *);
len = va_arg(ap, ber_int_t);
rc = ber_put_big_int(ber, ber->ber_tag, s, len);
break;
case 'e':
i = va_arg(ap, int);
rc = kmfber_put_enum(ber, (ber_int_t)i, ber->ber_tag);
break;
case 'l':
t = va_arg(ap, int);
rc = kmfber_put_len(ber, t, 0);
break;
case 'n':
rc = kmfber_put_null(ber, ber->ber_tag);
break;
case 'o':
s = va_arg(ap, char *);
len = va_arg(ap, int);
rc = kmfber_put_ostring(ber, s, len, ber->ber_tag);
break;
case 's':
s = va_arg(ap, char *);
rc = kmfber_put_string(ber, s, ber->ber_tag);
break;
case 'B':
s = va_arg(ap, char *);
len = va_arg(ap, int);
rc = kmfber_put_bitstring(ber, s, len, ber->ber_tag);
break;
case 't':
ber->ber_tag = va_arg(ap, ber_tag_t);
ber->ber_usertag = 1;
break;
case 'T':
t = va_arg(ap, int);
rc = ber_put_tag(ber, t, 0);
break;
case 'v':
if ((ss = va_arg(ap, char **)) == NULL)
break;
for (i = 0; ss[i] != NULL; i++) {
if ((rc = kmfber_put_string(ber, ss[i],
ber->ber_tag)) == -1)
break;
}
break;
case 'V':
if ((bv = va_arg(ap, struct berval **)) == NULL)
break;
for (i = 0; bv[i] != NULL; i++) {
if ((rc = kmfber_put_ostring(ber, bv[i]->bv_val,
bv[i]->bv_len, ber->ber_tag)) == -1)
break;
}
break;
case '{':
rc = kmfber_start_seq(ber, ber->ber_tag);
break;
case '}':
rc = ber_put_seqorset(ber);
break;
case '[':
rc = kmfber_start_set(ber, ber->ber_tag);
break;
case ']':
rc = ber_put_seqorset(ber);
break;
default: {
#ifdef KMFBER_DEBUG
char msg[80];
sprintf(msg, "unknown fmt %c\n", *fmt);
ber_err_print(msg);
#endif
rc = -1;
break;
}
}
if (ber->ber_usertag == 0)
ber->ber_tag = KMFBER_DEFAULT;
else
ber->ber_usertag = 0;
}
va_end(ap);
return (rc);
}