rpmio/dumpasn1.c

Go to the documentation of this file.
00001 /* ASN.1 object dumping code, copyright Peter Gutmann
00002    <pgut001@cs.auckland.ac.nz>, based on ASN.1 dump program by David Kemp
00003    <dpkemp@missi.ncsc.mil>, with contributions from various people including
00004    Matthew Hamrick <hamrick@rsa.com>, Bruno Couillard
00005    <bcouillard@chrysalis-its.com>, Hallvard Furuseth
00006    <h.b.furuseth@usit.uio.no>, Geoff Thorpe <geoff@raas.co.nz>, David Boyce
00007    <d.boyce@isode.com>, John Hughes <john.hughes@entegrity.com>, Life is
00008    hard, and then you die <ronald@trustpoint.com>, and several other people
00009    whose names I've misplaced.
00010 
00011    Available from http://www.cs.auckland.ac.nz/~pgut001/dumpasn1.c.
00012    Last updated 21 November 2000.
00013 
00014    This version of dumpasn1 requires a config file dumpasn1.cfg to be present
00015    in the same location as the program itself or in a standard directory
00016    where binaries live (it will run without it but will display a warning
00017    message, you can configure the path either by hardcoding it in or using an
00018    environment variable as explained further down).  The config file is
00019    available from http://www.cs.auckland.ac.nz/~pgut001/dumpasn1.cfg.
00020 
00021    This code assumes that the input data is binary, having come from a MIME-
00022    aware mailer or been piped through a decoding utility if the original
00023    format used base64 encoding.  Bruno Couillard has created a modified
00024    version which will read raw base64-encoded data (ie without any MIME
00025    encapsulation or other headers) directly, at the expense of being somewhat
00026    non-portable.  Alternatively, you can use utilities like uudeview (which
00027    will strip virtually any kind of encoding, MIME, PEM, PGP, whatever) to
00028    recover the binary original.
00029 
00030    You can use this code in whatever way you want, as long as you don't try
00031    to claim you wrote it.
00032 
00033    Editing notes: Tabs to 4, phasers to stun */
00034 
00035 #include <ctype.h>
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 
00040 /* Useful defines */
00041 
00042 #ifndef TRUE
00043   #define FALSE 0
00044   #define TRUE  ( !FALSE )
00045 #endif /* TRUE */
00046 
00047 /* SunOS 4.x doesn't define seek codes or exit codes or FILENAME_MAX (it does
00048    define _POSIX_MAX_PATH, but in funny locations and to different values
00049    depending on which include file you use).  Some OS's also define
00050    FILENAME_MAX to silly values (eg 14 bytes), so we replace it with a more
00051    sensible setting if necessary */
00052 
00053 #ifndef SEEK_SET
00054   #define SEEK_SET      0
00055   #define SEEK_CUR      2
00056 #endif /* No fseek() codes defined */
00057 #ifndef EXIT_FAILURE
00058   #define EXIT_FAILURE  1
00059   #define EXIT_SUCCESS  ( !EXIT_FAILURE )
00060 #endif /* No exit() codes defined */
00061 #ifndef FILENAME_MAX
00062   #define FILENAME_MAX  512
00063 #else
00064   #if FILENAME_MAX < 128
00065         #undef FILENAME_MAX
00066         #define FILENAME_MAX    512
00067   #endif /* FILENAME_MAX < 128 */
00068 #endif /* FILENAME_MAX */
00069 
00070 /* Under Windows we can do special-case handling for things like BMPStrings */
00071 
00072 #if ( defined( _WINDOWS ) || defined( WIN32 ) || defined( _WIN32 ) || \
00073           defined( __WIN32__ ) )
00074   #define __WIN32__
00075 #endif /* Win32 */
00076 
00077 /* Some OS's don't define the min() macro */
00078 
00079 #ifndef min
00080   #define min(a,b)              ( ( a ) < ( b ) ? ( a ) : ( b ) )
00081 #endif /* !min */
00082 
00083 /* The level of recursion can get scary for deeply-nested structures so we
00084    use a larger-than-normal stack under DOS */
00085 
00086 #ifdef  __TURBOC__
00087   extern unsigned _stklen = 16384;
00088 #endif /* __TURBOC__ */
00089 
00090 /* When we dump a nested data object encapsulated within a larger object, the
00091    length is initially set to a magic value which is adjusted to the actual
00092    length once we start parsing the object */
00093 
00094 #define LENGTH_MAGIC    177545L
00095 
00096 /* Tag classes */
00097 
00098 #define CLASS_MASK              0xC0    /* Bits 8 and 7 */
00099 #define UNIVERSAL               0x00    /* 0 = Universal (defined by ITU X.680) */
00100 #define APPLICATION             0x40    /* 1 = Application */
00101 #define CONTEXT                 0x80    /* 2 = Context-specific */
00102 #define PRIVATE                 0xC0    /* 3 = Private */
00103 
00104 /* Encoding type */
00105 
00106 #define FORM_MASK               0x20    /* Bit 6 */
00107 #define PRIMITIVE               0x00    /* 0 = primitive */
00108 #define CONSTRUCTED             0x20    /* 1 = constructed */
00109 
00110 /* Universal tags */
00111 
00112 #define TAG_MASK                0x1F    /* Bits 5 - 1 */
00113 #define EOC                             0x00    /*  0: End-of-contents octets */
00114 #define BOOLEAN                 0x01    /*  1: Boolean */
00115 #define INTEGER                 0x02    /*  2: Integer */
00116 #define BITSTRING               0x03    /*  2: Bit string */
00117 #define OCTETSTRING             0x04    /*  4: Byte string */
00118 #define NULLTAG                 0x05    /*  5: NULL */
00119 #define OID                             0x06    /*  6: Object Identifier */
00120 #define OBJDESCRIPTOR   0x07    /*  7: Object Descriptor */
00121 #define EXTERNAL                0x08    /*  8: External */
00122 #define REAL                    0x09    /*  9: Real */
00123 #define ENUMERATED              0x0A    /* 10: Enumerated */
00124 #define EMBEDDED_PDV    0x0B    /* 11: Embedded Presentation Data Value */
00125 #define UTF8STRING              0x0C    /* 12: UTF8 string */
00126 #define SEQUENCE                0x10    /* 16: Sequence/sequence of */
00127 #define SET                             0x11    /* 17: Set/set of */
00128 #define NUMERICSTRING   0x12    /* 18: Numeric string */
00129 #define PRINTABLESTRING 0x13    /* 19: Printable string (ASCII subset) */
00130 #define T61STRING               0x14    /* 20: T61/Teletex string */
00131 #define VIDEOTEXSTRING  0x15    /* 21: Videotex string */
00132 #define IA5STRING               0x16    /* 22: IA5/ASCII string */
00133 #define UTCTIME                 0x17    /* 23: UTC time */
00134 #define GENERALIZEDTIME 0x18    /* 24: Generalized time */
00135 #define GRAPHICSTRING   0x19    /* 25: Graphic string */
00136 #define VISIBLESTRING   0x1A    /* 26: Visible string (ASCII subset) */
00137 #define GENERALSTRING   0x1B    /* 27: General string */
00138 #define UNIVERSALSTRING 0x1C    /* 28: Universal string */
00139 #define BMPSTRING               0x1E    /* 30: Basic Multilingual Plane/Unicode string */
00140 
00141 /* Length encoding */
00142 
00143 #define LEN_XTND  0x80          /* Indefinite or long form */
00144 #define LEN_MASK  0x7F          /* Bits 7 - 1 */
00145 
00146 /* Various special-case operations to perform on strings */
00147 
00148 typedef enum {
00149         STR_NONE,                               /* No special handling */
00150         STR_UTCTIME,                    /* Check it's UTCTime */
00151         STR_PRINTABLE,                  /* Check it's a PrintableString */
00152         STR_IA5,                                /* Check it's an IA5String */
00153         STR_BMP                                 /* Read and display string as Unicode */
00154         } STR_OPTION;
00155 
00156 /* Structure to hold info on an ASN.1 item */
00157 
00158 typedef struct {
00159         int id;                                         /* Identifier */
00160         int tag;                                        /* Tag */
00161         long length;                            /* Data length */
00162         int indefinite;                         /* Item has indefinite length */
00163         int headerSize;                         /* Size of tag+length */
00164         unsigned char header[ 8 ];      /* Tag+length data */
00165         } ASN1_ITEM;
00166 
00167 /* Config options */
00168 
00169 static int printDots = FALSE;           /* Whether to print dots to align columns */
00170 static int doPure = FALSE;                      /* Print data without LHS info column */
00171 static int doDumpHeader = FALSE;        /* Dump tag+len in hex (level = 0, 1, 2) */
00172 static int extraOIDinfo = FALSE;        /* Print extra information about OIDs */
00173 static int doHexValues = FALSE;         /* Display size, offset in hex not dec.*/
00174 static int useStdin = FALSE;            /* Take input from stdin */
00175 static int zeroLengthAllowed = FALSE;/* Zero-length items allowed */
00176 static int dumpText = FALSE;            /* Dump text alongside hex data */
00177 static int printAllData = FALSE;        /* Whether to print all data in long blocks */
00178 static int checkEncaps = TRUE;          /* Print encaps.data in BIT/OCTET STRINGs */
00179 
00180 /* Error and warning information */
00181 
00182 static int noErrors = 0;                        /* Number of errors found */
00183 static int noWarnings = 0;                      /* Number of warnings */
00184 
00185 /* Position in the input stream */
00186 
00187 static int fPos = 0;                            /* Absolute position in data */
00188 
00189 /* The output stream */
00190 
00191 static FILE *output;                            /* Output stream */
00192 
00193 /* Information on an ASN.1 Object Identifier */
00194 
00195 #define MAX_OID_SIZE    32
00196 
00197 typedef struct tagOIDINFO {
00198         struct tagOIDINFO *next;                /* Next item in list */
00199         char oid[ MAX_OID_SIZE ], *comment, *description;
00200         int oidLength;                                  /* Name, rank, serial number */
00201         int warn;                                               /* Whether to warn if OID encountered */
00202         } OIDINFO;
00203 
00204 static OIDINFO *oidList = NULL;
00205 
00206 /* If the config file isn't present in the current directory, we search the
00207    following paths (this is needed for Unix with dumpasn1 somewhere in the
00208    path, since this doesn't set up argv[0] to the full path).  Anything
00209    beginning with a '$' uses the appropriate environment variable */
00210 
00211 #define CONFIG_NAME             "dumpasn1.cfg"
00212 
00213 static const char *configPaths[] = {
00214         /* Unix absolute paths */
00215         "/bin/", "/usr/bin/", "/usr/local/bin/",
00216 
00217         /* Windoze absolute paths.  Usually things are on C:, but older NT setups
00218            are easier to do on D: if the initial copy is done to C: */
00219         "c:\\dos\\", "d:\\dos\\", "c:\\windows\\", "d:\\windows\\",
00220         "c:\\winnt\\", "d:\\winnt\\",
00221 
00222         /* It's my program, I'm allowed to hardcode in strange paths which noone
00223            else uses */
00224         "$HOME/BIN/", "c:\\program files\\bin\\",
00225 
00226         /* Unix environment-based paths */
00227         "$HOME/", "$HOME/bin/",
00228 
00229         /* General environment-based paths */
00230         "$DUMPASN1_PATH/",
00231 
00232         NULL
00233         };
00234 
00235 #define isEnvTerminator( c )    \
00236         ( ( ( c ) == '/' ) || ( ( c ) == '.' ) || ( ( c ) == '$' ) || \
00237           ( ( c ) == '\0' ) || ( ( c ) == '~' ) )
00238 
00239 /****************************************************************************
00240 *                                                                                                                                                       *
00241 *                                       Object Identification/Description Routines                              *
00242 *                                                                                                                                                       *
00243 ****************************************************************************/
00244 
00245 /* Return descriptive strings for universal tags */
00246 
00247 char *idstr( const int tagID )
00248         {
00249         switch( tagID )
00250                 {
00251                 case EOC:
00252                         return( "End-of-contents octets" );
00253                 case BOOLEAN:
00254                         return( "BOOLEAN" );
00255                 case INTEGER:
00256                         return( "INTEGER" );
00257                 case BITSTRING:
00258                         return( "BIT STRING" );
00259                 case OCTETSTRING:
00260                         return( "OCTET STRING" );
00261                 case NULLTAG:
00262                         return( "NULL" );
00263                 case OID:
00264                         return( "OBJECT IDENTIFIER" );
00265                 case OBJDESCRIPTOR:
00266                         return( "ObjectDescriptor" );
00267                 case EXTERNAL:
00268                         return( "EXTERNAL" );
00269                 case REAL:
00270                         return( "REAL" );
00271                 case ENUMERATED:
00272                         return( "ENUMERATED" );
00273                 case EMBEDDED_PDV:
00274                         return( "EMBEDDED PDV" );
00275                 case UTF8STRING:
00276                         return( "UTF8String" );
00277                 case SEQUENCE:
00278                         return( "SEQUENCE" );
00279                 case SET:
00280                         return( "SET" );
00281                 case NUMERICSTRING:
00282                         return( "NumericString" );
00283                 case PRINTABLESTRING:
00284                         return( "PrintableString" );
00285                 case T61STRING:
00286                         return( "TeletexString" );
00287                 case VIDEOTEXSTRING:
00288                         return( "VideotexString" );
00289                 case IA5STRING:
00290                         return( "IA5String" );
00291                 case UTCTIME:
00292                         return( "UTCTime" );
00293                 case GENERALIZEDTIME:
00294                         return( "GeneralizedTime" );
00295                 case GRAPHICSTRING:
00296                         return( "GraphicString" );
00297                 case VISIBLESTRING:
00298                         return( "VisibleString" );
00299                 case GENERALSTRING:
00300                         return( "GeneralString" );
00301                 case UNIVERSALSTRING:
00302                         return( "UniversalString" );
00303                 case BMPSTRING:
00304                         return( "BMPString" );
00305                 default:
00306                         return( "Unknown (Reserved)" );
00307                 }
00308         }
00309 
00310 /* Return information on an object identifier */
00311 
00312 static OIDINFO *getOIDinfo( char *oid, const int oidLength )
00313         {
00314         OIDINFO *oidPtr;
00315 
00316         memset( oid + oidLength, 0, 2 );
00317         for( oidPtr = oidList; oidPtr != NULL; oidPtr = oidPtr->next )
00318                 if( oidLength == oidPtr->oidLength - 2 && \
00319                         !memcmp( oidPtr->oid + 2, oid, oidLength ) )
00320                         return( oidPtr );
00321 
00322         return( NULL );
00323         }
00324 
00325 /* Add an OID attribute */
00326 
00327 static int addAttribute( char **buffer, char *attribute )
00328         {
00329         if( ( *buffer = ( char * ) malloc( strlen( attribute ) + 1 ) ) == NULL )
00330                 {
00331                 puts( "Out of memory." );
00332                 return( FALSE );
00333                 }
00334         strcpy( *buffer, attribute );
00335         return( TRUE );
00336         }
00337 
00338 /* Table to identify valid string chars (taken from cryptlib) */
00339 
00340 #define P       1                                               /* PrintableString */
00341 #define I       2                                               /* IA5String */
00342 #define PI      3                                               /* IA5String and PrintableString */
00343 
00344 static int charFlags[] = {
00345         /* 00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F */
00346                 0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,
00347         /* 10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F */
00348                 0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,
00349         /*              !       "       #       $       %       &       '       (       )       *       +       ,       -       .       / */
00350            PI,  I,      I,      I,      I,      I,      I, PI, PI, PI,  I, PI, PI, PI, PI, PI,
00351         /*      0       1       2       3       4       5       6       7       8       9       :       ;       <       =       >       ? */
00352            PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,  I,      I, PI,  I, PI,
00353         /*      @       A       B       C       D       E       F       G       H       I       J       K       L       M       N       O */
00354                 I, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,
00355         /*      P       Q       R       S       T       U       V       W       X       Y       Z       [       \       ]       ^ _ */
00356            PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,  I,      I,      I,      I,      I,
00357         /*      `       a       b       c       d       e       f       g       h       i       j       k       l       m       n       o */
00358                 I, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,
00359         /*      p       q       r       s       t       u       v       w       x       y       z       {       |       }       ~  DL */
00360            PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,  I,      I,      I,      I,      0
00361         };
00362 
00363 static int isPrintable( int ch )
00364         {
00365         if( ch >= 128 || !( charFlags[ ch ] & P ) )
00366                 return( FALSE );
00367         return( TRUE );
00368         }
00369 
00370 static int isIA5( int ch )
00371         {
00372         if( ch >= 128 || !( charFlags[ ch ] & I ) )
00373                 return( FALSE );
00374         return( TRUE );
00375         }
00376 
00377 /****************************************************************************
00378 *                                                                                                                                                       *
00379 *                                                       Config File Read Routines                                               *
00380 *                                                                                                                                                       *
00381 ****************************************************************************/
00382 
00383 /* Files coming from DOS/Windows systems may have a ^Z (the CP/M EOF char)
00384    at the end, so we need to filter this out */
00385 
00386 #define CPM_EOF 0x1A            /* ^Z = CPM EOF char */
00387 
00388 /* The maximum input line length */
00389 
00390 #define MAX_LINESIZE    512
00391 
00392 /* Read a line of text from the config file */
00393 
00394 static int lineNo;
00395 
00396 static int readLine( FILE *file, char *buffer )
00397         {
00398         int bufCount = 0, ch;
00399 
00400         /* Skip whitespace */
00401         while( ( ( ch = getc( file ) ) == ' ' || ch == '\t' ) && !feof( file ) );
00402 
00403         /* Get a line into the buffer */
00404         while( ch != '\r' && ch != '\n' && ch != CPM_EOF && !feof( file ) )
00405                 {
00406                 /* Check for an illegal char in the data.  Note that we don't just
00407                    check for chars with high bits set because these are legal in
00408                    non-ASCII strings */
00409                 if( ( ch & 0x7F ) < ' ' )
00410                         {
00411                         printf( "Bad character '%c' in config file line %d.\n",
00412                                         ch, lineNo );
00413                         return( FALSE );
00414                         }
00415 
00416                 /* Check to see if it's a comment line */
00417                 if( ch == '#' && !bufCount )
00418                         {
00419                         /* Skip comment section and trailing whitespace */
00420                         while( ch != '\r' && ch != '\n' && ch != CPM_EOF && !feof( file ) )
00421                                 ch = getc( file );
00422                         break;
00423                         }
00424 
00425                 /* Make sure the line is of the correct length */
00426                 if( bufCount > MAX_LINESIZE )
00427                         {
00428                         printf( "Config file line %d too long.\n", lineNo );
00429                         return( FALSE );
00430                         }
00431                 else
00432                         if( ch )        /* Can happen if we read a binary file */
00433                                 buffer[ bufCount++ ] = ch;
00434 
00435                 /* Get next character */
00436                 ch = getc( file );
00437                 }
00438 
00439         /* If we've just passed a CR, check for a following LF */
00440         if( ch == '\r' )
00441                 if( ( ch = getc( file ) ) != '\n' )
00442                         ungetc( ch, file );
00443 
00444         /* Skip trailing whitespace and add der terminador */
00445         while( bufCount > 0 &&
00446                    ( ( ch = buffer[ bufCount - 1 ] ) == ' ' || ch == '\t' ) )
00447                 bufCount--;
00448         buffer[ bufCount ] = '\0';
00449 
00450         /* Handle special-case of ^Z if file came off an MSDOS system */
00451         if( ch == CPM_EOF )
00452                 while( !feof( file ) )
00453                         /* Keep going until we hit the true EOF (or some sort of error) */
00454                         ch = getc( file );
00455 
00456         return( ferror( file ) ? FALSE : TRUE );
00457         }
00458 
00459 /* Process an OID specified as space-separated hex digits */
00460 
00461 static int processHexOID( OIDINFO *oidInfo, char *string )
00462         {
00463         int value, index = 0;
00464 
00465         while( *string && index < MAX_OID_SIZE - 1 )
00466                 {
00467                 if( sscanf( string, "%x", &value ) != 1 || value > 255 )
00468                         {
00469                         printf( "Invalid hex value in config file line %d.\n", lineNo );
00470                         return( FALSE );
00471                         }
00472                 oidInfo->oid[ index++ ] = value;
00473                 string += 2;
00474                 if( *string && *string++ != ' ' )
00475                         {
00476                         printf( "Invalid hex string in config file line %d.\n", lineNo );
00477                         return( FALSE );
00478                         }
00479                 }
00480         oidInfo->oid[ index ] = 0;
00481         oidInfo->oidLength = index;
00482         if( index >= MAX_OID_SIZE - 1 )
00483                 {
00484                 printf( "OID value in config file line %d too long.\n", lineNo );
00485                 return( FALSE );
00486                 }
00487         return( TRUE );
00488         }
00489 
00490 /* Read a config file */
00491 
00492 static int readConfig( const char *path, const int isDefaultConfig )
00493         {
00494         OIDINFO dummyOID = { NULL, "Dummy", "Dummy", "Dummy", 1 }, *oidPtr;
00495         FILE *file;
00496         char buffer[ MAX_LINESIZE ];
00497         int status;
00498 
00499         /* Try and open the config file */
00500         if( ( file = fopen( path, "rb" ) ) == NULL )
00501                 {
00502                 /* If we can't open the default config file, issue a warning but
00503                    continue anyway */
00504                 if( isDefaultConfig )
00505                         {
00506                         puts( "Cannot open config file 'dumpasn1.cfg', which should be in the same" );
00507                         puts( "directory as the dumpasn1 program.  Operation will continue without" );
00508                         puts( "the ability to display Object Identifier information." );
00509                         puts( "" );
00510                         puts( "If the config file is located elsewhere, you can set the environment" );
00511                         puts( "variable DUMPASN1_CFG to the path to the file." );
00512                         return( TRUE );
00513                         }
00514 
00515                 printf( "Cannot open config file '%s'.\n", path );
00516                 return( FALSE );
00517                 }
00518 
00519         /* Add the new config entries at the appropriate point in the OID list */
00520         if( oidList == NULL )
00521                 oidPtr = &dummyOID;
00522         else
00523                 for( oidPtr = oidList; oidPtr->next != NULL; oidPtr = oidPtr->next );
00524 
00525         /* Read each line in the config file */
00526         lineNo = 1;
00527         while( ( status = readLine( file, buffer ) ) == TRUE && !feof( file ) )
00528                 {
00529                 /* If it's a comment line, skip it */
00530                 if( !*buffer )
00531                         {
00532                         lineNo++;
00533                         continue;
00534                         }
00535 
00536                 /* Check for an attribute tag */
00537                 if( !strncmp( buffer, "OID = ", 6 ) )
00538                         {
00539                         /* Make sure all the required attributes for the current OID are
00540                            present */
00541                         if( oidPtr->description == NULL )
00542                                 {
00543                                 printf( "OID ending on config file line %d has no "
00544                                                 "description attribute.\n", lineNo - 1 );
00545                                 return( FALSE );
00546                                 }
00547 
00548                         /* Allocate storage for the new OID */
00549                         if( ( oidPtr->next = ( struct tagOIDINFO * ) \
00550                                                                  malloc( sizeof( OIDINFO ) ) ) == NULL )
00551                                 {
00552                                 puts( "Out of memory." );
00553                                 return( FALSE );
00554                                 }
00555                         oidPtr = oidPtr->next;
00556                         if( oidList == NULL )
00557                                 oidList = oidPtr;
00558                         memset( oidPtr, 0, sizeof( OIDINFO ) );
00559 
00560                         /* Add the new OID */
00561                         if( !processHexOID( oidPtr, buffer + 6 ) )
00562                                 return( FALSE );
00563                         }
00564                 else if( !strncmp( buffer, "Description = ", 14 ) )
00565                         {
00566                         if( oidPtr->description != NULL )
00567                                 {
00568                                 printf( "Duplicate OID description in config file line %d.\n",
00569                                                 lineNo );
00570                                 return( FALSE );
00571                                 }
00572                         if( !addAttribute( &oidPtr->description, buffer + 14 ) )
00573                                 return( FALSE );
00574                         }
00575                 else if( !strncmp( buffer, "Comment = ", 10 ) )
00576                         {
00577                         if( oidPtr->comment != NULL )
00578                                 {
00579                                 printf( "Duplicate OID comment in config file line %d.\n",
00580                                                 lineNo );
00581                                 return( FALSE );
00582                                 }
00583                         if( !addAttribute( &oidPtr->comment, buffer + 10 ) )
00584                                 return( FALSE );
00585                         }
00586                 else if( !strncmp( buffer, "Warning", 7 ) )
00587                         {
00588                         if( oidPtr->warn )
00589                                 {
00590                                 printf( "Duplicate OID warning in config file line %d.\n",
00591                                                 lineNo );
00592                                 return( FALSE );
00593                                 }
00594                         oidPtr->warn = TRUE;
00595                         }
00596                 else
00597                         {
00598                         printf( "Unrecognised attribute '%s', line %d.\n", buffer,
00599                                         lineNo );
00600                         return( FALSE );
00601                         }
00602 
00603                 lineNo++;
00604                 }
00605         fclose( file );
00606 
00607         return( status );
00608         }
00609 
00610 /* Check for the existence of a config file path */
00611 
00612 static int testConfigPath( const char *path )
00613         {
00614         FILE *file;
00615 
00616         /* Try and open the config file */
00617         if( ( file = fopen( path, "rb" ) ) == NULL )
00618                 return( FALSE );
00619         fclose( file );
00620 
00621         return( TRUE );
00622         }
00623 
00624 /* Build a config path by substituting environment strings for $NAMEs */
00625 
00626 static void buildConfigPath( char *path, const char *pathTemplate )
00627         {
00628         char pathBuffer[ FILENAME_MAX ], newPath[ FILENAME_MAX ];
00629         int pathLen, pathPos = 0, newPathPos = 0;
00630 
00631         /* Add the config file name at the end */
00632         strcpy( pathBuffer, pathTemplate );
00633         strcat( pathBuffer, CONFIG_NAME );
00634         pathLen = strlen( pathBuffer );
00635 
00636         while( pathPos < pathLen )
00637                 {
00638                 char *strPtr;
00639                 int substringSize;
00640 
00641                 /* Find the next $ and copy the data before it to the new path */
00642                 if( ( strPtr = strstr( pathBuffer + pathPos, "$" ) ) != NULL )
00643                         substringSize = ( int ) ( ( strPtr - pathBuffer ) - pathPos );
00644                 else
00645                         substringSize = pathLen - pathPos;
00646                 if( substringSize > 0 )
00647                         memcpy( newPath + newPathPos, pathBuffer + pathPos,
00648                                         substringSize );
00649                 newPathPos += substringSize;
00650                 pathPos += substringSize;
00651 
00652                 /* Get the environment string for the $NAME */
00653                 if( strPtr != NULL )
00654                         {
00655                         char envName[ MAX_LINESIZE ], *envString;
00656                         int i;
00657 
00658                         /* Skip the '$', find the end of the $NAME, and copy the name
00659                            into an internal buffer */
00660                         pathPos++;      /* Skip the $ */
00661                         for( i = 0; !isEnvTerminator( pathBuffer[ pathPos + i ] ); i++ );
00662                         memcpy( envName, pathBuffer + pathPos, i );
00663                         envName[ i ] = '\0';
00664 
00665                         /* Get the env.string and copy it over */
00666                         if( ( envString = getenv( envName ) ) != NULL )
00667                                 {
00668                                 const int envStrLen = strlen( envString );
00669 
00670                                 if( newPathPos + envStrLen < FILENAME_MAX - 2 )
00671                                         {
00672                                         memcpy( newPath + newPathPos, envString, envStrLen );
00673                                         newPathPos += envStrLen;
00674                                         }
00675                                 }
00676                         pathPos += i;
00677                         }
00678                 }
00679         newPath[ newPathPos ] = '\0';   /* Add der terminador */
00680 
00681         /* Copy the new path to the output */
00682         strcpy( path, newPath );
00683         }
00684 
00685 /* Read the global config file */
00686 
00687 static int readGlobalConfig( const char *path )
00688         {
00689         char buffer[ FILENAME_MAX ], *namePos;
00690         int i;
00691 
00692         /* First, try and find the config file in the same directory as the
00693            executable.  This requires that argv[0] be set up properly, which
00694            isn't the case if Unix search paths are being used, and seems to be
00695            pretty broken under Windows */
00696         namePos = strstr( path, "dumpasn1" );
00697         if( namePos == NULL )
00698                 namePos = strstr( path, "DUMPASN1" );
00699         if( strlen( path ) < FILENAME_MAX - 13 && namePos != NULL )
00700                 {
00701                 strcpy( buffer, path );
00702                 strcpy( buffer + ( int ) ( namePos - ( char * ) path ), CONFIG_NAME );
00703                 if( testConfigPath( buffer ) )
00704                         return( readConfig( buffer, TRUE ) );
00705                 }
00706 
00707         /* Now try each of the possible absolute locations for the config file */
00708         for( i = 0; configPaths[ i ] != NULL; i++ )
00709                 {
00710                 buildConfigPath( buffer, configPaths[ i ] );
00711                 if( testConfigPath( buffer ) )
00712                         return( readConfig( buffer, TRUE ) );
00713                 }
00714 
00715         /* Default out to just the config name (which should fail as it was the
00716            first entry in configPaths[]).  readConfig() will display the
00717            appropriate warning */
00718         return( readConfig( CONFIG_NAME, TRUE ) );
00719         }
00720 
00721 /****************************************************************************
00722 *                                                                                                                                                       *
00723 *                                                       Output/Formatting Routines                                              *
00724 *                                                                                                                                                       *
00725 ****************************************************************************/
00726 
00727 /* Indent a string by the appropriate amount */
00728 
00729 static void doIndent( const int level )
00730         {
00731         int i;
00732 
00733         for( i = 0; i < level; i++ )
00734                 fprintf( output, ( printDots ) ? ". " : "  " );
00735         }
00736 
00737 /* Complain about an error in the ASN.1 object */
00738 
00739 static void complain( const char *message, const int level )
00740         {
00741         if( !doPure )
00742                 fprintf( output, "            : " );
00743         doIndent( level + 1 );
00744         fprintf( output, "Error: %s.\n", message );
00745         noErrors++;
00746         }
00747 
00748 /* Dump data as a string of hex digits up to a maximum of 128 bytes */
00749 
00750 static void dumpHex( FILE *inFile, long length, int level, int isInteger )
00751         {
00752         const int lineLength = ( dumpText ) ? 8 : 16;
00753         char printable[ 9 ];
00754         long noBytes = length;
00755         int zeroPadded = FALSE, warnPadding = FALSE, warnNegative = isInteger;
00756         int maxLevel = ( doPure ) ? 15 : 8, i;
00757 
00758         if( noBytes > 128 && !printAllData )
00759                 noBytes = 128;  /* Only output a maximum of 128 bytes */
00760         if( level > maxLevel )
00761                 level = maxLevel;       /* Make sure we don't go off edge of screen */
00762         printable[ 8 ] = printable[ 0 ] = '\0';
00763         for( i = 0; i < noBytes; i++ )
00764                 {
00765                 int ch;
00766 
00767                 if( !( i % lineLength ) )
00768                         {
00769                         if( dumpText )
00770                                 {
00771                                 /* If we're dumping text alongside the hex data, print the
00772                                    accumulated text string */
00773                                 fputs( "    ", output );
00774                                 fputs( printable, output );
00775                                 }
00776                         fputc( '\n', output );
00777                         if( !doPure )
00778                                 fprintf( output, "            : " );
00779                         doIndent( level + 1 );
00780                         }
00781                 ch = getc( inFile );
00782                 fprintf( output, "%s%02X", i % lineLength ? " " : "", ch );
00783                 printable[ i % 8 ] = ( ch >= ' ' && ch < 127 ) ? ch : '.';
00784                 fPos++;
00785 
00786                 /* If we need to check for negative values and zero padding, check
00787                    this now */
00788                 if( !i )
00789                         {
00790                         if( !ch )
00791                                 zeroPadded = TRUE;
00792                         if( !( ch & 0x80 ) )
00793                                 warnNegative = FALSE;
00794                         }
00795                 if( i == 1 && zeroPadded && ch < 0x80 )
00796                         warnPadding = TRUE;
00797                 }
00798         if( dumpText )
00799                 {
00800                 /* Print any remaining text */
00801                 i %= lineLength;
00802                 printable[ i ] = '\0';
00803                 while( i < lineLength )
00804                         {
00805                         fprintf( output, "   " );
00806                         i++;
00807                         }
00808                 fputs( "    ", output );
00809                 fputs( printable, output );
00810                 }
00811         if( length > 128 && !printAllData )
00812                 {
00813                 length -= 128;
00814                 fputc( '\n', output );
00815                 if( !doPure )
00816                         fprintf( output, "            : " );
00817                 doIndent( level + 5 );
00818                 fprintf( output, "[ Another %ld bytes skipped ]", length );
00819                 if( useStdin )
00820                         {
00821                         while( length-- )
00822                                 getc( inFile );
00823                         }
00824                 else
00825                         fseek( inFile, length, SEEK_CUR );
00826                 fPos += length;
00827                 }
00828         fputs( "\n", output );
00829 
00830         if( isInteger )
00831                 {
00832                 if( warnPadding )
00833                         complain( "Integer has non-DER encoding", level );
00834                 if( warnNegative )
00835                         complain( "Integer has a negative value", level );
00836                 }
00837         }
00838 
00839 /* Dump a bitstring, reversing the bits into the standard order in the
00840    process */
00841 
00842 static void dumpBitString( FILE *inFile, const int length, const int unused,
00843                                                    const int level )
00844         {
00845         unsigned int bitString = 0, currentBitMask = 0x80, remainderMask = 0xFF;
00846         int bitFlag, value = 0, noBits, bitNo = -1, i;
00847         char *errorStr = NULL;
00848 
00849         if( unused < 0 || unused > 7 )
00850                 complain( "Invalid number of unused bits", level );
00851         noBits = ( length * 8 ) - unused;
00852 
00853         /* ASN.1 bitstrings start at bit 0, so we need to reverse the order of
00854            the bits */
00855         if( length )
00856                 {
00857                 bitString = fgetc( inFile );
00858                 fPos++;
00859                 }
00860         for( i = noBits - 8; i > 0; i -= 8 )
00861                 {
00862                 bitString = ( bitString << 8 ) | fgetc( inFile );
00863                 currentBitMask <<= 8;
00864                 remainderMask = ( remainderMask << 8 ) | 0xFF;
00865                 fPos++;
00866                 }
00867         for( i = 0, bitFlag = 1; i < noBits; i++ )
00868                 {
00869                 if( bitString & currentBitMask )
00870                         value |= bitFlag;
00871                 if( !( bitString & remainderMask ) )
00872                         /* The last valid bit should be a one bit */
00873                         errorStr = "Spurious zero bits in bitstring";
00874                 bitFlag <<= 1;
00875                 bitString <<= 1;
00876                 }
00877         if( ( remainderMask << noBits ) & value )
00878                 /* There shouldn't be any bits set after the last valid one */
00879                 errorStr = "Spurious one bits in bitstring";
00880 
00881         /* Now that it's in the right order, dump it.  If there's only one
00882            bit set (which is often the case for bit flags) we also print the
00883            bit number to save users having to count the zeroes to figure out
00884            which flag is set */
00885         fputc( '\n', output );
00886         if( !doPure )
00887                 fprintf( output, "            : " );
00888         doIndent( level + 1 );
00889         fputc( '\'', output );
00890         currentBitMask = 1 << ( noBits - 1 );
00891         for( i = 0; i < noBits; i++ )
00892                 {
00893                 if( value & currentBitMask )
00894                         {
00895                         bitNo = ( bitNo == -1 ) ? ( noBits - 1 ) - i : -2;
00896                         fputc( '1', output );
00897                         }
00898                 else
00899                         fputc( '0', output );
00900                 currentBitMask >>= 1;
00901                 }
00902         if( bitNo >= 0 )
00903                 fprintf( output, "'B (bit %d)\n", bitNo );
00904         else
00905                 fputs( "'B\n", output );
00906 
00907         if( errorStr != NULL )
00908                 complain( errorStr, level );
00909         }
00910 
00911 /* Display data as a text string up to a maximum of 240 characters (8 lines
00912    of 48 chars to match the hex limit of 8 lines of 16 bytes) with special
00913    treatement for control characters and other odd things which can turn up
00914    in BMPString and UniversalString types.
00915 
00916    If the string is less than 40 chars in length, we try to print it on the
00917    same line as the rest of the text (even if it wraps), otherwise we break
00918    it up into 48-char chunks in a somewhat less nice text-dump format */
00919 
00920 static void displayString( FILE *inFile, long length, int level,
00921                                                    STR_OPTION strOption )
00922         {
00923         long noBytes = ( length > 384 ) ? 384 : length;
00924         int lineLength = 48;
00925         int maxLevel = ( doPure ) ? 15 : 8, firstTime = TRUE, i;
00926         int warnIA5 = FALSE, warnPrintable = FALSE, warnUTC = FALSE;
00927         int warnBMP = FALSE;
00928 
00929         if( strOption == STR_UTCTIME && length != 13 )
00930                 warnUTC = TRUE;
00931         if( length <= 40 )
00932                 fprintf( output, " '" );                /* Print string on same line */
00933         if( level > maxLevel )
00934                 level = maxLevel;       /* Make sure we don't go off edge of screen */
00935         for( i = 0; i < noBytes; i++ )
00936                 {
00937                 int ch;
00938 
00939                 /* If the string is longer than 40 chars, break it up into multiple
00940                    sections */
00941                 if( length > 40 && !( i % lineLength ) )
00942                         {
00943                         if( !firstTime )
00944                                 fputc( '\'', output );
00945                         fputc( '\n', output );
00946                         if( !doPure )
00947                                 fprintf( output, "            : " );
00948                         doIndent( level + 1 );
00949                         fputc( '\'', output );
00950                         firstTime = FALSE;
00951                         }
00952                 ch = getc( inFile );
00953 #ifdef __WIN32__
00954                 if( strOption == STR_BMP )
00955                         {
00956                         if( i == noBytes - 1 && ( noBytes & 1 ) )
00957                                 /* Odd-length BMP string, complain */
00958                                 warnBMP = TRUE;
00959                         else
00960                                 {
00961                                 wchar_t wCh = ( ch << 8 ) | getc( inFile );
00962                                 unsigned char outBuf[ 8 ];
00963                                 int outLen;
00964 
00965                                 /* Attempting to display Unicode characters is pretty hit and
00966                                    miss, and if it fails nothing is displayed.  To try and
00967                                    detect this we use wcstombs() to see if anything can be
00968                                    displayed, if it can't we drop back to trying to display
00969                                    the data as non-Unicode */
00970                                 outLen = wcstombs( outBuf, &wCh, 1 );
00971                                 if( outLen < 1 )
00972                                         {
00973                                         /* Can't be displayed as Unicode, fall back to
00974                                            displaying it as normal text */
00975                                         ungetc( wCh & 0xFF, inFile );
00976                                         }
00977                                 else
00978                                         {
00979                                         lineLength++;
00980                                         i++;    /* We've read two characters for a wchar_t */
00981                                         wprintf( L"%c", wCh );
00982                                         fPos += 2;
00983                                         continue;
00984                                         }
00985                                 }
00986                         }
00987 #endif /* __WIN32__ */
00988                 if( strOption == STR_PRINTABLE || strOption == STR_IA5 )
00989                         {
00990                         if( strOption == STR_PRINTABLE && !isPrintable( ch ) )
00991                                 warnPrintable = TRUE;
00992                         if( strOption == STR_IA5 && !isIA5( ch ) )
00993                                 warnIA5 = TRUE;
00994                         if( ch < ' ' || ch >= 0x7F )
00995                                 ch = '.';               /* Convert non-ASCII to placeholders */
00996                         }
00997                 else
00998                         if( strOption == STR_UTCTIME )
00999                                 {
01000                                 if( !isdigit( ch ) && ch != 'Z' )
01001                                         {
01002                                         warnUTC = TRUE;
01003                                         ch = '.';       /* Convert non-numeric to placeholders */
01004                                         }
01005                                 }
01006                         else
01007                                 if( ( ch & 0x7F ) < ' ' || ch == 0xFF )
01008                                         ch = '.';       /* Convert control chars to placeholders */
01009                 fputc( ch, output );
01010                 fPos++;
01011                 }
01012         if( length > 384 )
01013                 {
01014                 length -= 384;
01015                 fprintf( output, "'\n" );
01016                 if( !doPure )
01017                         fprintf( output, "            : " );
01018                 doIndent( level + 5 );
01019                 fprintf( output, "[ Another %ld characters skipped ]", length );
01020                 fPos += length;
01021                 while( length-- )
01022                         {
01023                         int ch = getc( inFile );
01024 
01025                         if( strOption == STR_PRINTABLE && !isPrintable( ch ) )
01026                                 warnPrintable = TRUE;
01027                         if( strOption == STR_IA5 && !isIA5( ch ) )
01028                                 warnIA5 = TRUE;
01029                         }
01030                 }
01031         else
01032                 fputc( '\'', output );
01033         fputc( '\n', output );
01034 
01035         /* Display any problems we encountered */
01036         if( warnPrintable )
01037                 complain( "PrintableString contains illegal character(s)", level );
01038         if( warnIA5 )
01039                 complain( "IA5String contains illegal character(s)", level );
01040         if( warnUTC )
01041                 complain( "UTCTime is encoded incorrectly", level );
01042         if( warnBMP )
01043                 complain( "BMPString has missing final byte/half character", level );
01044         }
01045 
01046 /****************************************************************************
01047 *                                                                                                                                                       *
01048 *                                                               ASN.1 Parsing Routines                                          *
01049 *                                                                                                                                                       *
01050 ****************************************************************************/
01051 
01052 /* Get an integer value */
01053 
01054 static long getValue( FILE *inFile, const long length )
01055         {
01056         long value;
01057         char ch;
01058         int i;
01059 
01060         ch = getc( inFile );
01061         value = ch;
01062         for( i = 0; i < length - 1; i++ )
01063                 value = ( value << 8 ) | getc( inFile );
01064         fPos += length;
01065 
01066         return( value );
01067         }
01068 
01069 /* Get an ASN.1 objects tag and length */
01070 
01071 int getItem( FILE *inFile, ASN1_ITEM *item )
01072         {
01073         int tag, length, index = 0;
01074 
01075         memset( item, 0, sizeof( ASN1_ITEM ) );
01076         item->indefinite = FALSE;
01077         tag = item->header[ index++ ] = fgetc( inFile );
01078         item->id = tag & ~TAG_MASK;
01079         tag &= TAG_MASK;
01080         if( tag == TAG_MASK )
01081                 {
01082                 int value;
01083 
01084                 /* Long tag encoded as sequence of 7-bit values.  This doesn't try to
01085                    handle tags > INT_MAX, it'd be pretty peculiar ASN.1 if it had to
01086                    use tags this large */
01087                 tag = 0;
01088                 do
01089                         {
01090                         value = fgetc( inFile );
01091                         tag = ( tag << 7 ) | ( value & 0x7F );
01092                         item->header[ index++ ] = value;
01093                         fPos++;
01094                         }
01095                 while( value & LEN_XTND && !feof( inFile ) );
01096                 }
01097         item->tag = tag;
01098         if( feof( inFile ) )
01099                 {
01100                 fPos++;
01101                 return( FALSE );
01102                 }
01103         fPos += 2;                      /* Tag + length */
01104         length = item->header[ index++ ] = fgetc( inFile );
01105         item->headerSize = index;
01106         if( length & LEN_XTND )
01107                 {
01108                 int i;
01109 
01110                 length &= LEN_MASK;
01111                 if( length > 4 )
01112                         /* Impossible length value, probably because we've run into
01113                            the weeds */
01114                         return( -1 );
01115                 item->headerSize += length;
01116                 item->length = 0;
01117                 if( !length )
01118                         item->indefinite = TRUE;
01119                 for( i = 0; i < length; i++ )
01120                         {
01121                         int ch = fgetc( inFile );
01122 
01123                         item->length = ( item->length << 8 ) | ch;
01124                         item->header[ i + index ] = ch;
01125                         }
01126                 fPos += length;
01127                 }
01128         else
01129                 item->length = length;
01130 
01131         return( TRUE );
01132         }
01133 
01134 /* Check whether a BIT STRING or OCTET STRING encapsulates another object */
01135 
01136 static int checkEncapsulate( FILE *inFile, const int tag, const int length )
01137         {
01138         ASN1_ITEM nestedItem;
01139         const int currentPos = fPos;
01140         int diffPos;
01141 
01142         /* If we're not looking for encapsulated objects, return */
01143         if( !checkEncaps )
01144                 return( FALSE );
01145 
01146 #if 1
01147         /* Read the details of the next item in the input stream */
01148         getItem( inFile, &nestedItem );
01149         diffPos = fPos - currentPos;
01150         fPos = currentPos;
01151         fseek( inFile, -diffPos, SEEK_CUR );
01152 
01153         /* If it fits exactly within the current item and has a valid-looking
01154            tag, treat it as nested data */
01155         if( ( ( nestedItem.id & CLASS_MASK ) == UNIVERSAL || \
01156                   ( nestedItem.id & CLASS_MASK ) == CONTEXT ) && \
01157                 ( nestedItem.tag > 0 && nestedItem.tag <= 0x31 ) && \
01158                 nestedItem.length == length - diffPos )
01159                 return( TRUE );
01160 #else
01161         /* Older code which used heuristics but was actually less accurate than
01162            the above code */
01163         int ch;
01164 
01165         /* Get the first character and see if it's an INTEGER or SEQUENCE */
01166         ch = getc( inFile );
01167         ungetc( ch, inFile );
01168         if( ch == INTEGER || ch == ( SEQUENCE | CONSTRUCTED ) )
01169                 return( TRUE );
01170 
01171         /* All sorts of weird things get bundled up in octet strings in
01172            certificate extensions */
01173         if( tag == OCTETSTRING && ch == BITSTRING )
01174                 return( TRUE );
01175 
01176         /* If we're looking for all sorts of things which might be encapsulated,
01177            check for these as well.  At the moment we only check for a small
01178            number of possibilities, this list will probably change as more
01179            oddities are discovered, the idea is to keep the amount of burrowing
01180            we do to a minimum in order to reduce problems with false positives */
01181         if( level > 1 && tag == OCTETSTRING )
01182                 {
01183                 int length;
01184 
01185                 if( ch == IA5STRING )
01186                         /* Verisign extensions */
01187                         return( TRUE );
01188 
01189                 /* For the following possibilities we have to look ahead a bit
01190                    further and check the length as well */
01191                 getc( inFile );
01192                 length = getc( inFile );
01193                 fseek( inFile, -2, SEEK_CUR );
01194                 if( ( ch == OID && length < 9 ) || \
01195                         ( ch == ENUMERATED && length == 1 ) || \
01196                         ( ch == GENERALIZEDTIME && length == 15 ) )
01197                         /* CRL per-entry extensions */
01198                         return( TRUE );
01199                 }
01200 #endif /* 0 */
01201 
01202         return( FALSE );
01203         }
01204 
01205 /* Check whether a zero-length item is OK */
01206 
01207 int zeroLengthOK( const ASN1_ITEM *item )
01208         {
01209         /* If we can't recognise the type from the tag, reject it */
01210         if( ( item->id & CLASS_MASK ) != UNIVERSAL )
01211                 return( FALSE );
01212 
01213         /* The following types are zero-length by definition */
01214         if( item->tag == EOC || item->tag == NULLTAG )
01215                 return( TRUE );
01216 
01217         /* A real with a value of zero has zero length */
01218         if( item->tag == REAL )
01219                 return( TRUE );
01220 
01221         /* Everything after this point requires input from the user to say that
01222            zero-length data is OK (usually it's not, so we flag it as a
01223            problem) */
01224         if( !zeroLengthAllowed )
01225                 return( FALSE );
01226 
01227         /* String types can have zero length except for the Unrestricted
01228            Character String type ([UNIVERSAL 29]) which has to have at least one
01229            octet for the CH-A/CH-B index */
01230         if( item->tag == OCTETSTRING || item->tag == NUMERICSTRING || \
01231                 item->tag == PRINTABLESTRING || item->tag == T61STRING || \
01232                 item->tag == VIDEOTEXSTRING || item->tag == VISIBLESTRING || \
01233                 item->tag == IA5STRING || item->tag == GRAPHICSTRING || \
01234                 item->tag == GENERALSTRING || item->tag == UNIVERSALSTRING || \
01235                 item->tag == BMPSTRING || item->tag == UTF8STRING || \
01236                 item->tag == OBJDESCRIPTOR )
01237                 return( TRUE );
01238 
01239         /* SEQUENCE and SET can be zero if there are absent optional/default
01240            components */
01241         if( item->tag == SEQUENCE || item->tag == SET )
01242                 return( TRUE );
01243 
01244         return( FALSE );
01245         }
01246 
01247 /* Check whether the next item looks like text */
01248 
01249 static int looksLikeText( FILE *inFile, const int length )
01250         {
01251         char buffer[ 16 ];
01252         int sampleLength = min( length, 16 ), i;
01253 
01254         /* If the sample size is too small, don't try anything */
01255         if( sampleLength < 4 )
01256                 return( FALSE );
01257 
01258         /* Check for ASCII-looking text */
01259         sampleLength = fread( buffer, 1, sampleLength, inFile );
01260         fseek( inFile, -sampleLength, SEEK_CUR );
01261         for( i = 0; i < sampleLength; i++ )
01262                 {
01263                 if( !( i & 1 ) && !buffer[ i ] )
01264                         /* If even bytes are zero, it could be a BMPString */
01265                         continue;
01266                 if( buffer[ i ] < 0x20 || buffer[ i ] > 0x7E )
01267                         return( FALSE );
01268                 }
01269 
01270         /* It looks like a text string */
01271         return( TRUE);
01272         }
01273 
01274 /* Dump the header bytes for an object, useful for vgrepping the original
01275    object from a hex dump */
01276 
01277 static void dumpHeader( FILE *inFile, const ASN1_ITEM *item )
01278         {
01279         int extraLen = 24 - item->headerSize, i;
01280 
01281         /* Dump the tag and length bytes */
01282         if( !doPure )
01283                 fprintf( output, "    " );
01284         fprintf( output, "<%02X", *item->header );
01285         for( i = 1; i < item->headerSize; i++ )
01286                 fprintf( output, " %02X", item->header[ i ] );
01287 
01288         /* If we're asked for more, dump enough extra data to make up 24 bytes.
01289            This is somewhat ugly since it assumes we can seek backwards over the
01290            data, which means it won't always work on streams */
01291         if( extraLen > 0 && doDumpHeader > 1 )
01292                 {
01293                 /* Make sure we don't print too much data.  This doesn't work for
01294                    indefinite-length data, we don't try and guess the length with
01295                    this since it involves picking apart what we're printing */
01296                 if( extraLen > item->length && !item->indefinite )
01297                         extraLen = ( int ) item->length;
01298 
01299                 for( i = 0; i < extraLen; i++ )
01300                         {
01301                         int ch = fgetc( inFile );
01302 
01303                         if( feof( inFile ) )
01304                                 extraLen = i;   /* Exit loop and get fseek() correct */
01305                         else
01306                                 fprintf( output, " %02X", ch );
01307                         }
01308                 fseek( inFile, -extraLen, SEEK_CUR );
01309                 }
01310 
01311         fputs( ">\n", output );
01312         }
01313 
01314 /* Print a constructed ASN.1 object */
01315 
01316 int printAsn1( FILE *inFile, const int level, long length, const int isIndefinite );
01317 
01318 static void printConstructed( FILE *inFile, int level, const ASN1_ITEM *item )
01319         {
01320         int result;
01321 
01322         /* Special case for zero-length objects */
01323         if( !item->length && !item->indefinite )
01324                 {
01325                 fputs( " {}\n", output );
01326                 return;
01327                 }
01328 
01329         fputs( " {\n", output );
01330         result = printAsn1( inFile, level + 1, item->length, item->indefinite );
01331         if( result )
01332                 {
01333                 fprintf( output, "Error: Inconsistent object length, %d byte%s "
01334                                  "difference.\n", result, ( result > 1 ) ? "s" : "" );
01335                 noErrors++;
01336                 }
01337         if( !doPure )
01338                 fprintf( output, "            : " );
01339         fprintf( output, ( printDots ) ? ". " : "  " );
01340         doIndent( level );
01341         fputs( "}\n", output );
01342         }
01343 
01344 /* Print a single ASN.1 object */
01345 
01346 void printASN1object( FILE *inFile, ASN1_ITEM *item, int level )
01347         {
01348         OIDINFO *oidInfo;
01349         char buffer[ MAX_OID_SIZE ];
01350         long value;
01351         int x, y;
01352 
01353         if( ( item->id & CLASS_MASK ) != UNIVERSAL )
01354                 {
01355                 static const char *const classtext[] =
01356                         { "UNIVERSAL ", "APPLICATION ", "", "PRIVATE " };
01357 
01358                 /* Print the object type */
01359                 fprintf( output, "[%s%d]",
01360                                  classtext[ ( item->id & CLASS_MASK ) >> 6 ], item->tag );
01361 
01362                 /* Perform a sanity check */
01363                 if( ( item->tag != NULLTAG ) && ( item->length < 0 ) )
01364                         {
01365                         int i;
01366 
01367                         fprintf( stderr, "\nError: Object has bad length field, tag = %02X, "
01368                                          "length = %lX, value =", item->tag, item->length );
01369                         fprintf( stderr, "<%02X", *item->header );
01370                         for( i = 1; i < item->headerSize; i++ )
01371                                 fprintf( stderr, " %02X", item->header[ i ] );
01372                         fputs( ">.\n", stderr );
01373                         exit( EXIT_FAILURE );
01374                         }
01375 
01376                 if( !item->length && !item->indefinite )
01377                         {
01378                         fputc( '\n', output );
01379                         complain( "Object has zero length", level );
01380                         return;
01381                         }
01382 
01383                 /* If it's constructed, print the various fields in it */
01384                 if( ( item->id & FORM_MASK ) == CONSTRUCTED )
01385                         {
01386                         printConstructed( inFile, level, item );
01387                         return;
01388                         }
01389 
01390                 /* It's primitive, if it's a seekable stream try and determine
01391                    whether it's text so we can display it as such */
01392                 if( !useStdin && looksLikeText( inFile, item->length ) )
01393                         {
01394                         /* It looks like a text string, dump it as text */
01395                         displayString( inFile, item->length, level, STR_NONE );
01396                         return;
01397                         }
01398 
01399                 /* This could be anything, dump it as hex data */
01400                 dumpHex( inFile, item->length, level, FALSE );
01401 
01402                 return;
01403                 }
01404 
01405         /* Print the object type */
01406         fprintf( output, "%s", idstr( item->tag ) );
01407 
01408         /* Perform a sanity check */
01409         if( ( item->tag != NULLTAG ) && ( item->length < 0 ) )
01410                 {
01411                 int i;
01412 
01413                 fprintf( stderr, "\nError: Object has bad length field, tag = %02X, "
01414                                  "length = %lX, value =", item->tag, item->length );
01415                 fprintf( stderr, "<%02X", *item->header );
01416                 for( i = 1; i < item->headerSize; i++ )
01417                         fprintf( stderr, " %02X", item->header[ i ] );
01418                 fputs( ">.\n", stderr );
01419                 exit( EXIT_FAILURE );
01420                 }
01421 
01422         /* If it's constructed, print the various fields in it */
01423         if( ( item->id & FORM_MASK ) == CONSTRUCTED )
01424                 {
01425                 printConstructed( inFile, level, item );
01426                 return;
01427                 }
01428 
01429         /* It's primitive */
01430         if( !item->length && !zeroLengthOK( item ) )
01431                 {
01432                 fputc( '\n', output );
01433                 complain( "Object has zero length", level );
01434                 return;
01435                 }
01436         switch( item->tag )
01437                 {
01438                 case BOOLEAN:
01439                         x = getc( inFile );
01440                         fprintf( output, " %s\n", x ? "TRUE" : "FALSE" );
01441                         if( x != 0 && x != 0xFF )
01442                                 complain( "BOOLEAN has non-DER encoding", level );
01443                         fPos++;
01444                         break;
01445 
01446                 case INTEGER:
01447                 case ENUMERATED:
01448                         if( item->length > 4 )
01449                                 dumpHex( inFile, item->length, level, TRUE );
01450                         else
01451                                 {
01452                                 value = getValue( inFile, item->length );
01453                                 fprintf( output, " %ld\n", value );
01454                                 if( value < 0 )
01455                                         complain( "Integer has a negative value", level );
01456                                 }
01457                         break;
01458 
01459                 case BITSTRING:
01460                         fprintf( output, " %d unused bits", x = getc( inFile ) );
01461                         fPos++;
01462                         if( !--item->length && !x )
01463                                 {
01464                                 fputc( '\n', output );
01465                                 complain( "Object has zero length", level );
01466                                 return;
01467                                 }
01468                         if( item->length <= sizeof( int ) )
01469                                 {
01470                                 /* It's short enough to be a bit flag, dump it as a sequence
01471                                    of bits */
01472                                 dumpBitString( inFile, ( int ) item->length, x, level );
01473                                 break;
01474                                 }
01475                 case OCTETSTRING:
01476                         if( checkEncapsulate( inFile, item->tag, item->length ) )
01477                                 {
01478                                 /* It's something encapsulated inside the string, print it as
01479                                    a constructed item */
01480                                 fprintf( output, ", encapsulates" );
01481                                 printConstructed( inFile, level + 1, item );
01482                                 break;
01483                                 }
01484                         if( !useStdin && !dumpText && \
01485                                 looksLikeText( inFile, item->length ) )
01486                                 {
01487                                 /* If we'd be doing a straight hex dump and it looks like
01488                                    encapsulated text, display it as such */
01489                                 displayString( inFile, item->length, level, STR_NONE );
01490                                 return;
01491                                 }
01492                         dumpHex( inFile, item->length, level, FALSE );
01493                         break;
01494 
01495                 case OID:
01496                         /* Hierarchical Object Identifier: The first two levels are
01497                            encoded into one byte, since the root level has only 3 nodes
01498                            (40*x + y).  However if x = joint-iso-itu-t(2) then y may be
01499                            > 39, so we have to add special-case handling for this */
01500                         if( item->length > MAX_OID_SIZE )
01501                                 {
01502                                 fprintf( stderr, "\nError: Object identifier length %ld too "
01503                                                  "large.\n", item->length );
01504                                 exit( EXIT_FAILURE );
01505                                 }
01506                         fread( buffer, 1, ( size_t ) item->length, inFile );
01507                         fPos += item->length;
01508                         if( ( oidInfo = getOIDinfo( buffer, ( int ) item->length ) ) != NULL )
01509                                 {
01510                                 int lhsSize = ( doPure ) ? 0 : 14;
01511 
01512                                 /* Check if LHS status info + indent + "OID " string + oid
01513                                    name will wrap */
01514                                 if( lhsSize + ( level * 2 ) + 18 + strlen( oidInfo->description ) >= 80 )
01515                                         {
01516                                         fputc( '\n', output );
01517                                         if( !doPure )
01518                                                 fprintf( output, "            : " );
01519                                         doIndent( level + 1 );
01520                                         }
01521                                 else
01522                                         fputc( ' ', output );
01523                                 fprintf( output, "%s\n", oidInfo->description );
01524 
01525                                 /* Display extra comments about the OID if required */
01526                                 if( extraOIDinfo && oidInfo->comment != NULL )
01527                                         {
01528                                         if( !doPure )
01529                                                 fprintf( output, "            : " );
01530                                         doIndent( level + 1 );
01531                                         fprintf( output, "(%s)\n", oidInfo->comment );
01532                                         }
01533 
01534                                 /* If there's a warning associated with this OID, remember
01535                                    that there was a problem */
01536                                 if( oidInfo->warn )
01537                                         noWarnings++;
01538 
01539                                 break;
01540                                 }
01541 
01542                         /* Pick apart the OID */
01543                         x = ( unsigned char ) buffer[ 0 ] / 40;
01544                         y = ( unsigned char ) buffer[ 0 ] % 40;
01545                         if( x > 2 )
01546                                 {
01547                                 /* Handle special case for large y if x = 2 */
01548                                 y += ( x - 2 ) * 40;
01549                                 x = 2;
01550                                 }
01551                         fprintf( output, " '%d %d", x, y );
01552                         value = 0;
01553                         for( x = 1; x < item->length; x++ )
01554                                 {
01555                                 value = ( value << 7 ) | ( buffer[ x ] & 0x7F );
01556                                 if( !( buffer[ x ] & 0x80 ) )
01557                                         {
01558                                         fprintf( output, " %ld", value );
01559                                         value = 0;
01560                                         }
01561                                 }
01562                         fprintf( output, "'\n" );
01563                         break;
01564 
01565                 case EOC:
01566                 case NULLTAG:
01567                         fputc( '\n', output );
01568                         break;
01569 
01570                 case OBJDESCRIPTOR:
01571                 case GENERALIZEDTIME:
01572                 case GRAPHICSTRING:
01573                 case VISIBLESTRING:
01574                 case GENERALSTRING:
01575                 case UNIVERSALSTRING:
01576                 case NUMERICSTRING:
01577                 case T61STRING:
01578                 case VIDEOTEXSTRING:
01579                 case UTF8STRING:
01580                         displayString( inFile, item->length, level, STR_NONE );
01581                         break;
01582                 case PRINTABLESTRING:
01583                         displayString( inFile, item->length, level, STR_PRINTABLE );
01584                         break;
01585                 case BMPSTRING:
01586                         displayString( inFile, item->length, level, STR_BMP );
01587                         break;
01588                 case UTCTIME:
01589                         displayString( inFile, item->length, level, STR_UTCTIME );
01590                         break;
01591                 case IA5STRING:
01592                         displayString( inFile, item->length, level, STR_IA5 );
01593                         break;
01594 
01595                 default:
01596                         fputc( '\n', output );
01597                         if( !doPure )
01598                                 fprintf( output, "            : " );
01599                         doIndent( level + 1 );
01600                         fprintf( output, "Unrecognised primitive, hex value is:");
01601                         dumpHex( inFile, item->length, level, FALSE );
01602                         noErrors++;             /* Treat it as an error */
01603                 }
01604         }
01605 
01606 /* Print a complex ASN.1 object */
01607 
01608 int printAsn1( FILE *inFile, const int level, long length,
01609                            const int isIndefinite )
01610         {
01611         ASN1_ITEM item;
01612         long lastPos = fPos;
01613         int seenEOC = FALSE, status;
01614 
01615         /* Special-case for zero-length objects */
01616         if( !length && !isIndefinite )
01617                 return( 0 );
01618 
01619         while( ( status = getItem( inFile, &item ) ) > 0 )
01620                 {
01621                 /* If the length isn't known and the item has a definite length, set
01622                    the length to the items length */
01623                 if( length == LENGTH_MAGIC && !item.indefinite )
01624                         length = item.headerSize + item.length;
01625 
01626                 /* Dump the header as hex data if requested */
01627                 if( doDumpHeader )
01628                         dumpHeader( inFile, &item );
01629 
01630                 /* Print offset into buffer, tag, and length */
01631                 if( !doPure )
01632                         if( item.indefinite )
01633                                 fprintf( output, ( doHexValues ) ? "%04lX %02X NDEF: " :
01634                                                  "%4ld %02X NDEF: ", lastPos, item.id | item.tag );
01635                         else
01636                                 if( ( item.id | item.tag ) == EOC )
01637                                         seenEOC = TRUE;
01638                                 else
01639                                         fprintf( output, ( doHexValues ) ? "%04lX %02X %4lX: " :
01640                                                          "%4ld %02X %4ld: ", lastPos, item.id | item.tag,
01641                                                          item.length );
01642 
01643                 /* Print details on the item */
01644                 if( !seenEOC )
01645                         {
01646                         doIndent( level );
01647                         printASN1object( inFile, &item, level );
01648                         }
01649 
01650                 /* If it was an indefinite-length object (no length was ever set) and
01651                    we've come back to the top level, exit */
01652                 if( length == LENGTH_MAGIC )
01653                         return( 0 );
01654 
01655                 length -= fPos - lastPos;
01656                 lastPos = fPos;
01657                 if( isIndefinite )
01658                         {
01659                         if( seenEOC )
01660                                 return( 0 );
01661                         }
01662                 else
01663                         if( length <= 0 )
01664                                 {
01665                                 if( length < 0 )
01666                                         return( ( int ) -length );
01667                                 return( 0 );
01668                                 }
01669                         else
01670                                 if( length == 1 )
01671                                         {
01672                                         const int ch = fgetc( inFile );
01673 
01674                                         /* No object can be one byte long, try and recover.  This
01675                                            only works sometimes because it can be caused by
01676                                            spurious data in an OCTET STRING hole or an incorrect
01677                                            length encoding.  The following workaround tries to
01678                                            recover from spurious data by skipping the byte if
01679                                            it's zero or a non-basic-ASN.1 tag, but keeping it if
01680                                            it could be valid ASN.1 */
01681                                         if( ch && ch <= 0x31 )
01682                                                 ungetc( ch, inFile );
01683                                         else
01684                                                 {
01685                                                 fPos++;
01686                                                 return( 1 );
01687                                                 }
01688                                         }
01689                 }
01690         if( status == -1 )
01691                 {
01692                 fprintf( stderr, "\nError: Invalid data encountered at position "
01693                                  "%d.\n", fPos );
01694                 exit( EXIT_FAILURE );
01695                 }
01696 
01697         /* If we see an EOF and there's supposed to be more data present,
01698            complain */
01699         if( length && length != LENGTH_MAGIC )
01700                 {
01701                 fprintf( output, "Error: Inconsistent object length, %ld byte%s "
01702                                  "difference.\n", length, ( length > 1 ) ? "s" : "" );
01703                 noErrors++;
01704                 }
01705         return( 0 );
01706         }
01707 
01708 /* Show usage and exit */
01709 
01710 void usageExit( void )
01711         {
01712         puts( "DumpASN1 - ASN.1 object dump/syntax check program." );
01713         puts( "Copyright Peter Gutmann 1997 - 2000.  Last updated 21 November 2000." );
01714         puts( "" );
01715         puts( "Usage: dumpasn1 [-acdefhlpsxz] <file>" );
01716         puts( "       - = Take input from stdin (some options may not work properly)" );
01717         puts( "       -<number> = Start <number> bytes into the file" );
01718         puts( "       -- = End of arg list" );
01719         puts( "       -a = Print all data in long data blocks, not just the first 128 bytes" );
01720         puts( "       -c<file> = Read Object Identifier info from alternate config file" );
01721         puts( "            (values will override equivalents in global config file)" );
01722         puts( "       -d = Print dots to show column alignment" );
01723         puts( "       -e = Don't print encapsulated data inside OCTET/BIT STRINGs" );
01724         puts( "       -f<file> = Dump object at offset -<number> to file (allows data to be" );
01725         puts( "            extracted from encapsulating objects)" );
01726         puts( "       -h = Hex dump object header (tag+length) before the decoded output" );
01727         puts( "       -hh = Same as -h but display more of the object as hex data" );
01728         puts( "       -l = Long format, display extra info about Object Identifiers" );
01729         puts( "       -p = Pure ASN.1 output without encoding information" );
01730         puts( "       -s = Syntax check only, don't dump ASN.1 structures" );
01731         puts( "       -t = Display text values next to hex dump of data" );
01732         puts( "       -x = Display size and offset in hex not decimal" );
01733         puts( "       -z = Allow zero-length items" );
01734         puts( "" );
01735         puts( "Warnings generated by deprecated OIDs require the use of '-l' to be displayed." );
01736         puts( "Program return code is the number of errors found or EXIT_SUCCESS." );
01737         exit( EXIT_FAILURE );
01738         }
01739 
01740 int main( int argc, char *argv[] )
01741         {
01742         FILE *inFile, *outFile = NULL;
01743         char *pathPtr = argv[ 0 ];
01744         long offset = 0;
01745         int moreArgs = TRUE, doCheckOnly = FALSE;
01746 
01747         /* Skip the program name */
01748         argv++; argc--;
01749 
01750         /* Display usage if no args given */
01751         if( argc < 1 )
01752                 usageExit();
01753         output = stdout;        /* Needs to be assigned at runtime */
01754 
01755         /* Check for arguments */
01756         while( argc && *argv[ 0 ] == '-' && moreArgs )
01757                 {
01758                 char *argPtr = argv[ 0 ] + 1;
01759 
01760                 if( !*argPtr )
01761                         useStdin = TRUE;
01762                 while( *argPtr )
01763                         {
01764                         if( isdigit( *argPtr ) )
01765                                 {
01766                                 offset = atol( argPtr );
01767                                 break;
01768                                 }
01769                         switch( toupper( *argPtr ) )
01770                                 {
01771                                 case '-':
01772                                         moreArgs = FALSE;       /* GNU-style end-of-args flag */
01773                                         break;
01774 
01775                                 case 'A':
01776                                         printAllData = TRUE;
01777                                         break;
01778 
01779                                 case 'C':
01780                                         if( !readConfig( argPtr + 1, FALSE ) )
01781                                                 exit( EXIT_FAILURE );
01782                                         while( argPtr[ 1 ] )
01783                                                 argPtr++;       /* Skip rest of arg */
01784                                         break;
01785 
01786                                 case 'D':
01787                                         printDots = TRUE;
01788                                         break;
01789 
01790                                 case 'E':
01791                                         checkEncaps = FALSE;
01792                                         break;
01793 
01794                                 case 'F':
01795                                         if( ( outFile = fopen( argPtr + 1, "wb" ) ) == NULL )
01796                                                 {
01797                                                 perror( argPtr + 1 );
01798                                                 exit( EXIT_FAILURE );
01799                                                 }
01800                                         while( argPtr[ 1 ] )
01801                                                 argPtr++;       /* Skip rest of arg */
01802                                         break;
01803 
01804                                 case 'L':
01805                                         extraOIDinfo = TRUE;
01806                                         break;
01807 
01808                                 case 'H':
01809                                         doDumpHeader++;
01810                                         break;
01811 
01812                                 case 'P':
01813                                         doPure = TRUE;
01814                                         break;
01815 
01816                                 case 'S':
01817                                         doCheckOnly = TRUE;
01818 #ifdef __WIN32__
01819                                         /* Under Windows we can't fclose( stdout ) because the
01820                                            VC++ runtime reassigns the stdout handle to the next
01821                                            open file (which is valid) but then scribbles stdout
01822                                            garbage all over it for files larger than about 16K
01823                                            (which isn't), so we have to make sure that the
01824                                            stdout is handle pointed to something somewhere */
01825                                         freopen( "nul", "w", stdout );
01826 #else
01827                                         /* If we know we're running under Unix we can also
01828                                            freopen( "/dev/null", "w", stdout ); */
01829                                         fclose( stdout );
01830 #endif /* __WIN32__ */
01831                                         break;
01832 
01833                                 case 'T':
01834                                         dumpText = TRUE;
01835                                         break;
01836 
01837                                 case 'X':
01838                                         doHexValues = TRUE;
01839                                         break;
01840 
01841                                 case 'Z':
01842                                         zeroLengthAllowed = TRUE;
01843                                         break;
01844 
01845                                 default:
01846                                         printf( "Unknown argument '%c'.\n", *argPtr );
01847                                         return( EXIT_SUCCESS );
01848                                 }
01849                         argPtr++;
01850                         }
01851                 argv++;
01852                 argc--;
01853                 }
01854 
01855         /* We can't use options which perform an fseek() if reading from stdin */
01856         if( useStdin && ( doDumpHeader || outFile != NULL ) )
01857                 {
01858                 puts( "Can't use -f or -h when taking input from stdin" );
01859                 exit( EXIT_FAILURE );
01860                 }
01861 
01862         /* Check args and read the config file.  We don't bother weeding out
01863            dups during the read because (a) the linear search would make the
01864            process n^2, (b) during the dump process the search will terminate on
01865            the first match so dups aren't that serious, and (c) there should be
01866            very few dups present */
01867         if( argc != 1 && !useStdin )
01868                 usageExit();
01869         if( !readGlobalConfig( pathPtr ) )
01870                 exit( EXIT_FAILURE );
01871 
01872         /* Dump the given file */
01873         if( useStdin )
01874                 inFile = stdin;
01875         else
01876                 if( ( inFile = fopen( argv[ 0 ], "rb" ) ) == NULL )
01877                         {
01878                         perror( argv[ 0 ] );
01879                         exit( EXIT_FAILURE );
01880                         }
01881         if( useStdin )
01882                 {
01883                 while( offset-- )
01884                         getc( inFile );
01885                 }
01886         else
01887                 fseek( inFile, offset, SEEK_SET );
01888         if( outFile != NULL )
01889                 {
01890                 ASN1_ITEM item;
01891                 long length;
01892                 int i, status;
01893 
01894                 /* Make sure there's something there, and that it has a definite
01895                    length */
01896                 status = getItem( inFile, &item );
01897                 if( status == -1 )
01898                         {
01899                         puts( "Non-ASN.1 data encountered." );
01900                         exit( EXIT_FAILURE );
01901                         }
01902                 if( status == 0 )
01903                         {
01904                         puts( "Nothing to read." );
01905                         exit( EXIT_FAILURE );
01906                         }
01907                 if( item.indefinite )
01908                         {
01909                         puts( "Cannot process indefinite-length item." );
01910                         exit( EXIT_FAILURE );
01911                         }
01912 
01913                 /* Copy the item across, first the header and then the data */
01914                 for( i = 0; i < item.headerSize; i++ )
01915                         putc( item.header[ i ], outFile );
01916                 for( length = 0; length < item.length && !feof( inFile ); length++ )
01917                         putc( getc( inFile ), outFile );
01918                 fclose( outFile );
01919 
01920                 fseek( inFile, offset, SEEK_SET );
01921                 }
01922         printAsn1( inFile, 0, LENGTH_MAGIC, 0 );
01923         fclose( inFile );
01924 
01925         /* Print a summary of warnings/errors if it's required or appropriate */
01926         if( !doPure )
01927                 {
01928                 if( !doCheckOnly )
01929                         fputc( '\n', stderr );
01930                 fprintf( stderr, "%d warning%s, %d error%s.\n", noWarnings,
01931                                 ( noWarnings != 1 ) ? "s" : "", noErrors,
01932                                 ( noErrors != 1 ) ? "s" : "" );
01933                 }
01934 
01935         return( ( noErrors ) ? noErrors : EXIT_SUCCESS );
01936         }

Generated on Tue Feb 19 22:26:20 2008 for rpm by  doxygen 1.5.1