Plan 9 from Bell Labs’s /usr/web/sources/contrib/lucio/asn1/devel/ber.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
**	@(#) 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);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.