aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORSbin72809 -> 72871 bytes
-rw-r--r--Makefile.am9
-rw-r--r--acinclude.m4157
-rw-r--r--configure.in19
-rw-r--r--epan/dissectors/packet-ssl-utils.c1265
-rw-r--r--epan/dissectors/packet-ssl-utils.h172
-rw-r--r--epan/dissectors/packet-ssl.c1069
-rw-r--r--gtk/Makefile.common1
-rw-r--r--gtk/main.c2
-rw-r--r--gtk/menu.c14
-rw-r--r--gtk/ssl-dlg.c1049
11 files changed, 3570 insertions, 187 deletions
diff --git a/AUTHORS b/AUTHORS
index db133cbf81..a1e3ab2794 100644
--- a/AUTHORS
+++ b/AUTHORS
Binary files differ
diff --git a/Makefile.am b/Makefile.am
index 6b8960ba52..4dcce27016 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -304,7 +304,8 @@ ethereal_LDADD = \
@SNMP_LIBS@ @SSL_LIBS@ \
$(plugin_ldadd) \
@PCRE_LIBS@ \
- @PCAP_LIBS@ @GTK_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ @FRAMEWORKS@
+ @PCAP_LIBS@ @GTK_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ @FRAMEWORKS@ \
+ @LIBGNUTLS_LIBS@
# Additional libs that I know how to build. These will be
# linked into the tethereal executable.
@@ -326,7 +327,8 @@ tethereal_LDADD = \
$(plugin_ldadd) \
@PCRE_LIBS@ \
@GLIB_LIBS@ -lm \
- @PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@
+ @PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ \
+ @LIBGNUTLS_LIBS@
if ENABLE_STATIC
tethereal_LDFLAGS = -Wl,-static -all-static
@@ -442,7 +444,8 @@ dftest_LDADD = \
$(plugin_ldadd) \
@PCRE_LIBS@ \
@GLIB_LIBS@ -lm \
- @PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@
+ @PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ \
+ @LIBGNUTLS_LIBS@
dftest_LDFLAGS = -export-dynamic
diff --git a/acinclude.m4 b/acinclude.m4
index 2e15ce5eb1..991decceed 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -1411,3 +1411,160 @@ AC_DEFUN([AC_ETHEREAL_KRB5_CHECK],
fi
AC_SUBST(KRB5_LIBS)
])
+
+dnl Autoconf macros for libgnutls
+
+# Modified for LIBGNUTLS -- nmav
+# Configure paths for LIBGCRYPT
+# Shamelessly stolen from the one of XDELTA by Owen Taylor
+# Werner Koch 99-12-09
+
+dnl AM_PATH_LIBGNUTLS([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libgnutls, and define LIBGNUTLS_CFLAGS and LIBGNUTLS_LIBS
+dnl
+AC_DEFUN([AM_PATH_LIBGNUTLS],
+[dnl
+dnl Get the cflags and libraries from the libgnutls-config script
+dnl
+AC_ARG_WITH(libgnutls-prefix,
+ [ --with-libgnutls-prefix=PFX Prefix where libgnutls is installed (optional)],
+ libgnutls_config_prefix="$withval", libgnutls_config_prefix="")
+
+ if test x$libgnutls_config_prefix != x ; then
+ if test x${LIBGNUTLS_CONFIG+set} != xset ; then
+ LIBGNUTLS_CONFIG=$libgnutls_config_prefix/bin/libgnutls-config
+ fi
+ fi
+
+ AC_PATH_PROG(LIBGNUTLS_CONFIG, libgnutls-config, no)
+ min_libgnutls_version=ifelse([$1], ,0.1.0,$1)
+ AC_MSG_CHECKING(for libgnutls - version >= $min_libgnutls_version)
+ no_libgnutls=""
+ if test "$LIBGNUTLS_CONFIG" = "no" ; then
+ no_libgnutls=yes
+ else
+ LIBGNUTLS_CFLAGS=`$LIBGNUTLS_CONFIG $libgnutls_config_args --cflags`
+ LIBGNUTLS_LIBS=`$LIBGNUTLS_CONFIG $libgnutls_config_args --libs`
+ libgnutls_config_version=`$LIBGNUTLS_CONFIG $libgnutls_config_args --version`
+
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $LIBGNUTLS_CFLAGS"
+ LIBS="$LIBS $LIBGNUTLS_LIBS"
+dnl
+dnl Now check if the installed libgnutls is sufficiently new. Also sanity
+dnl checks the results of libgnutls-config to some extent
+dnl
+ rm -f conf.libgnutlstest
+ AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+
+int
+main ()
+{
+ system ("touch conf.libgnutlstest");
+
+ if( strcmp( gnutls_check_version(NULL), "$libgnutls_config_version" ) )
+ {
+ printf("\n*** 'libgnutls-config --version' returned %s, but LIBGNUTLS (%s)\n",
+ "$libgnutls_config_version", gnutls_check_version(NULL) );
+ printf("*** was found! If libgnutls-config was correct, then it is best\n");
+ printf("*** to remove the old version of LIBGNUTLS. You may also be able to fix the error\n");
+ printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
+ printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
+ printf("*** required on your system.\n");
+ printf("*** If libgnutls-config was wrong, set the environment variable LIBGNUTLS_CONFIG\n");
+ printf("*** to point to the correct copy of libgnutls-config, and remove the file config.cache\n");
+ printf("*** before re-running configure\n");
+ }
+ else if ( strcmp(gnutls_check_version(NULL), LIBGNUTLS_VERSION ) )
+ {
+ printf("\n*** LIBGNUTLS header file (version %s) does not match\n", LIBGNUTLS_VERSION);
+ printf("*** library (version %s)\n", gnutls_check_version(NULL) );
+ }
+ else
+ {
+ if ( gnutls_check_version( "$min_libgnutls_version" ) )
+ {
+ return 0;
+ }
+ else
+ {
+ printf("no\n*** An old version of LIBGNUTLS (%s) was found.\n",
+ gnutls_check_version(NULL) );
+ printf("*** You need a version of LIBGNUTLS newer than %s. The latest version of\n",
+ "$min_libgnutls_version" );
+ printf("*** LIBGNUTLS is always available from ftp://gnutls.hellug.gr/pub/gnutls.\n");
+ printf("*** \n");
+ printf("*** If you have already installed a sufficiently new version, this error\n");
+ printf("*** probably means that the wrong copy of the libgnutls-config shell script is\n");
+ printf("*** being found. The easiest way to fix this is to remove the old version\n");
+ printf("*** of LIBGNUTLS, but you can also set the LIBGNUTLS_CONFIG environment to point to the\n");
+ printf("*** correct copy of libgnutls-config. (In this case, you will have to\n");
+ printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+ printf("*** so that the correct libraries are found at run-time))\n");
+ }
+ }
+ return 1;
+}
+],, no_libgnutls=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+
+ if test "x$no_libgnutls" = x ; then
+ AC_MSG_RESULT(yes)
+ ifelse([$2], , :, [$2])
+ else
+ if test -f conf.libgnutlstest ; then
+ :
+ else
+ AC_MSG_RESULT(no)
+ fi
+ if test "$LIBGNUTLS_CONFIG" = "no" ; then
+ echo "*** The libgnutls-config script installed by LIBGNUTLS could not be found"
+ echo "*** If LIBGNUTLS was installed in PREFIX, make sure PREFIX/bin is in"
+ echo "*** your path, or set the LIBGNUTLS_CONFIG environment variable to the"
+ echo "*** full path to libgnutls-config."
+ else
+ if test -f conf.libgnutlstest ; then
+ :
+ else
+ echo "*** Could not run libgnutls test program, checking why..."
+ CFLAGS="$CFLAGS $LIBGNUTLS_CFLAGS"
+ LIBS="$LIBS $LIBGNUTLS_LIBS"
+ AC_TRY_LINK([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+], [ return !!gnutls_check_version(NULL); ],
+ [ echo "*** The test program compiled, but did not run. This usually means"
+ echo "*** that the run-time linker is not finding LIBGNUTLS or finding the wrong"
+ echo "*** version of LIBGNUTLS. If it is not finding LIBGNUTLS, you'll need to set your"
+ echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+ echo "*** to the installed location Also, make sure you have run ldconfig if that"
+ echo "*** is required on your system"
+ echo "***"
+ echo "*** If you have an old version installed, it is best to remove it, although"
+ echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
+ echo "***" ],
+ [ echo "*** The test program failed to compile or link. See the file config.log for the"
+ echo "*** exact error that occured. This usually means LIBGNUTLS was incorrectly installed"
+ echo "*** or that you have moved LIBGNUTLS since it was installed. In the latter case, you"
+ echo "*** may want to edit the libgnutls-config script: $LIBGNUTLS_CONFIG" ])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ LIBGNUTLS_CFLAGS=""
+ LIBGNUTLS_LIBS=""
+ ifelse([$3], , :, [$3])
+ fi
+ rm -f conf.libgnutlstest
+ AC_SUBST(LIBGNUTLS_CFLAGS)
+ AC_SUBST(LIBGNUTLS_LIBS)
+])
diff --git a/configure.in b/configure.in
index 3d52e711bc..eae4c49c3e 100644
--- a/configure.in
+++ b/configure.in
@@ -63,6 +63,24 @@ AC_PATH_PROG(DOXYGEN, doxygen)
AC_CHECK_PROG(HAVE_DOXYGEN, doxygen, "yes", "no")
AM_CONDITIONAL(HAVE_DOXYGEN, test x$HAVE_DOXYGEN = xyes)
+# gnu tls
+AM_PATH_LIBGNUTLS(1.0.0,
+ [
+ echo "gnuTLS found, enabling ssl decryption"
+ AC_DEFINE(HAVE_LIBGNUTLS, 1, [Define to use gnutls library])
+ tls_message="yes"
+ ]
+ , [
+ if test x$libgnutls_config_prefix != x ; then
+ AC_MSG_ERROR([[gnuTLS not found; install gnuTLS-devel package for your system]])
+ else
+ echo echo "gnuTLS not found, disabling ssl decryption"
+ tls_message="no"
+ fi
+ ]
+)
+
+
# Check for xsltproc
AC_PATH_PROG(XSLTPROC, xsltproc)
AC_CHECK_PROG(HAVE_XSLTPROC, xsltproc, "yes", "no")
@@ -1391,3 +1409,4 @@ echo " Use GNU ADNS library : $adns_message"
echo " Use SSL crypto library : $ssl_message"
echo " Use IPv6 name resolution : $enable_ipv6"
echo " Use UCD SNMP/Net-SNMP library : $snmp_libs_message"
+echo " Use gnutls library : $tls_message"
diff --git a/epan/dissectors/packet-ssl-utils.c b/epan/dissectors/packet-ssl-utils.c
new file mode 100644
index 0000000000..be6b363f58
--- /dev/null
+++ b/epan/dissectors/packet-ssl-utils.c
@@ -0,0 +1,1265 @@
+/* packet-ssl-utils.c
+ *
+ * $Id$
+ *
+ * ssl manipulation functions
+ * By Paolo Abeni <paolo.abeni@email.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "packet-ssl-utils.h"
+
+#ifdef HAVE_LIBGNUTLS
+
+/* hmac abstraction layer */
+#define SSL_HMAC gcry_md_hd_t
+
+inline void ssl_hmac_init(SSL_HMAC* md, const void * key, int len, int algo)
+{
+ if (*(md))
+ gcry_md_close(*(md));
+ gcry_md_open(md,algo, GCRY_MD_FLAG_HMAC);
+ gcry_md_setkey (*(md), key, len);
+}
+
+inline void ssl_hmac_update(SSL_HMAC* md, const void* data, int len)
+{
+ gcry_md_write(*(md), data, len);
+}
+inline void ssl_hmac_final(SSL_HMAC* md, unsigned char* data, unsigned int* datalen)
+{
+ int algo = gcry_md_get_algo (*(md));
+ unsigned int len = gcry_md_get_algo_dlen(algo);
+ memcpy(data, gcry_md_read(*(md), algo), len);
+ *datalen =len;
+}
+inline void ssl_hmac_cleanup(SSL_HMAC* md) { gcry_md_close(*(md)); }
+
+/* memory digest abstraction layer*/
+#define SSL_MD gcry_md_hd_t
+
+inline void ssl_md_init(SSL_MD* md, int algo)
+{
+ if (*(md))
+ gcry_md_close(*(md));
+ gcry_md_open(md,algo, 0);
+}
+inline void ssl_md_update(SSL_MD* md, unsigned char* data, int len)
+{
+ gcry_md_write(*(md), data, len);
+}
+inline void ssl_md_final(SSL_MD* md, unsigned char* data, unsigned int* datalen)
+{
+ int algo = gcry_md_get_algo (*(md));
+ int len = gcry_md_get_algo_dlen (algo);
+ memcpy(data, gcry_md_read(*(md), algo), len);
+ *datalen = len;
+}
+inline void ssl_md_cleanup(SSL_MD* md) { gcry_md_close(*(md)); }
+
+/* md5 /sha abstraction layer */
+#define SSL_SHA_CTX gcry_md_hd_t
+#define SSL_MD5_CTX gcry_md_hd_t
+
+void ssl_sha_init(SSL_SHA_CTX* md)
+{
+ if (*(md))
+ gcry_md_close(*(md));
+ gcry_md_open(md,GCRY_MD_SHA1, 0);
+}
+inline void ssl_sha_update(SSL_SHA_CTX* md, unsigned char* data, int len)
+{
+ gcry_md_write(*(md), data, len);
+}
+inline void ssl_sha_final(unsigned char* buf, SSL_SHA_CTX* md)
+{
+ memcpy(buf, gcry_md_read(*(md), GCRY_MD_SHA1),
+ gcry_md_get_algo_dlen(GCRY_MD_SHA1));
+}
+
+inline int ssl_md5_init(SSL_MD5_CTX* md)
+{
+ if (*(md))
+ gcry_md_close(*(md));
+ return gcry_md_open(md,GCRY_MD_MD5, 0);
+}
+inline void ssl_md5_update(SSL_MD5_CTX* md, unsigned char* data, int len)
+{
+ gcry_md_write(*(md), data, len);
+}
+inline void ssl_md5_final(unsigned char* buf, SSL_MD5_CTX* md)
+{
+ memcpy(buf, gcry_md_read(*(md), GCRY_MD_MD5),
+ gcry_md_get_algo_dlen(GCRY_MD_MD5));
+}
+
+
+/* stream cipher abstraction layer*/
+int ssl_cipher_init(gcry_cipher_hd_t *cipher, int algo, unsigned char* sk,
+ unsigned char* iv, int mode)
+{
+ int gcry_modes[]={
+ GCRY_CIPHER_MODE_STREAM,
+ GCRY_CIPHER_MODE_CBC
+ };
+ int err = gcry_cipher_open(cipher, algo, gcry_modes[mode], 0);
+ if (err !=0)
+ return -1;
+ err = gcry_cipher_setkey(*(cipher), sk, gcry_cipher_get_algo_keylen (algo));
+ if (err != 0)
+ return -1;
+ err = gcry_cipher_setiv(*(cipher), iv, gcry_cipher_get_algo_blklen (algo));
+ if (err != 0)
+ return -1;
+ return 0;
+}
+inline int ssl_cipher_decrypt(gcry_cipher_hd_t *cipher, unsigned char * out, int outl,
+ const unsigned char * in,int inl)
+{
+ return gcry_cipher_decrypt ( *(cipher), out, outl, in, inl);
+}
+inline int ssl_get_digest_by_name(const char*name)
+{
+ return gcry_md_map_name(name);
+}
+inline int ssl_get_cipher_by_name(const char* name)
+{
+ return gcry_cipher_map_name(name);
+}
+
+/* private key abstraction layer */
+inline int ssl_get_key_len(SSL_PRIVATE_KEY* pk) {return gcry_pk_get_nbits (pk); }
+
+gcry_err_code_t
+_gcry_rsa_decrypt (int algo, gcry_mpi_t *result, gcry_mpi_t *data,
+ gcry_mpi_t *skey, int flags);
+
+#define PUBKEY_FLAG_NO_BLINDING (1 << 0)
+
+/* decrypt data with private key. Store decrypted data directly into input
+ * buffer */
+int ssl_private_decrypt(unsigned int len, unsigned char* encr_data, SSL_PRIVATE_KEY* pk)
+{
+ int rc;
+ size_t decr_len = 0;
+ gcry_sexp_t s_data, s_plain;
+ gcry_mpi_t encr_mpi;
+ size_t i, encr_len = len;
+ unsigned char* decr_data_ptr;
+ gcry_mpi_t text=NULL;
+
+ /* build up a mpi rappresentation for encrypted data */
+ rc = gcry_mpi_scan(&encr_mpi, GCRYMPI_FMT_USG,encr_data, encr_len, &encr_len);
+ if (rc != 0 ) {
+ ssl_debug_printf("pcry_private_decrypt: can't convert encr_data to mpi (size %d):%s\n",
+ len, gcry_strerror(rc));
+ return 0;
+ }
+
+#ifndef SSL_FAST
+ /* put the data into a simple list */
+ rc = gcry_sexp_build(&s_data, NULL, "(enc-val(rsa(a%m)))", encr_mpi);
+ if (rc != 0) {
+ ssl_debug_printf("pcry_private_decrypt: can't build encr_sexp:%s \n",
+ gcry_strerror(rc));
+ return 0;
+ }
+
+ /* pass it to libgcrypt */
+ rc = gcry_pk_decrypt(&s_plain, s_data, pk);
+ gcry_sexp_release(s_data);
+ if (rc != 0)
+ {
+ ssl_debug_printf("pcry_private_decrypt: can't decrypt key:%s\n",
+ gcry_strerror(rc));
+ goto out;
+ }
+
+ /* convert plain text sexp to mpi format */
+ text = gcry_sexp_nth_mpi(s_plain, 0, 0);
+
+ /* compute size requested for plaintext buffer */
+ decr_len = len;
+ if (gcry_mpi_print(GCRYMPI_FMT_USG, NULL, decr_len, &decr_len, text) != 0) {
+ ssl_debug_printf("pcry_private_decrypt: can't compute decr size:%s\n",
+ gcry_strerror(rc));
+ decr_len = 0;
+ goto out;
+ }
+
+ /* sanity check on out buffer */
+ if (decr_len > len) {
+ ssl_debug_printf("pcry_private_decrypt: decrypted data is too long ?!? (%d max %d)\n",
+ decr_len, len);
+ return 0;
+ }
+
+ /* write plain text to encrypted data buffer */
+ decr_data_ptr = encr_data;
+ if (gcry_mpi_print( GCRYMPI_FMT_USG, decr_data_ptr, decr_len, &decr_len,
+ text) != 0) {
+ ssl_debug_printf("pcry_private_decrypt: can't print decr data to mpi (size %d):%s\n",
+ decr_len, gcry_strerror(rc));
+ g_free(decr_data_ptr);
+ decr_len = 0;
+ goto out;
+ }
+
+ /* strip the padding*/
+ rc = 0;
+ for (i = 1; i < decr_len; i++) {
+ if (decr_data_ptr[i] == 0) {
+ rc = i+1;
+ break;
+ }
+ }
+
+ ssl_debug_printf("pcry_private_decrypt: stripping %d bytes, decr_len %d\n",
+ rc, decr_len);
+ ssl_print_data("decypted_unstrip_pre_master", decr_data_ptr, decr_len);
+ g_memmove(decr_data_ptr, &decr_data_ptr[rc], decr_len - rc);
+ decr_len -= rc;
+
+out:
+ gcry_sexp_release(s_plain);
+#else
+ rc = _gcry_rsa_decrypt(0, &text, &encr_mpi, pk,0);
+ gcry_mpi_print( GCRYMPI_FMT_USG, 0, 0, &decr_len, text);
+
+ /* sanity check on out buffer */
+ if (decr_len > len) {
+ ssl_debug_printf("pcry_private_decrypt: decrypted data is too long ?!? (%d max %d)\n",
+ decr_len, len);
+ return 0;
+ }
+
+ /* write plain text to newly allocated buffer */
+ decr_data_ptr = encr_data;
+ if (gcry_mpi_print( GCRYMPI_FMT_USG, decr_data_ptr, decr_len, &decr_len,
+ text) != 0) {
+ ssl_debug_printf("pcry_private_decrypt: can't print decr data to mpi (size %d):%s\n",
+ decr_len, gcry_strerror(rc));
+ return 0;
+ }
+
+ /* strip the padding*/
+ rc = 0;
+ for (i = 1; i < decr_len; i++) {
+ if (decr_data_ptr[i] == 0) {
+ rc = i+1;
+ break;
+ }
+ }
+
+ ssl_debug_printf("pcry_private_decrypt: stripping %d bytes, decr_len %d\n",
+ rc, decr_len);
+ ssl_print_data("decypted_unstrip_pre_master", decr_data_ptr, decr_len);
+ g_memmove(decr_data_ptr, &decr_data_ptr[rc], decr_len - rc);
+ decr_len -= rc;
+#endif
+ gcry_mpi_release(text);
+ return decr_len;
+}
+
+
+#define PRF(ssl,secret,usage,rnd1,rnd2,out) ((ssl->version_netorder==SSLV3_VERSION)? \
+ ssl3_prf(secret,usage,rnd1,rnd2,out): \
+ tls_prf(secret,usage,rnd1,rnd2,out))
+
+static const char *digests[]={
+ "MD5",
+ "SHA1"
+};
+
+static const char *ciphers[]={
+ "DES",
+ "DES3",
+ "ARCFOUR", /* gnutls does not support rc4, but this should be 100% compatible*/
+ "RC2",
+ "IDEA",
+ "AES",
+ "AES256"
+};
+
+/* look in openssl/ssl/ssl_lib.c for a complete list of available cipersuite*/
+static SslCipherSuite cipher_suites[]={
+ {1,KEX_RSA,SIG_RSA,ENC_NULL,0,0,0,DIG_MD5,16,0, SSL_CIPHER_MODE_STREAM},
+ {2,KEX_RSA,SIG_RSA,ENC_NULL,0,0,0,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {3,KEX_RSA,SIG_RSA,ENC_RC4,1,128,40,DIG_MD5,16,1, SSL_CIPHER_MODE_STREAM},
+ {4,KEX_RSA,SIG_RSA,ENC_RC4,1,128,128,DIG_MD5,16,0, SSL_CIPHER_MODE_STREAM},
+ {5,KEX_RSA,SIG_RSA,ENC_RC4,1,128,128,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {6,KEX_RSA,SIG_RSA,ENC_RC2,8,128,40,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+ {7,KEX_RSA,SIG_RSA,ENC_IDEA,8,128,128,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {8,KEX_RSA,SIG_RSA,ENC_DES,8,64,40,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+ {9,KEX_RSA,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {10,KEX_RSA,SIG_RSA,ENC_3DES,8,192,192,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {11,KEX_DH,SIG_DSS,ENC_DES,8,64,40,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+ {12,KEX_DH,SIG_DSS,ENC_DES,8,64,64,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {13,KEX_DH,SIG_DSS,ENC_3DES,8,192,192,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {14,KEX_DH,SIG_RSA,ENC_DES,8,64,40,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+ {15,KEX_DH,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {16,KEX_DH,SIG_RSA,ENC_3DES,8,192,192,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {17,KEX_DH,SIG_DSS,ENC_DES,8,64,40,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+ {18,KEX_DH,SIG_DSS,ENC_DES,8,64,64,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {19,KEX_DH,SIG_DSS,ENC_3DES,8,192,192,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {20,KEX_DH,SIG_RSA,ENC_DES,8,64,40,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+ {21,KEX_DH,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {22,KEX_DH,SIG_RSA,ENC_3DES,8,192,192,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {23,KEX_DH,SIG_NONE,ENC_RC4,1,128,40,DIG_MD5,16,1, SSL_CIPHER_MODE_STREAM},
+ {24,KEX_DH,SIG_NONE,ENC_RC4,1,128,128,DIG_MD5,16,0, SSL_CIPHER_MODE_STREAM},
+ {25,KEX_DH,SIG_NONE,ENC_DES,8,64,40,DIG_MD5,16,1, SSL_CIPHER_MODE_STREAM},
+ {26,KEX_DH,SIG_NONE,ENC_DES,8,64,64,DIG_MD5,16,0, SSL_CIPHER_MODE_STREAM},
+ {27,KEX_DH,SIG_NONE,ENC_3DES,8,192,192,DIG_MD5,16,0, SSL_CIPHER_MODE_STREAM},
+ {47,KEX_RSA,SIG_RSA,ENC_AES,16,128,128,DIG_SHA,20,0, SSL_CIPHER_MODE_CBC},
+ {53,KEX_RSA,SIG_RSA,ENC_AES256,16,256,256,DIG_SHA,20,0, SSL_CIPHER_MODE_CBC},
+ {96,KEX_RSA,SIG_RSA,ENC_RC4,1,128,56,DIG_MD5,16,1, SSL_CIPHER_MODE_STREAM},
+ {97,KEX_RSA,SIG_RSA,ENC_RC2,1,128,56,DIG_MD5,16,1, SSL_CIPHER_MODE_STREAM},
+ {98,KEX_RSA,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+ {99,KEX_DH,SIG_DSS,ENC_DES,8,64,64,DIG_SHA,16,1, SSL_CIPHER_MODE_STREAM},
+ {100,KEX_RSA,SIG_RSA,ENC_RC4,1,128,56,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+ {101,KEX_DH,SIG_DSS,ENC_RC4,1,128,56,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+ {102,KEX_DH,SIG_DSS,ENC_RC4,1,128,128,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+ {-1, 0,0,0,0,0,0,0,0,0, 0}
+};
+
+#define MAX_BLOCK_SIZE 16
+#define MAX_KEY_SIZE 32
+
+int ssl_find_cipher(int num,SslCipherSuite* cs)
+{
+ SslCipherSuite *c;
+
+ for(c=cipher_suites;c->number!=-1;c++){
+ if(c->number==num){
+ *cs=*c;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int tls_hash(StringInfo* secret,
+ StringInfo* seed, int md, StringInfo* out)
+{
+ guint8 *ptr=out->data;
+ unsigned int left=out->data_len;
+ int tocpy;
+ guint8 *A;
+ guint8 _A[20],tmp[20];
+ unsigned int A_l,tmp_l;
+ SSL_HMAC hm;
+
+ memset(&hm, 0, sizeof(hm));
+ ssl_print_string("tls_hash: hash secret", secret);
+ ssl_print_string("tls_hash: hash seed", seed);
+ A=seed->data;
+ A_l=seed->data_len;
+
+ while(left){
+ ssl_hmac_init(&hm,secret->data,secret->data_len,md);
+ ssl_hmac_update(&hm,A,A_l);
+ ssl_hmac_final(&hm,_A,&A_l);
+ A=_A;
+
+ ssl_hmac_init(&hm,secret->data,secret->data_len,md);
+ ssl_hmac_update(&hm,A,A_l);
+ ssl_hmac_update(&hm,seed->data,seed->data_len);
+ ssl_hmac_final(&hm,tmp,&tmp_l);
+
+ tocpy=MIN(left,tmp_l);
+ memcpy(ptr,tmp,tocpy);
+ ptr+=tocpy;
+ left-=tocpy;
+ }
+
+ ssl_hmac_cleanup(&hm);
+ ssl_print_string("hash out", out);
+ return (0);
+}
+
+static int tls_prf(StringInfo* secret, const char *usage,
+ StringInfo* rnd1, StringInfo* rnd2, StringInfo* out)
+{
+ StringInfo seed, sha_out, md5_out;
+ guint8 *ptr;
+ StringInfo s1, s2;
+ unsigned int i,s_l, r=-1;
+ int usage_len = strlen(usage);
+
+ /* initalize buffer for sha, md5 random seed*/
+ if (ssl_data_alloc(&sha_out, MAX(out->data_len,20)) < 0)
+ return -1;
+ if (ssl_data_alloc(&md5_out, MAX(out->data_len,16)) < 0)
+ goto free_sha;
+ if (ssl_data_alloc(&seed, usage_len+rnd1->data_len+rnd2->data_len) < 0)
+ goto free_md5;
+
+ ptr=seed.data;
+ memcpy(ptr,usage,usage_len); ptr+=usage_len;
+ memcpy(ptr,rnd1->data,rnd1->data_len); ptr+=rnd1->data_len;
+ memcpy(ptr,rnd2->data,rnd2->data_len); ptr+=rnd2->data_len;
+
+ /* initalize buffer for client/server seeds*/
+ s_l=secret->data_len/2 + secret->data_len%2;
+ if (ssl_data_alloc(&s1, s_l) < 0)
+ goto free_seed;
+ if (ssl_data_alloc(&s2, s_l) < 0)
+ goto free_s1;
+
+ memcpy(s1.data,secret->data,s_l);
+ memcpy(s2.data,secret->data + (secret->data_len - s_l),s_l);
+
+ ssl_debug_printf("tls_prf: tls_hash(md5 secret_len %d seed_len %d )\n", s1.data_len, seed.data_len);
+ if(tls_hash(&s1,&seed,ssl_get_digest_by_name("MD5"),&md5_out) != 0)
+ goto free_all;
+ ssl_debug_printf("tls_prf: tls_hash(sha)\n");
+ if(tls_hash(&s2,&seed,ssl_get_digest_by_name("SHA1"),&sha_out) != 0)
+ goto free_all;
+
+ for(i=0;i<out->data_len;i++)
+ out->data[i]=md5_out.data[i] ^ sha_out.data[i];
+ r =0;
+
+ ssl_print_string("PRF out",out);
+free_all:
+ free(s2.data);
+free_s1:
+ free(s1.data);
+free_seed:
+ free(seed.data);
+free_md5:
+ free(md5_out.data);
+free_sha:
+ free(sha_out.data);
+ return r;
+}
+
+static int ssl3_generate_export_iv(StringInfo* r1,
+ StringInfo* r2, StringInfo* out)
+{
+ SSL_MD5_CTX md5;
+ guint8 tmp[16];
+
+ ssl_md5_init(&md5);
+ ssl_md5_update(&md5,r1->data,r1->data_len);
+ ssl_md5_update(&md5,r2->data,r2->data_len);
+ ssl_md5_final(tmp,&md5);
+
+ memcpy(out->data,tmp,out->data_len);
+ ssl_print_string("export iv", out);
+
+ return(0);
+}
+
+static int ssl3_prf(StringInfo* secret, const char* usage,
+ StringInfo* r1,
+ StringInfo* r2,StringInfo* out)
+{
+ SSL_MD5_CTX md5;
+ SSL_SHA_CTX sha;
+ StringInfo *rnd1,*rnd2;
+ unsigned int off;
+ int i=0,j;
+ guint8 buf[20];
+
+ rnd1=r1; rnd2=r2;
+
+ ssl_md5_init(&md5);
+ memset(&sha,0,sizeof(sha));
+ ssl_sha_init(&sha);
+
+ for(off=0;off<out->data_len;off+=16){
+ unsigned char outbuf[16];
+ int tocpy;
+ i++;
+
+ ssl_debug_printf("ssl3_prf: sha1_update(%d)\n",i);
+ /* A, BB, CCC, ... */
+ for(j=0;j<i;j++){
+ buf[j]=64+i;
+ }
+
+ ssl_sha_update(&sha,buf,i);
+ if (secret)
+ ssl_sha_update(&sha,secret->data,secret->data_len);
+
+ if(!strcmp(usage,"client write key") || !strcmp(usage,"server write key")){
+ ssl_sha_update(&sha,rnd2->data,rnd2->data_len);
+ ssl_sha_update(&sha,rnd1->data,rnd1->data_len);
+ }
+ else{
+ ssl_sha_update(&sha,rnd1->data,rnd1->data_len);
+ ssl_sha_update(&sha,rnd2->data,rnd2->data_len);
+ }
+
+ ssl_sha_final(buf,&sha);
+
+ ssl_sha_init(&sha);
+
+ ssl_debug_printf("ssl3_prf: md5_update(%d)\n",i);
+ ssl_md5_update(&md5,secret->data,secret->data_len);
+ ssl_md5_update(&md5,buf,20);
+ ssl_md5_final(outbuf,&md5);
+ tocpy=MIN(out->data_len-off,16);
+ memcpy(out->data+off,outbuf,tocpy);
+
+ ssl_md5_init(&md5);
+ }
+
+ return(0);
+}
+
+int ssl_create_decoder(SslDecoder *dec, SslCipherSuite *cipher_suite,
+ guint8 *mk, guint8 *sk, guint8 *iv)
+{
+ int ciph=0;
+
+ /* Find the SSLeay cipher */
+ if(cipher_suite->enc!=ENC_NULL) {
+ ssl_debug_printf("ssl_create_decoder CIPHER: %s\n", ciphers[cipher_suite->enc-0x30]);
+ ciph=ssl_get_cipher_by_name(ciphers[cipher_suite->enc-0x30]);
+ }
+ if (ciph == 0) {
+ ssl_debug_printf("ssl_create_decoder can't find cipher %s\n",
+ ciphers[cipher_suite->enc-0x30]);
+ return -1;
+ }
+
+ /* init mac buffer: mac storage is embedded into decoder struct to save a
+ memory allocation and waste samo more memory*/
+ dec->cipher_suite=cipher_suite;
+ dec->mac_key.data = dec->_mac_key;
+ ssl_data_set(&dec->mac_key, mk, cipher_suite->dig_len);
+
+ if (ssl_cipher_init(&dec->evp,ciph,sk,iv,cipher_suite->mode) < 0) {
+ ssl_debug_printf("ssl_create_decoder: can't create cipher id:%d mode:%d\n",
+ ciph, cipher_suite->mode);
+ return -1;
+ }
+
+ ssl_debug_printf("decoder initialized (digest len %d)\n", cipher_suite->dig_len);
+ return 0;
+}
+
+int ssl_generate_keyring_material(SslDecryptSession*ssl_session)
+{
+ StringInfo key_block;
+ guint8 _iv_c[MAX_BLOCK_SIZE],_iv_s[MAX_BLOCK_SIZE];
+ guint8 _key_c[MAX_KEY_SIZE],_key_s[MAX_KEY_SIZE];
+ int needed;
+ guint8 *ptr,*c_wk,*s_wk,*c_mk,*s_mk,*c_iv = _iv_c,*s_iv = _iv_s;
+
+ /* if master_key is not yet generate, create it now*/
+ if (!(ssl_session->state & SSL_MASTER_SECRET)) {
+ ssl_debug_printf("ssl_generate_keyring_material:PRF(pre_master_secret)\n");
+ if (PRF(ssl_session,&ssl_session->pre_master_secret,"master secret",
+ &ssl_session->client_random,
+ &ssl_session->server_random, &ssl_session->master_secret)) {
+ ssl_debug_printf("ssl_generate_keyring_material can't generate master_secret\n");
+ return -1;
+ }
+ ssl_print_string("master secret",&ssl_session->master_secret);
+ }
+
+ /* Compute the key block. First figure out how much data we need*/
+ needed=ssl_session->cipher_suite.dig_len*2;
+ needed+=ssl_session->cipher_suite.bits / 4;
+ if(ssl_session->cipher_suite.block>1)
+ needed+=ssl_session->cipher_suite.block*2;
+
+ key_block.data_len = needed;
+ key_block.data = g_malloc(needed);
+ if (!key_block.data) {
+ ssl_debug_printf("ssl_generate_keyring_material can't allacate key_block\n");
+ return -1;
+ }
+ ssl_debug_printf("ssl_generate_keyring_material sess key generation\n");
+ if (PRF(ssl_session,&ssl_session->master_secret,"key expansion",
+ &ssl_session->server_random,&ssl_session->client_random,
+ &key_block)) {
+ ssl_debug_printf("ssl_generate_keyring_material can't generate key_block\n");
+ goto fail;
+ }
+ ssl_print_string("key expansion", &key_block);
+
+ ptr=key_block.data;
+ c_mk=ptr; ptr+=ssl_session->cipher_suite.dig_len;
+ s_mk=ptr; ptr+=ssl_session->cipher_suite.dig_len;
+
+ c_wk=ptr; ptr+=ssl_session->cipher_suite.eff_bits/8;
+ s_wk=ptr; ptr+=ssl_session->cipher_suite.eff_bits/8;
+
+ if(ssl_session->cipher_suite.block>1){
+ c_iv=ptr; ptr+=ssl_session->cipher_suite.block;
+ s_iv=ptr; ptr+=ssl_session->cipher_suite.block;
+ }
+
+ if(ssl_session->cipher_suite.export){
+ StringInfo iv_c,iv_s;
+ StringInfo key_c,key_s;
+ StringInfo k;
+
+ if(ssl_session->cipher_suite.block>1){
+
+ /* We only have room for MAX_BLOCK_SIZE bytes IVs, but that's
+ all we should need. This is a sanity check */
+ if(ssl_session->cipher_suite.block>MAX_BLOCK_SIZE) {
+ ssl_debug_printf("ssl_generate_keyring_material cipher suite block must be at most %d nut is %d\n",
+ MAX_BLOCK_SIZE, ssl_session->cipher_suite.block);
+ goto fail;
+ }
+
+ iv_c.data = _iv_c;
+ iv_c.data_len = ssl_session->cipher_suite.block;
+ iv_s.data = _iv_s;
+ iv_s.data_len = ssl_session->cipher_suite.block;
+
+ if(ssl_session->version_netorder==SSLV3_VERSION){
+ ssl_debug_printf("ssl_generate_keyring_material ssl3_generate_export_iv\n");
+ if (ssl3_generate_export_iv(&ssl_session->client_random,
+ &ssl_session->server_random,&iv_c)) {
+ ssl_debug_printf("ssl_generate_keyring_material can't generate sslv3 client iv\n");
+ goto fail;
+ }
+ ssl_debug_printf("ssl_generate_keyring_material ssl3_generate_export_iv(2)\n");
+ if (ssl3_generate_export_iv(&ssl_session->server_random,
+ &ssl_session->client_random,&iv_s)) {
+ ssl_debug_printf("ssl_generate_keyring_material can't generate sslv3 server iv\n");
+ goto fail;
+ }
+ }
+ else{
+ guint8 _iv_block[MAX_BLOCK_SIZE * 2];
+ StringInfo iv_block;
+ StringInfo key_null;
+ guint8 _key_null;
+
+ key_null.data = &_key_null;
+ key_null.data_len = 0;
+
+ iv_block.data = _iv_block;
+ iv_block.data_len = ssl_session->cipher_suite.block*2;
+
+ ssl_debug_printf("ssl_generate_keyring_material prf(iv_block)\n");
+ if(PRF(ssl_session,&key_null, "IV block",
+ &ssl_session->client_random,
+ &ssl_session->server_random,&iv_block)) {
+ ssl_debug_printf("ssl_generate_keyring_material can't generate tls31 iv block\n");
+ goto fail;
+ }
+
+ memcpy(_iv_c,iv_block.data,ssl_session->cipher_suite.block);
+ memcpy(_iv_s,iv_block.data+ssl_session->cipher_suite.block,
+ ssl_session->cipher_suite.block);
+ }
+
+ c_iv=_iv_c;
+ s_iv=_iv_s;
+ }
+
+ if (ssl_session->version_netorder==SSLV3_VERSION){
+
+ SSL_MD5_CTX md5;
+ ssl_debug_printf("ssl_generate_keyring_material MD5(client_random)\n");
+ ssl_md5_init(&md5);
+ ssl_md5_update(&md5,c_wk,ssl_session->cipher_suite.eff_bits/8);
+ ssl_md5_update(&md5,ssl_session->client_random.data,
+ ssl_session->client_random.data_len);
+ ssl_md5_update(&md5,ssl_session->server_random.data,
+ ssl_session->server_random.data_len);
+ ssl_md5_final(_key_c,&md5);
+ c_wk=_key_c;
+
+ ssl_md5_init(&md5);
+ ssl_debug_printf("ssl_generate_keyring_material MD5(server_random)\n");
+ ssl_md5_update(&md5,s_wk,ssl_session->cipher_suite.eff_bits/8);
+ ssl_md5_update(&md5,ssl_session->server_random.data,
+ ssl_session->server_random.data_len);
+ ssl_md5_update(&md5,ssl_session->client_random.data,
+ ssl_session->client_random.data_len);
+ ssl_md5_final(_key_s,&md5);
+ s_wk=_key_s;
+ }
+ else{
+ key_c.data = _key_c;
+ key_c.data_len = sizeof(_key_c);
+ key_s.data = _key_s;
+ key_s.data_len = sizeof(_key_s);
+
+ k.data = c_wk;
+ k.data_len = ssl_session->cipher_suite.eff_bits/8;
+ ssl_debug_printf("ssl_generate_keyring_material PRF(key_c)\n");
+ if (PRF(ssl_session,&k,"client write key",
+ &ssl_session->client_random,
+ &ssl_session->server_random, &key_c)) {
+ ssl_debug_printf("ssl_generate_keyring_material can't generate tll31 server key \n");
+ goto fail;
+ }
+ c_wk=_key_c;
+
+ k.data = s_wk;
+ k.data_len = ssl_session->cipher_suite.eff_bits/8;
+ ssl_debug_printf("ssl_generate_keyring_material PRF(key_s)\n");
+ if(PRF(ssl_session,&k,"server write key",
+ &ssl_session->client_random,
+ &ssl_session->server_random, &key_s)) {
+ ssl_debug_printf("ssl_generate_keyring_material can't generate tll31 client key \n");
+ goto fail;
+ }
+ s_wk=_key_s;
+ }
+ }
+
+ /* show key material info */
+ ssl_print_data("Client MAC key",c_mk,ssl_session->cipher_suite.dig_len);
+ ssl_print_data("Server MAC key",s_mk,ssl_session->cipher_suite.dig_len);
+ ssl_print_data("Client Write key",c_wk,ssl_session->cipher_suite.bits/8);
+ ssl_print_data("Server Write key",s_wk,ssl_session->cipher_suite.bits/8);
+
+ if(ssl_session->cipher_suite.block>1) {
+ ssl_print_data("Client Write IV",c_iv,ssl_session->cipher_suite.block);
+ ssl_print_data("Server Write IV",s_iv,ssl_session->cipher_suite.block);
+ }
+ else {
+ ssl_print_data("Client Write IV",c_iv,8);
+ ssl_print_data("Server Write IV",s_iv,8);
+ }
+
+ /* create both client and server ciphers*/
+ ssl_debug_printf("ssl_generate_keyring_material ssl_create_decoder(client)\n");
+ if (ssl_create_decoder(&ssl_session->client,
+ &ssl_session->cipher_suite,c_mk,c_wk,c_iv)) {
+ ssl_debug_printf("ssl_generate_keyring_material can't init client decoder\n");
+ goto fail;
+ }
+ ssl_debug_printf("ssl_generate_keyring_material ssl_create_decoder(server)\n");
+ if (ssl_create_decoder(&ssl_session->server,
+ &ssl_session->cipher_suite,s_mk,s_wk,s_iv)) {
+ ssl_debug_printf("ssl_generate_keyring_material can't init client decoder\n");
+ goto fail;
+ }
+
+ g_free(key_block.data);
+ return 0;
+
+fail:
+ free(key_block.data);
+ return -1;
+}
+
+int ssl_decrypt_pre_master_secret(SslDecryptSession*ssl_session,
+ StringInfo* entrypted_pre_master, SSL_PRIVATE_KEY *pk)
+{
+ int i;
+
+ if(ssl_session->cipher_suite.kex!=KEX_RSA) {
+ ssl_debug_printf("ssl_decrypt_pre_master_secret key %d diferent from KEX_RSA(%d)\n",
+ ssl_session->cipher_suite.kex, KEX_RSA);
+ return(-1);
+ }
+
+#if 0
+ /* can't find any place where ephemeral_rsa is set ...*/
+ if(d->ephemeral_rsa) {
+ ssl_debug_printf("ssl_decrypt_pre_master_secret ephimeral RSA\n");
+ return(-1);
+ }
+#endif
+
+ /* with tls key loading will fail if not rsa type, so no need to check*/
+ ssl_print_string("pre master encrypted",entrypted_pre_master);
+ ssl_debug_printf("ssl_decrypt_pre_master_secret:RSA_private_decrypt\n");
+ i=ssl_private_decrypt(entrypted_pre_master->data_len,
+ entrypted_pre_master->data, pk);
+
+ if (i!=48) {
+ ssl_debug_printf("ssl_decrypt_pre_master_secret wrong "
+ "pre_master_secret lenght (%d, expected %d)\n", i, 48);
+ return -1;
+ }
+
+ /* the decrypted data has been written into the pre_master key buffer */
+ ssl_session->pre_master_secret.data = entrypted_pre_master->data;
+ ssl_session->pre_master_secret.data_len=48;
+ ssl_print_string("pre master secret",&ssl_session->pre_master_secret);
+
+ /* Remove the master secret if it was there.
+ This force keying material regeneration in
+ case we're renegotiating */
+ ssl_session->state &= ~SSL_MASTER_SECRET;
+ return 0;
+}
+
+#define MSB(a) ((a>>8)&0xff)
+#define LSB(a) (a&0xff)
+
+/* convert network byte order 32 byte number to right-aligned host byte order *
+ * 8 bytes buffer */
+static int fmt_seq(guint32 num, guint8* buf)
+{
+ guint32 netnum;
+
+ memset(buf,0,8);
+ netnum=g_htonl(num);
+ memcpy(buf+4,&netnum,4);
+
+ return(0);
+}
+
+static int tls_check_mac(SslDecoder*decoder, int ct,int ver, guint8* data,
+ guint32 datalen, guint8* mac)
+{
+ SSL_HMAC hm;
+ int md;
+ guint32 l;
+ guint8 buf[20];
+
+ memset(&hm, 0, sizeof(hm));
+ md=ssl_get_digest_by_name(digests[decoder->cipher_suite->dig-0x40]);
+ ssl_debug_printf("tls_check_mac mac type:%s md %d\n",
+ digests[decoder->cipher_suite->dig-0x40], md);
+
+ ssl_hmac_init(&hm,decoder->mac_key.data,decoder->mac_key.data_len,md);
+
+ fmt_seq(decoder->seq,buf);
+ decoder->seq++;
+ ssl_hmac_update(&hm,buf,8);
+
+ buf[0]=ct;
+ ssl_hmac_update(&hm,buf,1);
+
+ buf[0]=MSB(ver);
+ buf[1]=LSB(ver);
+ ssl_hmac_update(&hm,buf,2);
+
+ buf[0]=MSB(datalen);
+ buf[1]=LSB(datalen);
+ ssl_hmac_update(&hm,buf,2);
+
+ ssl_hmac_update(&hm,data,datalen);
+
+ ssl_hmac_final(&hm,buf,&l);
+ ssl_print_data("Mac", buf, l);
+ if(memcmp(mac,buf,l))
+ return -1;
+
+ ssl_hmac_cleanup(&hm);
+ return(0);
+}
+
+int ssl3_check_mac(SslDecoder*decoder,int ct,guint8* data,
+ guint32 datalen, guint8* mac)
+{
+ SSL_MD mc;
+ int md;
+ guint32 l;
+ guint8 buf[64],dgst[20];
+ int pad_ct;
+
+ pad_ct=(decoder->cipher_suite->dig==DIG_SHA)?40:48;
+
+ /* get cipher used for digest comptuation */
+ md=ssl_get_digest_by_name(digests[decoder->cipher_suite->dig-0x40]);
+ ssl_debug_printf("ssl3_check_mac digest%s md %d\n",
+ digests[decoder->cipher_suite->dig-0x40], md);
+ memset(&mc, 0, sizeof(mc));
+ ssl_md_init(&mc,md);
+ ssl_debug_printf("ssl3_check_mac memory digest %p\n",mc);
+
+ /* do hash computation on data && padding */
+ ssl_md_update(&mc,decoder->mac_key.data,decoder->mac_key.data_len);
+
+ memset(buf,0x36,pad_ct);
+ ssl_md_update(&mc,buf,pad_ct);
+
+ fmt_seq(decoder->seq,buf);
+ decoder->seq++;
+ ssl_md_update(&mc,buf,8);
+
+ buf[0]=ct;
+ ssl_md_update(&mc,buf,1);
+
+ buf[0]=MSB(datalen);
+ buf[1]=LSB(datalen);
+ ssl_md_update(&mc,buf,2);
+ ssl_md_update(&mc,data,datalen);
+
+ ssl_md_final(&mc,dgst,&l);
+
+ ssl_md_init(&mc,md);
+
+ ssl_md_update(&mc,decoder->mac_key.data,decoder->mac_key.data_len);
+
+ memset(buf,0x5c,pad_ct);
+ ssl_md_update(&mc,buf,pad_ct);
+ ssl_md_update(&mc,dgst,l);
+
+ ssl_md_final(&mc,dgst,&l);
+
+ if(memcmp(mac,dgst,l))
+ return -1;
+
+ return(0);
+}
+
+int ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, int ct,
+ const unsigned char* in, int inl,unsigned char*out,int* outl)
+{
+ int pad, worklen;
+ guint8 *mac;
+
+ ssl_debug_printf("ssl_decrypt_record ciphertext len %d\n", inl);
+ ssl_print_data("Ciphertext",in, inl);
+
+ /* First decrypt*/
+ if ((pad = ssl_cipher_decrypt(&decoder->evp,out,*outl,in,inl))!= 0)
+ ssl_debug_printf("ssl_decrypt_record: %s %s\n", gcry_strsource (pad),
+ gcry_strerror (pad));
+
+ ssl_print_data("Plaintext",out,inl);
+ worklen=inl;
+
+ /* Now strip off the padding*/
+ if(decoder->cipher_suite->block!=1){
+ pad=out[inl-1];
+ worklen-=(pad+1);
+ ssl_debug_printf("ssl_decrypt_record found padding %d final len %d\n",
+ pad, *outl);
+ }
+
+ /* And the MAC */
+ worklen-=decoder->cipher_suite->dig_len;
+ if (worklen < 0)
+ {
+ ssl_debug_printf("ssl_decrypt_record wrong record len/padding outlen %d\n work %d\n",*outl, worklen);
+ return -1;
+ }
+ mac=out+worklen;
+ /*ssl_print_data("Record data",out,*outl);*/
+
+ /* Now check the MAC */
+ ssl_debug_printf("checking mac (len %d, version %X, ct %d)\n", worklen,ssl->version_netorder, ct);
+ if(ssl->version_netorder==0x300){
+ if(ssl3_check_mac(decoder,ct,out,worklen,mac) < 0) {
+ ssl_debug_printf("ssl_decrypt_record: mac falied\n");
+ return -1;
+ }
+ }
+ else{
+ if(tls_check_mac(decoder,ct,ssl->version_netorder,out,worklen,mac)< 0) {
+ ssl_debug_printf("ssl_decrypt_record: mac falied\n");
+ return -1;
+ }
+ }
+ ssl_debug_printf("ssl_decrypt_record: mac ok\n");
+ *outl = worklen;
+ return(0);
+}
+
+/* old relase of gnutls does not define the appropriate macros, so get
+ * them from the string*/
+void ssl_get_version(int* major, int* minor, int* patch)
+{
+ const char* str = gnutls_check_version(NULL);
+
+ ssl_debug_printf("ssl_get_version: %s\n", str);
+ sscanf(str, "%d.%d.%d", major, minor, patch);
+}
+
+
+SSL_PRIVATE_KEY* ssl_load_key(FILE* fp)
+{
+ /* gnutls make our work much harded, since we have to work internally with
+ * s-exp formatted data, but PEM loader export only in "gnutls_datum"
+ * format, and a datum -> s-exp convertion function does not exist.
+ */
+ struct gnutls_x509_privkey_int* priv_key;
+ gnutls_datum key;
+ gnutls_datum m, e, d, p,q, u;
+ int size, major, minor, patch;
+ unsigned int bytes;
+ unsigned int tmp_size;
+#ifdef SSL_FAST
+ gcry_mpi_t* rsa_params = g_malloc(sizeof(gcry_mpi_t)*6);
+#else
+ gcry_mpi_t rsa_params[6];
+#endif
+ gcry_sexp_t rsa_priv_key;
+
+ /* init private key data*/
+ gnutls_x509_privkey_init(&priv_key);
+
+ /* compute file size and load all file contents into a datum buffer*/
+ if (fseek(fp, 0, SEEK_END) < 0) {
+ ssl_debug_printf("ssl_load_key: can't fseek file\n");
+ return NULL;
+ }
+ if ((size = ftell(fp)) < 0) {
+ ssl_debug_printf("ssl_load_key: can't ftell file\n");
+ return NULL;
+ }
+ if (fseek(fp, 0, SEEK_SET) < 0) {
+ ssl_debug_printf("ssl_load_key: can't refseek file\n");
+ return NULL;
+ }
+ key.data = g_malloc(size);
+ key.size = size;
+ bytes = fread(key.data, 1, key.size, fp);
+ if (bytes < key.size) {
+ ssl_debug_printf("ssl_load_key: can't read from file %d bytes, got %d\n",
+ key.size, bytes);
+ return NULL;
+ }
+
+ /* import PEM data*/
+ if (gnutls_x509_privkey_import(priv_key, &key, GNUTLS_X509_FMT_PEM)!=0) {
+ ssl_debug_printf("ssl_load_key: can't import pem data\n");
+ return NULL;
+ }
+ free(key.data);
+
+ /* RSA get parameter */
+ if (gnutls_x509_privkey_export_rsa_raw(priv_key, &m, &e, &d, &p, &q, &u) != 0) {
+ ssl_debug_printf("ssl_load_key: can't export rsa param (is a rsa private key file ?!?)\n");
+ return NULL;
+ }
+
+ /* convert each rsa parameter to mpi format*/
+ if (gcry_mpi_scan( &rsa_params[0], GCRYMPI_FMT_USG, m.data, m.size, &tmp_size) !=0) {
+ ssl_debug_printf("ssl_load_key: can't convert m rsa param to int (size %d)\n", m.size);
+ return NULL;
+ }
+
+ if (gcry_mpi_scan( &rsa_params[1], GCRYMPI_FMT_USG, e.data, e.size, &tmp_size) != 0) {
+ ssl_debug_printf("ssl_load_key: can't convert e rsa param to int (size %d)\n", e.size);
+ return NULL;
+ }
+
+ // note: openssl and gnutls use 'p' and 'q' with opposite meaning:
+ // our 'p' must be equal to 'q' as provided from openssl and viceversa
+ if (gcry_mpi_scan( &rsa_params[2], GCRYMPI_FMT_USG, d.data, d.size, &tmp_size) !=0) {
+ ssl_debug_printf("ssl_load_key: can't convert d rsa param to int (size %d)\n", d.size);
+ return NULL;
+ }
+
+ if (gcry_mpi_scan( &rsa_params[3], GCRYMPI_FMT_USG, q.data, q.size, &tmp_size) !=0) {
+ ssl_debug_printf("ssl_load_key: can't convert q rsa param to int (size %d)\n", q.size);
+ return NULL;
+ }
+
+ if (gcry_mpi_scan( &rsa_params[4], GCRYMPI_FMT_USG, p.data, p.size, &tmp_size) !=0) {
+ ssl_debug_printf("ssl_load_key: can't convert p rsa param to int (size %d)\n", p.size);
+ return NULL;
+ }
+
+ if (gcry_mpi_scan( &rsa_params[5], GCRYMPI_FMT_USG, u.data, u.size, &tmp_size) !=0) {
+ ssl_debug_printf("ssl_load_key: can't convert u rsa param to int (size %d)\n", m.size);
+ return NULL;
+ }
+
+ ssl_get_version(&major, &minor, &patch);
+
+ /* certain versions of gnutls require swap of rsa params 'p' and 'q' */
+ if ((major <= 1) && (minor <= 0) && (patch <=13))
+ {
+ gcry_mpi_t tmp;
+ ssl_debug_printf("ssl_load_key: swapping p and q parametes\n");
+ tmp = rsa_params[4];
+ rsa_params[4] = rsa_params[3];
+ rsa_params[3] = tmp;
+ }
+
+ if (gcry_sexp_build( &rsa_priv_key, NULL,
+ "(private-key(rsa((n%m)(e%m)(d%m)(p%m)(q%m)(u%m))))", rsa_params[0],
+ rsa_params[1], rsa_params[2], rsa_params[3], rsa_params[4],
+ rsa_params[5]) != 0) {
+ ssl_debug_printf("ssl_load_key: can't built rsa private key s-exp\n");
+ return NULL;
+ }
+
+#if SSL_FAST
+ return rsa_params;
+#else
+ {
+ int i;
+ for (i=0; i< 6; i++)
+ gcry_mpi_release(rsa_params[i]);
+ }
+ return rsa_priv_key;
+#endif
+}
+
+void ssl_free_key(SSL_PRIVATE_KEY* key)
+{
+#if SSL_FAST
+ int i;
+ for (i=0; i< 6; i++)
+ gcry_mpi_release(key[i]);
+#else
+ gcry_sexp_release(key);
+#endif
+}
+
+static FILE* myout=NULL;
+void ssl_lib_init(void)
+{
+ gnutls_global_init();
+
+#ifdef _WIN32
+ /* we don't have standard I/O file available, open a log */
+ myout = fopen("ssl-decrypt.txt","w");
+ if (!myout)
+#endif
+ myout = stderr;
+}
+
+
+#else
+/* no libgnutl: dummy operation to keep interface consistent*/
+void ssl_lib_init(void)
+{
+}
+
+SSL_PRIVATE_KEY* ssl_load_key(FILE* fp)
+{
+ ssl_debug_printf("ssl_load_key: impossible without glutls. fp %p\n",fp);
+ return NULL;
+}
+void ssl_free_key(SSL_PRIVATE_KEY* key)
+{
+}
+
+int ssl_find_cipher(int num,SslCipherSuite* cs)
+{
+ ssl_debug_printf("ssl_find_cipher: dummy without glutls. num %d cs %p\n",
+ num,cs);
+ return 0;
+}
+int ssl_generate_keyring_material(SslDecryptSession*ssl)
+{
+ ssl_debug_printf("ssl_generate_keyring_material: impossible without glutls. ssl %p\n",
+ ssl);
+ return 0;
+}
+int ssl_decrypt_pre_master_secret(SslDecryptSession* ssl_session,
+ StringInfo* entrypted_pre_master, SSL_PRIVATE_KEY *pk)
+{
+ ssl_debug_printf("ssl_decrypt_pre_master_secret: impossible without glutls."
+ " ssl %p entrypted_pre_master %p pk %p\n", ssl_session,
+ entrypted_pre_master, pk);
+ return 0;
+}
+
+int ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, int ct,
+ const unsigned char* in, int inl,unsigned char*out,int* outl)
+{
+ ssl_debug_printf("ssl_decrypt_record: impossible without gnutls. ssl %p"
+ "decoder %p ct %d, in %p inl %d out %p outl %d\n", ssl, decoder, ct,
+ in, inl, out, outl);
+ return 0;
+}
+
+#endif
+
+/* get ssl data for this session. if no ssl data is found allocate a new one*/
+void ssl_session_init(SslDecryptSession* ssl_session)
+{
+ ssl_debug_printf("ssl_session_init: initializing ptr %p size %d\n",
+ ssl_session, sizeof(SslDecryptSession));
+
+ ssl_session->master_secret.data = ssl_session->_master_secret;
+ ssl_session->session_id.data = ssl_session->_session_id;
+ ssl_session->client_random.data = ssl_session->_client_random;
+ ssl_session->server_random.data = ssl_session->_server_random;
+ ssl_session->master_secret.data_len = 48;
+}
+
+int ssl_data_init(StringInfo* str, unsigned char* src, unsigned int len)
+{
+ str->data = g_realloc(str->data,len);
+ if (!str->data)
+ return -1;
+ if (src)
+ memcpy(str->data, src,len);
+ str->data_len = len;
+ return 0;
+}
+
+int ssl_data_alloc(StringInfo* str, unsigned int len)
+{
+ str->data = g_malloc(len);
+ if (!str->data)
+ return -1;
+ str->data_len = len;
+ return 0;
+}
+
+int ssl_data_set(StringInfo* str, unsigned char* data, unsigned int len)
+{
+ memcpy(str->data, data, len);
+ str->data_len = len;
+ return 0;
+}
+
+#ifdef SSL_DECRYPT_DEBUG
+void ssl_debug_printf(const char* fmt, ...)
+{
+ va_list ap;
+ int ret=0;
+ va_start(ap, fmt);
+ ret += vfprintf(myout, fmt, ap);
+ va_end(ap);
+ fflush(myout);
+}
+
+
+void ssl_print_text_data(const char* name, const unsigned char* data, int len)
+{
+ int i;
+ fprintf(myout,"%s: ",name);
+ for (i=0; i< len; i++) {
+ fprintf(myout,"%c",data[i]);
+ }
+ fprintf(myout,"\n");
+ fflush(myout);
+}
+
+void ssl_print_data(const char* name, const unsigned char* data, int len)
+{
+ int i;
+ fprintf(myout,"%s[%d]:\n",name, len);
+ for (i=0; i< len; i++) {
+ if ((i>0) && (i%16 == 0))
+ fprintf(myout,"\n");
+ fprintf(myout,"%.2x ",data[i]&255);
+ }
+ fprintf(myout,"\n");
+ fflush(myout);
+}
+
+void ssl_print_string(const char* name, const StringInfo* data)
+{
+ ssl_print_data(name, data->data, data->data_len);
+}
+#endif
diff --git a/epan/dissectors/packet-ssl-utils.h b/epan/dissectors/packet-ssl-utils.h
new file mode 100644
index 0000000000..0ec60353c0
--- /dev/null
+++ b/epan/dissectors/packet-ssl-utils.h
@@ -0,0 +1,172 @@
+/* packet-ss-utils.c
+ *
+ * $Id$
+ *
+ * ssl manipulation functions
+ * By Paolo Abeni <paolo.abeni@email.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SSL_UTILS_H_
+#define __SSL_UTILS_H_
+
+#include <glib.h>
+
+#ifdef HAVE_LIBGNUTLS
+
+#ifdef _WIN32
+/* #include <gnutls_conf.h> */
+#include <gcrypt_conf.h>
+#endif
+
+#include <stdio.h>
+#include <gcrypt.h>
+#include <gnutls/x509.h>
+#include <gnutls/openssl.h>
+
+/* #define SSL_FAST 1 */
+#define SSL_DECRYPT_DEBUG
+
+#define SSL_CIPHER_CTX gcry_cipher_hd_t
+#ifdef SSL_FAST
+#define SSL_PRIVATE_KEY gcry_mpi_t
+#else
+#define SSL_PRIVATE_KEY struct gcry_sexp
+#endif
+#else
+#define SSL_CIPHER_CTX void*
+#define SSL_PRIVATE_KEY void
+#endif
+
+typedef struct _StringInfo {
+ unsigned char* data;
+ unsigned int data_len;
+} StringInfo;
+
+#define SSL_WRITE_KEY 1
+
+#define SSLV3_VERSION 0x300
+#define TLSV1_VERSION 0x301
+
+#define SSL_CLIENT_RANDOM 1
+#define SSL_SERVER_RANDOM 2
+#define SSL_CIPHER 4
+#define SSL_HAVE_SESSION_KEY 8
+#define SSL_VERSION 0x10
+#define SSL_MASTER_SECRET 0x20
+
+#define SSL_CIPHER_MODE_STREAM 0
+#define SSL_CIPHER_MODE_CBC 1
+
+typedef struct _SslCipherSuite {
+ int number;
+ int kex;
+ int sig;
+ int enc;
+ int block;
+ int bits;
+ int eff_bits;
+ int dig;
+ int dig_len;
+ int export;
+ int mode;
+} SslCipherSuite;
+
+typedef struct _SslDecoder {
+ SslCipherSuite* cipher_suite;
+ unsigned char _mac_key[20];
+ StringInfo mac_key;
+ SSL_CIPHER_CTX evp;
+ guint32 seq;
+} SslDecoder;
+
+#define KEX_RSA 0x10
+#define KEX_DH 0x11
+
+#define SIG_RSA 0x20
+#define SIG_DSS 0x21
+#define SIG_NONE 0x22
+
+#define ENC_DES 0x30
+#define ENC_3DES 0x31
+#define ENC_RC4 0x32
+#define ENC_RC2 0x33
+#define ENC_IDEA 0x34
+#define ENC_AES 0x35
+#define ENC_AES256 0x36
+#define ENC_NULL 0x37
+
+#define DIG_MD5 0x40
+#define DIG_SHA 0x41
+
+/*typedef struct _SslService {
+ address addr;
+ guint port;
+} SslService;*/
+
+typedef struct _SslDecryptSession {
+ unsigned char _master_secret[48];
+ unsigned char _session_id[256];
+ unsigned char _client_random[32];
+ unsigned char _server_random[32];
+ StringInfo session_id;
+ StringInfo server_random;
+ StringInfo client_random;
+ StringInfo master_secret;
+ StringInfo pre_master_secret;
+
+ int cipher;
+ int state;
+ SslCipherSuite cipher_suite;
+ SslDecoder server;
+ SslDecoder client;
+ SSL_PRIVATE_KEY* private_key;
+ guint32 version;
+ guint16 version_netorder;
+
+} SslDecryptSession;
+
+void ssl_lib_init(void);
+void ssl_session_init(SslDecryptSession*);
+int ssl_data_alloc(StringInfo* str, unsigned int len);
+int ssl_data_set(StringInfo* data, unsigned char* src, unsigned int len);
+
+SSL_PRIVATE_KEY* ssl_load_key(FILE* fp);
+void ssl_free_key(SSL_PRIVATE_KEY*);
+
+int ssl_find_cipher(int num,SslCipherSuite* cs);
+
+int ssl_generate_keyring_material(SslDecryptSession*ssl_session);
+
+int ssl_decrypt_pre_master_secret(SslDecryptSession*ssl_session,
+ StringInfo* entrypted_pre_master, SSL_PRIVATE_KEY *pk);
+
+int ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, int ct,
+ const unsigned char* in, int inl,unsigned char*out,int* outl);
+
+#ifdef SSL_DECRYPT_DEBUG
+void ssl_debug_printf(const char* fmt,...);
+void ssl_print_data(const char* name, const unsigned char* data, int len);
+void ssl_print_string(const char* name, const StringInfo* data);
+void ssl_print_text_data(const char* name, const unsigned char* data, int len);
+#else
+static inline char* ssl_debug_printf(const char* fmt,...) { return fmt; }
+#define ssl_print_data(a, b, c)
+#define ssl_print_string(a, b)
+#define ssl_print_text_data(a, b, c)
+#endif
+
+#endif
diff --git a/epan/dissectors/packet-ssl.c b/epan/dissectors/packet-ssl.c
index fd3357887f..8373c36d43 100644
--- a/epan/dissectors/packet-ssl.c
+++ b/epan/dissectors/packet-ssl.c
@@ -56,14 +56,7 @@
*
* Notes:
*
- * - Uses conversations in a no-malloc fashion. Since we just want to
- * remember the version of the conversation, we store the version
- * integer directly in the void *data member of the conversation
- * structure. This means that we don't have to manage any memory,
- * but will cause problems if anyone assumes that all data pointers
- * are actually pointers to memory allocated by g_mem_chunk_alloc.
- *
- * - Does not support decryption of encrypted frames, nor dissection
+ * - Does not support dissection
* of frames that would require state maintained between frames
* (e.g., single ssl records spread across multiple tcp frames)
*
@@ -82,6 +75,17 @@
* - Request Certificate
* - Client Certificate
*
+ * - Decryption is supported only for session that use RSA key exchange,
+ * if the host private key is provided via preference.
+ *
+ * - Decryption need to be performed 'sequentially', so it's done
+ * at packet reception time. This may cause a significative packet capture
+ * slow down. This also cause do dissect some ssl info that in previous
+ * dissector version were dissected only when a proto_tree context was
+ * available
+ *
+ * We are at Packet reception if time pinfo->fd->flags.visited == 0
+ *
*/
#ifdef HAVE_CONFIG_H
@@ -96,7 +100,12 @@
#include <epan/conversation.h>
#include <epan/prefs.h>
+#include <epan/inet_v6defs.h>
#include <epan/dissectors/packet-x509af.h>
+#include <epan/emem.h>
+#include <epan/tap.h>
+#include "packet-ssl-utils.h"
+
static gboolean ssl_desegment = TRUE;
@@ -108,12 +117,14 @@ static gboolean ssl_desegment = TRUE;
*********************************************************************/
/* Initialize the protocol and registered fields */
+static int ssl_tap = -1;
static int proto_ssl = -1;
static int hf_ssl_record = -1;
static int hf_ssl_record_content_type = -1;
static int hf_ssl_record_version = -1;
static int hf_ssl_record_length = -1;
static int hf_ssl_record_appdata = -1;
+static int hf_ssl_record_appdata_decrypted = -1;
static int hf_ssl2_record = -1;
static int hf_ssl2_record_is_escape = -1;
static int hf_ssl2_record_padding_length = -1;
@@ -199,6 +210,321 @@ static gint ett_pct_hash_suites = -1;
static gint ett_pct_cert_suites = -1;
static gint ett_pct_exch_suites = -1;
+typedef struct {
+ unsigned int ssl_port;
+ unsigned int decrypted_port;
+ dissector_handle_t handle;
+ char* info;
+} SslAssociation;
+
+#ifdef _WIN32
+#define TEST_DIR "\\Program Files\\Ethereal\\esp_data\\"
+#else
+#define TEST_DIR "/usr/share/ethereal-ssl-decrypt/"
+#endif
+static char* ssl_keys_list = "127.0.0.1:443:"TEST_DIR"server.key,"
+ "127.0.0.1:4433:"TEST_DIR"server.pem";
+static char* ssl_ports_list = NULL;
+
+typedef struct _SslService {
+ address addr;
+ guint port;
+} SslService;
+
+static GHashTable *ssl_session_hash = NULL;
+static GHashTable *ssl_key_hash = NULL;
+static GTree* ssl_associations = NULL;
+static dissector_handle_t ssl_handle = NULL;
+static StringInfo ssl_decrypted_data = {NULL, 0};
+
+/* Hash Functions for ssl sessions table and private keys table*/
+static gint ssl_equal (gconstpointer v, gconstpointer v2)
+{
+ const StringInfo *val1 = (const StringInfo *)v;
+ const StringInfo *val2 = (const StringInfo *)v2;
+
+ if (val1->data_len == val2->data_len &&
+ !memcmp(val1->data, val2->data, val2->data_len)) {
+ return 1;
+ }
+ return 0;
+}
+
+static guint ssl_hash (gconstpointer v)
+{
+ guint l,hash = 0;
+ StringInfo* id = (StringInfo*) v;
+ guint* cur = (guint*) id->data;
+ for (l=4; (l<id->data_len); l+=4, cur++)
+ hash = hash ^ (*cur);
+
+ return hash;
+}
+
+static gint ssl_private_key_equal (gconstpointer v, gconstpointer v2)
+{
+ const SslService *val1 = (const SslService *)v;
+ const SslService *val2 = (const SslService *)v2;
+
+ if ((val1->port == val2->port) &&
+ ! CMP_ADDRESS(&val1->addr, &val2->addr)) {
+ return 1;
+ }
+ return 0;
+}
+
+static guint ssl_private_key_hash (gconstpointer v)
+{
+ const SslService *key = (const SslService *)v;
+ guint l,hash = key->port, len = key->addr.len;
+
+ guint* cur = (guint*) key->addr.data;
+ for (l=4; (l<len); l+=4, cur++)
+ hash = hash ^ (*cur);
+
+ return hash;
+}
+
+/* private key table entries have a scope 'larger' then packet capture,
+ * so we can't relay on se_alloc** function */
+static void ssl_private_key_free(gpointer id, gpointer key, gpointer dummy)
+{
+ g_free(id);
+ ssl_free_key((SSL_PRIVATE_KEY*) key);
+}
+
+/* handling of association between ssl ports and clear text protocol */
+static void ssl_association_add(unsigned int port, unsigned int ctport,
+ const char* info)
+{
+ dissector_table_t tcp_dissectors = find_dissector_table( "tcp.port");
+ SslAssociation* assoc = g_malloc(sizeof(SslAssociation)+strlen(info)+1);
+
+ assoc->info = (char*) assoc+sizeof(SslAssociation);
+ strcpy(assoc->info, info);
+ assoc->ssl_port = port;
+ assoc->decrypted_port = ctport;
+ assoc->handle = dissector_get_port_handle(tcp_dissectors, ctport);
+
+ ssl_debug_printf("ssl_association_add port %d ctport %d info %s handle %p\n",
+ port, ctport, info, assoc->handle);
+
+ dissector_add("tcp.port", port, ssl_handle);
+ g_tree_insert(ssl_associations, (gpointer)port, assoc);
+}
+
+static gint ssl_association_cmp(gconstpointer a, gconstpointer b)
+{
+ return (gint)a-(gint)b;
+}
+
+static inline SslAssociation* ssl_association_find(unsigned int port)
+{
+ register SslAssociation* ret = g_tree_lookup(ssl_associations, (gpointer)port);
+ ssl_debug_printf("ssl_association_find: port %d found %p\n", port, ret);
+ return ret;
+}
+
+static gint ssl_association_remove_handle (gpointer key,
+ gpointer data, gpointer user_data)
+{
+ SslAssociation* assoc = (SslAssociation*) data;
+ ssl_debug_printf("ssl_association_remove_handle removing ptr %p handle\n",
+ data, assoc->handle);
+ if (assoc->handle)
+ dissector_delete("tcp.port", assoc->ssl_port, assoc->handle);
+ g_free(data);
+ return 0;
+}
+
+static inline int ssl_packet_from_server(unsigned int port)
+{
+ register int ret = ssl_association_find(port) != 0;
+ ssl_debug_printf("ssl_packet_from_server: is from server %d\n", ret);
+ return ret;
+}
+
+/* initialize/reset per capture state data (ssl sessions cache) */
+static void ssl_init(void)
+{
+ if (ssl_session_hash)
+ g_hash_table_destroy(ssl_session_hash);
+ ssl_session_hash = g_hash_table_new(ssl_hash, ssl_equal);
+ if (ssl_decrypted_data.data)
+ g_free(ssl_decrypted_data.data);
+ ssl_decrypted_data.data = g_malloc0(32);
+ ssl_decrypted_data.data_len = 32;
+}
+
+/* parse ssl related preferences (private keys and ports association strings) */
+static void ssl_parse(void)
+{
+ if (ssl_key_hash)
+ {
+ g_hash_table_foreach(ssl_key_hash, ssl_private_key_free, NULL);
+ g_hash_table_destroy(ssl_key_hash);
+ }
+ if (ssl_associations)
+ {
+ g_tree_traverse(ssl_associations, ssl_association_remove_handle, G_IN_ORDER, NULL);
+ g_tree_destroy(ssl_associations);
+ }
+
+ /* parse private keys string, load available keys and put them in key hash*/
+ ssl_key_hash = g_hash_table_new(ssl_private_key_hash,ssl_private_key_equal);
+ ssl_associations = g_tree_new(ssl_association_cmp);
+
+ if (ssl_keys_list && (ssl_keys_list[0] != 0))
+ {
+ char* end;
+ char* start = strdup(ssl_keys_list);
+ char* tmp = start;
+
+ ssl_debug_printf("ssl_init keys string %s\n", start);
+ do {
+ char* addr, *port, *filename;
+ unsigned char* ip;
+ SslService* service;
+ SSL_PRIVATE_KEY * private_key;
+ FILE* fp;
+
+ addr = start;
+ /* split ip/file couple with ',' separator*/
+ end = strchr(start, ',');
+ if (end) {
+ *end = 0;
+ start = end+1;
+ }
+
+ /* for each entry split ip, port, filename with ':' separator */
+ ssl_debug_printf("ssl_init found host entry %s\n", addr);
+ port = strchr(addr, ':');
+ if (!port)
+ break;
+ *port = 0;
+ port++;
+
+ filename = strchr(port,':');
+ if (!filename)
+ break;
+ *filename=0;
+ filename++;
+
+ /* convert ip and port string to network rappresentation*/
+ service = g_malloc(sizeof(SslService) + 4);
+ service->addr.type = AT_IPv4;
+ service->addr.len = 4;
+ service->addr.data = ip = ((unsigned char*)service) + sizeof(SslService);
+ sscanf(addr, "%hhu.%hhu.%hhu.%hhu", &ip[0], &ip[1], &ip[2], &ip[3]);
+ service->port = atoi(port);
+ ssl_debug_printf("ssl_init addr %hhu.%hhu.%hhu.%hhu port %d filename %s\n",
+ ip[0], ip[1], ip[2], ip[3], service->port, filename);
+
+ /* try to load pen file*/
+ fp = fopen(filename, "rb");
+ if (!fp) {
+ fprintf(stderr, "can't open file %s \n",filename);
+ break;
+ }
+
+ private_key = ssl_load_key(fp);
+ if (!private_key) {
+ fprintf(stderr,"can't load private key from %s\n",
+ filename);
+ break;
+ }
+ fclose(fp);
+
+ ssl_debug_printf("ssl_init private key file %s successfully loaded\n",
+ filename);
+ g_hash_table_insert(ssl_key_hash, service, private_key);
+
+ } while (end != NULL);
+ free(tmp);
+ }
+
+ /* parse ssl ports string and add ssl dissector to specifed port[s]*/
+ if (ssl_ports_list && (ssl_ports_list[0] != 0))
+ {
+ char* end;
+ char* start = strdup(ssl_ports_list);
+ char* tmp = start;
+
+ ssl_debug_printf("ssl_init ports string %s\n", start);
+ do {
+ char* port, *ctport, *info;
+ unsigned int portn, ctportn;
+
+ port = start;
+ /* split ip/file couple with ',' separator*/
+ end = strchr(start, ',');
+ if (end) {
+ *end = 0;
+ start = end+1;
+ }
+
+ /* for each entry split ip, port, filename with ':' separator */
+ ssl_debug_printf("ssl_init found port entry %s\n", port);
+ ctport = strchr(port, ':');
+ if (!ctport)
+ break;
+ *ctport = 0;
+ ctport++;
+
+ info = strchr(ctport,':');
+ if (!info)
+ break;
+ *info=0;
+ info++;
+
+ /* add dissector to this port */
+ portn = atoi(port);
+ ctportn = atoi(ctport);
+ if (!portn || !ctportn)
+ break;
+
+ ssl_debug_printf("ssl_init adding dissector to port %d (ct port %d)\n", portn, ctportn);
+ ssl_association_add(portn, ctportn, info);
+ } while (end != NULL);
+ free(tmp);
+ }
+
+ /* [re] add ssl dissection to defaults ports */
+ ssl_association_add(443, 80, "Hypertext transfer protocol");
+ ssl_association_add(636, 389, "Lightweight directory access protocol");
+ ssl_association_add(993, 143, "Interactive mail access protocol");
+ ssl_association_add(995, 110, "Post office protocol");
+}
+
+/* store master secret into session data cache */
+static void ssl_save_session(SslDecryptSession* ssl)
+{
+ /* allocate stringinfo chunks for session id and master secret data*/
+ StringInfo* session_id = se_alloc0(sizeof(StringInfo) + ssl->session_id.data_len);
+ StringInfo* master_secret = se_alloc0(48 + sizeof(StringInfo));
+
+ master_secret->data = ((unsigned char*)master_secret+sizeof(StringInfo));
+ session_id->data = ((unsigned char*)session_id+sizeof(StringInfo));
+
+ ssl_data_set(session_id, ssl->session_id.data, ssl->session_id.data_len);
+ ssl_data_set(master_secret, ssl->master_secret.data, ssl->master_secret.data_len);
+ g_hash_table_insert(ssl_session_hash, session_id, master_secret);
+ ssl_print_string("ssl_save_session stored session id", session_id);
+ ssl_print_string("ssl_save_session stored master secret", master_secret);
+}
+
+static void ssl_restore_session(SslDecryptSession* ssl)
+{
+ StringInfo* ms = g_hash_table_lookup(ssl_session_hash, &ssl->session_id);
+ if (!ms) {
+ ssl_debug_printf("ssl_restore_session can't find stored session\n");
+ return;
+ }
+ ssl_data_set(&ssl->master_secret, ms->data, ms->data_len);
+ ssl->state |= SSL_MASTER_SECRET;
+ ssl_debug_printf("ssl_restore_session master key retrived\n");
+}
+
/* The TCP port to associate with by default */
#define TCP_PORT_SSL 443
#define TCP_PORT_SSL_LDAP 636
@@ -704,7 +1030,8 @@ static const value_string tls_hello_extension_types[] = {
static int dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, guint32 offset,
guint *conv_version,
- gboolean *need_desegmentation);
+ gboolean *need_desegmentation,
+ SslDecryptSession *conv_data);
/* change cipher spec dissector */
static void dissect_ssl3_change_cipher_spec(tvbuff_t *tvb,
@@ -721,16 +1048,19 @@ static void dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo,
static void dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, guint32 offset,
guint32 record_length,
- guint *conv_version, guint8 content_type);
+ guint *conv_version,
+ SslDecryptSession *conv_data, guint8 content_type);
static void dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
proto_tree *tree,
- guint32 offset, guint32 length);
+ guint32 offset, guint32 length,
+ SslDecryptSession* ssl);
static void dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb,
proto_tree *tree,
- guint32 offset, guint32 length);
+ guint32 offset, guint32 length,
+ SslDecryptSession* ssl);
static void dissect_ssl3_hnd_cert(tvbuff_t *tvb,
proto_tree *tree, guint32 offset, packet_info *pinfo);
@@ -742,7 +1072,7 @@ static void dissect_ssl3_hnd_cert_req(tvbuff_t *tvb,
static void dissect_ssl3_hnd_finished(tvbuff_t *tvb,
proto_tree *tree,
guint32 offset,
- guint *conv_version);
+ guint* conv_version);
/*
@@ -754,12 +1084,14 @@ static void dissect_ssl3_hnd_finished(tvbuff_t *tvb,
static int dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, guint32 offset,
guint *conv_version,
- gboolean *need_desegmentation);
+ gboolean *need_desegmentation,
+ SslDecryptSession* ssl);
/* client hello dissector */
static void dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
proto_tree *tree,
- guint32 offset);
+ guint32 offset,
+ SslDecryptSession* ssl);
static void dissect_pct_msg_client_hello(tvbuff_t *tvb,
proto_tree *tree,
@@ -794,7 +1126,7 @@ static void dissect_pct_msg_error(tvbuff_t *tvb,
* Support Functions
*
*/
-static void ssl_set_conv_version(packet_info *pinfo, guint version);
+/*static void ssl_set_conv_version(packet_info *pinfo, guint version);*/
static int ssl_is_valid_handshake_type(guint8 type);
static int ssl_is_valid_content_type(guint8 type);
static int ssl_is_valid_ssl_version(guint16 version);
@@ -809,7 +1141,6 @@ static int ssl_looks_like_valid_v2_handshake(tvbuff_t *tvb,
static int ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb,
guint32 offset,
guint32 record_length);
-
/*********************************************************************
*
* Main dissector
@@ -824,12 +1155,13 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
conversation_t *conversation;
void *conv_data;
- guint conv_version = SSL_VER_UNKNOWN;
proto_item *ti = NULL;
proto_tree *ssl_tree = NULL;
guint32 offset = 0;
gboolean first_record_in_frame = TRUE;
gboolean need_desegmentation;
+ SslDecryptSession* ssl_session = NULL;
+ guint* conv_version;
/* Track the version using conversations to reduce the
* chance that a packet that simply *looks* like a v2 or
@@ -852,10 +1184,48 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
pinfo->srcport, pinfo->destport, 0);
}
conv_data = conversation_get_proto_data(conversation, proto_ssl);
+
+ /* PAOLO: manage ssl decryption data */
+ /*get a valid ssl session pointer*/
if (conv_data != NULL)
- {
- conv_version = GPOINTER_TO_UINT(conv_data);
+ ssl_session = conv_data;
+ else {
+ SslService dummy;
+
+ ssl_session = se_alloc0(sizeof(SslDecryptSession));
+ ssl_session_init(ssl_session);
+ ssl_session->version = SSL_VER_UNKNOWN;
+ conversation_add_proto_data(conversation, proto_ssl, ssl_session);
+
+ /* we need to know witch side of conversation is speaking*/
+ if (ssl_packet_from_server(pinfo->srcport)) {
+ dummy.addr = pinfo->net_src;
+ dummy.port = pinfo->srcport;
+ }
+ else {
+ dummy.addr = pinfo->net_dst;
+ dummy.port = pinfo->destport;
+ }
+ ssl_debug_printf("dissect_ssl server %hhd.%hhd.%hhd.%hhd:%d\n",
+ dummy.addr.data[0],
+ dummy.addr.data[1],dummy.addr.data[2],
+ dummy.addr.data[3],dummy.port);
+
+ /* try to retrive private key for this service. Do it now 'cause pinfo
+ * is not always available
+ * Note that with HAVE_LIBGNUTLS undefined private_key is allways 0
+ * and thus decryption never engaged*/
+ ssl_session->private_key = g_hash_table_lookup(ssl_key_hash, &dummy);
+ if (!ssl_session->private_key)
+ ssl_debug_printf("dissect_ssl can't find private key for this server!\n");
}
+ conv_version= & ssl_session->version;
+
+ /* try decryption only the first time we see this packet
+ * (to keep cipher syncronized)and only if we have
+ * the server private key*/
+ if (!ssl_session->private_key || pinfo->fd->flags.visited)
+ ssl_session = NULL;
/* Initialize the protocol column; we'll set it later when we
* figure out what flavor of SSL it is (assuming we don't
@@ -910,12 +1280,13 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
/* first try to dispatch off the cached version
* known to be associated with the conversation
*/
- switch(conv_version) {
+ switch(*conv_version) {
case SSL_VER_SSLv2:
case SSL_VER_PCT:
offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
- offset, &conv_version,
- &need_desegmentation);
+ offset, conv_version,
+ &need_desegmentation,
+ ssl_session);
break;
case SSL_VER_SSLv3:
@@ -929,14 +1300,16 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
if (ssl_is_v2_client_hello(tvb, offset))
{
offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
- offset, &conv_version,
- &need_desegmentation);
+ offset, conv_version,
+ &need_desegmentation,
+ ssl_session);
}
else
{
offset = dissect_ssl3_record(tvb, pinfo, ssl_tree,
- offset, &conv_version,
- &need_desegmentation);
+ offset, conv_version,
+ &need_desegmentation,
+ ssl_session);
}
break;
@@ -948,15 +1321,17 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
/* looks like sslv2 or pct client hello */
offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
- offset, &conv_version,
- &need_desegmentation);
+ offset, conv_version,
+ &need_desegmentation,
+ ssl_session);
}
else if (ssl_looks_like_sslv3(tvb, offset))
{
/* looks like sslv3 or tls */
offset = dissect_ssl3_record(tvb, pinfo, ssl_tree,
- offset, &conv_version,
- &need_desegmentation);
+ offset, conv_version,
+ &need_desegmentation,
+ ssl_session);
}
else
{
@@ -972,7 +1347,7 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
if (check_col(pinfo->cinfo, COL_PROTOCOL))
{
col_set_str(pinfo->cinfo, COL_PROTOCOL,
- ssl_version_short_names[conv_version]);
+ ssl_version_short_names[*conv_version]);
}
}
break;
@@ -981,22 +1356,96 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
/* Desegmentation return check */
if (need_desegmentation)
return;
-
- /* If we haven't already set the version information for
- * this conversation, do so. */
- if (conv_data == NULL)
- {
- conv_data = GINT_TO_POINTER(conv_version);
- conversation_add_proto_data(conversation, proto_ssl, conv_data);
- }
-
/* set up for next record in frame, if any */
first_record_in_frame = FALSE;
}
+ tap_queue_packet(ssl_tap, pinfo, (gpointer)proto_ssl);
+}
+static void
+decrypt_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
+ guint32 record_length, guint8 content_type, SslDecryptSession* ssl,
+ gboolean save_plaintext)
+{
+ int len, direction;
+ SslDecoder* decoder;
+
+ /* if we can decrypt and decryption have success
+ * add decrypted data to this packet info*/
+ ssl_debug_printf("decrypt_ssl3_record: app_data len %d ssl state %X\n",
+ record_length, ssl->state);
+ if (!(ssl->state & SSL_HAVE_SESSION_KEY)) {
+ ssl_debug_printf("decrypt_ssl3_record: no session key\n");
+ return ;
+ }
+
+ /* retrive decoder for this packet direction*/
+ if ((direction = ssl_packet_from_server(pinfo->srcport)) != 0) {
+ ssl_debug_printf("decrypt_ssl3_record: using server decoder\n");
+ decoder = &ssl->server;
+ }
+ else {
+ ssl_debug_printf("decrypt_ssl3_record: using client decoder\n");
+ decoder = &ssl->client;
+ }
+
+ /* ensure we have enough storage space for decrypted data */
+ if (record_length > ssl_decrypted_data.data_len)
+ {
+ ssl_debug_printf("decrypt_ssl3_record: allocating %d bytes"
+ " for decrypt data (old len %d)\n",
+ record_length + 32, ssl_decrypted_data.data_len);
+ ssl_decrypted_data.data = g_realloc(ssl_decrypted_data.data,
+ record_length + 32);
+ ssl_decrypted_data.data_len = record_length + 32;
+ }
+
+ /* run decryption and add decrypted payload to protocol data, if decryption
+ * is successful*/
+ len = ssl_decrypted_data.data_len;
+ if ((ssl_decrypt_record(ssl, decoder,
+ content_type, tvb_get_ptr(tvb, offset, record_length),
+ record_length, ssl_decrypted_data.data, &len) == 0) &&
+ save_plaintext)
+ {
+ StringInfo* data = p_get_proto_data(pinfo->fd, proto_ssl);
+ if (!data)
+ {
+ ssl_debug_printf("decrypt_ssl3_record: allocating app_data %d "
+ "bytes for app data\n", len);
+ /* first app data record: allocate and put packet data*/
+ data = se_alloc(sizeof(StringInfo));
+ data->data = se_alloc(len);
+ data->data_len = len;
+ memcpy(data->data, ssl_decrypted_data.data, len);
+ }
+ else {
+ unsigned char* store;
+ /* update previus record*/
+ ssl_debug_printf("decrypt_ssl3_record: reallocating app_data "
+ "%d bytes for app data (total %d appdata bytes)\n",
+ len, data->data_len + len);
+ store = se_alloc(data->data_len + len);
+ memcpy(store, data->data, data->data_len);
+ memcpy(&store[data->data_len], ssl_decrypted_data.data, len);
+ data->data_len += len;
+
+ /* old decrypted data ptr here appare to be leaked, but it's
+ * collected by emem allocator */
+ data->data = store;
+
+ /* data ptr is changed, so remove old one and re-add the new one*/
+ ssl_debug_printf("decrypt_ssl3_record: removing old app_data ptr\n");
+ p_rem_proto_data(pinfo->fd, proto_ssl);
+ }
+
+ ssl_debug_printf("decrypt_ssl3_record: setting decrypted app_data ptr %p\n",data);
+ p_add_proto_data(pinfo->fd, proto_ssl, data);
+ }
}
+
/*********************************************************************
*
* SSL version 3 and TLS Dissection Routines
@@ -1005,7 +1454,8 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
static int
dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, guint32 offset,
- guint *conv_version, gboolean *need_desegmentation)
+ guint *conv_version, gboolean *need_desegmentation,
+ SslDecryptSession* ssl)
{
/*
@@ -1033,6 +1483,8 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *ti = NULL;
proto_tree *ssl_record_tree = NULL;
guint32 available_bytes = 0;
+ StringInfo* decrypted;
+ SslAssociation* association;
available_bytes = tvb_length_remaining(tvb, offset);
@@ -1152,12 +1604,21 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
if (version == 0x0300)
{
*conv_version = SSL_VER_SSLv3;
- ssl_set_conv_version(pinfo, *conv_version);
+ if (ssl) {
+ ssl->version_netorder = version;
+ ssl->state |= SSL_VERSION;
+ }
+ /*ssl_set_conv_version(pinfo, ssl->version);*/
}
else if (version == 0x0301)
{
+
*conv_version = SSL_VER_TLS;
- ssl_set_conv_version(pinfo, *conv_version);
+ if (ssl) {
+ ssl->version_netorder = version;
+ ssl->state |= SSL_VERSION;
+ }
+ /*ssl_set_conv_version(pinfo, ssl->version);*/
}
}
if (check_col(pinfo->cinfo, COL_PROTOCOL))
@@ -1182,6 +1643,11 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
/*
* now dissect the next layer
*/
+ ssl_debug_printf("dissect_ssl3_record: content_type %d\n",content_type);
+
+ /* PAOLO try to decrypt each record (we must keep ciphers "in sync")
+ * store plain text only for app data */
+
switch (content_type) {
case SSL_ID_CHG_CIPHER_SPEC:
if (check_col(pinfo->cinfo, COL_INFO))
@@ -1190,26 +1656,76 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
offset, conv_version, content_type);
break;
case SSL_ID_ALERT:
+ if (ssl)
+ decrypt_ssl3_record(tvb, pinfo, offset,
+ record_length, content_type, ssl, FALSE);
dissect_ssl3_alert(tvb, pinfo, ssl_record_tree, offset,
conv_version);
break;
case SSL_ID_HANDSHAKE:
+ if (ssl)
+ decrypt_ssl3_record(tvb, pinfo, offset,
+ record_length, content_type, ssl, FALSE);
dissect_ssl3_handshake(tvb, pinfo, ssl_record_tree, offset,
- record_length, conv_version, content_type);
+ record_length, conv_version, ssl, content_type);
break;
case SSL_ID_APP_DATA:
+ if (ssl)
+ decrypt_ssl3_record(tvb, pinfo, offset,
+ record_length, content_type, ssl, TRUE);
+
+ /* show on info colum what we are decoding */
if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO, "Application Data");
- if (ssl_record_tree)
+
+ if (!ssl_record_tree)
+ break;
+
+ /* we need dissector information when the selected packet is shown.
+ * ssl session pointer is NULL at that time, so we can't access
+ * info cached there*/
+ association = ssl_association_find(pinfo->srcport);
+ association = association ? association: ssl_association_find(pinfo->destport);
+
+ proto_item_set_text(ssl_record_tree,
+ "%s Record Layer: %s Protocol: %s",
+ ssl_version_short_names[*conv_version],
+ val_to_str(content_type, ssl_31_content_type, "unknown"),
+ association?association->info:"Application Data");
+
+ /* show decrypted data info, if available */
+ decrypted = p_get_proto_data(pinfo->fd, proto_ssl);
+ if (decrypted)
{
- proto_item_set_text(ssl_record_tree,
- "%s Record Layer: %s Protocol: Application Data",
- ssl_version_short_names[*conv_version],
- val_to_str(content_type, ssl_31_content_type, "unknown"));
+ tvbuff_t* new_tvb;
+
+ /* try to dissect decrypted data*/
+ ssl_debug_printf("dissect_ssl3_record decrypted len %d\n", decrypted->data_len);
+
+ /* create new tvbuff for the decrypted data */
+ new_tvb = tvb_new_real_data(decrypted->data,
+ decrypted->data_len, decrypted->data_len);
+ tvb_set_free_cb(new_tvb, g_free);
+ //tvb_set_child_real_data_tvbuff(tvb, new_tvb);
+
+ /* find out a dissector using server port*/
+ if (association && association->handle) {
+ ssl_debug_printf("dissect_ssl3_record found association %p\n", association);
+ ssl_print_text_data("decrypted app data",decrypted->data,
+ decrypted->data_len);
+
+ call_dissector(association->handle, new_tvb, pinfo, ssl_record_tree);
+ }
+ /* add raw decrypted data only if a decoder is not found*/
+ else
+ proto_tree_add_string(ssl_record_tree, hf_ssl_record_appdata_decrypted, tvb,
+ offset, decrypted->data_len, (char*) decrypted->data);
+ }
+ else {
tvb_ensure_bytes_exist(tvb, offset, record_length);
proto_tree_add_item(ssl_record_tree, hf_ssl_record_appdata, tvb,
- offset, record_length, 0);
- }
+ offset, record_length, 0);
+ }
break;
default:
@@ -1227,7 +1743,7 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
static void
dissect_ssl3_change_cipher_spec(tvbuff_t *tvb,
proto_tree *tree, guint32 offset,
- guint *conv_version, guint8 content_type)
+ guint* conv_version, guint8 content_type)
{
/*
* struct {
@@ -1250,7 +1766,7 @@ dissect_ssl3_change_cipher_spec(tvbuff_t *tvb,
static void
dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, guint32 offset,
- guint *conv_version)
+ guint* conv_version)
{
/* struct {
* AlertLevel level;
@@ -1294,7 +1810,7 @@ dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo,
if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO, "Encrypted Alert");
}
-
+
if (tree)
{
if (level && desc)
@@ -1325,7 +1841,8 @@ dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo,
static void
dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, guint32 offset,
- guint32 record_length, guint *conv_version, guint8 content_type)
+ guint32 record_length, guint *conv_version,
+ SslDecryptSession* ssl, guint8 content_type)
{
/* struct {
* HandshakeType msg_type;
@@ -1367,6 +1884,8 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
msg_type_str = match_strval(msg_type, ssl_31_handshake_type);
length = tvb_get_ntoh24(tvb, offset + 1);
+ ssl_debug_printf("dissect_ssl3_handshake iteration %d type %d offset %d lenght %d "
+ "bytes, remaning %d \n", first_iteration, msg_type, offset, length, record_length);
if (!msg_type_str && !first_iteration)
{
/* only dissect / report messages if they're
@@ -1425,17 +1944,18 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
/* if we don't have a valid handshake type, just quit dissecting */
if (!msg_type_str)
- {
return;
- }
-
- if (ssl_hand_tree)
+
+ /* PAOLO: if we are doing ssl decryption we must dissect some requests type */
+ if (ssl_hand_tree || ssl)
{
/* add nodes for the message type and message length */
- proto_tree_add_item(ssl_hand_tree, hf_ssl_handshake_type,
- tvb, offset, 1, msg_type);
+ if (ssl_hand_tree)
+ proto_tree_add_item(ssl_hand_tree, hf_ssl_handshake_type,
+ tvb, offset, 1, msg_type);
offset++;
- proto_tree_add_uint(ssl_hand_tree, hf_ssl_handshake_length,
+ if (ssl_hand_tree)
+ proto_tree_add_uint(ssl_hand_tree, hf_ssl_handshake_length,
tvb, offset, 3, length);
offset += 3;
@@ -1446,11 +1966,11 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
break;
case SSL_HND_CLIENT_HELLO:
- dissect_ssl3_hnd_cli_hello(tvb, ssl_hand_tree, offset, length);
+ dissect_ssl3_hnd_cli_hello(tvb, ssl_hand_tree, offset, length, ssl);
break;
case SSL_HND_SERVER_HELLO:
- dissect_ssl3_hnd_srv_hello(tvb, ssl_hand_tree, offset, length);
+ dissect_ssl3_hnd_srv_hello(tvb, ssl_hand_tree, offset, length, ssl);
break;
case SSL_HND_CERTIFICATE:
@@ -1473,8 +1993,51 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
/* unimplemented */
break;
- case SSL_HND_CLIENT_KEY_EXCHG:
- /* unimplemented */
+ case SSL_HND_CLIENT_KEY_EXCHG:
+ {
+ /* PAOLO: here we can have all the data to build session key*/
+ StringInfo encrypted_pre_master;
+ int ret;
+
+ if (!ssl)
+ break;
+
+ /* check for required session data */
+ ssl_debug_printf("dissect_ssl3_handshake found SSL_HND_CLIENT_KEY_EXCHG state %X\n",
+ ssl->state);
+ if ((ssl->state & (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION)) !=
+ (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION)) {
+ ssl_debug_printf("dissect_ssl3_handshake not enough data to generate key (required %X)\n",
+ (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION));
+ break;
+ }
+
+ /* get encrypted data, we must skip tls record len && version and
+ * 2 bytes of record data */
+ encrypted_pre_master.data = se_alloc(length - 2);
+ encrypted_pre_master.data_len = length-2;
+ tvb_memcpy(tvb, encrypted_pre_master.data, offset+2, length-2);
+
+ if (!ssl->private_key) {
+ ssl_debug_printf("dissect_ssl3_handshake can't find private key\n");
+ break;
+ }
+
+ /* go with ssl key processessing; encrypted_pre_master
+ * will be used for master secret store*/
+ ret = ssl_decrypt_pre_master_secret(ssl, &encrypted_pre_master, ssl->private_key);
+ if (ret < 0) {
+ ssl_debug_printf("dissect_ssl3_handshake can't decrypt pre master secret\n");
+ break;
+ }
+ if (ssl_generate_keyring_material(ssl)<0) {
+ ssl_debug_printf("dissect_ssl3_handshake can't generate keyring material\n");
+ break;
+ }
+ ssl->state |= SSL_HAVE_SESSION_KEY;
+ ssl_save_session(ssl);
+ ssl_debug_printf("dissect_ssl3_handshake session keys succesfully generated\n");
+ }
break;
case SSL_HND_FINISHED:
@@ -1485,9 +2048,8 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
}
else
- {
- offset += 4; /* skip the handshake header */
- }
+ offset += 4; /* skip the handshake header when handshake is not processed*/
+
offset += length;
first_iteration = FALSE; /* set up for next pass, if any */
}
@@ -1495,13 +2057,50 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
static int
dissect_ssl3_hnd_hello_common(tvbuff_t *tvb, proto_tree *tree,
- guint32 offset)
+ guint32 offset, SslDecryptSession* ssl, gint from_server)
{
/* show the client's random challenge */
- guint32 initial_offset = offset;
nstime_t gmt_unix_time;
guint8 session_id_length = 0;
+ if (ssl)
+ {
+ /* PAOLO: get proper peer information*/
+ StringInfo* rnd;
+ if (from_server)
+ rnd = &ssl->server_random;
+ else
+ rnd = &ssl->client_random;
+
+ /* get provided random for keyring generation*/
+ tvb_memcpy(tvb, rnd->data, offset, 32);
+ rnd->data_len = 32;
+ if (from_server)
+ ssl->state |= SSL_SERVER_RANDOM;
+ else
+ ssl->state |= SSL_CLIENT_RANDOM;
+ ssl_debug_printf("dissect_ssl3_hnd_hello_common found random state %X\n",
+ ssl->state);
+
+ session_id_length = tvb_get_guint8(tvb, offset + 32);
+ /* check stored session id info */
+ if (from_server && (session_id_length == ssl->session_id.data_len) &&
+ (tvb_memeql(tvb, offset+33, ssl->session_id.data, session_id_length) == 0))
+ {
+ /* clinet/server id match: try to restore a previous cached session*/
+ ssl_restore_session(ssl);
+ }
+ else {
+ /* reset state on renegotiation*/
+ if (!from_server)
+ ssl->state &= ~(SSL_HAVE_SESSION_KEY|SSL_MASTER_SECRET|
+ SSL_CIPHER|SSL_SERVER_RANDOM);
+
+ tvb_memcpy(tvb,ssl->session_id.data, offset+33, session_id_length);
+ ssl->session_id.data_len = session_id_length;
+ }
+ }
+
if (tree)
{
/* show the time */
@@ -1533,7 +2132,9 @@ dissect_ssl3_hnd_hello_common(tvbuff_t *tvb, proto_tree *tree,
}
}
- return offset - initial_offset;
+
+ // XXXX
+ return session_id_length+33;
}
static int
@@ -1591,7 +2192,8 @@ dissect_ssl3_hnd_hello_ext(tvbuff_t *tvb,
static void
dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
- proto_tree *tree, guint32 offset, guint32 length)
+ proto_tree *tree, guint32 offset, guint32 length,
+ SslDecryptSession*ssl)
{
/* struct {
* ProtocolVersion client_version;
@@ -1610,20 +2212,23 @@ dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
guint8 compression_method;
guint16 start_offset = offset;
- if (tree)
+ if (tree || ssl)
{
/* show the client version */
- proto_tree_add_item(tree, hf_ssl_handshake_client_version, tvb,
+ if (tree)
+ proto_tree_add_item(tree, hf_ssl_handshake_client_version, tvb,
offset, 2, FALSE);
offset += 2;
/* show the fields in common with server hello */
- offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset);
+ offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset, ssl, 0);
/* tell the user how many cipher suites there are */
cipher_suite_length = tvb_get_ntohs(tvb, offset);
+ if (!tree)
+ return;
proto_tree_add_uint(tree, hf_ssl_handshake_cipher_suites_len,
- tvb, offset, 2, cipher_suite_length);
+ tvb, offset, 2, cipher_suite_length);
offset += 2; /* skip opaque length */
if (cipher_suite_length > 0)
@@ -1706,7 +2311,7 @@ dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
static void
dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb,
- proto_tree *tree, guint32 offset, guint32 length)
+ proto_tree *tree, guint32 offset, guint32 length, SslDecryptSession* ssl)
{
/* struct {
* ProtocolVersion server_version;
@@ -1719,21 +2324,56 @@ dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb,
*/
guint16 start_offset = offset;
- if (tree)
+ if (tree || ssl)
{
/* show the server version */
- proto_tree_add_item(tree, hf_ssl_handshake_server_version, tvb,
+ if (tree)
+ proto_tree_add_item(tree, hf_ssl_handshake_server_version, tvb,
offset, 2, FALSE);
offset += 2;
/* first display the elements conveniently in
* common with client hello
*/
- offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset);
+ offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset, ssl, 1);
+
+ /* PAOLO: handle session cipher suite */
+ if (ssl) {
+ /* store selected cipher suite for decryption */
+ ssl->cipher = tvb_get_ntohs(tvb, offset);
+ if (ssl_find_cipher(ssl->cipher,&ssl->cipher_suite) < 0) {
+ ssl_debug_printf("dissect_ssl3_hnd_srv_hello can't find cipher suite %X\n", ssl->cipher);
+ goto no_cipher;
+ }
+
+ ssl->state |= SSL_CIPHER;
+ ssl_debug_printf("dissect_ssl3_hnd_srv_hello found cipher %X, state %X\n",
+ ssl->cipher, ssl->state);
+
+ /* if we have restored a session now we can have enought material
+ * to build session key, check it out*/
+ if ((ssl->state &
+ (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET)) !=
+ (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET)) {
+ ssl_debug_printf("dissect_ssl3_hnd_srv_hello not enough data to generate key (required %X)\n",
+ (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET));
+ goto no_cipher;
+ }
+
+ ssl_debug_printf("dissect_ssl3_hnd_srv_hello trying to generate keys\n");
+ if (ssl_generate_keyring_material(ssl)<0) {
+ ssl_debug_printf("dissect_ssl3_hnd_srv_hello can't generate keyring material\n");
+ goto no_cipher;
+ }
+ ssl->state |= SSL_HAVE_SESSION_KEY;
+ }
+no_cipher:
+ if (!tree)
+ return;
/* now the server-selected cipher suite */
proto_tree_add_item(tree, hf_ssl_handshake_cipher_suite,
- tvb, offset, 2, FALSE);
+ tvb, offset, 2, FALSE);
offset += 2;
/* and the server-selected compression method */
@@ -1910,7 +2550,7 @@ dissect_ssl3_hnd_cert_req(tvbuff_t *tvb,
static void
dissect_ssl3_hnd_finished(tvbuff_t *tvb,
proto_tree *tree, guint32 offset,
- guint *conv_version)
+ guint* conv_version)
{
/* For TLS:
* struct {
@@ -1957,8 +2597,9 @@ dissect_ssl3_hnd_finished(tvbuff_t *tvb,
/* record layer dissector */
static int
dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
- guint32 offset, guint *conv_version,
- gboolean *need_desegmentation)
+ guint32 offset, guint* conv_version,
+ gboolean *need_desegmentation,
+ SslDecryptSession* ssl)
{
guint32 initial_offset = offset;
guint8 byte = 0;
@@ -2057,13 +2698,13 @@ dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
(initial_offset +
record_length_length),
record_length)) {
- *conv_version = SSL_VER_PCT;
- ssl_set_conv_version(pinfo, *conv_version);
+ *conv_version = SSL_VER_PCT;
+ /*ssl_set_conv_version(pinfo, ssl->version);*/
}
else if (msg_type >= 2 && msg_type <= 8)
{
*conv_version = SSL_VER_SSLv2;
- ssl_set_conv_version(pinfo, *conv_version);
+ /*ssl_set_conv_version(pinfo, ssl->version);*/
}
}
@@ -2136,7 +2777,7 @@ dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
proto_tree_add_boolean(ssl_record_tree,
hf_ssl2_record_is_escape, tvb,
initial_offset, 1, is_escape);
- }
+ }
if (ssl_record_tree && padding_length != -1)
{
proto_tree_add_uint(ssl_record_tree,
@@ -2166,7 +2807,7 @@ dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
/* dissect the message (only handle client hello right now) */
switch (msg_type) {
case SSL2_HND_CLIENT_HELLO:
- dissect_ssl2_hnd_client_hello(tvb, ssl_record_tree, offset);
+ dissect_ssl2_hnd_client_hello(tvb, ssl_record_tree, offset, ssl);
break;
case SSL2_HND_CLIENT_MASTER_KEY:
@@ -2219,7 +2860,8 @@ dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
static void
dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
- proto_tree *tree, guint32 offset)
+ proto_tree *tree, guint32 offset,
+ SslDecryptSession* ssl)
{
/* struct {
* uint8 msg_type;
@@ -2241,7 +2883,7 @@ dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
guint16 challenge_length;
proto_tree *ti;
- proto_tree *cs_tree;
+ proto_tree *cs_tree=0;
version = tvb_get_ntohs(tvb, offset);
if (!ssl_is_valid_ssl_version(version))
@@ -2250,46 +2892,54 @@ dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
return;
}
- if (tree)
+ if (tree || ssl)
{
/* show the version */
- proto_tree_add_item(tree, hf_ssl_record_version, tvb,
+ if (tree)
+ proto_tree_add_item(tree, hf_ssl_record_version, tvb,
offset, 2, FALSE);
offset += 2;
cipher_spec_length = tvb_get_ntohs(tvb, offset);
- proto_tree_add_item(tree, hf_ssl2_handshake_cipher_spec_len,
+ if (tree)
+ proto_tree_add_item(tree, hf_ssl2_handshake_cipher_spec_len,
tvb, offset, 2, FALSE);
offset += 2;
session_id_length = tvb_get_ntohs(tvb, offset);
- proto_tree_add_item(tree, hf_ssl2_handshake_session_id_len,
+ if (tree)
+ proto_tree_add_item(tree, hf_ssl2_handshake_session_id_len,
tvb, offset, 2, FALSE);
offset += 2;
challenge_length = tvb_get_ntohs(tvb, offset);
- proto_tree_add_item(tree, hf_ssl2_handshake_challenge_len,
+ if (tree)
+ proto_tree_add_item(tree, hf_ssl2_handshake_challenge_len,
tvb, offset, 2, FALSE);
offset += 2;
- /* tell the user how many cipher specs they've won */
- tvb_ensure_bytes_exist(tvb, offset, cipher_spec_length);
- ti = proto_tree_add_none_format(tree, hf_ssl_handshake_cipher_suites,
+ if (tree)
+ {
+ /* tell the user how many cipher specs they've won */
+ tvb_ensure_bytes_exist(tvb, offset, cipher_spec_length);
+ ti = proto_tree_add_none_format(tree, hf_ssl_handshake_cipher_suites,
tvb, offset, cipher_spec_length,
"Cipher Specs (%u specs)",
cipher_spec_length/3);
- /* make this a subtree and expand the actual specs below */
- cs_tree = proto_item_add_subtree(ti, ett_ssl_cipher_suites);
- if (!cs_tree)
- {
- cs_tree = tree; /* failsafe */
+ /* make this a subtree and expand the actual specs below */
+ cs_tree = proto_item_add_subtree(ti, ett_ssl_cipher_suites);
+ if (!cs_tree)
+ {
+ cs_tree = tree; /* failsafe */
+ }
}
/* iterate through the cipher specs, showing them */
while (cipher_spec_length > 0)
{
- proto_tree_add_item(cs_tree, hf_ssl2_handshake_cipher_spec,
+ if (cs_tree)
+ proto_tree_add_item(cs_tree, hf_ssl2_handshake_cipher_spec,
tvb, offset, 3, FALSE);
offset += 3; /* length of one cipher spec */
cipher_spec_length -= 3;
@@ -2298,15 +2948,26 @@ dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
/* if there's a session id, show it */
if (session_id_length > 0)
{
- tvb_ensure_bytes_exist(tvb, offset, session_id_length);
- proto_tree_add_bytes_format(tree,
- hf_ssl_handshake_session_id,
- tvb, offset, session_id_length,
- tvb_get_ptr(tvb, offset, session_id_length),
- "Session ID (%u byte%s)",
- session_id_length,
- plurality(session_id_length, "", "s"));
-
+ if (tree)
+ {
+ tvb_ensure_bytes_exist(tvb, offset, session_id_length);
+ proto_tree_add_bytes_format(tree,
+ hf_ssl_handshake_session_id,
+ tvb, offset, session_id_length,
+ tvb_get_ptr(tvb, offset, session_id_length),
+ "Session ID (%u byte%s)",
+ session_id_length,
+ plurality(session_id_length, "", "s"));
+ }
+
+ //PAOLO: get session id and reset session state for key [re]negotiation
+ if (ssl)
+ {
+ tvb_memcpy(tvb,ssl->session_id.data, offset, session_id_length);
+ ssl->session_id.data_len = session_id_length;
+ ssl->state &= ~(SSL_HAVE_SESSION_KEY|SSL_MASTER_SECRET|
+ SSL_CIPHER|SSL_SERVER_RANDOM);
+ }
offset += session_id_length;
}
@@ -2314,8 +2975,26 @@ dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
if (challenge_length > 0)
{
tvb_ensure_bytes_exist(tvb, offset, challenge_length);
- proto_tree_add_item(tree, hf_ssl2_handshake_challenge,
+
+ if (tree)
+ proto_tree_add_item(tree, hf_ssl2_handshake_challenge,
tvb, offset, challenge_length, 0);
+ if (ssl)
+ {
+ //PAOLO: get client random data; we get at most 32 bytes from
+ // challenge
+ int max = challenge_length > 32? 32: challenge_length;
+
+ ssl_debug_printf("client random len: %d padded to 32\n",
+ challenge_length);
+
+ // client random is padded with zero and 'right' aligned
+ memset(ssl->client_random.data, 0, 32 - max);
+ tvb_memcpy(tvb, &ssl->client_random.data[32 - max], offset, max);
+ ssl->client_random.data_len = 32;
+ ssl->state |= SSL_CLIENT_RANDOM;
+
+ }
offset += challenge_length;
}
}
@@ -2864,7 +3543,7 @@ dissect_ssl2_hnd_server_hello(tvbuff_t *tvb,
* Support Functions
*
*********************************************************************/
-
+#if 0
static void
ssl_set_conv_version(packet_info *pinfo, guint version)
{
@@ -2895,6 +3574,7 @@ ssl_set_conv_version(packet_info *pinfo, guint version)
}
conversation_add_proto_data(conversation, proto_ssl, GINT_TO_POINTER(version));
}
+#endif
static int
ssl_is_valid_handshake_type(guint8 type)
@@ -3181,6 +3861,9 @@ ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb, guint32 offset,
* Standard Ethereal Protocol Registration and housekeeping
*
*********************************************************************/
+#ifndef SSL_SUFFIX
+#define SSL_SUFFIX ""
+#endif
void
proto_register_ssl(void)
{
@@ -3222,6 +3905,12 @@ proto_register_ssl(void)
FT_NONE, BASE_NONE, NULL, 0x0,
"Payload is application data", HFILL }
},
+ { &hf_ssl_record_appdata_decrypted,
+ { "Application Data decrypted", "ssl.app_data_decrypted",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Payload is decrypted application data", HFILL }
+ },
+
{ & hf_ssl2_record,
{ "SSLv2/PCT Record Header", "ssl.record",
FT_NONE, BASE_DEC, NULL, 0x0,
@@ -3497,61 +4186,61 @@ proto_register_ssl(void)
FT_NONE, BASE_NONE, NULL, 0x0,
"Server's challenge to client", HFILL }
},
- { &hf_pct_handshake_cipher_spec,
- { "Cipher Spec", "pct.handshake.cipherspec",
- FT_NONE, BASE_NONE, NULL, 0x0,
- "PCT Cipher specification", HFILL }
- },
- { &hf_pct_handshake_cipher,
- { "Cipher", "pct.handshake.cipher",
- FT_UINT16, BASE_HEX, VALS(pct_cipher_type), 0x0,
- "PCT Ciper", HFILL }
+ { &hf_pct_handshake_cipher_spec,
+ { "Cipher Spec", "pct.handshake.cipherspec",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ "PCT Cipher specification", HFILL }
+ },
+ { &hf_pct_handshake_cipher,
+ { "Cipher", "pct.handshake.cipher",
+ FT_UINT16, BASE_HEX, VALS(pct_cipher_type), 0x0,
+ "PCT Ciper", HFILL }
},
- { &hf_pct_handshake_hash_spec,
- { "Hash Spec", "pct.handshake.hashspec",
- FT_NONE, BASE_NONE, NULL, 0x0,
- "PCT Hash specification", HFILL }
- },
- { &hf_pct_handshake_hash,
- { "Hash", "pct.handshake.hash",
- FT_UINT16, BASE_HEX, VALS(pct_hash_type), 0x0,
- "PCT Hash", HFILL }
- },
- { &hf_pct_handshake_cert_spec,
- { "Cert Spec", "pct.handshake.certspec",
- FT_NONE, BASE_NONE, NULL, 0x0,
- "PCT Certificate specification", HFILL }
- },
- { &hf_pct_handshake_cert,
- { "Cert", "pct.handshake.cert",
- FT_UINT16, BASE_HEX, VALS(pct_cert_type), 0x0,
- "PCT Certificate", HFILL }
- },
- { &hf_pct_handshake_exch_spec,
- { "Exchange Spec", "pct.handshake.exchspec",
- FT_NONE, BASE_NONE, NULL, 0x0,
- "PCT Exchange specification", HFILL }
- },
- { &hf_pct_handshake_exch,
- { "Exchange", "pct.handshake.exch",
- FT_UINT16, BASE_HEX, VALS(pct_exch_type), 0x0,
- "PCT Exchange", HFILL }
- },
- { &hf_pct_handshake_sig,
- { "Sig Spec", "pct.handshake.sig",
- FT_UINT16, BASE_HEX, VALS(pct_sig_type), 0x0,
- "PCT Signature", HFILL }
- },
- { &hf_pct_msg_error_type,
- { "PCT Error Code", "pct.msg_error_code",
- FT_UINT16, BASE_HEX, VALS(pct_error_code), 0x0,
- "PCT Error Code", HFILL }
- },
- { &hf_pct_handshake_server_cert,
- { "Server Cert", "pct.handshake.server_cert",
- FT_NONE, BASE_NONE, NULL , 0x0,
- "PCT Server Certificate", HFILL }
- },
+ { &hf_pct_handshake_hash_spec,
+ { "Hash Spec", "pct.handshake.hashspec",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ "PCT Hash specification", HFILL }
+ },
+ { &hf_pct_handshake_hash,
+ { "Hash", "pct.handshake.hash",
+ FT_UINT16, BASE_HEX, VALS(pct_hash_type), 0x0,
+ "PCT Hash", HFILL }
+ },
+ { &hf_pct_handshake_cert_spec,
+ { "Cert Spec", "pct.handshake.certspec",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ "PCT Certificate specification", HFILL }
+ },
+ { &hf_pct_handshake_cert,
+ { "Cert", "pct.handshake.cert",
+ FT_UINT16, BASE_HEX, VALS(pct_cert_type), 0x0,
+ "PCT Certificate", HFILL }
+ },
+ { &hf_pct_handshake_exch_spec,
+ { "Exchange Spec", "pct.handshake.exchspec",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ "PCT Exchange specification", HFILL }
+ },
+ { &hf_pct_handshake_exch,
+ { "Exchange", "pct.handshake.exch",
+ FT_UINT16, BASE_HEX, VALS(pct_exch_type), 0x0,
+ "PCT Exchange", HFILL }
+ },
+ { &hf_pct_handshake_sig,
+ { "Sig Spec", "pct.handshake.sig",
+ FT_UINT16, BASE_HEX, VALS(pct_sig_type), 0x0,
+ "PCT Signature", HFILL }
+ },
+ { &hf_pct_msg_error_type,
+ { "PCT Error Code", "pct.msg_error_code",
+ FT_UINT16, BASE_HEX, VALS(pct_error_code), 0x0,
+ "PCT Error Code", HFILL }
+ },
+ { &hf_pct_handshake_server_cert,
+ { "Server Cert", "pct.handshake.server_cert",
+ FT_NONE, BASE_NONE, NULL , 0x0,
+ "PCT Server Certificate", HFILL }
+ },
};
/* Setup protocol subtree array */
@@ -3573,8 +4262,8 @@ proto_register_ssl(void)
};
/* Register the protocol name and description */
- proto_ssl = proto_register_protocol("Secure Socket Layer",
- "SSL", "ssl");
+ proto_ssl = proto_register_protocol("Secure Socket Layer"SSL_SUFFIX,
+ "SSL"SSL_SUFFIX, "ssl"SSL_SUFFIX);
/* Required function calls to register the header fields and
* subtrees used */
@@ -3582,17 +4271,32 @@ proto_register_ssl(void)
proto_register_subtree_array(ett, array_length(ett));
{
- module_t *ssl_module = prefs_register_protocol(proto_ssl, NULL);
+ module_t *ssl_module = prefs_register_protocol(proto_ssl, ssl_parse);
prefs_register_bool_preference(ssl_module,
"desegment_ssl_records",
"Reassemble SSL records spanning multiple TCP segments",
"Whether the SSL dissector should reassemble SSL records spanning multiple TCP segments. "
"To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
&ssl_desegment);
+ prefs_register_string_preference(ssl_module, "keys_list", "RSA keys list",
+ "comma separated list of private RSA keys used for SSL decryption; "
+ "each list entry must be in the form of <ip>:<port>:<key_file_name>\n"
+ "<key_file_name> is the local file name of the RSA private key used by the specified server\n",
+ (const char **)&ssl_keys_list);
+ prefs_register_string_preference(ssl_module, "ports_list", "SSL ports list",
+ "comma separated list of tcp ports numbers to be dissectes as SSL; "
+ "each list entry must be in the form of <port>:<clear-text-port>\n"
+ "<clear-text-port> is the port numbert associated with the protocol tunneled over SSL for this port\n",
+ (const char **)&ssl_ports_list);
}
- register_dissector("ssl", dissect_ssl, proto_ssl);
-
+ register_dissector("ssl"SSL_SUFFIX, dissect_ssl, proto_ssl);
+
+ register_init_routine(ssl_init);
+ ssl_lib_init();
+ ssl_tap = register_tap("ssl"SSL_SUFFIX);
+ ssl_debug_printf("proto_register_ssl: registered tap %s:%d\n",
+ "ssl"SSL_SUFFIX, ssl_tap);
}
/* If this dissector uses sub-dissector registration add a registration
@@ -3602,11 +4306,8 @@ proto_register_ssl(void)
void
proto_reg_handoff_ssl(void)
{
- dissector_handle_t ssl_handle;
-
- ssl_handle = find_dissector("ssl");
- dissector_add("tcp.port", TCP_PORT_SSL, ssl_handle);
- dissector_add("tcp.port", TCP_PORT_SSL_LDAP, ssl_handle);
- dissector_add("tcp.port", TCP_PORT_SSL_IMAP, ssl_handle);
- dissector_add("tcp.port", TCP_PORT_SSL_POP, ssl_handle);
+ ssl_handle = find_dissector("ssl"SSL_SUFFIX);
+
+ /* add now dissector to default ports.*/
+ ssl_parse();
}
diff --git a/gtk/Makefile.common b/gtk/Makefile.common
index 93c14065e6..07e7040af6 100644
--- a/gtk/Makefile.common
+++ b/gtk/Makefile.common
@@ -95,6 +95,7 @@ ETHEREAL_GTK_SRC = \
sctp_error_dlg.c \
service_response_time_table.c \
simple_dialog.c \
+ ssl-dlg.c \
stream_prefs.c \
summary_dlg.c \
supported_protos_dlg.c \
diff --git a/gtk/main.c b/gtk/main.c
index 0716ff9201..758d886b82 100644
--- a/gtk/main.c
+++ b/gtk/main.c
@@ -3575,6 +3575,8 @@ create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
filter_te);
set_menu_object_data("/Analyze/Follow TCP Stream", E_DFILTER_TE_KEY,
filter_te);
+ set_menu_object_data("/Analyze/Follow SSL Stream", E_DFILTER_TE_KEY,
+ filter_te);
set_menu_object_data("/Analyze/Apply as Filter/Selected", E_DFILTER_TE_KEY,
filter_te);
set_menu_object_data("/Analyze/Apply as Filter/Not Selected", E_DFILTER_TE_KEY,
diff --git a/gtk/menu.c b/gtk/menu.c
index 1d69b863ac..8be15950bc 100644
--- a/gtk/menu.c
+++ b/gtk/menu.c
@@ -171,6 +171,8 @@ File/Close: the Gnome HIG suggests putting this item just above the Quit
currently opened/captured file only.
*/
+void
+ssl_stream_cb(GtkWidget * w, gpointer data _U_);
/* main menu */
static GtkItemFactoryEntry menu_items[] =
@@ -377,6 +379,8 @@ static GtkItemFactoryEntry menu_items[] =
ITEM_FACTORY_ENTRY("/Analyze/<separator>", NULL, NULL, 0, "<Separator>", NULL),
ITEM_FACTORY_ENTRY("/Analyze/_Follow TCP Stream", NULL,
follow_stream_cb, 0, NULL, NULL),
+ ITEM_FACTORY_ENTRY("/Analyze/_Follow SSL Stream", NULL,
+ ssl_stream_cb, 0, NULL, NULL),
ITEM_FACTORY_ENTRY("/_Statistics", NULL, NULL, 0, "<Branch>", NULL),
ITEM_FACTORY_STOCK_ENTRY("/Statistics/_Summary", NULL, summary_open_cb, 0, GTK_STOCK_PROPERTIES),
ITEM_FACTORY_ENTRY("/Statistics/_Protocol Hierarchy", NULL,
@@ -458,6 +462,8 @@ static GtkItemFactoryEntry packet_list_menu_items[] =
ITEM_FACTORY_ENTRY("/Follow TCP Stream", NULL, follow_stream_cb,
0, NULL, NULL),
+ ITEM_FACTORY_ENTRY("/Follow SSL Stream", NULL, ssl_stream_cb,
+ 0, NULL, NULL),
ITEM_FACTORY_ENTRY("/<separator>", NULL, NULL, 0, "<Separator>", NULL),
@@ -505,6 +511,8 @@ static GtkItemFactoryEntry tree_view_menu_items[] =
ITEM_FACTORY_ENTRY("/Follow TCP Stream", NULL, follow_stream_cb,
0, NULL, NULL),
+ ITEM_FACTORY_ENTRY("/Follow SSL Stream", NULL, ssl_stream_cb,
+ 0, NULL, NULL),
ITEM_FACTORY_ENTRY("/<separator>", NULL, NULL, 0, "<Separator>", NULL),
@@ -1988,6 +1996,12 @@ set_menus_for_selected_packet(capture_file *cf)
cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
set_menu_sensitivity(tree_view_menu_factory, "/Follow TCP Stream",
cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
+ set_menu_sensitivity(main_menu_factory, "/Analyze/Follow SSL Stream",
+ cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
+ set_menu_sensitivity(packet_list_menu_factory, "/Follow SSL Stream",
+ cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
+ set_menu_sensitivity(tree_view_menu_factory, "/Follow SSL Stream",
+ cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
set_menu_sensitivity(main_menu_factory, "/Analyze/Decode As...",
cf->current_frame != NULL && decode_as_ok());
set_menu_sensitivity(packet_list_menu_factory, "/Decode As...",
diff --git a/gtk/ssl-dlg.c b/gtk/ssl-dlg.c
new file mode 100644
index 0000000000..91354dce6b
--- /dev/null
+++ b/gtk/ssl-dlg.c
@@ -0,0 +1,1049 @@
+/* ssl_dlg.c
+ *
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <stdio.h>
+#include <string.h>
+
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <ctype.h>
+
+#include <color.h>
+#include <gtk/colors.h>
+#include <gtk/main.h>
+#include <epan/follow.h>
+#include <gtk/dlg_utils.h>
+#include <gtk/keys.h>
+#include <globals.h>
+#include <alert_box.h>
+#include <simple_dialog.h>
+#include <epan/dissectors/packet-ipv6.h>
+#include <epan/prefs.h>
+#include <epan/addr_resolv.h>
+#include <epan/charsets.h>
+#include <util.h>
+#include <gtk/gui_utils.h>
+#include <epan/epan_dissect.h>
+#include <epan/filesystem.h>
+#include <gtk/compat_macros.h>
+#include <epan/ipproto.h>
+#include <gtk/font_utils.h>
+#include <wiretap/file_util.h>
+#include <epan/tap.h>
+
+#ifdef SSL_PLUGIN
+#include "packet-ssl-utils.h"
+#else
+#include <epan/dissectors/packet-ssl-utils.h>
+#endif
+
+#ifndef SSL_SUFFIX
+#define SSL_SUFFIX ""
+#endif
+
+/* Show Stream */
+typedef enum {
+ FROM_CLIENT,
+ FROM_SERVER,
+ BOTH_HOSTS
+} show_stream_t;
+
+/* Show Type */
+typedef enum {
+ SHOW_ASCII,
+ SHOW_HEXDUMP,
+ SHOW_CARRAY,
+ SHOW_RAW
+} show_type_t;
+
+typedef struct {
+ show_stream_t show_stream;
+ show_type_t show_type;
+ GtkWidget *text;
+ GtkWidget *ascii_bt;
+ GtkWidget *ebcdic_bt;
+ GtkWidget *hexdump_bt;
+ GtkWidget *carray_bt;
+ GtkWidget *raw_bt;
+ GtkWidget *follow_save_as_w;
+ gboolean is_ipv6;
+ char *filter_out_filter;
+ GtkWidget *filter_te;
+ GtkWidget *streamwindow;
+ GList *ssl_decrypted_data;
+ guint bytes_written[2];
+ guint client_port;
+ char client_ip[MAX_IPADDR_LEN];
+} follow_info_t;
+
+static void follow_destroy_cb(GtkWidget * win, gpointer data);
+static void follow_charset_toggle_cb(GtkWidget * w, gpointer parent_w);
+static void follow_load_text(follow_info_t *follow_info);
+static void follow_filter_out_stream(GtkWidget * w, gpointer parent_w);
+static void follow_save_as_cmd_cb(GtkWidget * w, gpointer data);
+static void follow_save_as_ok_cb(GtkWidget * w, gpointer fs);
+static void follow_save_as_destroy_cb(GtkWidget * win, gpointer user_data);
+static void follow_stream_om_both(GtkWidget * w, gpointer data);
+static void follow_stream_om_client(GtkWidget * w, gpointer data);
+static void follow_stream_om_server(GtkWidget * w, gpointer data);
+
+
+#define E_FOLLOW_INFO_KEY "follow_info_key"
+
+/* List of "follow_info_t" structures for all "Follow TCP Stream" windows,
+ so we can redraw them all if the colors or font changes. */
+static GList *follow_infos;
+
+typedef struct {
+ gboolean is_server;
+ StringInfo* data;
+} SslDecryptedRecord;
+
+/* Add a "follow_info_t" structure to the list. */
+static void
+remember_follow_info(follow_info_t *follow_info)
+{
+ follow_infos = g_list_append(follow_infos, follow_info);
+}
+
+/* Remove a "follow_info_t" structure from the list. */
+static void
+forget_follow_info(follow_info_t *follow_info)
+{
+ follow_infos = g_list_remove(follow_infos, follow_info);
+}
+
+static int
+ssl_queue_packet_data(void *tapdata, packet_info *pinfo, epan_dissect_t *edt, const void *ssl)
+{
+ follow_info_t* follow_info = tapdata;
+ SslDecryptedRecord* rec;
+ int proto_ssl = (int) ssl;
+ StringInfo* data = p_get_proto_data(pinfo->fd, proto_ssl);
+ /*ssl_debug_printf("ssl_queue_packet_data: pinfo %p proto_ssl %d data %p\n",
+ pinfo, proto_ssl, data);*/
+
+ /* skip packet without decrypted data payload*/
+ if (!data)
+ return 0;
+
+ /* compute packet direction */
+ rec = g_malloc(sizeof(SslDecryptedRecord));
+
+ if (follow_info->client_port == 0) {
+ follow_info->client_port = pinfo->srcport;
+ memcpy(follow_info->client_ip, pinfo->src.data, pinfo->src.len);
+ }
+ if (memcmp(follow_info->client_ip, pinfo->src.data, pinfo->src.len) == 0 &&
+ follow_info->client_port == pinfo->srcport) {
+ rec->is_server = 0;
+ }
+ else
+ rec->is_server = 1;
+
+ /* update stream counter */
+ follow_info->bytes_written[rec->is_server] += data->data_len;
+
+ /* extract decrypted data and queue it locally */
+ rec->data = data;
+ follow_info->ssl_decrypted_data = g_list_append(
+ follow_info->ssl_decrypted_data,rec);
+ /*ssl_debug_printf("ssl_queue_packet_data: ssl_decrypted_data %p data len %d\n",
+ follow_info->ssl_decrypted_data, data->data_len);*/
+
+ return 0;
+}
+
+/* Follow the TCP stream, if any, to which the last packet that we called
+ a dissection routine on belongs (this might be the most recently
+ selected packet, or it might be the last packet in the file). */
+void
+ssl_stream_cb(GtkWidget * w, gpointer data _U_)
+{
+ GtkWidget *streamwindow, *vbox, *txt_scrollw, *text, *filter_te;
+ GtkWidget *hbox, *button_hbox, *button, *radio_bt;
+ GtkWidget *stream_fr, *stream_vb;
+ GtkWidget *stream_om, *stream_menu, *stream_mi;
+ GtkTooltips *tooltips;
+ gchar *follow_filter;
+ const gchar *previous_filter;
+ int filter_out_filter_len, previus_filter_len;
+ const char *hostname0, *hostname1;
+ char *port0, *port1;
+ char string[128];
+ follow_tcp_stats_t stats;
+ follow_info_t *follow_info;
+ GString* msg;
+
+ /* we got tcp so we can follow */
+ if (cfile.edt->pi.ipproto != 6) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error following stream. Please make\n"
+ "sure you have an SSL packet selected.");
+ return;
+ }
+
+ follow_info = g_new0(follow_info_t, 1);
+
+ /* data will be passed via tap callback*/
+ msg = register_tap_listener("ssl"SSL_SUFFIX, follow_info, NULL,
+ NULL, ssl_queue_packet_data, NULL);
+ if (msg)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't register ssl tap: %s\n",msg->str);
+ return;
+ }
+
+ /* Create a new filter that matches all packets in the TCP stream,
+ and set the display filter entry accordingly */
+ reset_tcp_reassembly();
+ follow_filter = build_follow_filter(&cfile.edt->pi);
+
+ /* Set the display filter entry accordingly */
+ filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY);
+
+ /* needed in follow_filter_out_stream(), is there a better way? */
+ follow_info->filter_te = filter_te;
+
+ /* save previous filter, const since we're not supposed to alter */
+ previous_filter =
+ (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
+
+ /* allocate our new filter. API claims g_malloc terminates program on failure */
+ /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
+ previus_filter_len = previous_filter?strlen(previous_filter):0;
+ filter_out_filter_len = strlen(follow_filter) + previus_filter_len + 16;
+ follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
+
+ /* append the negation */
+ if(previus_filter_len) {
+ g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
+ "%s and !(%s)", previous_filter, follow_filter);
+ } else {
+ g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
+ "!(%s)", follow_filter);
+ }
+
+
+ gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
+
+ /* Run the display filter so it goes in effect - even if it's the
+ same as the previous display filter. */
+ main_filter_packets(&cfile, follow_filter, TRUE);
+
+ /* Free the filter string, as we're done with it. */
+ g_free(follow_filter);
+
+ /* The data_out_file should now be full of the streams information */
+ remove_tap_listener(follow_info);
+
+ /* The data_out_filename file now has all the text that was in the session */
+ streamwindow = dlg_window_new("Follow TCP stream");
+
+ /* needed in follow_filter_out_stream(), is there a better way? */
+ follow_info->streamwindow = streamwindow;
+
+ gtk_widget_set_name(streamwindow, "TCP stream window");
+ gtk_window_set_default_size(GTK_WINDOW(streamwindow), DEF_WIDTH, DEF_HEIGHT);
+ gtk_container_border_width(GTK_CONTAINER(streamwindow), 6);
+
+ /* setup the container */
+ tooltips = gtk_tooltips_new ();
+
+ vbox = gtk_vbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
+
+ /* content frame */
+ if (incomplete_tcp_stream) {
+ stream_fr = gtk_frame_new("Stream Content (incomplete)");
+ } else {
+ stream_fr = gtk_frame_new("Stream Content");
+ }
+ gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
+ gtk_widget_show(stream_fr);
+
+ stream_vb = gtk_vbox_new(FALSE, 6);
+ gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
+ gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
+
+ /* create a scrolled window for the text */
+ txt_scrollw = scrolled_window_new(NULL, NULL);
+#if GTK_MAJOR_VERSION >= 2
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_SHADOW_IN);
+#endif
+ gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
+
+ /* create a text box */
+#if GTK_MAJOR_VERSION < 2
+ text = gtk_text_new(NULL, NULL);
+ gtk_text_set_editable(GTK_TEXT(text), FALSE);
+#else
+ text = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
+#endif
+ gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
+ follow_info->text = text;
+
+
+ /* stream hbox */
+ hbox = gtk_hbox_new(FALSE, 1);
+ gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
+
+ /* Create Save As Button */
+ button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
+ SIGNAL_CONNECT(button, "clicked", follow_save_as_cmd_cb, follow_info);
+ gtk_tooltips_set_tip (tooltips, button, "Save the content as currently displayed ", NULL);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+ /* Stream to show */
+ follow_tcp_stats(&stats);
+
+ if (stats.is_ipv6) {
+ struct e_in6_addr ipaddr;
+ memcpy(&ipaddr, stats.ip_address[0], 16);
+ hostname0 = get_hostname6(&ipaddr);
+ memcpy(&ipaddr, stats.ip_address[0], 16);
+ hostname1 = get_hostname6(&ipaddr);
+ } else {
+ guint32 ipaddr;
+ memcpy(&ipaddr, stats.ip_address[0], 4);
+ hostname0 = get_hostname(ipaddr);
+ memcpy(&ipaddr, stats.ip_address[1], 4);
+ hostname1 = get_hostname(ipaddr);
+ }
+
+ port0 = get_tcp_port(stats.tcp_port[0]);
+ port1 = get_tcp_port(stats.tcp_port[1]);
+
+ follow_info->is_ipv6 = stats.is_ipv6;
+
+ stream_om = gtk_option_menu_new();
+ stream_menu = gtk_menu_new();
+
+ /* Both Stream Directions */
+ g_snprintf(string, sizeof(string),
+ "Entire conversation (%u bytes)",
+ follow_info->bytes_written[0] + follow_info->bytes_written[1]);
+ stream_mi = gtk_menu_item_new_with_label(string);
+ SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_both,
+ follow_info);
+ gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
+ gtk_widget_show(stream_mi);
+ follow_info->show_stream = BOTH_HOSTS;
+
+ /* Host 0 --> Host 1 */
+ g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
+ hostname0, port0, hostname1, port1,
+ follow_info->bytes_written[0]);
+ stream_mi = gtk_menu_item_new_with_label(string);
+ SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_client,
+ follow_info);
+ gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
+ gtk_widget_show(stream_mi);
+
+ /* Host 1 --> Host 0 */
+ g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
+ hostname1, port1, hostname0, port0,
+ follow_info->bytes_written[1]);
+ stream_mi = gtk_menu_item_new_with_label(string);
+ SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_server,
+ follow_info);
+ gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
+ gtk_widget_show(stream_mi);
+
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(stream_om), stream_menu);
+ /* Set history to 0th item, i.e., the first item. */
+ gtk_option_menu_set_history(GTK_OPTION_MENU(stream_om), 0);
+ gtk_tooltips_set_tip (tooltips, stream_om,
+ "Select the stream direction to display", NULL);
+ gtk_box_pack_start(GTK_BOX(hbox), stream_om, FALSE, FALSE, 0);
+
+ /* ASCII radio button */
+ radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
+ gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"ASCII\" format", NULL);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), TRUE);
+ gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
+ SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
+ follow_info);
+ follow_info->ascii_bt = radio_bt;
+ follow_info->show_type = SHOW_ASCII;
+
+ /* HEX DUMP radio button */
+ radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
+ (GTK_RADIO_BUTTON(radio_bt)),
+ "Hex Dump");
+ gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Hexdump\" format", NULL);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
+ gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
+ SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
+ follow_info);
+ follow_info->hexdump_bt = radio_bt;
+
+ /* C Array radio button */
+ radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
+ (GTK_RADIO_BUTTON(radio_bt)),
+ "C Arrays");
+ gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"C Array\" format", NULL);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
+ gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
+ SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
+ follow_info);
+ follow_info->carray_bt = radio_bt;
+
+ /* Raw radio button */
+ radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
+ (GTK_RADIO_BUTTON(radio_bt)),
+ "Raw");
+ gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Raw\" (binary) format. "
+ "As this contains non printable characters, the screen output will be in ASCII format", NULL);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
+ gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
+ SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
+ follow_info);
+ follow_info->raw_bt = radio_bt;
+
+ /* button hbox */
+ button_hbox = gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(vbox), button_hbox, FALSE, FALSE, 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
+ gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
+
+ /* Create exclude stream button */
+ button = gtk_button_new_with_label("Filter out this stream");
+ SIGNAL_CONNECT(button, "clicked", follow_filter_out_stream, follow_info);
+ gtk_tooltips_set_tip (tooltips, button,
+ "Build a display filter which cuts this stream from the capture", NULL);
+ gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
+
+ /* Create Close Button */
+ button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
+ gtk_tooltips_set_tip (tooltips, button,
+ "Close the dialog and keep the current display filter", NULL);
+ gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+
+ window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
+
+ /* Tuck away the follow_info object into the window */
+ OBJECT_SET_DATA(streamwindow, E_FOLLOW_INFO_KEY, follow_info);
+
+ follow_load_text(follow_info);
+ remember_follow_info(follow_info);
+
+ SIGNAL_CONNECT(streamwindow, "delete_event", window_delete_event_cb, NULL);
+ SIGNAL_CONNECT(streamwindow, "destroy", follow_destroy_cb, NULL);
+
+ /* Make sure this widget gets destroyed if we quit the main loop,
+ so that if we exit, we clean up any temporary files we have
+ for "Follow TCP Stream" windows. */
+ gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
+
+ gtk_widget_show_all(streamwindow);
+ window_present(streamwindow);
+}
+
+/* The destroy call back has the responsibility of
+ * unlinking the temporary file
+ * and freeing the filter_out_filter */
+static void
+follow_destroy_cb(GtkWidget *w, gpointer data _U_)
+{
+ GList* cur;
+ follow_info_t *follow_info;
+
+ follow_info = OBJECT_GET_DATA(w, E_FOLLOW_INFO_KEY);
+ g_free(follow_info->filter_out_filter);
+ forget_follow_info(follow_info);
+
+ /* free decrypted data list*/
+ for (cur = follow_info->ssl_decrypted_data; cur; cur = g_list_next(cur))
+ if (cur->data)
+ {
+ /*ssl_debug_printf("follow_destroy_cb: freeing chunk %p\n", cur->data);*/
+ g_free(cur->data);
+ cur->data = NULL;
+ }
+ g_list_free (follow_info->ssl_decrypted_data);
+ g_free(follow_info);
+}
+
+/* XXX - can I emulate follow_charset_toggle_cb() instead of having
+ * 3 different functions here?
+ * That might not be a bad idea, as it might mean we only reload
+ * the window once, not twice - see follow_charset_toggle_cb()
+ * for an explanation. */
+static void
+follow_stream_om_both(GtkWidget *w _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+ follow_info->show_stream = BOTH_HOSTS;
+ follow_load_text(follow_info);
+}
+
+static void
+follow_stream_om_client(GtkWidget *w _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+ follow_info->show_stream = FROM_CLIENT;
+ follow_load_text(follow_info);
+}
+
+static void
+follow_stream_om_server(GtkWidget *w _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+ follow_info->show_stream = FROM_SERVER;
+ follow_load_text(follow_info);
+}
+
+
+/* Handles the display style toggling */
+static void
+follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+
+ /*
+ * A radio button toggles when it goes on and when it goes
+ * off, so when you click a radio button two signals are
+ * delivered. We only want to reprocess the display once,
+ * so we do it only when the button goes on.
+ */
+ if (GTK_TOGGLE_BUTTON(w)->active) {
+ if (w == follow_info->hexdump_bt)
+ follow_info->show_type = SHOW_HEXDUMP;
+ else if (w == follow_info->carray_bt)
+ follow_info->show_type = SHOW_CARRAY;
+ else if (w == follow_info->ascii_bt)
+ follow_info->show_type = SHOW_ASCII;
+ else if (w == follow_info->raw_bt)
+ follow_info->show_type = SHOW_RAW;
+ follow_load_text(follow_info);
+ }
+}
+
+#define FLT_BUF_SIZE 1024
+
+typedef enum {
+ FRS_OK,
+ FRS_OPEN_ERROR,
+ FRS_READ_ERROR,
+ FRS_PRINT_ERROR
+} frs_return_t;
+
+/*
+ * XXX - the routine pointed to by "print_line" doesn't get handed lines,
+ * it gets handed bufferfuls. That's fine for "follow_write_raw()"
+ * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
+ * the "print_line()" routine from "print.c", and as that routine might
+ * genuinely expect to be handed a line (if, for example, it's using
+ * some OS or desktop environment's printing API, and that API expects
+ * to be handed lines), "follow_print_text()" should probably accumulate
+ * lines in a buffer and hand them "print_line()". (If there's a
+ * complete line in a buffer - i.e., there's nothing of the line in
+ * the previous buffer or the next buffer - it can just hand that to
+ * "print_line()" after filtering out non-printables, as an
+ * optimization.)
+ *
+ * This might or might not be the reason why C arrays display
+ * correctly but get extra blank lines very other line when printed.
+ */
+static frs_return_t
+follow_read_stream(follow_info_t *follow_info,
+ gboolean (*print_line) (char *, size_t, gboolean, void *),
+ void *arg)
+{
+ int iplen;
+ guint32 current_pos, global_client_pos = 0, global_server_pos = 0;
+ guint32 *global_pos;
+ gboolean skip;
+ gchar initbuf[256];
+ guint32 server_packet_count = 0;
+ guint32 client_packet_count = 0;
+ static const gchar hexchars[16] = "0123456789abcdef";
+ GList* cur;
+
+ iplen = (follow_info->is_ipv6) ? 16 : 4;
+
+ /*ssl_debug_printf("follow_read_stream: iplen %d list %p\n", iplen,
+ follow_info->ssl_decrypted_data);*/
+
+ for (cur = follow_info->ssl_decrypted_data; cur; cur = g_list_next(cur)) {
+ SslDecryptedRecord* rec = cur->data;
+ skip = FALSE;
+ if (!rec->is_server) {
+ global_pos = &global_client_pos;
+ if (follow_info->show_stream == FROM_SERVER) {
+ skip = TRUE;
+ }
+ }
+ else {
+ global_pos = &global_server_pos;
+ if (follow_info->show_stream == FROM_CLIENT) {
+ skip = TRUE;
+ }
+ }
+
+ if (!skip) {
+ size_t nchars = rec->data->data_len;
+ char* buffer = (char*) rec->data->data;
+
+ /*ssl_debug_printf("follow_read_stream: chunk len %d is_server %d\n",
+ nchars, rec->is_server);*/
+
+ switch (follow_info->show_type) {
+
+ case SHOW_ASCII:
+ /* If our native arch is EBCDIC, call:
+ * ASCII_TO_EBCDIC(buffer, nchars);
+ */
+ if (!(*print_line) (buffer, nchars, rec->is_server, arg))
+ goto print_error;
+ break;
+
+ case SHOW_RAW:
+ /* Don't translate, no matter what the native arch
+ * is.
+ */
+ if (!(*print_line) (buffer, nchars, rec->is_server, arg))
+ goto print_error;
+ break;
+
+ case SHOW_HEXDUMP:
+ current_pos = 0;
+ while (current_pos < nchars) {
+ gchar hexbuf[256];
+ int i;
+ gchar *cur = hexbuf, *ascii_start;
+
+ /* is_server indentation : put 78 spaces at the
+ * beginning of the string */
+ if (rec->is_server && follow_info->show_stream == BOTH_HOSTS) {
+ memset(cur, ' ', 78);
+ cur += 78;
+ }
+ cur += g_snprintf(cur, 20, "%08X ", *global_pos);
+ /* 49 is space consumed by hex chars */
+ ascii_start = cur + 49;
+ for (i = 0; i < 16 && current_pos + i < nchars; i++) {
+ *cur++ =
+ hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
+ *cur++ =
+ hexchars[buffer[current_pos + i] & 0x0f];
+ *cur++ = ' ';
+ if (i == 7)
+ *cur++ = ' ';
+ }
+ /* Fill it up if column isn't complete */
+ while (cur < ascii_start)
+ *cur++ = ' ';
+
+ /* Now dump bytes as text */
+ for (i = 0; i < 16 && current_pos + i < nchars; i++) {
+ *cur++ =
+ (isprint((guchar)buffer[current_pos + i]) ?
+ buffer[current_pos + i] : '.' );
+ if (i == 7) {
+ *cur++ = ' ';
+ }
+ }
+ current_pos += i;
+ (*global_pos) += i;
+ *cur++ = '\n';
+ *cur = 0;
+ if (!(*print_line) (hexbuf, strlen(hexbuf), rec->is_server, arg))
+ goto print_error;
+ }
+ break;
+
+ case SHOW_CARRAY:
+ current_pos = 0;
+ g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n",
+ rec->is_server ? 1 : 0,
+ rec->is_server ? server_packet_count++ : client_packet_count++);
+ if (!(*print_line) (initbuf, strlen(initbuf), rec->is_server, arg))
+ goto print_error;
+ while (current_pos < nchars) {
+ gchar hexbuf[256];
+ int i, cur;
+
+ cur = 0;
+ for (i = 0; i < 8 && current_pos + i < nchars; i++) {
+ /* Prepend entries with "0x" */
+ hexbuf[cur++] = '0';
+ hexbuf[cur++] = 'x';
+ hexbuf[cur++] =
+ hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
+ hexbuf[cur++] =
+ hexchars[buffer[current_pos + i] & 0x0f];
+
+ /* Delimit array entries with a comma */
+ if (current_pos + i + 1 < nchars)
+ hexbuf[cur++] = ',';
+
+ hexbuf[cur++] = ' ';
+ }
+
+ /* Terminate the array if we are at the end */
+ if (current_pos + i == nchars) {
+ hexbuf[cur++] = '}';
+ hexbuf[cur++] = ';';
+ }
+
+ current_pos += i;
+ (*global_pos) += i;
+ hexbuf[cur++] = '\n';
+ hexbuf[cur] = 0;
+ if (!(*print_line) (hexbuf, strlen(hexbuf), rec->is_server, arg))
+ goto print_error;
+ }
+ break;
+ }
+ }
+ }
+ return FRS_OK;
+
+print_error:
+ return FRS_PRINT_ERROR;
+}
+
+/*
+ * XXX - for text printing, we probably want to wrap lines at 80 characters;
+ * (PostScript printing is doing this already), and perhaps put some kind of
+ * dingbat (to use the technical term) to indicate a wrapped line, along the
+ * lines of what's done when displaying this in a window, as per Warren Young's
+ * suggestion.
+ */
+static gboolean
+follow_print_text(char *buffer, size_t nchars, gboolean is_server _U_, void *arg)
+{
+ print_stream_t *stream = arg;
+ size_t i;
+ char *str;
+
+ /* convert non printable characters */
+ for (i = 0; i < nchars; i++) {
+ if (buffer[i] == '\n' || buffer[i] == '\r')
+ continue;
+ if (! isprint((guchar)buffer[i])) {
+ buffer[i] = '.';
+ }
+ }
+
+ /* convert unterminated char array to a zero terminated string */
+ str = g_malloc(nchars + 1);
+ memcpy(str, buffer, nchars);
+ str[nchars] = 0;
+ print_line(stream, /*indent*/ 0, str);
+ g_free(str);
+
+ return TRUE;
+}
+
+static gboolean
+follow_write_raw(char *buffer, size_t nchars, gboolean is_server _U_, void *arg)
+{
+ FILE *fh = arg;
+ size_t nwritten;
+
+ nwritten = fwrite(buffer, 1, nchars, fh);
+ if (nwritten != nchars)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+
+ /* Lock out user from messing with us. (ie. don't free our data!) */
+ gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
+
+ /* Set the display filter. */
+ gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te), follow_info->filter_out_filter);
+
+ /* Run the display filter so it goes in effect. */
+ main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
+
+ /* we force a subsequent close */
+ window_destroy(follow_info->streamwindow);
+
+ return;
+}
+
+/* static variable declarations to speed up the performance
+ * of follow_load_text and follow_add_to_gtk_text
+ */
+static GdkColor server_fg, server_bg;
+static GdkColor client_fg, client_bg;
+#if GTK_MAJOR_VERSION >= 2
+static GtkTextTag *server_tag, *client_tag;
+#endif
+
+static gboolean
+follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server,
+ void *arg)
+{
+ GtkWidget *text = arg;
+#if GTK_MAJOR_VERSION >= 2
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
+ GtkTextIter iter;
+#endif
+
+#if GTK_MAJOR_VERSION >= 2 || GTK_MINOR_VERSION >= 3
+ /* While our isprint() hack is in place, we
+ * have to use convert some chars to '.' in order
+ * to be able to see the data we *should* see
+ * in the GtkText widget.
+ */
+ size_t i;
+
+ for (i = 0; i < nchars; i++) {
+ if (buffer[i] == '\n' || buffer[i] == '\r')
+ continue;
+ if (! isprint(buffer[i])) {
+ buffer[i] = '.';
+ }
+ }
+#endif
+
+#if GTK_MAJOR_VERSION < 2
+ if (is_server) {
+ gtk_text_insert(GTK_TEXT(text), user_font_get_regular(), &server_fg,
+ &server_bg, buffer, nchars);
+ } else {
+ gtk_text_insert(GTK_TEXT(text), user_font_get_regular(), &client_fg,
+ &client_bg, buffer, nchars);
+ }
+#else
+ gtk_text_buffer_get_end_iter(buf, &iter);
+ if (is_server) {
+ gtk_text_buffer_insert_with_tags(buf, &iter, buffer, nchars,
+ server_tag, NULL);
+ } else {
+ gtk_text_buffer_insert_with_tags(buf, &iter, buffer, nchars,
+ client_tag, NULL);
+ }
+#endif
+ return TRUE;
+}
+
+static void
+follow_load_text(follow_info_t *follow_info)
+{
+#if GTK_MAJOR_VERSION < 2
+ int bytes_already;
+#else
+ GtkTextBuffer *buf;
+
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
+#endif
+
+ /* prepare colors one time for repeated use by follow_add_to_gtk_text */
+ color_t_to_gdkcolor(&server_fg, &prefs.st_server_fg);
+ color_t_to_gdkcolor(&server_bg, &prefs.st_server_bg);
+ color_t_to_gdkcolor(&client_fg, &prefs.st_client_fg);
+ color_t_to_gdkcolor(&client_bg, &prefs.st_client_bg);
+
+ /* Delete any info already in text box */
+#if GTK_MAJOR_VERSION < 2
+ bytes_already = gtk_text_get_length(GTK_TEXT(follow_info->text));
+ if (bytes_already > 0) {
+ gtk_text_set_point(GTK_TEXT(follow_info->text), 0);
+ gtk_text_forward_delete(GTK_TEXT(follow_info->text), bytes_already);
+ }
+
+ /* stop the updates while we fill the text box */
+ gtk_text_freeze(GTK_TEXT(follow_info->text));
+#else
+ /* prepare tags one time for repeated use by follow_add_to_gtk_text */
+ server_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &server_fg,
+ "background-gdk", &server_bg, "font-desc",
+ user_font_get_regular(), NULL);
+ client_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &client_fg,
+ "background-gdk", &client_bg, "font-desc",
+ user_font_get_regular(), NULL);
+
+ gtk_text_buffer_set_text(buf, "", -1);
+#endif
+ follow_read_stream(follow_info, follow_add_to_gtk_text, follow_info->text);
+#if GTK_MAJOR_VERSION < 2
+ gtk_text_thaw(GTK_TEXT(follow_info->text));
+#endif
+}
+
+
+/*
+ * Keep a static pointer to the current "Save TCP Follow Stream As" window, if
+ * any, so that if somebody tries to do "Save"
+ * while there's already a "Save TCP Follow Stream" window up, we just pop
+ * up the existing one, rather than creating a new one.
+ */
+static void
+follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
+{
+ GtkWidget *new_win;
+ follow_info_t *follow_info = data;
+
+ if (follow_info->follow_save_as_w != NULL) {
+ /* There's already a dialog box; reactivate it. */
+ reactivate_window(follow_info->follow_save_as_w);
+ return;
+ }
+
+ new_win = file_selection_new("Ethereal: Save TCP Follow Stream As",
+ FILE_SELECTION_SAVE);
+ follow_info->follow_save_as_w = new_win;
+
+ /* Tuck away the follow_info object into the window */
+ OBJECT_SET_DATA(new_win, E_FOLLOW_INFO_KEY, follow_info);
+
+ SIGNAL_CONNECT(new_win, "destroy", follow_save_as_destroy_cb, follow_info);
+
+#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
+ if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
+ {
+ follow_save_as_ok_cb(new_win, new_win);
+ } else {
+ window_destroy(new_win);
+ }
+#else
+ /* Connect the ok_button to file_save_as_ok_cb function and pass along a
+ pointer to the file selection box widget */
+ SIGNAL_CONNECT(GTK_FILE_SELECTION(new_win)->ok_button,
+ "clicked", follow_save_as_ok_cb, new_win);
+
+ window_set_cancel_button(new_win,
+ GTK_FILE_SELECTION(new_win)->cancel_button, window_cancel_button_cb);
+
+ gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win), "");
+
+ SIGNAL_CONNECT(new_win, "delete_event", window_delete_event_cb, NULL);
+
+ gtk_widget_show_all(new_win);
+ window_present(new_win);
+#endif
+}
+
+
+static void
+follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
+{
+ gchar *to_name;
+ follow_info_t *follow_info;
+ FILE *fh;
+ print_stream_t *stream = NULL;
+ gchar *dirname;
+
+#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
+ to_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
+#else
+ to_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
+#endif
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if (test_for_directory(to_name) == EISDIR) {
+ /* It's a directory - set the file selection box to display that
+ directory, and leave the selection box displayed. */
+ set_last_open_dir(to_name);
+ g_free(to_name);
+ file_selection_set_current_folder(fs, get_last_open_dir());
+ return;
+ }
+
+ follow_info = OBJECT_GET_DATA(fs, E_FOLLOW_INFO_KEY);
+ if (follow_info->show_type == SHOW_RAW) {
+ /* Write the data out as raw binary data */
+ fh = eth_fopen(to_name, "wb");
+ } else {
+ /* Write it out as text */
+ fh = eth_fopen(to_name, "w");
+ }
+ if (fh == NULL) {
+ open_failure_alert_box(to_name, errno, TRUE);
+ g_free(to_name);
+ return;
+ }
+
+ gtk_widget_hide(GTK_WIDGET(fs));
+ window_destroy(GTK_WIDGET(fs));
+
+ if (follow_info->show_type == SHOW_RAW) {
+ switch (follow_read_stream(follow_info, follow_write_raw, fh)) {
+ case FRS_OK:
+ if (fclose(fh) == EOF)
+ write_failure_alert_box(to_name, errno);
+ break;
+
+ case FRS_OPEN_ERROR:
+ case FRS_READ_ERROR:
+ fclose(fh);
+ break;
+
+ case FRS_PRINT_ERROR:
+ write_failure_alert_box(to_name, errno);
+ fclose(fh);
+ break;
+ }
+ } else {
+ stream = print_stream_text_stdio_new(fh);
+ switch (follow_read_stream(follow_info, follow_print_text, stream)) {
+ case FRS_OK:
+ if (!destroy_print_stream(stream))
+ write_failure_alert_box(to_name, errno);
+ break;
+
+ case FRS_OPEN_ERROR:
+ case FRS_READ_ERROR:
+ destroy_print_stream(stream);
+ break;
+
+ case FRS_PRINT_ERROR:
+ write_failure_alert_box(to_name, errno);
+ destroy_print_stream(stream);
+ break;
+ }
+ }
+
+ /* Save the directory name for future file dialogs. */
+ dirname = get_dirname(to_name); /* Overwrites to_name */
+ set_last_open_dir(dirname);
+ g_free(to_name);
+}
+
+static void
+follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+
+ /* Note that we no longer have a dialog box. */
+ follow_info->follow_save_as_w = NULL;
+}