michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * ssltap.c michael@0: * michael@0: * Version 1.0 : Frederick Roeber : 11 June 1997 michael@0: * Version 2.0 : Steve Parkinson : 13 November 1997 michael@0: * Version 3.0 : Nelson Bolyard : 22 July 1998 michael@0: * Version 3.1 : Nelson Bolyard : 24 May 1999 michael@0: * michael@0: * changes in version 2.0: michael@0: * Uses NSPR20 michael@0: * Shows structure of SSL negotiation, if enabled. michael@0: * michael@0: * This "proxies" a socket connection (like a socks tunnel), but displays the michael@0: * data is it flies by. michael@0: * michael@0: * In the code, the 'client' socket is the one on the client side of the michael@0: * proxy, and the server socket is on the server side. michael@0: * michael@0: */ michael@0: michael@0: #include "nspr.h" michael@0: #include "plstr.h" michael@0: #include "secutil.h" michael@0: #include /* for memcpy, etc. */ michael@0: #include michael@0: #include michael@0: michael@0: #include "plgetopt.h" michael@0: #include "nss.h" michael@0: #include "cert.h" michael@0: #include "sslproto.h" michael@0: #include "ocsp.h" michael@0: #include "ocspti.h" /* internals for pretty-printing routines *only* */ michael@0: michael@0: struct _DataBufferList; michael@0: struct _DataBuffer; michael@0: michael@0: typedef struct _DataBufferList { michael@0: struct _DataBuffer *first,*last; michael@0: int size; michael@0: int isEncrypted; michael@0: unsigned char * msgBuf; michael@0: int msgBufOffset; michael@0: int msgBufSize; michael@0: int hMACsize; michael@0: } DataBufferList; michael@0: michael@0: typedef struct _DataBuffer { michael@0: unsigned char *buffer; michael@0: int length; michael@0: int offset; /* offset of first good byte */ michael@0: struct _DataBuffer *next; michael@0: } DataBuffer; michael@0: michael@0: michael@0: michael@0: struct sslhandshake { michael@0: PRUint8 type; michael@0: PRUint32 length; michael@0: }; michael@0: michael@0: typedef struct _SSLRecord { michael@0: PRUint8 type; michael@0: PRUint8 ver_maj,ver_min; michael@0: michael@0: PRUint8 length[2]; michael@0: } SSLRecord; michael@0: michael@0: typedef struct _ClientHelloV2 { michael@0: PRUint8 length[2]; michael@0: PRUint8 type; michael@0: PRUint8 version[2]; michael@0: PRUint8 cslength[2]; michael@0: PRUint8 sidlength[2]; michael@0: PRUint8 rndlength[2]; michael@0: PRUint8 csuites[1]; michael@0: } ClientHelloV2; michael@0: michael@0: typedef struct _ServerHelloV2 { michael@0: PRUint8 length[2]; michael@0: PRUint8 type; michael@0: PRUint8 sidhit; michael@0: PRUint8 certtype; michael@0: PRUint8 version[2]; michael@0: PRUint8 certlength[2]; michael@0: PRUint8 cslength[2]; michael@0: PRUint8 cidlength[2]; michael@0: } ServerHelloV2; michael@0: michael@0: typedef struct _ClientMasterKeyV2 { michael@0: PRUint8 length[2]; michael@0: PRUint8 type; michael@0: michael@0: PRUint8 cipherkind[3]; michael@0: PRUint8 clearkey[2]; michael@0: PRUint8 secretkey[2]; michael@0: michael@0: } ClientMasterKeyV2; michael@0: michael@0: /* forward declaration */ michael@0: void showErr(const char * msg); michael@0: michael@0: #define TAPBUFSIZ 16384 michael@0: michael@0: #define DEFPORT 1924 michael@0: #include michael@0: michael@0: const char * progName; michael@0: int hexparse=0; michael@0: int sslparse=0; michael@0: int sslhexparse=0; michael@0: int looparound=0; michael@0: int fancy=0; michael@0: int isV2Session=0; michael@0: int currentcipher=0; michael@0: DataBufferList clientstream, serverstream; michael@0: michael@0: #define PR_FPUTS(x) PR_fprintf(PR_STDOUT, x ) michael@0: michael@0: #define GET_SHORT(x) ((PRUint16)(((PRUint16)((PRUint8*)x)[0]) << 8) + ((PRUint16)((PRUint8*)x)[1])) michael@0: #define GET_24(x) ((PRUint32) ( \ michael@0: (((PRUint32)((PRUint8*)x)[0]) << 16) \ michael@0: + \ michael@0: (((PRUint32)((PRUint8*)x)[1]) << 8) \ michael@0: + \ michael@0: (((PRUint32)((PRUint8*)x)[2]) << 0) \ michael@0: ) ) michael@0: #define GET_32(x) ((PRUint32) ( \ michael@0: (((PRUint32)((PRUint8*)x)[0]) << 24) \ michael@0: + \ michael@0: (((PRUint32)((PRUint8*)x)[1]) << 16) \ michael@0: + \ michael@0: (((PRUint32)((PRUint8*)x)[2]) << 8) \ michael@0: + \ michael@0: (((PRUint32)((PRUint8*)x)[3]) << 0) \ michael@0: ) ) michael@0: michael@0: void print_hex(int amt, unsigned char *buf); michael@0: void read_stream_bytes(unsigned char *d, DataBufferList *db, int length); michael@0: michael@0: void myhalt(int dblsize,int collectedsize) michael@0: { michael@0: michael@0: PR_fprintf(PR_STDERR,"HALTED\n"); michael@0: PR_ASSERT(dblsize == collectedsize); michael@0: exit(13); michael@0: } michael@0: michael@0: const char *get_error_text(int error) michael@0: { michael@0: switch (error) { michael@0: case PR_IO_TIMEOUT_ERROR: michael@0: return "Timeout"; michael@0: break; michael@0: case PR_CONNECT_REFUSED_ERROR: michael@0: return "Connection refused"; michael@0: break; michael@0: case PR_NETWORK_UNREACHABLE_ERROR: michael@0: return "Network unreachable"; michael@0: break; michael@0: case PR_BAD_ADDRESS_ERROR: michael@0: return "Bad address"; michael@0: break; michael@0: case PR_CONNECT_RESET_ERROR: michael@0: return "Connection reset"; michael@0: break; michael@0: case PR_PIPE_ERROR: michael@0: return "Pipe error"; michael@0: break; michael@0: } michael@0: michael@0: return ""; michael@0: } michael@0: michael@0: michael@0: michael@0: michael@0: michael@0: void check_integrity(DataBufferList *dbl) michael@0: { michael@0: DataBuffer *db; michael@0: int i; michael@0: michael@0: db = dbl->first; michael@0: i =0; michael@0: while (db) { michael@0: i+= db->length - db->offset; michael@0: db = db->next; michael@0: } michael@0: if (i != dbl->size) { michael@0: myhalt(dbl->size,i); michael@0: } michael@0: } michael@0: michael@0: /* Free's the DataBuffer at the head of the list and returns the pointer michael@0: * to the new head of the list. michael@0: */ michael@0: DataBuffer * michael@0: free_head(DataBufferList *dbl) michael@0: { michael@0: DataBuffer *db = dbl->first; michael@0: PR_ASSERT(db->offset >= db->length); michael@0: if (db->offset >= db->length) { michael@0: dbl->first = db->next; michael@0: if (dbl->first == NULL) { michael@0: dbl->last = NULL; michael@0: } michael@0: PORT_Free(db->buffer); michael@0: PORT_Free(db); michael@0: db = dbl->first; michael@0: } michael@0: return db; michael@0: } michael@0: michael@0: void michael@0: read_stream_bytes(unsigned char *d, DataBufferList *dbl, int length) michael@0: { michael@0: int copied = 0; michael@0: DataBuffer *db = dbl->first; michael@0: michael@0: if (!db) { michael@0: PR_fprintf(PR_STDERR,"assert failed - dbl->first is null\n"); michael@0: exit(8); michael@0: } michael@0: while (length) { michael@0: int toCopy; michael@0: /* find the number of bytes to copy from the head buffer */ michael@0: /* if there's too many in this buffer, then only copy 'length' */ michael@0: toCopy = PR_MIN(db->length - db->offset, length); michael@0: michael@0: memcpy(d + copied, db->buffer + db->offset, toCopy); michael@0: copied += toCopy; michael@0: db->offset += toCopy; michael@0: length -= toCopy; michael@0: dbl->size -= toCopy; michael@0: michael@0: /* if we emptied the head buffer */ michael@0: if (db->offset >= db->length) { michael@0: db = free_head(dbl); michael@0: } michael@0: } michael@0: michael@0: check_integrity(dbl); michael@0: michael@0: } michael@0: michael@0: void michael@0: flush_stream(DataBufferList *dbl) michael@0: { michael@0: DataBuffer *db = dbl->first; michael@0: check_integrity(dbl); michael@0: while (db) { michael@0: db->offset = db->length; michael@0: db = free_head(dbl); michael@0: } michael@0: dbl->size = 0; michael@0: check_integrity(dbl); michael@0: if (dbl->msgBuf) { michael@0: PORT_Free(dbl->msgBuf); michael@0: dbl->msgBuf = NULL; michael@0: } michael@0: dbl->msgBufOffset = 0; michael@0: dbl->msgBufSize = 0; michael@0: dbl->hMACsize = 0; michael@0: } michael@0: michael@0: michael@0: const char * V2CipherString(int cs_int) michael@0: { michael@0: char *cs_str; michael@0: cs_str = NULL; michael@0: switch (cs_int) { michael@0: michael@0: case 0x010080: cs_str = "SSL2/RSA/RC4-128/MD5"; break; michael@0: case 0x020080: cs_str = "SSL2/RSA/RC4-40/MD5"; break; michael@0: case 0x030080: cs_str = "SSL2/RSA/RC2CBC128/MD5"; break; michael@0: case 0x040080: cs_str = "SSL2/RSA/RC2CBC40/MD5"; break; michael@0: case 0x050080: cs_str = "SSL2/RSA/IDEA128CBC/MD5"; break; michael@0: case 0x060040: cs_str = "SSL2/RSA/DES56-CBC/MD5"; break; michael@0: case 0x0700C0: cs_str = "SSL2/RSA/3DES192EDE-CBC/MD5"; break; michael@0: michael@0: case 0x000001: cs_str = "SSL3/RSA/NULL/MD5"; break; michael@0: case 0x000002: cs_str = "SSL3/RSA/NULL/SHA"; break; michael@0: case 0x000003: cs_str = "SSL3/RSA/RC4-40/MD5"; break; michael@0: case 0x000004: cs_str = "SSL3/RSA/RC4-128/MD5"; break; michael@0: case 0x000005: cs_str = "SSL3/RSA/RC4-128/SHA"; break; michael@0: case 0x000006: cs_str = "SSL3/RSA/RC2CBC40/MD5"; break; michael@0: case 0x000007: cs_str = "SSL3/RSA/IDEA128CBC/SHA"; break; michael@0: case 0x000008: cs_str = "SSL3/RSA/DES40-CBC/SHA"; break; michael@0: case 0x000009: cs_str = "SSL3/RSA/DES56-CBC/SHA"; break; michael@0: case 0x00000A: cs_str = "SSL3/RSA/3DES192EDE-CBC/SHA"; break; michael@0: michael@0: case 0x00000B: cs_str = "SSL3/DH-DSS/DES40-CBC/SHA"; break; michael@0: case 0x00000C: cs_str = "SSL3/DH-DSS/DES56-CBC/SHA"; break; michael@0: case 0x00000D: cs_str = "SSL3/DH-DSS/DES192EDE3CBC/SHA"; break; michael@0: case 0x00000E: cs_str = "SSL3/DH-RSA/DES40-CBC/SHA"; break; michael@0: case 0x00000F: cs_str = "SSL3/DH-RSA/DES56-CBC/SHA"; break; michael@0: case 0x000010: cs_str = "SSL3/DH-RSA/3DES192EDE-CBC/SHA"; break; michael@0: michael@0: case 0x000011: cs_str = "SSL3/DHE-DSS/DES40-CBC/SHA"; break; michael@0: case 0x000012: cs_str = "SSL3/DHE-DSS/DES56-CBC/SHA"; break; michael@0: case 0x000013: cs_str = "SSL3/DHE-DSS/DES192EDE3CBC/SHA"; break; michael@0: case 0x000014: cs_str = "SSL3/DHE-RSA/DES40-CBC/SHA"; break; michael@0: case 0x000015: cs_str = "SSL3/DHE-RSA/DES56-CBC/SHA"; break; michael@0: case 0x000016: cs_str = "SSL3/DHE-RSA/3DES192EDE-CBC/SHA"; break; michael@0: michael@0: case 0x000017: cs_str = "SSL3/DH-anon/RC4-40/MD5"; break; michael@0: case 0x000018: cs_str = "SSL3/DH-anon/RC4-128/MD5"; break; michael@0: case 0x000019: cs_str = "SSL3/DH-anon/DES40-CBC/SHA"; break; michael@0: case 0x00001A: cs_str = "SSL3/DH-anon/DES56-CBC/SHA"; break; michael@0: case 0x00001B: cs_str = "SSL3/DH-anon/3DES192EDE-CBC/SHA"; break; michael@0: michael@0: case 0x00001C: cs_str = "SSL3/FORTEZZA-DMS/NULL/SHA"; break; michael@0: case 0x00001D: cs_str = "SSL3/FORTEZZA-DMS/FORTEZZA-CBC/SHA"; break; michael@0: case 0x00001E: cs_str = "SSL3/FORTEZZA-DMS/RC4-128/SHA"; break; michael@0: michael@0: case 0x00002F: cs_str = "TLS/RSA/AES128-CBC/SHA"; break; michael@0: case 0x000030: cs_str = "TLS/DH-DSS/AES128-CBC/SHA"; break; michael@0: case 0x000031: cs_str = "TLS/DH-RSA/AES128-CBC/SHA"; break; michael@0: case 0x000032: cs_str = "TLS/DHE-DSS/AES128-CBC/SHA"; break; michael@0: case 0x000033: cs_str = "TLS/DHE-RSA/AES128-CBC/SHA"; break; michael@0: case 0x000034: cs_str = "TLS/DH-ANON/AES128-CBC/SHA"; break; michael@0: michael@0: case 0x000035: cs_str = "TLS/RSA/AES256-CBC/SHA"; break; michael@0: case 0x000036: cs_str = "TLS/DH-DSS/AES256-CBC/SHA"; break; michael@0: case 0x000037: cs_str = "TLS/DH-RSA/AES256-CBC/SHA"; break; michael@0: case 0x000038: cs_str = "TLS/DHE-DSS/AES256-CBC/SHA"; break; michael@0: case 0x000039: cs_str = "TLS/DHE-RSA/AES256-CBC/SHA"; break; michael@0: case 0x00003A: cs_str = "TLS/DH-ANON/AES256-CBC/SHA"; break; michael@0: michael@0: case 0x00003B: cs_str = "TLS/RSA/NULL/SHA256"; break; michael@0: case 0x00003C: cs_str = "TLS/RSA/AES128-CBC/SHA256"; break; michael@0: case 0x00003D: cs_str = "TLS/RSA/AES256-CBC/SHA256"; break; michael@0: case 0x00003E: cs_str = "TLS/DH-DSS/AES128-CBC/SHA256"; break; michael@0: case 0x00003F: cs_str = "TLS/DH-RSA/AES128-CBC/SHA256"; break; michael@0: case 0x000040: cs_str = "TLS/DHE-DSS/AES128-CBC/SHA256"; break; michael@0: michael@0: case 0x000041: cs_str = "TLS/RSA/CAMELLIA128-CBC/SHA"; break; michael@0: case 0x000042: cs_str = "TLS/DH-DSS/CAMELLIA128-CBC/SHA"; break; michael@0: case 0x000043: cs_str = "TLS/DH-RSA/CAMELLIA128-CBC/SHA"; break; michael@0: case 0x000044: cs_str = "TLS/DHE-DSS/CAMELLIA128-CBC/SHA"; break; michael@0: case 0x000045: cs_str = "TLS/DHE-RSA/CAMELLIA128-CBC/SHA"; break; michael@0: case 0x000046: cs_str = "TLS/DH-ANON/CAMELLIA128-CBC/SHA"; break; michael@0: michael@0: case 0x000060: cs_str = "TLS/RSA-EXPORT1024/RC4-56/MD5"; break; michael@0: case 0x000061: cs_str = "TLS/RSA-EXPORT1024/RC2CBC56/MD5"; break; michael@0: case 0x000062: cs_str = "TLS/RSA-EXPORT1024/DES56-CBC/SHA"; break; michael@0: case 0x000064: cs_str = "TLS/RSA-EXPORT1024/RC4-56/SHA"; break; michael@0: case 0x000063: cs_str = "TLS/DHE-DSS_EXPORT1024/DES56-CBC/SHA"; break; michael@0: case 0x000065: cs_str = "TLS/DHE-DSS_EXPORT1024/RC4-56/SHA"; break; michael@0: case 0x000066: cs_str = "TLS/DHE-DSS/RC4-128/SHA"; break; michael@0: michael@0: case 0x000067: cs_str = "TLS/DHE-RSA/AES128-CBC/SHA256"; break; michael@0: case 0x000068: cs_str = "TLS/DH-DSS/AES256-CBC/SHA256"; break; michael@0: case 0x000069: cs_str = "TLS/DH-RSA/AES256-CBC/SHA256"; break; michael@0: case 0x00006A: cs_str = "TLS/DHE-DSS/AES256-CBC/SHA256"; break; michael@0: case 0x00006B: cs_str = "TLS/DHE-RSA/AES256-CBC/SHA256"; break; michael@0: michael@0: case 0x000072: cs_str = "TLS/DHE-DSS/3DESEDE-CBC/RMD160"; break; michael@0: case 0x000073: cs_str = "TLS/DHE-DSS/AES128-CBC/RMD160"; break; michael@0: case 0x000074: cs_str = "TLS/DHE-DSS/AES256-CBC/RMD160"; break; michael@0: michael@0: case 0x000079: cs_str = "TLS/DHE-RSA/AES256-CBC/RMD160"; break; michael@0: michael@0: case 0x00007C: cs_str = "TLS/RSA/3DESEDE-CBC/RMD160"; break; michael@0: case 0x00007D: cs_str = "TLS/RSA/AES128-CBC/RMD160"; break; michael@0: case 0x00007E: cs_str = "TLS/RSA/AES256-CBC/RMD160"; break; michael@0: michael@0: case 0x000080: cs_str = "TLS/GOST341094/GOST28147-OFB/GOST28147"; break; michael@0: case 0x000081: cs_str = "TLS/GOST34102001/GOST28147-OFB/GOST28147"; break; michael@0: case 0x000082: cs_str = "TLS/GOST341094/NULL/GOSTR3411"; break; michael@0: case 0x000083: cs_str = "TLS/GOST34102001/NULL/GOSTR3411"; break; michael@0: michael@0: case 0x000084: cs_str = "TLS/RSA/CAMELLIA256-CBC/SHA"; break; michael@0: case 0x000085: cs_str = "TLS/DH-DSS/CAMELLIA256-CBC/SHA"; break; michael@0: case 0x000086: cs_str = "TLS/DH-RSA/CAMELLIA256-CBC/SHA"; break; michael@0: case 0x000087: cs_str = "TLS/DHE-DSS/CAMELLIA256-CBC/SHA"; break; michael@0: case 0x000088: cs_str = "TLS/DHE-RSA/CAMELLIA256-CBC/SHA"; break; michael@0: case 0x000089: cs_str = "TLS/DH-ANON/CAMELLIA256-CBC/SHA"; break; michael@0: case 0x00008A: cs_str = "TLS/PSK/RC4-128/SHA"; break; michael@0: case 0x00008B: cs_str = "TLS/PSK/3DES-EDE-CBC/SHA"; break; michael@0: case 0x00008C: cs_str = "TLS/PSK/AES128-CBC/SHA"; break; michael@0: case 0x00008D: cs_str = "TLS/PSK/AES256-CBC/SHA"; break; michael@0: case 0x00008E: cs_str = "TLS/DHE-PSK/RC4-128/SHA"; break; michael@0: case 0x00008F: cs_str = "TLS/DHE-PSK/3DES-EDE-CBC/SHA"; break; michael@0: case 0x000090: cs_str = "TLS/DHE-PSK/AES128-CBC/SHA"; break; michael@0: case 0x000091: cs_str = "TLS/DHE-PSK/AES256-CBC/SHA"; break; michael@0: case 0x000092: cs_str = "TLS/RSA-PSK/RC4-128/SHA"; break; michael@0: case 0x000093: cs_str = "TLS/RSA-PSK/3DES-EDE-CBC/SHA"; break; michael@0: case 0x000094: cs_str = "TLS/RSA-PSK/AES128-CBC/SHA"; break; michael@0: case 0x000095: cs_str = "TLS/RSA-PSK/AES256-CBC/SHA"; break; michael@0: case 0x000096: cs_str = "TLS/RSA/SEED-CBC/SHA"; break; michael@0: case 0x000097: cs_str = "TLS/DH-DSS/SEED-CBC/SHA"; break; michael@0: case 0x000098: cs_str = "TLS/DH-RSA/SEED-CBC/SHA"; break; michael@0: case 0x000099: cs_str = "TLS/DHE-DSS/SEED-CBC/SHA"; break; michael@0: case 0x00009A: cs_str = "TLS/DHE-RSA/SEED-CBC/SHA"; break; michael@0: case 0x00009B: cs_str = "TLS/DH-ANON/SEED-CBC/SHA"; break; michael@0: case 0x00009C: cs_str = "TLS/RSA/AES128-GCM/SHA256"; break; michael@0: case 0x00009E: cs_str = "TLS/DHE-RSA/AES128-GCM/SHA256"; break; michael@0: michael@0: case 0x0000FF: cs_str = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"; break; michael@0: michael@0: case 0x00C001: cs_str = "TLS/ECDH-ECDSA/NULL/SHA"; break; michael@0: case 0x00C002: cs_str = "TLS/ECDH-ECDSA/RC4-128/SHA"; break; michael@0: case 0x00C003: cs_str = "TLS/ECDH-ECDSA/3DES-EDE-CBC/SHA"; break; michael@0: case 0x00C004: cs_str = "TLS/ECDH-ECDSA/AES128-CBC/SHA"; break; michael@0: case 0x00C005: cs_str = "TLS/ECDH-ECDSA/AES256-CBC/SHA"; break; michael@0: case 0x00C006: cs_str = "TLS/ECDHE-ECDSA/NULL/SHA"; break; michael@0: case 0x00C007: cs_str = "TLS/ECDHE-ECDSA/RC4-128/SHA"; break; michael@0: case 0x00C008: cs_str = "TLS/ECDHE-ECDSA/3DES-EDE-CBC/SHA";break; michael@0: case 0x00C009: cs_str = "TLS/ECDHE-ECDSA/AES128-CBC/SHA"; break; michael@0: case 0x00C00A: cs_str = "TLS/ECDHE-ECDSA/AES256-CBC/SHA"; break; michael@0: case 0x00C00B: cs_str = "TLS/ECDH-RSA/NULL/SHA"; break; michael@0: case 0x00C00C: cs_str = "TLS/ECDH-RSA/RC4-128/SHA"; break; michael@0: case 0x00C00D: cs_str = "TLS/ECDH-RSA/3DES-EDE-CBC/SHA"; break; michael@0: case 0x00C00E: cs_str = "TLS/ECDH-RSA/AES128-CBC/SHA"; break; michael@0: case 0x00C00F: cs_str = "TLS/ECDH-RSA/AES256-CBC/SHA"; break; michael@0: case 0x00C010: cs_str = "TLS/ECDHE-RSA/NULL/SHA"; break; michael@0: case 0x00C011: cs_str = "TLS/ECDHE-RSA/RC4-128/SHA"; break; michael@0: case 0x00C012: cs_str = "TLS/ECDHE-RSA/3DES-EDE-CBC/SHA"; break; michael@0: case 0x00C013: cs_str = "TLS/ECDHE-RSA/AES128-CBC/SHA"; break; michael@0: case 0x00C014: cs_str = "TLS/ECDHE-RSA/AES256-CBC/SHA"; break; michael@0: case 0x00C015: cs_str = "TLS/ECDH-anon/NULL/SHA"; break; michael@0: case 0x00C016: cs_str = "TLS/ECDH-anon/RC4-128/SHA"; break; michael@0: case 0x00C017: cs_str = "TLS/ECDH-anon/3DES-EDE-CBC/SHA"; break; michael@0: case 0x00C018: cs_str = "TLS/ECDH-anon/AES128-CBC/SHA"; break; michael@0: case 0x00C019: cs_str = "TLS/ECDH-anon/AES256-CBC/SHA"; break; michael@0: michael@0: case 0x00C023: cs_str = "TLS/ECDHE-ECDSA/AES128-CBC/SHA256"; break; michael@0: case 0x00C024: cs_str = "TLS/ECDHE-ECDSA/AES256-CBC/SHA384"; break; michael@0: case 0x00C025: cs_str = "TLS/ECDH-ECDSA/AES128-CBC/SHA256"; break; michael@0: case 0x00C026: cs_str = "TLS/ECDH-ECDSA/AES256-CBC/SHA384"; break; michael@0: case 0x00C027: cs_str = "TLS/ECDHE-RSA/AES128-CBC/SHA256"; break; michael@0: case 0x00C028: cs_str = "TLS/ECDHE-RSA/AES256-CBC/SHA384"; break; michael@0: case 0x00C029: cs_str = "TLS/ECDH-RSA/AES128-CBC/SHA256"; break; michael@0: case 0x00C02A: cs_str = "TLS/ECDH-RSA/AES256-CBC/SHA384"; break; michael@0: case 0x00C02B: cs_str = "TLS/ECDHE-ECDSA/AES128-GCM/SHA256"; break; michael@0: case 0x00C02C: cs_str = "TLS/ECDHE-ECDSA/AES256-GCM/SHA384"; break; michael@0: case 0x00C02F: cs_str = "TLS/ECDHE-RSA/AES128-GCM/SHA256"; break; michael@0: michael@0: case 0x00FEFF: cs_str = "SSL3/RSA-FIPS/3DESEDE-CBC/SHA"; break; michael@0: case 0x00FEFE: cs_str = "SSL3/RSA-FIPS/DES-CBC/SHA"; break; michael@0: case 0x00FFE1: cs_str = "SSL3/RSA-FIPS/DES56-CBC/SHA"; break; michael@0: case 0x00FFE0: cs_str = "SSL3/RSA-FIPS/3DES192EDE-CBC/SHA";break; michael@0: michael@0: /* the string literal is broken up to avoid trigraphs */ michael@0: default: cs_str = "????" "/????????" "/?????????" "/???"; break; michael@0: } michael@0: michael@0: return cs_str; michael@0: } michael@0: michael@0: const char * CompressionMethodString(int cm_int) michael@0: { michael@0: char *cm_str; michael@0: cm_str = NULL; michael@0: switch (cm_int) { michael@0: case 0: cm_str = "NULL"; break; michael@0: case 1: cm_str = "DEFLATE"; break; /* RFC 3749 */ michael@0: case 64: cm_str = "LZS"; break; /* RFC 3943 */ michael@0: default: cm_str = "???"; break; michael@0: } michael@0: michael@0: return cm_str; michael@0: } michael@0: michael@0: const char * helloExtensionNameString(int ex_num) michael@0: { michael@0: const char *ex_name = NULL; michael@0: static char buf[10]; michael@0: michael@0: switch (ex_num) { michael@0: case 0: ex_name = "server_name"; break; michael@0: case 1: ex_name = "max_fragment_length"; break; michael@0: case 2: ex_name = "client_certificate_url"; break; michael@0: case 3: ex_name = "trusted_ca_keys"; break; michael@0: case 4: ex_name = "truncated_hmac"; break; michael@0: case 5: ex_name = "status_request"; break; michael@0: case 10: ex_name = "elliptic_curves"; break; michael@0: case 11: ex_name = "ec_point_formats"; break; michael@0: case 13: ex_name = "signature_algorithms"; break; michael@0: case 35: ex_name = "session_ticket"; break; michael@0: case 0xff01: ex_name = "renegotiation_info"; break; michael@0: default: sprintf(buf, "%d", ex_num); ex_name = (const char *)buf; break; michael@0: } michael@0: michael@0: return ex_name; michael@0: } michael@0: michael@0: static int isNULLmac(int cs_int) michael@0: { michael@0: return (cs_int == TLS_NULL_WITH_NULL_NULL); michael@0: } michael@0: michael@0: static int isNULLcipher(int cs_int) michael@0: { michael@0: return ((cs_int == TLS_RSA_WITH_NULL_MD5) || michael@0: (cs_int == TLS_RSA_WITH_NULL_SHA) || michael@0: (cs_int == SSL_FORTEZZA_DMS_WITH_NULL_SHA) || michael@0: (cs_int == TLS_ECDH_ECDSA_WITH_NULL_SHA) || michael@0: (cs_int == TLS_ECDHE_ECDSA_WITH_NULL_SHA) || michael@0: (cs_int == TLS_ECDH_RSA_WITH_NULL_SHA) || michael@0: (cs_int == TLS_ECDHE_RSA_WITH_NULL_SHA)); michael@0: } michael@0: michael@0: void partial_packet(int thispacket, int size, int needed) michael@0: { michael@0: PR_fprintf(PR_STDOUT,"(%u bytes", thispacket); michael@0: if (thispacket < needed) { michael@0: PR_fprintf(PR_STDOUT,", making %u", size); michael@0: } michael@0: PR_fprintf(PR_STDOUT," of %u", needed); michael@0: if (size > needed) { michael@0: PR_fprintf(PR_STDOUT,", with %u left over", size - needed); michael@0: } michael@0: PR_fprintf(PR_STDOUT,")\n"); michael@0: } michael@0: michael@0: char * get_time_string(void) michael@0: { michael@0: char *cp; michael@0: char *eol; michael@0: time_t tt; michael@0: michael@0: time(&tt); michael@0: cp = ctime(&tt); michael@0: eol = strchr(cp, '\n'); michael@0: if (eol) michael@0: *eol = 0; michael@0: return cp; michael@0: } michael@0: michael@0: void print_sslv2(DataBufferList *s, unsigned char *recordBuf, unsigned int recordLen) michael@0: { michael@0: ClientHelloV2 *chv2; michael@0: ServerHelloV2 *shv2; michael@0: unsigned char *pos; michael@0: unsigned int p; michael@0: unsigned int q; michael@0: PRUint32 len; michael@0: michael@0: chv2 = (ClientHelloV2 *)recordBuf; michael@0: shv2 = (ServerHelloV2 *)recordBuf; michael@0: if (s->isEncrypted) { michael@0: PR_fprintf(PR_STDOUT," [ssl2] Encrypted {...}\n"); michael@0: return; michael@0: } michael@0: PR_fprintf(PR_STDOUT," [%s]", get_time_string() ); michael@0: switch(chv2->type) { michael@0: case 1: michael@0: PR_fprintf(PR_STDOUT," [ssl2] ClientHelloV2 {\n"); michael@0: PR_fprintf(PR_STDOUT," version = {0x%02x, 0x%02x}\n", michael@0: (PRUint32)chv2->version[0],(PRUint32)chv2->version[1]); michael@0: PR_fprintf(PR_STDOUT," cipher-specs-length = %d (0x%02x)\n", michael@0: (PRUint32)(GET_SHORT((chv2->cslength))), michael@0: (PRUint32)(GET_SHORT((chv2->cslength)))); michael@0: PR_fprintf(PR_STDOUT," sid-length = %d (0x%02x)\n", michael@0: (PRUint32)(GET_SHORT((chv2->sidlength))), michael@0: (PRUint32)(GET_SHORT((chv2->sidlength)))); michael@0: PR_fprintf(PR_STDOUT," challenge-length = %d (0x%02x)\n", michael@0: (PRUint32)(GET_SHORT((chv2->rndlength))), michael@0: (PRUint32)(GET_SHORT((chv2->rndlength)))); michael@0: PR_fprintf(PR_STDOUT," cipher-suites = { \n"); michael@0: for (p=0;pcslength));p+=3) { michael@0: PRUint32 cs_int = GET_24((&chv2->csuites[p])); michael@0: const char *cs_str = V2CipherString(cs_int); michael@0: michael@0: PR_fprintf(PR_STDOUT," (0x%06x) %s\n", michael@0: cs_int, cs_str); michael@0: } michael@0: q = p; michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: if (chv2->sidlength) { michael@0: PR_fprintf(PR_STDOUT," session-id = { "); michael@0: for (p=0;psidlength));p+=2) { michael@0: PR_fprintf(PR_STDOUT,"0x%04x ",(PRUint32)(GET_SHORT((&chv2->csuites[p+q])))); michael@0: } michael@0: } michael@0: q += p; michael@0: PR_fprintf(PR_STDOUT,"}\n"); michael@0: if (chv2->rndlength) { michael@0: PR_fprintf(PR_STDOUT," challenge = { "); michael@0: for (p=0;prndlength));p+=2) { michael@0: PR_fprintf(PR_STDOUT,"0x%04x ",(PRUint32)(GET_SHORT((&chv2->csuites[p+q])))); michael@0: } michael@0: PR_fprintf(PR_STDOUT,"}\n"); michael@0: } michael@0: PR_fprintf(PR_STDOUT,"}\n"); michael@0: break; michael@0: /* end of V2 CLientHello Parsing */ michael@0: michael@0: case 2: /* Client Master Key */ michael@0: { michael@0: const char *cs_str=NULL; michael@0: PRUint32 cs_int=0; michael@0: ClientMasterKeyV2 *cmkv2; michael@0: cmkv2 = (ClientMasterKeyV2 *)chv2; michael@0: isV2Session = 1; michael@0: michael@0: PR_fprintf(PR_STDOUT," [ssl2] ClientMasterKeyV2 { \n"); michael@0: michael@0: cs_int = GET_24(&cmkv2->cipherkind[0]); michael@0: cs_str = V2CipherString(cs_int); michael@0: PR_fprintf(PR_STDOUT," cipher-spec-chosen = (0x%06x) %s\n", michael@0: cs_int, cs_str); michael@0: michael@0: PR_fprintf(PR_STDOUT," clear-portion = %d bits\n", michael@0: 8*(PRUint32)(GET_SHORT((cmkv2->clearkey)))); michael@0: michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: clientstream.isEncrypted = 1; michael@0: serverstream.isEncrypted = 1; michael@0: } michael@0: break; michael@0: michael@0: michael@0: case 3: michael@0: PR_fprintf(PR_STDOUT," [ssl2] Client Finished V2 {...}\n"); michael@0: isV2Session = 1; michael@0: break; michael@0: michael@0: michael@0: case 4: /* V2 Server Hello */ michael@0: isV2Session = 1; michael@0: michael@0: PR_fprintf(PR_STDOUT," [ssl2] ServerHelloV2 {\n"); michael@0: PR_fprintf(PR_STDOUT," sid hit = {0x%02x}\n", michael@0: (PRUintn)shv2->sidhit); michael@0: PR_fprintf(PR_STDOUT," version = {0x%02x, 0x%02x}\n", michael@0: (PRUint32)shv2->version[0],(PRUint32)shv2->version[1]); michael@0: PR_fprintf(PR_STDOUT," cipher-specs-length = %d (0x%02x)\n", michael@0: (PRUint32)(GET_SHORT((shv2->cslength))), michael@0: (PRUint32)(GET_SHORT((shv2->cslength)))); michael@0: PR_fprintf(PR_STDOUT," sid-length = %d (0x%02x)\n", michael@0: (PRUint32)(GET_SHORT((shv2->cidlength))), michael@0: (PRUint32)(GET_SHORT((shv2->cidlength)))); michael@0: michael@0: pos = (unsigned char *)shv2; michael@0: pos += 2; /* skip length header */ michael@0: pos += 11; /* position pointer to Certificate data area */ michael@0: q = GET_SHORT(&shv2->certlength); michael@0: if (q >recordLen) { michael@0: goto eosh; michael@0: } michael@0: pos += q; /* skip certificate */ michael@0: michael@0: PR_fprintf(PR_STDOUT," cipher-suites = { "); michael@0: len = GET_SHORT((shv2->cslength)); michael@0: for (p = 0; p < len; p += 3) { michael@0: PRUint32 cs_int = GET_24((pos+p)); michael@0: const char *cs_str = V2CipherString(cs_int); michael@0: PR_fprintf(PR_STDOUT,"\n "); michael@0: PR_fprintf(PR_STDOUT,"(0x%06x) %s", cs_int, cs_str); michael@0: } michael@0: pos += len; michael@0: PR_fprintf(PR_STDOUT," }\n"); /* End of cipher suites */ michael@0: len = (PRUint32)GET_SHORT((shv2->cidlength)); michael@0: if (len) { michael@0: PR_fprintf(PR_STDOUT," connection-id = { "); michael@0: for (p = 0; p < len; p += 2) { michael@0: PR_fprintf(PR_STDOUT,"0x%04x ", (PRUint32)(GET_SHORT((pos + p)))); michael@0: } michael@0: PR_fprintf(PR_STDOUT," }\n"); /* End of connection id */ michael@0: } michael@0: eosh: michael@0: PR_fprintf(PR_STDOUT,"\n }\n"); /* end of ServerHelloV2 */ michael@0: if (shv2->sidhit) { michael@0: clientstream.isEncrypted = 1; michael@0: serverstream.isEncrypted = 1; michael@0: } michael@0: break; michael@0: michael@0: case 5: michael@0: PR_fprintf(PR_STDOUT," [ssl2] Server Verify V2 {...}\n"); michael@0: isV2Session = 1; michael@0: break; michael@0: michael@0: case 6: michael@0: PR_fprintf(PR_STDOUT," [ssl2] Server Finished V2 {...}\n"); michael@0: isV2Session = 1; michael@0: break; michael@0: michael@0: case 7: michael@0: PR_fprintf(PR_STDOUT," [ssl2] Request Certificate V2 {...}\n"); michael@0: isV2Session = 1; michael@0: break; michael@0: michael@0: case 8: michael@0: PR_fprintf(PR_STDOUT," [ssl2] Client Certificate V2 {...}\n"); michael@0: isV2Session = 1; michael@0: break; michael@0: michael@0: default: michael@0: PR_fprintf(PR_STDOUT," [ssl2] UnknownType 0x%02x {...}\n", michael@0: (PRUint32)chv2->type); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: unsigned int print_hello_extension(unsigned char * hsdata, michael@0: unsigned int length, michael@0: unsigned int pos) michael@0: { michael@0: /* pretty print extensions, if any */ michael@0: if (pos < length) { michael@0: int exListLen = GET_SHORT((hsdata+pos)); pos += 2; michael@0: PR_fprintf(PR_STDOUT, michael@0: " extensions[%d] = {\n", exListLen); michael@0: while (exListLen > 0 && pos < length) { michael@0: int exLen; michael@0: int exType = GET_SHORT((hsdata+pos)); pos += 2; michael@0: exLen = GET_SHORT((hsdata+pos)); pos += 2; michael@0: /* dump the extension */ michael@0: PR_fprintf(PR_STDOUT, michael@0: " extension type %s, length [%d]", michael@0: helloExtensionNameString(exType), exLen); michael@0: if (exLen > 0) { michael@0: PR_fprintf(PR_STDOUT, " = {\n"); michael@0: print_hex(exLen, hsdata + pos); michael@0: PR_fprintf(PR_STDOUT, " }\n"); michael@0: } else { michael@0: PR_fprintf(PR_STDOUT, "\n"); michael@0: } michael@0: pos += exLen; michael@0: exListLen -= 2 + exLen; michael@0: } michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: } michael@0: return pos; michael@0: } michael@0: michael@0: /* michael@0: * Note this must match (exactly) the enumeration ocspResponseStatus. michael@0: */ michael@0: static char *responseStatusNames[] = { michael@0: "successful (Response has valid confirmations)", michael@0: "malformedRequest (Illegal confirmation request)", michael@0: "internalError (Internal error in issuer)", michael@0: "tryLater (Try again later)", michael@0: "unused ((4) is not used)", michael@0: "sigRequired (Must sign the request)", michael@0: "unauthorized (Request unauthorized)", michael@0: }; michael@0: michael@0: static void michael@0: print_ocsp_cert_id (FILE *out_file, CERTOCSPCertID *cert_id, int level) michael@0: { michael@0: SECU_Indent (out_file, level); michael@0: fprintf (out_file, "Cert ID:\n"); michael@0: level++; michael@0: /* michael@0: SECU_PrintAlgorithmID (out_file, &(cert_id->hashAlgorithm), michael@0: "Hash Algorithm", level); michael@0: SECU_PrintAsHex (out_file, &(cert_id->issuerNameHash), michael@0: "Issuer Name Hash", level); michael@0: SECU_PrintAsHex (out_file, &(cert_id->issuerKeyHash), michael@0: "Issuer Key Hash", level); michael@0: */ michael@0: SECU_PrintInteger (out_file, &(cert_id->serialNumber), michael@0: "Serial Number", level); michael@0: /* XXX lookup the cert; if found, print something nice (nickname?) */ michael@0: } michael@0: michael@0: static void michael@0: print_ocsp_version (FILE *out_file, SECItem *version, int level) michael@0: { michael@0: if (version->len > 0) { michael@0: SECU_PrintInteger (out_file, version, "Version", level); michael@0: } else { michael@0: SECU_Indent (out_file, level); michael@0: fprintf (out_file, "Version: DEFAULT\n"); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: print_responder_id (FILE *out_file, ocspResponderID *responderID, int level) michael@0: { michael@0: SECU_Indent (out_file, level); michael@0: fprintf (out_file, "Responder ID "); michael@0: michael@0: switch (responderID->responderIDType) { michael@0: case ocspResponderID_byName: michael@0: fprintf (out_file, "(byName):\n"); michael@0: SECU_PrintName (out_file, &(responderID->responderIDValue.name), michael@0: "Name", level + 1); michael@0: break; michael@0: case ocspResponderID_byKey: michael@0: fprintf (out_file, "(byKey):\n"); michael@0: SECU_PrintAsHex (out_file, &(responderID->responderIDValue.keyHash), michael@0: "Key Hash", level + 1); michael@0: break; michael@0: default: michael@0: fprintf (out_file, "Unrecognized Responder ID Type\n"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: static void michael@0: print_ocsp_extensions (FILE *out_file, CERTCertExtension **extensions, michael@0: char *msg, int level) michael@0: { michael@0: if (extensions) { michael@0: SECU_PrintExtensions (out_file, extensions, msg, level); michael@0: } else { michael@0: SECU_Indent (out_file, level); michael@0: fprintf (out_file, "No %s\n", msg); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: print_revoked_info (FILE *out_file, ocspRevokedInfo *revoked_info, int level) michael@0: { michael@0: SECU_PrintGeneralizedTime (out_file, &(revoked_info->revocationTime), michael@0: "Revocation Time", level); michael@0: michael@0: if (revoked_info->revocationReason != NULL) { michael@0: SECU_PrintAsHex (out_file, revoked_info->revocationReason, michael@0: "Revocation Reason", level); michael@0: } else { michael@0: SECU_Indent (out_file, level); michael@0: fprintf (out_file, "No Revocation Reason.\n"); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: print_cert_status (FILE *out_file, ocspCertStatus *status, int level) michael@0: { michael@0: SECU_Indent (out_file, level); michael@0: fprintf (out_file, "Status: "); michael@0: michael@0: switch (status->certStatusType) { michael@0: case ocspCertStatus_good: michael@0: fprintf (out_file, "Cert is good.\n"); michael@0: break; michael@0: case ocspCertStatus_revoked: michael@0: fprintf (out_file, "Cert has been revoked.\n"); michael@0: print_revoked_info (out_file, status->certStatusInfo.revokedInfo, michael@0: level + 1); michael@0: break; michael@0: case ocspCertStatus_unknown: michael@0: fprintf (out_file, "Cert is unknown to responder.\n"); michael@0: break; michael@0: default: michael@0: fprintf (out_file, "Unrecognized status.\n"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: static void michael@0: print_single_response (FILE *out_file, CERTOCSPSingleResponse *single, michael@0: int level) michael@0: { michael@0: print_ocsp_cert_id (out_file, single->certID, level); michael@0: michael@0: print_cert_status (out_file, single->certStatus, level); michael@0: michael@0: SECU_PrintGeneralizedTime (out_file, &(single->thisUpdate), michael@0: "This Update", level); michael@0: michael@0: if (single->nextUpdate != NULL) { michael@0: SECU_PrintGeneralizedTime (out_file, single->nextUpdate, michael@0: "Next Update", level); michael@0: } else { michael@0: SECU_Indent (out_file, level); michael@0: fprintf (out_file, "No Next Update\n"); michael@0: } michael@0: michael@0: print_ocsp_extensions (out_file, single->singleExtensions, michael@0: "Single Response Extensions", level); michael@0: } michael@0: michael@0: static void michael@0: print_response_data (FILE *out_file, ocspResponseData *responseData, int level) michael@0: { michael@0: SECU_Indent (out_file, level); michael@0: fprintf (out_file, "Response Data:\n"); michael@0: level++; michael@0: michael@0: print_ocsp_version (out_file, &(responseData->version), level); michael@0: michael@0: print_responder_id (out_file, responseData->responderID, level); michael@0: michael@0: SECU_PrintGeneralizedTime (out_file, &(responseData->producedAt), michael@0: "Produced At", level); michael@0: michael@0: if (responseData->responses != NULL) { michael@0: int i; michael@0: michael@0: for (i = 0; responseData->responses[i] != NULL; i++) { michael@0: SECU_Indent (out_file, level); michael@0: fprintf (out_file, "Response %d:\n", i); michael@0: print_single_response (out_file, responseData->responses[i], michael@0: level + 1); michael@0: } michael@0: } else { michael@0: fprintf (out_file, "Response list is empty.\n"); michael@0: } michael@0: michael@0: print_ocsp_extensions (out_file, responseData->responseExtensions, michael@0: "Response Extensions", level); michael@0: } michael@0: michael@0: static void michael@0: print_basic_response (FILE *out_file, ocspBasicOCSPResponse *basic, int level) michael@0: { michael@0: SECU_Indent (out_file, level); michael@0: fprintf (out_file, "Basic OCSP Response:\n"); michael@0: level++; michael@0: michael@0: print_response_data (out_file, basic->tbsResponseData, level); michael@0: } michael@0: michael@0: static void michael@0: print_status_response(SECItem *data) michael@0: { michael@0: int level = 2; michael@0: CERTOCSPResponse *response; michael@0: response = CERT_DecodeOCSPResponse (data); michael@0: if (!response) { michael@0: SECU_Indent (stdout, level); michael@0: fprintf(stdout,"unable to decode certificate_status\n"); michael@0: return; michael@0: } michael@0: michael@0: SECU_Indent (stdout, level); michael@0: if (response->statusValue >= ocspResponse_min && michael@0: response->statusValue <= ocspResponse_max) { michael@0: fprintf (stdout, "Response Status: %s\n", michael@0: responseStatusNames[response->statusValue]); michael@0: } else { michael@0: fprintf (stdout, michael@0: "Response Status: other (Status value %d out of defined range)\n", michael@0: (int)response->statusValue); michael@0: } michael@0: michael@0: if (response->statusValue == ocspResponse_successful) { michael@0: ocspResponseBytes *responseBytes = response->responseBytes; michael@0: PORT_Assert (responseBytes != NULL); michael@0: michael@0: level++; michael@0: SECU_PrintObjectID (stdout, &(responseBytes->responseType), michael@0: "Response Type", level); michael@0: switch (response->responseBytes->responseTypeTag) { michael@0: case SEC_OID_PKIX_OCSP_BASIC_RESPONSE: michael@0: print_basic_response (stdout, michael@0: responseBytes->decodedResponse.basic, michael@0: level); michael@0: break; michael@0: default: michael@0: SECU_Indent (stdout, level); michael@0: fprintf (stdout, "Unknown response syntax\n"); michael@0: break; michael@0: } michael@0: } else { michael@0: SECU_Indent (stdout, level); michael@0: fprintf (stdout, "Unsuccessful response, no more information.\n"); michael@0: } michael@0: michael@0: CERT_DestroyOCSPResponse (response); michael@0: } michael@0: michael@0: /* In the case of renegotiation, handshakes that occur in an already MAC'ed michael@0: * channel, by the time of this call, the caller has already removed the MAC michael@0: * from input recordLen. The only MAC'ed record that will get here with its michael@0: * MAC intact (not removed) is the first Finished message on the connection. michael@0: */ michael@0: void print_ssl3_handshake(unsigned char *recordBuf, michael@0: unsigned int recordLen, michael@0: SSLRecord * sr, michael@0: DataBufferList *s) michael@0: { michael@0: struct sslhandshake sslh; michael@0: unsigned char * hsdata; michael@0: int offset=0; michael@0: michael@0: PR_fprintf(PR_STDOUT," handshake {\n"); michael@0: michael@0: if (s->msgBufOffset && s->msgBuf) { michael@0: /* append recordBuf to msgBuf, then use msgBuf */ michael@0: if (s->msgBufOffset + recordLen > s->msgBufSize) { michael@0: int newSize = s->msgBufOffset + recordLen; michael@0: unsigned char * newBuf = PORT_Realloc(s->msgBuf, newSize); michael@0: if (!newBuf) { michael@0: PR_ASSERT(newBuf); michael@0: showErr( "Realloc failed"); michael@0: exit(10); michael@0: } michael@0: s->msgBuf = newBuf; michael@0: s->msgBufSize = newSize; michael@0: } michael@0: memcpy(s->msgBuf + s->msgBufOffset, recordBuf, recordLen); michael@0: s->msgBufOffset += recordLen; michael@0: recordLen = s->msgBufOffset; michael@0: recordBuf = s->msgBuf; michael@0: } michael@0: while (offset + 4 <= recordLen) { michael@0: sslh.type = recordBuf[offset]; michael@0: sslh.length = GET_24(recordBuf+offset+1); michael@0: if (offset + 4 + sslh.length > recordLen) michael@0: break; michael@0: /* finally have a complete message */ michael@0: if (sslhexparse) michael@0: print_hex(4,recordBuf+offset); michael@0: michael@0: hsdata = &recordBuf[offset+4]; michael@0: michael@0: PR_fprintf(PR_STDOUT," type = %d (",sslh.type); michael@0: switch(sslh.type) { michael@0: case 0: PR_FPUTS("hello_request)\n" ); break; michael@0: case 1: PR_FPUTS("client_hello)\n" ); break; michael@0: case 2: PR_FPUTS("server_hello)\n" ); break; michael@0: case 4: PR_FPUTS("new_session_ticket)\n" ); break; michael@0: case 11: PR_FPUTS("certificate)\n" ); break; michael@0: case 12: PR_FPUTS("server_key_exchange)\n" ); break; michael@0: case 13: PR_FPUTS("certificate_request)\n" ); break; michael@0: case 14: PR_FPUTS("server_hello_done)\n" ); break; michael@0: case 15: PR_FPUTS("certificate_verify)\n" ); break; michael@0: case 16: PR_FPUTS("client_key_exchange)\n" ); break; michael@0: case 20: PR_FPUTS("finished)\n" ); break; michael@0: case 22: PR_FPUTS("certificate_status)\n" ); break; michael@0: default: PR_FPUTS("unknown)\n" ); break; michael@0: } michael@0: michael@0: PR_fprintf(PR_STDOUT," length = %d (0x%06x)\n",sslh.length,sslh.length); michael@0: switch (sslh.type) { michael@0: michael@0: case 0: /* hello_request */ /* not much to show here. */ break; michael@0: michael@0: case 1: /* client hello */ michael@0: switch (sr->ver_maj) { michael@0: case 3: /* ssl version 3 */ michael@0: { michael@0: unsigned int pos; michael@0: int w; michael@0: michael@0: PR_fprintf(PR_STDOUT," ClientHelloV3 {\n"); michael@0: PR_fprintf(PR_STDOUT," client_version = {%d, %d}\n", michael@0: (PRUint8)hsdata[0],(PRUint8)hsdata[1]); michael@0: PR_fprintf(PR_STDOUT," random = {...}\n"); michael@0: if (sslhexparse) print_hex(32,&hsdata[2]); michael@0: michael@0: /* pretty print Session ID */ michael@0: { michael@0: int sidlength = (int)hsdata[2+32]; michael@0: PR_fprintf(PR_STDOUT," session ID = {\n"); michael@0: PR_fprintf(PR_STDOUT," length = %d\n",sidlength); michael@0: PR_fprintf(PR_STDOUT," contents = {...}\n"); michael@0: if (sslhexparse) print_hex(sidlength,&hsdata[2+32+1]); michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: pos = 2+32+1+sidlength; michael@0: } michael@0: michael@0: /* pretty print cipher suites */ michael@0: { michael@0: int csuitelength = GET_SHORT((hsdata+pos)); michael@0: PR_fprintf(PR_STDOUT," cipher_suites[%d] = {\n", michael@0: csuitelength/2); michael@0: if (csuitelength % 2) { michael@0: PR_fprintf(PR_STDOUT, michael@0: "*error in protocol - csuitelength shouldn't be odd*\n"); michael@0: } michael@0: for (w=0; wver_maj, sr->ver_min ); michael@0: if (sslhexparse) print_hex(sslh.length, hsdata); michael@0: break; michael@0: } /* end of switch sr->ver_maj */ michael@0: break; michael@0: michael@0: case 2: /* server hello */ michael@0: { michael@0: unsigned int sidlength, pos; michael@0: michael@0: PR_fprintf(PR_STDOUT," ServerHello {\n"); michael@0: michael@0: PR_fprintf(PR_STDOUT," server_version = {%d, %d}\n", michael@0: (PRUint8)hsdata[0],(PRUint8)hsdata[1]); michael@0: PR_fprintf(PR_STDOUT," random = {...}\n"); michael@0: if (sslhexparse) print_hex(32,&hsdata[2]); michael@0: PR_fprintf(PR_STDOUT," session ID = {\n"); michael@0: sidlength = (int)hsdata[2+32]; michael@0: PR_fprintf(PR_STDOUT," length = %d\n",sidlength); michael@0: PR_fprintf(PR_STDOUT," contents = {...}\n"); michael@0: if (sslhexparse) print_hex(sidlength,&hsdata[2+32+1]); michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: pos = 2+32+1+sidlength; michael@0: michael@0: /* pretty print chosen cipher suite */ michael@0: { michael@0: PRUint32 cs_int = GET_SHORT((hsdata+pos)); michael@0: const char *cs_str = V2CipherString(cs_int); michael@0: PR_fprintf(PR_STDOUT," cipher_suite = (0x%04x) %s\n", michael@0: cs_int, cs_str); michael@0: currentcipher = cs_int; michael@0: pos += 2; michael@0: } michael@0: /* pretty print chosen compression method */ michael@0: { michael@0: PRUint32 cm_int = hsdata[pos++]; michael@0: const char *cm_str = CompressionMethodString(cm_int); michael@0: PR_fprintf(PR_STDOUT," compression method = (%02x) %s\n", michael@0: cm_int, cm_str); michael@0: } michael@0: michael@0: /* pretty print extensions, if any */ michael@0: pos = print_hello_extension(hsdata, sslh.length, pos); michael@0: michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: } michael@0: break; michael@0: michael@0: case 4: /* new session ticket */ michael@0: { michael@0: PRUint32 lifetimehint; michael@0: PRUint16 ticketlength; michael@0: char lifetime[32]; michael@0: lifetimehint = GET_32(hsdata); michael@0: if (lifetimehint) { michael@0: PRExplodedTime et; michael@0: PRTime t = lifetimehint; michael@0: t *= PR_USEC_PER_SEC; michael@0: PR_ExplodeTime(t, PR_GMTParameters, &et); michael@0: /* use HTTP Cookie header's date format */ michael@0: PR_FormatTimeUSEnglish(lifetime, sizeof lifetime, michael@0: "%a, %d-%b-%Y %H:%M:%S GMT", &et); michael@0: } else { michael@0: /* 0 means the lifetime of the ticket is unspecified */ michael@0: strcpy(lifetime, "unspecified"); michael@0: } michael@0: ticketlength = GET_SHORT((hsdata+4)); michael@0: PR_fprintf(PR_STDOUT," NewSessionTicket {\n"); michael@0: PR_fprintf(PR_STDOUT," ticket_lifetime_hint = %s\n", michael@0: lifetime); michael@0: PR_fprintf(PR_STDOUT," ticket = {\n"); michael@0: PR_fprintf(PR_STDOUT," length = %d\n",ticketlength); michael@0: PR_fprintf(PR_STDOUT," contents = {...}\n"); michael@0: if (sslhexparse) print_hex(ticketlength,&hsdata[4+2]); michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: } michael@0: break; michael@0: michael@0: case 11: /* certificate */ michael@0: { michael@0: PRFileDesc *cfd; michael@0: int pos; michael@0: int certslength; michael@0: int certlength; michael@0: int certbytesread = 0; michael@0: static int certFileNumber; michael@0: char certFileName[20]; michael@0: michael@0: PR_fprintf(PR_STDOUT," CertificateChain {\n"); michael@0: certslength = GET_24(hsdata); michael@0: PR_fprintf(PR_STDOUT," chainlength = %d (0x%04x)\n", michael@0: certslength,certslength); michael@0: pos = 3; michael@0: while (certbytesread < certslength) { michael@0: certlength = GET_24((hsdata+pos)); michael@0: pos += 3; michael@0: PR_fprintf(PR_STDOUT," Certificate {\n"); michael@0: PR_fprintf(PR_STDOUT," size = %d (0x%04x)\n", michael@0: certlength,certlength); michael@0: certbytesread += certlength+3; michael@0: if (certbytesread <= certslength) { michael@0: PR_snprintf(certFileName, sizeof certFileName, "cert.%03d", michael@0: ++certFileNumber); michael@0: cfd = PR_Open(certFileName, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, michael@0: 0664); michael@0: if (!cfd) { michael@0: PR_fprintf(PR_STDOUT, michael@0: " data = { couldn't save file '%s' }\n", michael@0: certFileName); michael@0: } else { michael@0: PR_Write(cfd, (hsdata+pos), certlength); michael@0: PR_fprintf(PR_STDOUT, michael@0: " data = { saved in file '%s' }\n", michael@0: certFileName); michael@0: PR_Close(cfd); michael@0: } michael@0: } michael@0: michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: pos += certlength; michael@0: } michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: } michael@0: break; michael@0: michael@0: case 12: /* server_key_exchange */ michael@0: if (sslhexparse) print_hex(sslh.length, hsdata); michael@0: break; michael@0: michael@0: case 13: /* certificate request */ michael@0: { michael@0: unsigned int pos = 0; michael@0: int w, reqLength; michael@0: michael@0: PR_fprintf(PR_STDOUT," CertificateRequest {\n"); michael@0: michael@0: /* pretty print requested certificate types */ michael@0: reqLength = hsdata[pos]; michael@0: PR_fprintf(PR_STDOUT," certificate types[%d] = {", michael@0: reqLength); michael@0: for (w=0; w < reqLength; w++) { michael@0: PR_fprintf(PR_STDOUT, " %02x", hsdata[pos+1+w]); michael@0: } michael@0: pos += 1 + reqLength; michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: michael@0: /* pretty print CA names, if any */ michael@0: if (pos < sslh.length) { michael@0: int exListLen = GET_SHORT((hsdata+pos)); pos += 2; michael@0: PR_fprintf(PR_STDOUT, michael@0: " certificate_authorities[%d] = {\n", michael@0: exListLen); michael@0: while (exListLen > 0 && pos < sslh.length) { michael@0: char * ca_name; michael@0: SECItem it; michael@0: int dnLen = GET_SHORT((hsdata+pos)); pos += 2; michael@0: michael@0: /* dump the CA name */ michael@0: it.type = siBuffer; michael@0: it.data = hsdata + pos; michael@0: it.len = dnLen; michael@0: ca_name = CERT_DerNameToAscii(&it); michael@0: if (ca_name) { michael@0: PR_fprintf(PR_STDOUT," %s\n", ca_name); michael@0: PORT_Free(ca_name); michael@0: } else { michael@0: PR_fprintf(PR_STDOUT, michael@0: " distinguished name [%d]", dnLen); michael@0: if (dnLen > 0 && sslhexparse) { michael@0: PR_fprintf(PR_STDOUT, " = {\n"); michael@0: print_hex(dnLen, hsdata + pos); michael@0: PR_fprintf(PR_STDOUT, " }\n"); michael@0: } else { michael@0: PR_fprintf(PR_STDOUT, "\n"); michael@0: } michael@0: } michael@0: pos += dnLen; michael@0: exListLen -= 2 + dnLen; michael@0: } michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: } michael@0: michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: } michael@0: break; michael@0: michael@0: case 14: /* server_hello_done */ /* not much to show here. */ break; michael@0: michael@0: case 15: /* certificate_verify */ michael@0: if (sslhexparse) print_hex(sslh.length, hsdata); michael@0: break; michael@0: michael@0: case 16: /* client key exchange */ michael@0: { michael@0: PR_fprintf(PR_STDOUT," ClientKeyExchange {\n"); michael@0: PR_fprintf(PR_STDOUT," message = {...}\n"); michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: } michael@0: break; michael@0: michael@0: case 20: /* finished */ michael@0: PR_fprintf(PR_STDOUT," Finished {\n"); michael@0: PR_fprintf(PR_STDOUT," verify_data = {...}\n"); michael@0: if (sslhexparse) print_hex(sslh.length, hsdata); michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: michael@0: if (!isNULLmac(currentcipher) && !s->hMACsize) { michael@0: /* To calculate the size of MAC, we subtract the number of known michael@0: * bytes of message from the number of remaining bytes in the michael@0: * record. This assumes that this is the first record on the michael@0: * connection to have a MAC, and that the sender has not put another michael@0: * message after the finished message in the handshake record. michael@0: * This is only correct for the first transition from unMACed to michael@0: * MACed. If the connection switches from one cipher suite to michael@0: * another one with a different MAC, this logic will not track that michael@0: * change correctly. michael@0: */ michael@0: s->hMACsize = recordLen - (sslh.length + 4); michael@0: sslh.length += s->hMACsize; /* skip over the MAC data */ michael@0: } michael@0: break; michael@0: michael@0: case 22: /* certificate_status */ michael@0: { michael@0: SECItem data; michael@0: PRFileDesc *ofd; michael@0: static int ocspFileNumber; michael@0: char ocspFileName[20]; michael@0: michael@0: /* skip 4 bytes with handshake numbers, as in ssl3_HandleCertificateStatus */ michael@0: data.type = siBuffer; michael@0: data.data = hsdata + 4; michael@0: data.len = sslh.length - 4; michael@0: print_status_response(&data); michael@0: michael@0: PR_snprintf(ocspFileName, sizeof ocspFileName, "ocsp.%03d", michael@0: ++ocspFileNumber); michael@0: ofd = PR_Open(ocspFileName, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, michael@0: 0664); michael@0: if (!ofd) { michael@0: PR_fprintf(PR_STDOUT, michael@0: " data = { couldn't save file '%s' }\n", michael@0: ocspFileName); michael@0: } else { michael@0: PR_Write(ofd, data.data, data.len); michael@0: PR_fprintf(PR_STDOUT, michael@0: " data = { saved in file '%s' }\n", michael@0: ocspFileName); michael@0: PR_Close(ofd); michael@0: } michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: { michael@0: PR_fprintf(PR_STDOUT," UNKNOWN MESSAGE TYPE %d [%d] {\n", michael@0: sslh.type, sslh.length); michael@0: if (sslhexparse) print_hex(sslh.length, hsdata); michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: michael@0: } michael@0: } /* end of switch sslh.type */ michael@0: offset += sslh.length + 4; michael@0: } /* while */ michael@0: if (offset < recordLen) { /* stuff left over */ michael@0: int newMsgLen = recordLen - offset; michael@0: if (!s->msgBuf) { michael@0: s->msgBuf = PORT_Alloc(newMsgLen); michael@0: if (!s->msgBuf) { michael@0: PR_ASSERT(s->msgBuf); michael@0: showErr( "Malloc failed"); michael@0: exit(11); michael@0: } michael@0: s->msgBufSize = newMsgLen; michael@0: memcpy(s->msgBuf, recordBuf + offset, newMsgLen); michael@0: } else if (newMsgLen > s->msgBufSize) { michael@0: unsigned char * newBuf = PORT_Realloc(s->msgBuf, newMsgLen); michael@0: if (!newBuf) { michael@0: PR_ASSERT(newBuf); michael@0: showErr( "Realloc failed"); michael@0: exit(12); michael@0: } michael@0: s->msgBuf = newBuf; michael@0: s->msgBufSize = newMsgLen; michael@0: } else if (offset || s->msgBuf != recordBuf) { michael@0: memmove(s->msgBuf, recordBuf + offset, newMsgLen); michael@0: } michael@0: s->msgBufOffset = newMsgLen; michael@0: PR_fprintf(PR_STDOUT," [incomplete handshake message]\n"); michael@0: } else { michael@0: s->msgBufOffset = 0; michael@0: } michael@0: PR_fprintf(PR_STDOUT," }\n"); michael@0: } michael@0: michael@0: michael@0: void print_ssl(DataBufferList *s, int length, unsigned char *buffer) michael@0: { michael@0: /* -------------------------------------------------------- */ michael@0: /* first, create a new buffer object for this piece of data. */ michael@0: michael@0: DataBuffer *db; michael@0: michael@0: if (s->size == 0 && length > 0 && buffer[0] >= 32 && buffer[0] < 128) { michael@0: /* Not an SSL record, treat entire buffer as plaintext */ michael@0: PR_Write(PR_STDOUT,buffer,length); michael@0: return; michael@0: } michael@0: michael@0: check_integrity(s); michael@0: michael@0: db = PR_NEW(struct _DataBuffer); michael@0: michael@0: db->buffer = (unsigned char*)PORT_Alloc(length); michael@0: db->length = length; michael@0: db->offset = 0; michael@0: memcpy(db->buffer, buffer, length); michael@0: db->next = NULL; michael@0: michael@0: /* now, add it to the stream */ michael@0: michael@0: if (s->last != NULL) s->last->next = db; michael@0: s->last = db; michael@0: s->size += length; michael@0: if (s->first == NULL) s->first = db; michael@0: michael@0: check_integrity(s); michael@0: michael@0: /*------------------------------------------------------- */ michael@0: /* now we look at the stream to see if we have enough data to michael@0: decode */ michael@0: michael@0: while (s->size > 0 ) { michael@0: unsigned char *recordBuf = NULL; michael@0: michael@0: SSLRecord sr; michael@0: unsigned recordLen; michael@0: unsigned recordsize; michael@0: michael@0: check_integrity(s); michael@0: michael@0: if ( s->first == NULL) { michael@0: PR_fprintf(PR_STDOUT,"ERROR: s->first is null\n"); michael@0: exit(9); michael@0: } michael@0: michael@0: /* in the case of an SSL 2 client-hello */ michael@0: /* will have the high-bit set, whereas an SSL 3 client-hello will not */ michael@0: /* SSL2 can also send records that begin with the high bit clear. michael@0: * This code will incorrectly handle them. XXX michael@0: */ michael@0: if (isV2Session || s->first->buffer[s->first->offset] & 0x80) { michael@0: /* it's an SSL 2 packet */ michael@0: unsigned char lenbuf[3]; michael@0: michael@0: /* first, we check if there's enough data for it to be an SSL2-type michael@0: * record. What a pain.*/ michael@0: if (s->size < sizeof lenbuf) { michael@0: partial_packet(length, s->size, sizeof lenbuf); michael@0: return; michael@0: } michael@0: michael@0: /* read the first two bytes off the stream. */ michael@0: read_stream_bytes(lenbuf, s, sizeof(lenbuf)); michael@0: recordLen = ((unsigned int)(lenbuf[0] & 0x7f) << 8) + lenbuf[1] + michael@0: ((lenbuf[0] & 0x80) ? 2 : 3); michael@0: PR_fprintf(PR_STDOUT, "recordLen = %u bytes\n", recordLen); michael@0: michael@0: /* put 'em back on the head of the stream. */ michael@0: db = PR_NEW(struct _DataBuffer); michael@0: michael@0: db->length = sizeof lenbuf; michael@0: db->buffer = (unsigned char*) PORT_Alloc(db->length); michael@0: db->offset = 0; michael@0: memcpy(db->buffer, lenbuf, sizeof lenbuf); michael@0: michael@0: db->next = s->first; michael@0: s->first = db; michael@0: if (s->last == NULL) michael@0: s->last = db; michael@0: s->size += db->length; michael@0: michael@0: /* if there wasn't enough, go back for more. */ michael@0: if (s->size < recordLen) { michael@0: check_integrity(s); michael@0: partial_packet(length, s->size, recordLen); michael@0: return; michael@0: } michael@0: partial_packet(length, s->size, recordLen); michael@0: michael@0: /* read in the whole record. */ michael@0: recordBuf = PORT_Alloc(recordLen); michael@0: read_stream_bytes(recordBuf, s, recordLen); michael@0: michael@0: print_sslv2(s, recordBuf, recordLen); michael@0: PR_FREEIF(recordBuf); michael@0: check_integrity(s); michael@0: michael@0: continue; michael@0: } michael@0: michael@0: /***********************************************************/ michael@0: /* It's SSL v3 */ michael@0: /***********************************************************/ michael@0: check_integrity(s); michael@0: michael@0: if (s->size < sizeof sr) { michael@0: partial_packet(length, s->size, sizeof(SSLRecord)); michael@0: return; michael@0: } michael@0: michael@0: read_stream_bytes((unsigned char *)&sr, s, sizeof sr); michael@0: michael@0: /* we have read the stream bytes. Look at the length of michael@0: the ssl record. If we don't have enough data to satisfy this michael@0: request, then put the bytes we just took back at the head michael@0: of the queue */ michael@0: recordsize = GET_SHORT(sr.length); michael@0: michael@0: if (recordsize > s->size) { michael@0: db = PR_NEW(struct _DataBuffer); michael@0: michael@0: db->length = sizeof sr; michael@0: db->buffer = (unsigned char*) PORT_Alloc(db->length); michael@0: db->offset = 0; michael@0: memcpy(db->buffer, &sr, sizeof sr); michael@0: db->next = s->first; michael@0: michael@0: /* now, add it back on to the head of the stream */ michael@0: michael@0: s->first = db; michael@0: if (s->last == NULL) michael@0: s->last = db; michael@0: s->size += db->length; michael@0: michael@0: check_integrity(s); michael@0: partial_packet(length, s->size, recordsize); michael@0: return; michael@0: } michael@0: partial_packet(length, s->size, recordsize); michael@0: michael@0: michael@0: PR_fprintf(PR_STDOUT,"SSLRecord { [%s]\n", get_time_string() ); michael@0: if (sslhexparse) { michael@0: print_hex(5,(unsigned char*)&sr); michael@0: } michael@0: michael@0: check_integrity(s); michael@0: michael@0: PR_fprintf(PR_STDOUT," type = %d (",sr.type); michael@0: switch(sr.type) { michael@0: case 20 : michael@0: PR_fprintf(PR_STDOUT,"change_cipher_spec)\n"); michael@0: break; michael@0: case 21 : michael@0: PR_fprintf(PR_STDOUT,"alert)\n"); michael@0: break; michael@0: case 22 : michael@0: PR_fprintf(PR_STDOUT,"handshake)\n"); michael@0: break; michael@0: case 23 : michael@0: PR_fprintf(PR_STDOUT,"application_data)\n"); michael@0: break; michael@0: default: michael@0: PR_fprintf(PR_STDOUT,"unknown)\n"); michael@0: break; michael@0: } michael@0: PR_fprintf(PR_STDOUT," version = { %d,%d }\n", michael@0: (PRUint32)sr.ver_maj,(PRUint32)sr.ver_min); michael@0: PR_fprintf(PR_STDOUT," length = %d (0x%x)\n", michael@0: (PRUint32)GET_SHORT(sr.length), (PRUint32)GET_SHORT(sr.length)); michael@0: michael@0: michael@0: recordLen = recordsize; michael@0: PR_ASSERT(s->size >= recordLen); michael@0: if (s->size >= recordLen) { michael@0: recordBuf = (unsigned char*) PORT_Alloc(recordLen); michael@0: read_stream_bytes(recordBuf, s, recordLen); michael@0: michael@0: if (s->isEncrypted) { michael@0: PR_fprintf(PR_STDOUT," < encrypted >\n"); michael@0: } else { /* not encrypted */ michael@0: michael@0: switch(sr.type) { michael@0: case 20 : /* change_cipher_spec */ michael@0: if (sslhexparse) print_hex(recordLen - s->hMACsize,recordBuf); michael@0: /* mark to say we can only dump hex form now on michael@0: * if it is not one on a null cipher */ michael@0: s->isEncrypted = isNULLcipher(currentcipher) ? 0 : 1; michael@0: break; michael@0: michael@0: case 21 : /* alert */ michael@0: switch(recordBuf[0]) { michael@0: case 1: PR_fprintf(PR_STDOUT, " warning: "); break; michael@0: case 2: PR_fprintf(PR_STDOUT, " fatal: "); break; michael@0: default: PR_fprintf(PR_STDOUT, " unknown level %d: ", recordBuf[0]); break; michael@0: } michael@0: michael@0: switch(recordBuf[1]) { michael@0: case 0: PR_FPUTS("close_notify\n" ); break; michael@0: case 10: PR_FPUTS("unexpected_message\n" ); break; michael@0: case 20: PR_FPUTS("bad_record_mac\n" ); break; michael@0: case 21: PR_FPUTS("decryption_failed\n" ); break; michael@0: case 22: PR_FPUTS("record_overflow\n" ); break; michael@0: case 30: PR_FPUTS("decompression_failure\n" ); break; michael@0: case 40: PR_FPUTS("handshake_failure\n" ); break; michael@0: case 41: PR_FPUTS("no_certificate\n" ); break; michael@0: case 42: PR_FPUTS("bad_certificate\n" ); break; michael@0: case 43: PR_FPUTS("unsupported_certificate\n" ); break; michael@0: case 44: PR_FPUTS("certificate_revoked\n" ); break; michael@0: case 45: PR_FPUTS("certificate_expired\n" ); break; michael@0: case 46: PR_FPUTS("certificate_unknown\n" ); break; michael@0: case 47: PR_FPUTS("illegal_parameter\n" ); break; michael@0: case 48: PR_FPUTS("unknown_ca\n" ); break; michael@0: case 49: PR_FPUTS("access_denied\n" ); break; michael@0: case 50: PR_FPUTS("decode_error\n" ); break; michael@0: case 51: PR_FPUTS("decrypt_error\n" ); break; michael@0: case 60: PR_FPUTS("export_restriction\n" ); break; michael@0: case 70: PR_FPUTS("protocol_version\n" ); break; michael@0: case 71: PR_FPUTS("insufficient_security\n" ); break; michael@0: case 80: PR_FPUTS("internal_error\n" ); break; michael@0: case 90: PR_FPUTS("user_canceled\n" ); break; michael@0: case 100: PR_FPUTS("no_renegotiation\n" ); break; michael@0: case 110: PR_FPUTS("unsupported_extension\n" ); break; michael@0: case 111: PR_FPUTS("certificate_unobtainable\n" ); break; michael@0: case 112: PR_FPUTS("unrecognized_name\n" ); break; michael@0: case 113: PR_FPUTS("bad_certificate_status_response\n" ); break; michael@0: case 114: PR_FPUTS("bad_certificate_hash_value\n" ); break; michael@0: michael@0: default: PR_fprintf(PR_STDOUT, "unknown alert %d\n", recordBuf[1]); michael@0: break; michael@0: } michael@0: michael@0: if (sslhexparse) print_hex(recordLen - s->hMACsize,recordBuf); michael@0: break; michael@0: michael@0: case 22 : /* handshake */ michael@0: print_ssl3_handshake( recordBuf, recordLen - s->hMACsize, &sr, s ); michael@0: break; michael@0: michael@0: case 23 : /* application data */ michael@0: print_hex(recordLen - s->hMACsize,recordBuf); michael@0: break; michael@0: michael@0: default: michael@0: print_hex(recordLen - s->hMACsize,recordBuf); michael@0: break; michael@0: } michael@0: if (s->hMACsize) { michael@0: PR_fprintf(PR_STDOUT," MAC = {...}\n"); michael@0: if (sslhexparse) { michael@0: unsigned char *offset = recordBuf + (recordLen - s->hMACsize); michael@0: print_hex(s->hMACsize, offset); michael@0: } michael@0: } michael@0: } /* not encrypted */ michael@0: } michael@0: PR_fprintf(PR_STDOUT,"}\n"); michael@0: PR_FREEIF(recordBuf); michael@0: check_integrity(s); michael@0: } michael@0: } michael@0: michael@0: void print_hex(int amt, unsigned char *buf) michael@0: { michael@0: int i,j,k; michael@0: char t[20]; michael@0: static char string[5000]; michael@0: michael@0: michael@0: for(i=0;i= 0x20 && j < 0x80) ? j : '.'; michael@0: michael@0: if (fancy) { michael@0: switch (t[0]) { michael@0: case '<': michael@0: strcpy(t,"<"); michael@0: break; michael@0: case '>': michael@0: strcpy(t,">"); michael@0: break; michael@0: case '&': michael@0: strcpy(t,"&"); michael@0: break; michael@0: } michael@0: } michael@0: strcat(string,t); michael@0: michael@0: PR_fprintf(PR_STDOUT,"%02x ",(PRUint8) buf[i]); michael@0: michael@0: /* if we've reached the end of the line - add the string */ michael@0: if (i%16 == 15) PR_fprintf(PR_STDOUT," | %s\n",string); michael@0: } michael@0: /* we reached the end of the buffer,*/ michael@0: /* do we have buffer left over? */ michael@0: j = i%16; michael@0: if (j > 0) { michael@0: for (k=0;k<(16-j);k++) { michael@0: /* print additional space after every four bytes */ michael@0: if ((k + j)%4 == 0) { michael@0: PR_fprintf(PR_STDOUT," "); michael@0: } michael@0: PR_fprintf(PR_STDOUT," "); michael@0: } michael@0: PR_fprintf(PR_STDOUT," | %s\n",string); michael@0: } michael@0: } michael@0: michael@0: void Usage(void) michael@0: { michael@0: PR_fprintf(PR_STDERR, "SSLTAP (C) 1997, 1998 Netscape Communications Corporation.\n"); michael@0: PR_fprintf(PR_STDERR, "Usage: ssltap [-vhfsxl] [-p port] hostname:port\n"); michael@0: PR_fprintf(PR_STDERR, " -v [prints version string]\n"); michael@0: PR_fprintf(PR_STDERR, " -h [outputs hex instead of ASCII]\n"); michael@0: PR_fprintf(PR_STDERR, " -f [turn on Fancy HTML coloring]\n"); michael@0: PR_fprintf(PR_STDERR, " -s [turn on SSL decoding]\n"); michael@0: PR_fprintf(PR_STDERR, " -x [turn on extra SSL hex dumps]\n"); michael@0: PR_fprintf(PR_STDERR, " -p port [specify rendezvous port (default 1924)]\n"); michael@0: PR_fprintf(PR_STDERR, " -l [loop - continue to wait for more connections]\n"); michael@0: michael@0: michael@0: } michael@0: michael@0: void michael@0: showErr(const char * msg) michael@0: { michael@0: PRErrorCode err = PR_GetError(); michael@0: const char * errString; michael@0: michael@0: if (err == PR_UNKNOWN_ERROR) michael@0: err = PR_CONNECT_RESET_ERROR; /* bug in NSPR. */ michael@0: errString = SECU_Strerror(err); michael@0: michael@0: if (!errString) michael@0: errString = "(no text available)"; michael@0: PR_fprintf(PR_STDERR, "%s: Error %d: %s: %s", progName, err, errString, msg); michael@0: } michael@0: michael@0: int main(int argc, char *argv[]) michael@0: { michael@0: char *hostname=NULL; michael@0: PRUint16 rendport=DEFPORT,port; michael@0: PRAddrInfo *ai; michael@0: void *iter; michael@0: PRStatus r; michael@0: PRNetAddr na_client,na_server,na_rend; michael@0: PRFileDesc *s_server,*s_client,*s_rend; /*rendezvous */ michael@0: int c_count=0; michael@0: PLOptState *optstate; michael@0: PLOptStatus status; michael@0: SECStatus rv; michael@0: michael@0: progName = argv[0]; michael@0: optstate = PL_CreateOptState(argc,argv,"fxhslp:"); michael@0: while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { michael@0: switch (optstate->option) { michael@0: case 'f': michael@0: fancy++; michael@0: break; michael@0: case 'h': michael@0: hexparse++; michael@0: break; michael@0: case 's': michael@0: sslparse++; michael@0: break; michael@0: case 'x': michael@0: sslhexparse++; michael@0: break; michael@0: case 'l': michael@0: looparound++; michael@0: break; michael@0: case 'p': michael@0: rendport = atoi(optstate->value); michael@0: break; michael@0: case '\0': michael@0: hostname = PL_strdup(optstate->value); michael@0: } michael@0: } michael@0: if (status == PL_OPT_BAD) michael@0: Usage(); michael@0: michael@0: if (fancy) { michael@0: if (!hexparse && !sslparse) { michael@0: PR_fprintf(PR_STDERR, michael@0: "Note: use of -f without -s or -h not recommended, \n" michael@0: "as the output looks a little strange. It may be useful, however\n"); michael@0: } michael@0: } michael@0: michael@0: if(! hostname ) Usage(), exit(2); michael@0: michael@0: { michael@0: char *colon = (char *)strchr(hostname, ':'); michael@0: if (!colon) { michael@0: PR_fprintf(PR_STDERR, michael@0: "You must specify the host AND port you wish to connect to\n"); michael@0: Usage(), exit(3); michael@0: } michael@0: port = atoi(&colon[1]); michael@0: *colon = '\0'; michael@0: michael@0: if (port == 0) { michael@0: PR_fprintf(PR_STDERR, "Port must be a nonzero number.\n"); michael@0: exit(4); michael@0: } michael@0: } michael@0: michael@0: /* find the 'server' IP address so we don't have to look it up later */ michael@0: michael@0: if (fancy) { michael@0: PR_fprintf(PR_STDOUT,"SSLTAP output\n"); michael@0: PR_fprintf(PR_STDOUT,"
\n");
michael@0:     }
michael@0:   PR_fprintf(PR_STDERR,"Looking up \"%s\"...\n", hostname);
michael@0:   ai = PR_GetAddrInfoByName(hostname, PR_AF_UNSPEC, PR_AI_ADDRCONFIG);
michael@0:   if (!ai) {
michael@0:     showErr("Host Name lookup failed\n");
michael@0:     exit(5);
michael@0:   }
michael@0: 
michael@0:   iter = NULL;
michael@0:   iter = PR_EnumerateAddrInfo(iter, ai, port, &na_server);
michael@0:   /* set up the port which the client will connect to */
michael@0: 
michael@0:   r = PR_InitializeNetAddr(PR_IpAddrAny,rendport,&na_rend);
michael@0:   if (r == PR_FAILURE) {
michael@0:     PR_fprintf(PR_STDERR,
michael@0:     "PR_InitializeNetAddr(,%d,) failed with error %d\n",PR_GetError());
michael@0:     exit(0);
michael@0:   }
michael@0: 
michael@0:   rv = NSS_NoDB_Init("");
michael@0:   if (rv != SECSuccess) {
michael@0:     PR_fprintf(PR_STDERR,
michael@0:     "NSS_NoDB_Init() failed with error %d\n",PR_GetError());
michael@0:     exit(5);
michael@0:   }
michael@0: 
michael@0:   s_rend = PR_NewTCPSocket();
michael@0:   if (!s_rend) {
michael@0:     showErr("Couldn't create socket\n");
michael@0:     exit(6);
michael@0:   }
michael@0: 
michael@0:   if (PR_Bind(s_rend, &na_rend )) {
michael@0:     PR_fprintf(PR_STDERR,"Couldn't bind to port %d (error %d)\n",rendport, PR_GetError());
michael@0:     exit(-1);
michael@0:   }
michael@0: 
michael@0:   if ( PR_Listen(s_rend, 5)) {
michael@0:     showErr("Couldn't listen\n");
michael@0:     exit(-1);
michael@0:   }
michael@0: 
michael@0:   PR_fprintf(PR_STDERR,"Proxy socket ready and listening\n");
michael@0:   do {	/* accept one connection and process it. */
michael@0:       PRPollDesc pds[2];
michael@0: 
michael@0:       s_client = PR_Accept(s_rend,&na_client,PR_SecondsToInterval(3600));
michael@0:       if (s_client == NULL) {
michael@0: 	showErr("accept timed out\n");
michael@0: 	exit(7);
michael@0:       }
michael@0: 
michael@0:       s_server = PR_OpenTCPSocket(na_server.raw.family);
michael@0:       if (s_server == NULL) {
michael@0: 	showErr("couldn't open new socket to connect to server \n");
michael@0: 	exit(8);
michael@0:       }
michael@0: 
michael@0:       r = PR_Connect(s_server,&na_server,PR_SecondsToInterval(5));
michael@0: 
michael@0:       if ( r == PR_FAILURE )
michael@0:         {
michael@0: 	  showErr("Couldn't connect\n");
michael@0: 	  return -1;
michael@0:         }
michael@0: 
michael@0:       if (looparound) {
michael@0: 	if (fancy)  PR_fprintf(PR_STDOUT,"


"); michael@0: PR_fprintf(PR_STDOUT,"Connection #%d [%s]\n", c_count+1, michael@0: get_time_string()); michael@0: if (fancy) PR_fprintf(PR_STDOUT,"

"); michael@0: } michael@0: michael@0: michael@0: PR_fprintf(PR_STDOUT,"Connected to %s:%d\n", hostname, port); michael@0: michael@0: #define PD_C 0 michael@0: #define PD_S 1 michael@0: michael@0: pds[PD_C].fd = s_client; michael@0: pds[PD_S].fd = s_server; michael@0: pds[PD_C].in_flags = PR_POLL_READ; michael@0: pds[PD_S].in_flags = PR_POLL_READ; michael@0: michael@0: /* make sure the new connections don't start out encrypted. */ michael@0: clientstream.isEncrypted = 0; michael@0: serverstream.isEncrypted = 0; michael@0: isV2Session = 0; michael@0: michael@0: while( (pds[PD_C].in_flags & PR_POLL_READ) != 0 || michael@0: (pds[PD_S].in_flags & PR_POLL_READ) != 0 ) michael@0: { /* Handle all messages on the connection */ michael@0: PRInt32 amt; michael@0: PRInt32 wrote; michael@0: unsigned char buffer[ TAPBUFSIZ ]; michael@0: michael@0: amt = PR_Poll(pds,2,PR_INTERVAL_NO_TIMEOUT); michael@0: if (amt <= 0) { michael@0: if (amt) michael@0: showErr( "PR_Poll failed.\n"); michael@0: else michael@0: showErr( "PR_Poll timed out.\n"); michael@0: break; michael@0: } michael@0: michael@0: if (pds[PD_C].out_flags & PR_POLL_EXCEPT) { michael@0: showErr( "Exception on client-side socket.\n"); michael@0: break; michael@0: } michael@0: michael@0: if (pds[PD_S].out_flags & PR_POLL_EXCEPT) { michael@0: showErr( "Exception on server-side socket.\n"); michael@0: break; michael@0: } michael@0: michael@0: michael@0: /* read data, copy it to stdout, and write to other socket */ michael@0: michael@0: if ((pds[PD_C].in_flags & PR_POLL_READ) != 0 && michael@0: (pds[PD_C].out_flags & PR_POLL_READ) != 0 ) { michael@0: michael@0: amt = PR_Read(s_client, buffer, sizeof(buffer)); michael@0: michael@0: if ( amt < 0) { michael@0: showErr( "Client socket read failed.\n"); michael@0: break; michael@0: } michael@0: michael@0: if( amt == 0 ) { michael@0: PR_fprintf(PR_STDOUT, "Read EOF on Client socket. [%s]\n", michael@0: get_time_string() ); michael@0: pds[PD_C].in_flags &= ~PR_POLL_READ; michael@0: PR_Shutdown(s_server, PR_SHUTDOWN_SEND); michael@0: continue; michael@0: } michael@0: michael@0: PR_fprintf(PR_STDOUT,"--> [\n"); michael@0: if (fancy) PR_fprintf(PR_STDOUT,""); michael@0: michael@0: if (hexparse) print_hex(amt, buffer); michael@0: if (sslparse) print_ssl(&clientstream,amt,buffer); michael@0: if (!hexparse && !sslparse) PR_Write(PR_STDOUT,buffer,amt); michael@0: if (fancy) PR_fprintf(PR_STDOUT,""); michael@0: PR_fprintf(PR_STDOUT,"]\n"); michael@0: michael@0: wrote = PR_Write(s_server, buffer, amt); michael@0: if (wrote != amt ) { michael@0: if (wrote < 0) { michael@0: showErr("Write to server socket failed.\n"); michael@0: break; michael@0: } else { michael@0: PR_fprintf(PR_STDERR, "Short write to server socket!\n"); michael@0: } michael@0: } michael@0: } /* end of read from client socket. */ michael@0: michael@0: /* read data, copy it to stdout, and write to other socket */ michael@0: if ((pds[PD_S].in_flags & PR_POLL_READ) != 0 && michael@0: (pds[PD_S].out_flags & PR_POLL_READ) != 0 ) { michael@0: michael@0: amt = PR_Read(s_server, buffer, sizeof(buffer)); michael@0: michael@0: if ( amt < 0) { michael@0: showErr( "error on server-side socket.\n"); michael@0: break; michael@0: } michael@0: michael@0: if( amt == 0 ) { michael@0: PR_fprintf(PR_STDOUT, "Read EOF on Server socket. [%s]\n", michael@0: get_time_string() ); michael@0: pds[PD_S].in_flags &= ~PR_POLL_READ; michael@0: PR_Shutdown(s_client, PR_SHUTDOWN_SEND); michael@0: continue; michael@0: } michael@0: michael@0: PR_fprintf(PR_STDOUT,"<-- [\n"); michael@0: if (fancy) PR_fprintf(PR_STDOUT,""); michael@0: if (hexparse) print_hex(amt, (unsigned char *)buffer); michael@0: if (sslparse) print_ssl(&serverstream,amt,(unsigned char *)buffer); michael@0: if (!hexparse && !sslparse) PR_Write(PR_STDOUT,buffer,amt); michael@0: if (fancy) PR_fprintf(PR_STDOUT,""); michael@0: PR_fprintf(PR_STDOUT,"]\n"); michael@0: michael@0: michael@0: wrote = PR_Write(s_client, buffer, amt); michael@0: if (wrote != amt ) { michael@0: if (wrote < 0) { michael@0: showErr("Write to client socket failed.\n"); michael@0: break; michael@0: } else { michael@0: PR_fprintf(PR_STDERR, "Short write to client socket!\n"); michael@0: } michael@0: } michael@0: michael@0: } /* end of read from server socket. */ michael@0: michael@0: /* Loop, handle next message. */ michael@0: michael@0: } /* handle messages during a connection loop */ michael@0: PR_Close(s_client); michael@0: PR_Close(s_server); michael@0: flush_stream(&clientstream); michael@0: flush_stream(&serverstream); michael@0: /* Connection is closed, so reset the current cipher */ michael@0: currentcipher = 0; michael@0: c_count++; michael@0: PR_fprintf(PR_STDERR,"Connection %d Complete [%s]\n", c_count, michael@0: get_time_string() ); michael@0: } while (looparound); /* accept connection and process it. */ michael@0: PR_Close(s_rend); michael@0: NSS_Shutdown(); michael@0: return 0; michael@0: }