blob: 5b4963859ac343efbafcd7f8e37197c2bd7a09b2 [file] [log] [blame]
Lev Walkin12984672004-09-24 21:00:15 +00001/*-
Lev Walkind36ac722005-03-18 08:33:14 +00002 * Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
Lev Walkin12984672004-09-24 21:00:15 +00003 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 * $Id$
26 */
Lev Walkinc0d04912005-02-25 12:52:27 +000027#include "sys-common.h"
Lev Walkin12984672004-09-24 21:00:15 +000028
29#include <asn1parser.h> /* For static string tables */
30
31#include <asn_application.h>
32#include <constraints.c>
33#include <ber_tlv_tag.c>
34#include <ber_tlv_length.c>
35
Lev Walkinc0d04912005-02-25 12:52:27 +000036#undef COPYRIGHT
37#define COPYRIGHT \
Lev Walkind36ac722005-03-18 08:33:14 +000038 "Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>\n"
Lev Walkinc0d04912005-02-25 12:52:27 +000039
Lev Walkin0cb0fc52004-09-27 20:31:19 +000040static void usage(const char *av0, int);/* Print the Usage screen and exit */
Lev Walkin12984672004-09-24 21:00:15 +000041static int process(const char *fname); /* Perform the BER decoding */
42static int process_line(const char *fname, char *line, int lineno);
43
Lev Walkin0cb0fc52004-09-27 20:31:19 +000044static int no_validation; /* -n */
45
Lev Walkin12984672004-09-24 21:00:15 +000046int
47main(int ac, char **av) {
48 int ch; /* Command line character */
49 int i; /* Index in some loops */
50
51 /*
52 * Process command-line options.
53 */
Lev Walkin0cb0fc52004-09-27 20:31:19 +000054 while((ch = getopt(ac, av, "nhv")) != -1)
Lev Walkin12984672004-09-24 21:00:15 +000055 switch(ch) {
Lev Walkin0cb0fc52004-09-27 20:31:19 +000056 case 'n':
57 no_validation++;
58 break;
Lev Walkin12984672004-09-24 21:00:15 +000059 case 'v':
Lev Walkin0cb0fc52004-09-27 20:31:19 +000060 usage(av[0], 1);
Lev Walkin12984672004-09-24 21:00:15 +000061 break;
62 case 'h':
63 default:
Lev Walkin0cb0fc52004-09-27 20:31:19 +000064 usage(av[0], 0);
Lev Walkin12984672004-09-24 21:00:15 +000065 }
66
67 /*
68 * Ensure that there are some input files present.
69 */
70 if(ac > optind) {
71 ac -= optind;
72 av += optind;
73 } else {
74 fprintf(stderr, "%s: No input files specified\n", av[0]);
75 exit(1);
76 }
77
78 setvbuf(stdout, 0, _IOLBF, 0);
79
80 /*
81 * Iterate over input files and parse each.
82 * All syntax trees from all files will be bundled together.
83 */
84 for(i = 0; i < ac; i++) {
85 if(process(av[i]))
86 exit(EX_DATAERR);
87 }
88
89 return 0;
90}
91
92/*
93 * Print the usage screen and exit(EX_USAGE).
94 */
95static void
Lev Walkin0cb0fc52004-09-27 20:31:19 +000096usage(const char *av0, int copyright_only) {
97 fprintf(stderr,
98 "Convert unber(1)'s output back into BER, "
99 "v" VERSION "\n" COPYRIGHT);
100 if(copyright_only) exit(0);
Lev Walkin12984672004-09-24 21:00:15 +0000101 fprintf(stderr,
Lev Walkin0cb0fc52004-09-27 20:31:19 +0000102 "Usage: %s [-n] [-] [file ...]\n"
103 "Options:\n"
104 " -n Disable XML input validation\n", av0);
Lev Walkin12984672004-09-24 21:00:15 +0000105 exit(EX_USAGE);
106}
107
108/*
109 * Open the file and initiate recursive processing.
110 */
111static int
112process(const char *fname) {
113 char buf[8192];
114 char *collector = 0;
115 size_t collector_size = sizeof(buf);
116 size_t collector_offset = 0;
117 int lineno = 0;
118 FILE *fp;
119
120 if(strcmp(fname, "-")) {
121 fp = fopen(fname, "r");
122 if(!fp) {
123 perror(fname);
124 return -1;
125 }
126 } else {
127 fp = stdin;
128 }
129
130
131 while(fgets(buf, sizeof(buf), fp) || !feof(fp)) {
132 size_t len = strlen(buf);
133
134 if(!len) continue;
135 if(collector_offset || buf[len-1] != '\n') {
136 if((collector_size - collector_offset) <= len
137 || !collector) {
138 collector_size <<= 1;
Lev Walkin9db7ce82006-09-13 02:59:46 +0000139 collector = REALLOC(collector, collector_size);
Lev Walkin12984672004-09-24 21:00:15 +0000140 if(!collector) {
141 perror("realloc()");
142 exit(EX_OSERR);
143 }
144 }
145 memcpy(collector + collector_offset, buf, len + 1);
146 collector_offset += len;
147 }
148 if(buf[len-1] != '\n') continue;
149
150 if(collector_offset) {
151 assert(collector[collector_offset-1] == '\n');
152 process_line(fname, collector, ++lineno);
153 collector_offset = 0;
154 } else {
155 process_line(fname, buf, ++lineno);
156 }
157 }
158
159 if(fp != stdin)
160 fclose(fp);
161
162 return 0;
163}
164
165static int
166process_line(const char *fname, char *line, int lineno) {
167 char buf[32];
168 char *op; /* '<' */
169 char *cl; /* '>' */
Lev Walkined8173b2005-04-08 04:36:06 +0000170 char *tcl_pos; /* tag class (T=") position */
171 char *tl_pos; /* tag length (TL=") position */
172 char *v_pos; /* value length (V=") position */
Lev Walkin12984672004-09-24 21:00:15 +0000173 int constr;
174 ber_tlv_tag_t tag_value;
175 ber_tlv_tag_t tag_class;
176 ber_tlv_tag_t tlv_tag;
177 ber_tlv_len_t tlv_len;
Lev Walkined8173b2005-04-08 04:36:06 +0000178 ber_tlv_len_t opt_tl_len; /* optional TL length */
Lev Walkin12984672004-09-24 21:00:15 +0000179 ssize_t ret;
180 (void)fname;
181
Lev Walkinf9854342005-06-08 00:06:59 +0000182 /* Skip the whitespace */
Lev Walkin0cb0fc52004-09-27 20:31:19 +0000183 for(; *line == ' ' || *line == '\t'; line++);
Lev Walkinf9854342005-06-08 00:06:59 +0000184
185 /* Find a tag opening angle bracket */
Lev Walkin12984672004-09-24 21:00:15 +0000186 op = line;
Lev Walkin0cb0fc52004-09-27 20:31:19 +0000187 switch(*op) {
188 case '<': /* That's what we want! A tag opening */
189 break;
190 case '-': /* This is a comment (dash-dash) */
191 if(op[1] == *op)
192 case '\r':
193 case '\n':
194 case '#': /* This is a comment */
195 return 0;
196 default:
Lev Walkinf9854342005-06-08 00:06:59 +0000197 fprintf(stderr,
198 "%s: Missing '<' after whitespace at line %d\n",
199 fname, lineno);
Lev Walkin12984672004-09-24 21:00:15 +0000200 exit(EX_DATAERR);
201 }
202
203 /* Find a tag closing angle bracket */
204 for(; *line && *line != '>'; line++) {
205 if(*line < ' ') {
Lev Walkin0af5f1a2005-01-17 11:45:14 +0000206 fprintf(stderr, "%s: Invalid charset (%d) at line %d\n",
207 fname, *(const unsigned char *)line, lineno);
Lev Walkin12984672004-09-24 21:00:15 +0000208 exit(EX_DATAERR);
209 }
210 }
211 cl = line;
212 if(*cl != '>') {
Lev Walkind36ac722005-03-18 08:33:14 +0000213 fprintf(stderr, "%s: Missing '>' at line %d\n", fname, lineno);
Lev Walkin12984672004-09-24 21:00:15 +0000214 exit(EX_DATAERR);
215 }
216
217 /* Ignore closing tags */
218 if(op[1] == '/') {
219 if(strchr(cl, '<')) { /* We are not very robust */
Lev Walkind36ac722005-03-18 08:33:14 +0000220 fprintf(stderr,
221 "%s: Multiple tags per line at line %d\n",
222 fname, lineno);
Lev Walkin12984672004-09-24 21:00:15 +0000223 exit(EX_DATAERR);
224 }
225 /* End-of-content octets */
226 if(op[2] == 'I') {
227 buf[0] = buf[1] = 0x00;
228 fwrite(buf, 1, 2, stdout);
229 }
230 return 0;
231 }
232
233 switch(op[1]) {
Lev Walkind36ac722005-03-18 08:33:14 +0000234 case '!': return 0; /* A comment */
235 case '?': return 0; /* An XML preamble */
Lev Walkin12984672004-09-24 21:00:15 +0000236 case 'C': constr = 1; break;
237 case 'P': constr = 0; break;
238 case 'I': constr = 2; break;
239 default:
240 fprintf(stderr,
Lev Walkind36ac722005-03-18 08:33:14 +0000241 "%s: Expected \"C\"/\"P\"/\"I\" as the XML tag name (%c) at line %d\n",
242 fname, op[1], lineno);
Lev Walkin12984672004-09-24 21:00:15 +0000243 exit(EX_DATAERR);
244 }
245
246 *cl = '\0';
247 if(cl[-1] == 'F') {
Lev Walkind36ac722005-03-18 08:33:14 +0000248 fprintf(stderr,
249 "%s: Detected pretty-printing of primitive types at line %d. "
250 "Re-run `unber` with -p option to disable pretty-printing.\n", fname, lineno);
Lev Walkin12984672004-09-24 21:00:15 +0000251 exit(EX_DATAERR);
252 }
253
254 tcl_pos = strstr(op, "T=\"[");
255 tl_pos = strstr(op, "TL=\"");
256 v_pos = strstr(op, "V=\"");
Lev Walkined8173b2005-04-08 04:36:06 +0000257 if(!tcl_pos || (!v_pos && constr != 2)) {
Lev Walkin12984672004-09-24 21:00:15 +0000258 fprintf(stderr,
259 "%s: Mandatory attribute %s is not found at line %d\n",
Lev Walkined8173b2005-04-08 04:36:06 +0000260 fname, (!tcl_pos)?"T":"V", lineno);
Lev Walkin12984672004-09-24 21:00:15 +0000261 exit(EX_DATAERR);
262 }
263 errno = 0;
Lev Walkined8173b2005-04-08 04:36:06 +0000264 opt_tl_len = tl_pos ? strtoul(tl_pos + 4, 0, 10) : 0;
Lev Walkin12984672004-09-24 21:00:15 +0000265 if(constr == 2) {
266 tlv_len = 0;
267 } else {
268 tlv_len = strtoul(v_pos + 3, 0, 10);
269 }
Lev Walkined8173b2005-04-08 04:36:06 +0000270 if(errno || (opt_tl_len && opt_tl_len < 2) || tlv_len < 0) {
Lev Walkin12984672004-09-24 21:00:15 +0000271 fprintf(stderr, "%s: Invalid TL or V value at line %d\n",
272 fname, lineno);
273 exit(EX_DATAERR);
274 }
275
276 tcl_pos += 4;
277 switch(*tcl_pos) {
278 case 'U': /* UNIVERSAL */
279 tag_class = ASN_TAG_CLASS_UNIVERSAL; break;
280 case 'P': /* PRIVATE */
281 tag_class = ASN_TAG_CLASS_PRIVATE; break;
282 case 'A': /* APPLICATION */
283 tag_class = ASN_TAG_CLASS_APPLICATION; break;
284 case '0': case '1': case '2': case '3': case '4':
285 case '5': case '6': case '7': case '8': case '9': /* context */
286 tag_class = ASN_TAG_CLASS_CONTEXT; break;
287 default:
288 fprintf(stderr, "%s: Invalid tag class (%c) at line %d\n",
289 fname, tcl_pos[4], lineno);
290 exit(EX_DATAERR);
291 }
292 for(;; tcl_pos++) {
293 switch(*tcl_pos) {
294 case '"': tcl_pos = "";
295 case '\0':
296 case '0': case '1': case '2': case '3': case '4':
297 case '5': case '6': case '7': case '8': case '9':
298 break;
299 default: continue;
300 }
301 break;
302 }
303 errno = 0;
304 if(!*tcl_pos
305 || ((long)(tag_value = strtoul(tcl_pos, 0, 10))) < 0
306 || errno) {
307 fprintf(stderr, "%s: Invalid tag value (%c) at line %d\n",
308 fname, *tcl_pos, lineno);
309 exit(EX_DATAERR);
310 }
311 tlv_tag = ((tag_value << 2) | tag_class);
312
Lev Walkin12984672004-09-24 21:00:15 +0000313 ret = ber_tlv_tag_serialize(tlv_tag, buf, sizeof(buf));
314 assert(ret >= 1 && (size_t)ret < sizeof(buf));
315 if(constr == 2) {
316 buf[ret] = 0x80;
317 ret += 1;
318 } else {
319 ret += der_tlv_length_serialize(tlv_len,
320 buf + ret, sizeof(buf) - ret);
321 assert(ret >= 2 && (size_t)ret < sizeof(buf));
322 }
Lev Walkined8173b2005-04-08 04:36:06 +0000323 if(opt_tl_len && ret != opt_tl_len) {
Lev Walkin12984672004-09-24 21:00:15 +0000324 fprintf(stderr, "%s: Cannot encode TL at line %d "
Lev Walkinfeae8de2005-01-17 14:56:25 +0000325 "in the given number of bytes (%ld!=%ld)\n",
Lev Walkined8173b2005-04-08 04:36:06 +0000326 fname, lineno, (long)ret, (long)opt_tl_len);
Lev Walkin12984672004-09-24 21:00:15 +0000327 exit(EX_DATAERR);
328 }
329 if(constr) *buf |= 0x20; /* Enable "constructed" bit */
Lev Walkined8173b2005-04-08 04:36:06 +0000330 fwrite(buf, 1, ret, stdout);
Lev Walkin12984672004-09-24 21:00:15 +0000331
332 if(!constr) {
333 ber_tlv_len_t len;
334 for(len = 0, cl++; *cl && *cl != '<'; cl++, len++) {
335 unsigned char v;
336 int h;
337 if(*cl != '&') {
338 fputc(*cl, stdout);
339 continue;
340 }
341 cl++;
Lev Walkin451af472004-10-25 22:58:49 +0000342 if(*cl != '#') {
343 fputc(*cl, stdout);
344 continue;
345 }
346 cl++;
Lev Walkin12984672004-09-24 21:00:15 +0000347 if(*cl != 'x') {
Lev Walkin451af472004-10-25 22:58:49 +0000348 fprintf(stderr, "%s: Expected \"&#xNN;\" at line %d\n",
Lev Walkin12984672004-09-24 21:00:15 +0000349 fname, lineno);
350 exit(EX_DATAERR);
351 }
352 for(v = 0, h = 0; h < 2; h++) {
353 unsigned char clv = *++cl;
354 v <<= 4;
355 switch(clv) {
356 case '0': case '1': case '2': case '3': case '4':
357 case '5': case '6': case '7': case '8': case '9':
358 v |= clv - '0'; break;
359 case 'A': case 'B': case 'C':
360 case 'D': case 'E': case 'F':
361 v |= clv - 'A' + 10; break;
362 case 'a': case 'b': case 'c':
363 case 'd': case 'e': case 'f':
364 v |= clv - 'a' + 10; break;
365 default:
366 fprintf(stderr,
Lev Walkin451af472004-10-25 22:58:49 +0000367 "%s: Expected \"&#xNN;\" at line %d (%c)\n",
Lev Walkin12984672004-09-24 21:00:15 +0000368 fname, lineno, clv);
369 exit(EX_DATAERR);
370 }
371 }
372 cl++;
373 if(*cl != ';') {
374 fprintf(stderr,
Lev Walkin451af472004-10-25 22:58:49 +0000375 "%s: Expected \"&#xNN;\" at line %d\n",
Lev Walkin12984672004-09-24 21:00:15 +0000376 fname, lineno);
377 exit(EX_DATAERR);
378 }
379 fputc(v, stdout);
380 }
381 if(len != tlv_len) {
Lev Walkin0cb0fc52004-09-27 20:31:19 +0000382 if(no_validation) fprintf(stderr, "Warning: ");
Lev Walkin12984672004-09-24 21:00:15 +0000383 fprintf(stderr,
Lev Walkin0320d872005-06-02 17:53:17 +0000384 "%s: Could not encode value of %ld chars "
385 "at line %d in %ld bytes\n",
386 fname, (long)len, lineno, (long)tlv_len);
Lev Walkin0cb0fc52004-09-27 20:31:19 +0000387 if(!no_validation) exit(EX_DATAERR);
Lev Walkin12984672004-09-24 21:00:15 +0000388 }
389 }
390
391 return 0;
392}
393