Commit 12984671 authored by Lev Walkin's avatar Lev Walkin

unber and enber

parent 5beb7deb
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
......
......@@ -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:
......
.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>
/*-
* 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.
......
.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>
/*-
* 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;
}
.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>
/*-
* 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,24 +355,85 @@ 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.
*/
......@@ -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; }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment