/*
** @(#) ber.c - (for RDP) ASN.1 BER implementation
** @(#) $Id: ber.c,v 1.8 2003/12/04 11:31:42 lucio Exp $
*/
/*
** ==================================================================
**
** $Logfile:$
** $RCSfile: ber.c,v $
** $Revision: 1.8 $
** $Date: 2003/12/04 11:31:42 $
** $Author: lucio $
**
** ==================================================================
**
** $Log: ber.c,v $
** Revision 1.8 2003/12/04 11:31:42 lucio
** Streamlined - specially OID management
**
** Revision 1.7 2003/11/30 19:01:12 lucio
** Advanced - plenty to go still, of course.
**
** Revision 1.6 2003/11/27 18:36:27 lucio
** Checkpoint - some progress with DNs
**
** Revision 1.5 2003/11/26 16:33:31 lucio
** Checkpoint - unworkable
**
** Revision 1.4 2003/11/25 14:33:16 lucio
** Checkpoint to take home.
**
** Revision 1.3 2003/11/25 08:52:43 lucio
** On return from home
**
** Revision 1.2 2003/11/24 17:43:14 lucio
** Checkpoint after some progress
**
** Revision 1.1.1.1 2003/11/10 10:34:00 lucio
** ASN.1 developments.
**
** ==================================================================
*/
/*
TODO:
ber_free () fails on some parsed objects (see pemout.c)
*/
#include <u.h>
#include <libc.h>
#include <stdio.h>
#include <ctype.h>
#include <mp.h>
#include "ber.h"
long strftime (char *s, long maxsize, const char *format, const Tm *t);
int
ber_seal (BerObj *op) {
uchar buf[BERSZ], *bp = buf, *bpp;
int len, temp, count, size;
BerObj *opd;
if (op->size == -1) // invalid object
return -1;
if (op->size == 0) // already sealed
return op->len;
opd = op->down;
*bp = opd ? 0x20 : 0x00;
while (opd) {
if ((len = ber_seal (opd)) < 0)
return -1;
size = op->len + len;
if (!(op->buf = realloc (op->buf, size)))
return -1;
bpp = op->buf + op->len;
memcpy (bpp, opd->buf, len);
free (opd->buf);
opd->buf = bpp;
opd->size = 0;
opd = opd->next;
op->size = size;
}
if (op->tag > 0x1E) {
*bp++ |= 0x1F;
for (temp = op->tag; temp > 0x7F; temp >>= 7)
++bp;
temp = bp - buf;
*bp-- = op->tag & 0x7F;
while ((op->tag >>= 7) > 0)
*bp-- = 0x80 | (op->tag & 0x7F);
bp = buf + temp + 1;
} else {
*bp++ |= op->tag;
}
if (op->len >= 0x80) {
*bp++ = 0x80;
for (temp = op->len, count = 0; temp > 0; temp >>= 8, ++count)
++bp;
len = bp - buf;
for (temp = op->len; temp > 0; temp >>= 8)
*--bp = temp & 0xFF;
*--bp |= count;
} else {
*bp++ = op->len;
len = bp - buf;
}
if (!(bp = realloc (op->buf, op->len + len))) {
return -1;
}
op->buf = bp;
memmove (op->buf + len, op->buf, op->len);
memcpy (op->buf, buf, len);
op->len += len;
op->size = 0; // seal the object
return op->len;
}
BerObj *
ber_simple (int tag, uchar *value, int len) {
BerObj *op = malloc (sizeof (BerObj));
if (!op)
return nil;
op->next = op->down = nil;
if (!(op->buf = malloc (len))) {
free (op);
op = nil;
}
op->tag = tag;
op->len = len;
memcpy (op->buf, value, len);
return op;
}
BerObj *
ber_int2 (int val) {
BerObj *op = malloc (sizeof (BerObj));
if (!op)
return nil;
op->next = op->down = nil;
if (!(op->buf = malloc (op->len = op->size = 2))) {
free (op);
op = nil;
}
op->tag = BER_TAG_INTEGER;
op->buf[0] = (val >> 8) & 0xFF;
op->buf[1] = val & 0xFF;
return op;
}
BerObj *
ber_int4 (long val) {
BerObj *op = malloc (sizeof (BerObj));
if (!op)
return nil;
op->next = op->down = nil;
if (!(op->buf = malloc (op->len = op->size = 4))) {
free (op);
op = nil;
}
op->tag = BER_TAG_INTEGER;
op->buf[3] = val & 0xFF;
op->buf[2] = (val >> 8) & 0xFF;
op->buf[1] = (val >> 16) & 0xFF;
op->buf[0] = (val >> 24) & 0xFF;
return op;
}
BerObj *
ber_mpint (mpint *val) {
BerObj *op = malloc (sizeof (BerObj));
uchar *bp;
mpint *mp0 = mpcopy (val), mp1, *mp256 = itomp (256, nil);
if (!op)
return nil;
op->next = op->down = nil;
if (!(op->buf = malloc (op->size = (BERSZ > val->top ? BERSZ : val->top)))) {
free (op);
op = nil;
}
op->tag = BER_TAG_INTEGER;
bp = op->buf + BERSZ;
// do something about the sign
while (mpcmp (mp0, mpzero) != 0) {
mpmod (val, mp256, &mp1);
*--bp = mptoi (&mp1);
mpright (mp0, 8, mp0);
}
mpfree (mp0);
mpfree (mp256);
return op;
}
BerObj *
ber_bstr (uchar *val, int len) {
BerObj *op = malloc (sizeof (BerObj));
uchar *bp;
if (!op)
return nil;
op->next = op->down = nil;
if (!(op->buf = malloc (op->len = op->size = (len >> 3) + 1))) {
free (op);
op = nil;
}
op->tag = BER_TAG_BITSTRING;
bp = op->buf;
*bp++ = len & 0x7;
memcpy (bp, val, len >> 3);
return op;
}
BerObj *
ber_ostr (uchar *val, int len) {
BerObj *op = malloc (sizeof (BerObj));
if (!op)
return nil;
op->next = op->down = nil;
if (!(op->buf = malloc (op->len = op->size = len))) {
free (op);
op = nil;
}
op->tag = BER_TAG_OCTETSTRING;
memcpy (op->buf, val, len);
return op;
}
BerObj *
ber_bool (int val) {
BerObj *op = malloc (sizeof (BerObj));
if (!op)
return nil;
op->next = op->down = nil;
if (!(op->buf = malloc (op->len = op->size = 1))) {
free (op);
op = nil;
}
op->tag = BER_TAG_BOOLEAN;
op->buf[0] = val & 0xFF;
return op;
}
static int
ber_packobjid (uchar *bp0, uchar *bp, long long v) {
int l = 0;
while (v >= 128) {
*--bp = v & 0x7F;
if (l++)
*bp |= 0x80;
v >>= 7;
}
*--bp = v;
if (l++)
*bp |= 0x80;
memmove (bp0, bp, l);
return l;
}
/*
translate a string in the form a.bb.ccc.dd.... (where the
fields are decimal values) to an OBJECT ID primitive object.
Special treatment of the first two fields and weird numeric
representation all included. The input string must be NUL
terminated.
*/
BerObj *
ber_objid (char *val) {
BerObj *op = malloc (sizeof (BerObj));
long long v;
char *p;
uchar *bp, *bpe;
if (!op)
return nil;
op->tag = BER_TAG_OBJECTID;
op->len = 0;
op->next = op->down = op->id = nil;
if (!(op->buf = malloc (op->size = BERSZ))) {
free (op);
return nil;
}
bp = op->buf;
bpe = bp + BERSZ;
v = strtoll (val, &p, 10) * 40;
if (*p == '.') {
v += strtoll (++p, &p, 10);
}
while (*p == '.') {
bp += ber_packobjid (bp, bpe, v);
v = strtoll (++p, &p, 10);
}
bp += ber_packobjid (bp, bpe, v);
op->len = bp - op->buf;
return op;
}
/*
Create an empty object. For some reason we allocate an arbitrary buffer,
probably as a precaution. We shouldn't, in my new opinion.
*/
BerObj *
ber_init (int tag) {
BerObj *op = malloc (sizeof (BerObj));
if (op) {
op->tag = tag;
op->len = 0;
op->next = op->down = op->id = nil;
if (!(op->buf = malloc (op->size = BERSZ))) {
free (op);
op = nil;
}
}
return op;
}
/*
Attach to an existing object. The first attaches to op->down, others
append to op-down->next->next...->next at the first empty slot.
*/
BerObj *
ber_attach (BerObj *op, BerObj *comp) {
BerObj *opd, *opn;
if (op->size < 0) { // we don't attach to sealed objects
return nil;
}
if (!(opd = op->down)) {
op->down = comp;
} else {
while (opn = opd->next)
opd = opn;
opd->next = comp;
}
return op;
}
BerObj *
ber_parse (uchar *pkt, uchar *endp) {
uchar *bp = pkt;
int temp, x;
BerObj *op = malloc (sizeof (BerObj)), *opd, *opn;
if (!op)
return nil;
op->next = op->id = nil;
op->tag = *bp++ & 0x1F;
if (op->tag == 0x1F) {
op->tag = 0;
do {
temp = *bp++;
op->tag <<= 7;
op->tag += temp & 0x7F;
} while ((temp & 0x80) == 0x80);
}
op->len = *bp++ & 0xFF;
if (op->len & 0x80) {
op->len &= ~0x80;
if (op->len > 0) {
temp = op->len;
op->len = 0;
for (x = 0; x < temp; x++) {
op->len <<= 8;
op->len += *bp++ & 0xFF;
}
} else {
op->len = -1;
}
}
op->buf = bp;
op->size = 0;
if (pkt[0] & 0x20
/* -- fudges --
|| (pkt[0] == 4 && (pkt[3] == '0' || pkt[3] == '1' || pkt[4] == '0' || pkt[4] == '1'))
|| (pkt[0] == 3 && (pkt[5] == '0' || pkt[5] == '1'))
*/
) { // constructed record
op->down = opd = ber_parse (bp, endp);
switch (opd->tag) {
case BER_TAG_OBJECTID:
/*
The idea here is to thread objects that have their own ID
together into a separate, distinct linked list. At this
point it's not clear what the relationship between such
objects ought to be, although a parent/child/sibling will
probably be quite adequate. We then just need to add fields
to the object data structure and ensure that it is NULL for
unidentified objects. I'm also not yet comfortable with
objects and their relationship with their ID, perhaps the
object to be flagged is the parent of the OBJECT_ID object,
which makes the relationship marginally more complex.
*/
op->id = opd;
break;
}
bp = opd->buf + opd->size;
if (op->len == -1) {
while (bp < endp && (bp[0] != 0 || bp[1] != 0)) {
opd->next = opn = ber_parse (bp, endp);
bp = opn->buf + opn->size;
opd = opn;
}
op->len = bp - op->buf;
op->size = 2;
} else {
while (bp < op->buf + op->len) {
opd->next = opn = ber_parse (bp, endp);
bp = opn->buf + opn->size;
opd = opn;
}
}
op->size += op->len;
opd->next = nil;
} else {
op->down = nil;
if (op->len == -1) {
while (bp < endp && (bp[0] != 0 || bp[1] != 0))
++bp;
op->len = bp - op->buf;
op->size = 2;
}
op->size += op->len;
}
return op;
}
static void
ber_show (BerObj *op, int depth) {
int c, s;
long long v = 0;
uchar *p, *pe;
print ("%*s", depth * 4, "");
for (s = 0; s < op->size; s++) {
c = op->buf[s] & 0xFF;
if (isprint (c))
print (" %c", c);
else
print (" %#2.2x", c);
}
print ("\n");
switch (op->tag) {
case 0: // unknown
print ("%*s\n", depth * 4 + 2, "--");
break;
case BER_TAG_INTEGER:
v = 0;
p = op->buf;
pe = p + op->len;
print ("%*s%s", depth * 4, "", " Value = ");
while (p < pe) {
v = (v << 8) | (*p++ & 0xFF);
}
print ("%lld\n", v);
break;
case BER_TAG_OBJECTID:
v = 0;
s = 0;
p = op->buf;
pe = p + op->len;
print ("%*s%s", depth * 4, "", " Value = ");
while (p < pe) {
v = (v << 7) | (*p & 0x7F);
if (!(*p++ & 0x80)) {
if (s++)
print (".%lld", v);
else
print ("%d.%d", (int) (v / 40), (int) (v % 40));
v = 0;
}
}
if (v)
print ("%lld?", v);
print ("\n");
break;
}
}
void
ber_print (BerObj *op, int depth) {
BerObj *opd;
if (!op)
return;
if (opd = op->down) { // constructed record
print ("%*s tag = %d - len = %d - size = %d\n", depth * 4, "", op->tag, op->len, op->size);
ber_print (opd, ++depth);
while (opd = opd->next) {
ber_print (opd, depth);
}
} else {
print ("%*s tag = %d - len = %d - size = %d\n", depth * 4, "", op->tag, op->len, op->size);
ber_show (op, depth);
}
return;
}
/*
** Format an integer irrespective of size
*/
int
ber_fmtint (Fmt *f) {
long long v = 0;
char q[BERSZ];
BerObj *op;
uchar *p, *pe;
if ((op = va_arg (f->args, BerObj *)) == nil)
return -1;
p = op->buf;
pe = p + op->len;
if (op->len < 8) {
while (p < pe) {
v = (v << 8) | (*p++ & 0xFF);
}
snprint (q, BERSZ - 1, "%lld", v);
} else {
snprint (q, BERSZ - 1, "*Unsupported*"); // requires MPs
}
return fmtprint (f, q);
}
/*
** Extension to print(2) for Validity time limits.
*/
int
ber_fmtdate (Fmt *f) {
char q[BERSZ];
BerObj *obj;
Tm tm;
// int l;
if ((obj = va_arg (f->args, BerObj *)) == nil)
return -1;
// l = snprint (q, sizeof (q), "%.*s", obj->len, obj->buf);
if (sscanf ((char *) obj->buf, "%2d%2d%2d%2d%2d%2dZ", &tm.year, &tm.mon, &tm.mday,
&tm.hour, &tm.min, &tm.sec) == 6) {
if (tm.year < 49)
tm.year += 100;
strftime (q, sizeof (q), "%Y-%m-%d %H:%M:%S", &tm);
}
return fmtprint (f, q);
}
/*
** Extension to print(2) for OIDs.
*/
int
ber_fmtoid (Fmt *f) {
long long v = 0;
int l = 0, s = 0;
char q[BERSZ];
BerObj *id;
uchar *p, *pe;
if ((id = va_arg (f->args, BerObj *)) == nil)
return -1;
p = id->buf;
pe = p + id->len;
while (p < pe) {
v = (v << 7) | (*p & 0x7F);
if (!(*p++ & 0x80)) {
if (s++) {
l += snprint (q + l, BERSZ - l, ".%lld", v);
} else {
l += snprint (q + l, BERSZ - l, "%d.%d", (int) (v / 40), (int) (v % 40));
}
v = 0;
}
}
if (v)
snprint (q + l, BERSZ - l, "%lld?", v);
return fmtprint (f, q);
}
/* The two procedures immediately below should probably be used to generate strings */
/*
** ber_proid() - display OID in component form
*/
char *
ber_proid (BerObj *id) {
static char buf[256];
long long v = 0;
int s = 0;
uchar *p = id->buf;
uchar *pe = p + id->len;
char *bp = buf, *bpe = buf + sizeof (buf) - 1;
while (p < pe) {
v = (v << 7) | (*p & 0x7F);
if (!(*p++ & 0x80)) {
if (s++) {
bp = seprint (bp, bpe, ".%lld", v);
} else {
bp = seprint (bp, bpe, "%d.%d", (int) (v / 40), (int) (v % 40));
}
v = 0;
}
}
if (v) {
bp = seprint (bp, bpe, "%lld?", v);
}
*bp = '\0';
return buf;
}
/*
** ber_objs() - display all OIDS within an object, nested
*/
void
ber_objs (BerObj *op, int depth) {
BerObj *opd;
if (!op)
return;
if (opd = op->down) { // constructed record
if (op->id) {
print ("%*s tag = %d - len = %d - size = %d - OID = ", depth * 4, "", op->tag, op->len, op->size);
print ("%s\n", ber_proid (op->id));
}
ber_objs (opd, ++depth);
while (opd = opd->next) {
ber_objs (opd, depth);
}
}
return;
}
void
ber_free (BerObj *op) {
BerObj *opd, *opn;
if (op->size > -1) { // not canonical
if (opd = op->down) {
for (opn = opd->next; opn != nil; opn = opn->next) {
ber_free (opn);
}
ber_free (opd);
}
}
free (op->buf);
free (op);
}
|