00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include <ctype.h>
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039
00040
00041
00042 #ifndef TRUE
00043 #define FALSE 0
00044 #define TRUE ( !FALSE )
00045 #endif
00046
00047
00048
00049
00050
00051
00052
00053 #ifndef SEEK_SET
00054 #define SEEK_SET 0
00055 #define SEEK_CUR 2
00056 #endif
00057 #ifndef EXIT_FAILURE
00058 #define EXIT_FAILURE 1
00059 #define EXIT_SUCCESS ( !EXIT_FAILURE )
00060 #endif
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
00068 #endif
00069
00070
00071
00072 #if ( defined( _WINDOWS ) || defined( WIN32 ) || defined( _WIN32 ) || \
00073 defined( __WIN32__ ) )
00074 #define __WIN32__
00075 #endif
00076
00077
00078
00079 #ifndef min
00080 #define min(a,b) ( ( a ) < ( b ) ? ( a ) : ( b ) )
00081 #endif
00082
00083
00084
00085
00086 #ifdef __TURBOC__
00087 extern unsigned _stklen = 16384;
00088 #endif
00089
00090
00091
00092
00093
00094 #define LENGTH_MAGIC 177545L
00095
00096
00097
00098 #define CLASS_MASK 0xC0
00099 #define UNIVERSAL 0x00
00100 #define APPLICATION 0x40
00101 #define CONTEXT 0x80
00102 #define PRIVATE 0xC0
00103
00104
00105
00106 #define FORM_MASK 0x20
00107 #define PRIMITIVE 0x00
00108 #define CONSTRUCTED 0x20
00109
00110
00111
00112 #define TAG_MASK 0x1F
00113 #define EOC 0x00
00114 #define BOOLEAN 0x01
00115 #define INTEGER 0x02
00116 #define BITSTRING 0x03
00117 #define OCTETSTRING 0x04
00118 #define NULLTAG 0x05
00119 #define OID 0x06
00120 #define OBJDESCRIPTOR 0x07
00121 #define EXTERNAL 0x08
00122 #define REAL 0x09
00123 #define ENUMERATED 0x0A
00124 #define EMBEDDED_PDV 0x0B
00125 #define UTF8STRING 0x0C
00126 #define SEQUENCE 0x10
00127 #define SET 0x11
00128 #define NUMERICSTRING 0x12
00129 #define PRINTABLESTRING 0x13
00130 #define T61STRING 0x14
00131 #define VIDEOTEXSTRING 0x15
00132 #define IA5STRING 0x16
00133 #define UTCTIME 0x17
00134 #define GENERALIZEDTIME 0x18
00135 #define GRAPHICSTRING 0x19
00136 #define VISIBLESTRING 0x1A
00137 #define GENERALSTRING 0x1B
00138 #define UNIVERSALSTRING 0x1C
00139 #define BMPSTRING 0x1E
00140
00141
00142
00143 #define LEN_XTND 0x80
00144 #define LEN_MASK 0x7F
00145
00146
00147
00148 typedef enum {
00149 STR_NONE,
00150 STR_UTCTIME,
00151 STR_PRINTABLE,
00152 STR_IA5,
00153 STR_BMP
00154 } STR_OPTION;
00155
00156
00157
00158 typedef struct {
00159 int id;
00160 int tag;
00161 long length;
00162 int indefinite;
00163 int headerSize;
00164 unsigned char header[ 8 ];
00165 } ASN1_ITEM;
00166
00167
00168
00169 static int printDots = FALSE;
00170 static int doPure = FALSE;
00171 static int doDumpHeader = FALSE;
00172 static int extraOIDinfo = FALSE;
00173 static int doHexValues = FALSE;
00174 static int useStdin = FALSE;
00175 static int zeroLengthAllowed = FALSE;
00176 static int dumpText = FALSE;
00177 static int printAllData = FALSE;
00178 static int checkEncaps = TRUE;
00179
00180
00181
00182 static int noErrors = 0;
00183 static int noWarnings = 0;
00184
00185
00186
00187 static int fPos = 0;
00188
00189
00190
00191 static FILE *output;
00192
00193
00194
00195 #define MAX_OID_SIZE 32
00196
00197 typedef struct tagOIDINFO {
00198 struct tagOIDINFO *next;
00199 char oid[ MAX_OID_SIZE ], *comment, *description;
00200 int oidLength;
00201 int warn;
00202 } OIDINFO;
00203
00204 static OIDINFO *oidList = NULL;
00205
00206
00207
00208
00209
00210
00211 #define CONFIG_NAME "dumpasn1.cfg"
00212
00213 static const char *configPaths[] = {
00214
00215 "/bin/", "/usr/bin/", "/usr/local/bin/",
00216
00217
00218
00219 "c:\\dos\\", "d:\\dos\\", "c:\\windows\\", "d:\\windows\\",
00220 "c:\\winnt\\", "d:\\winnt\\",
00221
00222
00223
00224 "$HOME/BIN/", "c:\\program files\\bin\\",
00225
00226
00227 "$HOME/", "$HOME/bin/",
00228
00229
00230 "$DUMPASN1_PATH/",
00231
00232 NULL
00233 };
00234
00235 #define isEnvTerminator( c ) \
00236 ( ( ( c ) == '/' ) || ( ( c ) == '.' ) || ( ( c ) == '$' ) || \
00237 ( ( c ) == '\0' ) || ( ( c ) == '~' ) )
00238
00239
00240
00241
00242
00243
00244
00245
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
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
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
00339
00340 #define P 1
00341 #define I 2
00342 #define PI 3
00343
00344 static int charFlags[] = {
00345
00346 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00347
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
00352 PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, I, I, PI, I, PI,
00353
00354 I, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,
00355
00356 PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, I, I, I, I, I,
00357
00358 I, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,
00359
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
00380
00381
00382
00383
00384
00385
00386 #define CPM_EOF 0x1A
00387
00388
00389
00390 #define MAX_LINESIZE 512
00391
00392
00393
00394 static int lineNo;
00395
00396 static int readLine( FILE *file, char *buffer )
00397 {
00398 int bufCount = 0, ch;
00399
00400
00401 while( ( ( ch = getc( file ) ) == ' ' || ch == '\t' ) && !feof( file ) );
00402
00403
00404 while( ch != '\r' && ch != '\n' && ch != CPM_EOF && !feof( file ) )
00405 {
00406
00407
00408
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
00417 if( ch == '#' && !bufCount )
00418 {
00419
00420 while( ch != '\r' && ch != '\n' && ch != CPM_EOF && !feof( file ) )
00421 ch = getc( file );
00422 break;
00423 }
00424
00425
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 )
00433 buffer[ bufCount++ ] = ch;
00434
00435
00436 ch = getc( file );
00437 }
00438
00439
00440 if( ch == '\r' )
00441 if( ( ch = getc( file ) ) != '\n' )
00442 ungetc( ch, file );
00443
00444
00445 while( bufCount > 0 &&
00446 ( ( ch = buffer[ bufCount - 1 ] ) == ' ' || ch == '\t' ) )
00447 bufCount--;
00448 buffer[ bufCount ] = '\0';
00449
00450
00451 if( ch == CPM_EOF )
00452 while( !feof( file ) )
00453
00454 ch = getc( file );
00455
00456 return( ferror( file ) ? FALSE : TRUE );
00457 }
00458
00459
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
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
00500 if( ( file = fopen( path, "rb" ) ) == NULL )
00501 {
00502
00503
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
00520 if( oidList == NULL )
00521 oidPtr = &dummyOID;
00522 else
00523 for( oidPtr = oidList; oidPtr->next != NULL; oidPtr = oidPtr->next );
00524
00525
00526 lineNo = 1;
00527 while( ( status = readLine( file, buffer ) ) == TRUE && !feof( file ) )
00528 {
00529
00530 if( !*buffer )
00531 {
00532 lineNo++;
00533 continue;
00534 }
00535
00536
00537 if( !strncmp( buffer, "OID = ", 6 ) )
00538 {
00539
00540
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
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
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
00611
00612 static int testConfigPath( const char *path )
00613 {
00614 FILE *file;
00615
00616
00617 if( ( file = fopen( path, "rb" ) ) == NULL )
00618 return( FALSE );
00619 fclose( file );
00620
00621 return( TRUE );
00622 }
00623
00624
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
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
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
00653 if( strPtr != NULL )
00654 {
00655 char envName[ MAX_LINESIZE ], *envString;
00656 int i;
00657
00658
00659
00660 pathPos++;
00661 for( i = 0; !isEnvTerminator( pathBuffer[ pathPos + i ] ); i++ );
00662 memcpy( envName, pathBuffer + pathPos, i );
00663 envName[ i ] = '\0';
00664
00665
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';
00680
00681
00682 strcpy( path, newPath );
00683 }
00684
00685
00686
00687 static int readGlobalConfig( const char *path )
00688 {
00689 char buffer[ FILENAME_MAX ], *namePos;
00690 int i;
00691
00692
00693
00694
00695
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
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
00716
00717
00718 return( readConfig( CONFIG_NAME, TRUE ) );
00719 }
00720
00721
00722
00723
00724
00725
00726
00727
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
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
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;
00760 if( level > maxLevel )
00761 level = maxLevel;
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
00772
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
00787
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
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
00840
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
00854
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
00873 errorStr = "Spurious zero bits in bitstring";
00874 bitFlag <<= 1;
00875 bitString <<= 1;
00876 }
00877 if( ( remainderMask << noBits ) & value )
00878
00879 errorStr = "Spurious one bits in bitstring";
00880
00881
00882
00883
00884
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
00912
00913
00914
00915
00916
00917
00918
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, " '" );
00933 if( level > maxLevel )
00934 level = maxLevel;
00935 for( i = 0; i < noBytes; i++ )
00936 {
00937 int ch;
00938
00939
00940
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
00958 warnBMP = TRUE;
00959 else
00960 {
00961 wchar_t wCh = ( ch << 8 ) | getc( inFile );
00962 unsigned char outBuf[ 8 ];
00963 int outLen;
00964
00965
00966
00967
00968
00969
00970 outLen = wcstombs( outBuf, &wCh, 1 );
00971 if( outLen < 1 )
00972 {
00973
00974
00975 ungetc( wCh & 0xFF, inFile );
00976 }
00977 else
00978 {
00979 lineLength++;
00980 i++;
00981 wprintf( L"%c", wCh );
00982 fPos += 2;
00983 continue;
00984 }
00985 }
00986 }
00987 #endif
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 = '.';
00996 }
00997 else
00998 if( strOption == STR_UTCTIME )
00999 {
01000 if( !isdigit( ch ) && ch != 'Z' )
01001 {
01002 warnUTC = TRUE;
01003 ch = '.';
01004 }
01005 }
01006 else
01007 if( ( ch & 0x7F ) < ' ' || ch == 0xFF )
01008 ch = '.';
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
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
01049
01050
01051
01052
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
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
01085
01086
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;
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
01113
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
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
01143 if( !checkEncaps )
01144 return( FALSE );
01145
01146 #if 1
01147
01148 getItem( inFile, &nestedItem );
01149 diffPos = fPos - currentPos;
01150 fPos = currentPos;
01151 fseek( inFile, -diffPos, SEEK_CUR );
01152
01153
01154
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
01162
01163 int ch;
01164
01165
01166 ch = getc( inFile );
01167 ungetc( ch, inFile );
01168 if( ch == INTEGER || ch == ( SEQUENCE | CONSTRUCTED ) )
01169 return( TRUE );
01170
01171
01172
01173 if( tag == OCTETSTRING && ch == BITSTRING )
01174 return( TRUE );
01175
01176
01177
01178
01179
01180
01181 if( level > 1 && tag == OCTETSTRING )
01182 {
01183 int length;
01184
01185 if( ch == IA5STRING )
01186
01187 return( TRUE );
01188
01189
01190
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
01198 return( TRUE );
01199 }
01200 #endif
01201
01202 return( FALSE );
01203 }
01204
01205
01206
01207 int zeroLengthOK( const ASN1_ITEM *item )
01208 {
01209
01210 if( ( item->id & CLASS_MASK ) != UNIVERSAL )
01211 return( FALSE );
01212
01213
01214 if( item->tag == EOC || item->tag == NULLTAG )
01215 return( TRUE );
01216
01217
01218 if( item->tag == REAL )
01219 return( TRUE );
01220
01221
01222
01223
01224 if( !zeroLengthAllowed )
01225 return( FALSE );
01226
01227
01228
01229
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
01240
01241 if( item->tag == SEQUENCE || item->tag == SET )
01242 return( TRUE );
01243
01244 return( FALSE );
01245 }
01246
01247
01248
01249 static int looksLikeText( FILE *inFile, const int length )
01250 {
01251 char buffer[ 16 ];
01252 int sampleLength = min( length, 16 ), i;
01253
01254
01255 if( sampleLength < 4 )
01256 return( FALSE );
01257
01258
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
01265 continue;
01266 if( buffer[ i ] < 0x20 || buffer[ i ] > 0x7E )
01267 return( FALSE );
01268 }
01269
01270
01271 return( TRUE);
01272 }
01273
01274
01275
01276
01277 static void dumpHeader( FILE *inFile, const ASN1_ITEM *item )
01278 {
01279 int extraLen = 24 - item->headerSize, i;
01280
01281
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
01289
01290
01291 if( extraLen > 0 && doDumpHeader > 1 )
01292 {
01293
01294
01295
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;
01305 else
01306 fprintf( output, " %02X", ch );
01307 }
01308 fseek( inFile, -extraLen, SEEK_CUR );
01309 }
01310
01311 fputs( ">\n", output );
01312 }
01313
01314
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
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
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
01359 fprintf( output, "[%s%d]",
01360 classtext[ ( item->id & CLASS_MASK ) >> 6 ], item->tag );
01361
01362
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
01384 if( ( item->id & FORM_MASK ) == CONSTRUCTED )
01385 {
01386 printConstructed( inFile, level, item );
01387 return;
01388 }
01389
01390
01391
01392 if( !useStdin && looksLikeText( inFile, item->length ) )
01393 {
01394
01395 displayString( inFile, item->length, level, STR_NONE );
01396 return;
01397 }
01398
01399
01400 dumpHex( inFile, item->length, level, FALSE );
01401
01402 return;
01403 }
01404
01405
01406 fprintf( output, "%s", idstr( item->tag ) );
01407
01408
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
01423 if( ( item->id & FORM_MASK ) == CONSTRUCTED )
01424 {
01425 printConstructed( inFile, level, item );
01426 return;
01427 }
01428
01429
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
01471
01472 dumpBitString( inFile, ( int ) item->length, x, level );
01473 break;
01474 }
01475 case OCTETSTRING:
01476 if( checkEncapsulate( inFile, item->tag, item->length ) )
01477 {
01478
01479
01480 fprintf( output, ", encapsulates" );
01481 printConstructed( inFile, level + 1, item );
01482 break;
01483 }
01484 if( !useStdin && !dumpText && \
01485 looksLikeText( inFile, item->length ) )
01486 {
01487
01488
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
01497
01498
01499
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
01513
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
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
01535
01536 if( oidInfo->warn )
01537 noWarnings++;
01538
01539 break;
01540 }
01541
01542
01543 x = ( unsigned char ) buffer[ 0 ] / 40;
01544 y = ( unsigned char ) buffer[ 0 ] % 40;
01545 if( x > 2 )
01546 {
01547
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++;
01603 }
01604 }
01605
01606
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
01616 if( !length && !isIndefinite )
01617 return( 0 );
01618
01619 while( ( status = getItem( inFile, &item ) ) > 0 )
01620 {
01621
01622
01623 if( length == LENGTH_MAGIC && !item.indefinite )
01624 length = item.headerSize + item.length;
01625
01626
01627 if( doDumpHeader )
01628 dumpHeader( inFile, &item );
01629
01630
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
01644 if( !seenEOC )
01645 {
01646 doIndent( level );
01647 printASN1object( inFile, &item, level );
01648 }
01649
01650
01651
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
01675
01676
01677
01678
01679
01680
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
01698
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
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
01748 argv++; argc--;
01749
01750
01751 if( argc < 1 )
01752 usageExit();
01753 output = stdout;
01754
01755
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;
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++;
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++;
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
01820
01821
01822
01823
01824
01825 freopen( "nul", "w", stdout );
01826 #else
01827
01828
01829 fclose( stdout );
01830 #endif
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
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
01863
01864
01865
01866
01867 if( argc != 1 && !useStdin )
01868 usageExit();
01869 if( !readGlobalConfig( pathPtr ) )
01870 exit( EXIT_FAILURE );
01871
01872
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
01895
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
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
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 }