diff options
-rw-r--r-- | asn1c/Makefile.am | 6 | ||||
-rw-r--r-- | asn1c/Makefile.in | 22 | ||||
-rw-r--r-- | asn1c/asn1c.1 | 5 | ||||
-rw-r--r-- | asn1c/asn1c.c | 27 | ||||
-rw-r--r-- | asn1c/enber.1 | 29 | ||||
-rw-r--r-- | asn1c/enber.c | 381 | ||||
-rw-r--r-- | asn1c/unber.1 | 85 | ||||
-rw-r--r-- | asn1c/unber.c | 286 |
8 files changed, 778 insertions, 63 deletions
diff --git a/asn1c/Makefile.am b/asn1c/Makefile.am index a5caec37..14222667 100644 --- a/asn1c/Makefile.am +++ b/asn1c/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS = . tests webcgi +SUBDIRS = . webcgi tests AM_CFLAGS = @ADD_CFLAGS@ AM_CPPFLAGS = \ @@ -16,9 +16,9 @@ asn1c_LDADD = \ $(top_builddir)/libasn1fix/libasn1fix.la \ $(top_builddir)/libasn1compiler/libasn1compiler.la -bin_PROGRAMS = asn1c unber +bin_PROGRAMS = asn1c unber enber -dist_man1_MANS = asn1c.1 unber.1 +dist_man1_MANS = asn1c.1 unber.1 enber.1 check_SCRIPTS = check-parsing.sh TESTS = check-parsing.sh diff --git a/asn1c/Makefile.in b/asn1c/Makefile.in index 73c25f35..dd6652aa 100644 --- a/asn1c/Makefile.in +++ b/asn1c/Makefile.in @@ -14,7 +14,7 @@ @SET_MAKE@ -SOURCES = asn1c.c unber.c +SOURCES = asn1c.c enber.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) unber$(EXEEXT) +bin_PROGRAMS = asn1c$(EXEEXT) unber$(EXEEXT) enber$(EXEEXT) subdir = asn1c DIST_COMMON = README $(dist_man1_MANS) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in @@ -57,13 +57,17 @@ asn1c_DEPENDENCIES = $(top_builddir)/libasn1parser/libasn1parser.la \ $(top_builddir)/libasn1print/libasn1print.la \ $(top_builddir)/libasn1fix/libasn1fix.la \ $(top_builddir)/libasn1compiler/libasn1compiler.la +enber_SOURCES = enber.c +enber_OBJECTS = enber.$(OBJEXT) +enber_LDADD = $(LDADD) 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 ./$(DEPDIR)/unber.Po +@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/asn1c.Po ./$(DEPDIR)/enber.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/unber.Po COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) \ @@ -72,8 +76,8 @@ LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) \ CCLD = $(CC) LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ -SOURCES = asn1c.c unber.c -DIST_SOURCES = asn1c.c unber.c +SOURCES = asn1c.c enber.c unber.c +DIST_SOURCES = asn1c.c enber.c unber.c RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ install-exec-recursive install-info-recursive \ @@ -196,7 +200,7 @@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ -SUBDIRS = . tests webcgi +SUBDIRS = . webcgi tests AM_CFLAGS = @ADD_CFLAGS@ AM_CPPFLAGS = \ -I${top_srcdir}/libasn1compiler \ @@ -212,7 +216,7 @@ asn1c_LDADD = \ $(top_builddir)/libasn1fix/libasn1fix.la \ $(top_builddir)/libasn1compiler/libasn1compiler.la -dist_man1_MANS = asn1c.1 unber.1 +dist_man1_MANS = asn1c.1 unber.1 enber.1 check_SCRIPTS = check-parsing.sh TESTS = check-parsing.sh EXTRA_DIST = check-parsing.sh @@ -281,6 +285,9 @@ clean-binPROGRAMS: asn1c$(EXEEXT): $(asn1c_OBJECTS) $(asn1c_DEPENDENCIES) @rm -f asn1c$(EXEEXT) $(LINK) $(asn1c_LDFLAGS) $(asn1c_OBJECTS) $(asn1c_LDADD) $(LIBS) +enber$(EXEEXT): $(enber_OBJECTS) $(enber_DEPENDENCIES) + @rm -f enber$(EXEEXT) + $(LINK) $(enber_LDFLAGS) $(enber_OBJECTS) $(enber_LDADD) $(LIBS) unber$(EXEEXT): $(unber_OBJECTS) $(unber_DEPENDENCIES) @rm -f unber$(EXEEXT) $(LINK) $(unber_LDFLAGS) $(unber_OBJECTS) $(unber_LDADD) $(LIBS) @@ -292,6 +299,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)/enber.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unber.Po@am__quote@ .c.o: diff --git a/asn1c/asn1c.1 b/asn1c/asn1c.1 index d6be9458..359724b2 100644 --- a/asn1c/asn1c.1 +++ b/asn1c/asn1c.1 @@ -1,6 +1,3 @@ -.de Id -.. -.Id $Id" .TH ASN1C 1 "\*(Dt" "ASN.1 Compiler" "ASN.1 Compiler" .SH NAME asn1c \- ASN.1 Compiler @@ -100,6 +97,6 @@ its internal understanding of subtype constraints. Generate "-- #line" comments in \fB-E\fR output. .SH SEE ALSO .TP -\&\fIunber\fR\|(1) +\&\fIunber\fR\|(1), \&\fIenber\fR\|(1) .SH AUTHORS Lev Walkin <vlm@lionet.info> diff --git a/asn1c/asn1c.c b/asn1c/asn1c.c index e3097bd5..8c3a7e58 100644 --- a/asn1c/asn1c.c +++ b/asn1c/asn1c.c @@ -1,3 +1,30 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ /* * This is the program that connects the libasn1* libraries together. * It uses them in turn to parse, fix and then compile or print the ASN.1 tree. diff --git a/asn1c/enber.1 b/asn1c/enber.1 new file mode 100644 index 00000000..b3984336 --- /dev/null +++ b/asn1c/enber.1 @@ -0,0 +1,29 @@ +.de Vb +.sp +.ft CW +.nf +.. +.de Ve +.ft R +.fi +.sp +.. +.TH ENBER 1 "\*(Dt" "unber counterpart" "unber counterpart" +.SH NAME +enber \- Convert the unber XML output back into BER +.SH SYNOPSIS +enber [\fB-\fR] \fIinfile\fR... +.SH DESCRIPTION +enber takes the XML-encoded files produced by \fIunber\fR\|(1) and converts +them back into the BER format. +A single dash represents the standard input. +.SH EXAMPLES +Decode the BER sequence and immediately encode it back +.Vb +\& enber \fB-p\fR \fIfilename.ber\fR | unber \fB-\fR > \fIreconstructed.ber\fR +.Ve +.SH SEE ALSO +.TP +\&\fIunber\fR\|(1), \&\fIasn1c\fR\|(1) +.SH AUTHORS +Lev Walkin <vlm@lionet.info> diff --git a/asn1c/enber.c b/asn1c/enber.c new file mode 100644 index 00000000..45fda434 --- /dev/null +++ b/asn1c/enber.c @@ -0,0 +1,381 @@ +/*- + * Copyright (c) 2004 Lev Walkin <vlm@lionet.info> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#include <sysexits.h> /* for EX_USAGE */ +#include <assert.h> +#include <errno.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <asn1parser.h> /* For static string tables */ + +#include <asn_application.h> +#include <constraints.c> +#include <ber_tlv_tag.c> +#include <ber_tlv_length.c> + +static void usage(const char *av0); /* Print the Usage screen and exit */ +static int process(const char *fname); /* Perform the BER decoding */ +static int process_line(const char *fname, char *line, int lineno); + +#undef COPYRIGHT +#define COPYRIGHT \ + "Copyright (c) 2004 Lev Walkin <vlm@lionet.info>\n" + +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, "hv")) != -1) + switch(ch) { + case 'v': + fprintf(stderr, "ASN.1 BER Decoder, v" VERSION "\n" COPYRIGHT); + exit(0); + break; + case 'h': + 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, +"Convertor of under(1) output back into BER, v" VERSION "\n" COPYRIGHT +"Usage: %s [-] [file ...]\n" + , av0); + exit(EX_USAGE); +} + +/* + * Open the file and initiate recursive processing. + */ +static int +process(const char *fname) { + char buf[8192]; + char *collector = 0; + size_t collector_size = sizeof(buf); + size_t collector_offset = 0; + int lineno = 0; + FILE *fp; + + if(strcmp(fname, "-")) { + fp = fopen(fname, "r"); + if(!fp) { + perror(fname); + return -1; + } + } else { + fp = stdin; + } + + + while(fgets(buf, sizeof(buf), fp) || !feof(fp)) { + size_t len = strlen(buf); + + if(!len) continue; + if(collector_offset || buf[len-1] != '\n') { + if((collector_size - collector_offset) <= len + || !collector) { + collector_size <<= 1; + collector = realloc(collector, collector_size); + if(!collector) { + perror("realloc()"); + exit(EX_OSERR); + } + } + memcpy(collector + collector_offset, buf, len + 1); + collector_offset += len; + } + if(buf[len-1] != '\n') continue; + + if(collector_offset) { + assert(collector[collector_offset-1] == '\n'); + process_line(fname, collector, ++lineno); + collector_offset = 0; + } else { + process_line(fname, buf, ++lineno); + } + } + + if(fp != stdin) + fclose(fp); + + return 0; +} + +static int +process_line(const char *fname, char *line, int lineno) { + char buf[32]; + char *op; /* '<' */ + char *cl; /* '>' */ + char *tcl_pos; /* tag class position */ + char *tl_pos; + char *v_pos; + int constr; + ber_tlv_tag_t tag_value; + ber_tlv_tag_t tag_class; + ber_tlv_tag_t tlv_tag; + ber_tlv_len_t tlv_len; + ber_tlv_len_t tl_len; + ssize_t ret; + (void)fname; + + /* Find a tag opening angle bracket */ + for(; *line == ' '; line++); + op = line; + if(*op != '<') { + fprintf(stderr, "%s: Missing '<' after whitespace\n", fname); + exit(EX_DATAERR); + } + + /* Find a tag closing angle bracket */ + for(; *line && *line != '>'; line++) { + if(*line < ' ') { + fprintf(stderr, "%s: Invalid charset (%d)\n", + fname, *(const unsigned char *)line); + exit(EX_DATAERR); + } + } + cl = line; + if(*cl != '>') { + fprintf(stderr, "%s: Missing '>'\n", fname); + exit(EX_DATAERR); + } + + /* Ignore closing tags */ + if(op[1] == '/') { + if(strchr(cl, '<')) { /* We are not very robust */ + fprintf(stderr, "%s: Multiple tags per line\n", fname); + exit(EX_DATAERR); + } + /* End-of-content octets */ + if(op[2] == 'I') { + buf[0] = buf[1] = 0x00; + fwrite(buf, 1, 2, stdout); + } + return 0; + } + + switch(op[1]) { + case 'C': constr = 1; break; + case 'P': constr = 0; break; + case 'I': constr = 2; break; + default: + fprintf(stderr, + "%s: Expected \"C\"/\"P\"/\"I\" as the XML tag name (%c)\n", + fname, op[1]); + exit(EX_DATAERR); + } + + *cl = '\0'; + if(cl[-1] == 'F') { + fprintf(stderr, "%s: Uses pretty-printing of values. " + "Use -p option to unber\n", fname); + exit(EX_DATAERR); + } + + tcl_pos = strstr(op, "T=\"["); + tl_pos = strstr(op, "TL=\""); + v_pos = strstr(op, "V=\""); + if(!tcl_pos || !tl_pos || !v_pos) { + fprintf(stderr, + "%s: Mandatory attribute %s is not found at line %d\n", + fname, (!tcl_pos)?"T":(v_pos?"V":"TCL"), lineno); + exit(EX_DATAERR); + } + errno = 0; + tl_len = strtoul(tl_pos + 4, 0, 10); + if(constr == 2) { + tlv_len = 0; + } else { + tlv_len = strtoul(v_pos + 3, 0, 10); + } + if(errno || tl_len < 2 || tlv_len < 0) { + fprintf(stderr, "%s: Invalid TL or V value at line %d\n", + fname, lineno); + exit(EX_DATAERR); + } + + tcl_pos += 4; + switch(*tcl_pos) { + case 'U': /* UNIVERSAL */ + tag_class = ASN_TAG_CLASS_UNIVERSAL; break; + case 'P': /* PRIVATE */ + tag_class = ASN_TAG_CLASS_PRIVATE; break; + case 'A': /* APPLICATION */ + tag_class = ASN_TAG_CLASS_APPLICATION; break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': /* context */ + tag_class = ASN_TAG_CLASS_CONTEXT; break; + default: + fprintf(stderr, "%s: Invalid tag class (%c) at line %d\n", + fname, tcl_pos[4], lineno); + exit(EX_DATAERR); + } + for(;; tcl_pos++) { + switch(*tcl_pos) { + case '"': tcl_pos = ""; + case '\0': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + break; + default: continue; + } + break; + } + errno = 0; + if(!*tcl_pos + || ((long)(tag_value = strtoul(tcl_pos, 0, 10))) < 0 + || errno) { + fprintf(stderr, "%s: Invalid tag value (%c) at line %d\n", + fname, *tcl_pos, lineno); + exit(EX_DATAERR); + } + tlv_tag = ((tag_value << 2) | tag_class); + + if(0) { + printf("[%s>]\n", op); + printf(" <%c T=\"%s\" TL=\"%d\" V=\"%d\">\n", + constr?'C':'P', + ber_tlv_tag_string(tlv_tag), + tl_len, tlv_len); + } + + ret = ber_tlv_tag_serialize(tlv_tag, buf, sizeof(buf)); + assert(ret >= 1 && (size_t)ret < sizeof(buf)); + if(constr == 2) { + buf[ret] = 0x80; + ret += 1; + } else { + ret += der_tlv_length_serialize(tlv_len, + buf + ret, sizeof(buf) - ret); + assert(ret >= 2 && (size_t)ret < sizeof(buf)); + } + if(ret != tl_len) { + fprintf(stderr, "%s: Cannot encode TL at line %d " + "in the given number of bytes (%d!=%d)\n", + fname, lineno, ret, tl_len); + exit(EX_DATAERR); + } + if(constr) *buf |= 0x20; /* Enable "constructed" bit */ + fwrite(buf, 1, tl_len, stdout); + + if(!constr) { + ber_tlv_len_t len; + for(len = 0, cl++; *cl && *cl != '<'; cl++, len++) { + unsigned char v; + int h; + if(*cl != '&') { + fputc(*cl, stdout); + continue; + } + cl++; + if(*cl != 'x') { + fprintf(stderr, "%s: Expected \"&xNN;\" at line %d\n", + fname, lineno); + exit(EX_DATAERR); + } + for(v = 0, h = 0; h < 2; h++) { + unsigned char clv = *++cl; + v <<= 4; + switch(clv) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + v |= clv - '0'; break; + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + v |= clv - 'A' + 10; break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + v |= clv - 'a' + 10; break; + default: + fprintf(stderr, + "%s: Expected \"&xNN;\" at line %d (%c)\n", + fname, lineno, clv); + exit(EX_DATAERR); + } + } + cl++; + if(*cl != ';') { + fprintf(stderr, + "%s: Expected \"&xNN;\" at line %d\n", + fname, lineno); + exit(EX_DATAERR); + } + fputc(v, stdout); + } + if(len != tlv_len) { + fprintf(stderr, + "%s: Could not encode value of %d chars " + "at line %d in %d bytes\n", + fname, len, lineno, tlv_len); + exit(EX_DATAERR); + } + } + + return 0; +} + diff --git a/asn1c/unber.1 b/asn1c/unber.1 index ec731a04..a96b0f5d 100644 --- a/asn1c/unber.1 +++ b/asn1c/unber.1 @@ -1,22 +1,95 @@ -.de Id +.de Vb +.sp +.ft CW +.nf +.. +.de Ve +.ft R +.fi +.sp .. -.Id $Id" .TH UNBER 1 "\*(Dt" "ASN.1 BER Decoder" "ASN.1 BER Decoder" .SH NAME unber \- ASN.1 BER Decoder .SH SYNOPSIS -unber [\fB\-t\fR\fIdata-string\fR] [\fB-\fR] \fIinfile\fR... +unber [\fB-1\fR] [\fB-i\fRindent] [\fB-p\fR] [\fB\-t\fR\fIdata-string\fR] [\fB-\fR] \fIinfile\fR... .SH DESCRIPTION -unber takes the BER-encoded files and dumps their internal structure to stdout. -(The DER and CER formats are both subsets of the BER, and are also supported.) +unber takes the BER-encoded files and dumps their internal structure as human readable text. A single dash represents the standard input. +.sp +(The DER and CER formats are both subsets of the BER, and are also supported.) .SH OPTIONS .TP +\fB\-1\fR +Do \fInot\fR attempt to read the next BER structure after the first one. +This may be useful if the input contains garbage past the single BER sequence. +By default, unber continues decoding until the end of file (input stream). +.TP +\fB\-i\fR \fIindent\fR +Use the specified number of spaces for output indentation. Default is 4 spaces. +.TP +\fB\-p\fR +Do \fInot\fR attempt pretty-printing of known ASN.1 types (strings, INTEGER, BOOLEAN, etc). +.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 XML FORMAT +unber dumps the output in the regular XML format which preserves most of the +information from the underlying binary encoding. +.P +The XML opening tag format is as follows: +.Vb +\& <\fItform\fR T="\fItag\fR" TL="\fItl_len\fR" V="{Indefinite|\fIv_len\fR}" [A="\fItype\fR"] [\fIF\fR]> +.Ve +Where: +.TP +\fItform\fR +Which form the value is in: primitive ("P") or constructed ("C") or constructed with indefinite length ("I") +.TP +\fItag\fR +The tag class and value +.TP +\fItl_len\fR +The length of the TL (BER Tag and Length) encoding +.TP +\fIv_len\fR +The length of the value (V, encoded by the L), may be "Indefinite" +.TP +\fItype\fR +Likely name of the underlying ASN.1 type (for UNIVERSAL tags) +.TP +[\fIF\fR] +Indicates that the value was reformatted (pretty-printed) +.P +Example: +.Vb +\& <I T="[UNIVERSAL 16]" TL="2" V="Indefinite" A="SEQUENCE"> +\& <P T="[UNIVERSAL 19]" TL="2" V="2" A="PrintableString">US</P> +\& <C T="[UNIVERSAL 16]" TL="2" V="11" A="SEQUENCE"> +\& <P T="[UNIVERSAL 2]" TL="2" V="4" A="INTEGER" F>832970823</P> +\& </C T="[UNIVERSAL 16]" A="SEQUENCE"> +\& </I T="[UNIVERSAL 16]" A="SEQUENCE"> +.Ve +.SH EXAMPLES +Decode the given Tag/Length sequence given in hexadecimal form: +.Vb +\& unber \fB-t\fR "\fIbf 20\fR" +.Ve +Decode the given DER file using two-spaces indentation: +.Vb +\& unber \fB-i\fR \fI2\fR \fIfilename.der\fR +.Ve +Decode the binary stream taken from the standard input: +.Vb +\& cat \fI...\fR | unber \fB-\fR +.Ve +Decode the binary stream into the same stream (see \fIenber\fR\|(1)): +.Vb +\& cat \fI...\fR | unber \fB-\fR | enber \fB-\fR > filename.ber\fI\fR +.Ve .SH SEE ALSO .TP -\&\fIasn1c\fR\|(1) +\&\fIenber\fR\|(1), \&\fIasn1c\fR\|(1) .SH AUTHORS Lev Walkin <vlm@lionet.info> diff --git a/asn1c/unber.c b/asn1c/unber.c index a954ece1..60f3a036 100644 --- a/asn1c/unber.c +++ b/asn1c/unber.c @@ -1,3 +1,30 @@ +/*- + * Copyright (c) 2004 Lev Walkin <vlm@lionet.info> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -13,11 +40,13 @@ #include <asn1parser.h> /* For static string tables */ -#include <asn_types.h> -#include <ber_tlv_tag.h> -#include <ber_tlv_length.h> +#include <asn_application.h> +#include <constraints.c> #include <ber_tlv_tag.c> #include <ber_tlv_length.c> +#include <OBJECT_IDENTIFIER.c> +#include <RELATIVE-OID.c> +#include <INTEGER.c> static void usage(const char *av0); /* Print the Usage screen and exit */ static int process(const char *fname); /* Perform the BER decoding */ @@ -27,7 +56,9 @@ static int decode_tlv_from_string(const char *datastring); #define COPYRIGHT \ "Copyright (c) 2004 Lev Walkin <vlm@lionet.info>\n" -static char *indent_buffer = " "; +static int single_type_decoding = 0; /* -1 enables that */ +static int pretty_printing = 1; /* -p disables that */ +static char *indent_buffer = " "; /* -i controls that */ int main(int ac, char **av) { @@ -37,12 +68,18 @@ main(int ac, char **av) { /* * Process command-line options. */ - while((ch = getopt(ac, av, "i:t:v")) != -1) + while((ch = getopt(ac, av, "1hi:pt:v")) != -1) switch(ch) { + case '1': + single_type_decoding = 1; + break; case 't': if(decode_tlv_from_string(optarg)) exit(EX_DATAERR); exit(0); + case 'p': + pretty_printing = 0; + break; case 'i': i = atoi(optarg); if(i >= 0 && i < 16) { @@ -58,6 +95,7 @@ main(int ac, char **av) { fprintf(stderr, "ASN.1 BER Decoder, v" VERSION "\n" COPYRIGHT); exit(0); break; + case 'h': default: usage(av[0]); } @@ -96,9 +134,21 @@ usage(const char *av0) { "ASN.1 BER Decoder, v" VERSION "\n" COPYRIGHT "Usage: %s [options] [-] [file ...]\n" "Options:\n" -" -i <indent> Amount of spaces for indentation (default is 4)\n" -" -t <data-string> Decode the given tag[/length] sequence\n" -" (e.g. -t \"bf 20\")\n" +" -1 Decode only the first BER structure (otherwise, until EOF)\n" +" -i <indent> Amount of spaces for output indentation (default is 4)\n" +" -p Do not attempt pretty-printing of known ASN.1 types\n" +" -t <data-string> Decode the given tag[/length] sequence (e.g. -t \"bf20\")\n" +"\n" +"The XML opening tag format is as follows:\n" +" <tform T=\"tag\" TL=\"tl_len\" V=\"{Indefinite|v_len}\" [A=\"type\"] [F]>\n" +"Where:\n" +" tform Which form the value is in: constructed (\"C\", \"I\") or primitive (\"P\")\n" +" tag The tag class and value\n" +" tl_len The length of the TL (BER Tag and Length) encoding\n" +" v_len The length of the value (V, encoded by the L), may be \"Indefinite\"\n" +" type Likely name of the underlying ASN.1 type (for [UNIVERSAL n] tags)\n" +" [F] Indicates that the value was reformatted (pretty-printed)\n" +"See the manual page for details\n" , av0); exit(EX_USAGE); } @@ -135,7 +185,7 @@ process(const char *fname) { */ do { pdc = process_deeper(fname, fp, 0, -1); - } while(pdc == PD_FINISHED); /* Wait until PD_EOF */ + } while(pdc == PD_FINISHED && !single_type_decoding); if(fp != stdin) fclose(fp); @@ -216,6 +266,11 @@ static pd_code_e process_deeper(const char *fname, FILE *fp, int level, ssize_t continue; } + if(tagbuf[0] == '\0' && tagbuf[1] == '\0') { + /* End of content octets */ + return PD_FINISHED; + } + /* Make sure the T & L decoders took exactly the whole buffer */ assert((t_len + l_len) == tblen); @@ -249,6 +304,7 @@ static pd_code_e process_deeper(const char *fname, FILE *fp, int level, ssize_t if(tlv_len == -1) tlv_len = limit; + printf(">\n"); /* Close the opening tag */ pdc = process_deeper(fname, fp, level + 1, tlv_len); if(pdc == PD_FAILED) return pdc; } else { @@ -269,11 +325,6 @@ static pd_code_e process_deeper(const char *fname, FILE *fp, int level, ssize_t 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("</P>\n"); return; @@ -282,19 +333,19 @@ print_TL(int fin, int level, int constr, ssize_t tlen, ber_tlv_tag_t tlv_tag, be while(level-- > 0) printf(indent_buffer); /* Print indent */ printf(fin ? "</" : "<"); - printf(constr ? "C" : "P"); + printf(constr ? ((tlv_len == -1) ? "I" : "C") : "P"); printf(" T=\""); ber_tlv_tag_fwrite(tlv_tag, stdout); printf("\""); if(!fin) { - printf(" tL=\"%ld\"", (long)tlen); + printf(" TL=\"%ld\"", (long)tlen); if(tlv_len == -1) - printf(" L=\"Indefinite\""); + printf(" V=\"Indefinite\""); else - printf(" L=\"%ld\"", (long)tlv_len); + printf(" V=\"%ld\"", (long)tlv_len); } if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL) { @@ -304,25 +355,86 @@ print_TL(int fin, int level, int constr, ssize_t tlen, ber_tlv_tag_t tlv_tag, be if(str) printf(" A=\"%s\"", str); } - if(fin || constr) - printf(">\n"); - else - printf(">"); + if(fin) printf(">\n"); } +/* + * Print the value in binary form, or reformat for pretty-printing. + */ static int print_V(const char *fname, FILE *fp, ber_tlv_tag_t tlv_tag, ber_tlv_len_t tlv_len) { + asn1_integer_t *arcs = 0; /* Object identifier arcs */ + unsigned char *vbuf = 0; asn1p_expr_type_e etype = 0; - long collector = 0; + asn1_integer_t collector = 0; + int special_format = 0; ssize_t i; /* Figure out what type is it */ - if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL) { + if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL + && pretty_printing) { ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag); etype = ASN_UNIVERSAL_TAG2TYPE(tvalue); } /* + * Determine how to print the value, either in its native binary form, + * encoded with &xNN characters, or using pretty-printing. + * The basic string types (including "useful types", like UTCTime) + * are excempt from this determination logic, because their alphabets + * are subsets of the XML's native UTF-8 encoding. + */ + switch(etype) { + case ASN_BASIC_BOOLEAN: + if(tlv_len == 1) + special_format = 1; + else + etype = 0; + break; + case ASN_BASIC_INTEGER: + case ASN_BASIC_ENUMERATED: + if((size_t)tlv_len <= sizeof(collector)) + special_format = 1; + else + etype = 0; + break; + case ASN_BASIC_OBJECT_IDENTIFIER: + case ASN_BASIC_RELATIVE_OID: + if(tlv_len > 0 && tlv_len < 128*1024 /* VERY long OID! */) { + arcs = malloc(sizeof(*arcs) * (tlv_len + 1)); + if(arcs) { + vbuf = malloc(tlv_len + 1); + /* Not checking is intentional */ + } + } + case ASN_BASIC_UTCTime: + case ASN_BASIC_GeneralizedTime: + case ASN_STRING_NumericString: + case ASN_STRING_PrintableString: + case ASN_STRING_VisibleString: + case ASN_STRING_IA5String: + case ASN_STRING_UTF8String: + break; /* Directly compatible with UTF-8 */ + case ASN_STRING_BMPString: + case ASN_STRING_UniversalString: + break; /* Not directly compatible with UTF-8 */ + default: + /* Conditionally compatible with UTF-8 */ + if(( + (etype & ASN_STRING_MASK) + || + (etype == ASN_BASIC_OCTET_STRING) + ) && (tlv_len > 0 && tlv_len < 128 * 1024)) { + vbuf = malloc(tlv_len + 1); + /* Not checking is intentional */ + } + break; + } + + /* If collection vbuf is present, defer printing the F flag. */ + if(!vbuf) printf(special_format ? " F>" : ">"); + + /* * Print the value in binary or text form. */ for(i = 0; i < tlv_len; i++) { @@ -330,6 +442,8 @@ print_V(const char *fname, FILE *fp, ber_tlv_tag_t tlv_tag, ber_tlv_len_t tlv_le if(ch == -1) { fprintf(stderr, "%s: Unexpected end of file (V)\n", fname); + if(vbuf) free(vbuf); + if(arcs) free(arcs); return -1; } switch(etype) { @@ -338,6 +452,7 @@ print_V(const char *fname, FILE *fp, ber_tlv_tag_t tlv_tag, ber_tlv_len_t tlv_le case ASN_STRING_NumericString: case ASN_STRING_PrintableString: case ASN_STRING_VisibleString: + case ASN_STRING_IA5String: case ASN_STRING_UTF8String: switch(ch) { default: @@ -354,33 +469,112 @@ print_V(const char *fname, FILE *fp, ber_tlv_tag_t tlv_tag, ber_tlv_len_t tlv_le } break; case ASN_BASIC_BOOLEAN: - if(tlv_len == 1) { - switch(ch) { - case 0: printf("<false/>"); break; - case 0xff: printf("<true/>"); break; - default: printf("<true value=\"&x%02x\"/>", ch); - } - break; + switch(ch) { + case 0: printf("<false/>"); break; + case 0xff: printf("<true/>"); break; + default: printf("<true value=\"&x%02x\"/>", ch); } - /* Fall through */ + break; 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) collector = collector * 256 + ch; + else collector = (int)(signed char)ch; + break; + default: + if(vbuf) { + vbuf[i] = ch; + } else { + printf("&x%02x;", ch); + } + } + } + + /* Do post-processing */ + switch(etype) { + case ASN_BASIC_INTEGER: + case ASN_BASIC_ENUMERATED: + printf("%" PRIdMAX, collector); + break; + case ASN_BASIC_OBJECT_IDENTIFIER: + if(vbuf) { + OBJECT_IDENTIFIER_t oid; + int arcno; + + oid.buf = vbuf; + oid.size = tlv_len; + + arcno = OBJECT_IDENTIFIER_get_arcs(&oid, arcs, + sizeof(*arcs), tlv_len + 1); + if(arcno >= 0) { + assert(arcno <= (tlv_len + 1)); + printf(" F>"); + for(i = 0; i < arcno; i++) { + if(i) printf("."); + printf("%" PRIuASN, arcs[i]); } - if((i+1) == tlv_len) - printf("%ld", collector); - break; + free(vbuf); + vbuf = 0; + } + } + break; + case ASN_BASIC_RELATIVE_OID: + if(vbuf) { + RELATIVE_OID_t oid; + int arcno; + + oid.buf = vbuf; + oid.size = tlv_len; + + arcno = RELATIVE_OID_get_arcs(&oid, arcs, + sizeof(*arcs), tlv_len); + if(arcno >= 0) { + assert(arcno <= (tlv_len + 1)); + printf(" F>"); + for(i = 0; i < arcno; i++) { + if(i) printf("."); + printf("%" PRIuASN, arcs[i]); + } + free(vbuf); + vbuf = 0; } - /* Fall through */ - default: - printf("&x%02x;", ch); } + break; + default: break; } + /* + * If the buffer was not consumed, print it out. + */ + if(vbuf) { + int binary; + + /* + * Check whether the data could be represented as text + */ + binary = -1 * (tlv_len >> 2); /* Threshold is 25% binary */ + for(i = 0; i < tlv_len; i++) { + switch(vbuf[i]) { + case 0x1b: binary = 1; break; + case 0x09: case 0x0a: case 0x0d: continue; + default: + if(vbuf[i] < 0x20 || (vbuf[i] & 0x80)) + if(++binary > 0) /* Way too many */ + break; + continue; + } + break; + } + printf(">"); + for(i = 0; i < tlv_len; i++) { + if(binary > 0 || vbuf[i] < 0x20 || (vbuf[i] & 0x80)) + printf("&x%02x;", vbuf[i]); + else + printf("%c", vbuf[i]); + } + free(vbuf); + } + + if(arcs) free(arcs); return 0; } @@ -479,3 +673,9 @@ decode_tlv_from_string(const char *datastring) { return 0; } + +/* + * Dummy functions. + */ +ber_dec_rval_t ber_check_tags(asn1_TYPE_descriptor_t *td, ber_dec_ctx_t *opt_ctx, void *ptr, size_t size, int tag_mode, ber_tlv_len_t *last_length, int *opt_tlv_form) { ber_dec_rval_t rv; (void)td; (void)opt_ctx; (void)ptr; (void)size; (void)tag_mode; (void)last_length; (void)opt_tlv_form; return rv; } +ssize_t der_write_tags(asn1_TYPE_descriptor_t *td, size_t slen, int tag_mode, ber_tlv_tag_t tag, asn_app_consume_bytes_f *cb, void *app_key) { (void)td; (void)slen; (void)tag_mode; (void)tag; (void)cb; (void)app_key; return -1; } |