diff options
Diffstat (limited to 'security/nss/cmd/ssltap')
-rw-r--r-- | security/nss/cmd/ssltap/Makefile | 51 | ||||
-rw-r--r-- | security/nss/cmd/ssltap/manifest.mn | 21 | ||||
-rw-r--r-- | security/nss/cmd/ssltap/ssltap-manual.html | 170 | ||||
-rw-r--r-- | security/nss/cmd/ssltap/ssltap.c | 2584 | ||||
-rw-r--r-- | security/nss/cmd/ssltap/ssltap.gyp | 25 |
5 files changed, 2851 insertions, 0 deletions
diff --git a/security/nss/cmd/ssltap/Makefile b/security/nss/cmd/ssltap/Makefile new file mode 100644 index 0000000000..bc7d35df4e --- /dev/null +++ b/security/nss/cmd/ssltap/Makefile @@ -0,0 +1,51 @@ +#! gmake +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +# Since ssltap doesn't use any of NSS, we'll skip NSS's link libs, +# and just link with NSPR. +# +include ../platlibs.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + + + +-include ../platrules.mk + diff --git a/security/nss/cmd/ssltap/manifest.mn b/security/nss/cmd/ssltap/manifest.mn new file mode 100644 index 0000000000..92302a1b8b --- /dev/null +++ b/security/nss/cmd/ssltap/manifest.mn @@ -0,0 +1,21 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +CORE_DEPTH = ../.. + +# MODULE public and private header directories are implicitly REQUIRED. +MODULE = nss + +CSRCS = ssltap.c \ + $(NULL) + +PROGRAM = ssltap + +REQUIRES = seccmd dbm + +PACKAGE_FILES = ssltap-manual.html licence.doc ssltap.exe + +ARCHIVE_NAME = ssltap + diff --git a/security/nss/cmd/ssltap/ssltap-manual.html b/security/nss/cmd/ssltap/ssltap-manual.html new file mode 100644 index 0000000000..619c93f8cd --- /dev/null +++ b/security/nss/cmd/ssltap/ssltap-manual.html @@ -0,0 +1,170 @@ +<HTML> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="GENERATOR" CONTENT="Mozilla/4.05 [en] (WinNT; U) [Netscape]"> + <META NAME="Author" CONTENT="Steve Parkinson"> + <TITLE>SSLTap - manual</TITLE> +</HEAD> +<BODY> + +<H1> +SSLTap Manual page</H1> + +<H3> +Summary</H3> +A command-line proxy which is SSL-aware. It snoops on TCP connections, +and displays the data going by, including SSL records and handshaking +if the connection is SSL. +<H3> +Synopsis</H3> +<TT>ssltap [-vhfsxl] [-p port] hostname:port</TT> + +<P><TT> -v [prints version string]</TT> +<BR><TT> -h [outputs hex instead +of ASCII]</TT> +<BR><TT> -f [turn on Fancy HTML +coloring]</TT> +<BR><TT> -s [turn on SSL decoding]</TT> +<BR><TT> -x [turn on extra SSL +hex dumps]</TT> +<BR><TT> -p port [specify rendezvous port (default 1924)]</TT> +<BR><TT> -l [loop - continue +to wait for more connections]</TT> +<H3> +Description</H3> +SSLTap opens a socket on a rendezvous port, and waits for an incoming connection +(client side). Once this connection arrives, SSLTap makes another connection +to hostname:port (server side). It passes any data sent by the client to +the server, and vice versa. However, SSLTap will also display the data +to the console. It can do this for plain HTTP connections, or any TCP protocol. +However, SSLTap can also work with SSL streams, as detailed below. + +<P>Let's assume your development machine is called 'intercept'. The simplest +usage of SSLTap is to run the command <TT>'ssltap www.netscape.com:80'</TT> +on intercept. The program will wait for an incoming connection on port +1924. Next you would want to go to your browser, and enter the URL http://intercept:1924. +The page retrieved by the browser will actually be gotten from the server +at www.netscape.com, but will go via SSLTap. + +<P>Data sent from the client to the server is surrounded by a '--> [ ]' +symbol, and data sent from the server to the client, a '<---[ +]' symbol. + +<P>You'll notice that the page retrieved with this example looks incomplete. +This is because SSLTap by default closes down after the first connection +is complete, so the browser is not able to load images. To make the SSLTap +continue to accept connections, switch on looping mode with the -l option. + +<P>You can change the default rendezvous port to something else with the +-p option. + +<P>The remaining options change the way the output is produced. + +<P>The -f option prints 'fancy' output - in colored HTML. Data sent from +the client to the server is in blue. The server's reply is in red. This +is designed so you can load the output up into a browser. When used with +looping mode, the different connections are separated with horizontal lines. + +<P>-x will turn on HEX printing. Instead of being output as ascii, the +data is shown as Hex, like this: +<UL><TT><-- [</TT> +<BR><TT> 0: 56 d5 16 3e a1 6b b1 4a 8f 67 c4 d7 +21 2f 6f dd | V..>.k.J.g..!/o.</TT> +<BR><TT> 10: bb 22 c4 75 8c f4 ce 28 16 a6 20 aa +fb 9a 59 a1 | .".u...(.. ...Y.</TT> +<BR><TT> 20: 51 91 14 d2 fc 9f a7 ea 4d 9c f7 3a +9d 83 62 4a | Q.......M..:..bJ</TT> +<BR><TT>]</TT> +<BR> </UL> + +<H4> +SSL Parse mode</H4> +The following options deal with SSL connections. +<UL>-s will turn on SSL parsing. (SSLTap doesn't automatically detect SSL +sessions.) +<BR>-x will turn on extra SSL hexdumps. Mostly, if SSL can decode the data, +it doesn't display the hex.</UL> +The following SSL3 Data structures are parsed: Handshake, ClientHello, +ServerHello, CertificateChain, Certificate. In addition, SSL2 ClientHello, +ServerHello, ClientMasterKey are also partly parsed. NO DECRYPTION IS PERFORMED +ON THE DATA. SSLTAP CANNOT DECRYPT the data. + +<P>If a certificate chain is detected, DER-encoded certificates will be +saved into files in the current directory called 'cert.0x' where x is the +sequence number of the certificate. +<BR> +<H3> +Operation Hints</H3> +Often, you'll find that the server certificate does not get transferred, +or other parts of the handshake do not happen. This is because the browser +is taking advantage of session-id-reuse (using the handshake results from +a previous session). If you restart the browser, it'll clear the session +id cache. + +<P>If you run the ssltap on a different machine that the ssl server you're +trying to connect to, the browser will complain that the host name you're +trying to connect to is different to the certificate, but it will still +let you connect, after showing you a dialog. +<H3> +Bugs</H3> +Please contact <A HREF="mailto:ssltap-support@netscape.com">ssltap-support@netscape.com</A> +for bug reports. +<H3> +History</H3> +2.1 - First public release (March 1998) +<BR> +<H3> +Other</H3> +For reference, here is a table of some well-known port numbers: +<BR> +<TABLE BORDER=2 > +<TR> +<TD>HTTP</TD> + +<TD>80</TD> +</TR> + +<TR> +<TD>SMTP</TD> + +<TD>25</TD> +</TR> + +<TR> +<TD>HTTPS</TD> + +<TD>443</TD> +</TR> + +<TR> +<TD>FTP</TD> + +<TD>21</TD> +</TR> + +<TR> +<TD>IMAPS</TD> + +<TD>993</TD> +</TR> + +<TR> +<TD>NNTP</TD> + +<TD>119</TD> +</TR> + +<TR> +<TD>NNTPS</TD> + +<TD>563</TD> +</TR> +</TABLE> + + +<P> +</BODY> +</HTML> diff --git a/security/nss/cmd/ssltap/ssltap.c b/security/nss/cmd/ssltap/ssltap.c new file mode 100644 index 0000000000..0a9ec11eec --- /dev/null +++ b/security/nss/cmd/ssltap/ssltap.c @@ -0,0 +1,2584 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * ssltap.c + * + * Version 1.0 : Frederick Roeber : 11 June 1997 + * Version 2.0 : Steve Parkinson : 13 November 1997 + * Version 3.0 : Nelson Bolyard : 22 July 1998 + * Version 3.1 : Nelson Bolyard : 24 May 1999 + * + * changes in version 2.0: + * Uses NSPR20 + * Shows structure of SSL negotiation, if enabled. + * + * This "proxies" a socket connection (like a socks tunnel), but displays the + * data is it flies by. + * + * In the code, the 'client' socket is the one on the client side of the + * proxy, and the server socket is on the server side. + * + */ + +#include "nspr.h" +#include "plstr.h" +#include "secutil.h" +#include <memory.h> /* for memcpy, etc. */ +#include <string.h> +#include <time.h> + +#include "plgetopt.h" +#include "nss.h" +#include "cert.h" +#include "sslproto.h" +#include "ocsp.h" +#include "ocspti.h" /* internals for pretty-printing routines *only* */ + +struct _DataBufferList; +struct _DataBuffer; + +typedef struct _DataBufferList { + struct _DataBuffer *first, *last; + unsigned int size; + int isEncrypted; + unsigned char *msgBuf; + unsigned int msgBufOffset; + unsigned int msgBufSize; + unsigned int hMACsize; +} DataBufferList; + +typedef struct _DataBuffer { + unsigned char *buffer; + int length; + int offset; /* offset of first good byte */ + struct _DataBuffer *next; +} DataBuffer; + +struct sslhandshake { + PRUint8 type; + PRUint32 length; +}; + +typedef struct _SSLRecord { + PRUint8 type; + PRUint8 ver_maj, ver_min; + + PRUint8 length[2]; +} SSLRecord; + +typedef struct _ClientHelloV2 { + PRUint8 length[2]; + PRUint8 type; + PRUint8 version[2]; + PRUint8 cslength[2]; + PRUint8 sidlength[2]; + PRUint8 rndlength[2]; + PRUint8 csuites[1]; +} ClientHelloV2; + +typedef struct _ServerHelloV2 { + PRUint8 length[2]; + PRUint8 type; + PRUint8 sidhit; + PRUint8 certtype; + PRUint8 version[2]; + PRUint8 certlength[2]; + PRUint8 cslength[2]; + PRUint8 cidlength[2]; +} ServerHelloV2; + +typedef struct _ClientMasterKeyV2 { + PRUint8 length[2]; + PRUint8 type; + + PRUint8 cipherkind[3]; + PRUint8 clearkey[2]; + PRUint8 secretkey[2]; + +} ClientMasterKeyV2; + +/* forward declaration */ +void showErr(const char *msg); + +#define TAPBUFSIZ 16384 + +#define DEFPORT 1924 +#include <ctype.h> + +const char *progName; +int hexparse = 0; +int sslparse = 0; +int sslhexparse = 0; +int looparound = 0; +int fancy = 0; +int isV2Session = 0; +int currentcipher = 0; +DataBufferList clientstream, serverstream; + +#define PR_FPUTS(x) PR_fprintf(PR_STDOUT, x) + +#define GET_SHORT(x) ((PRUint16)(((PRUint16)((PRUint8 *)x)[0]) << 8) + ((PRUint16)((PRUint8 *)x)[1])) +#define GET_24(x) ((PRUint32)( \ + (((PRUint32)((PRUint8 *)x)[0]) << 16) + \ + (((PRUint32)((PRUint8 *)x)[1]) << 8) + \ + (((PRUint32)((PRUint8 *)x)[2]) << 0))) +#define GET_32(x) ((PRUint32)( \ + (((PRUint32)((PRUint8 *)x)[0]) << 24) + \ + (((PRUint32)((PRUint8 *)x)[1]) << 16) + \ + (((PRUint32)((PRUint8 *)x)[2]) << 8) + \ + (((PRUint32)((PRUint8 *)x)[3]) << 0))) + +void print_hex(int amt, unsigned char *buf); +void read_stream_bytes(unsigned char *d, DataBufferList *db, int length); + +void +myhalt(int dblsize, int collectedsize) +{ + + PR_fprintf(PR_STDERR, "HALTED\n"); + PR_ASSERT(dblsize == collectedsize); + exit(13); +} + +const char * +get_error_text(int error) +{ + switch (error) { + case PR_IO_TIMEOUT_ERROR: + return "Timeout"; + break; + case PR_CONNECT_REFUSED_ERROR: + return "Connection refused"; + break; + case PR_NETWORK_UNREACHABLE_ERROR: + return "Network unreachable"; + break; + case PR_BAD_ADDRESS_ERROR: + return "Bad address"; + break; + case PR_CONNECT_RESET_ERROR: + return "Connection reset"; + break; + case PR_PIPE_ERROR: + return "Pipe error"; + break; + } + + return ""; +} + +void +check_integrity(DataBufferList *dbl) +{ + DataBuffer *db; + int i; + + db = dbl->first; + i = 0; + while (db) { + i += db->length - db->offset; + db = db->next; + } + if (i != dbl->size) { + myhalt(dbl->size, i); + } +} + +/* Free's the DataBuffer at the head of the list and returns the pointer + * to the new head of the list. + */ +DataBuffer * +free_head(DataBufferList *dbl) +{ + DataBuffer *db = dbl->first; + PR_ASSERT(db->offset >= db->length); + if (db->offset >= db->length) { + dbl->first = db->next; + if (dbl->first == NULL) { + dbl->last = NULL; + } + PORT_Free(db->buffer); + PORT_Free(db); + db = dbl->first; + } + return db; +} + +void +read_stream_bytes(unsigned char *d, DataBufferList *dbl, int length) +{ + int copied = 0; + DataBuffer *db = dbl->first; + + if (!db) { + PR_fprintf(PR_STDERR, "assert failed - dbl->first is null\n"); + exit(8); + } + while (length) { + int toCopy; + /* find the number of bytes to copy from the head buffer */ + /* if there's too many in this buffer, then only copy 'length' */ + toCopy = PR_MIN(db->length - db->offset, length); + + memcpy(d + copied, db->buffer + db->offset, toCopy); + copied += toCopy; + db->offset += toCopy; + length -= toCopy; + dbl->size -= toCopy; + + /* if we emptied the head buffer */ + if (db->offset >= db->length) { + db = free_head(dbl); + } + } + + check_integrity(dbl); +} + +void +flush_stream(DataBufferList *dbl) +{ + DataBuffer *db = dbl->first; + check_integrity(dbl); + while (db) { + db->offset = db->length; + db = free_head(dbl); + } + dbl->size = 0; + check_integrity(dbl); + if (dbl->msgBuf) { + PORT_Free(dbl->msgBuf); + dbl->msgBuf = NULL; + } + dbl->msgBufOffset = 0; + dbl->msgBufSize = 0; + dbl->hMACsize = 0; +} + +const char * +V2CipherString(int cs_int) +{ + char *cs_str; + cs_str = NULL; + switch (cs_int) { + + case 0x010080: + cs_str = "SSL2/RSA/RC4-128/MD5"; + break; + case 0x020080: + cs_str = "SSL2/RSA/RC4-40/MD5"; + break; + case 0x030080: + cs_str = "SSL2/RSA/RC2CBC128/MD5"; + break; + case 0x040080: + cs_str = "SSL2/RSA/RC2CBC40/MD5"; + break; + case 0x050080: + cs_str = "SSL2/RSA/IDEA128CBC/MD5"; + break; + case 0x060040: + cs_str = "SSL2/RSA/DES56-CBC/MD5"; + break; + case 0x0700C0: + cs_str = "SSL2/RSA/3DES192EDE-CBC/MD5"; + break; + + case 0x000001: + cs_str = "SSL3/RSA/NULL/MD5"; + break; + case 0x000002: + cs_str = "SSL3/RSA/NULL/SHA"; + break; + case 0x000003: + cs_str = "SSL3/RSA/RC4-40/MD5"; + break; + case 0x000004: + cs_str = "SSL3/RSA/RC4-128/MD5"; + break; + case 0x000005: + cs_str = "SSL3/RSA/RC4-128/SHA"; + break; + case 0x000006: + cs_str = "SSL3/RSA/RC2CBC40/MD5"; + break; + case 0x000007: + cs_str = "SSL3/RSA/IDEA128CBC/SHA"; + break; + case 0x000008: + cs_str = "SSL3/RSA/DES40-CBC/SHA"; + break; + case 0x000009: + cs_str = "SSL3/RSA/DES56-CBC/SHA"; + break; + case 0x00000A: + cs_str = "SSL3/RSA/3DES192EDE-CBC/SHA"; + break; + + case 0x00000B: + cs_str = "SSL3/DH-DSS/DES40-CBC/SHA"; + break; + case 0x00000C: + cs_str = "SSL3/DH-DSS/DES56-CBC/SHA"; + break; + case 0x00000D: + cs_str = "SSL3/DH-DSS/DES192EDE3CBC/SHA"; + break; + case 0x00000E: + cs_str = "SSL3/DH-RSA/DES40-CBC/SHA"; + break; + case 0x00000F: + cs_str = "SSL3/DH-RSA/DES56-CBC/SHA"; + break; + case 0x000010: + cs_str = "SSL3/DH-RSA/3DES192EDE-CBC/SHA"; + break; + + case 0x000011: + cs_str = "SSL3/DHE-DSS/DES40-CBC/SHA"; + break; + case 0x000012: + cs_str = "SSL3/DHE-DSS/DES56-CBC/SHA"; + break; + case 0x000013: + cs_str = "SSL3/DHE-DSS/DES192EDE3CBC/SHA"; + break; + case 0x000014: + cs_str = "SSL3/DHE-RSA/DES40-CBC/SHA"; + break; + case 0x000015: + cs_str = "SSL3/DHE-RSA/DES56-CBC/SHA"; + break; + case 0x000016: + cs_str = "SSL3/DHE-RSA/3DES192EDE-CBC/SHA"; + break; + + case 0x000017: + cs_str = "SSL3/DH-anon/RC4-40/MD5"; + break; + case 0x000018: + cs_str = "SSL3/DH-anon/RC4-128/MD5"; + break; + case 0x000019: + cs_str = "SSL3/DH-anon/DES40-CBC/SHA"; + break; + case 0x00001A: + cs_str = "SSL3/DH-anon/DES56-CBC/SHA"; + break; + case 0x00001B: + cs_str = "SSL3/DH-anon/3DES192EDE-CBC/SHA"; + break; + + case 0x00001C: + cs_str = "SSL3/FORTEZZA-DMS/NULL/SHA"; + break; + case 0x00001D: + cs_str = "SSL3/FORTEZZA-DMS/FORTEZZA-CBC/SHA"; + break; + case 0x00001E: + cs_str = "SSL3/FORTEZZA-DMS/RC4-128/SHA"; + break; + + case 0x00002F: + cs_str = "TLS/RSA/AES128-CBC/SHA"; + break; + case 0x000030: + cs_str = "TLS/DH-DSS/AES128-CBC/SHA"; + break; + case 0x000031: + cs_str = "TLS/DH-RSA/AES128-CBC/SHA"; + break; + case 0x000032: + cs_str = "TLS/DHE-DSS/AES128-CBC/SHA"; + break; + case 0x000033: + cs_str = "TLS/DHE-RSA/AES128-CBC/SHA"; + break; + case 0x000034: + cs_str = "TLS/DH-ANON/AES128-CBC/SHA"; + break; + + case 0x000035: + cs_str = "TLS/RSA/AES256-CBC/SHA"; + break; + case 0x000036: + cs_str = "TLS/DH-DSS/AES256-CBC/SHA"; + break; + case 0x000037: + cs_str = "TLS/DH-RSA/AES256-CBC/SHA"; + break; + case 0x000038: + cs_str = "TLS/DHE-DSS/AES256-CBC/SHA"; + break; + case 0x000039: + cs_str = "TLS/DHE-RSA/AES256-CBC/SHA"; + break; + case 0x00003A: + cs_str = "TLS/DH-ANON/AES256-CBC/SHA"; + break; + + case 0x00003B: + cs_str = "TLS/RSA/NULL/SHA256"; + break; + case 0x00003C: + cs_str = "TLS/RSA/AES128-CBC/SHA256"; + break; + case 0x00003D: + cs_str = "TLS/RSA/AES256-CBC/SHA256"; + break; + case 0x00003E: + cs_str = "TLS/DH-DSS/AES128-CBC/SHA256"; + break; + case 0x00003F: + cs_str = "TLS/DH-RSA/AES128-CBC/SHA256"; + break; + case 0x000040: + cs_str = "TLS/DHE-DSS/AES128-CBC/SHA256"; + break; + + case 0x000041: + cs_str = "TLS/RSA/CAMELLIA128-CBC/SHA"; + break; + case 0x000042: + cs_str = "TLS/DH-DSS/CAMELLIA128-CBC/SHA"; + break; + case 0x000043: + cs_str = "TLS/DH-RSA/CAMELLIA128-CBC/SHA"; + break; + case 0x000044: + cs_str = "TLS/DHE-DSS/CAMELLIA128-CBC/SHA"; + break; + case 0x000045: + cs_str = "TLS/DHE-RSA/CAMELLIA128-CBC/SHA"; + break; + case 0x000046: + cs_str = "TLS/DH-ANON/CAMELLIA128-CBC/SHA"; + break; + + case 0x000060: + cs_str = "TLS/RSA-EXPORT1024/RC4-56/MD5"; + break; + case 0x000061: + cs_str = "TLS/RSA-EXPORT1024/RC2CBC56/MD5"; + break; + case 0x000062: + cs_str = "TLS/RSA-EXPORT1024/DES56-CBC/SHA"; + break; + case 0x000064: + cs_str = "TLS/RSA-EXPORT1024/RC4-56/SHA"; + break; + case 0x000063: + cs_str = "TLS/DHE-DSS_EXPORT1024/DES56-CBC/SHA"; + break; + case 0x000065: + cs_str = "TLS/DHE-DSS_EXPORT1024/RC4-56/SHA"; + break; + case 0x000066: + cs_str = "TLS/DHE-DSS/RC4-128/SHA"; + break; + + case 0x000067: + cs_str = "TLS/DHE-RSA/AES128-CBC/SHA256"; + break; + case 0x000068: + cs_str = "TLS/DH-DSS/AES256-CBC/SHA256"; + break; + case 0x000069: + cs_str = "TLS/DH-RSA/AES256-CBC/SHA256"; + break; + case 0x00006A: + cs_str = "TLS/DHE-DSS/AES256-CBC/SHA256"; + break; + case 0x00006B: + cs_str = "TLS/DHE-RSA/AES256-CBC/SHA256"; + break; + + case 0x000072: + cs_str = "TLS/DHE-DSS/3DESEDE-CBC/RMD160"; + break; + case 0x000073: + cs_str = "TLS/DHE-DSS/AES128-CBC/RMD160"; + break; + case 0x000074: + cs_str = "TLS/DHE-DSS/AES256-CBC/RMD160"; + break; + + case 0x000079: + cs_str = "TLS/DHE-RSA/AES256-CBC/RMD160"; + break; + + case 0x00007C: + cs_str = "TLS/RSA/3DESEDE-CBC/RMD160"; + break; + case 0x00007D: + cs_str = "TLS/RSA/AES128-CBC/RMD160"; + break; + case 0x00007E: + cs_str = "TLS/RSA/AES256-CBC/RMD160"; + break; + + case 0x000080: + cs_str = "TLS/GOST341094/GOST28147-OFB/GOST28147"; + break; + case 0x000081: + cs_str = "TLS/GOST34102001/GOST28147-OFB/GOST28147"; + break; + case 0x000082: + cs_str = "TLS/GOST341094/NULL/GOSTR3411"; + break; + case 0x000083: + cs_str = "TLS/GOST34102001/NULL/GOSTR3411"; + break; + + case 0x000084: + cs_str = "TLS/RSA/CAMELLIA256-CBC/SHA"; + break; + case 0x000085: + cs_str = "TLS/DH-DSS/CAMELLIA256-CBC/SHA"; + break; + case 0x000086: + cs_str = "TLS/DH-RSA/CAMELLIA256-CBC/SHA"; + break; + case 0x000087: + cs_str = "TLS/DHE-DSS/CAMELLIA256-CBC/SHA"; + break; + case 0x000088: + cs_str = "TLS/DHE-RSA/CAMELLIA256-CBC/SHA"; + break; + case 0x000089: + cs_str = "TLS/DH-ANON/CAMELLIA256-CBC/SHA"; + break; + case 0x00008A: + cs_str = "TLS/PSK/RC4-128/SHA"; + break; + case 0x00008B: + cs_str = "TLS/PSK/3DES-EDE-CBC/SHA"; + break; + case 0x00008C: + cs_str = "TLS/PSK/AES128-CBC/SHA"; + break; + case 0x00008D: + cs_str = "TLS/PSK/AES256-CBC/SHA"; + break; + case 0x00008E: + cs_str = "TLS/DHE-PSK/RC4-128/SHA"; + break; + case 0x00008F: + cs_str = "TLS/DHE-PSK/3DES-EDE-CBC/SHA"; + break; + case 0x000090: + cs_str = "TLS/DHE-PSK/AES128-CBC/SHA"; + break; + case 0x000091: + cs_str = "TLS/DHE-PSK/AES256-CBC/SHA"; + break; + case 0x000092: + cs_str = "TLS/RSA-PSK/RC4-128/SHA"; + break; + case 0x000093: + cs_str = "TLS/RSA-PSK/3DES-EDE-CBC/SHA"; + break; + case 0x000094: + cs_str = "TLS/RSA-PSK/AES128-CBC/SHA"; + break; + case 0x000095: + cs_str = "TLS/RSA-PSK/AES256-CBC/SHA"; + break; + case 0x000096: + cs_str = "TLS/RSA/SEED-CBC/SHA"; + break; + case 0x000097: + cs_str = "TLS/DH-DSS/SEED-CBC/SHA"; + break; + case 0x000098: + cs_str = "TLS/DH-RSA/SEED-CBC/SHA"; + break; + case 0x000099: + cs_str = "TLS/DHE-DSS/SEED-CBC/SHA"; + break; + case 0x00009A: + cs_str = "TLS/DHE-RSA/SEED-CBC/SHA"; + break; + case 0x00009B: + cs_str = "TLS/DH-ANON/SEED-CBC/SHA"; + break; + case 0x00009C: + cs_str = "TLS/RSA/AES128-GCM/SHA256"; + break; + case 0x00009E: + cs_str = "TLS/DHE-RSA/AES128-GCM/SHA256"; + break; + + case 0x0000FF: + cs_str = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"; + break; + case 0x005600: + cs_str = "TLS_FALLBACK_SCSV"; + break; + + case 0x00C001: + cs_str = "TLS/ECDH-ECDSA/NULL/SHA"; + break; + case 0x00C002: + cs_str = "TLS/ECDH-ECDSA/RC4-128/SHA"; + break; + case 0x00C003: + cs_str = "TLS/ECDH-ECDSA/3DES-EDE-CBC/SHA"; + break; + case 0x00C004: + cs_str = "TLS/ECDH-ECDSA/AES128-CBC/SHA"; + break; + case 0x00C005: + cs_str = "TLS/ECDH-ECDSA/AES256-CBC/SHA"; + break; + case 0x00C006: + cs_str = "TLS/ECDHE-ECDSA/NULL/SHA"; + break; + case 0x00C007: + cs_str = "TLS/ECDHE-ECDSA/RC4-128/SHA"; + break; + case 0x00C008: + cs_str = "TLS/ECDHE-ECDSA/3DES-EDE-CBC/SHA"; + break; + case 0x00C009: + cs_str = "TLS/ECDHE-ECDSA/AES128-CBC/SHA"; + break; + case 0x00C00A: + cs_str = "TLS/ECDHE-ECDSA/AES256-CBC/SHA"; + break; + case 0x00C00B: + cs_str = "TLS/ECDH-RSA/NULL/SHA"; + break; + case 0x00C00C: + cs_str = "TLS/ECDH-RSA/RC4-128/SHA"; + break; + case 0x00C00D: + cs_str = "TLS/ECDH-RSA/3DES-EDE-CBC/SHA"; + break; + case 0x00C00E: + cs_str = "TLS/ECDH-RSA/AES128-CBC/SHA"; + break; + case 0x00C00F: + cs_str = "TLS/ECDH-RSA/AES256-CBC/SHA"; + break; + case 0x00C010: + cs_str = "TLS/ECDHE-RSA/NULL/SHA"; + break; + case 0x00C011: + cs_str = "TLS/ECDHE-RSA/RC4-128/SHA"; + break; + case 0x00C012: + cs_str = "TLS/ECDHE-RSA/3DES-EDE-CBC/SHA"; + break; + case 0x00C013: + cs_str = "TLS/ECDHE-RSA/AES128-CBC/SHA"; + break; + case 0x00C014: + cs_str = "TLS/ECDHE-RSA/AES256-CBC/SHA"; + break; + case 0x00C015: + cs_str = "TLS/ECDH-anon/NULL/SHA"; + break; + case 0x00C016: + cs_str = "TLS/ECDH-anon/RC4-128/SHA"; + break; + case 0x00C017: + cs_str = "TLS/ECDH-anon/3DES-EDE-CBC/SHA"; + break; + case 0x00C018: + cs_str = "TLS/ECDH-anon/AES128-CBC/SHA"; + break; + case 0x00C019: + cs_str = "TLS/ECDH-anon/AES256-CBC/SHA"; + break; + + case 0x00C023: + cs_str = "TLS/ECDHE-ECDSA/AES128-CBC/SHA256"; + break; + case 0x00C024: + cs_str = "TLS/ECDHE-ECDSA/AES256-CBC/SHA384"; + break; + case 0x00C025: + cs_str = "TLS/ECDH-ECDSA/AES128-CBC/SHA256"; + break; + case 0x00C026: + cs_str = "TLS/ECDH-ECDSA/AES256-CBC/SHA384"; + break; + case 0x00C027: + cs_str = "TLS/ECDHE-RSA/AES128-CBC/SHA256"; + break; + case 0x00C028: + cs_str = "TLS/ECDHE-RSA/AES256-CBC/SHA384"; + break; + case 0x00C029: + cs_str = "TLS/ECDH-RSA/AES128-CBC/SHA256"; + break; + case 0x00C02A: + cs_str = "TLS/ECDH-RSA/AES256-CBC/SHA384"; + break; + case 0x00C02B: + cs_str = "TLS/ECDHE-ECDSA/AES128-GCM/SHA256"; + break; + case 0x00C02C: + cs_str = "TLS/ECDHE-ECDSA/AES256-GCM/SHA384"; + break; + case 0x00C02F: + cs_str = "TLS/ECDHE-RSA/AES128-GCM/SHA256"; + break; + + case 0x00CCA8: + cs_str = "TLS/ECDHE-RSA/CHACHA20-POLY1305/SHA256"; + break; + case 0x00CCA9: + cs_str = "TLS/ECDHE-ECDSA/CHACHA20-POLY1305/SHA256"; + break; + case 0x00CCAA: + cs_str = "TLS/DHE-RSA/CHACHA20-POLY1305/SHA256"; + break; + + case 0x00FEFF: + cs_str = "SSL3/RSA-FIPS/3DESEDE-CBC/SHA"; + break; + case 0x00FEFE: + cs_str = "SSL3/RSA-FIPS/DES-CBC/SHA"; + break; + case 0x00FFE1: + cs_str = "SSL3/RSA-FIPS/DES56-CBC/SHA"; + break; + case 0x00FFE0: + cs_str = "SSL3/RSA-FIPS/3DES192EDE-CBC/SHA"; + break; + + /* the string literal is broken up to avoid trigraphs */ + default: + cs_str = "????" + "/????????" + "/?????????" + "/???"; + break; + } + + return cs_str; +} + +const char * +CompressionMethodString(int cm_int) +{ + char *cm_str; + cm_str = NULL; + switch (cm_int) { + case 0: + cm_str = "NULL"; + break; + case 1: + cm_str = "DEFLATE"; + break; /* RFC 3749 */ + case 64: + cm_str = "LZS"; + break; /* RFC 3943 */ + default: + cm_str = "???"; + break; + } + + return cm_str; +} + +const char * +helloExtensionNameString(int ex_num) +{ + const char *ex_name = NULL; + static char buf[10]; + + switch (ex_num) { + case 0: + ex_name = "server_name"; + break; + case 1: + ex_name = "max_fragment_length"; + break; + case 2: + ex_name = "client_certificate_url"; + break; + case 3: + ex_name = "trusted_ca_keys"; + break; + case 4: + ex_name = "truncated_hmac"; + break; + case 5: + ex_name = "status_request"; + break; + case 10: + ex_name = "elliptic_curves"; + break; + case 11: + ex_name = "ec_point_formats"; + break; + case 13: + ex_name = "signature_algorithms"; + break; + case 35: + ex_name = "session_ticket"; + break; + case 0xff01: + ex_name = "renegotiation_info"; + break; + default: + snprintf(buf, sizeof(buf), "%d", ex_num); + ex_name = (const char *)buf; + break; + } + + return ex_name; +} + +static int +isNULLmac(int cs_int) +{ + return (cs_int == TLS_NULL_WITH_NULL_NULL); +} + +static int +isNULLcipher(int cs_int) +{ + return ((cs_int == TLS_RSA_WITH_NULL_MD5) || + (cs_int == TLS_RSA_WITH_NULL_SHA) || + (cs_int == SSL_FORTEZZA_DMS_WITH_NULL_SHA) || + (cs_int == TLS_ECDH_ECDSA_WITH_NULL_SHA) || + (cs_int == TLS_ECDHE_ECDSA_WITH_NULL_SHA) || + (cs_int == TLS_ECDH_RSA_WITH_NULL_SHA) || + (cs_int == TLS_ECDHE_RSA_WITH_NULL_SHA)); +} + +void +partial_packet(int thispacket, int size, int needed) +{ + PR_fprintf(PR_STDOUT, "(%u bytes", thispacket); + if (thispacket < needed) { + PR_fprintf(PR_STDOUT, ", making %u", size); + } + PR_fprintf(PR_STDOUT, " of %u", needed); + if (size > needed) { + PR_fprintf(PR_STDOUT, ", with %u left over", size - needed); + } + PR_fprintf(PR_STDOUT, ")\n"); +} + +char * +get_time_string(void) +{ + char *cp; + char *eol; + time_t tt; + + time(&tt); + cp = ctime(&tt); + eol = strchr(cp, '\n'); + if (eol) + *eol = 0; + return cp; +} + +void +print_sslv2(DataBufferList *s, unsigned char *recordBuf, unsigned int recordLen) +{ + ClientHelloV2 *chv2; + ServerHelloV2 *shv2; + unsigned char *pos; + unsigned int p; + unsigned int q; + PRUint32 len; + + chv2 = (ClientHelloV2 *)recordBuf; + shv2 = (ServerHelloV2 *)recordBuf; + if (s->isEncrypted) { + PR_fprintf(PR_STDOUT, " [ssl2] Encrypted {...}\n"); + return; + } + PR_fprintf(PR_STDOUT, " [%s]", get_time_string()); + switch (chv2->type) { + case 1: + PR_fprintf(PR_STDOUT, " [ssl2] ClientHelloV2 {\n"); + PR_fprintf(PR_STDOUT, " version = {0x%02x, 0x%02x}\n", + (PRUint32)chv2->version[0], (PRUint32)chv2->version[1]); + PR_fprintf(PR_STDOUT, " cipher-specs-length = %d (0x%02x)\n", + (PRUint32)(GET_SHORT((chv2->cslength))), + (PRUint32)(GET_SHORT((chv2->cslength)))); + PR_fprintf(PR_STDOUT, " sid-length = %d (0x%02x)\n", + (PRUint32)(GET_SHORT((chv2->sidlength))), + (PRUint32)(GET_SHORT((chv2->sidlength)))); + PR_fprintf(PR_STDOUT, " challenge-length = %d (0x%02x)\n", + (PRUint32)(GET_SHORT((chv2->rndlength))), + (PRUint32)(GET_SHORT((chv2->rndlength)))); + PR_fprintf(PR_STDOUT, " cipher-suites = { \n"); + for (p = + 0; + p < (PRUint32)GET_SHORT((chv2->cslength)); p += 3) { + PRUint32 cs_int = GET_24((&chv2->csuites[p])); + const char *cs_str = + V2CipherString(cs_int); + + PR_fprintf(PR_STDOUT, " (0x%06x) %s\n", + cs_int, cs_str); + } + q = p; + PR_fprintf(PR_STDOUT, " }\n"); + if (GET_SHORT((chv2->sidlength))) { + PR_fprintf(PR_STDOUT, " session-id = { "); + for (p = 0; + p < (PRUint32)GET_SHORT((chv2->sidlength)); p += 2) { + PR_fprintf(PR_STDOUT, "0x%04x ", (PRUint32)(GET_SHORT((&chv2->csuites[p + q])))); + } + } + q += p; + PR_fprintf(PR_STDOUT, "}\n"); + if (GET_SHORT((chv2->rndlength))) { + PR_fprintf(PR_STDOUT, " challenge = { "); + for (p = 0; + p < (PRUint32)GET_SHORT((chv2->rndlength)); p += 2) { + PR_fprintf(PR_STDOUT, "0x%04x ", (PRUint32)(GET_SHORT((&chv2->csuites[p + q])))); + } + PR_fprintf(PR_STDOUT, "}\n"); + } + PR_fprintf(PR_STDOUT, "}\n"); + break; + /* end of V2 CLientHello Parsing */ + + case 2: /* Client Master Key */ + { + const char *cs_str = + NULL; + PRUint32 cs_int = + 0; + ClientMasterKeyV2 *cmkv2; + cmkv2 = (ClientMasterKeyV2 *)chv2; + isV2Session = 1; + + PR_fprintf(PR_STDOUT, " [ssl2] ClientMasterKeyV2 { \n"); + + cs_int = GET_24(&cmkv2->cipherkind[0]); + cs_str = V2CipherString(cs_int); + PR_fprintf(PR_STDOUT, " cipher-spec-chosen = (0x%06x) %s\n", + cs_int, cs_str); + + PR_fprintf(PR_STDOUT, " clear-portion = %d bits\n", + 8 * + (PRUint32)(GET_SHORT((cmkv2->clearkey)))); + + PR_fprintf(PR_STDOUT, " }\n"); + clientstream.isEncrypted = 1; + serverstream.isEncrypted = 1; + } break; + + case 3: + PR_fprintf(PR_STDOUT, " [ssl2] Client Finished V2 {...}\n"); + isV2Session = 1; + break; + + case 4: /* V2 Server Hello */ + isV2Session = 1; + + PR_fprintf(PR_STDOUT, " [ssl2] ServerHelloV2 {\n"); + PR_fprintf(PR_STDOUT, " sid hit = {0x%02x}\n", + (PRUintn)shv2->sidhit); + PR_fprintf(PR_STDOUT, " version = {0x%02x, 0x%02x}\n", + (PRUint32)shv2->version[0], (PRUint32)shv2->version[1]); + PR_fprintf(PR_STDOUT, " cipher-specs-length = %d (0x%02x)\n", + (PRUint32)(GET_SHORT((shv2->cslength))), + (PRUint32)(GET_SHORT((shv2->cslength)))); + PR_fprintf(PR_STDOUT, " sid-length = %d (0x%02x)\n", + (PRUint32)(GET_SHORT((shv2->cidlength))), + (PRUint32)(GET_SHORT((shv2->cidlength)))); + + pos = (unsigned char *)shv2; + pos += 2; /* skip length header */ + pos += 11; /* position pointer to Certificate data area */ + q = GET_SHORT(&shv2->certlength); + if (q > recordLen) { + goto eosh; + } + pos += q; /* skip certificate */ + + PR_fprintf(PR_STDOUT, " cipher-suites = { "); + len = GET_SHORT((shv2->cslength)); + for (p = 0; p < len; p += 3) { + PRUint32 cs_int = GET_24((pos + p)); + const char *cs_str = + V2CipherString(cs_int); + PR_fprintf(PR_STDOUT, "\n "); + PR_fprintf(PR_STDOUT, "(0x%06x) %s", cs_int, cs_str); + } + pos += len; + PR_fprintf(PR_STDOUT, " }\n"); /* End of cipher suites */ + len = (PRUint32)GET_SHORT((shv2->cidlength)); + if (len) { + PR_fprintf(PR_STDOUT, " connection-id = { "); + for (p = + 0; + p < len; p += 2) { + PR_fprintf(PR_STDOUT, "0x%04x ", (PRUint32)(GET_SHORT((pos + p)))); + } + PR_fprintf(PR_STDOUT, " }\n"); /* End of connection id */ + } + eosh: + PR_fprintf(PR_STDOUT, "\n }\n"); /* end of ServerHelloV2 */ + if (shv2->sidhit) { + clientstream.isEncrypted = + 1; + serverstream.isEncrypted = + 1; + } + break; + + case 5: + PR_fprintf(PR_STDOUT, " [ssl2] Server Verify V2 {...}\n"); + isV2Session = 1; + break; + + case 6: + PR_fprintf(PR_STDOUT, " [ssl2] Server Finished V2 {...}\n"); + isV2Session = 1; + break; + + case 7: + PR_fprintf(PR_STDOUT, " [ssl2] Request Certificate V2 {...}\n"); + isV2Session = 1; + break; + + case 8: + PR_fprintf(PR_STDOUT, " [ssl2] Client Certificate V2 {...}\n"); + isV2Session = 1; + break; + + default: + PR_fprintf(PR_STDOUT, " [ssl2] UnknownType 0x%02x {...}\n", + (PRUint32)chv2->type); + break; + } +} + +unsigned int +print_hello_extension(unsigned char *hsdata, + unsigned int length, + unsigned int pos) +{ + /* pretty print extensions, if any */ + if (pos < length) { + int exListLen = GET_SHORT((hsdata + pos)); + pos += 2; + PR_fprintf(PR_STDOUT, + " extensions[%d] = {\n", exListLen); + while (exListLen > 0 && pos < length) { + int exLen; + int exType = GET_SHORT((hsdata + pos)); + pos += 2; + exLen = GET_SHORT((hsdata + pos)); + pos += 2; + /* dump the extension */ + PR_fprintf(PR_STDOUT, + " extension type %s, length [%d]", + helloExtensionNameString(exType), exLen); + if (exLen > 0) { + PR_fprintf(PR_STDOUT, " = {\n"); + print_hex(exLen, hsdata + pos); + PR_fprintf(PR_STDOUT, " }\n"); + } else { + PR_fprintf(PR_STDOUT, "\n"); + } + pos += exLen; + exListLen -= 2 + exLen; + } + PR_fprintf(PR_STDOUT, " }\n"); + } + return pos; +} + +/* + * Note this must match (exactly) the enumeration ocspResponseStatus. + */ +static char *responseStatusNames[] = { + "successful (Response has valid confirmations)", + "malformedRequest (Illegal confirmation request)", + "internalError (Internal error in issuer)", + "tryLater (Try again later)", + "unused ((4) is not used)", + "sigRequired (Must sign the request)", + "unauthorized (Request unauthorized)", +}; + +static void +print_ocsp_cert_id(FILE *out_file, CERTOCSPCertID *cert_id, int level) +{ + SECU_Indent(out_file, level); + fprintf(out_file, "Cert ID:\n"); + level++; + /* + SECU_PrintAlgorithmID (out_file, &(cert_id->hashAlgorithm), + "Hash Algorithm", level); + SECU_PrintAsHex (out_file, &(cert_id->issuerNameHash), + "Issuer Name Hash", level); + SECU_PrintAsHex (out_file, &(cert_id->issuerKeyHash), + "Issuer Key Hash", level); +*/ + SECU_PrintInteger(out_file, &(cert_id->serialNumber), + "Serial Number", level); + /* XXX lookup the cert; if found, print something nice (nickname?) */ +} + +static void +print_ocsp_version(FILE *out_file, SECItem *version, int level) +{ + if (version->len > 0) { + SECU_PrintInteger(out_file, version, "Version", level); + } else { + SECU_Indent(out_file, level); + fprintf(out_file, "Version: DEFAULT\n"); + } +} + +static void +print_responder_id(FILE *out_file, ocspResponderID *responderID, int level) +{ + SECU_Indent(out_file, level); + fprintf(out_file, "Responder ID "); + + switch (responderID->responderIDType) { + case ocspResponderID_byName: + fprintf(out_file, "(byName):\n"); + SECU_PrintName(out_file, &(responderID->responderIDValue.name), + "Name", level + 1); + break; + case ocspResponderID_byKey: + fprintf(out_file, "(byKey):\n"); + SECU_PrintAsHex(out_file, &(responderID->responderIDValue.keyHash), + "Key Hash", level + 1); + break; + default: + fprintf(out_file, "Unrecognized Responder ID Type\n"); + break; + } +} + +static void +print_ocsp_extensions(FILE *out_file, CERTCertExtension **extensions, + char *msg, int level) +{ + if (extensions) { + SECU_PrintExtensions(out_file, extensions, msg, level); + } else { + SECU_Indent(out_file, level); + fprintf(out_file, "No %s\n", msg); + } +} + +static void +print_revoked_info(FILE *out_file, ocspRevokedInfo *revoked_info, int level) +{ + SECU_PrintGeneralizedTime(out_file, &(revoked_info->revocationTime), + "Revocation Time", level); + + if (revoked_info->revocationReason != NULL) { + SECU_PrintAsHex(out_file, revoked_info->revocationReason, + "Revocation Reason", level); + } else { + SECU_Indent(out_file, level); + fprintf(out_file, "No Revocation Reason.\n"); + } +} + +static void +print_cert_status(FILE *out_file, ocspCertStatus *status, int level) +{ + SECU_Indent(out_file, level); + fprintf(out_file, "Status: "); + + switch (status->certStatusType) { + case ocspCertStatus_good: + fprintf(out_file, "Cert is good.\n"); + break; + case ocspCertStatus_revoked: + fprintf(out_file, "Cert has been revoked.\n"); + print_revoked_info(out_file, status->certStatusInfo.revokedInfo, + level + 1); + break; + case ocspCertStatus_unknown: + fprintf(out_file, "Cert is unknown to responder.\n"); + break; + default: + fprintf(out_file, "Unrecognized status.\n"); + break; + } +} + +static void +print_single_response(FILE *out_file, CERTOCSPSingleResponse *single, + int level) +{ + print_ocsp_cert_id(out_file, single->certID, level); + + print_cert_status(out_file, single->certStatus, level); + + SECU_PrintGeneralizedTime(out_file, &(single->thisUpdate), + "This Update", level); + + if (single->nextUpdate != NULL) { + SECU_PrintGeneralizedTime(out_file, single->nextUpdate, + "Next Update", level); + } else { + SECU_Indent(out_file, level); + fprintf(out_file, "No Next Update\n"); + } + + print_ocsp_extensions(out_file, single->singleExtensions, + "Single Response Extensions", level); +} + +static void +print_response_data(FILE *out_file, ocspResponseData *responseData, int level) +{ + SECU_Indent(out_file, level); + fprintf(out_file, "Response Data:\n"); + level++; + + print_ocsp_version(out_file, &(responseData->version), level); + + print_responder_id(out_file, responseData->responderID, level); + + SECU_PrintGeneralizedTime(out_file, &(responseData->producedAt), + "Produced At", level); + + if (responseData->responses != NULL) { + int i; + + for (i = 0; responseData->responses[i] != NULL; i++) { + SECU_Indent(out_file, level); + fprintf(out_file, "Response %d:\n", i); + print_single_response(out_file, responseData->responses[i], + level + 1); + } + } else { + fprintf(out_file, "Response list is empty.\n"); + } + + print_ocsp_extensions(out_file, responseData->responseExtensions, + "Response Extensions", level); +} + +static void +print_basic_response(FILE *out_file, ocspBasicOCSPResponse *basic, int level) +{ + SECU_Indent(out_file, level); + fprintf(out_file, "Basic OCSP Response:\n"); + level++; + + print_response_data(out_file, basic->tbsResponseData, level); +} + +static void +print_status_response(SECItem *data) +{ + int level = 2; + CERTOCSPResponse *response; + response = CERT_DecodeOCSPResponse(data); + if (!response) { + SECU_Indent(stdout, level); + fprintf(stdout, "unable to decode certificate_status\n"); + return; + } + + SECU_Indent(stdout, level); + if (response->statusValue >= ocspResponse_min && + response->statusValue <= ocspResponse_max) { + fprintf(stdout, "Response Status: %s\n", + responseStatusNames[response->statusValue]); + } else { + fprintf(stdout, + "Response Status: other (Status value %d out of defined range)\n", + (int)response->statusValue); + } + + if (response->statusValue == ocspResponse_successful) { + ocspResponseBytes *responseBytes = response->responseBytes; + PORT_Assert(responseBytes != NULL); + + level++; + SECU_PrintObjectID(stdout, &(responseBytes->responseType), + "Response Type", level); + switch (response->responseBytes->responseTypeTag) { + case SEC_OID_PKIX_OCSP_BASIC_RESPONSE: + print_basic_response(stdout, + responseBytes->decodedResponse.basic, + level); + break; + default: + SECU_Indent(stdout, level); + fprintf(stdout, "Unknown response syntax\n"); + break; + } + } else { + SECU_Indent(stdout, level); + fprintf(stdout, "Unsuccessful response, no more information.\n"); + } + + CERT_DestroyOCSPResponse(response); +} + +/* In the case of renegotiation, handshakes that occur in an already MAC'ed + * channel, by the time of this call, the caller has already removed the MAC + * from input recordLen. The only MAC'ed record that will get here with its + * MAC intact (not removed) is the first Finished message on the connection. + */ +void +print_ssl3_handshake(unsigned char *recordBuf, + unsigned int recordLen, + SSLRecord *sr, + DataBufferList *s) +{ + struct sslhandshake sslh; + unsigned char *hsdata; + unsigned int offset = 0; + + PR_fprintf(PR_STDOUT, " handshake {\n"); + + if (s->msgBufOffset && s->msgBuf) { + /* append recordBuf to msgBuf, then use msgBuf */ + if (s->msgBufOffset + recordLen > s->msgBufSize) { + int newSize = s->msgBufOffset + recordLen; + unsigned char *newBuf = PORT_Realloc(s->msgBuf, newSize); + if (!newBuf) { + PR_ASSERT(newBuf); + showErr("Realloc failed"); + exit(10); + } + s->msgBuf = newBuf; + s->msgBufSize = newSize; + } + memcpy(s->msgBuf + s->msgBufOffset, recordBuf, recordLen); + s->msgBufOffset += recordLen; + recordLen = s->msgBufOffset; + recordBuf = s->msgBuf; + } + while (offset + 4 <= recordLen) { + sslh.type = recordBuf[offset]; + sslh.length = GET_24(recordBuf + offset + 1); + if (offset + 4 + sslh.length > recordLen) + break; + /* finally have a complete message */ + if (sslhexparse) + print_hex(4, recordBuf + offset); + + hsdata = &recordBuf[offset + 4]; + + PR_fprintf(PR_STDOUT, " type = %d (", sslh.type); + switch (sslh.type) { + case 0: + PR_FPUTS("hello_request)\n"); + break; + case 1: + PR_FPUTS("client_hello)\n"); + break; + case 2: + PR_FPUTS("server_hello)\n"); + break; + case 4: + PR_FPUTS("new_session_ticket)\n"); + break; + case 11: + PR_FPUTS("certificate)\n"); + break; + case 12: + PR_FPUTS("server_key_exchange)\n"); + break; + case 13: + PR_FPUTS("certificate_request)\n"); + break; + case 14: + PR_FPUTS("server_hello_done)\n"); + break; + case 15: + PR_FPUTS("certificate_verify)\n"); + break; + case 16: + PR_FPUTS("client_key_exchange)\n"); + break; + case 20: + PR_FPUTS("finished)\n"); + break; + case 22: + PR_FPUTS("certificate_status)\n"); + break; + default: + PR_FPUTS("unknown)\n"); + break; + } + + PR_fprintf(PR_STDOUT, " length = %d (0x%06x)\n", sslh.length, sslh.length); + switch (sslh.type) { + + case 0: /* hello_request */ /* not much to show here. */ + break; + + case 1: /* client hello */ + switch (sr->ver_maj) { + case 3: /* ssl version 3 */ + { + unsigned int pos; + int w; + + PR_fprintf(PR_STDOUT, " ClientHelloV3 {\n"); + PR_fprintf(PR_STDOUT, " client_version = {%d, %d}\n", + (PRUint8)hsdata[0], (PRUint8)hsdata[1]); + PR_fprintf(PR_STDOUT, " random = {...}\n"); + if (sslhexparse) + print_hex(32, &hsdata[2]); + + /* pretty print Session ID */ + { + int sidlength = + (int)hsdata[2 + 32]; + PR_fprintf(PR_STDOUT, " session ID = {\n"); + PR_fprintf(PR_STDOUT, " length = %d\n", sidlength); + PR_fprintf(PR_STDOUT, " contents = {...}\n"); + if (sslhexparse) + print_hex(sidlength, &hsdata[2 + 32 + 1]); + PR_fprintf(PR_STDOUT, " }\n"); + pos = + 2 + + 32 + + 1 + + sidlength; + } + + /* pretty print cipher suites */ + { + int csuitelength = + GET_SHORT((hsdata + pos)); + PR_fprintf(PR_STDOUT, " cipher_suites[%d] = {\n", + csuitelength / + 2); + if (csuitelength % + 2) { + PR_fprintf(PR_STDOUT, + "*error in protocol - csuitelength shouldn't be odd*\n"); + } + for (w = + 0; + w < + csuitelength; + w += 2) { + PRUint32 cs_int = + GET_SHORT((hsdata + pos + 2 + w)); + const char *cs_str = + V2CipherString(cs_int); + PR_fprintf(PR_STDOUT, + " (0x%04x) %s\n", cs_int, cs_str); + } + pos += + 2 + + csuitelength; + PR_fprintf(PR_STDOUT, " }\n"); + } + + /* pretty print compression methods */ + { + int complength = + hsdata[pos]; + PR_fprintf(PR_STDOUT, " compression[%d] = {\n", + complength); + for (w = + 0; + w < + complength; + w++) { + PRUint32 cm_int = + hsdata[pos + 1 + w]; + const char *cm_str = + CompressionMethodString(cm_int); + PR_fprintf(PR_STDOUT, + " (%02x) %s\n", cm_int, cm_str); + } + pos += + 1 + + complength; + PR_fprintf(PR_STDOUT, " }\n"); + } + + /* pretty print extensions, if any */ + pos = + print_hello_extension(hsdata, sslh.length, pos); + + PR_fprintf(PR_STDOUT, " }\n"); + } /* end of ssl version 3 */ + break; + default: + PR_fprintf(PR_STDOUT, " UNDEFINED VERSION %d.%d {...}\n", + sr->ver_maj, sr->ver_min); + if (sslhexparse) + print_hex(sslh.length, hsdata); + break; + } /* end of switch sr->ver_maj */ + break; + + case 2: /* server hello */ + { + unsigned int sidlength, pos; + + PR_fprintf(PR_STDOUT, " ServerHello {\n"); + + PR_fprintf(PR_STDOUT, " server_version = {%d, %d}\n", + (PRUint8)hsdata[0], (PRUint8)hsdata[1]); + PR_fprintf(PR_STDOUT, " random = {...}\n"); + if (sslhexparse) + print_hex(32, &hsdata[2]); + PR_fprintf(PR_STDOUT, " session ID = {\n"); + sidlength = (int)hsdata[2 + + 32]; + PR_fprintf(PR_STDOUT, " length = %d\n", sidlength); + PR_fprintf(PR_STDOUT, " contents = {...}\n"); + if (sslhexparse) + print_hex(sidlength, &hsdata[2 + 32 + 1]); + PR_fprintf(PR_STDOUT, " }\n"); + pos = 2 + + 32 + 1 + + sidlength; + + /* pretty print chosen cipher suite */ + { + PRUint32 cs_int = GET_SHORT((hsdata + pos)); + const char *cs_str = + V2CipherString(cs_int); + PR_fprintf(PR_STDOUT, " cipher_suite = (0x%04x) %s\n", + cs_int, cs_str); + currentcipher = + cs_int; + pos += + 2; + } + /* pretty print chosen compression method */ + { + PRUint32 cm_int = hsdata[pos++]; + const char *cm_str = + CompressionMethodString(cm_int); + PR_fprintf(PR_STDOUT, " compression method = (%02x) %s\n", + cm_int, cm_str); + } + + /* pretty print extensions, if any */ + pos = print_hello_extension(hsdata, sslh.length, pos); + + PR_fprintf(PR_STDOUT, " }\n"); + } break; + + case 4: /* new session ticket */ + { + PRUint32 lifetimehint; + PRUint16 ticketlength; + char lifetime[32]; + lifetimehint = GET_32(hsdata); + if (lifetimehint) { + PRExplodedTime et; + PRTime t = + lifetimehint; + t *= + PR_USEC_PER_SEC; + PR_ExplodeTime(t, PR_GMTParameters, &et); + /* use HTTP Cookie header's date format */ + PR_FormatTimeUSEnglish(lifetime, sizeof lifetime, + "%a, %d-%b-%Y %H:%M:%S GMT", &et); + } else { + /* 0 means the lifetime of the ticket is unspecified */ + strcpy(lifetime, "unspecified"); + } + ticketlength = GET_SHORT((hsdata + + 4)); + PR_fprintf(PR_STDOUT, " NewSessionTicket {\n"); + PR_fprintf(PR_STDOUT, " ticket_lifetime_hint = %s\n", + lifetime); + PR_fprintf(PR_STDOUT, " ticket = {\n"); + PR_fprintf(PR_STDOUT, " length = %d\n", ticketlength); + PR_fprintf(PR_STDOUT, " contents = {...}\n"); + if (sslhexparse) + print_hex(ticketlength, &hsdata[4 + 2]); + PR_fprintf(PR_STDOUT, " }\n"); + PR_fprintf(PR_STDOUT, " }\n"); + } break; + + case 11: /* certificate */ + { + PRFileDesc *cfd; + int pos; + int certslength; + int certlength; + int certbytesread = 0; + static int certFileNumber; + char certFileName[20]; + + PR_fprintf(PR_STDOUT, " CertificateChain {\n"); + certslength = GET_24(hsdata); + PR_fprintf(PR_STDOUT, " chainlength = %d (0x%04x)\n", + certslength, certslength); + pos = 3; + while (certbytesread < certslength) { + certlength = + GET_24((hsdata + pos)); + pos += + 3; + PR_fprintf(PR_STDOUT, " Certificate {\n"); + PR_fprintf(PR_STDOUT, " size = %d (0x%04x)\n", + certlength, certlength); + certbytesread += + certlength + 3; + if (certbytesread <= + certslength) { + PR_snprintf(certFileName, sizeof certFileName, "cert.%03d", + ++certFileNumber); + cfd = + PR_Open(certFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, + 0664); + if (!cfd) { + PR_fprintf(PR_STDOUT, + " data = { couldn't save file '%s' }\n", + certFileName); + } else { + PR_Write(cfd, (hsdata + pos), + certlength); + PR_fprintf(PR_STDOUT, + " data = { saved in file '%s' }\n", + certFileName); + PR_Close(cfd); + } + } + + PR_fprintf(PR_STDOUT, " }\n"); + pos += certlength; + } + PR_fprintf(PR_STDOUT, " }\n"); + } break; + + case 12: /* server_key_exchange */ + if (sslhexparse) + print_hex(sslh.length, hsdata); + break; + + case 13: /* certificate request */ + { + unsigned int pos = 0; + int w, reqLength; + + PR_fprintf(PR_STDOUT, " CertificateRequest {\n"); + + /* pretty print requested certificate types */ + reqLength = hsdata[pos]; + PR_fprintf(PR_STDOUT, " certificate types[%d] = {", + reqLength); + for (w = + 0; + w < reqLength; w++) { + PR_fprintf(PR_STDOUT, " %02x", hsdata[pos + 1 + w]); + } + pos += 1 + reqLength; + PR_fprintf(PR_STDOUT, " }\n"); + + /* pretty print CA names, if any */ + if (pos < sslh.length) { + int exListLen = + GET_SHORT((hsdata + pos)); + pos += 2; + PR_fprintf(PR_STDOUT, + " certificate_authorities[%d] = {\n", + exListLen); + while (exListLen > + 0 && + pos < sslh.length) { + char *ca_name; + SECItem it; + int dnLen = GET_SHORT((hsdata + + pos)); + pos += 2; + + /* dump the CA name */ + it.type = + siBuffer; + it.data = + hsdata + pos; + it.len = + dnLen; + ca_name = + CERT_DerNameToAscii(&it); + if (ca_name) { + PR_fprintf(PR_STDOUT, " %s\n", ca_name); + PORT_Free(ca_name); + } else { + PR_fprintf(PR_STDOUT, + " distinguished name [%d]", dnLen); + if (dnLen > + 0 && + sslhexparse) { + PR_fprintf(PR_STDOUT, " = {\n"); + print_hex(dnLen, hsdata + pos); + PR_fprintf(PR_STDOUT, " }\n"); + } else { + PR_fprintf(PR_STDOUT, "\n"); + } + } + pos += + dnLen; + exListLen -= + 2 + dnLen; + } + PR_fprintf(PR_STDOUT, " }\n"); + } + + PR_fprintf(PR_STDOUT, " }\n"); + } break; + + case 14: /* server_hello_done */ /* not much to show here. */ + break; + + case 15: /* certificate_verify */ + if (sslhexparse) + print_hex(sslh.length, hsdata); + break; + + case 16: /* client key exchange */ + { + PR_fprintf(PR_STDOUT, " ClientKeyExchange {\n"); + PR_fprintf(PR_STDOUT, " message = {...}\n"); + PR_fprintf(PR_STDOUT, " }\n"); + } break; + + case 20: /* finished */ + PR_fprintf(PR_STDOUT, " Finished {\n"); + PR_fprintf(PR_STDOUT, " verify_data = {...}\n"); + if (sslhexparse) + print_hex(sslh.length, hsdata); + PR_fprintf(PR_STDOUT, " }\n"); + + if (!isNULLmac(currentcipher) && + !s->hMACsize) { + /* To calculate the size of MAC, we subtract the number of known + * bytes of message from the number of remaining bytes in the + * record. This assumes that this is the first record on the + * connection to have a MAC, and that the sender has not put another + * message after the finished message in the handshake record. + * This is only correct for the first transition from unMACed to + * MACed. If the connection switches from one cipher suite to + * another one with a different MAC, this logic will not track that + * change correctly. + */ + s->hMACsize = + recordLen - (sslh.length + 4); + sslh.length += + s->hMACsize; /* skip over the MAC data */ + } + break; + + case 22: /* certificate_status */ + { + SECItem data; + PRFileDesc *ofd; + static int ocspFileNumber; + char ocspFileName[20]; + + /* skip 4 bytes with handshake numbers, as in ssl3_HandleCertificateStatus */ + data.type = siBuffer; + data.data = hsdata + 4; + data.len = sslh.length - 4; + print_status_response(&data); + + PR_snprintf(ocspFileName, sizeof ocspFileName, "ocsp.%03d", + ++ocspFileNumber); + ofd = PR_Open(ocspFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, + 0664); + if (!ofd) { + PR_fprintf(PR_STDOUT, + " data = { couldn't save file '%s' }\n", + ocspFileName); + } else { + PR_Write(ofd, data.data, data.len); + PR_fprintf(PR_STDOUT, + " data = { saved in file '%s' }\n", + ocspFileName); + PR_Close(ofd); + } + } break; + + default: { + PR_fprintf(PR_STDOUT, " UNKNOWN MESSAGE TYPE %d [%d] {\n", + sslh.type, sslh.length); + if (sslhexparse) + print_hex(sslh.length, hsdata); + PR_fprintf(PR_STDOUT, " }\n"); + } + } /* end of switch sslh.type */ + offset += sslh.length + 4; + } /* while */ + if (offset < recordLen) { /* stuff left over */ + unsigned int newMsgLen = recordLen - offset; + if (!s->msgBuf) { + s->msgBuf = PORT_Alloc(newMsgLen); + if (!s->msgBuf) { + PR_ASSERT(s->msgBuf); + showErr("Malloc failed"); + exit(11); + } + s->msgBufSize = newMsgLen; + memcpy(s->msgBuf, recordBuf + offset, newMsgLen); + } else if (newMsgLen > s->msgBufSize) { + unsigned char *newBuf = PORT_Realloc(s->msgBuf, newMsgLen); + if (!newBuf) { + PR_ASSERT(newBuf); + showErr("Realloc failed"); + exit(12); + } + s->msgBuf = newBuf; + s->msgBufSize = newMsgLen; + } else if (offset || s->msgBuf != recordBuf) { + memmove(s->msgBuf, recordBuf + offset, newMsgLen); + } + s->msgBufOffset = newMsgLen; + PR_fprintf(PR_STDOUT, " [incomplete handshake message]\n"); + } else { + s->msgBufOffset = 0; + } + PR_fprintf(PR_STDOUT, " }\n"); +} + +void +print_ssl(DataBufferList *s, int length, unsigned char *buffer) +{ + /* -------------------------------------------------------- */ + /* first, create a new buffer object for this piece of data. */ + + DataBuffer *db; + + if (s->size == 0 && length > 0 && buffer[0] >= 32 && buffer[0] < 128) { + /* Not an SSL record, treat entire buffer as plaintext */ + PR_Write(PR_STDOUT, buffer, length); + return; + } + + check_integrity(s); + + db = PR_NEW(struct _DataBuffer); + + if (!db) { + return; + } + + db->buffer = (unsigned char *)PORT_Alloc(length); + db->length = length; + db->offset = 0; + memcpy(db->buffer, buffer, length); + db->next = NULL; + + /* now, add it to the stream */ + + if (s->last != NULL) + s->last->next = db; + s->last = db; + s->size += length; + if (s->first == NULL) + s->first = db; + + check_integrity(s); + + /*------------------------------------------------------- */ + /* now we look at the stream to see if we have enough data to + decode */ + + while (s->size > 0) { + unsigned char *recordBuf = NULL; + + SSLRecord sr; + unsigned recordLen; + unsigned recordsize; + + check_integrity(s); + + if (s->first == NULL) { + PR_fprintf(PR_STDOUT, "ERROR: s->first is null\n"); + exit(9); + } + + /* in the case of an SSL 2 client-hello */ + /* will have the high-bit set, whereas an SSL 3 client-hello will not */ + /* SSL2 can also send records that begin with the high bit clear. + * This code will incorrectly handle them. XXX + */ + if (isV2Session || s->first->buffer[s->first->offset] & 0x80) { + /* it's an SSL 2 packet */ + unsigned char lenbuf[3]; + + /* first, we check if there's enough data for it to be an SSL2-type + * record. What a pain.*/ + if (s->size < sizeof lenbuf) { + partial_packet(length, s->size, sizeof lenbuf); + return; + } + + /* read the first two bytes off the stream. */ + read_stream_bytes(lenbuf, s, sizeof(lenbuf)); + recordLen = ((unsigned int)(lenbuf[0] & 0x7f) << 8) + lenbuf[1] + + ((lenbuf[0] & 0x80) ? 2 : 3); + PR_fprintf(PR_STDOUT, "recordLen = %u bytes\n", recordLen); + + /* put 'em back on the head of the stream. */ + db = PR_NEW(struct _DataBuffer); + + db->length = sizeof lenbuf; + db->buffer = (unsigned char *)PORT_Alloc(db->length); + db->offset = 0; + memcpy(db->buffer, lenbuf, sizeof lenbuf); + + db->next = s->first; + s->first = db; + if (s->last == NULL) + s->last = db; + s->size += db->length; + + /* if there wasn't enough, go back for more. */ + if (s->size < recordLen) { + check_integrity(s); + partial_packet(length, s->size, recordLen); + return; + } + partial_packet(length, s->size, recordLen); + + /* read in the whole record. */ + recordBuf = PORT_Alloc(recordLen); + read_stream_bytes(recordBuf, s, recordLen); + + print_sslv2(s, recordBuf, recordLen); + PR_FREEIF(recordBuf); + check_integrity(s); + + continue; + } + + /***********************************************************/ + /* It's SSL v3 */ + /***********************************************************/ + check_integrity(s); + + if (s->size < sizeof sr) { + partial_packet(length, s->size, sizeof(SSLRecord)); + return; + } + + read_stream_bytes((unsigned char *)&sr, s, sizeof sr); + + /* we have read the stream bytes. Look at the length of + the ssl record. If we don't have enough data to satisfy this + request, then put the bytes we just took back at the head + of the queue */ + recordsize = GET_SHORT(sr.length); + + if (recordsize > s->size) { + db = PR_NEW(struct _DataBuffer); + + db->length = sizeof sr; + db->buffer = (unsigned char *)PORT_Alloc(db->length); + db->offset = 0; + memcpy(db->buffer, &sr, sizeof sr); + db->next = s->first; + + /* now, add it back on to the head of the stream */ + + s->first = db; + if (s->last == NULL) + s->last = db; + s->size += db->length; + + check_integrity(s); + partial_packet(length, s->size, recordsize); + return; + } + partial_packet(length, s->size, recordsize); + + PR_fprintf(PR_STDOUT, "SSLRecord { [%s]\n", get_time_string()); + if (sslhexparse) { + print_hex(5, (unsigned char *)&sr); + } + + check_integrity(s); + + PR_fprintf(PR_STDOUT, " type = %d (", sr.type); + switch (sr.type) { + case 20: + PR_fprintf(PR_STDOUT, "change_cipher_spec)\n"); + break; + case 21: + PR_fprintf(PR_STDOUT, "alert)\n"); + break; + case 22: + PR_fprintf(PR_STDOUT, "handshake)\n"); + break; + case 23: + PR_fprintf(PR_STDOUT, "application_data)\n"); + break; + default: + PR_fprintf(PR_STDOUT, "unknown)\n"); + break; + } + PR_fprintf(PR_STDOUT, " version = { %d,%d }\n", + (PRUint32)sr.ver_maj, (PRUint32)sr.ver_min); + PR_fprintf(PR_STDOUT, " length = %d (0x%x)\n", + (PRUint32)GET_SHORT(sr.length), (PRUint32)GET_SHORT(sr.length)); + + recordLen = recordsize; + PR_ASSERT(s->size >= recordLen); + if (s->size >= recordLen) { + recordBuf = (unsigned char *)PORT_Alloc(recordLen); + read_stream_bytes(recordBuf, s, recordLen); + + if (s->isEncrypted) { + PR_fprintf(PR_STDOUT, " < encrypted >\n"); + } else { /* not encrypted */ + + switch (sr.type) { + case 20: /* change_cipher_spec */ + if (sslhexparse) + print_hex(recordLen - s->hMACsize, recordBuf); + /* mark to say we can only dump hex form now on + * if it is not one on a null cipher */ + s->isEncrypted = + isNULLcipher(currentcipher) ? 0 : 1; + break; + + case 21: /* alert */ + switch (recordBuf[0]) { + case 1: + PR_fprintf(PR_STDOUT, " warning: "); + break; + case 2: + PR_fprintf(PR_STDOUT, " fatal: "); + break; + default: + PR_fprintf(PR_STDOUT, " unknown level %d: ", recordBuf[0]); + break; + } + + switch (recordBuf[1]) { + case 0: + PR_FPUTS("close_notify\n"); + break; + case 10: + PR_FPUTS("unexpected_message\n"); + break; + case 20: + PR_FPUTS("bad_record_mac\n"); + break; + case 21: + PR_FPUTS("decryption_failed\n"); + break; + case 22: + PR_FPUTS("record_overflow\n"); + break; + case 30: + PR_FPUTS("decompression_failure\n"); + break; + case 40: + PR_FPUTS("handshake_failure\n"); + break; + case 41: + PR_FPUTS("no_certificate\n"); + break; + case 42: + PR_FPUTS("bad_certificate\n"); + break; + case 43: + PR_FPUTS("unsupported_certificate\n"); + break; + case 44: + PR_FPUTS("certificate_revoked\n"); + break; + case 45: + PR_FPUTS("certificate_expired\n"); + break; + case 46: + PR_FPUTS("certificate_unknown\n"); + break; + case 47: + PR_FPUTS("illegal_parameter\n"); + break; + case 48: + PR_FPUTS("unknown_ca\n"); + break; + case 49: + PR_FPUTS("access_denied\n"); + break; + case 50: + PR_FPUTS("decode_error\n"); + break; + case 51: + PR_FPUTS("decrypt_error\n"); + break; + case 60: + PR_FPUTS("export_restriction\n"); + break; + case 70: + PR_FPUTS("protocol_version\n"); + break; + case 71: + PR_FPUTS("insufficient_security\n"); + break; + case 80: + PR_FPUTS("internal_error\n"); + break; + case 90: + PR_FPUTS("user_canceled\n"); + break; + case 100: + PR_FPUTS("no_renegotiation\n"); + break; + case 110: + PR_FPUTS("unsupported_extension\n"); + break; + case 111: + PR_FPUTS("certificate_unobtainable\n"); + break; + case 112: + PR_FPUTS("unrecognized_name\n"); + break; + case 113: + PR_FPUTS("bad_certificate_status_response\n"); + break; + case 114: + PR_FPUTS("bad_certificate_hash_value\n"); + break; + + default: + PR_fprintf(PR_STDOUT, "unknown alert %d\n", recordBuf[1]); + break; + } + + if (sslhexparse) + print_hex(recordLen - s->hMACsize, recordBuf); + break; + + case 22: /* handshake */ + print_ssl3_handshake(recordBuf, recordLen - s->hMACsize, + &sr, s); + break; + + case 23: /* application data */ + print_hex(recordLen - + s->hMACsize, + recordBuf); + break; + + default: + print_hex(recordLen - + s->hMACsize, + recordBuf); + break; + } + if (s->hMACsize) { + PR_fprintf(PR_STDOUT, " MAC = {...}\n"); + if (sslhexparse) { + unsigned char *offset = + recordBuf + (recordLen - s->hMACsize); + print_hex(s->hMACsize, offset); + } + } + } /* not encrypted */ + } + PR_fprintf(PR_STDOUT, "}\n"); + PR_FREEIF(recordBuf); + check_integrity(s); + } +} + +void +print_hex(int amt, unsigned char *buf) +{ + int i, j, k; + char t[20]; + static char string[5000]; + + for (i = 0; i < amt; i++) { + t[1] = 0; + + if (i % 16 == 0) { /* if we are at the beginning of a line */ + PR_fprintf(PR_STDOUT, "%4x:", i); /* print the line number */ + strcpy(string, ""); + } + + if (i % 4 == 0) { + PR_fprintf(PR_STDOUT, " "); + } + + j = buf[i]; + + t[0] = (j >= 0x20 && j < 0x80) ? j : '.'; + + if (fancy) { + switch (t[0]) { + case '<': + strcpy(t, "<"); + break; + case '>': + strcpy(t, ">"); + break; + case '&': + strcpy(t, "&"); + break; + } + } + strcat(string, t); + + PR_fprintf(PR_STDOUT, "%02x ", (PRUint8)buf[i]); + + /* if we've reached the end of the line - add the string */ + if (i % 16 == 15) + PR_fprintf(PR_STDOUT, " | %s\n", string); + } + /* we reached the end of the buffer,*/ + /* do we have buffer left over? */ + j = i % 16; + if (j > 0) { + for (k = 0; k < (16 - + j); + k++) { + /* print additional space after every four bytes */ + if ((k + j) % 4 == 0) { + PR_fprintf(PR_STDOUT, " "); + } + PR_fprintf(PR_STDOUT, " "); + } + PR_fprintf(PR_STDOUT, " | %s\n", string); + } +} + +void +Usage(void) +{ + PR_fprintf(PR_STDERR, "Usage: ssltap [-vhfsxl] [-p port] hostname:port\n"); + PR_fprintf(PR_STDERR, " -v [prints version string]\n"); + PR_fprintf(PR_STDERR, " -h [outputs hex instead of ASCII]\n"); + PR_fprintf(PR_STDERR, " -f [turn on Fancy HTML coloring]\n"); + PR_fprintf(PR_STDERR, " -s [turn on SSL decoding]\n"); + PR_fprintf(PR_STDERR, " -x [turn on extra SSL hex dumps]\n"); + PR_fprintf(PR_STDERR, " -p port [specify rendezvous port (default 1924)]\n"); + PR_fprintf(PR_STDERR, " -l [loop - continue to wait for more connections]\n"); +} + +void +showErr(const char *msg) +{ + PRErrorCode err = PR_GetError(); + const char *errString; + + if (err == PR_UNKNOWN_ERROR) + err = PR_CONNECT_RESET_ERROR; /* bug in NSPR. */ + errString = SECU_Strerror(err); + + if (!errString) + errString = "(no text available)"; + PR_fprintf(PR_STDERR, "%s: Error %d: %s: %s", progName, err, errString, msg); +} + +int +main(int argc, char *argv[]) +{ + char *hostname = NULL; + PRUint16 rendport = DEFPORT, port; + PRAddrInfo *ai; + void *iter; + PRStatus r; + PRNetAddr na_client, na_server, na_rend; + PRFileDesc *s_server, *s_client, *s_rend; /*rendezvous */ + int c_count = 0; + PLOptState *optstate; + PLOptStatus status; + SECStatus rv; + + progName = argv[0]; + optstate = PL_CreateOptState(argc, argv, "fxhslp:"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch (optstate->option) { + case 'f': + fancy++; + break; + case 'h': + hexparse++; + break; + case 's': + sslparse++; + break; + case 'x': + sslhexparse++; + break; + case 'l': + looparound++; + break; + case 'p': + rendport = + atoi(optstate->value); + break; + case '\0': + hostname = + PL_strdup(optstate->value); + } + } + if (status == PL_OPT_BAD) + Usage(); + + if (fancy) { + if (!hexparse && !sslparse) { + PR_fprintf(PR_STDERR, + "Note: use of -f without -s or -h not recommended, \n" + "as the output looks a little strange. It may be useful, however\n"); + } + } + + if (!hostname) + Usage(), exit(2); + + { + char *colon = (char *)strchr(hostname, ':'); + if (!colon) { + PR_fprintf(PR_STDERR, + "You must specify the host AND port you wish to connect to\n"); + Usage(), exit(3); + } + port = atoi(&colon[1]); + *colon = '\0'; + + if (port == 0) { + PR_fprintf(PR_STDERR, "Port must be a nonzero number.\n"); + exit(4); + } + } + + /* find the 'server' IP address so we don't have to look it up later */ + + if (fancy) { + PR_fprintf(PR_STDOUT, "<HTML><HEAD><TITLE>SSLTAP output</TITLE></HEAD>\n"); + PR_fprintf(PR_STDOUT, "<BODY><PRE>\n"); + } + PR_fprintf(PR_STDERR, "Looking up \"%s\"...\n", hostname); + ai = PR_GetAddrInfoByName(hostname, PR_AF_UNSPEC, PR_AI_ADDRCONFIG); + if (!ai) { + showErr("Host Name lookup failed\n"); + exit(5); + } + + iter = NULL; + iter = PR_EnumerateAddrInfo(iter, ai, port, &na_server); + /* set up the port which the client will connect to */ + + r = PR_InitializeNetAddr(PR_IpAddrAny, rendport, &na_rend); + if (r == PR_FAILURE) { + PR_fprintf(PR_STDERR, + "PR_InitializeNetAddr(,%d,) failed with error %d\n", PR_GetError()); + exit(0); + } + + rv = NSS_NoDB_Init(""); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "NSS_NoDB_Init() failed with error %d\n", PR_GetError()); + exit(5); + } + + s_rend = PR_NewTCPSocket(); + if (!s_rend) { + showErr("Couldn't create socket\n"); + exit(6); + } + + if (PR_Bind(s_rend, &na_rend)) { + PR_fprintf(PR_STDERR, "Couldn't bind to port %d (error %d)\n", rendport, PR_GetError()); + exit(-1); + } + + if (PR_Listen(s_rend, 5)) { + showErr("Couldn't listen\n"); + exit(-1); + } + + PR_fprintf(PR_STDERR, "Proxy socket ready and listening\n"); + do { /* accept one connection and process it. */ + PRPollDesc pds[2]; + + s_client = PR_Accept(s_rend, &na_client, PR_SecondsToInterval(3600)); + if (s_client == NULL) { + showErr("accept timed out\n"); + exit(7); + } + + s_server = PR_OpenTCPSocket(na_server.raw.family); + if (s_server == NULL) { + showErr("couldn't open new socket to connect to server \n"); + exit(8); + } + + r = PR_Connect(s_server, &na_server, PR_SecondsToInterval(5)); + + if (r == PR_FAILURE) { + showErr("Couldn't connect\n"); + return -1; + } + + if (looparound) { + if (fancy) + PR_fprintf(PR_STDOUT, "<p><HR><H2>"); + PR_fprintf(PR_STDOUT, "Connection #%d [%s]\n", c_count + 1, + get_time_string()); + if (fancy) + PR_fprintf(PR_STDOUT, "</H2>"); + } + + PR_fprintf(PR_STDOUT, "Connected to %s:%d\n", hostname, port); + +#define PD_C 0 +#define PD_S 1 + + pds[PD_C].fd = s_client; + pds[PD_S].fd = s_server; + pds[PD_C].in_flags = PR_POLL_READ; + pds[PD_S].in_flags = PR_POLL_READ; + + /* make sure the new connections don't start out encrypted. */ + clientstream.isEncrypted = 0; + serverstream.isEncrypted = 0; + isV2Session = 0; + + while ((pds[PD_C].in_flags & PR_POLL_READ) != 0 || + (pds[PD_S].in_flags & PR_POLL_READ) != 0) { /* Handle all messages on the connection */ + PRInt32 amt; + PRInt32 wrote; + unsigned char buffer[TAPBUFSIZ]; + + amt = PR_Poll(pds, 2, PR_INTERVAL_NO_TIMEOUT); + if (amt <= 0) { + if (amt) + showErr("PR_Poll failed.\n"); + else + showErr("PR_Poll timed out.\n"); + break; + } + + if (pds[PD_C].out_flags & PR_POLL_EXCEPT) { + showErr("Exception on client-side socket.\n"); + break; + } + + if (pds[PD_S].out_flags & PR_POLL_EXCEPT) { + showErr("Exception on server-side socket.\n"); + break; + } + + /* read data, copy it to stdout, and write to other socket */ + + if ((pds[PD_C].in_flags & PR_POLL_READ) != 0 && + (pds[PD_C].out_flags & PR_POLL_READ) != 0) { + + amt = PR_Read(s_client, buffer, sizeof(buffer)); + + if (amt < 0) { + showErr("Client socket read failed.\n"); + break; + } + + if (amt == 0) { + PR_fprintf(PR_STDOUT, "Read EOF on Client socket. [%s]\n", + get_time_string()); + pds[PD_C].in_flags &= ~PR_POLL_READ; + PR_Shutdown(s_server, PR_SHUTDOWN_SEND); + continue; + } + + PR_fprintf(PR_STDOUT, "--> [\n"); + if (fancy) + PR_fprintf(PR_STDOUT, "<font color=blue>"); + + if (hexparse) + print_hex(amt, buffer); + if (sslparse) + print_ssl(&clientstream, amt, buffer); + if (!hexparse && !sslparse) + PR_Write(PR_STDOUT, buffer, amt); + if (fancy) + PR_fprintf(PR_STDOUT, "</font>"); + PR_fprintf(PR_STDOUT, "]\n"); + + wrote = PR_Write(s_server, buffer, amt); + if (wrote != amt) { + if (wrote < 0) { + showErr("Write to server socket failed.\n"); + break; + } else { + PR_fprintf(PR_STDERR, "Short write to server socket!\n"); + } + } + } /* end of read from client socket. */ + + /* read data, copy it to stdout, and write to other socket */ + if ((pds[PD_S].in_flags & PR_POLL_READ) != 0 && + (pds[PD_S].out_flags & PR_POLL_READ) != 0) { + + amt = PR_Read(s_server, buffer, sizeof(buffer)); + + if (amt < 0) { + showErr("error on server-side socket.\n"); + break; + } + + if (amt == 0) { + PR_fprintf(PR_STDOUT, "Read EOF on Server socket. [%s]\n", + get_time_string()); + pds[PD_S].in_flags &= ~PR_POLL_READ; + PR_Shutdown(s_client, PR_SHUTDOWN_SEND); + continue; + } + + PR_fprintf(PR_STDOUT, "<-- [\n"); + if (fancy) + PR_fprintf(PR_STDOUT, "<font color=red>"); + if (hexparse) + print_hex(amt, (unsigned char *)buffer); + if (sslparse) + print_ssl(&serverstream, amt, (unsigned char *)buffer); + if (!hexparse && !sslparse) + PR_Write(PR_STDOUT, buffer, amt); + if (fancy) + PR_fprintf(PR_STDOUT, "</font>"); + PR_fprintf(PR_STDOUT, "]\n"); + + wrote = PR_Write(s_client, buffer, amt); + if (wrote != amt) { + if (wrote < 0) { + showErr("Write to client socket failed.\n"); + break; + } else { + PR_fprintf(PR_STDERR, "Short write to client socket!\n"); + } + } + + } /* end of read from server socket. */ + + /* Loop, handle next message. */ + + } /* handle messages during a connection loop */ + PR_Close(s_client); + PR_Close(s_server); + flush_stream(&clientstream); + flush_stream(&serverstream); + /* Connection is closed, so reset the current cipher */ + currentcipher = 0; + c_count++; + PR_fprintf(PR_STDERR, "Connection %d Complete [%s]\n", c_count, + get_time_string()); + } while (looparound); /* accept connection and process it. */ + PR_Close(s_rend); + if (NSS_Shutdown() != SECSuccess) { + return 1; + } + return 0; +} diff --git a/security/nss/cmd/ssltap/ssltap.gyp b/security/nss/cmd/ssltap/ssltap.gyp new file mode 100644 index 0000000000..9743ac262c --- /dev/null +++ b/security/nss/cmd/ssltap/ssltap.gyp @@ -0,0 +1,25 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +{ + 'includes': [ + '../../coreconf/config.gypi', + '../../cmd/platlibs.gypi' + ], + 'targets': [ + { + 'target_name': 'ssltap', + 'type': 'executable', + 'sources': [ + 'ssltap.c' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:dbm_exports', + '<(DEPTH)/exports.gyp:nss_exports' + ] + } + ], + 'variables': { + 'module': 'nss' + } +}
\ No newline at end of file |