Python253

Dumpasn1_Peter_Gutmann

May 11th, 2018
222
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 102.19 KB | None | 0 0
  1. /* ASN.1 data display code, copyright Peter Gutmann
  2.    <pgut001@cs.auckland.ac.nz>, based on ASN.1 dump program by David Kemp,
  3.    with contributions from various people including Matthew Hamrick, Bruno
  4.    Couillard, Hallvard Furuseth, Geoff Thorpe, David Boyce, John Hughes,
  5.    'Life is hard, and then you die', Hans-Olof Hermansson, Tor Rustad,
  6.    Kjetil Barvik, James Sweeny, Chris Ridd, David Lemley, John Tobey, James
  7.    Manger and several other people whose names I've misplaced.
  8.  
  9.    Available from http://www.cs.auckland.ac.nz/~pgut001/dumpasn1.c. Last
  10.    updated 9 March 2017 (version 20170309, if you prefer it that way).  
  11.    To build under Windows, use 'cl /MD dumpasn1.c'.  To build on OS390 or
  12.    z/OS, use '/bin/c89 -D OS390 -o dumpasn1 dumpasn1.c'.
  13.  
  14.    This code grew slowly over time without much design or planning, and with
  15.    extra features being tacked on as required.  It's not representative of my
  16.    normal coding style, and should only be used as a debugging/diagnostic
  17.    tool and not in a production environment (I'm not sure how you'd use
  18.    it in production anyway, but felt I should point that out).  cryptlib,
  19.    http://www.cs.auckland.ac.nz/~pgut001/cryptlib/, does a much better job of
  20.    checking ASN.1 than this does, since dumpasn1 is a display program written
  21.    to accept the widest possible range of input and not a compliance checker.
  22.    In other words it will bend over backwards to accept even invalid data,
  23.    since a common use for it is to try and locate encoding problems that lead
  24.    to invalid encoded data.  While it will warn about some types of common
  25.    errors, the fact that dumpasn1 will display an ASN.1 data item doesn't mean
  26.    that the item is valid.
  27.  
  28.    dumpasn1 requires a config file dumpasn1.cfg to be present in the same
  29.    location as the program itself or in a standard directory where binaries
  30.    live (it will run without it but will display a warning message, you can
  31.    configure the path either by hardcoding it in or using an environment
  32.    variable as explained further down).  The config file is available from
  33.    http://www.cs.auckland.ac.nz/~pgut001/dumpasn1.cfg.
  34.  
  35.    This code assumes that the input data is binary, having come from a MIME-
  36.    aware mailer or been piped through a decoding utility if the original
  37.    format used base64 encoding.  If you need to decode it, it's recommended
  38.    that you use a utility like uudeview, which will strip virtually any kind
  39.    of encoding (MIME, PEM, PGP, whatever) to recover the binary original.
  40.  
  41.    You can use this code in whatever way you want, as long as you don't try
  42.    to claim you wrote it.
  43.  
  44.    (Someone asked for clarification on what this means, treat it as a very
  45.    mild form of the BSD license in which you're not required to include LONG
  46.    LEGAL DISCLAIMERS IN ALL CAPS but just a small note in a corner somewhere
  47.    (e.g. the back of a manual) that you're using the dumpasn1 code.  And if
  48.    you do use it, please make sure you're using a recent version, I
  49.    occasionally see screen shots from incredibly ancient versions that are
  50.    nowhere near as good as what current versions produce).
  51.  
  52.    Editing notes: Tabs to 4, phasers to malky (and in case anyone wants to
  53.    complain about that, see "Program Indentation and Comprehensiblity",
  54.    Richard Miara, Joyce Musselman, Juan Navarro, and Ben Shneiderman,
  55.    Communications of the ACM, Vol.26, No.11 (November 1983), p.861) */
  56.  
  57. #include <ctype.h>
  58. #include <limits.h>
  59. #include <stdarg.h>
  60. #include <stdio.h>
  61. #include <stdlib.h>
  62. #include <string.h>
  63. #ifdef OS390
  64.   #include <unistd.h>
  65. #endif /* OS390 */
  66.  
  67. /* The update string, printed as part of the help screen */
  68.  
  69. #define UPDATE_STRING   "9 March 2017"
  70.  
  71. /* Useful defines */
  72.  
  73. #ifndef TRUE
  74.   #define FALSE 0
  75.   #define TRUE  ( !FALSE )
  76. #endif /* TRUE */
  77. #ifndef BYTE
  78.   typedef unsigned char     BYTE;
  79. #endif /* BYTE */
  80.  
  81. /* Tandem Guardian NonStop Kernel options */
  82.  
  83. #ifdef __TANDEM
  84.   #pragma nolist        /* Spare us the source listing, no GUI... */
  85.   #pragma nowarn (1506) /* Implicit type conversion: int to char etc */
  86. #endif /* __TANDEM */
  87.  
  88. /* SunOS 4.x doesn't define seek codes or exit codes or FILENAME_MAX (it does
  89.    define _POSIX_MAX_PATH, but in funny locations and to different values
  90.    depending on which include file you use).  Strictly speaking this code
  91.    isn't right since we need to use PATH_MAX, however not all systems define
  92.    this, some use _POSIX_PATH_MAX, and then there are all sorts of variations
  93.    and other defines that you have to check, which require about a page of
  94.    code to cover each OS, so we just use max( FILENAME_MAX, 512 ) which
  95.    should work for everything */
  96.  
  97. #ifndef SEEK_SET
  98.   #define SEEK_SET  0
  99.   #define SEEK_CUR  2
  100. #endif /* No fseek() codes defined */
  101. #ifndef EXIT_FAILURE
  102.   #define EXIT_FAILURE  1
  103.   #define EXIT_SUCCESS  ( !EXIT_FAILURE )
  104. #endif /* No exit() codes defined */
  105. #ifndef FILENAME_MAX
  106.   #define FILENAME_MAX  512
  107. #else
  108.   #if FILENAME_MAX < 128
  109.     #undef FILENAME_MAX
  110.     #define FILENAME_MAX    512
  111.   #endif /* FILENAME_MAX < 128 */
  112. #endif /* FILENAME_MAX */
  113.  
  114. /* Under Windows we can do special-case handling for paths and Unicode
  115.    strings (although in practice it can't really handle much except
  116.    latin-1) */
  117.  
  118. #if ( defined( _WINDOWS ) || defined( WIN32 ) || defined( _WIN32 ) || \
  119.       defined( __WIN32__ ) )
  120.   #include <windows.h>
  121.   #include <io.h>                   /* For _setmode() */
  122.   #include <fcntl.h>                /* For _setmode() codes */
  123.   #ifndef _O_U16TEXT
  124.     #define _O_U16TEXT      0x20000 /* _setmode() code */
  125.   #endif /* !_O_U16TEXT */
  126.   #define __WIN32__
  127. #endif /* Win32 */
  128.  
  129. /* Under Unix we can do special-case handling for paths and Unicode strings.
  130.    Detecting Unix systems is a bit tricky but the following should find most
  131.    versions.  This define implicitly assumes that the system has wchar_t
  132.    support, but this is almost always the case except for very old systems,
  133.    so it's best to default to allow-all rather than deny-all */
  134.  
  135. #if defined( linux ) || defined( __linux__ ) || defined( sun ) || \
  136.     defined( __bsdi__ ) || defined( __FreeBSD__ ) || defined( __NetBSD__ ) || \
  137.     defined( __OpenBSD__ ) || defined( __hpux ) || defined( _M_XENIX ) || \
  138.     defined( __osf__ ) || defined( _AIX ) || defined( __MACH__ )
  139.   #define __UNIX__
  140. #endif /* Every commonly-used Unix */
  141. #if defined( linux ) || defined( __linux__ )
  142.   #ifndef __USE_ISOC99
  143.     #define __USE_ISOC99
  144.   #endif /* __USE_ISOC99 */
  145.   #include <wchar.h>
  146. #endif /* Linux */
  147.  
  148. /* For IBM mainframe OSes we use the Posix environment, so it looks like
  149.    Unix */
  150.  
  151. #ifdef OS390
  152.   #define __OS390__
  153.   #define __UNIX__
  154. #endif /* OS390 / z/OS */
  155.  
  156. /* Tandem NSK: Don't tangle with Tandem OSS, which is almost UNIX */
  157.  
  158. #ifdef __TANDEM
  159.   #ifdef _GUARDIAN_TARGET
  160.     #define __TANDEM_NSK__
  161.   #else
  162.     #define __UNIX__
  163.   #endif /* _GUARDIAN_TARGET */
  164. #endif /* __TANDEM */
  165.  
  166. /* Some OSes don't define the min() macro */
  167.  
  168. #ifndef min
  169.   #define min(a,b)      ( ( a ) < ( b ) ? ( a ) : ( b ) )
  170. #endif /* !min */
  171.  
  172. /* Macros to avoid problems with sign extension */
  173.  
  174. #define byteToInt( x )  ( ( BYTE ) ( x ) )
  175.  
  176. /* Turn off pointless VC++ warnings */
  177.  
  178. #ifdef _MSC_VER
  179.   #pragma warning( disable: 4018 )
  180. #endif /* VC++ */
  181.  
  182. /* When we dump a nested data object encapsulated within a larger object, the
  183.    length is initially set to a magic value which is adjusted to the actual
  184.    length once we start parsing the object */
  185.  
  186. #define LENGTH_MAGIC    177545L
  187.  
  188. /* Tag classes */
  189.  
  190. #define CLASS_MASK      0xC0    /* Bits 8 and 7 */
  191. #define UNIVERSAL       0x00    /* 0 = Universal (defined by ITU X.680) */
  192. #define APPLICATION     0x40    /* 1 = Application */
  193. #define CONTEXT         0x80    /* 2 = Context-specific */
  194. #define PRIVATE         0xC0    /* 3 = Private */
  195.  
  196. /* Encoding type */
  197.  
  198. #define FORM_MASK       0x20    /* Bit 6 */
  199. #define PRIMITIVE       0x00    /* 0 = primitive */
  200. #define CONSTRUCTED     0x20    /* 1 = constructed */
  201.  
  202. /* Universal tags */
  203.  
  204. #define TAG_MASK        0x1F    /* Bits 5 - 1 */
  205. #define EOC             0x00    /*  0: End-of-contents octets */
  206. #define BOOLEAN         0x01    /*  1: Boolean */
  207. #define INTEGER         0x02    /*  2: Integer */
  208. #define BITSTRING       0x03    /*  2: Bit string */
  209. #define OCTETSTRING     0x04    /*  4: Byte string */
  210. #define NULLTAG         0x05    /*  5: NULL */
  211. #define OID             0x06    /*  6: Object Identifier */
  212. #define OBJDESCRIPTOR   0x07    /*  7: Object Descriptor */
  213. #define EXTERNAL        0x08    /*  8: External */
  214. #define REAL            0x09    /*  9: Real */
  215. #define ENUMERATED      0x0A    /* 10: Enumerated */
  216. #define EMBEDDED_PDV    0x0B    /* 11: Embedded Presentation Data Value */
  217. #define UTF8STRING      0x0C    /* 12: UTF8 string */
  218. #define SEQUENCE        0x10    /* 16: Sequence/sequence of */
  219. #define SET             0x11    /* 17: Set/set of */
  220. #define NUMERICSTRING   0x12    /* 18: Numeric string */
  221. #define PRINTABLESTRING 0x13    /* 19: Printable string (ASCII subset) */
  222. #define T61STRING       0x14    /* 20: T61/Teletex string */
  223. #define VIDEOTEXSTRING  0x15    /* 21: Videotex string */
  224. #define IA5STRING       0x16    /* 22: IA5/ASCII string */
  225. #define UTCTIME         0x17    /* 23: UTC time */
  226. #define GENERALIZEDTIME 0x18    /* 24: Generalized time */
  227. #define GRAPHICSTRING   0x19    /* 25: Graphic string */
  228. #define VISIBLESTRING   0x1A    /* 26: Visible string (ASCII subset) */
  229. #define GENERALSTRING   0x1B    /* 27: General string */
  230. #define UNIVERSALSTRING 0x1C    /* 28: Universal string */
  231. #define BMPSTRING       0x1E    /* 30: Basic Multilingual Plane/Unicode string */
  232.  
  233. /* Length encoding */
  234.  
  235. #define LEN_XTND  0x80      /* Indefinite or long form */
  236. #define LEN_MASK  0x7F      /* Bits 7 - 1 */
  237.  
  238. /* Various special-case operations to perform on strings */
  239.  
  240. typedef enum {
  241.     STR_NONE,               /* No special handling */
  242.     STR_UTCTIME,            /* Check it's UTCTime */
  243.     STR_GENERALIZED,        /* Check it's GeneralizedTime */
  244.     STR_PRINTABLE,          /* Check it's a PrintableString */
  245.     STR_IA5,                /* Check it's an IA5String */
  246.     STR_LATIN1,             /* Read and display string as latin-1 */
  247.     STR_UTF8,               /* Read and display string as UTF8 */
  248.     STR_BMP,                /* Read and display string as Unicode */
  249.     STR_BMP_REVERSED        /* STR_BMP with incorrect endianness */
  250.     } STR_OPTION;
  251.  
  252. /* Structure to hold info on an ASN.1 item */
  253.  
  254. typedef struct {
  255.     int id;                     /* Tag class + primitive/constructed */
  256.     int tag;                    /* Tag */
  257.     long length;                /* Data length */
  258.     int indefinite;             /* Item has indefinite length */
  259.     int nonCanonical;           /* Non-canonical length encoding used */
  260.     BYTE header[ 16 ];          /* Tag+length data */
  261.     int headerSize;             /* Size of tag+length */
  262.     } ASN1_ITEM;
  263.  
  264. /* Configuration options */
  265.  
  266. static int printDots = FALSE;       /* Whether to print dots to align columns */
  267. static int doPure = FALSE;          /* Print data without LHS info column */
  268. static int doDumpHeader = FALSE;    /* Dump tag+len in hex (level = 0, 1, 2) */
  269. static int extraOIDinfo = FALSE;    /* Print extra information about OIDs */
  270. static int doHexValues = FALSE;     /* Display size, offset in hex not dec.*/
  271. static int useStdin = FALSE;        /* Take input from stdin */
  272. static int zeroLengthAllowed = FALSE;/* Zero-length items allowed */
  273. static int dumpText = FALSE;        /* Dump text alongside hex data */
  274. static int printAllData = FALSE;    /* Whether to print all data in long blocks */
  275. static int checkEncaps = TRUE;      /* Print encaps.data in BIT/OCTET STRINGs */
  276. static int checkCharset = TRUE;     /* Check val.of char strs.hidden in OCTET STRs */
  277. #ifndef __OS390__
  278. static int reverseBitString = TRUE; /* Print BIT STRINGs in natural order */
  279. #else
  280. static int reverseBitString = FALSE;/* Natural order on OS390 is the same as ASN.1 */
  281. #endif /* __OS390__ */
  282. static int rawTimeString = FALSE;   /* Print raw time strings */
  283. static int shallowIndent = FALSE;   /* Perform shallow indenting */
  284. static int outputWidth = 80;        /* 80-column display */
  285. static int maxNestLevel = 100;      /* Maximum nesting level for which to display output */
  286. static int doOutlineOnly = FALSE;   /* Only display constructed-object outline */
  287.  
  288. /* Formatting information used for the fixed informational column to the
  289.    left of the displayed data */
  290.  
  291. static int infoWidth = 4;
  292. static const char *indentStringTbl[] = {
  293.     NULL, NULL, NULL,
  294.     "       : ",            /* "xxx xxx: " (3) */
  295.     "         : ",          /* "xxxx xxxx: " (4) */
  296.     "           : ",        /* "xxxxx xxxxx: " (5) */
  297.     "             : ",      /* "xxxxxx xxxxxx: " (6) */
  298.     "               : ",    /* "xxxxxxx xxxxxxx: " (7) */
  299.     "                 : ",  /* "xxxxxxxx xxxxxxxx: " (8) */
  300.     "", "", "", ""
  301.     };
  302. static const char *lenTbl[] = {
  303.     NULL, NULL, NULL,
  304.     "%3ld %3ld: ", "%4ld %4ld: ", "%5ld %5ld: ",
  305.     "%6ld %6ld: ", "%7ld %7ld: ", "%8ld %8ld: ",
  306.     "", "", "", ""
  307.     };
  308. static const char *lenIndefTbl[] = {
  309.     NULL, NULL, NULL,
  310.     "%3ld NDF: ", "%4ld NDEF: ", "%5ld INDEF: ",
  311.     "%6ld INDEF : ", "%7ld INDEF  : ", "%8ld INDEF   : ",
  312.     "", "", "", ""
  313.     };
  314. static const char *lenHexTbl[] = {
  315.     NULL, NULL, NULL,
  316.     "%03lX %3lX: ", "%04lX %4lX: ", "%05lX %5lX: ",
  317.     "%06lX %6lX: ", "%07lX %7lX: ", "%08lX %8lX: ",
  318.     "", "", "", ""
  319.     };
  320. static const char *lenHexIndefTbl[] = {
  321.     NULL, NULL, NULL,
  322.     "%03lX NDF: ", "%04lX NDEF: ", "%05lX INDEF: ",
  323.     "%06lX INDEF : ", "%07lX INDEF  : ", "%08lX INDEF   : ",
  324.     "", "", "", ""
  325.     };
  326.  
  327. #define INDENT_SIZE     ( infoWidth + 1 + infoWidth + 1 + 1 )
  328. #define INDENT_STRING   indentStringTbl[ infoWidth ]
  329. #define LEN             lenTbl[ infoWidth ]
  330. #define LEN_INDEF       lenIndefTbl[ infoWidth ]
  331. #define LEN_HEX         lenHexTbl[ infoWidth ]
  332. #define LEN_HEX_INDEF   lenHexIndefTbl[ infoWidth ]
  333.  
  334. /* Error and warning information */
  335.  
  336. static int noErrors = 0;            /* Number of errors found */
  337. static int noWarnings = 0;          /* Number of warnings */
  338.  
  339. /* Position in the input stream */
  340.  
  341. static int fPos = 0;                /* Absolute position in data */
  342.  
  343. /* The output stream */
  344.  
  345. static FILE *output;                /* Output stream */
  346.  
  347. /* OID data sizes.  Because of Microsoft's "encode random noise and call it
  348.    an OID" approach, we maintain two size limits, a sane one and one capable
  349.    of holding the random-noise OID data, which we warn about */
  350.  
  351. #define MAX_OID_SIZE        40
  352. #define MAX_SANE_OID_SIZE   32
  353.  
  354. /* Information on an ASN.1 Object Identifier */
  355.  
  356. typedef struct tagOIDINFO {
  357.     struct tagOIDINFO *next;        /* Next item in list */
  358.     BYTE oid[ MAX_OID_SIZE ];
  359.     int oidLength;
  360.     char *comment, *description;    /* Name, rank, serial number */
  361.     int warn;                       /* Whether to warn if OID encountered */
  362.     } OIDINFO;
  363.  
  364. static OIDINFO *oidList = NULL;
  365.  
  366. /* If the config file isn't present in the current directory, we search the
  367.    following paths (this is needed for Unix with dumpasn1 somewhere in the
  368.    path, since this doesn't set up argv[0] to the full path).  Anything
  369.    beginning with a '$' uses the appropriate environment variable.  In
  370.    addition under Unix we also walk down $PATH looking for it */
  371.  
  372. #ifdef __TANDEM_NSK__
  373.   #define CONFIG_NAME       "asn1cfg"
  374. #else
  375.   #define CONFIG_NAME       "dumpasn1.cfg"
  376. #endif /* __TANDEM_NSK__ */
  377.  
  378. #if defined( __TANDEM_NSK__ )
  379.  
  380. static const char *configPaths[] = {
  381.     "$system.security", "$system.system",
  382.  
  383.     NULL
  384.     };
  385.  
  386. #elif defined( __WIN32__ )
  387.  
  388. static const char *configPaths[] = {
  389.     /* Windoze absolute paths (yeah, this code has been around for awhile,
  390.        why do you ask?) */
  391.     "c:\\windows\\", "c:\\winnt\\",
  392.  
  393.     /* It's my program, I'm allowed to hardcode in strange paths that no-one
  394.        else uses */
  395.     "c:\\program files\\bin\\",
  396.     "c:\\program files (x86)\\bin\\",
  397.  
  398.     /* This one seems to be popular as well */
  399.     "c:\\program files\\utilities\\",
  400.     "c:\\program files (x86)\\utilities\\",
  401.  
  402.     /* General environment-based paths */
  403.     "$DUMPASN1_PATH/",
  404.  
  405.     NULL
  406.     };
  407.  
  408. #elif defined( __OS390__ )
  409.  
  410. static const char *configPaths[] = {
  411.     /* General environment-based paths */
  412.     "$DUMPASN1_PATH/",
  413.  
  414.     NULL
  415.     };
  416.  
  417. #else
  418.  
  419. static const char *configPaths[] = {
  420.   #ifndef DEBIAN
  421.     /* Unix absolute paths */
  422.     "/usr/bin/", "/usr/local/bin/", "/etc/dumpasn1/",
  423.  
  424.     /* Unix environment-based paths */
  425.     "$HOME/", "$HOME/bin/",
  426.  
  427.     /* It's my program, I'm allowed to hardcode in strange paths that no-one
  428.        else uses */
  429.     "$HOME/BIN/",
  430.   #else
  431.     /* Debian has specific places where you're supposed to dump things.  Note
  432.        the dot after $HOME, since config files are supposed to start with a
  433.        dot for Debian */
  434.     "$HOME/.", "/etc/dumpasn1/",
  435.   #endif /* DEBIAN-specific paths */
  436.  
  437.     /* General environment-based paths */
  438.     "$DUMPASN1_PATH/",
  439.  
  440.     NULL
  441.     };
  442. #endif /* OS-specific search paths */
  443.  
  444. #define isEnvTerminator( c )    \
  445.     ( ( ( c ) == '/' ) || ( ( c ) == '.' ) || ( ( c ) == '$' ) || \
  446.       ( ( c ) == '\0' ) || ( ( c ) == '~' ) )
  447.  
  448. /****************************************************************************
  449. *                                                                           *
  450. *                   Object Identification/Description Routines              *
  451. *                                                                           *
  452. ****************************************************************************/
  453.  
  454. /* Return descriptive strings for universal tags */
  455.  
  456. static char *idstr( const int tagID )
  457.     {
  458.     switch( tagID )
  459.         {
  460.         case EOC:
  461.             return( "End-of-contents octets" );
  462.         case BOOLEAN:
  463.             return( "BOOLEAN" );
  464.         case INTEGER:
  465.             return( "INTEGER" );
  466.         case BITSTRING:
  467.             return( "BIT STRING" );
  468.         case OCTETSTRING:
  469.             return( "OCTET STRING" );
  470.         case NULLTAG:
  471.             return( "NULL" );
  472.         case OID:
  473.             return( "OBJECT IDENTIFIER" );
  474.         case OBJDESCRIPTOR:
  475.             return( "ObjectDescriptor" );
  476.         case EXTERNAL:
  477.             return( "EXTERNAL" );
  478.         case REAL:
  479.             return( "REAL" );
  480.         case ENUMERATED:
  481.             return( "ENUMERATED" );
  482.         case EMBEDDED_PDV:
  483.             return( "EMBEDDED PDV" );
  484.         case UTF8STRING:
  485.             return( "UTF8String" );
  486.         case SEQUENCE:
  487.             return( "SEQUENCE" );
  488.         case SET:
  489.             return( "SET" );
  490.         case NUMERICSTRING:
  491.             return( "NumericString" );
  492.         case PRINTABLESTRING:
  493.             return( "PrintableString" );
  494.         case T61STRING:
  495.             return( "TeletexString" );
  496.         case VIDEOTEXSTRING:
  497.             return( "VideotexString" );
  498.         case IA5STRING:
  499.             return( "IA5String" );
  500.         case UTCTIME:
  501.             return( "UTCTime" );
  502.         case GENERALIZEDTIME:
  503.             return( "GeneralizedTime" );
  504.         case GRAPHICSTRING:
  505.             return( "GraphicString" );
  506.         case VISIBLESTRING:
  507.             return( "VisibleString" );
  508.         case GENERALSTRING:
  509.             return( "GeneralString" );
  510.         case UNIVERSALSTRING:
  511.             return( "UniversalString" );
  512.         case BMPSTRING:
  513.             return( "BMPString" );
  514.         default:
  515.             return( "Unknown (Reserved)" );
  516.         }
  517.     }
  518.  
  519. /* Return information on an object identifier */
  520.  
  521. static OIDINFO *getOIDinfo( const BYTE *oid, const int oidLength )
  522.     {
  523.     const BYTE oidByte = oid[ 1 ];
  524.     OIDINFO *oidPtr;
  525.  
  526.     for( oidPtr = oidList; oidPtr != NULL; oidPtr = oidPtr->next )
  527.         {
  528.         if( oidLength != oidPtr->oidLength - 2 )
  529.             continue;   /* Quick-reject check */
  530.         if( oidByte != oidPtr->oid[ 2 + 1 ] )
  531.             continue;   /* Quick-reject check */
  532.         if( !memcmp( oidPtr->oid + 2, oid, oidLength ) )
  533.             return( oidPtr );
  534.         }
  535.  
  536.     return( NULL );
  537.     }
  538.  
  539. /* Add an OID attribute */
  540.  
  541. static int addAttribute( char **buffer, char *attribute )
  542.     {
  543.     if( ( *buffer = ( char * ) malloc( strlen( attribute ) + 1 ) ) == NULL )
  544.         {
  545.         puts( "Out of memory." );
  546.         return( FALSE );
  547.         }
  548.     strcpy( *buffer, attribute );
  549.     return( TRUE );
  550.     }
  551.  
  552. /* Table to identify valid string chars (taken from cryptlib).  Note that
  553.    IA5String also allows control chars, but we warn about these since
  554.    finding them in a certificate is a sign that there's something
  555.    seriously wrong */
  556.  
  557. #define P   1                       /* PrintableString */
  558. #define I   2                       /* IA5String */
  559. #define PI  3                       /* IA5String and PrintableString */
  560.  
  561. static int charFlags[] = {
  562.     /* 00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F */
  563.         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  564.     /* 10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F */
  565.         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  566.     /*      !   "   #   $   %   &   '   (   )   *   +   ,   -   .   / */
  567.        PI,  I,  I,  I,  I,  I,  I, PI, PI, PI,  I, PI, PI, PI, PI, PI,
  568.     /*  0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ? */
  569.        PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,  I,  I, PI,  I, PI,
  570.     /*  @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O */
  571.         I, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,
  572.     /*  P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^ _ */
  573.        PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,  I,  I,  I,  I,  I,
  574.     /*  `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o */
  575.         I, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,
  576.     /*  p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  DL */
  577.        PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,  I,  I,  I,  I,  0
  578.     };
  579.  
  580. static int isPrintable( int ch )
  581.     {
  582.     if( ch >= 128 || !( charFlags[ ch ] & P ) )
  583.         return( FALSE );
  584.     return( TRUE );
  585.     }
  586.  
  587. static int isIA5( int ch )
  588.     {
  589.     if( ch >= 128 || !( charFlags[ ch ] & I ) )
  590.         return( FALSE );
  591.     return( TRUE );
  592.     }
  593.  
  594. /****************************************************************************
  595. *                                                                           *
  596. *                           Config File Read Routines                       *
  597. *                                                                           *
  598. ****************************************************************************/
  599.  
  600. /* Files coming from DOS/Windows systems may have a ^Z (the CP/M EOF char)
  601.    at the end, so we need to filter this out */
  602.  
  603. #define CPM_EOF 0x1A        /* ^Z = CPM EOF char */
  604.  
  605. /* The maximum input line length */
  606.  
  607. #define MAX_LINESIZE    512
  608.  
  609. /* Read a line of text from the config file */
  610.  
  611. static int lineNo;
  612.  
  613. static int readLine( FILE *file, char *buffer )
  614.     {
  615.     int bufCount = 0, ch;
  616.  
  617.     /* Skip whitespace */
  618.     while( ( ( ch = getc( file ) ) == ' ' || ch == '\t' ) && !feof( file ) );
  619.  
  620.     /* Get a line into the buffer */
  621.     while( ch != '\r' && ch != '\n' && ch != CPM_EOF && !feof( file ) )
  622.         {
  623.         /* Check for an illegal char in the data.  Note that we don't just
  624.            check for chars with high bits set because these are legal in
  625.            non-ASCII strings */
  626.         if( !isprint( ch ) )
  627.             {
  628.             printf( "Bad character '%c' in config file line %d.\n",
  629.                     ch, lineNo );
  630.             return( FALSE );
  631.             }
  632.  
  633.         /* Check to see if it's a comment line */
  634.         if( ch == '#' && !bufCount )
  635.             {
  636.             /* Skip comment section and trailing whitespace */
  637.             while( ch != '\r' && ch != '\n' && ch != CPM_EOF && !feof( file ) )
  638.                 ch = getc( file );
  639.             break;
  640.             }
  641.  
  642.         /* Make sure that the line is of the correct length */
  643.         if( bufCount > MAX_LINESIZE )
  644.             {
  645.             printf( "Config file line %d too long.\n", lineNo );
  646.             return( FALSE );
  647.             }
  648.         else
  649.             if( ch )    /* Can happen if we read a binary file */
  650.                 buffer[ bufCount++ ] = ch;
  651.  
  652.         /* Get next character */
  653.         ch = getc( file );
  654.         }
  655.  
  656.     /* If we've just passed a CR, check for a following LF */
  657.     if( ch == '\r' )
  658.         {
  659.         if( ( ch = getc( file ) ) != '\n' )
  660.             ungetc( ch, file );
  661.         }
  662.  
  663.     /* Skip trailing whitespace and add der terminador */
  664.     while( bufCount > 0 &&
  665.            ( ( ch = buffer[ bufCount - 1 ] ) == ' ' || ch == '\t' ) )
  666.         bufCount--;
  667.     buffer[ bufCount ] = '\0';
  668.  
  669.     /* Handle special-case of ^Z if file came off an MSDOS system */
  670.     if( ch == CPM_EOF )
  671.         {
  672.         while( !feof( file ) )
  673.             {
  674.             /* Keep going until we hit the true EOF (or some sort of error) */
  675.             ( void ) getc( file );
  676.             }
  677.         }
  678.  
  679.     return( ferror( file ) ? FALSE : TRUE );
  680.     }
  681.  
  682. /* Process an OID specified as space-separated decimal or hex digits */
  683.  
  684. static int processOID( OIDINFO *oidInfo, char *string )
  685.     {
  686.     BYTE binaryOID[ MAX_OID_SIZE ];
  687.     long value;
  688.     int firstValue = -1, valueIndex = 0, oidIndex = 3;
  689.  
  690.     memset( binaryOID, 0, MAX_OID_SIZE );
  691.     binaryOID[ 0 ] = OID;
  692.     while( *string && oidIndex < MAX_OID_SIZE )
  693.         {
  694.         if( oidIndex >= MAX_OID_SIZE - 4 )
  695.             {
  696.             printf( "Excessively long OID in config file line %d.\n",
  697.                     lineNo );
  698.             return( FALSE );
  699.             }
  700.         if( sscanf( string, "%8ld", &value ) != 1 || value < 0 )
  701.             {
  702.             printf( "Invalid value in config file line %d.\n", lineNo );
  703.             return( FALSE );
  704.             }
  705.         if( valueIndex == 0 )
  706.             {
  707.             firstValue = value;
  708.             valueIndex++;
  709.             }
  710.         else
  711.             {
  712.             if( valueIndex == 1 )
  713.                 {
  714.                 if( firstValue < 0 || firstValue > 2 || value < 0 || \
  715.                     ( ( firstValue < 2 && value > 39 ) || \
  716.                       ( firstValue == 2 && value > 175 ) ) )
  717.                     {
  718.                     printf( "Invalid value in config file line %d.\n",
  719.                             lineNo );
  720.                     return( FALSE );
  721.                     }
  722.                 binaryOID[ 2 ] = ( firstValue * 40 ) + ( int ) value;
  723.                 valueIndex++;
  724.                 }
  725.             else
  726.                 {
  727.                 int hasHighBits = FALSE;
  728.  
  729.                 if( value >= 0x200000L )                    /* 2^21 */
  730.                     {
  731.                     binaryOID[ oidIndex++ ] = 0x80 | ( int ) ( value >> 21 );
  732.                     value %= 0x200000L;
  733.                     hasHighBits = TRUE;
  734.                     }
  735.                 if( ( value >= 0x4000 ) || hasHighBits )    /* 2^14 */
  736.                     {
  737.                     binaryOID[ oidIndex++ ] = 0x80 | ( int ) ( value >> 14 );
  738.                     value %= 0x4000;
  739.                     hasHighBits = TRUE;
  740.                     }
  741.                 if( ( value >= 0x80 ) || hasHighBits )      /* 2^7 */
  742.                     {
  743.                     binaryOID[ oidIndex++ ] = 0x80 | ( int ) ( value >> 7 );
  744.                     value %= 128;
  745.                     }
  746.                 binaryOID[ oidIndex++ ] = ( int ) value;
  747.                 }
  748.             }
  749.         while( *string && isdigit( byteToInt( *string ) ) )
  750.             string++;
  751.         if( *string && *string++ != ' ' )
  752.             {
  753.             printf( "Invalid OID string in config file line %d.\n", lineNo );
  754.             return( FALSE );
  755.             }
  756.         }
  757.     binaryOID[ 1 ] = oidIndex - 2;
  758.     memcpy( oidInfo->oid, binaryOID, oidIndex );
  759.     oidInfo->oidLength = oidIndex;
  760.  
  761.     return( TRUE );
  762.     }
  763.  
  764. static int processHexOID( OIDINFO *oidInfo, char *string )
  765.     {
  766.     int value, index = 0;
  767.  
  768.     while( *string && index < MAX_OID_SIZE - 1 )
  769.         {
  770.         if( sscanf( string, "%4x", &value ) != 1 || value < 0 || value > 255 )
  771.             {
  772.             printf( "Invalid hex value in config file line %d.\n", lineNo );
  773.             return( FALSE );
  774.             }
  775.         oidInfo->oid[ index++ ] = value;
  776.         string += 2;
  777.         if( *string && *string++ != ' ' )
  778.             {
  779.             printf( "Invalid hex string in config file line %d.\n", lineNo );
  780.             return( FALSE );
  781.             }
  782.         }
  783.     oidInfo->oid[ index ] = 0;
  784.     oidInfo->oidLength = index;
  785.     if( index >= MAX_OID_SIZE - 1 )
  786.         {
  787.         printf( "OID value in config file line %d too long.\n", lineNo );
  788.         return( FALSE );
  789.         }
  790.     return( TRUE );
  791.     }
  792.  
  793. /* Read a config file */
  794.  
  795. static int readConfig( const char *path, const int isDefaultConfig )
  796.     {
  797.     OIDINFO dummyOID = { NULL, "Dummy", 0, "Dummy", "Dummy", 1 }, *oidPtr;
  798.     FILE *file;
  799.     int seenHexOID = FALSE;
  800.     char buffer[ MAX_LINESIZE ];
  801.     int status;
  802.  
  803.     /* Try and open the config file */
  804.     if( ( file = fopen( path, "rb" ) ) == NULL )
  805.         {
  806.         /* If we can't open the default config file, issue a warning but
  807.            continue anyway */
  808.         if( isDefaultConfig )
  809.             {
  810.             puts( "Cannot open config file 'dumpasn1.cfg', which should be in the same" );
  811.             puts( "directory as the dumpasn1 program, a standard system directory, or" );
  812.             puts( "in a location pointed to by the DUMPASN1_PATH environment variable." );
  813.             puts( "Operation will continue without the ability to display Object " );
  814.             puts( "Identifier information." );
  815.             puts( "" );
  816.             puts( "If the config file is located elsewhere, you can set the environment" );
  817.             puts( "variable DUMPASN1_PATH to the path to the file." );
  818.             return( TRUE );
  819.             }
  820.  
  821.         printf( "Cannot open config file '%s'.\n", path );
  822.         return( FALSE );
  823.         }
  824.  
  825.     /* Add the new config entries at the appropriate point in the OID list */
  826.     if( oidList == NULL )
  827.         oidPtr = &dummyOID;
  828.     else
  829.         for( oidPtr = oidList; oidPtr->next != NULL; oidPtr = oidPtr->next );
  830.  
  831.     /* Read each line in the config file */
  832.     lineNo = 1;
  833.     while( ( status = readLine( file, buffer ) ) == TRUE && !feof( file ) )
  834.         {
  835.         /* If it's a comment line, skip it */
  836.         if( !*buffer )
  837.             {
  838.             lineNo++;
  839.             continue;
  840.             }
  841.  
  842.         /* Check for an attribute tag */
  843.         if( !strncmp( buffer, "OID = ", 6 ) )
  844.             {
  845.             /* Make sure that all of the required attributes for the current
  846.                OID are present */
  847.             if( oidPtr->description == NULL )
  848.                 {
  849.                 printf( "OID ending on config file line %d has no "
  850.                         "description attribute.\n", lineNo - 1 );
  851.                 return( FALSE );
  852.                 }
  853.  
  854.             /* Allocate storage for the new OID */
  855.             if( ( oidPtr->next = ( OIDINFO * ) malloc( sizeof( OIDINFO ) ) ) == NULL )
  856.                 {
  857.                 puts( "Out of memory." );
  858.                 return( FALSE );
  859.                 }
  860.             oidPtr = oidPtr->next;
  861.             if( oidList == NULL )
  862.                 oidList = oidPtr;
  863.             memset( oidPtr, 0, sizeof( OIDINFO ) );
  864.  
  865.             /* Add the new OID */
  866.             if( !strncmp( buffer + 6, "06", 2 ) )
  867.                 {
  868.                 seenHexOID = TRUE;
  869.                 if( !processHexOID( oidPtr, buffer + 6 ) )
  870.                     return( FALSE );
  871.                 }
  872.             else
  873.                 {
  874.                 if( !processOID( oidPtr, buffer + 6 ) )
  875.                     return( FALSE );
  876.                 }
  877.  
  878.             /* Check that this OID isn't already present in the OID list.  
  879.                This is a quick-and-dirty n^2 algorithm so it's not enabled
  880.                by default */
  881. #if 0
  882.             {
  883.             OIDINFO *oidCursor;
  884.  
  885.             for( oidCursor = oidList; oidCursor->next != NULL; oidCursor = oidCursor->next )
  886.                 {
  887.                 if( oidCursor->oidLength == oidPtr->oidLength && \
  888.                     !memcmp( oidCursor->oid, oidPtr->oid, oidCursor->oidLength ) )
  889.                     {
  890.                     printf( "Duplicate OID '%s' at line %d.\n",
  891.                             buffer, lineNo );
  892.                     }
  893.                 }
  894.             }
  895. #endif /* 0 */
  896.             }
  897.         else if( !strncmp( buffer, "Description = ", 14 ) )
  898.             {
  899.             if( oidPtr->description != NULL )
  900.                 {
  901.                 printf( "Duplicate OID description in config file line %d.\n",
  902.                         lineNo );
  903.                 return( FALSE );
  904.                 }
  905.             if( !addAttribute( &oidPtr->description, buffer + 14 ) )
  906.                 return( FALSE );
  907.             }
  908.         else if( !strncmp( buffer, "Comment = ", 10 ) )
  909.             {
  910.             if( oidPtr->comment != NULL )
  911.                 {
  912.                 printf( "Duplicate OID comment in config file line %d.\n",
  913.                         lineNo );
  914.                 return( FALSE );
  915.                 }
  916.             if( !addAttribute( &oidPtr->comment, buffer + 10 ) )
  917.                 return( FALSE );
  918.             }
  919.         else if( !strncmp( buffer, "Warning", 7 ) )
  920.             {
  921.             if( oidPtr->warn )
  922.                 {
  923.                 printf( "Duplicate OID warning in config file line %d.\n",
  924.                         lineNo );
  925.                 return( FALSE );
  926.                 }
  927.             oidPtr->warn = TRUE;
  928.             }
  929.         else
  930.             {
  931.             printf( "Unrecognised attribute '%s', line %d.\n", buffer,
  932.                     lineNo );
  933.             return( FALSE );
  934.             }
  935.  
  936.         lineNo++;
  937.         }
  938.     fclose( file );
  939.  
  940.     /* If we're processing an old-style config file, tell the user to
  941.        upgrade */
  942.     if( seenHexOID )
  943.         {
  944.         puts( "\nWarning: Use of old-style hex OIDs detected in "
  945.               "configuration file, please\n         update your dumpasn1 "
  946.               "configuration file.\n" );
  947.         }
  948.  
  949.     return( status );
  950.     }
  951.  
  952. /* Check for the existence of a config file path (access() isn't available
  953.    on all systems) */
  954.  
  955. static int testConfigPath( const char *path )
  956.     {
  957.     FILE *file;
  958.  
  959.     /* Try and open the config file */
  960.     if( ( file = fopen( path, "rb" ) ) == NULL )
  961.         return( FALSE );
  962.     fclose( file );
  963.  
  964.     return( TRUE );
  965.     }
  966.  
  967. /* Build a config path by substituting environment strings for $NAMEs */
  968.  
  969. static void buildConfigPath( char *path, const char *pathTemplate )
  970.     {
  971.     char pathBuffer[ FILENAME_MAX ], newPath[ FILENAME_MAX ];
  972.     int pathLen, pathPos = 0, newPathPos = 0;
  973.  
  974.     /* Add the config file name at the end */
  975.     strcpy( pathBuffer, pathTemplate );
  976.     strcat( pathBuffer, CONFIG_NAME );
  977.     pathLen = strlen( pathBuffer );
  978.  
  979.     while( pathPos < pathLen )
  980.         {
  981.         char *strPtr;
  982.         int substringSize;
  983.  
  984.         /* Find the next $ and copy the data before it to the new path */
  985.         if( ( strPtr = strstr( pathBuffer + pathPos, "$" ) ) != NULL )
  986.             substringSize = ( int ) ( ( strPtr - pathBuffer ) - pathPos );
  987.         else
  988.             substringSize = pathLen - pathPos;
  989.         if( substringSize > 0 )
  990.             {
  991.             memcpy( newPath + newPathPos, pathBuffer + pathPos,
  992.                     substringSize );
  993.             }
  994.         newPathPos += substringSize;
  995.         pathPos += substringSize;
  996.  
  997.         /* Get the environment string for the $NAME */
  998.         if( strPtr != NULL )
  999.             {
  1000.             char envName[ MAX_LINESIZE ], *envString;
  1001.             int i;
  1002.  
  1003.             /* Skip the '$', find the end of the $NAME, and copy the name
  1004.                into an internal buffer */
  1005.             pathPos++;  /* Skip the $ */
  1006.             for( i = 0; !isEnvTerminator( pathBuffer[ pathPos + i ] ); i++ );
  1007.             memcpy( envName, pathBuffer + pathPos, i );
  1008.             envName[ i ] = '\0';
  1009.  
  1010.             /* Get the env.string and copy it over */
  1011.             if( ( envString = getenv( envName ) ) != NULL )
  1012.                 {
  1013.                 const int envStrLen = strlen( envString );
  1014.  
  1015.                 if( newPathPos + envStrLen < FILENAME_MAX - 2 )
  1016.                     {
  1017.                     memcpy( newPath + newPathPos, envString, envStrLen );
  1018.                     newPathPos += envStrLen;
  1019.                     }
  1020.                 }
  1021.             pathPos += i;
  1022.             }
  1023.         }
  1024.     newPath[ newPathPos ] = '\0';   /* Add der terminador */
  1025.  
  1026.     /* Copy the new path to the output */
  1027.     strcpy( path, newPath );
  1028.     }
  1029.  
  1030. /* Read the global config file */
  1031.  
  1032. static int readGlobalConfig( const char *path )
  1033.     {
  1034.     char buffer[ FILENAME_MAX ];
  1035.     char *searchPos = ( char * ) path, *namePos, *lastPos = NULL;
  1036. #ifdef __UNIX__
  1037.     char *envPath;
  1038. #endif /* __UNIX__ */
  1039. #ifdef __WIN32__
  1040.     char filePath[ _MAX_PATH ];
  1041.     DWORD count;
  1042. #endif /* __WIN32__ */
  1043.     int i;
  1044.  
  1045.     /* First, try and find the config file in the same directory as the
  1046.        executable by walking down the path until we find the last occurrence
  1047.        of the program name.  This requires that argv[0] be set up properly,
  1048.        which isn't the case if Unix search paths are being used and is a
  1049.        bit hit-and-miss under Windows where the contents of argv[0] depend
  1050.        on how the program is being executed.  To avoid this we perform some
  1051.        Windows-specific processing to try and find the path to the
  1052.        executable if we can't otherwise find it */
  1053.     do
  1054.         {
  1055.         namePos = lastPos;
  1056.         lastPos = strstr( searchPos, "dumpasn1" );
  1057.         if( lastPos == NULL )
  1058.             lastPos = strstr( searchPos, "DUMPASN1" );
  1059.         searchPos = lastPos + 1;
  1060.         }
  1061.     while( lastPos != NULL );
  1062. #ifdef __UNIX__
  1063.     if( namePos == NULL && ( namePos = strrchr( path, '/' ) ) != NULL )
  1064.         {
  1065.         const int endPos = ( int ) ( namePos - path ) + 1;
  1066.  
  1067.         /* If the executable isn't called dumpasn1, we won't be able to find
  1068.            it with the above code, fall back to looking for directory
  1069.            separators.  This requires a system where the only separator is
  1070.            the directory separator (ie it doesn't work for Windows or most
  1071.            mainframe environments) */
  1072.         if( endPos < FILENAME_MAX - 13 )
  1073.             {
  1074.             memcpy( buffer, path, endPos );
  1075.             strcpy( buffer + endPos, CONFIG_NAME );
  1076.             if( testConfigPath( buffer ) )
  1077.                 return( readConfig( buffer, TRUE ) );
  1078.             }
  1079.  
  1080.         /* That didn't work, try the absolute locations and $PATH */
  1081.         namePos = NULL;
  1082.         }
  1083. #endif /* __UNIX__ */
  1084.     if( strlen( path ) < FILENAME_MAX - 13 && namePos != NULL )
  1085.         {
  1086.         strcpy( buffer, path );
  1087.         strcpy( buffer + ( int ) ( namePos - ( char * ) path ), CONFIG_NAME );
  1088.         if( testConfigPath( buffer ) )
  1089.             return( readConfig( buffer, TRUE ) );
  1090.         }
  1091.  
  1092.     /* Now try each of the possible absolute locations for the config file */
  1093.     for( i = 0; configPaths[ i ] != NULL; i++ )
  1094.         {
  1095.         buildConfigPath( buffer, configPaths[ i ] );
  1096.         if( testConfigPath( buffer ) )
  1097.             return( readConfig( buffer, TRUE ) );
  1098.         }
  1099.  
  1100. #ifdef __UNIX__
  1101.     /* On Unix systems we can also search for the config file on $PATH */
  1102.     if( ( envPath = getenv( "PATH" ) ) != NULL )
  1103.         {
  1104.         char *pathPtr = strtok( envPath, ":" );
  1105.  
  1106.         do
  1107.             {
  1108.             sprintf( buffer, "%s/%s", pathPtr, CONFIG_NAME );
  1109.             if( testConfigPath( buffer ) )
  1110.                 return( readConfig( buffer, TRUE ) );
  1111.             pathPtr = strtok( NULL, ":" );
  1112.             }
  1113.         while( pathPtr != NULL );
  1114.         }
  1115. #endif /* __UNIX__ */
  1116. #ifdef __WIN32__
  1117.     /* Under Windows we can use GetModuleFileName() to find the location of
  1118.        the program */
  1119.     count = GetModuleFileName ( NULL, filePath, _MAX_PATH );
  1120.     if( count > 0 )
  1121.         {
  1122.         char *progNameStart = strrchr( filePath, '\\' );
  1123.         if( progNameStart != NULL && \
  1124.             ( progNameStart - filePath ) < _MAX_PATH - 13 )
  1125.             {
  1126.             /* Replace the program name with the config file name */
  1127.             strcpy( progNameStart + 1, CONFIG_NAME );
  1128.             if( testConfigPath( filePath ) )
  1129.                 return( readConfig( filePath, TRUE ) );
  1130.             }
  1131.         }
  1132. #endif /*__WIN32__*/
  1133.  
  1134.  
  1135.     /* Default to just the config name (which should fail as it was the
  1136.        first entry in configPaths[]).  readConfig() will display the
  1137.        appropriate warning */
  1138.     return( readConfig( CONFIG_NAME, TRUE ) );
  1139.     }
  1140.  
  1141. /* Free the in-memory config data */
  1142.  
  1143. static void freeConfig( void )
  1144.     {
  1145.     OIDINFO *oidPtr = oidList;
  1146.  
  1147.     while( oidPtr != NULL )
  1148.         {
  1149.         OIDINFO *oidCursor = oidPtr;
  1150.  
  1151.         oidPtr = oidPtr->next;
  1152.         if( oidCursor->comment != NULL )
  1153.             free( oidCursor->comment );
  1154.         if( oidCursor->description != NULL )
  1155.             free( oidCursor->description );
  1156.         free( oidCursor );
  1157.         }
  1158.     }
  1159.  
  1160. /****************************************************************************
  1161. *                                                                           *
  1162. *                           Output/Formatting Routines                      *
  1163. *                                                                           *
  1164. ****************************************************************************/
  1165.  
  1166. #ifdef __OS390__
  1167.  
  1168. static int asciiToEbcdic( const int ch )
  1169.     {
  1170.     char convBuffer[ 2 ];
  1171.  
  1172.     convBuffer[ 0 ] = ch;
  1173.     convBuffer[ 1 ] = '\0';
  1174.     __atoe( convBuffer ); /* Convert ASCII to EBCDIC for 390 */
  1175.     return( convBuffer[ 0 ] );
  1176.     }
  1177. #endif /* __OS390__ */
  1178.  
  1179. /* Output formatted text */
  1180.  
  1181. static int printString( const int level, const char *format, ... )
  1182.     {
  1183.     va_list argPtr;
  1184.     int length;
  1185.  
  1186.     if( level >= maxNestLevel )
  1187.         return( 0 );
  1188.     va_start( argPtr, format );
  1189.     length = vfprintf( output, format, argPtr );
  1190.     va_end( argPtr );
  1191.  
  1192.     return( length );
  1193.     }
  1194.  
  1195. /* Indent a string by the appropriate amount */
  1196.  
  1197. static void doIndent( const int level )
  1198.     {
  1199.     int i;
  1200.  
  1201.     if( level >= maxNestLevel )
  1202.         return;
  1203.     for( i = 0; i < level; i++ )
  1204.         {
  1205.         fprintf( output, printDots ? ". " : \
  1206.                          shallowIndent ? " " : "  " );
  1207.         }
  1208.     }
  1209.  
  1210. /* Complain about an error in the ASN.1 object */
  1211.  
  1212. static void complain( const char *message, const int messageParam,
  1213.                       const int level )
  1214.     {
  1215.     if( level < maxNestLevel )
  1216.         {
  1217.         if( !doPure )
  1218.             fprintf( output, "%s", INDENT_STRING );
  1219.         doIndent( level + 1 );
  1220.         }
  1221.     fputs( "Error: ", output );
  1222.     fprintf( output, message, messageParam );
  1223.     fputs( ".\n", output );
  1224.     noErrors++;
  1225.     }
  1226.  
  1227. static void complainLength( const ASN1_ITEM *item, const int level )
  1228.     {
  1229. #if 0
  1230.     /* This is a general error so we don't indent the message to the level
  1231.        of the item */
  1232. #else
  1233.     if( level < maxNestLevel )
  1234.         {
  1235.         if( !doPure )
  1236.             fprintf( output, "%s", INDENT_STRING );
  1237.         doIndent( level + 1 );
  1238.         }
  1239. #endif /* 0 */
  1240.     fprintf( output, "Error: %s has invalid length %ld.\n",
  1241.              idstr( item->tag ), item->length );
  1242.     noErrors++;
  1243.     }
  1244.  
  1245. static void complainLengthCanonical( const ASN1_ITEM *item, const int level )
  1246.     {
  1247.     int i;
  1248.  
  1249. #if 0
  1250.     /* This is a general error so we don't indent the message to the level
  1251.        of the item */
  1252. #else
  1253.     if( level < maxNestLevel )
  1254.         {
  1255.         if( !doPure )
  1256.             fprintf( output, "%s", INDENT_STRING );
  1257.         doIndent( level + 1 );
  1258.         }
  1259. #endif /* 0 */
  1260.     fputs( "Error: Length '", output );
  1261.     for( i = item->nonCanonical; i < item->headerSize; i++ )
  1262.         {
  1263.         fprintf( output, "%02X", item->header[ i ] );
  1264.         if( i < item->headerSize - 1 )
  1265.             fputc( ' ', output );
  1266.         }
  1267.     fputs( "' has non-canonical encoding.\n", output );
  1268.     noErrors++;
  1269.     }
  1270.  
  1271. static void complainInt( const BYTE *intValue, const int level )
  1272.     {
  1273.     if( level < maxNestLevel )
  1274.         {
  1275.         if( !doPure )
  1276.             fprintf( output, "%s", INDENT_STRING );
  1277.         doIndent( level + 1 );
  1278.         }
  1279.     fprintf( output, "Error: Integer '%02X %02X ...' has non-DER encoding.\n",
  1280.              intValue[ 0 ], intValue[ 1 ] );
  1281.     noErrors++;
  1282.     }
  1283.  
  1284. static void complainEOF( const int level, const int missingBytes )
  1285.     {
  1286.     printString( level, "%c", '\n' );
  1287.     complain( ( missingBytes > 1 ) ? \
  1288.                 "Unexpected EOF, %d bytes missing" : \
  1289.                 "Unexpected EOF, 1 byte missing", missingBytes, level );
  1290.     }
  1291.  
  1292. /* Adjust the nesting-level value to make sure that we don't go off the edge
  1293.    of the screen via doIndent() when we're displaying a text or hex dump of
  1294.    data */  
  1295.    
  1296. static int adjustLevel( const int level, const int maxLevel )
  1297.     {
  1298.     /* If we've been passed a very large pseudo-level to disable output then
  1299.        we don't try and override this */
  1300.     if( level >= 1000 )
  1301.         return( level );
  1302.  
  1303.     /* If we've exceeded the maximum level for display, cap the value at
  1304.        maxLevel to make sure that we don't end up indenting output off the
  1305.        edge of the screen */
  1306.     if( level > maxLevel )
  1307.         return( maxLevel );
  1308.  
  1309.     return( level );
  1310.     }
  1311.  
  1312. #if defined( __WIN32__ ) || defined( __UNIX__ ) || defined( __OS390__ )
  1313.  
  1314. /* Try and display to display a Unicode character.  This is pretty hit and
  1315.    miss, and if it fails nothing is displayed.  To try and detect this we
  1316.    use wcstombs() to see if anything can be displayed, if it can't we drop
  1317.    back to trying to display the data as non-Unicode */
  1318.  
  1319. static int displayUnicode( const wchar_t wCh, const int level )
  1320.     {
  1321.     char outBuf[ 8 ];
  1322.     int outLen;
  1323.  
  1324.     /* Check whether we can display this character */
  1325.     outLen = wcstombs( outBuf, &wCh, 8 );
  1326.     if( outLen < 1 )
  1327.         {
  1328.         /* Tell the caller that this can't be displayed as Unicode */
  1329.         return( FALSE );
  1330.         }
  1331.  
  1332. #if defined( __WIN32__ )
  1333.     if( level < maxNestLevel )
  1334.         {
  1335.         int oldmode;
  1336.                        
  1337.         /* To output Unicode to the Win32 console we need to switch the
  1338.            output stream to Unicode-16 mode, but the following may also
  1339.            depend on which code page is currently set for the console, which
  1340.            font is being used, and the phase of the moon (including the moons
  1341.            for Mars and Jupiter) */
  1342.         fflush( output );
  1343.         oldmode = _setmode( fileno( output ), _O_U16TEXT );
  1344.         fputwc( wCh, output );
  1345.         _setmode( fileno( output ), oldmode );
  1346.         }
  1347. #elif defined( __UNIX__ ) && !( defined( __MACH__ ) || defined( __OpenBSD__ ) )
  1348.     /* Unix environments are even more broken than Win32, like Win32 the
  1349.        output differentiates between char and widechar output, but there's
  1350.        no easy way to deal with this.  In theory fwide() can set it, but
  1351.        it's a one-way function, once we've set it a particular way we can't
  1352.        go back (exactly what level of braindamage it takes to have an
  1353.        implementation function like this is a mystery).  Other sources
  1354.        suggest using setlocale() tricks, printf() with "%lc" or "%ls" as the
  1355.        format specifier, and others, but none of these seem to work properly
  1356.        either */
  1357.     if( level < maxNestLevel )
  1358.         {
  1359. #if 0
  1360.         setlocale( LC_ALL, "" );
  1361.         fputwc( wCh, output );
  1362. #elif 1
  1363.         /* This (and the "%ls" variant below) seem to be the least broken
  1364.            options */
  1365.         fprintf( output, "%lc", wCh );
  1366. #elif 0
  1367.         wchar_t wChString[ 2 ];
  1368.  
  1369.         wChString[ 0 ] = wCh;
  1370.         wChString[ 1 ] = 0;
  1371.         fprintf( output, "%ls", wChString );
  1372. #else
  1373.         if( fwide( output, 1 ) > 0 )
  1374.             {
  1375.             fputwc( wCh, output );
  1376.             fwide( output, -1 );
  1377.             }
  1378.         else
  1379.             fputc( wCh, output );
  1380. #endif
  1381.         }
  1382. #else
  1383.   #ifdef __OS390__
  1384.     if( level < maxNestLevel )
  1385.         {
  1386.         char *p;
  1387.  
  1388.         /* This could use some improvement */
  1389.         for( p = outBuf; *p != '\0'; p++ )
  1390.             *p = asciiToEbcdic( *p );
  1391.         }
  1392.   #endif /* IBM ASCII -> EBCDIC conversion */
  1393.     printString( level, "%s", outBuf );
  1394. #endif /* OS-specific charset handling */
  1395.  
  1396.     return( TRUE );
  1397.     }
  1398. #endif /* __WIN32__ || __UNIX__ || __OS390__ */
  1399.  
  1400. /* Display an integer value */
  1401.  
  1402. static void printValue( FILE *inFile, const int valueLength,
  1403.                         const int level )
  1404.     {
  1405.     BYTE intBuffer[ 2 ];
  1406.     long value;
  1407.     int warnNegative = FALSE, warnNonDER = FALSE, i;
  1408.  
  1409.     value = getc( inFile );
  1410.     if( value == EOF )
  1411.         {
  1412.         complainEOF( level, valueLength );
  1413.         return;
  1414.         }
  1415.     if( value & 0x80 )
  1416.         warnNegative = TRUE;
  1417.     for( i = 0; i < valueLength - 1; i++ )
  1418.         {
  1419.         const int ch = getc( inFile );
  1420.  
  1421.         if( ch == EOF )
  1422.             {
  1423.             complainEOF( level, valueLength - i );
  1424.             return;
  1425.             }
  1426.  
  1427.         /* Check for the first 9 bits being identical */
  1428.         if( i == 0 )
  1429.             {
  1430.             if( ( value == 0x00 ) && ( ( ch & 0x80 ) == 0x00 ) )
  1431.                 warnNonDER = TRUE;
  1432.             if( ( value == 0xFF ) && ( ( ch & 0x80 ) == 0x80 ) )
  1433.                 warnNonDER = TRUE;
  1434.             if( warnNonDER )
  1435.                 {
  1436.                 intBuffer[ 0 ] = ( int ) value;
  1437.                 intBuffer[ 1 ] = ch;
  1438.                 }
  1439.             }
  1440.         value = ( value << 8 ) | ch;
  1441.         }
  1442.     fPos += valueLength;
  1443.  
  1444.     /* Display the integer value and any associated warnings.  Note that
  1445.        this will display an incorrectly-encoded integer as a negative value
  1446.        rather than the unsigned value that was probably intended to
  1447.        emphasise that it's incorrect */
  1448.     printString( level, " %ld\n", value );
  1449.     if( warnNonDER )
  1450.         complainInt( intBuffer, level );
  1451.     if( warnNegative )
  1452.         complain( "Integer is encoded as a negative value", 0, level );
  1453.     }
  1454.  
  1455. /* Dump data as a string of hex digits up to a maximum of 128 bytes */
  1456.  
  1457. static void dumpHex( FILE *inFile, long length, int level,
  1458.                      const int isInteger )
  1459.     {
  1460.     const int lineLength = ( dumpText ) ? 8 : 16;
  1461.     const int displayHeaderLength = ( ( doPure ) ? 0 : INDENT_SIZE ) + 2;
  1462.     BYTE intBuffer[ 2 ];
  1463.     char printable[ 9 ];
  1464.     long noBytes = length;
  1465.     int warnPadding = FALSE, warnNegative = isInteger, singleLine = FALSE;
  1466.     int displayLength = displayHeaderLength, prevCh = -1, i;
  1467.  
  1468.     memset( printable, 0, 9 );
  1469.  
  1470.     displayLength += ( length < lineLength ) ? ( length * 3 ) : \
  1471.                                                ( lineLength * 3 );
  1472.  
  1473.     /* Check if the size of the displayed data (LHS status info + hex data)
  1474.        plus the indent-level of spaces will fit into a single line behind
  1475.        the initial label, e.g. "INTEGER" */
  1476.     if( displayHeaderLength + ( level * 2 ) + ( length * 3 ) < outputWidth )
  1477.         singleLine = TRUE;
  1478.  
  1479.     /* By default we only output a maximum of 128 bytes to avoid dumping
  1480.        huge amounts of data, however if what's left is a partial lines'
  1481.        worth then we output that as well to avoid displaying a line of text
  1482.        indicating that less than a lines' worth of data remains to be
  1483.        displayed */
  1484.     if( noBytes >= 128 + lineLength && !printAllData )
  1485.         noBytes = 128;
  1486.  
  1487.     /* Make sure that the indent level doesn't push the text off the edge of
  1488.        the screen */
  1489.     level = adjustLevel( level, ( outputWidth - displayLength ) / 2 );
  1490.     for( i = 0; i < noBytes; i++ )
  1491.         {
  1492.         int ch;
  1493.  
  1494.         if( !( i % lineLength ) )
  1495.             {
  1496.             if( singleLine )
  1497.                 printString( level, "%c", ' ' );
  1498.             else
  1499.                 {
  1500.                 if( dumpText )
  1501.                     {
  1502.                     /* If we're dumping text alongside the hex data, print
  1503.                        the accumulated text string */
  1504.                     printString( level, "%s", "    " );
  1505.                     printString( level, "%s", printable );
  1506.                     }
  1507.                 printString( level, "%c", '\n' );
  1508.                 if( !doPure )
  1509.                     printString( level, "%s", INDENT_STRING );
  1510.                 doIndent( level + 1 );
  1511.                 }
  1512.             }
  1513.         ch = getc( inFile );
  1514.         if( ch == EOF )
  1515.             {
  1516.             complainEOF( level, length - i );
  1517.             return;
  1518.             }
  1519.         printString( level, "%s%02X", ( i % lineLength ) ? " " : "", ch );
  1520.         printable[ i % 8 ] = ( ch >= ' ' && ch < 127 ) ? ch : '.';
  1521.         fPos++;
  1522.  
  1523.         /* If we need to check for negative values, check this now */
  1524.         if( i == 0 )
  1525.             {
  1526.             prevCh = ch;
  1527.             if( !( ch & 0x80 ) )
  1528.                 warnNegative = FALSE;
  1529.             }
  1530.         if( i == 1 )
  1531.             {
  1532.             /* Check for the first 9 bits being identical */
  1533.             if( ( prevCh == 0x00 ) && ( ( ch & 0x80 ) == 0x00 ) )
  1534.                 warnPadding = TRUE;
  1535.             if( ( prevCh == 0xFF ) && ( ( ch & 0x80 ) == 0x80 ) )
  1536.                 warnPadding = TRUE;
  1537.             if( warnPadding )
  1538.                 {
  1539.                 intBuffer[ 0 ] = prevCh;
  1540.                 intBuffer[ 1 ] = ch;
  1541.                 }
  1542.             }
  1543.         }
  1544.     if( dumpText )
  1545.         {
  1546.         /* Print any remaining text */
  1547.         i %= lineLength;
  1548.         printable[ i ] = '\0';
  1549.         while( i < lineLength )
  1550.             {
  1551.             printString( level, "%s", "   " );
  1552.             i++;
  1553.             }
  1554.         printString( level, "%s", "    " );
  1555.         printString( level, "%s", printable );
  1556.         }
  1557.     if( length >= 128 + lineLength && !printAllData )
  1558.         {
  1559.         length -= 128;
  1560.         printString( level, "%c", '\n' );
  1561.         if( !doPure )
  1562.             printString( level, "%s", INDENT_STRING );
  1563.         doIndent( level + 5 );
  1564.         printString( level, "[ Another %ld bytes skipped ]", length );
  1565.         fPos += length;
  1566.         if( useStdin )
  1567.             {
  1568.             int ch;
  1569.  
  1570.             while( length-- )
  1571.                 {
  1572.                 ch = getc( inFile );
  1573.                 if( ch == EOF )
  1574.                     {
  1575.                     complainEOF( level, length - i );
  1576.                     return;
  1577.                     }
  1578.                 }
  1579.             }
  1580.         else
  1581.             fseek( inFile, length, SEEK_CUR );
  1582.         }
  1583.     printString( level, "%c", '\n' );
  1584.  
  1585.     if( isInteger )
  1586.         {
  1587.         if( warnPadding )
  1588.             complainInt( intBuffer, level );
  1589.         if( warnNegative )
  1590.             complain( "Integer is encoded as a negative value", 0, level );
  1591.         }
  1592.     }
  1593.  
  1594. /* Convert a binary OID to its string equivalent */
  1595.  
  1596. static int oidToString( char *textOID, int *textOIDlength,
  1597.                         const BYTE *oid, const int oidLength )
  1598.     {
  1599.     BYTE uuidBuffer[ 32 ];
  1600.     long value;
  1601.     int length = 0, uuidBufPos = -1, uuidBitCount = 5, i;
  1602.     int validEncoding = TRUE, isUUID = FALSE;
  1603.  
  1604.     for( i = 0, value = 0; i < oidLength; i++ )
  1605.         {
  1606.         const BYTE data = oid[ i ];
  1607.         const long valTmp = value << 7;
  1608.  
  1609.         /* Pick apart the encoding.  We keep going after hitting an encoding
  1610.            error at the start of an arc because the overall length is
  1611.            bounded and we may still be able to recover something worth
  1612.            printing */
  1613.         if( value == 0 && data == 0x80 )
  1614.             {
  1615.             /* Invalid leading zero value, 0x80 & 0x7F == 0 */
  1616.             validEncoding = FALSE;
  1617.             }
  1618.         if( isUUID )
  1619.             {
  1620.             value = 1;  /* Set up dummy value since we're bypassing normal read */
  1621.             if( uuidBitCount == 0 )
  1622.                 uuidBuffer[ uuidBufPos ] = data << 1;
  1623.             else
  1624.                 {
  1625.                 if( uuidBufPos >= 0 )
  1626.                     uuidBuffer[ uuidBufPos ] |= ( data & 0x7F ) >> ( 7 - uuidBitCount );
  1627.                 uuidBufPos++;
  1628.                 if( uuidBitCount < 7 )
  1629.                     uuidBuffer[ uuidBufPos ] = data << ( uuidBitCount + 1 );
  1630.                 }
  1631.             uuidBitCount++;
  1632.             if( uuidBitCount > 7 )
  1633.                 uuidBitCount = 0;
  1634.             if( !( data & 0x80 ) )
  1635.                 {
  1636.                 /* The following check isn't completely accurate since we
  1637.                    could have less than 16 bytes present if there are
  1638.                    leading zeroes, however to handle this properly we'd
  1639.                    have to decode the entire value as a bignum and then
  1640.                    format it appropriately, and given the fact that the use
  1641.                    of these things is practically nonexistent it's probably
  1642.                    not worth the code space to deal with this */
  1643.                 if( uuidBufPos != 16 )
  1644.                     {
  1645.                     validEncoding = FALSE;
  1646.                     break;
  1647.                     }
  1648.                 length += sprintf( textOID + length,
  1649.                                    " { %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x }",
  1650.                                    uuidBuffer[ 0 ], uuidBuffer[ 1 ],
  1651.                                    uuidBuffer[ 2 ], uuidBuffer[ 3 ],
  1652.                                    uuidBuffer[ 4 ], uuidBuffer[ 5 ],
  1653.                                    uuidBuffer[ 6 ], uuidBuffer[ 7 ],
  1654.                                    uuidBuffer[ 8 ], uuidBuffer[ 9 ],
  1655.                                    uuidBuffer[ 10 ], uuidBuffer[ 11 ],
  1656.                                    uuidBuffer[ 12 ], uuidBuffer[ 13 ],
  1657.                                    uuidBuffer[ 14 ], uuidBuffer[ 15 ] );
  1658.                 value = 0;
  1659.                 }
  1660.             continue;
  1661.             }
  1662.         if( value >= ( LONG_MAX >> 7 ) || \
  1663.             valTmp >= LONG_MAX - ( data & 0x7F ) )
  1664.             {
  1665.             validEncoding = FALSE;
  1666.             break;
  1667.             }
  1668.         value = valTmp | ( data & 0x7F );
  1669.         if( value < 0 || value > LONG_MAX / 2 )
  1670.             {
  1671.             validEncoding = FALSE;
  1672.             break;
  1673.             }
  1674.         if( !( data & 0x80 ) )
  1675.             {
  1676.             if( length == 0 )
  1677.                 {
  1678.                 long x, y;
  1679.  
  1680.                 /* The first two levels are encoded into one byte since the
  1681.                    root level has only 3 nodes (40*x + y), however if x =
  1682.                    joint-iso-itu-t(2) then y may be > 39, so we have to add
  1683.                    special-case handling for this */
  1684.                 x = value / 40;
  1685.                 y = value % 40;
  1686.                 if( x > 2 )
  1687.                     {
  1688.                     /* Handle special case for large y if x == 2 */
  1689.                     y += ( x - 2 ) * 40;
  1690.                     x = 2;
  1691.                     }
  1692.                 if( x < 0 || x > 2 || y < 0 || \
  1693.                     ( ( x < 2 && y > 39 ) || \
  1694.                       ( x == 2 && ( y > 50 && y != 100 ) ) ) )
  1695.                     {
  1696.                     /* If x = 0 or 1 then y has to be 0...39, for x = 3
  1697.                        it can take any value but there are no known
  1698.                        assigned values over 50 except for one contrived
  1699.                        example in X.690 which sets y = 100, so if we see
  1700.                        something outside this range it's most likely an
  1701.                        encoding error rather than some bizarre new ID
  1702.                        that's just appeared */
  1703.                     validEncoding = FALSE;
  1704.                     break;
  1705.                     }
  1706.                 length = sprintf( textOID, "%ld %ld", x, y );
  1707.  
  1708.                 /* A totally stupid ITU facility lets people register UUIDs
  1709.                    as OIDs (see http://www.itu.int/ITU-T/asn1/uuid.html), if
  1710.                    we find one of these, which live under the arc '2 25' =
  1711.                    0x69 we have to continue decoding the OID as a UUID
  1712.                    instead of a standard OID */
  1713.                 if( data == 0x69 )
  1714.                     isUUID = TRUE;
  1715.                 }
  1716.             else
  1717.                 length += sprintf( textOID + length, " %ld", value );
  1718.             value = 0;
  1719.             }
  1720.         }
  1721.     if( value != 0 )
  1722.         {
  1723.         /* We stopped in the middle of a continued value */
  1724.         validEncoding = FALSE;
  1725.         }
  1726.     textOID[ length ] = '\0';
  1727.     *textOIDlength = length;
  1728.  
  1729.     return( validEncoding );
  1730.     }
  1731.  
  1732. /* Dump a bitstring, reversing the bits into the standard order in the
  1733.    process */
  1734.  
  1735. static void dumpBitString( FILE *inFile, const int length, const int unused,
  1736.                            const int level )
  1737.     {
  1738.     unsigned int bitString = 0, currentBitMask = 0x80, remainderMask = 0xFF;
  1739.     int bitFlag, value = 0, noBits, bitNo = -1, i;
  1740.     char *errorStr = NULL;
  1741.  
  1742.     if( unused < 0 || unused > 7 )
  1743.         complain( "Invalid number %d of unused bits", unused, level );
  1744.     noBits = ( length * 8 ) - unused;
  1745.  
  1746.     /* ASN.1 bitstrings start at bit 0, so we need to reverse the order of
  1747.        the bits if necessary */
  1748.     if( length > 0 )
  1749.         {
  1750.         bitString = fgetc( inFile );
  1751.         if( bitString == EOF )
  1752.             {
  1753.             noBits = 0;
  1754.             errorStr = "Truncated BIT STRING data";
  1755.             }
  1756.         fPos++;
  1757.         }
  1758.     for( i = noBits - 8; i > 0; i -= 8 )
  1759.         {
  1760.         const int ch = fgetc( inFile );
  1761.  
  1762.         if( ch == EOF )
  1763.             {
  1764.             errorStr = "Truncated BIT STRING data";
  1765.             break;
  1766.             }
  1767.         bitString = ( bitString << 8 ) | ch;
  1768.         currentBitMask <<= 8;
  1769.         remainderMask = ( remainderMask << 8 ) | 0xFF;
  1770.         fPos++;
  1771.         }
  1772.     if( errorStr != NULL )
  1773.         {
  1774.         printString( level, "%c", '\n' );
  1775.         complain( errorStr, 0, level );
  1776.         return;
  1777.         }
  1778.     if( reverseBitString )
  1779.         {
  1780.         for( i = 0, bitFlag = 1; i < noBits; i++ )
  1781.             {
  1782.             if( bitString & currentBitMask )
  1783.                 value |= bitFlag;
  1784.             if( !( bitString & remainderMask ) && errorStr == NULL )
  1785.                 {
  1786.                 /* The last valid bit should be a one bit */
  1787.                 errorStr = "Spurious zero bits in bitstring";
  1788.                 }
  1789.             bitFlag <<= 1;
  1790.             bitString <<= 1;
  1791.             }
  1792.         if( noBits < sizeof( int ) && \
  1793.             ( ( remainderMask << noBits ) & value ) && \
  1794.             errorStr != NULL )
  1795.             {
  1796.             /* There shouldn't be any bits set after the last valid one.  We
  1797.                have to do the noBits check to avoid a fencepost error when
  1798.                there's exactly 32 bits */
  1799.             errorStr = "Spurious one bits in bitstring";
  1800.             }
  1801.         }
  1802.     else
  1803.         value = bitString;
  1804.  
  1805.     /* Now that it's in the right order, dump it.  If there's only one bit
  1806.        set (which is often the case for bit flags) we also print the bit
  1807.        number to save users having to count the zeroes to figure out which
  1808.        flag is set */
  1809.     printString( level, "%c", '\n' );
  1810.     if( !doPure )
  1811.         printString( level, "%s", INDENT_STRING );
  1812.     doIndent( level + 1 );
  1813.     printString( level, "%c", '\'' );
  1814.     if( reverseBitString )
  1815.         currentBitMask = 1 << ( noBits - 1 );
  1816.     for( i = 0; i < noBits; i++ )
  1817.         {
  1818.         if( value & currentBitMask )
  1819.             {
  1820.             bitNo = ( bitNo == -1 ) ? ( noBits - 1 ) - i : -2;
  1821.             printString( level, "%c", '1' );
  1822.             }
  1823.         else
  1824.             printString( level, "%c", '0' );
  1825.         currentBitMask >>= 1;
  1826.         }
  1827.     if( bitNo >= 0 )
  1828.         printString( level, "'B (bit %d)\n", bitNo );
  1829.     else
  1830.         printString( level, "%s", "'B\n" );
  1831.  
  1832.     if( errorStr != NULL )
  1833.         complain( errorStr, 0, level );
  1834.     }
  1835.  
  1836. /* Display data as a text string up to a maximum of 240 characters (8 lines
  1837.    of 48 chars to match the hex limit of 8 lines of 16 bytes) with special
  1838.    treatement for control characters and other odd things that can turn up
  1839.    in BMPString and UniversalString types.
  1840.  
  1841.    If the string is less than 40 chars in length, we try to print it on the
  1842.    same line as the rest of the text (even if it wraps), otherwise we break
  1843.    it up into 48-char chunks in a somewhat less nice text-dump format */
  1844.  
  1845. static void displayString( FILE *inFile, long length, int level,
  1846.                            const STR_OPTION strOption )
  1847.     {
  1848.     char timeStr[ 64 ];
  1849.     long noBytes = length;
  1850.     int lineLength = 48, i;
  1851.     int firstTime = TRUE, doTimeStr = FALSE, warnIA5 = FALSE;
  1852.     int warnPrintable = FALSE, warnTime = FALSE, warnBMP = FALSE;
  1853.  
  1854.     if( noBytes > 384 && !printAllData )
  1855.         noBytes = 384;  /* Only output a maximum of 384 bytes */
  1856.     if( strOption == STR_UTCTIME || strOption == STR_GENERALIZED )
  1857.         {
  1858.         if( ( strOption == STR_UTCTIME && length != 13 ) || \
  1859.             ( strOption == STR_GENERALIZED && length != 15 ) )
  1860.             warnTime = TRUE;
  1861.         else
  1862.             doTimeStr = rawTimeString ? FALSE : TRUE;
  1863.         }
  1864.     if( !doTimeStr && length <= 40 )
  1865.         printString( level, "%s", " '" );   /* Print string on same line */
  1866.     level = adjustLevel( level, ( doPure ) ? 15 : 8 );
  1867.     for( i = 0; i < noBytes; i++ )
  1868.         {
  1869.         int ch;
  1870.  
  1871.         /* If the string is longer than 40 chars, break it up into multiple
  1872.            sections */
  1873.         if( length > 40 && !( i % lineLength ) )
  1874.             {
  1875.             if( !firstTime )
  1876.                 printString( level, "%c", '\'' );
  1877.             printString( level, "%c", '\n' );
  1878.             if( !doPure )
  1879.                 printString( level, "%s", INDENT_STRING );
  1880.             doIndent( level + 1 );
  1881.             printString( level, "%c", '\'' );
  1882.             firstTime = FALSE;
  1883.             }
  1884.         ch = getc( inFile );
  1885.         if( ch == EOF )
  1886.             {
  1887.             complainEOF( level, noBytes - i );
  1888.             return;
  1889.             }
  1890. #if defined( __WIN32__ ) || defined( __UNIX__ ) || defined( __OS390__ )
  1891.         if( strOption == STR_BMP )
  1892.             {
  1893.             if( i == noBytes - 1 && ( noBytes & 1 ) )
  1894.                 {
  1895.                 /* Odd-length BMP string, complain */
  1896.                 warnBMP = TRUE;
  1897.                 }
  1898.             else
  1899.                 {
  1900.                 const wchar_t wCh = ( ch << 8 ) | getc( inFile );
  1901.                
  1902.                 if( displayUnicode( wCh, level ) )
  1903.                     {
  1904.                     lineLength++;
  1905.                     i++;    /* We've read two characters for a wchar_t */
  1906.                     fPos += 2;
  1907.                     continue;
  1908.                     }
  1909.  
  1910.                 /* The value can't be displayed as Unicode, fall back to
  1911.                    displaying it as normal text */
  1912.                 ungetc( wCh & 0xFF, inFile );
  1913.                 }
  1914.             }
  1915.         if( strOption == STR_UTF8 && ( ch & 0x80 ) )
  1916.             {
  1917.             const int secondCh = getc( inFile );
  1918.             wchar_t wCh;
  1919.  
  1920.             /* It's a multibyte UTF8 character, read it as a widechar */
  1921.             if( ( ch & 0xE0 ) == 0xC0 )     /* 111xxxxx -> 110xxxxx */
  1922.                 {
  1923.                 /* 2-byte character in the range 0x80...0x7FF */
  1924.                 wCh = ( ( ch & 0x1F ) << 6 ) | ( secondCh & 0x3F );
  1925.                 i++;        /* We've read 2 characters */
  1926.                 fPos += 2;
  1927.                 }
  1928.             else
  1929.                 {
  1930.                 if( ( ch & 0xF0 ) == 0xE0 ) /* 1111xxxx -> 1110xxxx */
  1931.                     {
  1932.                     const int thirdCh = getc( inFile );
  1933.  
  1934.                     /* 3-byte character in the range 0x800...0xFFFF */
  1935.                     wCh = ( ( ch & 0x1F ) << 12 ) | \
  1936.                           ( ( secondCh & 0x3F ) << 6 ) | \
  1937.                           ( thirdCh & 0x3F );
  1938.                     }
  1939.                 else
  1940.                     wCh = '.';
  1941.                 i += 2;     /* We've read 3 characters */
  1942.                 fPos += 3;
  1943.                 }
  1944.             if( !displayUnicode( wCh, level ) )
  1945.                 printString( level, "%c", '.' );
  1946.             lineLength++;
  1947.             continue;
  1948.             }
  1949. #endif /* __WIN32__ || __UNIX__ || __OS390__ */
  1950.         switch( strOption )
  1951.             {
  1952.             case STR_PRINTABLE:
  1953.             case STR_IA5:
  1954.             case STR_LATIN1:
  1955.                 if( strOption == STR_PRINTABLE && !isPrintable( ch ) )
  1956.                     warnPrintable = TRUE;
  1957.                 if( strOption == STR_IA5 && !isIA5( ch ) )
  1958.                     warnIA5 = TRUE;
  1959.                 if( strOption == STR_LATIN1 )
  1960.                     {
  1961.                     if( !isprint( ch & 0x7F ) )
  1962.                         ch = '.';   /* Convert non-ASCII to placeholders */
  1963.                     }
  1964.                 else
  1965.                     {
  1966.                     if( !isprint( ch ) )
  1967.                         ch = '.';   /* Convert non-ASCII to placeholders */
  1968.                     }
  1969. #ifdef __OS390__
  1970.                 ch = asciiToEbcdic( ch );
  1971. #endif /* __OS390__ */
  1972.                 break;
  1973.  
  1974.             case STR_UTCTIME:
  1975.             case STR_GENERALIZED:
  1976.                 if( !isdigit( ch ) && ch != 'Z' )
  1977.                     {
  1978.                     warnTime = TRUE;
  1979.                     if( !isprint( ch ) )
  1980.                         ch = '.';   /* Convert non-ASCII to placeholders */
  1981.                     }
  1982. #ifdef __OS390__
  1983.                 ch = asciiToEbcdic( ch );
  1984. #endif /* __OS390__ */
  1985.                 break;
  1986.  
  1987.             case STR_BMP_REVERSED:
  1988.                 if( i == noBytes - 1 && ( noBytes & 1 ) )
  1989.                     {
  1990.                     /* Odd-length BMP string, complain */
  1991.                     warnBMP = TRUE;
  1992.                     }
  1993.  
  1994.                 /* Wrong-endianness BMPStrings (Microsoft Unicode) can't be
  1995.                    handled through the usual widechar-handling mechanism
  1996.                    above since the first widechar looks like an ASCII char
  1997.                    followed by a null terminator, so we just treat them as
  1998.                    ASCII chars, skipping the following zero byte.  This is
  1999.                    safe since the code that detects reversed BMPStrings
  2000.                    has already checked that every second byte is zero */
  2001.                 getc( inFile );
  2002.                 i++;
  2003.                 fPos++;
  2004.                 /* Fall through */
  2005.  
  2006.             default:
  2007.                 if( !isprint( ch ) )
  2008.                     ch = '.';   /* Convert control chars to placeholders */
  2009. #ifdef __OS390__
  2010.                 ch = asciiToEbcdic( ch );
  2011. #endif /* __OS390__ */
  2012.             }
  2013.         if( doTimeStr )
  2014.             timeStr[ i ] = ch;
  2015.         else
  2016.             printString( level, "%c", ch );
  2017.         fPos++;
  2018.         }
  2019.     if( length > 384 && !printAllData )
  2020.         {
  2021.         length -= 384;
  2022.         printString( level, "%s", "'\n" );
  2023.         if( !doPure )
  2024.             printString( level, "%s", INDENT_STRING );
  2025.         doIndent( level + 5 );
  2026.         printString( level, "[ Another %ld characters skipped ]", length );
  2027.         fPos += length;
  2028.         while( length-- )
  2029.             {
  2030.             int ch = getc( inFile );
  2031.  
  2032.             if( ch == EOF )
  2033.                 {
  2034.                 complainEOF( level, length );
  2035.                 return;
  2036.                 }
  2037.             if( strOption == STR_PRINTABLE && !isPrintable( ch ) )
  2038.                 warnPrintable = TRUE;
  2039.             if( strOption == STR_IA5 && !isIA5( ch ) )
  2040.                 warnIA5 = TRUE;
  2041.             }
  2042.         }
  2043.     else
  2044.         {
  2045.         if( doTimeStr )
  2046.             {
  2047.             const char *timeStrPtr = ( strOption == STR_UTCTIME ) ? \
  2048.                                      timeStr : timeStr + 2;
  2049.  
  2050.             printString( level, " %c%c/%c%c/",
  2051.                          timeStrPtr[ 4 ], timeStrPtr[ 5 ],
  2052.                          timeStrPtr[ 2 ], timeStrPtr[ 3 ] );
  2053.             if( strOption == STR_UTCTIME )
  2054.                 {
  2055.                 printString( level, "%s",
  2056.                              ( timeStr[ 0 ] < '5' ) ? "20" : "19" );
  2057.                 }
  2058.             else
  2059.                 {
  2060.                 printString( level, "%c%c", timeStr[ 0 ], timeStr[ 1 ] );
  2061.                 }
  2062.             printString( level, "%c%c %c%c:%c%c:%c%c GMT",
  2063.                          timeStrPtr[ 0 ], timeStrPtr[ 1 ], timeStrPtr[ 6 ],
  2064.                          timeStrPtr[ 7 ], timeStrPtr[ 8 ], timeStrPtr[ 9 ],
  2065.                          timeStrPtr[ 10 ], timeStrPtr[ 11 ] );
  2066.             }
  2067.         else
  2068.             printString( level, "%c", '\'' );
  2069.         }
  2070.     printString( level, "%c", '\n' );
  2071.  
  2072.     /* Display any problems we encountered */
  2073.     if( warnPrintable )
  2074.         complain( "PrintableString contains illegal character(s)", 0, level );
  2075.     if( warnIA5 )
  2076.         complain( "IA5String contains illegal character(s)", 0, level );
  2077.     if( warnTime )
  2078.         complain( "Time is encoded incorrectly", 0, level );
  2079.     if( warnBMP )
  2080.         complain( "BMPString has missing final byte/half character", 0, level );
  2081.     }
  2082.  
  2083. /****************************************************************************
  2084. *                                                                           *
  2085. *                               ASN.1 Parsing Routines                      *
  2086. *                                                                           *
  2087. ****************************************************************************/
  2088.  
  2089. /* Get an ASN.1 object's tag and length.  Returns TRUE for an item
  2090.    available, FALSE for end-of-data, and a negative value for an invalid
  2091.    data */
  2092.  
  2093. static int getItem( FILE *inFile, ASN1_ITEM *item )
  2094.     {
  2095.     int tag, length, index = 0;
  2096.  
  2097.     memset( item, 0, sizeof( ASN1_ITEM ) );
  2098.     item->indefinite = FALSE;
  2099.     tag = item->header[ index++ ] = fgetc( inFile );
  2100.     if( tag == EOF )
  2101.         return( FALSE );
  2102.     fPos++;
  2103.     item->id = tag & ~TAG_MASK;
  2104.     tag &= TAG_MASK;
  2105.     if( tag == TAG_MASK )
  2106.         {
  2107.         int value;
  2108.  
  2109.         /* Long tag encoded as sequence of 7-bit values.  This doesn't try to
  2110.            handle tags > INT_MAX, it'd be pretty peculiar ASN.1 if it had to
  2111.            use tags this large */
  2112.         tag = 0;
  2113.         do
  2114.             {
  2115.             value = fgetc( inFile );
  2116.             if( value == EOF )
  2117.                 return( FALSE );
  2118.             tag = ( tag << 7 ) | ( value & 0x7F );
  2119.             item->header[ index++ ] = value;
  2120.             fPos++;
  2121.             }
  2122.         while( value & LEN_XTND && index < 5 && !feof( inFile ) );
  2123.         if( index >= 5 )
  2124.             return( FALSE );
  2125.         }
  2126.     item->tag = tag;
  2127.     length = fgetc( inFile );
  2128.     if( length == EOF )
  2129.         return( FALSE );
  2130.     fPos++;
  2131.     item->header[ index++ ] = length;
  2132.     item->headerSize = index;
  2133.     if( length & LEN_XTND )
  2134.         {
  2135.         const int lengthStart = index;
  2136.         int i;
  2137.  
  2138.         length &= LEN_MASK;
  2139.         if( length > 4 )
  2140.             {
  2141.             /* Impossible length value, probably because we've run into
  2142.                the weeds */
  2143.             return( -1 );
  2144.             }
  2145.         item->headerSize += length;
  2146.         item->length = 0;
  2147.         if( !length )
  2148.             item->indefinite = TRUE;
  2149.         for( i = 0; i < length; i++ )
  2150.             {
  2151.             int ch = fgetc( inFile );
  2152.    
  2153.             if( ch == EOF )
  2154.                 {
  2155.                 fPos += length - i;
  2156.                 return( FALSE );
  2157.                 }
  2158.             item->length = ( item->length << 8 ) | ch;
  2159.             item->header[ i + index ] = ch;
  2160.             }
  2161.         fPos += length;
  2162.  
  2163.         /* Check for the length being less then 128, which means it
  2164.            shouldn't be encoded as a long length */
  2165.         if( !item->indefinite && item->length < 128 )
  2166.             item->nonCanonical = lengthStart;
  2167.  
  2168.         /* Check for the first 9 bits of the length being identical and
  2169.            if they are, remember where the encoded non-canonical length
  2170.            starts */
  2171.         if( item->headerSize - lengthStart > 1 )
  2172.             {
  2173.             if( ( item->header[ lengthStart ] == 0x00 ) && \
  2174.                 ( ( item->header[ lengthStart + 1 ] & 0x80 ) == 0x00 ) )
  2175.                 item->nonCanonical = lengthStart - 1;
  2176.             if( ( item->header[ lengthStart ] == 0xFF ) && \
  2177.                 ( ( item->header[ lengthStart + 1 ] & 0x80 ) == 0x80 ) )
  2178.                 item->nonCanonical = lengthStart - 1;
  2179.             }
  2180.         }
  2181.     else
  2182.         item->length = length;
  2183.  
  2184.     return( TRUE );
  2185.     }
  2186.  
  2187. /* Check whether a BIT STRING or OCTET STRING encapsulates another object */
  2188.  
  2189. static int checkEncapsulate( FILE *inFile, const int length )
  2190.     {
  2191.     ASN1_ITEM nestedItem;
  2192.     const int currentPos = fPos;
  2193.     int diffPos, status;
  2194.  
  2195.     /* If we're not looking for encapsulated objects, return */
  2196.     if( !checkEncaps )
  2197.         return( FALSE );
  2198.  
  2199.     /* An item of length < 2 can never have encapsulated data.  Even for
  2200.        length 2 it can only be an encapsulated NULL, which is somewhat odd,
  2201.        but no doubt there's some PKI protocol somewhere that does this */
  2202.     if( length < 2 )
  2203.         return( FALSE );
  2204.  
  2205.     /* Read the details of the next item in the input stream */
  2206.     status = getItem( inFile, &nestedItem );
  2207.     diffPos = fPos - currentPos;
  2208.     fPos = currentPos;
  2209.     fseek( inFile, -diffPos, SEEK_CUR );
  2210.     if( status <= 0 )
  2211.         return( FALSE );
  2212.  
  2213.     /* If it's not a standard tag class, don't try and dig down into it */
  2214.     if( ( nestedItem.id & CLASS_MASK ) != UNIVERSAL && \
  2215.         ( nestedItem.id & CLASS_MASK ) != CONTEXT )
  2216.         return( FALSE );
  2217.  
  2218.     /* There is one special-case situation that overrides the check below,
  2219.        which is when the nested content is indefinite-length.  This is
  2220.        rather tricky to check for because we'd need to read some distance
  2221.        ahead into the stream to be able to safely decide whether we've got
  2222.        true nested content or a false positive, for now we require that
  2223.        the nested content has to be a SEQUENCE containing valid ASN.1 at
  2224.        the start, giving about 24 bits of checking.  There's a small risk
  2225.        of false negatives for encapsulated primitive items, but since
  2226.        they're primitive it should be relatively easy to make out the
  2227.        contents inside the OCTET STRING */
  2228.     if( nestedItem.tag == SEQUENCE && nestedItem.indefinite )
  2229.         {
  2230.         /* Skip the indefinite-length SEQUENCE and make sure that it's
  2231.            followed by a valid item */
  2232.         status = getItem( inFile, &nestedItem );
  2233.         if( status > 0 )
  2234.             status = getItem( inFile, &nestedItem );
  2235.         diffPos = fPos - currentPos;
  2236.         fPos = currentPos;
  2237.         fseek( inFile, -diffPos, SEEK_CUR );
  2238.         if( status <= 0 )
  2239.             return( FALSE );
  2240.  
  2241.         /* If the tag on the nest item looks vaguely valid, assume that we've
  2242.            go nested content */
  2243.         if( ( nestedItem.tag <= 0 || nestedItem.tag > 0x31 ) || \
  2244.             ( nestedItem.length >= length ) )
  2245.             return( FALSE );
  2246.         return( TRUE );
  2247.         }
  2248.  
  2249.     /* If it doesn't fit exactly within the current item it's not an
  2250.        encapsulated object */
  2251.     if( nestedItem.length != length - diffPos )
  2252.         return( FALSE );
  2253.  
  2254.     /* If it doesn't have a valid-looking tag, don't try and go any further */
  2255.     if( nestedItem.tag <= 0 || nestedItem.tag > 0x31 )
  2256.         return( FALSE );
  2257.  
  2258.     /* Now things get a bit complicated because it's possible to get some
  2259.        (very rare) false positives, for example if a NUMERICSTRING of
  2260.        exactly the right length is nested within an OCTET STRING, since
  2261.        numeric values all look like constructed tags of some kind.  To
  2262.        handle this we look for nested constructed items that should really
  2263.        be primitive */
  2264.     if( ( nestedItem.id & FORM_MASK ) == PRIMITIVE )
  2265.         return( TRUE );
  2266.  
  2267.     /* It's constructed, make sure that it's something for which it makes
  2268.        sense as a constructed object.  At worst this will give some false
  2269.        negatives for really wierd objects (nested constructed strings inside
  2270.        OCTET STRINGs), but these should probably never occur anyway */
  2271.     if( nestedItem.tag == SEQUENCE || \
  2272.         nestedItem.tag == SET )
  2273.         return( TRUE );
  2274.  
  2275.     return( FALSE );
  2276.     }
  2277.  
  2278. /* Check whether a zero-length item is OK */
  2279.  
  2280. static int zeroLengthOK( const ASN1_ITEM *item )
  2281.     {
  2282.     /* An implicitly-tagged NULL can have a zero length.  An occurrence of this
  2283.        type of item is almost always an error, however OCSP uses a weird status
  2284.        encoding that encodes result values in tags and then has to use a NULL
  2285.        value to indicate that there's nothing there except the tag that encodes
  2286.        the status, so we allow this as well if zero-length content is explicitly
  2287.        enabled */
  2288.     if( zeroLengthAllowed && ( item->id & CLASS_MASK ) == CONTEXT )
  2289.         return( TRUE );
  2290.  
  2291.     /* If we can't recognise the type from the tag, reject it */
  2292.     if( ( item->id & CLASS_MASK ) != UNIVERSAL )
  2293.         return( FALSE );
  2294.  
  2295.     /* The following types are zero-length by definition */
  2296.     if( item->tag == EOC || item->tag == NULLTAG )
  2297.         return( TRUE );
  2298.  
  2299.     /* A real with a value of zero has zero length */
  2300.     if( item->tag == REAL )
  2301.         return( TRUE );
  2302.  
  2303.     /* Everything after this point requires input from the user to say that
  2304.        zero-length data is OK (usually it's not, so we flag it as a
  2305.        problem) */
  2306.     if( !zeroLengthAllowed )
  2307.         return( FALSE );
  2308.  
  2309.     /* String types can have zero length except for the Unrestricted
  2310.        Character String type ([UNIVERSAL 29]) which has to have at least one
  2311.        octet for the CH-A/CH-B index */
  2312.     if( item->tag == OCTETSTRING || item->tag == NUMERICSTRING || \
  2313.         item->tag == PRINTABLESTRING || item->tag == T61STRING || \
  2314.         item->tag == VIDEOTEXSTRING || item->tag == VISIBLESTRING || \
  2315.         item->tag == IA5STRING || item->tag == GRAPHICSTRING || \
  2316.         item->tag == GENERALSTRING || item->tag == UNIVERSALSTRING || \
  2317.         item->tag == BMPSTRING || item->tag == UTF8STRING || \
  2318.         item->tag == OBJDESCRIPTOR )
  2319.         return( TRUE );
  2320.  
  2321.     /* SEQUENCE and SET can be zero if there are absent optional/default
  2322.        components */
  2323.     if( item->tag == SEQUENCE || item->tag == SET )
  2324.         return( TRUE );
  2325.  
  2326.     return( FALSE );
  2327.     }
  2328.  
  2329. /* Check whether the next item looks like text */
  2330.  
  2331. static STR_OPTION checkForText( FILE *inFile, const int length )
  2332.     {
  2333.     char buffer[ 16 ];
  2334.     int isBMP = FALSE, isUnicode = FALSE;
  2335.     int sampleLength = min( length, 16 ), i;
  2336.  
  2337.     /* If the sample is very short, we're more careful about what we
  2338.        accept */
  2339.     if( sampleLength < 4 )
  2340.         {
  2341.         /* If the sample size is too small, don't try anything */
  2342.         if( sampleLength <= 2 )
  2343.             return( STR_NONE );
  2344.  
  2345.         /* For samples of 3-4 characters we only allow ASCII text.  These
  2346.            short strings are used in some places (eg PKCS #12 files) as
  2347.            IDs */
  2348.         sampleLength = fread( buffer, 1, sampleLength, inFile );
  2349.         if( sampleLength <= 0 )
  2350.             return( STR_NONE );
  2351.         fseek( inFile, -sampleLength, SEEK_CUR );
  2352.         for( i = 0; i < sampleLength; i++ )
  2353.             {
  2354.             const int ch = byteToInt( buffer[ i ] );
  2355.  
  2356.             if( !( isalpha( ch ) || isdigit( ch ) || isspace( ch ) ) )
  2357.                 return( STR_NONE );
  2358.             }
  2359.         return( STR_IA5 );
  2360.         }
  2361.  
  2362.     /* Check for ASCII-looking text */
  2363.     sampleLength = fread( buffer, 1, sampleLength, inFile );
  2364.     if( sampleLength <= 0 )
  2365.         return( STR_NONE );
  2366.     fseek( inFile, -sampleLength, SEEK_CUR );
  2367.     if( isdigit( byteToInt( buffer[ 0 ] ) ) && \
  2368.         ( length == 13 || length == 15 ) && \
  2369.         buffer[ length - 1 ] == 'Z' )
  2370.         {
  2371.         /* It looks like a time string, make sure that it really is one */
  2372.         for( i = 0; i < length - 1; i++ )
  2373.             {
  2374.             if( !isdigit( byteToInt( buffer[ i ] ) ) )
  2375.                 break;
  2376.             }
  2377.         if( i == length - 1 )
  2378.             return( ( length == 13 ) ? STR_UTCTIME : STR_GENERALIZED );
  2379.         }
  2380.     for( i = 0; i < sampleLength; i++ )
  2381.         {
  2382.         /* If even bytes are zero, it could be a BMPString.  Initially
  2383.            we set isBMP to FALSE, if it looks like a BMPString we set it to
  2384.            TRUE, if we then encounter a nonzero byte it's neither an ASCII
  2385.            nor a BMPString */
  2386.         if( !( i & 1 ) )
  2387.             {
  2388.             if( !buffer[ i ] )
  2389.                 {
  2390.                 /* If we thought we were in a Unicode string but we've found a
  2391.                    zero byte where it'd occur in a BMP string, it's neither a
  2392.                    Unicode nor BMP string */
  2393.                 if( isUnicode )
  2394.                     return( STR_NONE );
  2395.  
  2396.                 /* We've collapsed the eigenstate (in an earlier incarnation
  2397.                    isBMP could take values of -1, 0, or 1, with 0 being
  2398.                    undecided, in which case this comment made a bit more
  2399.                    sense) */
  2400.                 if( i < sampleLength - 2 )
  2401.                     {
  2402.                     /* If the last char(s) are zero but preceding ones
  2403.                        weren't, don't treat it as a BMP string.  This can
  2404.                        happen when storing a null-terminated string if the
  2405.                        implementation gets the length wrong and stores the
  2406.                        null as well */
  2407.                     isBMP = TRUE;
  2408.                     }
  2409.                 continue;
  2410.                 }
  2411.             else
  2412.                 {
  2413.                 /* If we thought we were in a BMPString but we've found a
  2414.                    nonzero byte where there should be a zero, it's neither
  2415.                    an ASCII nor BMP string */
  2416.                 if( isBMP )
  2417.                     return( STR_NONE );
  2418.                 }
  2419.             }
  2420.         else
  2421.             {
  2422.             /* Just to make it tricky, Microsoft stuff Unicode strings into
  2423.                some places (to avoid having to convert them to BMPStrings,
  2424.                presumably) so we have to check for these as well */
  2425.             if( !buffer[ i ] )
  2426.                 {
  2427.                 if( isBMP )
  2428.                     return( STR_NONE );
  2429.                 isUnicode = TRUE;
  2430.                 continue;
  2431.                 }
  2432.             else
  2433.                 {
  2434.                 if( isUnicode )
  2435.                     return( STR_NONE );
  2436.                 }
  2437.             }
  2438.         if( buffer[ i ] < 0x20 || buffer[ i ] > 0x7E )
  2439.             return( STR_NONE );
  2440.         }
  2441.  
  2442.     /* It looks like a text string */
  2443.     return( isUnicode ? STR_BMP_REVERSED : isBMP ? STR_BMP : STR_IA5 );
  2444.     }
  2445.  
  2446. /* Dump the header bytes for an object, useful for vgrepping the original
  2447.    object from a hex dump */
  2448.  
  2449. static void dumpHeader( FILE *inFile, const ASN1_ITEM *item, const int level )
  2450.     {
  2451.     int extraLen = 24 - item->headerSize, i;
  2452.  
  2453.     /* Dump the tag and length bytes */
  2454.     if( !doPure )
  2455.         printString( level, "%s", "    " );
  2456.     printString( level, "<%02X", *item->header );
  2457.     for( i = 1; i < item->headerSize; i++ )
  2458.         printString( level, " %02X", item->header[ i ] );
  2459.  
  2460.     /* If we're asked for more, dump enough extra data to make up 24 bytes.
  2461.        This is somewhat ugly since it assumes we can seek backwards over the
  2462.        data, which means it won't always work on streams */
  2463.     if( extraLen > 0 && doDumpHeader > 1 )
  2464.         {
  2465.         /* Make sure that we don't print too much data.  This doesn't work
  2466.            for indefinite-length data, we don't try and guess the length with
  2467.            this since it involves picking apart what we're printing */
  2468.         if( extraLen > item->length && !item->indefinite )
  2469.             extraLen = ( int ) item->length;
  2470.  
  2471.         for( i = 0; i < extraLen; i++ )
  2472.             {
  2473.             const int ch = fgetc( inFile );
  2474.  
  2475.             if( ch == EOF )
  2476.                 {
  2477.                 /* Exit loop and get fseek() offset correct */
  2478.                 extraLen = i;
  2479.                 break;
  2480.                 }
  2481.             printString( level, " %02X", ch );
  2482.             }
  2483.         fseek( inFile, -extraLen, SEEK_CUR );
  2484.         }
  2485.  
  2486.     printString( level, "%s", ">\n" );
  2487.     }
  2488.  
  2489. /* Print a constructed ASN.1 object */
  2490.  
  2491. static int printAsn1( FILE *inFile, const int level, long length,
  2492.                       const int isIndefinite );
  2493.  
  2494. static void markConstructed( const int level, const ASN1_ITEM *item )
  2495.     {
  2496.     /* If it's a type that's not normally constructed, tag it as such */
  2497.     if( item->id == BOOLEAN || item->id == INTEGER || \
  2498.         item->id == BITSTRING || item->id == OCTETSTRING || \
  2499.         item->id == ENUMERATED  || item->id == UTF8STRING || \
  2500.         ( item->id >= NUMERICSTRING && item->id <= BMPSTRING ) )
  2501.         printString( level, "%s", " (constructed)" );
  2502.     }
  2503.  
  2504. static void printConstructed( FILE *inFile, int level, const ASN1_ITEM *item )
  2505.     {
  2506.     int result;
  2507.  
  2508.     /* Special case for zero-length objects */
  2509.     if( !item->length && !item->indefinite )
  2510.         {
  2511.         printString( level, "%s", " {}\n" );
  2512.         if( item->nonCanonical )
  2513.             complainLengthCanonical( item, level );
  2514.         return;
  2515.         }
  2516.  
  2517.     printString( level, "%s", " {\n" );
  2518.     if( item->nonCanonical )
  2519.         complainLengthCanonical( item, level );
  2520.     result = printAsn1( inFile, level + 1, item->length, item->indefinite );
  2521.     if( result )
  2522.         {
  2523.         fprintf( output, "Error: Inconsistent object length, %d byte%s "
  2524.                  "difference.\n", result, ( result > 1 ) ? "s" : "" );
  2525.         noErrors++;
  2526.         }
  2527.     if( !doPure )
  2528.         printString( level, "%s", INDENT_STRING );
  2529.     printString( level, "%s", ( printDots ) ? ". " : "  " );
  2530.     doIndent( level );
  2531.     printString( level, "%s", "}\n" );
  2532.     }
  2533.  
  2534. /* Print a single ASN.1 object */
  2535.  
  2536. static void printASN1object( FILE *inFile, ASN1_ITEM *item, int level )
  2537.     {
  2538.     OIDINFO *oidInfo;
  2539.     STR_OPTION stringType;
  2540.     BYTE buffer[ MAX_OID_SIZE ];
  2541.     const int nonOutlineObject = \
  2542.             ( doOutlineOnly && ( item->id & FORM_MASK ) != CONSTRUCTED ) ? \
  2543.             TRUE : FALSE;
  2544.  
  2545.     if( ( item->id & CLASS_MASK ) != UNIVERSAL )
  2546.         {
  2547.         static const char *const classtext[] =
  2548.             { "UNIVERSAL ", "APPLICATION ", "", "PRIVATE " };
  2549.  
  2550.         /* Print the object type */
  2551.         if( !nonOutlineObject )
  2552.             {
  2553.             printString( level, "[%s%d]",
  2554.                          classtext[ ( item->id & CLASS_MASK ) >> 6 ], item->tag );
  2555.             }
  2556.  
  2557.         /* Perform a sanity check */
  2558.         if( ( item->tag != NULLTAG ) && ( item->length < 0 ) )
  2559.             {
  2560.             int i;
  2561.  
  2562.             fflush( stdout );
  2563.             fprintf( stderr, "\nError: Object has bad length field, tag = %02X, "
  2564.                      "length = %lX, value =", item->tag, item->length );
  2565.             fprintf( stderr, "<%02X", *item->header );
  2566.             for( i = 1; i < item->headerSize; i++ )
  2567.                 fprintf( stderr, " %02X", item->header[ i ] );
  2568.             fputs( ">.\n", stderr );
  2569.             exit( EXIT_FAILURE );
  2570.             }
  2571.  
  2572.         if( !item->length && !item->indefinite && !zeroLengthOK( item ) )
  2573.             {
  2574.             printString( level, "%c", '\n' );
  2575.             complain( "Object has zero length", 0, level );
  2576.             if( item->nonCanonical )
  2577.                 complainLengthCanonical( item, level );
  2578.             return;
  2579.             }
  2580.  
  2581.         /* If it's constructed, print the various fields in it */
  2582.         if( ( item->id & FORM_MASK ) == CONSTRUCTED )
  2583.             {
  2584.             markConstructed( level, item );
  2585.             printConstructed( inFile, level, item );
  2586.             return;
  2587.             }
  2588.  
  2589.         /* It'sprimitive, if we're only displaying the ASN.1 in outline
  2590.            form, supress the display by dumping it with a nesting level that
  2591.            ensures it won't get output (this clears the data from the input
  2592.            without displaying it) */
  2593.         if( nonOutlineObject )
  2594.             {
  2595.             dumpHex( inFile, item->length, 1000, FALSE );
  2596.             if( item->nonCanonical )
  2597.                 complainLengthCanonical( item, level );
  2598.             printString( level, "%c", '\n' );
  2599.             return;
  2600.             }
  2601.  
  2602.         /* It's primitive, if it's a seekable stream try and determine
  2603.            whether it's text so we can display it as such */
  2604.         if( !useStdin && \
  2605.             ( stringType = checkForText( inFile, item->length ) ) != STR_NONE )
  2606.             {
  2607.             /* It looks like a text string, dump it as text */
  2608.             displayString( inFile, item->length, level, stringType );
  2609.             if( item->nonCanonical )
  2610.                 complainLengthCanonical( item, level );
  2611.             return;
  2612.             }
  2613.  
  2614.         /* This could be anything, dump it as hex data */
  2615.         dumpHex( inFile, item->length, level, FALSE );
  2616.         if( item->nonCanonical )
  2617.             complainLengthCanonical( item, level );
  2618.  
  2619.         return;
  2620.         }
  2621.  
  2622.     /* Print the object type */
  2623.     if( !doOutlineOnly || ( item->id & FORM_MASK ) == CONSTRUCTED )
  2624.         printString( level, "%s", idstr( item->tag ) );
  2625.  
  2626.     /* Perform a sanity check */
  2627.     if( ( item->tag != NULLTAG ) && ( item->length < 0 ) )
  2628.         {
  2629.         int i;
  2630.  
  2631.         fflush( stdout );
  2632.         fprintf( stderr, "\nError: Object has bad length field, tag = %02X, "
  2633.                  "length = %lX, value =", item->tag, item->length );
  2634.         fprintf( stderr, "<%02X", *item->header );
  2635.         for( i = 1; i < item->headerSize; i++ )
  2636.             fprintf( stderr, " %02X", item->header[ i ] );
  2637.         fputs( ">.\n", stderr );
  2638.         exit( EXIT_FAILURE );
  2639.         }
  2640.  
  2641.     /* If it's constructed, print the various fields in it */
  2642.     if( ( item->id & FORM_MASK ) == CONSTRUCTED )
  2643.         {
  2644.         markConstructed( level, item );
  2645.         printConstructed( inFile, level, item );
  2646.         return;
  2647.         }
  2648.  
  2649.     /* It's primitive */
  2650.     if( doOutlineOnly )
  2651.         {
  2652.         /* If we're only displaying the ASN.1 in outline form, set an
  2653.            artificially high nesting level that ensures it won't get output
  2654.            (this clears the data from the input without displaying it) */
  2655.         level = 1000;
  2656.         }
  2657.     if( !item->length && !zeroLengthOK( item ) )
  2658.         {
  2659.         printString( level, "%c", '\n' );
  2660.         complain( "Object has zero length", 0, level );
  2661.         if( item->nonCanonical )
  2662.             complainLengthCanonical( item, level );
  2663.         return;
  2664.         }
  2665.     switch( item->tag )
  2666.         {
  2667.         case BOOLEAN:
  2668.             {
  2669.             int ch;
  2670.  
  2671.             if( item->length != 1 )
  2672.                 complainLength( item, level );
  2673.             ch = getc( inFile );
  2674.             if( ch == EOF )
  2675.                 {
  2676.                 complainEOF( level, 1 );
  2677.                 return;
  2678.                 }
  2679.             printString( level, " %s\n", ch ? "TRUE" : "FALSE" );
  2680.             if( ch != 0 && ch != 0xFF )
  2681.                 {
  2682.                 complain( "BOOLEAN '%02X' has non-DER encoding", ch,
  2683.                           level );
  2684.                 }
  2685.             if( item->nonCanonical )
  2686.                 complainLengthCanonical( item, level );
  2687.             fPos++;
  2688.             break;
  2689.             }
  2690.  
  2691.         case INTEGER:
  2692.         case ENUMERATED:
  2693.             if( item->length > 4 )
  2694.                 {
  2695.                 dumpHex( inFile, item->length, level, TRUE );
  2696.                 if( item->nonCanonical )
  2697.                     complainLengthCanonical( item, level );
  2698.                 }
  2699.             else
  2700.                 {
  2701.                 printValue( inFile, item->length, level );
  2702.                 if( item->nonCanonical )
  2703.                     complainLengthCanonical( item, level );
  2704.                 }
  2705.             break;
  2706.  
  2707.         case BITSTRING:
  2708.             {
  2709.             int ch;
  2710.  
  2711.             if( item->length < 2 ) 
  2712.                 complainLength( item, level );
  2713.             if( ( ch = getc( inFile ) ) != 0 )
  2714.                 {
  2715.                 if( ch == EOF )
  2716.                     {
  2717.                     complainEOF( level, item->length );
  2718.                     return;
  2719.                     }
  2720.                 printString( level, " %d unused bit%s",
  2721.                              ch, ( ch != 1 ) ? "s" : "" );
  2722.                 }
  2723.             fPos++;
  2724.             if( !--item->length && !ch )
  2725.                 {
  2726.                 printString( level, "%c", '\n' );
  2727.                 complain( "Object has zero length", 0, level );
  2728.                 if( item->nonCanonical )
  2729.                     complainLengthCanonical( item, level );
  2730.                 return;
  2731.                 }
  2732.             if( item->length <= sizeof( int ) )
  2733.                 {
  2734.                 /* It's short enough to be a bit flag, dump it as a sequence
  2735.                    of bits */
  2736.                 dumpBitString( inFile, ( int ) item->length, ch, level );
  2737.                 if( item->nonCanonical )
  2738.                     complainLengthCanonical( item, level );
  2739.                 break;
  2740.                 }
  2741.             /* Fall through to dump it as an octet string */
  2742.             }
  2743.  
  2744.         case OCTETSTRING:
  2745.             if( checkEncapsulate( inFile, item->length ) )
  2746.                 {
  2747.                 /* It's something encapsulated inside the string, print it as
  2748.                    a constructed item */
  2749.                 printString( level, "%s", ", encapsulates" );
  2750.                 printConstructed( inFile, level, item );
  2751.                 break;
  2752.                 }
  2753.             if( !useStdin && !dumpText && \
  2754.                 ( stringType = checkForText( inFile, item->length ) ) != STR_NONE )
  2755.                 {
  2756.                 /* If we'd be doing a straight hex dump and it looks like
  2757.                    encapsulated text, display it as such.  If the user has
  2758.                    overridden character set type checking and it's a string
  2759.                    type for which we normally perform type checking, we reset
  2760.                    its type to none */
  2761.                 displayString( inFile, item->length, level, \
  2762.                     ( !checkCharset && ( stringType == STR_IA5 || \
  2763.                                          stringType == STR_PRINTABLE ) ) ? \
  2764.                     STR_NONE : stringType );
  2765.                 if( item->nonCanonical )
  2766.                     complainLengthCanonical( item, level );
  2767.                 return;
  2768.                 }
  2769.             dumpHex( inFile, item->length, level, FALSE );
  2770.             if( item->nonCanonical )
  2771.                 complainLengthCanonical( item, level );
  2772.             break;
  2773.  
  2774.         case OID:
  2775.             {
  2776.             char textOID[ 128 ];
  2777.             int length, isValid;
  2778.  
  2779.             /* Hierarchical Object Identifier */
  2780.             if( item->length <= 0 || item->length >= MAX_OID_SIZE )
  2781.                 {
  2782.                 fflush( stdout );
  2783.                 fprintf( stderr, "\nError: Object identifier length %ld too "
  2784.                          "large.\n", item->length );
  2785.                 exit( EXIT_FAILURE );
  2786.                 }
  2787.             length = fread( buffer, 1, ( size_t ) item->length, inFile );
  2788.             fPos += item->length;
  2789.             if( item->length < 3 ) 
  2790.                 {
  2791.                 fputs( ".\n", output );
  2792.                 complainLength( item, level );
  2793.                 break;
  2794.                 }
  2795.             if( length < item->length )
  2796.                 {
  2797.                 fputs( ".\n", output );
  2798.                 complain( "Invalid OID data", 0, level );
  2799.                 break;
  2800.                 }
  2801.             if( ( oidInfo = getOIDinfo( buffer, ( int ) item->length ) ) != NULL )
  2802.                 {
  2803.                 /* Convert the binary OID to text form */
  2804.                 isValid = oidToString( textOID, &length, buffer,
  2805.                                        ( int ) item->length );
  2806.  
  2807.                 /* Check if LHS status info + indent + "OID " string + oid
  2808.                    name + "(" + oid value + ")" will wrap */
  2809.                 if( ( ( doPure ) ? 0 : INDENT_SIZE ) + ( level * 2 ) + 18 + \
  2810.                     strlen( oidInfo->description ) + 2 + length >= outputWidth )
  2811.                     {
  2812.                     printString( level, "%c", '\n' );
  2813.                     if( !doPure )
  2814.                         printString( level, "%s", INDENT_STRING );
  2815.                     doIndent( level + 1 );
  2816.                     }
  2817.                 else
  2818.                     printString( level, "%c", ' ' );
  2819.                 printString( level, "%s (%s)\n", oidInfo->description, textOID );
  2820.  
  2821.                 /* Display extra comments about the OID if required */
  2822.                 if( extraOIDinfo && oidInfo->comment != NULL )
  2823.                     {
  2824.                     if( !doPure )
  2825.                         printString( level, "%s", INDENT_STRING );
  2826.                     doIndent( level + 1 );
  2827.                     printString( level, "(%s)\n", oidInfo->comment );
  2828.                     }
  2829.                 if( !isValid )
  2830.                     complain( "OID has invalid encoding", 0, level );
  2831.                 if( item->nonCanonical )
  2832.                     complainLengthCanonical( item, level );
  2833.  
  2834.                 /* If there's a warning associated with this OID, remember
  2835.                    that there was a problem */
  2836.                 if( oidInfo->warn )
  2837.                     noWarnings++;
  2838.  
  2839.                 break;
  2840.                 }
  2841.  
  2842.             /* Print the OID as a text string */
  2843.             isValid = oidToString( textOID, &length, buffer,
  2844.                                    ( int ) item->length );
  2845.             printString( level, " '%s'\n", textOID );
  2846.             if( item->length > MAX_SANE_OID_SIZE )
  2847.                 {
  2848.                 /* This only occurs with Microsoft's "encode random noise
  2849.                    and call it an OID" values, so we warn about the fact
  2850.                    that it's not really an OID */
  2851.                 complain( "OID contains random garbage", 0, level );
  2852.                 }
  2853.             if( !isValid )
  2854.                 complain( "OID has invalid encoding", 0, level );
  2855.             if( item->nonCanonical )
  2856.                 complainLengthCanonical( item, level );
  2857.             break;
  2858.             }
  2859.  
  2860.         case EOC:
  2861.             printString( level, "<<EOC>> %c", '\n' );
  2862.             if( item->nonCanonical )
  2863.                 complainLengthCanonical( item, level );
  2864.             break;
  2865.  
  2866.         case NULLTAG:
  2867.             printString( level, "%c", '\n' );
  2868.             if( item->nonCanonical )
  2869.                 complainLengthCanonical( item, level );
  2870.             break;
  2871.  
  2872.         case OBJDESCRIPTOR:
  2873.         case GRAPHICSTRING:
  2874.         case VISIBLESTRING:
  2875.         case GENERALSTRING:
  2876.         case UNIVERSALSTRING:
  2877.         case NUMERICSTRING:
  2878.         case VIDEOTEXSTRING:
  2879.         case PRINTABLESTRING:
  2880.             displayString( inFile, item->length, level, STR_PRINTABLE );
  2881.             if( item->nonCanonical )
  2882.                 complainLengthCanonical( item, level );
  2883.             break;
  2884.         case UTF8STRING:
  2885.             displayString( inFile, item->length, level, STR_UTF8 );
  2886.             if( item->nonCanonical )
  2887.                 complainLengthCanonical( item, level );
  2888.             break;
  2889.         case BMPSTRING:
  2890.             displayString( inFile, item->length, level, STR_BMP );
  2891.             if( item->nonCanonical )
  2892.                 complainLengthCanonical( item, level );
  2893.             break;
  2894.         case UTCTIME:
  2895.             displayString( inFile, item->length, level, STR_UTCTIME );
  2896.             if( item->nonCanonical )
  2897.                 complainLengthCanonical( item, level );
  2898.             break;
  2899.         case GENERALIZEDTIME:
  2900.             displayString( inFile, item->length, level, STR_GENERALIZED );
  2901.             if( item->nonCanonical )
  2902.                 complainLengthCanonical( item, level );
  2903.             break;
  2904.         case IA5STRING:
  2905.             displayString( inFile, item->length, level, STR_IA5 );
  2906.             if( item->nonCanonical )
  2907.                 complainLengthCanonical( item, level );
  2908.             break;
  2909.         case T61STRING:
  2910.             displayString( inFile, item->length, level, STR_LATIN1 );
  2911.             if( item->nonCanonical )
  2912.                 complainLengthCanonical( item, level );
  2913.             break;
  2914.  
  2915.         case SEQUENCE:
  2916.             printString( level, "%c", '\n' );
  2917.             complain( "SEQUENCE has invalid primitive encoding", 0, level );
  2918.             break;
  2919.  
  2920.         case SET:
  2921.             printString( level, "%c", '\n' );
  2922.             complain( "SET has invalid primitive encoding", 0, level );
  2923.             break;
  2924.  
  2925.         default:
  2926.             printString( level, "%c", '\n' );
  2927.             if( !doPure )
  2928.                 printString( level, "%s", INDENT_STRING );
  2929.             doIndent( level + 1 );
  2930.             printString( level, "%s",
  2931.                          "Unrecognised primitive, hex value is:");
  2932.             dumpHex( inFile, item->length, level, FALSE );
  2933.             if( item->nonCanonical )
  2934.                 complainLengthCanonical( item, level );
  2935.             noErrors++;     /* Treat it as an error */
  2936.         }
  2937.     }
  2938.  
  2939. /* Print a complex ASN.1 object */
  2940.  
  2941. static long processObjectStart( FILE *inFile, const ASN1_ITEM *item )
  2942.     {
  2943.     long length = LENGTH_MAGIC;
  2944.  
  2945.     /* If the length isn't known and the item has a definite length, set the
  2946.        length to the item's length */
  2947.     if( !item->indefinite )
  2948.         {
  2949.         length = item->headerSize + item->length;
  2950.  
  2951.         /* We can also adjust the width of the informational data column to
  2952.            maximise the amount of screen real estate (for lengths less than
  2953.            the default of four) or get rid of oversized columns (for lengths
  2954.            greater than four) */
  2955.         if( length < 1000 )
  2956.             infoWidth = 3;
  2957.         else
  2958.         if( length > 9999999 )
  2959.             infoWidth = 8;
  2960.         else
  2961.         if( length > 999999 )
  2962.             infoWidth = 7;
  2963.         else
  2964.         if( length > 99999 )
  2965.             infoWidth = 6;
  2966.         else
  2967.         if( length > 9999 )
  2968.             infoWidth = 5;
  2969.         }
  2970.  
  2971.     /* If the input isn't seekable, turn off some options that require the
  2972.        use of fseek().  This check isn't perfect (some streams are slightly
  2973.        seekable due to buffering) but it's better than nothing */
  2974.     if( fseek( inFile, -item->headerSize, SEEK_CUR ) )
  2975.         {
  2976.         useStdin = TRUE;
  2977.         checkEncaps = FALSE;
  2978.         puts( "Warning: Input is non-seekable, some functionality has been "
  2979.               "disabled." );
  2980.  
  2981.         return( length );
  2982.         }
  2983.  
  2984.     /* If it looks like we've been given a text file, typically due to the
  2985.        input being base64-encoded, check whether it is all text */
  2986.     if( ( isalnum( item->header[ 0 ] ) && isalnum( item->header[ 1 ] ) ) || \
  2987.           ( item->header[ 0 ] == '-' && item->header[ 1 ] == '-' ) )
  2988.         {
  2989.         BYTE buffer[ 4 ];
  2990.         int count, i;
  2991.  
  2992.         count = fread( buffer, 1, 4, inFile );
  2993.         for( i = 0; i < count; i++ )
  2994.             {
  2995.             if( buffer[ i ] != '-' && !isalnum( buffer[ i ] ) )
  2996.                 break;
  2997.             }
  2998.         if( i >= 4 )
  2999.             {
  3000.             fputs( "Error: This file appears to be a base64-encoded text "
  3001.                    "file, not binary data.\n", stderr );
  3002.             fputs( "       In order to display it you first need to decode "
  3003.                    "it into its\n", stderr );
  3004.             fputs( "       binary form.\n", stderr );
  3005.             exit( EXIT_FAILURE );
  3006.             }
  3007.         fseek( inFile, -4, SEEK_CUR );
  3008.         }
  3009.  
  3010.     /* Undo the fseek() that we used to determine whether the input was
  3011.        seekable */
  3012.     fseek( inFile, item->headerSize, SEEK_CUR );
  3013.  
  3014.     return( length );
  3015.     }
  3016.  
  3017. static int printAsn1( FILE *inFile, const int level, long length,
  3018.                       const int isIndefinite )
  3019.     {
  3020.     ASN1_ITEM item;
  3021.     long lastPos = fPos;
  3022.     int seenEOC = FALSE, status;
  3023.  
  3024.     /* Special-case for zero-length objects */
  3025.     if( !length && !isIndefinite )
  3026.         return( 0 );
  3027.  
  3028.     while( ( status = getItem( inFile, &item ) ) > 0 )
  3029.         {
  3030.         int nonOutlineObject = FALSE;
  3031.  
  3032.         /* Perform various special checks the first time that we're called */
  3033.         if( length == LENGTH_MAGIC )
  3034.             length = processObjectStart( inFile, &item );
  3035.  
  3036.         /* Dump the header as hex data if requested */
  3037.         if( doDumpHeader )
  3038.             dumpHeader( inFile, &item, level );
  3039.  
  3040.         /* If we're displaying the ASN.1 outline only and it's not a
  3041.            constructed object, don't display anything */
  3042.         if( doOutlineOnly && ( item.id & FORM_MASK ) != CONSTRUCTED )
  3043.             nonOutlineObject = TRUE;
  3044.  
  3045.         /* Print the offset and length, unless we're in pure ASN.1-only
  3046.            output mode or we're displaying the outline only and it's not
  3047.            a constructed object */
  3048.         if( item.header[ 0 ] == EOC )
  3049.             {
  3050.             seenEOC = TRUE;
  3051.             if( !isIndefinite)
  3052.                 complain( "Spurious EOC in definite-length item", 0, level );
  3053.             }
  3054.         if( !doPure && !nonOutlineObject )
  3055.             {
  3056.             if( item.indefinite )
  3057.                 {
  3058.                 printString( level, ( doHexValues ) ? \
  3059.                                 LEN_HEX_INDEF : LEN_INDEF, lastPos );
  3060.                 }
  3061.             else
  3062.                 {
  3063.                 if( !seenEOC )
  3064.                     {
  3065.                     printString( level, ( doHexValues ) ? \
  3066.                                     LEN_HEX : LEN, lastPos, item.length );
  3067.                     }
  3068.                 }
  3069.             }
  3070.  
  3071.         /* Print details on the item */
  3072.         if( !seenEOC )
  3073.             {
  3074.             if( !nonOutlineObject )
  3075.                 doIndent( level );
  3076.             printASN1object( inFile, &item, level );
  3077.             }
  3078.  
  3079.         /* If it was an indefinite-length object (no length was ever set) and
  3080.            we've come back to the top level, exit */
  3081.         if( length == LENGTH_MAGIC )
  3082.             return( 0 );
  3083.  
  3084.         length -= fPos - lastPos;
  3085.         lastPos = fPos;
  3086.         if( isIndefinite )
  3087.             {
  3088.             if( seenEOC )
  3089.                 return( 0 );
  3090.             }
  3091.         else
  3092.             {
  3093.             if( length <= 0 )
  3094.                 {
  3095.                 if( length < 0 )
  3096.                     return( ( int ) -length );
  3097.                 return( 0 );
  3098.                 }
  3099.             else
  3100.                 {
  3101.                 if( length == 1 )
  3102.                     {
  3103.                     const int ch = fgetc( inFile );
  3104.  
  3105.                     /* If we've run out of input but there should be more
  3106.                        present, let the caller know */
  3107.                     if( ch == EOF )
  3108.                         return( 1 );
  3109.  
  3110.                     /* No object can be one byte long, try and recover.  This
  3111.                        only works sometimes because it can be caused by
  3112.                        spurious data in an OCTET STRING hole or an incorrect
  3113.                        length encoding.  The following workaround tries to
  3114.                        recover from spurious data by skipping the byte if
  3115.                        it's zero or a non-basic-ASN.1 tag, but keeping it if
  3116.                        it could be valid ASN.1 */
  3117.                     if( ch > 0 && ch <= 0x31 )
  3118.                         ungetc( ch, inFile );
  3119.                     else
  3120.                         {
  3121.                         fPos++;
  3122.                         return( 1 );
  3123.                         }
  3124.                     }
  3125.                 }
  3126.             }
  3127.         }
  3128.     if( status == -1 )
  3129.         {
  3130.         int i;
  3131.  
  3132.         fflush( stdout );
  3133.         fprintf( stderr, "\nError: Invalid data encountered at position "
  3134.                  "%d:", fPos );
  3135.         for( i = 0; i < item.headerSize; i++ )
  3136.             fprintf( stderr, " %02X", item.header[ i ] );
  3137.         fprintf( stderr, ".\n" );
  3138.         exit( EXIT_FAILURE );
  3139.         }
  3140.  
  3141.     /* If we see an EOF and there's supposed to be more data present,
  3142.        complain */
  3143.     if( length && length != LENGTH_MAGIC )
  3144.         {
  3145.         fprintf( output, "Error: Inconsistent object length, %ld byte%s "
  3146.                  "difference.\n", length, ( length > 1 ) ? "s" : "" );
  3147.         noErrors++;
  3148.         }
  3149.     return( 0 );
  3150.     }
  3151.  
  3152. /* Show usage and exit */
  3153.  
  3154. static void usageExit( void )
  3155.     {
  3156.     puts( "DumpASN1 - ASN.1 object dump/syntax check program." );
  3157.     puts( "Copyright Peter Gutmann 1997 - 2016.  Last updated " UPDATE_STRING "." );
  3158.     puts( "" );
  3159.  
  3160.     puts( "Usage: dumpasn1 [-acdefghilmoprstuvwxz] <file>" );
  3161.     puts( "  Input options:" );
  3162.     puts( "       - = Take input from stdin (some options may not work properly)" );
  3163.     puts( "       -<number> = Start <number> bytes into the file" );
  3164.     puts( "       -- = End of arg list" );
  3165.     puts( "       -c<file> = Read Object Identifier info from alternate config file" );
  3166.     puts( "            (values will override equivalents in global config file)" );
  3167.     puts( "" );
  3168.  
  3169.     puts( "  Output options:" );
  3170.     puts( "       -f<file> = Dump object at offset -<number> to file (allows data to be" );
  3171.     puts( "            extracted from encapsulating objects)" );
  3172.     puts( "       -w<number> = Set width of output, default = 80 columns" );
  3173.     puts( "" );
  3174.  
  3175.     puts( "  Display options:" );
  3176.     puts( "       -a = Print all data in long data blocks, not just the first 128 bytes" );
  3177.     puts( "       -d = Print dots to show column alignment" );
  3178.     puts( "       -g = Display ASN.1 structure outline only (no primitive objects)" );
  3179.     puts( "       -h = Hex dump object header (tag+length) before the decoded output" );
  3180.     puts( "       -hh = Same as -h but display more of the object as hex data" );
  3181.     puts( "       -i = Use shallow indenting, for deeply-nested objects" );
  3182.     puts( "       -l = Long format, display extra info about Object Identifiers" );
  3183.     puts( "       -m<number>  = Maximum nesting level for which to display content" );
  3184.     puts( "       -p = Pure ASN.1 output without encoding information" );
  3185.     puts( "       -t = Display text values next to hex dump of data" );
  3186.     puts( "       -v = Verbose mode, equivalent to -ahlt" );
  3187.     puts( "" );
  3188.  
  3189.     puts( "  Format options:" );
  3190.     puts( "       -e = Don't print encapsulated data inside OCTET/BIT STRINGs" );
  3191.     puts( "       -r = Print bits in BIT STRING as encoded in reverse order" );
  3192.     puts( "       -u = Don't format UTCTime/GeneralizedTime string data" );
  3193.     puts( "       -x = Display size and offset in hex not decimal" );
  3194.     puts( "" );
  3195.  
  3196.     puts( "  Checking options:" );
  3197.     puts( "       -o = Don't check validity of character strings hidden in octet strings" );
  3198.     puts( "       -s = Syntax check only, don't dump ASN.1 structures" );
  3199.     puts( "       -z = Allow zero-length items" );
  3200.     puts( "" );
  3201.  
  3202.     puts( "Warnings generated by deprecated OIDs require the use of '-l' to be displayed." );
  3203.     puts( "Program return code is the number of errors found or EXIT_SUCCESS." );
  3204.     exit( EXIT_FAILURE );
  3205.     }
  3206.  
  3207. int main( int argc, char *argv[] )
  3208.     {
  3209.     FILE *inFile, *outFile = NULL;
  3210. #ifdef __WIN32__
  3211.     CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
  3212. #endif /* __WIN32__ */
  3213. #ifdef __OS390__
  3214.     char pathPtr[ FILENAME_MAX ];
  3215. #else
  3216.     char *pathPtr = argv[ 0 ];
  3217. #endif /* __OS390__ */
  3218.     long offset = 0;
  3219.     int moreArgs = TRUE, doCheckOnly = FALSE;
  3220.  
  3221. #ifdef __OS390__
  3222.     memset( pathPtr, '\0', sizeof( pathPtr ) );
  3223.     getcwd( pathPtr, sizeof( pathPtr ) );
  3224.     strcat( pathPtr, "/" );
  3225. #endif /* __OS390__ */
  3226.  
  3227.     /* Skip the program name */
  3228.     argv++; argc--;
  3229.  
  3230.     /* Display usage if no args given */
  3231.     if( argc < 1 )
  3232.         usageExit();
  3233.     output = stdout;    /* Needs to be assigned at runtime */
  3234.  
  3235.     /* Get the output width.  Under Unix there's no safe way to do this, so
  3236.        we default to 80 columns */
  3237. #ifdef __WIN32__
  3238.     if( GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ),
  3239.                                     &csbiInfo ) )
  3240.         outputWidth = csbiInfo.dwSize.X;
  3241. #endif /* __WIN32__ */
  3242.  
  3243.     /* Check for arguments */
  3244.     while( argc && *argv[ 0 ] == '-' && moreArgs )
  3245.         {
  3246.         char *argPtr = argv[ 0 ] + 1;
  3247.  
  3248.         if( !*argPtr )
  3249.             useStdin = TRUE;
  3250.         while( *argPtr )
  3251.             {
  3252.             if( isdigit( byteToInt( *argPtr ) ) )
  3253.                 {
  3254.                 offset = atol( argPtr );
  3255.                 break;
  3256.                 }
  3257.             switch( toupper( byteToInt( *argPtr ) ) )
  3258.                 {
  3259.                 case '-':
  3260.                     moreArgs = FALSE;   /* GNU-style end-of-args flag */
  3261.                     break;
  3262.  
  3263.                 case 'A':
  3264.                     printAllData = TRUE;
  3265.                     break;
  3266.  
  3267.                 case 'C':
  3268.                     if( !readConfig( argPtr + 1, FALSE ) )
  3269.                         exit( EXIT_FAILURE );
  3270.                     while( argPtr[ 1 ] )
  3271.                         argPtr++;   /* Skip rest of arg */
  3272.                     break;
  3273.  
  3274.                 case 'D':
  3275.                     printDots = TRUE;
  3276.                     break;
  3277.  
  3278.                 case 'E':
  3279.                     checkEncaps = FALSE;
  3280.                     break;
  3281.  
  3282.                 case 'F':
  3283.                     if( ( outFile = fopen( argPtr + 1, "wb" ) ) == NULL )
  3284.                         {
  3285.                         perror( argPtr + 1 );
  3286.                         exit( EXIT_FAILURE );
  3287.                         }
  3288.                     while( argPtr[ 1 ] )
  3289.                         argPtr++;   /* Skip rest of arg */
  3290.                     break;
  3291.  
  3292.                 case 'G':
  3293.                     doOutlineOnly = TRUE;
  3294.                     break;
  3295.  
  3296.                 case 'H':
  3297.                     doDumpHeader++;
  3298.                     break;
  3299.  
  3300.                 case 'I':
  3301.                     shallowIndent = TRUE;
  3302.                     break;
  3303.  
  3304.                 case 'L':
  3305.                     extraOIDinfo = TRUE;
  3306.                     break;
  3307.  
  3308.                 case 'M':
  3309.                     maxNestLevel = atoi( argPtr + 1 );
  3310.                     if( maxNestLevel < 1 || maxNestLevel > 100 )
  3311.                         {
  3312.                         puts( "Invalid maximum nesting level." );
  3313.                         exit( EXIT_FAILURE );
  3314.                         }
  3315.                     while( argPtr[ 1 ] )
  3316.                         argPtr++;   /* Skip rest of arg */
  3317.                     break;
  3318.  
  3319.                 case 'O':
  3320.                     checkCharset = FALSE;
  3321.                     break;
  3322.  
  3323.                 case 'P':
  3324.                     doPure = TRUE;
  3325.                     break;
  3326.  
  3327.                 case 'R':
  3328.                     reverseBitString = !reverseBitString;
  3329.                     break;
  3330.  
  3331.                 case 'S':
  3332.                     doCheckOnly = TRUE;
  3333. #if defined( __WIN32__ )
  3334.                     /* Under Windows we can't fclose( stdout ) because the
  3335.                        VC++ runtime reassigns the stdout handle to the next
  3336.                        open file (which is valid) but then scribbles stdout
  3337.                        garbage all over it for files larger than about 16K
  3338.                        (which isn't), so we have to make sure that the
  3339.                        stdout handle is pointed to something somewhere */
  3340.                     ( void ) freopen( "nul", "w", stdout );
  3341. #elif defined( __UNIX__ )
  3342.                     /* Safety feature in case any Unix libc is as broken
  3343.                        as the Win32 version */
  3344.                     ( void ) freopen( "/dev/null", "w", stdout );
  3345. #else
  3346.                     fclose( stdout );
  3347. #endif /* OS-specific bypassing of stdout */
  3348.                     break;
  3349.  
  3350.                 case 'T':
  3351.                     dumpText = TRUE;
  3352.                     break;
  3353.  
  3354.                 case 'U':
  3355.                     rawTimeString = TRUE;
  3356.                     break;
  3357.  
  3358.                 case 'V':
  3359.                     printAllData = doDumpHeader = TRUE;
  3360.                     extraOIDinfo = dumpText = TRUE;
  3361.                     break;
  3362.  
  3363.                 case 'W':
  3364.                     outputWidth = atoi( argPtr + 1 );
  3365.                     if( outputWidth < 40 || outputWidth > 500 )
  3366.                         {
  3367.                         puts( "Invalid output width." );
  3368.                         exit( EXIT_FAILURE );
  3369.                         }
  3370.                     while( argPtr[ 1 ] )
  3371.                         argPtr++;   /* Skip rest of arg */
  3372.                     break;
  3373.  
  3374.                 case 'X':
  3375.                     doHexValues = TRUE;
  3376.                     break;
  3377.  
  3378.                 case 'Z':
  3379.                     zeroLengthAllowed = TRUE;
  3380.                     break;
  3381.  
  3382.                 default:
  3383.                     printf( "Unknown argument '%c'.\n", *argPtr );
  3384.                     return( EXIT_SUCCESS );
  3385.                 }
  3386.             argPtr++;
  3387.             }
  3388.         argv++;
  3389.         argc--;
  3390.         }
  3391.  
  3392.     /* We can't use options that perform an fseek() if reading from stdin */
  3393.     if( useStdin && ( doDumpHeader || outFile != NULL ) )
  3394.         {
  3395.         puts( "Can't use -f or -h when taking input from stdin" );
  3396.         exit( EXIT_FAILURE );
  3397.         }
  3398.  
  3399.     /* Check args and read the config file.  We don't bother weeding out
  3400.        dups during the read because (a) the linear search would make the
  3401.        process n^2, (b) during the dump process the search will terminate on
  3402.        the first match so dups aren't that serious, and (c) there should be
  3403.        very few dups present */
  3404.     if( argc != 1 && !useStdin )
  3405.         usageExit();
  3406.     if( !readGlobalConfig( pathPtr ) )
  3407.         exit( EXIT_FAILURE );
  3408.  
  3409.     /* Dump the given file */
  3410.     if( useStdin )
  3411.         inFile = stdin;
  3412.     else
  3413.         {
  3414.         if( ( inFile = fopen( argv[ 0 ], "rb" ) ) == NULL )
  3415.             {
  3416.             perror( argv[ 0 ] );
  3417.             freeConfig();
  3418.             exit( EXIT_FAILURE );
  3419.             }
  3420.         }
  3421.     if( useStdin )
  3422.         {
  3423.         while( offset-- )
  3424.             getc( inFile );
  3425.         }
  3426.     else
  3427.         fseek( inFile, offset, SEEK_SET );
  3428.     if( outFile != NULL )
  3429.         {
  3430.         ASN1_ITEM item;
  3431.         long length;
  3432.         int i, status;
  3433.  
  3434.         /* Make sure that there's something there, and that it has a
  3435.            definite length */
  3436.         status = getItem( inFile, &item );
  3437.         if( status == -1 )
  3438.             {
  3439.             puts( "Non-ASN.1 data encountered." );
  3440.             freeConfig();
  3441.             exit( EXIT_FAILURE );
  3442.             }
  3443.         if( status == 0 )
  3444.             {
  3445.             puts( "Nothing to read." );
  3446.             freeConfig();
  3447.             exit( EXIT_FAILURE );
  3448.             }
  3449.         if( item.indefinite )
  3450.             {
  3451.             puts( "Cannot process indefinite-length item." );
  3452.             freeConfig();
  3453.             exit( EXIT_FAILURE );
  3454.             }
  3455.  
  3456.         /* Copy the item across, first the header and then the data */
  3457.         for( i = 0; i < item.headerSize; i++ )
  3458.             putc( item.header[ i ], outFile );
  3459.         for( length = 0; length < item.length && !feof( inFile ); length++ )
  3460.             putc( getc( inFile ), outFile );
  3461.         fclose( outFile );
  3462.  
  3463.         fseek( inFile, offset, SEEK_SET );
  3464.         }
  3465.     printAsn1( inFile, 0, LENGTH_MAGIC, 0 );
  3466.     if( !useStdin && offset == 0 )
  3467.         {
  3468.         BYTE buffer[ 16 ];
  3469.         long position = ftell( inFile );
  3470.  
  3471.         /* If we're dumping a standalone ASN.1 object and there's further
  3472.            data appended to it, warn the user of its existence.  This is a
  3473.            bit hit-and-miss since there may or may not be additional EOCs
  3474.            present, dumpasn1 always stops once it knows that the data should
  3475.            end (without trying to read any trailing EOCs) because data from
  3476.            some sources has the EOCs truncated, and most apps know that they
  3477.            have to stop at min( data_end, EOCs ).  To avoid false positives,
  3478.            we skip at least 4 EOCs worth of data and if there's still more
  3479.            present, we complain */
  3480.         ( void ) fread( buffer, 1, 8, inFile );     /* Skip 4 EOCs */
  3481.         if( !feof( inFile ) )
  3482.             {
  3483.             fprintf( output, "Warning: Further data follows ASN.1 data at "
  3484.                      "position %ld.\n", position );
  3485.             noWarnings++;
  3486.             }
  3487.         }
  3488.     fclose( inFile );
  3489.     freeConfig();
  3490.  
  3491.     /* Print a summary of warnings/errors if it's required or appropriate */
  3492.     if( !doPure )
  3493.         {
  3494.         fflush( stdout );
  3495.         if( !doCheckOnly )
  3496.             fputc( '\n', stderr );
  3497.         fprintf( stderr, "%d warning%s, %d error%s.\n", noWarnings,
  3498.                 ( noWarnings != 1 ) ? "s" : "", noErrors,
  3499.                 ( noErrors != 1 ) ? "s" : "" );
  3500.         }
  3501.  
  3502.     return( ( noErrors ) ? noErrors : EXIT_SUCCESS );
  3503.     }
Add Comment
Please, Sign In to add comment