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