#include #include #include "ber.h" void * emalloc(int n) { void *p; if(n==0) n=1; p = malloc(n); if(p == nil){ fprint(2,"out of memory"); abort(); exits("out of memory"); } memset(p, 0, n); setmalloctag(p, getcallerpc(&n)); return p; } char* estrdup(char *s) { char *d, *d0; if(!s) return 0; d0 = emalloc(strlen(s)+1); d = d0; while(*d++ = *s++) ; setmalloctag(d0,getcallerpc(s)); return d0; } /* * Decode a[0..len] as a BER encoding of an ASN1 type. * The return value is one of ASN_OK, etc. * Depending on the error, the returned elem may or may not * be nil. */ int decode(uchar* a, int alen, Elem* pelem) { uchar* p = a; return ber_decode(&p, &a[alen], pelem); } /* * Like decode, but continue decoding after first element * of array ends. */ int decode_seq(uchar* a, int alen, Elist** pelist) { uchar* p = a; return seq_decode(&p, &a[alen], -1, 1, pelist); } /* * Decode the whole array as a BER encoding of an ASN1 value, * (i.e., the part after the tag and length). * Assume the value is encoded as universal tag "kind". * The constr arg is 1 if the value is constructed, 0 if primitive. * If there's an error, the return string will contain the error. * Depending on the error, the returned value may or may not * be nil. */ int decode_value(uchar* a, int alen, int kind, int isconstr, Value* pval) { uchar* p = a; return value_decode(&p, &a[alen], alen, kind, isconstr, pval); } /* * All of the following decoding routines take arguments: * uchar **pp; * uchar *pend; * Where parsing is supposed to start at **pp, and when parsing * is done, *pp is updated to point at next char to be parsed. * The pend pointer is just past end of string; an error should * be returned parsing hasn't finished by then. * * The returned int is ASN_OK if all went fine, else ASN_ESHORT, etc. * The remaining argument(s) are pointers to where parsed entity goes. */ /* Decode an ASN1 'Elem' (tag, length, value) */ int ber_decode(uchar** pp, uchar* pend, Elem* pelem) { int err; int isconstr; int length; Tag tag; Value val; err = tag_decode(pp, pend, &tag, &isconstr); if(err == ASN_OK) { err = length_decode(pp, pend, &length); if(err == ASN_OK) { if(tag.class == Universal){ err = value_decode(pp, pend, length, tag.num, isconstr, &val); if(val.tag == VSeq || val.tag == VSet) setmalloctag(val.u.seqval, getcallerpc(&pp)); }else err = value_decode(pp, pend, length, OCTET_STRING, 0, &val); if(err == ASN_OK) { pelem->tag = tag; pelem->val = val; } } } return err; } /* Decode a tag field */ int tag_decode(uchar** pp, uchar* pend, Tag* ptag, int* pisconstr) { int err; int v; uchar* p; err = ASN_OK; p = *pp; if(pend-p >= 2) { v = *p++; if ( v > 0x30 ) // MOD: To support unknown classes ptag->class = v; else ptag->class = v&CLASS_MASK; if(v&CONSTR_MASK) *pisconstr = 1; else *pisconstr = 0; v &= TAG_MASK; if(v == TAG_MASK) err = uint7_decode(&p, pend, &v); ptag->num = v; } else err = ASN_ESHORT; if ( ptag->class > 0x30 ) { // MOD: To support unknown classes ptag->num = ptag->class; err = ASN_OK; } *pp = p; return err; } /* Decode a length field */ int length_decode(uchar** pp, uchar* pend, int* plength) { int err; int num; int v; uchar* p; err = ASN_OK; num = 0; p = *pp; if(p < pend) { v = *p++; if(v&0x80) err = int_decode(&p, pend, v&0x7F, 1, &num); else num = v; } else err = ASN_ESHORT; *pp = p; *plength = num; return err; } /* Decode a value field */ int value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval) { int err; Bytes* va; int num; int bitsunused; int subids[MAXOBJIDLEN]; int isubid; Elist* vl; uchar* p; uchar* pe; err = ASN_OK; p = *pp; if(length == -1) { /* "indefinite" length spec */ if(!isconstr) err = ASN_EINVAL; } else if(p + length > pend) err = ASN_EVALLEN; if(err != ASN_OK) return err; switch(kind) { case 0: /* marker for end of indefinite constructions */ if(length == 0) pval->tag = VNull; else err = ASN_EINVAL; break; case BOOLEAN: if(isconstr) err = ASN_ECONSTR; else if(length != 1) err = ASN_EVALLEN; else { pval->tag = VBool; pval->u.boolval = (*p++ != 0); } break; case INTEGER: case ENUMERATED: if(isconstr) err = ASN_ECONSTR; else if(length <= 4) { err = int_decode(&p, pend, length, 0, &num); if(err == ASN_OK) { pval->tag = VInt; pval->u.intval = num; } } else { pval->tag = VBigInt; pval->u.bigintval = makebytes(p, length); p += length; } break; case BIT_STRING: pval->tag = VBitString; if(isconstr) { if(length == -1 && p + 2 <= pend && *p == 0 && *(p+1) ==0) { pval->u.bitstringval = makebits(0, 0, 0); p += 2; } else /* TODO: recurse and concat results */ err = ASN_EUNIMPL; } else { if(length < 2) { if(length == 1 && *p == 0) { pval->u.bitstringval = makebits(0, 0, 0); p++; } else err = ASN_EINVAL; } else { bitsunused = *p; if(bitsunused > 7) err = ASN_EINVAL; else if(length > 0x0FFFFFFF) err = ASN_ETOOBIG; else { pval->u.bitstringval = makebits(p+1, length-1, bitsunused); p += length; } } } break; case OCTET_STRING: case ObjectDescriptor: err = octet_decode(&p, pend, length, isconstr, &va); if(err == ASN_OK) { pval->tag = VOctets; pval->u.octetsval = va; } break; case NULLTAG: if(isconstr) err = ASN_ECONSTR; else if(length != 0) err = ASN_EVALLEN; else pval->tag = VNull; break; case OBJECT_ID: if(isconstr) err = ASN_ECONSTR; else if(length == 0) err = ASN_EVALLEN; else { isubid = 0; pe = p+length; while(p < pe && isubid < MAXOBJIDLEN) { err = uint7_decode(&p, pend, &num); if(err != ASN_OK) break; if(isubid == 0) { subids[isubid++] = num / 40; subids[isubid++] = num % 40; } else subids[isubid++] = num; } if(err == ASN_OK) { if(p != pe) err = ASN_EVALLEN; else { pval->tag = VObjId; pval->u.objidval = makeints(subids, isubid); } } } break; case EXTERNAL: case EMBEDDED_PDV: /* TODO: parse this internally */ if(p+length > pend) err = ASN_EVALLEN; else { pval->tag = VOther; pval->u.otherval = makebytes(p, length); p += length; } break; case REAL: /* Let the application decode */ if(isconstr) err = ASN_ECONSTR; else if(p+length > pend) err = ASN_EVALLEN; else { pval->tag = VReal; pval->u.realval = makebytes(p, length); p += length; } break; case SEQUENCE: err = seq_decode(&p, pend, length, isconstr, &vl); setmalloctag(vl, getcallerpc(&pp)); if(err == ASN_OK) { pval->tag = VSeq ; pval->u.seqval = vl; } break; case SETOF: err = seq_decode(&p, pend, length, isconstr, &vl); setmalloctag(vl, getcallerpc(&pp)); if(err == ASN_OK) { pval->tag = VSet; pval->u.setval = vl; } break; case NumericString: case PrintableString: case TeletexString: case VideotexString: case IA5String: case UTCTime: case GeneralizedTime: case GraphicString: case VisibleString: case GeneralString: case UniversalString: case BMPString: /* TODO: figure out when character set conversion is necessary */ err = octet_decode(&p, pend, length, isconstr, &va); if(err == ASN_OK) { pval->tag = VString; pval->u.stringval = (char*)emalloc(va->len+1); memmove(pval->u.stringval, va->data, va->len); pval->u.stringval[va->len] = 0; free(va); } break; default: if(p+length > pend) err = ASN_EVALLEN; else { pval->tag = VOther; pval->u.otherval = makebytes(p, length); p += length; } break; } *pp = p; return err; } /* * Decode an int in format where count bytes are * concatenated to form value. * Although ASN1 allows any size integer, we return * an error if the result doesn't fit in a 32-bit int. * If unsgned is not set, make sure to propagate sign bit. */ int int_decode(uchar** pp, uchar* pend, int count, int unsgned, int* pint) { int err; int num; uchar* p; p = *pp; err = ASN_OK; num = 0; if(p+count <= pend) { if((count > 4) || (unsgned && count == 4 && (*p&0x80))) err = ASN_ETOOBIG; else { if(!unsgned && count > 0 && count < 4 && (*p&0x80)) num = -1; // set all bits, initially while(count--) num = (num << 8)|(*p++); } } else err = ASN_ESHORT; *pint = num; *pp = p; return err; } /* * Decode an unsigned int in format where each * byte except last has high bit set, and remaining * seven bits of each byte are concatenated to form value. * Although ASN1 allows any size integer, we return * an error if the result doesn't fit in a 32 bit int. */ int uint7_decode(uchar** pp, uchar* pend, int* pint) { int err; int num; int more; int v; uchar* p; p = *pp; err = ASN_OK; num = 0; more = 1; while(more && p < pend) { v = *p++; if(num&0x7F000000) { err = ASN_ETOOBIG; break; } num <<= 7; more = v&0x80; num |= (v&0x7F); } /* MOD i've got a null value that must be decoded, so p == pend is valid, . . . */ //if(p == pend) // err = ASN_ESHORT; *pint = num; *pp = p; return err; } /* * Decode an octet string, recursively if isconstr. * We've already checked that length==-1 implies isconstr==1, * and otherwise that specified length fits within (*pp..pend) */ int octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes) { int err; uchar* p; Bytes* ans; Bytes* newans; uchar* pstart; uchar* pold; Elem elem; err = ASN_OK; p = *pp; ans = nil; if(length >= 0 && !isconstr) { ans = makebytes(p, length); p += length; } else { /* constructed, either definite or indefinite length */ pstart = p; for(;;) { if(length >= 0 && p >= pstart + length) { if(p != pstart + length) err = ASN_EVALLEN; break; } pold = p; err = ber_decode(&p, pend, &elem); if(err != ASN_OK) break; switch(elem.val.tag) { case VOctets: newans = catbytes(ans, elem.val.u.octetsval); freebytes(ans); ans = newans; break; case VEOC: if(length != -1) { p = pold; err = ASN_EINVAL; } goto cloop_done; default: p = pold; err = ASN_EINVAL; goto cloop_done; } } cloop_done: ; } *pp = p; *pbytes = ans; return err; } /* * Decode a sequence or set. * We've already checked that length==-1 implies isconstr==1, * and otherwise that specified length fits within (*p..pend) */ int seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist) { int err; uchar* p; uchar* pstart; uchar* pold; Elist* ans; Elem elem; Elist* lve; Elist* lveold; err = ASN_OK; ans = nil; p = *pp; if(!isconstr) err = ASN_EPRIM; else { /* constructed, either definite or indefinite length */ lve = nil; pstart = p; for(;;) { if(length >= 0 && p >= pstart + length) { if(p != pstart + length) err = ASN_EVALLEN; break; } pold = p; err = ber_decode(&p, pend, &elem); if(err != ASN_OK) break; if(elem.val.tag == VEOC) { if(length != -1) { p = pold; err = ASN_EINVAL; } break; } else lve = mkel(elem, lve); } if(err == ASN_OK) { /* reverse back to original order */ while(lve != nil) { lveold = lve; lve = lve->tl; lveold->tl = ans; ans = lveold; } } } *pp = p; *pelist = ans; setmalloctag(ans, getcallerpc(&pp)); return err; } /* * Encode e by BER rules, putting answer in *pbytes. * This is done by first calling enc with lenonly==1 * to get the length of the needed buffer, * then allocating the buffer and using enc again to fill it up. */ int encode(Elem e, Bytes** pbytes) { uchar* p; Bytes* ans; int err; uchar uc; p = &uc; err = enc(&p, e, 1); if(err == ASN_OK) { ans = newbytes(p-&uc); p = ans->data; err = enc(&p, e, 0); *pbytes = ans; } return err; } /* * The various enc functions take a pointer to a pointer * into a buffer, and encode their entity starting there, * updating the pointer afterwards. * If lenonly is 1, only the pointer update is done, * allowing enc to be called first to calculate the needed * buffer length. * If lenonly is 0, it is assumed that the answer will fit. */ int enc(uchar** pp, Elem e, int lenonly) { int err; int vlen; int constr; Tag tag; int v; int ilen; uchar* p; uchar* psave; p = *pp; err = val_enc(&p, e, &constr, 1); if(err != ASN_OK) return err; vlen = p - *pp; p = *pp; tag = e.tag; v = tag.class|constr; if(tag.num < 31) { if(!lenonly) if ( tag.class > 0x30 ) /* MOD for custom objects */ *p = v; else *p = (v|tag.num); p++; } else { if(!lenonly) if ( tag.class > 0x30 ) /* MOD for custom objects */ *p = v; else *p = (v|31); p++; if(tag.num < 0) return ASN_EINVAL; } if(vlen < 0x80) { if(!lenonly) *p = vlen; p++; } else { psave = p; int_enc(&p, vlen, 1, 1); ilen = p-psave; p = psave; if(!lenonly) { *p++ = (0x80 | ilen); int_enc(&p, vlen, 1, 0); } else p += 1 + ilen; } if(!lenonly) val_enc(&p, e, &constr, 0); else p += vlen; *pp = p; return err; } int val_enc(uchar** pp, Elem e, int *pconstr, int lenonly) { int err; uchar* p; int kind; int cl; int v; Bytes* bb = nil; Bits* bits; Ints* oid; int k; Elist* el; char* s; p = *pp; err = ASN_OK; kind = e.tag.num; cl = e.tag.class; *pconstr = 0; if(cl != Universal) { switch(e.val.tag) { case VBool: kind = BOOLEAN; break; case VInt: kind = INTEGER; break; case VBigInt: kind = INTEGER; break; case VOctets: kind = OCTET_STRING; break; case VReal: kind = REAL; break; case VOther: kind = OCTET_STRING; break; case VBitString: kind = BIT_STRING; break; case VNull: kind = NULLTAG; break; case VObjId: kind = OBJECT_ID; break; case VString: kind = UniversalString; break; case VSeq: kind = SEQUENCE; break; case VSet: kind = SETOF; break; } } switch(kind) { case BOOLEAN: if(is_int(&e, &v)) { if(v != 0) v = 255; int_enc(&p, v, 1, lenonly); } else err = ASN_EINVAL; break; case INTEGER: case ENUMERATED: if(is_int(&e, &v)) int_enc(&p, v, 0, lenonly); else { if(is_bigint(&e, &bb)) { if(!lenonly) memmove(p, bb->data, bb->len); p += bb->len; } else err = ASN_EINVAL; } break; case BIT_STRING: if(is_bitstring(&e, &bits)) { if(bits->len == 0) { if(!lenonly) *p = 0; p++; } else { v = bits->unusedbits; if(v < 0 || v > 7) err = ASN_EINVAL; else { if(!lenonly) { *p = v; memmove(p+1, bits->data, bits->len); } p += 1 + bits->len; } } } else err = ASN_EINVAL; break; case OCTET_STRING: case ObjectDescriptor: case EXTERNAL: case REAL: case EMBEDDED_PDV: bb = nil; switch(e.val.tag) { case VOctets: bb = e.val.u.octetsval; break; case VReal: bb = e.val.u.realval; break; case VOther: bb = e.val.u.otherval; break; } if(bb != nil) { if(!lenonly) memmove(p, bb->data, bb->len); p += bb->len; } else err = ASN_EINVAL; break; case NULLTAG: break; case OBJECT_ID: if(is_oid(&e, &oid)) { for(k = 0; k < oid->len; k++) { v = oid->data[k]; if(k == 0) { v *= 40; if(oid->len > 1) v += oid->data[++k]; } uint7_enc(&p, v, lenonly); } } else err = ASN_EINVAL; break; case SEQUENCE: case SETOF: el = nil; if(e.val.tag == VSeq) el = e.val.u.seqval; else if(e.val.tag == VSet) el = e.val.u.setval; else err = ASN_EINVAL; if(el != nil) { *pconstr = CONSTR_MASK; for(; el != nil; el = el->tl) { err = enc(&p, el->hd, lenonly); if(err != ASN_OK) break; } } break; case NumericString: case PrintableString: case TeletexString: case VideotexString: case IA5String: case UTCTime: case GeneralizedTime: case GraphicString: case VisibleString: case GeneralString: case UniversalString: case BMPString: if(e.val.tag == VString) { s = e.val.u.stringval; if(s != nil) { v = strlen(s); if(!lenonly) memmove(p, s, v); p += v; } } else err = ASN_EINVAL; break; default: err = ASN_EINVAL; } *pp = p; return err; } /* * Encode num as unsigned 7 bit values with top bit 1 on all bytes * except last, only putting in bytes if !lenonly. */ void uint7_enc(uchar** pp, int num, int lenonly) { int n; int v; int k; uchar* p; p = *pp; n = 1; v = num >> 7; while(v > 0) { v >>= 7; n++; } if(lenonly) p += n; else { for(k = (n - 1)*7; k > 0; k -= 7) *p++= ((num >> k)|0x80); *p++ = (num&0x7F); } *pp = p; } /* * Encode num as unsigned or signed integer, * only putting in bytes if !lenonly. * Encoding is length followed by bytes to concatenate. */ void int_enc(uchar** pp, int num, int unsgned, int lenonly) { int v; int n; int prevv; int k; uchar* p; p = *pp; v = num; if(v < 0) v = -(v + 1); n = 1; prevv = v; v >>= 8; while(v > 0) { prevv = v; v >>= 8; n++; } if(!unsgned && (prevv&0x80)) n++; if(lenonly) p += n; else { for(k = (n - 1)*8; k >= 0; k -= 8) *p++ = (num >> k); } *pp = p; } int ints_eq(Ints* a, Ints* b) { int alen; int i; alen = a->len; if(alen != b->len) return 0; for(i = 0; i < alen; i++) if(a->data[i] != b->data[i]) return 0; return 1; } /* * Look up o in tab (which must have nil entry to terminate). * Return index of matching entry, or -1 if none. */ int oid_lookup(Ints* o, Ints** tab) { int i; for(i = 0; tab[i] != nil; i++) if(ints_eq(o, tab[i])) return i; return -1; } /* * Return true if *pe is a SEQUENCE, and set *pseq to * the value of the sequence if so. */ int is_seq(Elem* pe, Elist** pseq) { if(pe->tag.class == Universal && pe->tag.num == SEQUENCE && pe->val.tag == VSeq) { *pseq = pe->val.u.seqval; return 1; } return 0; } int is_set(Elem* pe, Elist** pset) { if(pe->tag.class == Universal && pe->tag.num == SETOF && pe->val.tag == VSet) { *pset = pe->val.u.setval; return 1; } return 0; } int is_int(Elem* pe, int* pint) { if(pe->tag.class == Universal) { if(pe->tag.num == INTEGER && pe->val.tag == VInt) { *pint = pe->val.u.intval; return 1; } else if(pe->tag.num == BOOLEAN && pe->val.tag == VBool) { *pint = pe->val.u.boolval; return 1; } } return 0; } /* * for convience, all VInt's are readable via this routine, * as well as all VBigInt's */ int is_bigint(Elem* pe, Bytes** pbigint) { int v, n, i; if(pe->tag.class == Universal && pe->tag.num == INTEGER) { if(pe->val.tag == VBigInt) *pbigint = pe->val.u.bigintval; else if(pe->val.tag == VInt){ v = pe->val.u.intval; for(n = 1; n < 4; n++) if((1 << (8 * n)) > v) break; *pbigint = newbytes(n); for(i = 0; i < n; i++) (*pbigint)->data[i] = (v >> ((n - 1 - i) * 8)); }else return 0; return 1; } return 0; } int is_bitstring(Elem* pe, Bits** pbits) { if(pe->tag.class == Universal && pe->tag.num == BIT_STRING && pe->val.tag == VBitString) { *pbits = pe->val.u.bitstringval; return 1; } return 0; } int is_octetstring(Elem* pe, Bytes** poctets) { if(pe->tag.class == Universal && pe->tag.num == OCTET_STRING && pe->val.tag == VOctets) { *poctets = pe->val.u.octetsval; return 1; } return 0; } int is_oid(Elem* pe, Ints** poid) { if(pe->tag.class == Universal && pe->tag.num == OBJECT_ID && pe->val.tag == VObjId) { *poid = pe->val.u.objidval; return 1; } return 0; } int is_string(Elem* pe, char** pstring) { if(pe->tag.class == Universal) { switch(pe->tag.num) { case NumericString: case PrintableString: case TeletexString: case VideotexString: case IA5String: case GraphicString: case VisibleString: case GeneralString: case UniversalString: case BMPString: if(pe->val.tag == VString) { *pstring = pe->val.u.stringval; return 1; } } } return 0; } int is_time(Elem* pe, char** ptime) { if(pe->tag.class == Universal && (pe->tag.num == UTCTime || pe->tag.num == GeneralizedTime) && pe->val.tag == VString) { *ptime = pe->val.u.stringval; return 1; } return 0; } /* * malloc and return a new Bytes structure capable of * holding len bytes. (len >= 0) */ Bytes* newbytes(int len) { Bytes* ans; ans = (Bytes*)emalloc(OFFSETOF(data[0], Bytes) + len); ans->len = len; setmalloctag(ans,getcallerpc(&len)); return ans; } /* * newbytes(len), with data initialized from buf */ Bytes* makebytes(uchar* buf, int len) { Bytes* ans; ans = newbytes(len); memmove(ans->data, buf, len); setmalloctag(ans,getcallerpc(buf)); return ans; } void freebytes(Bytes* b) { if(b != nil) free(b); } /* * Make a new Bytes, containing bytes of b1 followed by those of b2. * Either b1 or b2 or both can be nil. */ Bytes* catbytes(Bytes* b1, Bytes* b2) { Bytes* ans; int n; if(b1 == nil) { if(b2 == nil) ans = newbytes(0); else ans = makebytes(b2->data, b2->len); } else if(b2 == nil) { ans = makebytes(b1->data, b1->len); } else { n = b1->len + b2->len; ans = newbytes(n); ans->len = n; memmove(ans->data, b1->data, b1->len); memmove(ans->data+b1->len, b2->data, b2->len); } return ans; } /* len is number of ints */ Ints* newints(int len) { Ints* ans; ans = (Ints*)emalloc(OFFSETOF(data[0], Ints) + len*sizeof(int)); ans->len = len; setmalloctag(ans,getcallerpc(&len)); return ans; } Ints* makeints(int* buf, int len) { Ints* ans; ans = newints(len); if(len > 0) memmove(ans->data, buf, len*sizeof(int)); setmalloctag(ans,getcallerpc(buf)); return ans; } void freeints(Ints* b) { if(b != nil) free(b); } /* len is number of bytes */ Bits* newbits(int len) { Bits* ans; ans = (Bits*)emalloc(OFFSETOF(data[0], Bits) + len); ans->len = len; ans->unusedbits = 0; return ans; } Bits* makebits(uchar* buf, int len, int unusedbits) { Bits* ans; ans = newbits(len); memmove(ans->data, buf, len); ans->unusedbits = unusedbits; return ans; } void freebits(Bits* b) { if(b != nil) free(b); } Elist* mkel(Elem e, Elist* tail) { Elist *el; el = (Elist*)emalloc(sizeof(Elist)); setmalloctag(el, getcallerpc(&e)); el->hd = e; el->tl = tail; return el; } int elistlen(Elist* el) { int ans = 0; while(el != nil) { ans++; el = el->tl; } return ans; } /* Frees elist, but not fields inside values of constituent elems */ void freeelist(Elist* el) { Elist* next; while(el != nil) { next = el->tl; free(el); el = next; } } /* free any allocated structures inside v (recursively freeing Elists) */ void freevalfields(Value* v) { Elist* el; Elist* l; if(v == nil) return; switch(v->tag) { case VOctets: freebytes(v->u.octetsval); break; case VBigInt: freebytes(v->u.bigintval); break; case VReal: freebytes(v->u.realval); break; case VOther: freebytes(v->u.otherval); break; case VBitString: freebits(v->u.bitstringval); break; case VObjId: freeints(v->u.objidval); break; case VString: if(v->u.stringval) free(v->u.stringval); break; case VSeq: el = v->u.seqval; for(l = el; l != nil; l = l->tl) freevalfields(&l->hd.val); if(el) freeelist(el); break; case VSet: el = v->u.setval; for(l = el; l != nil; l = l->tl) freevalfields(&l->hd.val); if(el) freeelist(el); break; } } /* end of general ASN1 functions */ /* elem constructors */ Elem Null(void) { Elem e; e.tag.class = Universal; e.tag.num = NULLTAG; e.val.tag = VNull; return e; } Elem mkint(int j) { Elem e; e.tag.class = Universal; e.tag.num = INTEGER; e.val.tag = VInt; e.val.u.intval = j; return e; } Elem mkbigint(mpint *p) { Elem e; uchar *buf; int buflen; e.tag.class = Universal; e.tag.num = INTEGER; e.val.tag = VBigInt; buflen = mptobe(p, nil, 0, &buf); e.val.u.bigintval = makebytes(buf, buflen); free(buf); return e; } Elem mkstring(char *s) { Elem e; e.tag.class = Universal; e.tag.num = IA5String; e.val.tag = VString; e.val.u.stringval = estrdup(s); return e; } Elem mkoctet(uchar *buf, int buflen) { Elem e; e.tag.class = Universal; e.tag.num = OCTET_STRING; e.val.tag = VOctets; e.val.u.octetsval = makebytes(buf, buflen); return e; } Elem mkbits(uchar *buf, int buflen) { Elem e; e.tag.class = Universal; e.tag.num = BIT_STRING; e.val.tag = VBitString; e.val.u.bitstringval = makebits(buf, buflen, 0); return e; } Elem mkutc(long t) { Elem e; char utc[50]; Tm *tm = gmtime(t); e.tag.class = Universal; e.tag.num = UTCTime; e.val.tag = VString; snprint(utc, 50, "%.2d%.2d%.2d%.2d%.2d%.2dZ", tm->year % 100, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec); e.val.u.stringval = estrdup(utc); return e; } Elem mkoid(Ints *oid) { Elem e; e.tag.class = Universal; e.tag.num = OBJECT_ID; e.val.tag = VObjId; e.val.u.objidval = makeints(oid->data, oid->len); return e; } Elem mkseq(Elist *el) { Elem e; e.tag.class = Universal; e.tag.num = SEQUENCE; e.val.tag = VSeq; e.val.u.seqval = el; return e; } Elem mkset(Elist *el) { Elem e; e.tag.class = Universal; e.tag.num = SETOF; e.val.tag = VSet; e.val.u.setval = el; return e; } typedef struct Ints7pref { int len; int data[7]; char prefix[4]; } Ints7pref; Ints7pref DN_oid[] = { {4, 2, 5, 4, 6, 0, 0, 0, "C="}, {4, 2, 5, 4, 8, 0, 0, 0, "ST="}, {4, 2, 5, 4, 7, 0, 0, 0, "L="}, {4, 2, 5, 4, 10, 0, 0, 0, "O="}, {4, 2, 5, 4, 11, 0, 0, 0, "OU="}, {4, 2, 5, 4, 3, 0, 0, 0, "CN="}, {7, 1,2,840,113549,1,9,1, "E="}, }; Elem mkname(Ints7pref *oid, char *subj) { return mkset(mkel(mkseq(mkel(mkoid((Ints*)oid), mkel(mkstring(subj), nil))), nil)); } Elem mkDN(char *dn) { int i, j, nf; char *f[20], *prefix, *d2 = estrdup(dn); Elist* el = nil; nf = tokenize(d2, f, nelem(f)); for(i=nf-1; i>=0; i--){ for(j=0; jlen,v.u.octetsval->data[0],v.u.octetsval->data[1]); break; case VBigInt: print("BigInt[%d] %.2x%.2x...",v.u.bigintval->len,v.u.bigintval->data[0],v.u.bigintval->data[1]); break; case VReal: print("Real..."); break; case VOther: print("Other..."); break; case VBitString: print("BitString..."); break; case VNull: print("Null"); break; case VEOC: print("EOC..."); break; case VObjId: print("ObjId"); for(i = 0; ilen; i++) print(" %d", v.u.objidval->data[i]); break; case VString: print("String \"%s\"",v.u.stringval); break; case VSeq: print("Seq\n"); for(el = v.u.seqval; el!=nil; el = el->tl) edump(el->hd); break; case VSet: print("Set\n"); for(el = v.u.setval; el!=nil; el = el->tl) edump(el->hd); break; } print("}\n"); }