From 06b8d7aa3355121c8810850972955374f884dc20 Mon Sep 17 00:00:00 2001 From: Lev Walkin Date: Thu, 23 Sep 2004 22:06:02 +0000 Subject: implemented unber - the ASN.1 BER Decoder --- asn1c/Makefile.am | 9 +- asn1c/Makefile.in | 31 ++-- asn1c/README | 3 +- asn1c/asn1c.1 | 11 +- asn1c/asn1c.c | 20 +-- asn1c/unber.c | 481 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 511 insertions(+), 44 deletions(-) create mode 100644 asn1c/unber.c (limited to 'asn1c') diff --git a/asn1c/Makefile.am b/asn1c/Makefile.am index 63bb31c2..a5caec37 100644 --- a/asn1c/Makefile.am +++ b/asn1c/Makefile.am @@ -10,18 +10,15 @@ AM_CPPFLAGS = \ -I${top_srcdir}/skeletons \ -DDATADIR=\"$(pkgdatadir)\" -LDADD = \ +asn1c_LDADD = \ $(top_builddir)/libasn1parser/libasn1parser.la \ $(top_builddir)/libasn1print/libasn1print.la \ $(top_builddir)/libasn1fix/libasn1fix.la \ $(top_builddir)/libasn1compiler/libasn1compiler.la -asn1c_SOURCES = asn1c.c \ - tlv_decoder.c tlv_decoder.h +bin_PROGRAMS = asn1c unber -bin_PROGRAMS = asn1c - -dist_man1_MANS = asn1c.1 +dist_man1_MANS = asn1c.1 unber.1 check_SCRIPTS = check-parsing.sh TESTS = check-parsing.sh diff --git a/asn1c/Makefile.in b/asn1c/Makefile.in index 431b38ab..73c25f35 100644 --- a/asn1c/Makefile.in +++ b/asn1c/Makefile.in @@ -14,7 +14,7 @@ @SET_MAKE@ -SOURCES = $(asn1c_SOURCES) +SOURCES = asn1c.c unber.c srcdir = @srcdir@ top_srcdir = @top_srcdir@ @@ -37,7 +37,7 @@ NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : host_triplet = @host@ -bin_PROGRAMS = asn1c$(EXEEXT) +bin_PROGRAMS = asn1c$(EXEEXT) unber$(EXEEXT) subdir = asn1c DIST_COMMON = README $(dist_man1_MANS) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in @@ -51,18 +51,19 @@ CONFIG_CLEAN_FILES = am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) PROGRAMS = $(bin_PROGRAMS) -am_asn1c_OBJECTS = asn1c.$(OBJEXT) tlv_decoder.$(OBJEXT) -asn1c_OBJECTS = $(am_asn1c_OBJECTS) -asn1c_LDADD = $(LDADD) +asn1c_SOURCES = asn1c.c +asn1c_OBJECTS = asn1c.$(OBJEXT) asn1c_DEPENDENCIES = $(top_builddir)/libasn1parser/libasn1parser.la \ $(top_builddir)/libasn1print/libasn1print.la \ $(top_builddir)/libasn1fix/libasn1fix.la \ $(top_builddir)/libasn1compiler/libasn1compiler.la +unber_SOURCES = unber.c +unber_OBJECTS = unber.$(OBJEXT) +unber_LDADD = $(LDADD) DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles -@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/asn1c.Po \ -@AMDEP_TRUE@ ./$(DEPDIR)/tlv_decoder.Po +@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/asn1c.Po ./$(DEPDIR)/unber.Po COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) \ @@ -71,8 +72,8 @@ LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) \ CCLD = $(CC) LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ -SOURCES = $(asn1c_SOURCES) -DIST_SOURCES = $(asn1c_SOURCES) +SOURCES = asn1c.c unber.c +DIST_SOURCES = asn1c.c unber.c RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ install-exec-recursive install-info-recursive \ @@ -205,16 +206,13 @@ AM_CPPFLAGS = \ -I${top_srcdir}/skeletons \ -DDATADIR=\"$(pkgdatadir)\" -LDADD = \ +asn1c_LDADD = \ $(top_builddir)/libasn1parser/libasn1parser.la \ $(top_builddir)/libasn1print/libasn1print.la \ $(top_builddir)/libasn1fix/libasn1fix.la \ $(top_builddir)/libasn1compiler/libasn1compiler.la -asn1c_SOURCES = asn1c.c \ - tlv_decoder.c tlv_decoder.h - -dist_man1_MANS = asn1c.1 +dist_man1_MANS = asn1c.1 unber.1 check_SCRIPTS = check-parsing.sh TESTS = check-parsing.sh EXTRA_DIST = check-parsing.sh @@ -283,6 +281,9 @@ clean-binPROGRAMS: asn1c$(EXEEXT): $(asn1c_OBJECTS) $(asn1c_DEPENDENCIES) @rm -f asn1c$(EXEEXT) $(LINK) $(asn1c_LDFLAGS) $(asn1c_OBJECTS) $(asn1c_LDADD) $(LIBS) +unber$(EXEEXT): $(unber_OBJECTS) $(unber_DEPENDENCIES) + @rm -f unber$(EXEEXT) + $(LINK) $(unber_LDFLAGS) $(unber_OBJECTS) $(unber_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -291,7 +292,7 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1c.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tlv_decoder.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unber.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ diff --git a/asn1c/README b/asn1c/README index 158cd115..8bec7b9e 100644 --- a/asn1c/README +++ b/asn1c/README @@ -1 +1,2 @@ -The ASN.1 Compiler +asn1c - The ASN.1 Compiler +unber - The ASN.1 BER Decoder diff --git a/asn1c/asn1c.1 b/asn1c/asn1c.1 index 8d7cf061..d6be9458 100644 --- a/asn1c/asn1c.1 +++ b/asn1c/asn1c.1 @@ -4,9 +4,8 @@ .TH ASN1C 1 "\*(Dt" "ASN.1 Compiler" "ASN.1 Compiler" .SH NAME asn1c \- ASN.1 Compiler -.ND ASN.1 compiler .SH SYNOPSIS -asn1c [\fB\-E\fR [\fB-F\fR] | \fB\-P\fR | \fB\-R\fR | \fB\-t\fR\fIdata-string\fR] +asn1c [\fB\-E\fR [\fB-F\fR] | \fB\-P\fR | \fB\-R\fR] [\fB\-S\fR\fIdir\fR] [\fB\-W\fR\fIdebug-\fR...] [\fB\-f\fR\fIoption\fR...] [\fB\-p\fR\fIrint-\fR...] \fIinfile\fR... @@ -19,7 +18,6 @@ and other encoding standards. \fIOverall Options\fR \fB\-E \-F \-P \-R\fR .BI "\-S " directory -.BI "\-t " data-string .TP \fIWarning Options\fR .br @@ -55,10 +53,6 @@ omitting the usual support code. .TP \fB\-S\fR \fIdirectory\fR Use the specified directory with ASN.1 skeleton files. -.TP -\fB\-t\fR \fIdata-string\fR -Interpret the data-string as a sequence of hexadecimal values representing -the start of BER TLV encoding. Print the human readable explanation. .SH WARNING OPTIONS .TP .B \-Werror @@ -104,5 +98,8 @@ its internal understanding of subtype constraints. .TP .B \-print-lines Generate "-- #line" comments in \fB-E\fR output. +.SH SEE ALSO +.TP +\&\fIunber\fR\|(1) .SH AUTHORS Lev Walkin diff --git a/asn1c/asn1c.c b/asn1c/asn1c.c index 9dd5dde3..e3097bd5 100644 --- a/asn1c/asn1c.c +++ b/asn1c/asn1c.c @@ -19,9 +19,7 @@ #include /* Portable basename(3) and dirname(3) */ -#include "tlv_decoder.h" /* -t: decode TL[V?] string */ - -static void usage(char *av0); /* Print the Usage screen and exit(EX_USAGE) */ +static void usage(const char *av0); /* Print the Usage screen and exit */ #undef COPYRIGHT #define COPYRIGHT \ @@ -45,7 +43,7 @@ main(int ac, char **av) { /* * Process command-line options. */ - while((ch = getopt(ac, av, "EFf:hLPp:RS:t:vW:")) != -1) + while((ch = getopt(ac, av, "EFf:hLPp:RS:vW:")) != -1) switch(ch) { case 'E': print_arg__print_out = 1; @@ -97,10 +95,6 @@ main(int ac, char **av) { case 'S': skeletons_dir = optarg; break; - case 't': - if(decode_tlv_from_string(optarg)) - exit(EX_DATAERR); - exit(0); case 'v': fprintf(stderr, "ASN.1 Compiler, v" VERSION "\n" COPYRIGHT); exit(0); @@ -264,11 +258,11 @@ main(int ac, char **av) { * Print the usage screen and exit(EX_USAGE). */ static void -usage(char *av0) { +usage(const char *av0) { fprintf(stderr, "ASN.1 Compiler, v" VERSION "\n" COPYRIGHT -"Usage: %s [options] infile...\n" -"Where [options] are:\n" +"Usage: %s [options] file ...\n" +"Options:\n" " -E Run only the ASN.1 parser and print out the tree\n" " -F During -E operation, also perform tree fixing\n" "\n" @@ -278,10 +272,6 @@ usage(char *av0) { " (Default is \"%s\")\n" "\n" -" -t Decode the given tag[/length] sequence\n" -" (e.g. -t \"bf 20\")\n" -"\n" - " -Werror Treat warnings as errors; abort if any warning\n" " -Wdebug-lexer Enable verbose debugging output from lexer\n" " -Wdebug-fixer --//-- semantics processor\n" diff --git a/asn1c/unber.c b/asn1c/unber.c new file mode 100644 index 00000000..a954ece1 --- /dev/null +++ b/asn1c/unber.c @@ -0,0 +1,481 @@ +#include +#include +#include +#include +#include +#include /* for EX_USAGE */ +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include /* For static string tables */ + +#include +#include +#include +#include +#include + +static void usage(const char *av0); /* Print the Usage screen and exit */ +static int process(const char *fname); /* Perform the BER decoding */ +static int decode_tlv_from_string(const char *datastring); + +#undef COPYRIGHT +#define COPYRIGHT \ + "Copyright (c) 2004 Lev Walkin \n" + +static char *indent_buffer = " "; + +int +main(int ac, char **av) { + int ch; /* Command line character */ + int i; /* Index in some loops */ + + /* + * Process command-line options. + */ + while((ch = getopt(ac, av, "i:t:v")) != -1) + switch(ch) { + case 't': + if(decode_tlv_from_string(optarg)) + exit(EX_DATAERR); + exit(0); + case 'i': + i = atoi(optarg); + if(i >= 0 && i < 16) { + indent_buffer = alloca(i + 1); + memset(indent_buffer, ' ', i); + indent_buffer[i] = '\0'; + } else { + fprintf(stderr, "-i %s: Invalid indent value\n",optarg); + exit(EX_USAGE); + } + break; + case 'v': + fprintf(stderr, "ASN.1 BER Decoder, v" VERSION "\n" COPYRIGHT); + exit(0); + break; + default: + usage(av[0]); + } + + /* + * Ensure that there are some input files present. + */ + if(ac > optind) { + ac -= optind; + av += optind; + } else { + fprintf(stderr, "%s: No input files specified\n", av[0]); + exit(1); + } + + setvbuf(stdout, 0, _IOLBF, 0); + + /* + * Iterate over input files and parse each. + * All syntax trees from all files will be bundled together. + */ + for(i = 0; i < ac; i++) { + if(process(av[i])) + exit(EX_DATAERR); + } + + return 0; +} + +/* + * Print the usage screen and exit(EX_USAGE). + */ +static void +usage(const char *av0) { + fprintf(stderr, +"ASN.1 BER Decoder, v" VERSION "\n" COPYRIGHT +"Usage: %s [options] [-] [file ...]\n" +"Options:\n" +" -i Amount of spaces for indentation (default is 4)\n" +" -t Decode the given tag[/length] sequence\n" +" (e.g. -t \"bf 20\")\n" + , av0); + exit(EX_USAGE); +} + +typedef enum pd_code { + PD_FAILED = -1, + PD_FINISHED = 0, + PD_EOF = 1, +} pd_code_e; +static pd_code_e process_deeper(const char *fname, FILE *fp, int level, ssize_t limit); +static void print_TL(int fin, int level, int constr, ssize_t tlen, ber_tlv_tag_t, ber_tlv_len_t); +static int print_V(const char *fname, FILE *fp, ber_tlv_tag_t, ber_tlv_len_t); + +/* + * Open the file and initiate recursive processing. + */ +static int +process(const char *fname) { + FILE *fp; + pd_code_e pdc; + + if(strcmp(fname, "-")) { + fp = fopen(fname, "r"); + if(!fp) { + perror(fname); + return -1; + } + } else { + fp = stdin; + } + + /* + * Fetch out BER-encoded data until EOF or error. + */ + do { + pdc = process_deeper(fname, fp, 0, -1); + } while(pdc == PD_FINISHED); /* Wait until PD_EOF */ + + if(fp != stdin) + fclose(fp); + + if(pdc == PD_FAILED) + return -1; + return 0; +} + +/* + * Process the TLV recursively. + */ +static pd_code_e process_deeper(const char *fname, FILE *fp, int level, ssize_t limit) { + unsigned char tagbuf[32]; + ssize_t tblen = 0; + pd_code_e pdc = PD_FINISHED; + ber_tlv_tag_t tlv_tag; + ber_tlv_len_t tlv_len; + ssize_t t_len; + ssize_t l_len; + + do { + int constr; + int ch; + + if(limit == 0) + return PD_FINISHED; + + if(limit >= 0 && tblen >= limit) { + fprintf(stderr, + "%s: Too long TL sequence (%ld >= %ld). " + "Dangerous file\n", + fname, (long)tblen, (long)limit); + return PD_FAILED; + } + + ch = fgetc(fp); + if(ch == -1) { + if(tblen) { + fprintf(stderr, + "%s: Unexpected end of file (TL)\n", + fname); + return PD_FAILED; + } else { + return PD_EOF; + } + } + + tagbuf[tblen++] = ch; + + /* + * Decode the TLV tag. + */ + t_len = ber_fetch_tag(tagbuf, tblen, &tlv_tag); + switch(t_len) { + case -1: + fprintf(stderr, "%s: Fatal error deciphering tag\n", + fname); + return PD_FAILED; + case 0: + /* More data expected */ + continue; + } + + /* + * Decode the TLV length. + */ + constr = BER_TLV_CONSTRUCTED(tagbuf); + l_len = ber_fetch_length(constr, + tagbuf + t_len, tblen - t_len, &tlv_len); + switch(l_len) { + case -1: + fprintf(stderr, "%s: Fatal error deciphering length\n", + fname); + return PD_FAILED; + case 0: + /* More data expected */ + continue; + } + + /* Make sure the T & L decoders took exactly the whole buffer */ + assert((t_len + l_len) == tblen); + + print_TL(0, level, constr, tblen, tlv_tag, tlv_len); + + if(limit != -1) { + /* If limit is set, account for the TL sequence */ + limit -= (t_len + l_len); + assert(limit >= 0); + } + + if(limit != -1) { + if(tlv_len > limit) { + fprintf(stderr, + "%s: Structure advertizes length (%ld) " + "greater than of a parent container (%ld)\n", + fname, (long)tlv_len, (long)limit); + return PD_FAILED; + } else if(tlv_len != -1) { + /* Account for the V */ + limit -= tlv_len; + } + } + + if(constr) { + /* + * This is a constructed type. Process recursively. + */ + + /* Get the new subframe limit from the structure tags */ + if(tlv_len == -1) + tlv_len = limit; + + pdc = process_deeper(fname, fp, level + 1, tlv_len); + if(pdc == PD_FAILED) return pdc; + } else { + + assert(tlv_len >= 0); + if(print_V(fname, fp, tlv_tag, tlv_len)) + return PD_FAILED; + } + + print_TL(1, level, constr, tblen, tlv_tag, tlv_len); + + tblen = 0; + } while(1); + + return pdc; +} + +static void +print_TL(int fin, int level, int constr, ssize_t tlen, ber_tlv_tag_t tlv_tag, ber_tlv_len_t tlv_len) { + + if(fin && tlen == 2 && !constr && !tlv_tag && !tlv_len) { + /* end of content octets */ + return; + } + + if(fin && !constr) { + printf("

\n"); + return; + } + + while(level-- > 0) printf(indent_buffer); /* Print indent */ + printf(fin ? "\n"); + else + printf(">"); +} + +static int +print_V(const char *fname, FILE *fp, ber_tlv_tag_t tlv_tag, ber_tlv_len_t tlv_len) { + asn1p_expr_type_e etype = 0; + long collector = 0; + ssize_t i; + + /* Figure out what type is it */ + if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL) { + ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag); + etype = ASN_UNIVERSAL_TAG2TYPE(tvalue); + } + + /* + * Print the value in binary or text form. + */ + for(i = 0; i < tlv_len; i++) { + int ch = fgetc(fp); + if(ch == -1) { + fprintf(stderr, + "%s: Unexpected end of file (V)\n", fname); + return -1; + } + switch(etype) { + case ASN_BASIC_UTCTime: + case ASN_BASIC_GeneralizedTime: + case ASN_STRING_NumericString: + case ASN_STRING_PrintableString: + case ASN_STRING_VisibleString: + case ASN_STRING_UTF8String: + switch(ch) { + default: + if(((etype == ASN_STRING_UTF8String) + || !(ch & 0x80)) + && (ch >= 0x20) + ) { + printf("%c", ch); + break; + } + /* Fall through */ + case '<': case '>': case '&': + printf("&x%02x;", ch); + } + break; + case ASN_BASIC_BOOLEAN: + if(tlv_len == 1) { + switch(ch) { + case 0: printf(""); break; + case 0xff: printf(""); break; + default: printf("", ch); + } + break; + } + /* Fall through */ + case ASN_BASIC_INTEGER: + case ASN_BASIC_ENUMERATED: + if((size_t)tlv_len <= sizeof(collector)) { + if(i) { + collector = collector * 256 + ch; + } else { + collector = (int)(signed char)ch; + } + if((i+1) == tlv_len) + printf("%ld", collector); + break; + } + /* Fall through */ + default: + printf("&x%02x;", ch); + } + } + + return 0; +} + + +static int +decode_tlv_from_string(const char *datastring) { + unsigned char *data, *dp; + size_t dsize; /* Data size */ + ssize_t len; + ber_tlv_tag_t tlv_tag; + ber_tlv_len_t tlv_len; + const char *p; + int half; + + dsize = strlen(datastring) + 1; + dp = data = calloc(1, dsize); + assert(data); + + for(half = 0, p = datastring; *p; p++) { + switch(*p) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + *dp |= *p - '0'; break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + *dp |= *p - 'A' + 10; break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + *dp |= *p - 'a' + 10; break; + case ' ': case '\t': case '\r': case '\n': + continue; + default: + fprintf(stderr, "Unexpected symbols in data string:\n"); + fprintf(stderr, "%s\n", datastring); + for(dp = data; datastring < p; datastring++, dp++) + *dp = ' '; + *dp = '\0'; + fprintf(stderr, "%s^ <- here\n", (char *)data); + return -1; + } + if(half) dp++; else (*dp) <<= 4; + half = !half; + } + + assert((size_t)(dp - data) <= dsize); + dsize = dp - data; + + printf("BER: "); + for(dp = data; dp < data + dsize; dp++) + printf("%02X", *dp); + printf("\n"); + + len = ber_fetch_tag(data, dsize, &tlv_tag); + switch(len) { + case -1: + fprintf(stderr, "TAG: Fatal error deciphering tag\n"); + return -1; + case 0: + fprintf(stderr, "TAG: More data expected\n"); + return -1; + default: + printf("TAG: "); + ber_tlv_tag_fwrite(tlv_tag, stdout); + if(BER_TLV_CONSTRUCTED(data)) { + printf(" (constructed)"); + } else if(dsize >= 2 && data[0] == 0 && data[1] == 0) { + printf(" (end-of-content)"); + } else { + printf(" (primitive)"); + } + if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL) { + const char *str; + ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag); + str = ASN_UNIVERSAL_TAG2STR(tvalue); + if(str) printf(" \"%s\"", str); + } + printf("\n"); + } + + if(dsize > (size_t)len) { + len = ber_fetch_length(BER_TLV_CONSTRUCTED(data), + data + len, dsize - len, &tlv_len); + switch(len) { + case -1: + fprintf(stderr, + "LEN: Fatal error deciphering length\n"); + return -1; + case 0: + fprintf(stderr, "LEN: More data expected\n"); + return -1; + default: + if(tlv_len == (ber_tlv_len_t)-1) + printf("LEN: Indefinite length encoding\n"); + else + printf("LEN: %ld bytes\n", (long)tlv_len); + } + } + + return 0; +} -- cgit v1.2.3