-
Notifications
You must be signed in to change notification settings - Fork 12
/
dumpasn1.c
3312 lines (2932 loc) · 93.3 KB
/
dumpasn1.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* ASN.1 data display code, copyright Peter Gutmann
<[email protected]>, based on ASN.1 dump program by David Kemp,
with contributions from various people including Matthew Hamrick, Bruno
Couillard, Hallvard Furuseth, Geoff Thorpe, David Boyce, John Hughes,
'Life is hard, and then you die', Hans-Olof Hermansson, Tor Rustad,
Kjetil Barvik, James Sweeny, Chris Ridd, David Lemley, John Tobey, James
Manger and several other people whose names I've misplaced.
Available from http://www.cs.auckland.ac.nz/~pgut001/dumpasn1.c. Last
updated 8 June 2013 (version 20130608, if you prefer it that way).
To build under Windows, use 'cl /MD dumpasn1.c'. To build on OS390 or
z/OS, use '/bin/c89 -D OS390 -o dumpasn1 dumpasn1.c'.
This code grew slowly over time without much design or planning, and with
extra features being tacked on as required. It's not representative of my
normal coding style. cryptlib,
http://www.cs.auckland.ac.nz/~pgut001/cryptlib/, does a much better job of
checking ASN.1 than this does, since dumpasn1 is a display program written
to accept the widest possible range of input and not a compliance checker.
In other words it will bend over backwards to accept even invalid data,
since a common use for it is to try and locate encoding problems that lead
to invalid encoded data. While it will warn about some types of common
errors, the fact that dumpasn1 will display an ASN.1 data item doesn't mean
that the item is valid.
dumpasn1 requires a config file dumpasn1.cfg to be present in the same
location as the program itself or in a standard directory where binaries
live (it will run without it but will display a warning message, you can
configure the path either by hardcoding it in or using an environment
variable as explained further down). The config file is available from
http://www.cs.auckland.ac.nz/~pgut001/dumpasn1.cfg.
This code assumes that the input data is binary, having come from a MIME-
aware mailer or been piped through a decoding utility if the original
format used base64 encoding. If you need to decode it, it's recommended
that you use a utility like uudeview, which will strip virtually any kind
of encoding (MIME, PEM, PGP, whatever) to recover the binary original.
You can use this code in whatever way you want, as long as you don't try to
claim you wrote it.
Editing notes: Tabs to 4, phasers to malky (and in case anyone wants to
complain about that, see "Program Indentation and Comprehensiblity",
Richard Miara, Joyce Musselman, Juan Navarro, and Ben Shneiderman,
Communications of the ACM, Vol.26, No.11 (November 1983), p.861) */
#include <ctype.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef OS390
#include <unistd.h>
#endif /* OS390 */
/* The update string, printed as part of the help screen */
#define UPDATE_STRING "8 June 2013"
/* Useful defines */
#ifndef TRUE
#define FALSE 0
#define TRUE ( !FALSE )
#endif /* TRUE */
#ifndef BYTE
typedef unsigned char BYTE;
#endif /* BYTE */
/* Tandem Guardian NonStop Kernel options */
#ifdef __TANDEM
#pragma nolist /* Spare us the source listing, no GUI... */
#pragma nowarn (1506) /* Implicit type conversion: int to char etc */
#endif /* __TANDEM */
/* SunOS 4.x doesn't define seek codes or exit codes or FILENAME_MAX (it does
define _POSIX_MAX_PATH, but in funny locations and to different values
depending on which include file you use). Strictly speaking this code
isn't right since we need to use PATH_MAX, however not all systems define
this, some use _POSIX_PATH_MAX, and then there are all sorts of variations
and other defines that you have to check, which require about a page of
code to cover each OS, so we just use max( FILENAME_MAX, 512 ) which
should work for everything */
#ifndef SEEK_SET
#define SEEK_SET 0
#define SEEK_CUR 2
#endif /* No fseek() codes defined */
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#define EXIT_SUCCESS ( !EXIT_FAILURE )
#endif /* No exit() codes defined */
#ifndef FILENAME_MAX
#define FILENAME_MAX 512
#else
#if FILENAME_MAX < 128
#undef FILENAME_MAX
#define FILENAME_MAX 512
#endif /* FILENAME_MAX < 128 */
#endif /* FILENAME_MAX */
/* Under Windows we can do special-case handling for paths and Unicode
strings (although in practice it can't really handle much except
latin-1) */
#if ( defined( _WINDOWS ) || defined( WIN32 ) || defined( _WIN32 ) || \
defined( __WIN32__ ) )
#include <windows.h>
#include <io.h> /* For _setmode() */
#include <fcntl.h> /* For _setmode() codes */
#ifndef _O_U16TEXT
#define _O_U16TEXT 0x20000 /* _setmode() code */
#endif /* !_O_U16TEXT */
#define __WIN32__
#endif /* Win32 */
/* Under Unix we can do special-case handling for paths and Unicode strings.
Detecting Unix systems is a bit tricky but the following should find most
versions. This define implicitly assumes that the system has wchar_t
support, but this is almost always the case except for very old systems,
so it's best to default to allow-all rather than deny-all */
#if defined( linux ) || defined( __linux__ ) || defined( sun ) || \
defined( __bsdi__ ) || defined( __FreeBSD__ ) || defined( __NetBSD__ ) || \
defined( __OpenBSD__ ) || defined( __hpux ) || defined( _M_XENIX ) || \
defined( __osf__ ) || defined( _AIX ) || defined( __MACH__ )
#define __UNIX__
#endif /* Every commonly-used Unix */
#if defined( linux ) || defined( __linux__ )
#ifndef __USE_ISOC99
#define __USE_ISOC99
#endif /* __USE_ISOC99 */
#include <wchar.h>
#endif /* Linux */
/* For IBM mainframe OSes we use the Posix environment, so it looks like
Unix */
#ifdef OS390
#define __OS390__
#define __UNIX__
#endif /* OS390 / z/OS */
/* Tandem NSK: Don't tangle with Tandem OSS, which is almost UNIX */
#ifdef __TANDEM
#ifdef _GUARDIAN_TARGET
#define __TANDEM_NSK__
#else
#define __UNIX__
#endif /* _GUARDIAN_TARGET */
#endif /* __TANDEM */
/* Some OS's don't define the min() macro */
#ifndef min
#define min(a,b) ( ( a ) < ( b ) ? ( a ) : ( b ) )
#endif /* !min */
/* Macros to avoid problems with sign extension */
#define byteToInt( x ) ( ( BYTE ) ( x ) )
/* The level of recursion can get scary for deeply-nested structures so we
use a larger-than-normal stack under DOS */
#ifdef __TURBOC__
extern unsigned _stklen = 16384;
#endif /* __TURBOC__ */
/* Turn off pointless VC++ warnings */
#ifdef _MSC_VER
#pragma warning( disable: 4018 )
#endif /* VC++ */
/* When we dump a nested data object encapsulated within a larger object, the
length is initially set to a magic value which is adjusted to the actual
length once we start parsing the object */
#define LENGTH_MAGIC 177545L
/* Tag classes */
#define CLASS_MASK 0xC0 /* Bits 8 and 7 */
#define UNIVERSAL 0x00 /* 0 = Universal (defined by ITU X.680) */
#define APPLICATION 0x40 /* 1 = Application */
#define CONTEXT 0x80 /* 2 = Context-specific */
#define PRIVATE 0xC0 /* 3 = Private */
/* Encoding type */
#define FORM_MASK 0x20 /* Bit 6 */
#define PRIMITIVE 0x00 /* 0 = primitive */
#define CONSTRUCTED 0x20 /* 1 = constructed */
/* Universal tags */
#define TAG_MASK 0x1F /* Bits 5 - 1 */
#define EOC 0x00 /* 0: End-of-contents octets */
#define BOOLEAN 0x01 /* 1: Boolean */
#define INTEGER 0x02 /* 2: Integer */
#define BITSTRING 0x03 /* 2: Bit string */
#define OCTETSTRING 0x04 /* 4: Byte string */
#define NULLTAG 0x05 /* 5: NULL */
#define OID 0x06 /* 6: Object Identifier */
#define OBJDESCRIPTOR 0x07 /* 7: Object Descriptor */
#define EXTERNAL 0x08 /* 8: External */
#define REAL 0x09 /* 9: Real */
#define ENUMERATED 0x0A /* 10: Enumerated */
#define EMBEDDED_PDV 0x0B /* 11: Embedded Presentation Data Value */
#define UTF8STRING 0x0C /* 12: UTF8 string */
#define SEQUENCE 0x10 /* 16: Sequence/sequence of */
#define SET 0x11 /* 17: Set/set of */
#define NUMERICSTRING 0x12 /* 18: Numeric string */
#define PRINTABLESTRING 0x13 /* 19: Printable string (ASCII subset) */
#define T61STRING 0x14 /* 20: T61/Teletex string */
#define VIDEOTEXSTRING 0x15 /* 21: Videotex string */
#define IA5STRING 0x16 /* 22: IA5/ASCII string */
#define UTCTIME 0x17 /* 23: UTC time */
#define GENERALIZEDTIME 0x18 /* 24: Generalized time */
#define GRAPHICSTRING 0x19 /* 25: Graphic string */
#define VISIBLESTRING 0x1A /* 26: Visible string (ASCII subset) */
#define GENERALSTRING 0x1B /* 27: General string */
#define UNIVERSALSTRING 0x1C /* 28: Universal string */
#define BMPSTRING 0x1E /* 30: Basic Multilingual Plane/Unicode string */
/* Length encoding */
#define LEN_XTND 0x80 /* Indefinite or long form */
#define LEN_MASK 0x7F /* Bits 7 - 1 */
/* Various special-case operations to perform on strings */
typedef enum {
STR_NONE, /* No special handling */
STR_UTCTIME, /* Check it's UTCTime */
STR_GENERALIZED, /* Check it's GeneralizedTime */
STR_PRINTABLE, /* Check it's a PrintableString */
STR_IA5, /* Check it's an IA5String */
STR_LATIN1, /* Read and display string as latin-1 */
STR_BMP, /* Read and display string as Unicode */
STR_BMP_REVERSED /* STR_BMP with incorrect endianness */
} STR_OPTION;
/* Structure to hold info on an ASN.1 item */
typedef struct {
int id; /* Tag class + primitive/constructed */
int tag; /* Tag */
long length; /* Data length */
int indefinite; /* Item has indefinite length */
int nonCanonical; /* Non-canonical length encoding used */
BYTE header[ 16 ]; /* Tag+length data */
int headerSize; /* Size of tag+length */
} ASN1_ITEM;
/* Configuration options */
static int printDots = FALSE; /* Whether to print dots to align columns */
static int doPure = FALSE; /* Print data without LHS info column */
static int doDumpHeader = FALSE; /* Dump tag+len in hex (level = 0, 1, 2) */
static int extraOIDinfo = FALSE; /* Print extra information about OIDs */
static int doHexValues = FALSE; /* Display size, offset in hex not dec.*/
static int useStdin = FALSE; /* Take input from stdin */
static int zeroLengthAllowed = FALSE;/* Zero-length items allowed */
static int dumpText = FALSE; /* Dump text alongside hex data */
static int printAllData = FALSE; /* Whether to print all data in long blocks */
static int checkEncaps = TRUE; /* Print encaps.data in BIT/OCTET STRINGs */
static int checkCharset = TRUE; /* Check val.of char strs.hidden in OCTET STRs */
#ifndef __OS390__
static int reverseBitString = TRUE; /* Print BIT STRINGs in natural order */
#else
static int reverseBitString = FALSE;/* Natural order on OS390 is the same as ASN.1 */
#endif /* __OS390__ */
static int rawTimeString = FALSE; /* Print raw time strings */
static int shallowIndent = FALSE; /* Perform shallow indenting */
static int outputWidth = 80; /* 80-column display */
static int maxNestLevel = 100; /* Maximum nesting level for which to display output */
static int doOutlineOnly = FALSE; /* Only display constructed-object outline */
/* Formatting information used for the fixed informational column to the
left of the displayed data */
static int infoWidth = 4;
static const char *indentStringTbl[] = {
NULL, NULL, NULL,
" : ", /* "xxx xxx: " (3) */
" : ", /* "xxxx xxxx: " (4) */
" : ", /* "xxxxx xxxxx: " (5) */
" : ", /* "xxxxxx xxxxxx: " (6) */
" : ", /* "xxxxxxx xxxxxxx: " (7) */
" : ", /* "xxxxxxxx xxxxxxxx: " (8) */
"", "", "", ""
};
static const char *lenTbl[] = {
NULL, NULL, NULL,
"%3ld %3ld: ", "%4ld %4ld: ", "%5ld %5ld: ",
"%6ld %6ld: ", "%7ld %7ld: ", "%8ld %8ld: ",
"", "", "", ""
};
static const char *lenIndefTbl[] = {
NULL, NULL, NULL,
"%3ld NDF: ", "%4ld NDEF: ", "%5ld INDEF: ",
"%6ld INDEF : ", "%7ld INDEF : ", "%8ld INDEF : ",
"", "", "", ""
};
static const char *lenHexTbl[] = {
NULL, NULL, NULL,
"%03lX %3lX: ", "%04lX %4lX: ", "%05lX %5lX: ",
"%06lX %6lX: ", "%07lX %7lX: ", "%08lX %8lX: ",
"", "", "", ""
};
static const char *lenHexIndefTbl[] = {
NULL, NULL, NULL,
"%03lX NDF: ", "%04lX NDEF: ", "%05lX INDEF: ",
"%06lX INDEF : ", "%07lX INDEF : ", "%08lX INDEF : ",
"", "", "", ""
};
#define INDENT_SIZE ( infoWidth + 1 + infoWidth + 1 + 1 )
#define INDENT_STRING indentStringTbl[ infoWidth ]
#define LEN lenTbl[ infoWidth ]
#define LEN_INDEF lenIndefTbl[ infoWidth ]
#define LEN_HEX lenHexTbl[ infoWidth ]
#define LEN_HEX_INDEF lenHexIndefTbl[ infoWidth ]
/* Error and warning information */
static int noErrors = 0; /* Number of errors found */
static int noWarnings = 0; /* Number of warnings */
/* Position in the input stream */
static int fPos = 0; /* Absolute position in data */
/* The output stream */
static FILE *output; /* Output stream */
/* Information on an ASN.1 Object Identifier */
#define MAX_OID_SIZE 32
typedef struct tagOIDINFO {
struct tagOIDINFO *next; /* Next item in list */
BYTE oid[ MAX_OID_SIZE ];
int oidLength;
char *comment, *description; /* Name, rank, serial number */
int warn; /* Whether to warn if OID encountered */
} OIDINFO;
static OIDINFO *oidList = NULL;
/* If the config file isn't present in the current directory, we search the
following paths (this is needed for Unix with dumpasn1 somewhere in the
path, since this doesn't set up argv[0] to the full path). Anything
beginning with a '$' uses the appropriate environment variable. In
addition under Unix we also walk down $PATH looking for it */
#ifdef __TANDEM_NSK__
#define CONFIG_NAME "asn1cfg"
#else
#define CONFIG_NAME "dumpasn1.cfg"
#endif /* __TANDEM_NSK__ */
#if defined( __TANDEM_NSK__ )
static const char *configPaths[] = {
"$system.security", "$system.system",
NULL
};
#elif defined( __WIN32__ )
static const char *configPaths[] = {
/* Windoze absolute paths (yeah, this code has been around for awhile,
why do you ask?) */
"c:\\windows\\", "c:\\winnt\\",
/* It's my program, I'm allowed to hardcode in strange paths that no-one
else uses */
"c:\\program files\\bin\\",
"c:\\program files (x86)\\bin\\",
/* This one seems to be popular as well */
"c:\\program files\\utilities\\",
"c:\\program files (x86)\\utilities\\",
/* General environment-based paths */
"$DUMPASN1_PATH/",
NULL
};
#elif defined( __OS390__ )
static const char *configPaths[] = {
/* General environment-based paths */
"$DUMPASN1_PATH/",
NULL
};
#else
static const char *configPaths[] = {
#ifndef DEBIAN
/* Unix absolute paths */
"/usr/bin/", "/usr/local/bin/", "/etc/dumpasn1/",
/* Unix environment-based paths */
"$HOME/", "$HOME/bin/",
/* It's my program, I'm allowed to hardcode in strange paths that no-one
else uses */
"$HOME/BIN/",
#else
/* Debian has specific places where you're supposed to dump things. Note
the dot after $HOME, since config files are supposed to start with a
dot for Debian */
"$HOME/.", "/etc/dumpasn1/",
#endif /* DEBIAN-specific paths */
/* General environment-based paths */
"$DUMPASN1_PATH/",
NULL
};
#endif /* OS-specific search paths */
#define isEnvTerminator( c ) \
( ( ( c ) == '/' ) || ( ( c ) == '.' ) || ( ( c ) == '$' ) || \
( ( c ) == '\0' ) || ( ( c ) == '~' ) )
/****************************************************************************
* *
* Object Identification/Description Routines *
* *
****************************************************************************/
/* Return descriptive strings for universal tags */
static char *idstr( const int tagID )
{
switch( tagID )
{
case EOC:
return( "End-of-contents octets" );
case BOOLEAN:
return( "BOOLEAN" );
case INTEGER:
return( "INTEGER" );
case BITSTRING:
return( "BIT STRING" );
case OCTETSTRING:
return( "OCTET STRING" );
case NULLTAG:
return( "NULL" );
case OID:
return( "OBJECT IDENTIFIER" );
case OBJDESCRIPTOR:
return( "ObjectDescriptor" );
case EXTERNAL:
return( "EXTERNAL" );
case REAL:
return( "REAL" );
case ENUMERATED:
return( "ENUMERATED" );
case EMBEDDED_PDV:
return( "EMBEDDED PDV" );
case UTF8STRING:
return( "UTF8String" );
case SEQUENCE:
return( "SEQUENCE" );
case SET:
return( "SET" );
case NUMERICSTRING:
return( "NumericString" );
case PRINTABLESTRING:
return( "PrintableString" );
case T61STRING:
return( "TeletexString" );
case VIDEOTEXSTRING:
return( "VideotexString" );
case IA5STRING:
return( "IA5String" );
case UTCTIME:
return( "UTCTime" );
case GENERALIZEDTIME:
return( "GeneralizedTime" );
case GRAPHICSTRING:
return( "GraphicString" );
case VISIBLESTRING:
return( "VisibleString" );
case GENERALSTRING:
return( "GeneralString" );
case UNIVERSALSTRING:
return( "UniversalString" );
case BMPSTRING:
return( "BMPString" );
default:
return( "Unknown (Reserved)" );
}
}
/* Return information on an object identifier */
static OIDINFO *getOIDinfo( const BYTE *oid, const int oidLength )
{
const BYTE oidByte = oid[ 1 ];
OIDINFO *oidPtr;
for( oidPtr = oidList; oidPtr != NULL; oidPtr = oidPtr->next )
{
if( oidLength != oidPtr->oidLength - 2 )
continue; /* Quick-reject check */
if( oidByte != oidPtr->oid[ 2 + 1 ] )
continue; /* Quick-reject check */
if( !memcmp( oidPtr->oid + 2, oid, oidLength ) )
return( oidPtr );
}
return( NULL );
}
/* Add an OID attribute */
static int addAttribute( char **buffer, char *attribute )
{
if( ( *buffer = ( char * ) malloc( strlen( attribute ) + 1 ) ) == NULL )
{
puts( "Out of memory." );
return( FALSE );
}
strcpy( *buffer, attribute );
return( TRUE );
}
/* Table to identify valid string chars (taken from cryptlib). Note that
IA5String also allows control chars, but we warn about these since
finding them in a certificate is a sign that there's something
seriously wrong */
#define P 1 /* PrintableString */
#define I 2 /* IA5String */
#define PI 3 /* IA5String and PrintableString */
static int charFlags[] = {
/* 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* ! " # $ % & ' ( ) * + , - . / */
PI, I, I, I, I, I, I, PI, PI, PI, I, PI, PI, PI, PI, PI,
/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, I, I, PI, I, PI,
/* @ A B C D E F G H I J K L M N O */
I, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,
/* P Q R S T U V W X Y Z [ \ ] ^ _ */
PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, I, I, I, I, I,
/* ` a b c d e f g h i j k l m n o */
I, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,
/* p q r s t u v w x y z { | } ~ DL */
PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, I, I, I, I, 0
};
static int isPrintable( int ch )
{
if( ch >= 128 || !( charFlags[ ch ] & P ) )
return( FALSE );
return( TRUE );
}
static int isIA5( int ch )
{
if( ch >= 128 || !( charFlags[ ch ] & I ) )
return( FALSE );
return( TRUE );
}
/****************************************************************************
* *
* Config File Read Routines *
* *
****************************************************************************/
/* Files coming from DOS/Windows systems may have a ^Z (the CP/M EOF char)
at the end, so we need to filter this out */
#define CPM_EOF 0x1A /* ^Z = CPM EOF char */
/* The maximum input line length */
#define MAX_LINESIZE 512
/* Read a line of text from the config file */
static int lineNo;
static int readLine( FILE *file, char *buffer )
{
int bufCount = 0, ch;
/* Skip whitespace */
while( ( ( ch = getc( file ) ) == ' ' || ch == '\t' ) && !feof( file ) );
/* Get a line into the buffer */
while( ch != '\r' && ch != '\n' && ch != CPM_EOF && !feof( file ) )
{
/* Check for an illegal char in the data. Note that we don't just
check for chars with high bits set because these are legal in
non-ASCII strings */
if( !isprint( ch ) )
{
printf( "Bad character '%c' in config file line %d.\n",
ch, lineNo );
return( FALSE );
}
/* Check to see if it's a comment line */
if( ch == '#' && !bufCount )
{
/* Skip comment section and trailing whitespace */
while( ch != '\r' && ch != '\n' && ch != CPM_EOF && !feof( file ) )
ch = getc( file );
break;
}
/* Make sure that the line is of the correct length */
if( bufCount > MAX_LINESIZE )
{
printf( "Config file line %d too long.\n", lineNo );
return( FALSE );
}
else
if( ch ) /* Can happen if we read a binary file */
buffer[ bufCount++ ] = ch;
/* Get next character */
ch = getc( file );
}
/* If we've just passed a CR, check for a following LF */
if( ch == '\r' )
{
if( ( ch = getc( file ) ) != '\n' )
ungetc( ch, file );
}
/* Skip trailing whitespace and add der terminador */
while( bufCount > 0 &&
( ( ch = buffer[ bufCount - 1 ] ) == ' ' || ch == '\t' ) )
bufCount--;
buffer[ bufCount ] = '\0';
/* Handle special-case of ^Z if file came off an MSDOS system */
if( ch == CPM_EOF )
{
while( !feof( file ) )
{
/* Keep going until we hit the true EOF (or some sort of error) */
( void ) getc( file );
}
}
return( ferror( file ) ? FALSE : TRUE );
}
/* Process an OID specified as space-separated decimal or hex digits */
static int processOID( OIDINFO *oidInfo, char *string )
{
BYTE binaryOID[ MAX_OID_SIZE ];
long value;
int firstValue, valueIndex = 0, oidIndex = 3;
memset( binaryOID, 0, MAX_OID_SIZE );
binaryOID[ 0 ] = OID;
while( *string && oidIndex < MAX_OID_SIZE )
{
if( oidIndex >= MAX_OID_SIZE - 4 )
{
printf( "Excessively long OID in config file line %d.\n",
lineNo );
return( FALSE );
}
if( sscanf( string, "%8ld", &value ) != 1 || value < 0 )
{
printf( "Invalid value in config file line %d.\n", lineNo );
return( FALSE );
}
if( valueIndex == 0 )
{
firstValue = value;
valueIndex++;
}
else
{
if( valueIndex == 1 )
{
if( firstValue < 0 || firstValue > 2 || value < 0 || \
( ( firstValue < 2 && value > 39 ) || \
( firstValue == 2 && value > 175 ) ) )
{
printf( "Invalid value in config file line %d.\n",
lineNo );
return( FALSE );
}
binaryOID[ 2 ] = ( firstValue * 40 ) + ( int ) value;
valueIndex++;
}
else
{
int hasHighBits = FALSE;
if( value >= 0x200000L ) /* 2^21 */
{
binaryOID[ oidIndex++ ] = 0x80 | ( int ) ( value >> 21 );
value %= 0x200000L;
hasHighBits = TRUE;
}
if( ( value >= 0x4000 ) || hasHighBits ) /* 2^14 */
{
binaryOID[ oidIndex++ ] = 0x80 | ( int ) ( value >> 14 );
value %= 0x4000;
hasHighBits = TRUE;
}
if( ( value >= 0x80 ) || hasHighBits ) /* 2^7 */
{
binaryOID[ oidIndex++ ] = 0x80 | ( int ) ( value >> 7 );
value %= 128;
}
binaryOID[ oidIndex++ ] = ( int ) value;
}
}
while( *string && isdigit( byteToInt( *string ) ) )
string++;
if( *string && *string++ != ' ' )
{
printf( "Invalid OID string in config file line %d.\n", lineNo );
return( FALSE );
}
}
binaryOID[ 1 ] = oidIndex - 2;
memcpy( oidInfo->oid, binaryOID, oidIndex );
oidInfo->oidLength = oidIndex;
return( TRUE );
}
static int processHexOID( OIDINFO *oidInfo, char *string )
{
int value, index = 0;
while( *string && index < MAX_OID_SIZE - 1 )
{
if( sscanf( string, "%4x", &value ) != 1 || value > 255 )
{
printf( "Invalid hex value in config file line %d.\n", lineNo );
return( FALSE );
}
oidInfo->oid[ index++ ] = value;
string += 2;
if( *string && *string++ != ' ' )
{
printf( "Invalid hex string in config file line %d.\n", lineNo );
return( FALSE );
}
}
oidInfo->oid[ index ] = 0;
oidInfo->oidLength = index;
if( index >= MAX_OID_SIZE - 1 )
{
printf( "OID value in config file line %d too long.\n", lineNo );
return( FALSE );
}
return( TRUE );
}
/* Read a config file */
static int readConfig( const char *path, const int isDefaultConfig )
{
OIDINFO dummyOID = { NULL, "Dummy", 0, "Dummy", "Dummy", 1 }, *oidPtr;
FILE *file;
int seenHexOID = FALSE;
char buffer[ MAX_LINESIZE ];
int status;
/* Try and open the config file */
if( ( file = fopen( path, "rb" ) ) == NULL )
{
/* If we can't open the default config file, issue a warning but
continue anyway */
if( isDefaultConfig )
{
puts( "Cannot open config file 'dumpasn1.cfg', which should be in the same" );
puts( "directory as the dumpasn1 program, a standard system directory, or" );
puts( "in a location pointed to by the DUMPASN1_PATH environment variable." );
puts( "Operation will continue without the ability to display Object " );
puts( "Identifier information." );
puts( "" );
puts( "If the config file is located elsewhere, you can set the environment" );
puts( "variable DUMPASN1_PATH to the path to the file." );
return( TRUE );
}
printf( "Cannot open config file '%s'.\n", path );
return( FALSE );
}
/* Add the new config entries at the appropriate point in the OID list */
if( oidList == NULL )
oidPtr = &dummyOID;
else
for( oidPtr = oidList; oidPtr->next != NULL; oidPtr = oidPtr->next );
/* Read each line in the config file */
lineNo = 1;
while( ( status = readLine( file, buffer ) ) == TRUE && !feof( file ) )
{
/* If it's a comment line, skip it */
if( !*buffer )
{
lineNo++;
continue;
}
/* Check for an attribute tag */
if( !strncmp( buffer, "OID = ", 6 ) )
{
/* Make sure that all of the required attributes for the current
OID are present */
if( oidPtr->description == NULL )
{
printf( "OID ending on config file line %d has no "
"description attribute.\n", lineNo - 1 );
return( FALSE );
}
/* Allocate storage for the new OID */
if( ( oidPtr->next = ( OIDINFO * ) malloc( sizeof( OIDINFO ) ) ) == NULL )
{
puts( "Out of memory." );
return( FALSE );
}
oidPtr = oidPtr->next;
if( oidList == NULL )
oidList = oidPtr;
memset( oidPtr, 0, sizeof( OIDINFO ) );
/* Add the new OID */
if( !strncmp( buffer + 6, "06", 2 ) )
{
seenHexOID = TRUE;
if( !processHexOID( oidPtr, buffer + 6 ) )
return( FALSE );
}
else
{
if( !processOID( oidPtr, buffer + 6 ) )
return( FALSE );
}
/* Check that this OID isn't already present in the OID list.
This is a quick-and-dirty n^2 algorithm so it's not enabled
by default */
#if 0
{
OIDINFO *oidCursor;
for( oidCursor = oidList; oidCursor->next != NULL; oidCursor = oidCursor->next )
{
if( oidCursor->oidLength == oidPtr->oidLength && \
!memcmp( oidCursor->oid, oidPtr->oid, oidCursor->oidLength ) )
{
printf( "Duplicate OID '%s' at line %d.\n",
buffer, lineNo );
}
}
}
#endif /* 0 */
}
else if( !strncmp( buffer, "Description = ", 14 ) )
{
if( oidPtr->description != NULL )
{
printf( "Duplicate OID description in config file line %d.\n",
lineNo );
return( FALSE );
}
if( !addAttribute( &oidPtr->description, buffer + 14 ) )
return( FALSE );
}
else if( !strncmp( buffer, "Comment = ", 10 ) )
{
if( oidPtr->comment != NULL )
{
printf( "Duplicate OID comment in config file line %d.\n",
lineNo );
return( FALSE );
}
if( !addAttribute( &oidPtr->comment, buffer + 10 ) )
return( FALSE );
}
else if( !strncmp( buffer, "Warning", 7 ) )
{
if( oidPtr->warn )
{
printf( "Duplicate OID warning in config file line %d.\n",
lineNo );
return( FALSE );
}
oidPtr->warn = TRUE;
}
else
{
printf( "Unrecognised attribute '%s', line %d.\n", buffer,
lineNo );
return( FALSE );
}
lineNo++;
}
fclose( file );
/* If we're processing an old-style config file, tell the user to
upgrade */
if( seenHexOID )
{
puts( "\nWarning: Use of old-style hex OIDs detected in "
"configuration file, please\n update your dumpasn1 "
"configuration file.\n" );
}
return( status );
}
/* Check for the existence of a config file path (access() isn't available
on all systems) */
static int testConfigPath( const char *path )
{
FILE *file;
/* Try and open the config file */
if( ( file = fopen( path, "rb" ) ) == NULL )
return( FALSE );
fclose( file );
return( TRUE );
}
/* Build a config path by substituting environment strings for $NAMEs */
static void buildConfigPath( char *path, const char *pathTemplate )
{
char pathBuffer[ FILENAME_MAX ], newPath[ FILENAME_MAX ];
int pathLen, pathPos = 0, newPathPos = 0;
/* Add the config file name at the end */
strcpy( pathBuffer, pathTemplate );
strcat( pathBuffer, CONFIG_NAME );
pathLen = strlen( pathBuffer );
while( pathPos < pathLen )
{
char *strPtr;
int substringSize;
/* Find the next $ and copy the data before it to the new path */
if( ( strPtr = strstr( pathBuffer + pathPos, "$" ) ) != NULL )
substringSize = ( int ) ( ( strPtr - pathBuffer ) - pathPos );
else
substringSize = pathLen - pathPos;
if( substringSize > 0 )
{
memcpy( newPath + newPathPos, pathBuffer + pathPos,
substringSize );
}
newPathPos += substringSize;
pathPos += substringSize;
/* Get the environment string for the $NAME */
if( strPtr != NULL )
{
char envName[ MAX_LINESIZE ], *envString;
int i;
/* Skip the '$', find the end of the $NAME, and copy the name
into an internal buffer */
pathPos++; /* Skip the $ */
for( i = 0; !isEnvTerminator( pathBuffer[ pathPos + i ] ); i++ );
memcpy( envName, pathBuffer + pathPos, i );
envName[ i ] = '\0';