Ethereal-dev: Re: [Ethereal-dev] SSL decryption patch for ethereal 0.10.13
Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.
From: Paolo Abeni <paolo.abeni@xxxxxxxx>
Date: Wed, 07 Dec 2005 10:00:26 +0100
On Mon, 2005-12-05 at 12:46 +0100, Joerg Mayer wrote: [...] > So the > idea is to handle this like most other features requesting libs that may > not be present on all systems (e.g. pcre): By default it should be used > if a usable installation exists and it should be possible to require its > existence (failing if isn't usable). Last it should be possible to build > the package without checking or using the lib Ok the attached version of the patch should works as requested. The relevant configure option is '--with-libgnutls-prefix'. Let me know if there are any trouble. Best regards, Paolo -- Email.it, the professional e-mail, gratis per te: http://www.email.it/f Sponsor: Natsabe.it la più grande erboristeria online italiana * prezzi bassi tutto l'anno ! Clicca qui: http://adv.email.it/cgi-bin/foclick.cgi?mid=1298&d=7-12
diff -uNr ethereal-0.10.13/acinclude.m4 ethereal-0.10.13-patch/acinclude.m4 --- ethereal-0.10.13/acinclude.m4 2005-10-10 15:23:13.000000000 +0200 +++ ethereal-0.10.13-patch/acinclude.m4 2005-12-07 09:51:53.000000000 +0100 @@ -1268,3 +1268,161 @@ fi AC_SUBST(KRB5_LIBS) ]) + +dnl Autoconf macros for libgnutls +dnl $id$ + +# 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 -uNr ethereal-0.10.13/config.h.in ethereal-0.10.13-patch/config.h.in --- ethereal-0.10.13/config.h.in 2005-10-14 21:18:33.000000000 +0200 +++ ethereal-0.10.13-patch/config.h.in 2005-12-07 09:51:53.000000000 +0100 @@ -55,6 +55,9 @@ /* Define if krb5.h defines KEYTYPE_ARCFOUR_56 */ #undef HAVE_KEYTYPE_ARCFOUR_56 +/* Define to use gnutls library */ +#undef HAVE_LIBGNUTLS + /* Define to use libpcap library */ #undef HAVE_LIBPCAP diff -uNr ethereal-0.10.13/configure.in ethereal-0.10.13-patch/configure.in --- ethereal-0.10.13/configure.in 2005-10-14 21:10:13.000000000 +0200 +++ ethereal-0.10.13-patch/configure.in 2005-12-07 09:51:53.000000000 +0100 @@ -63,6 +63,24 @@ 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") @@ -1330,3 +1348,4 @@ 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 -uNr ethereal-0.10.13/epan/dissectors/Makefile.common ethereal-0.10.13-patch/epan/dissectors/Makefile.common --- ethereal-0.10.13/epan/dissectors/Makefile.common 2005-10-10 15:23:00.000000000 +0200 +++ ethereal-0.10.13-patch/epan/dissectors/Makefile.common 2005-12-07 09:51:53.000000000 +0100 @@ -540,6 +540,7 @@ packet-skinny.c \ packet-slimp3.c \ packet-sll.c \ + packet-ssl-utils.c \ packet-slowprotocols.c \ packet-slsk.c \ packet-smb-browse.c \ diff -uNr ethereal-0.10.13/epan/dissectors/packet-ssl.c ethereal-0.10.13-patch/epan/dissectors/packet-ssl.c --- ethereal-0.10.13/epan/dissectors/packet-ssl.c 2005-10-10 15:23:04.000000000 +0200 +++ ethereal-0.10.13-patch/epan/dissectors/packet-ssl.c 2005-12-07 09:51:53.000000000 +0100 @@ -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 @@ -97,6 +101,8 @@ #include <epan/conversation.h> #include <epan/prefs.h> #include <epan/dissectors/packet-x509af.h> +#include <epan/dissectors/packet-ssl-utils.h> + static gboolean ssl_desegment = TRUE; @@ -114,6 +120,7 @@ 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 +206,191 @@ 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; + +static char* ssl_keys_list = "127.0.0.1:443:/etc/ssl/apache/server.key"; +static SslAssociation ssl_associations[] = { + {443, 80, 0, "Hypertext transfer protocol"}, /* https */ + {636, 389, 0, "Lightweight directory access protocol"}, /* ldap */ + {993, 143, 0, "Interactive mail access protocol"}, /* imap */ + {995, 110, 0, "Post office protocol"}, /* pop3 */ + {4433, 80, 0, "Hypertext transfer protocol"}, /* https */ + {0, 0, 0, 0}}; + +typedef struct _SslService { + address addr; + guint port; +} SslService; + +static GHashTable *ssl_session_hash = NULL; +static GHashTable *ssl_key_hash = NULL; + +int ssl_packet_from_server(unsigned int port) +{ + SslAssociation* current; + for (current = ssl_associations; current->ssl_port != 0; current++) + { + if (current->ssl_port == port) + return 1; + } + return 0; +} + +/* Hash Functions */ +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; +} + +static void ssl_init(void) +{ + if (ssl_session_hash) + g_hash_table_destroy(ssl_session_hash); + if (ssl_key_hash) + g_hash_table_destroy(ssl_key_hash); + + ssl_key_hash = g_hash_table_new(ssl_private_key_hash,ssl_private_key_equal); + ssl_session_hash = g_hash_table_new(ssl_hash, ssl_equal); + if (ssl_keys_list) + { + char* end; + char* start = strdup(ssl_keys_list); + char* tmp = 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 and filename with ':' separator */ + ssl_debug_printf("ssl_init found host entry %s\n", addr); + port = strchr(addr, ':'); + if (!port) + return; + *port = 0; + port++; + + filename = strchr(port,':'); + if (!filename) + return; + *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); + return; + } + + private_key = ssl_load_key(fp); + if (!private_key) { + fprintf(stderr,"can't load private key from %s\n", + filename); + return; + } + 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); + } +} + +static void ssl_save_session(SslDecryptSession* ssl) +{ + StringInfo* session_id = g_malloc0(sizeof(StringInfo) + ssl->session_id.data_len); + StringInfo* master_secret = g_malloc0(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 +896,8 @@ 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 +914,19 @@ 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 +938,7 @@ static void dissect_ssl3_hnd_finished(tvbuff_t *tvb, proto_tree *tree, guint32 offset, - guint *conv_version); + guint* conv_version); /* @@ -754,12 +950,14 @@ 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 +992,7 @@ * 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 +1007,7 @@ static int ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb, guint32 offset, guint32 record_length); - +static SslAssociation* ssl_find_association(packet_info* pinfo); /********************************************************************* * * Main dissector @@ -824,12 +1022,13 @@ 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 +1051,48 @@ 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= ssl_alloc_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; + } + + /* 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 " + "%hhd.%hhd.%hhd.%hhd:%d\n", dummy.addr.data[0], + dummy.addr.data[1],dummy.addr.data[2], + dummy.addr.data[3],dummy.port); + } + } + 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 +1147,13 @@ /* 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 +1167,16 @@ 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 +1188,17 @@ { /* 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 +1214,7 @@ 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,21 +1223,85 @@ /* 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; } } +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; + SslDecoder* decoder; + StringInfo* data; + + /* retrive decoder for this packet direction*/ + if (ssl_packet_from_server(pinfo->srcport)) { + 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; + } + ssl_debug_printf("decrypt_ssl3_record: app_data len %d ssl state %X\n", + record_length, ssl->state); + + /* if we can decrypt and decryption have success + * add decrypted data to this packet info*/ + if (!(ssl->state & SSL_HAVE_SESSION_KEY)) { + return ; + } + + /* ensure we have enough storage space for decrypted data */ + if (record_length > decoder->decrypted_data.data_len) + { + ssl_debug_printf("decrypt_ssl3_record: allocating decrypt %d bytes for decrypt data\n", record_length + 32); + ssl_data_init(&decoder->decrypted_data, NULL, record_length + 32); + } + + /* run decryption and add decrypted payload to protocol data, if decryption + * is successful*/ + len = decoder->decrypted_data.data_len; + if ((ssl_decrypt_record(ssl, decoder, + content_type, tvb_get_ptr(tvb, offset, record_length), + record_length, decoder->decrypted_data.data, &len) == 0) && + save_plaintext) + { + 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 = g_malloc0(sizeof(StringInfo)+ len); + data->data = ((unsigned char*)data) + sizeof(StringInfo); + ssl_data_set(data, decoder->decrypted_data.data, len); + } + else { + /* 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+sizeof(StringInfo)); + data = g_realloc(data, data->data_len + len+sizeof(StringInfo)); + data->data= ((unsigned char*)data) + sizeof(StringInfo); + memcpy(&data->data[data->data_len], decoder->decrypted_data.data, len); + data->data_len += len; + + /* realloc can change ptr so remove old one and readd 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); + } +} + + /********************************************************************* * @@ -1005,7 +1311,8 @@ 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 +1340,8 @@ 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 +1461,21 @@ 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 +1500,11 @@ /* * 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,18 +1513,28 @@ 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); if (check_col(pinfo->cinfo, COL_INFO)) col_append_str(pinfo->cinfo, COL_INFO, "Application Data"); if (ssl_record_tree) { + //proto_name_str = match_strval(content_type, ssl_31_content_type); proto_item_set_text(ssl_record_tree, "%s Record Layer: %s Protocol: Application Data", ssl_version_short_names[*conv_version], @@ -1209,7 +1542,54 @@ tvb_ensure_bytes_exist(tvb, offset, record_length); proto_tree_add_item(ssl_record_tree, hf_ssl_record_appdata, tvb, offset, record_length, 0); - } + } + + /* we need dissector information when the selected packet is shown + * and ssl session pointer is NULL at that time, so we can't access + * info cached there*/ + association = ssl_find_association(pinfo); + + /* show on info colum what we are decoding */ + if (check_col(pinfo->cinfo, COL_INFO) && association) + { + ssl_debug_printf("adding COL_INFO %s\n",association->info); + col_clear(pinfo->cinfo, COL_INFO); + col_append_str(pinfo->cinfo, COL_INFO, association->info); + } + + /* show decrypted data info, if available */ + decrypted = p_get_proto_data(pinfo->fd, proto_ssl); + if (decrypted && ssl_record_tree) + { + 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) { + 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 + if (ssl_record_tree) + proto_tree_add_item(ssl_record_tree, hf_ssl_record_appdata, tvb, + offset, record_length, 0); + break; default: @@ -1227,7 +1607,7 @@ 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 +1630,7 @@ 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 +1674,7 @@ if (check_col(pinfo->cinfo, COL_INFO)) col_append_str(pinfo->cinfo, COL_INFO, "Encrypted Alert"); } - + if (tree) { if (level && desc) @@ -1325,7 +1705,8 @@ 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 +1748,8 @@ 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 +1808,18 @@ /* 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 +1830,11 @@ 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 +1857,51 @@ /* 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 = g_malloc(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*/ + ret = ssl_decrypt_pre_master_secret(ssl, &encrypted_pre_master, ssl->private_key); + g_free(encrypted_pre_master.data); + 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 +1912,8 @@ } 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 +1921,50 @@ 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 +1996,9 @@ } } - return offset - initial_offset; + + // XXXX + return session_id_length+33; } static int @@ -1591,7 +2056,8 @@ 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 +2076,23 @@ 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 +2175,7 @@ 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 +2188,56 @@ */ 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 +2414,7 @@ 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 +2461,9 @@ /* 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 +2562,13 @@ (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);*/ } } @@ -2166,7 +2671,7 @@ /* 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 +2724,8 @@ 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 +2747,7 @@ 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 +2756,54 @@ 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 +2812,26 @@ /* 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 +2839,26 @@ 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 +3407,7 @@ * Support Functions * *********************************************************************/ - +#if 0 static void ssl_set_conv_version(packet_info *pinfo, guint version) { @@ -2895,6 +3438,7 @@ } conversation_add_proto_data(conversation, proto_ssl, GINT_TO_POINTER(version)); } +#endif static int ssl_is_valid_handshake_type(guint8 type) @@ -3222,6 +3766,12 @@ 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 +4047,61 @@ 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 */ @@ -3589,10 +4139,32 @@ "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, "rsa_private_key", "RSA private keys list", + "comma separated list of RSA private key to be used for decryption; " + "each list entry must be in the form of <ip>:<port>:<key_file_name>", + (const char **)&ssl_keys_list); + + } register_dissector("ssl", dissect_ssl, proto_ssl); + + register_init_routine(ssl_init); + SSL_LIB_INIT(); +} +SslAssociation* ssl_find_association(packet_info* pinfo) +{ + SslAssociation* cur; + for (cur = ssl_associations; cur->ssl_port != 0; cur++) { + ssl_debug_printf("ssl_find_dissector cur port %d src port %d dest port %d\n", + cur->ssl_port, pinfo->srcport, pinfo->destport); + if (((cur->ssl_port == pinfo->srcport) || (cur->ssl_port == pinfo->destport)) && cur->handle) { + ssl_debug_printf("ssl_find_dissector found %p\n", cur->handle); + return cur; + } + } + return 0; } /* If this dissector uses sub-dissector registration add a registration @@ -3603,10 +4175,25 @@ proto_reg_handoff_ssl(void) { dissector_handle_t ssl_handle; + dissector_table_t tcp_dissectors; ssl_handle = find_dissector("ssl"); + + tcp_dissectors = find_dissector_table( "tcp.port"); + if (tcp_dissectors) + { + ssl_associations[0].handle = dissector_get_port_handle(tcp_dissectors, 80); + ssl_associations[1].handle = dissector_get_port_handle(tcp_dissectors, 389); + ssl_associations[2].handle = dissector_get_port_handle(tcp_dissectors, 143); + ssl_associations[3].handle = dissector_get_port_handle(tcp_dissectors, 110); + ssl_associations[4].handle = dissector_get_port_handle(tcp_dissectors, 80); + } + ssl_debug_printf("http: %p ldap %p imap %p pop %p\n", + ssl_associations[0].handle, ssl_associations[1].handle, + ssl_associations[2].handle, ssl_associations[3].handle); 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); + dissector_add("tcp.port", 4433, ssl_handle); } diff -uNr ethereal-0.10.13/epan/dissectors/packet-ssl-utils.c ethereal-0.10.13-patch/epan/dissectors/packet-ssl-utils.c --- ethereal-0.10.13/epan/dissectors/packet-ssl-utils.c 1970-01-01 01:00:00.000000000 +0100 +++ ethereal-0.10.13-patch/epan/dissectors/packet-ssl-utils.c 2005-12-07 09:51:53.000000000 +0100 @@ -0,0 +1,1143 @@ +/* packet-ss-utils.c + * + * $Id: ethereal_ssl_decrypt_0.10.13.patch,v 1.10 2005/12/07 07:47:00 cvs Exp $ + * + * ssl manipulation functions + * By Paolo Abeni <paolo.abeni@xxxxxxxxx> + * + * 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" + +#include <glib.h> + +#ifdef HAVE_LIBGNUTLS + +#define SSL_HMAC gcry_md_hd_t +#define SSL_HMAC_INIT(md,key,len,algo) if (*(md)) gcry_md_close(*(md)); \ + gcry_md_open(md,algo, GCRY_MD_FLAG_HMAC); \ + gcry_md_setkey (*(md), key, len) +#define SSL_HMAC_UPDATE(md, data, len) gcry_md_write(*(md), data, len) +#define SSL_HMAC_FINAL(md, data, len) ({ int __algo = gcry_md_get_algo (*(md)); \ + int __len = gcry_md_get_algo_dlen (__algo);\ + memcpy(data, gcry_md_read(*(md), __algo), __len); \ + *len =__len;}) +#define SSL_HMAC_CLEANUP(md) gcry_md_close(*(md)) + +#define SSL_MD gcry_md_hd_t +#define SSL_MD_INIT(md,algo) if (*(md)) gcry_md_close(*(md)); \ + gcry_md_open(md,algo, 0); +#define SSL_MD_UPDATE(md, data, len) gcry_md_write(*(md), data, len) +#define SSL_MD_FINAL(md, data, len) ({ int __algo = gcry_md_get_algo (*(md)); \ + int __len = gcry_md_get_algo_dlen (__algo);\ + memcpy(data, gcry_md_read(*(md), __algo), __len); \ + *len =__len;}) +#define SSL_MD_CLEANUP(md) gcry_md_close(*(md)) + +#define SSL_SHA_CTX gcry_md_hd_t +#define SSL_SHA_INIT(md) if (*(md)) gcry_md_close(*(md)); \ + gcry_md_open(md,GCRY_MD_SHA1, 0); +#define SSL_SHA_UPDATE(md, data, len) gcry_md_write(*(md), data, len) +#define SSL_SHA_FINAL(buf, md) memcpy(buf, gcry_md_read(*(md), GCRY_MD_SHA1), \ + gcry_md_get_algo_dlen(GCRY_MD_SHA1)) + +#define SSL_MD5_CTX gcry_md_hd_t +#define SSL_MD5_INIT(md) if (*(md)) gcry_md_close(*(md)); \ + gcry_md_open(md,GCRY_MD_MD5, 0); +#define SSL_MD5_UPDATE(md, data, len) gcry_md_write(*(md), data, len) +#define SSL_MD5_FINAL(buf, md) memcpy(buf, gcry_md_read(*(md), GCRY_MD_MD5), \ + gcry_md_get_algo_dlen(GCRY_MD_MD5)) + +#define SSL_CIPHER_INIT(cipher, algo,sk,iv, mode) gcry_cipher_init(cipher, algo, sk, iv, mode) + +#define SSL_CIPHER_DECRYPT(chiper, out, outl, in, inl) \ + gcry_cipher_decrypt ( *(chiper), out, outl, in, inl); + +#define SSL_GET_DIGEST_BY_NAME(name) gcry_md_map_name(name) +#define SSL_GET_CIPHER_BY_NAME(name) gcry_cipher_map_name(name) + +#define SSL_GET_KEY_LEN(pk) gcry_pk_get_nbits (pk) +#define SSL_PRIVATE_DECYPT(len, encr_data, decryped_data, pk) \ + pcry_private_decrypt(len, encr_data, decryped_data, 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) + +int pcry_private_decrypt(int len, unsigned char* encr_data, unsigned char** decrypted_data, SSL_PRIVATE_KEY* pk) +{ + int rc, i; + int decr_len = 0; + gcry_sexp_t s_data, s_plain; + gcry_mpi_t encr_mpi; + int encr_len = len; + unsigned char* decr_data_ptr; + gcry_mpi_t text; + + /* 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", + len, 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; + } + + /* write plain text to newly allocated buffer */ + *decrypted_data = decr_data_ptr = g_malloc(decr_len); + 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); + memcpy(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); + + /* write plain text to newly allocated buffer */ + *decrypted_data = decr_data_ptr = g_malloc(decr_len); + 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); + 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); + memcpy(decr_data_ptr, &decr_data_ptr[rc], decr_len - rc); + decr_len -= rc; +#endif + return decr_len; +} + +int gcry_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; +} + +#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 char *digests[]={ + "MD5", + "SHA1" +}; + +static 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) +{ + u_int8_t *ptr=out->data; + unsigned int left=out->data_len; + int tocpy; + u_int8_t *A; + u_int8_t _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,char *usage, + StringInfo* rnd1, StringInfo* rnd2, StringInfo* out) +{ + StringInfo seed, sha_out, md5_out; + u_int8_t *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; + u_int8_t 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, 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; + u_int8_t 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){ + 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, + u_int8_t *mk, u_int8_t *sk, u_int8_t *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; + u_int8_t _iv_c[MAX_BLOCK_SIZE],_iv_s[MAX_BLOCK_SIZE]; + u_int8_t _key_c[MAX_KEY_SIZE],_key_s[MAX_KEY_SIZE]; + int needed; + u_int8_t *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 = 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{ + u_int8_t _iv_block[MAX_BLOCK_SIZE * 2]; + StringInfo iv_block; + StringInfo key_null; + u_int8_t _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; + } + + 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_DECYPT(entrypted_pre_master->data_len, + entrypted_pre_master->data,&ssl_session->pre_master_secret.data, + pk); + + ssl_debug_printf("ssl_decrypt_pre_master_secret private decrypt(%d) %d\n", + entrypted_pre_master->data_len,i); + if(i!=48) { + return -1; + } + + 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 + to 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) + +/* This should go to 2^128, but we're never really going to see + more than 2^64, so we cheat*/ +static int fmt_seq(u_int32_t num, u_int8_t* buf) +{ + u_int32_t netnum; + + memset(buf,0,8); + netnum=htonl(num); + memcpy(buf+4,&netnum,4); + + return(0); +} + +static int tls_check_mac(SslDecoder*decoder, int ct,int ver, u_int8_t* data, + u_int32_t datalen, u_int8_t* mac) +{ + SSL_HMAC hm; + int md; + u_int32_t l; + u_int8_t 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:%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); + ssl_debug_printf("tls_check_mac hmac %p\n",hm); + + 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); + if(memcmp(mac,buf,l)) + return -1; + + SSL_HMAC_CLEANUP(&hm); + return(0); +} + +int ssl3_check_mac(SslDecoder*decoder,int ct,u_int8_t* data, + u_int32_t datalen, u_int8_t* mac) +{ + SSL_MD mc; + int md; + u_int32_t l; + u_int8_t 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; + u_int8_t *mac; + + ssl_debug_printf("ssl_decrypt_record ciphertext len %d\n", inl); + ssl_print_data("Ciphertext",in, inl); + + /* First decrypt*/ + SSL_CIPHER_DECRYPT(&decoder->evp,out,*outl,in,inl); + + 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); +} + + +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; + 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_params = priv_key->params; + + /* 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; + } + ssl_debug_printf("ssl_load_key: got rsa param m size %d (%d)\n", m.size, tmp_size); + + 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; + } + ssl_debug_printf("ssl_load_key: got rsa param e size %d (%d)\n", e.size, tmp_size); + + // 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; + } + ssl_debug_printf("ssl_load_key: got rsa param d size %d (%d)\n", d.size, tmp_size); + + 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; + } + ssl_debug_printf("ssl_load_key: got rsa param q size %d (%d)\n", q.size, tmp_size); + + 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; + } + ssl_debug_printf("ssl_load_key: got rsa param p size %d (%d)\n", q.size, tmp_size); + + 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_debug_printf("ssl_load_key: got rsa param u size %d (%d)\n", u.size, tmp_size); + + 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 + return rsa_priv_key; +#endif +} + +#else +/* no libgnutl: dummy operation to keep interface consistent*/ +SSL_PRIVATE_KEY* ssl_load_key(FILE* fp) +{ + ssl_debug_printf("ssl_load_key: impossible without glutls. fp %p\n",fp); + return NULL; +} +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*/ +SslDecryptSession* ssl_alloc_session(void) +{ + SslDecryptSession*ssl_session = g_malloc0(sizeof(SslDecryptSession)); + if (!ssl_session) + return NULL; + + ssl_debug_printf("ssl_alloc_session 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; + return ssl_session; +} + +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(char* fmt, ...) +{ + va_list ap; + int ret=0; + va_start(ap, fmt); + ret += vfprintf(stderr, fmt, ap); + va_end(ap); +} + + +void ssl_print_text_data(const char* name, const unsigned char* data, int len) +{ + int i; + fprintf(stderr,"%s: ",name); + for (i=0; i< len; i++) { + fprintf(stderr,"%c",data[i]); + } + fprintf(stderr,"\n"); +} + +void ssl_print_data(const char* name, const unsigned char* data, int len) +{ + int i; + fprintf(stderr,"%s[%d]:\n",name, len); + for (i=0; i< len; i++) { + if ((i>0) && (i%16 == 0)) + fprintf(stderr,"\n"); + fprintf(stderr,"%.2x ",data[i]&255); + } + fprintf(stderr,"\n"); +} + +void ssl_print_string(const char* name, const StringInfo* data) +{ + ssl_print_data(name, data->data, data->data_len); +} +#endif diff -uNr ethereal-0.10.13/epan/dissectors/packet-ssl-utils.h ethereal-0.10.13-patch/epan/dissectors/packet-ssl-utils.h --- ethereal-0.10.13/epan/dissectors/packet-ssl-utils.h 1970-01-01 01:00:00.000000000 +0100 +++ ethereal-0.10.13-patch/epan/dissectors/packet-ssl-utils.h 2005-12-07 09:51:53.000000000 +0100 @@ -0,0 +1,145 @@ +#ifndef __SSL_UTILS_H_ +#define __SSL_UTILS_H_ + +#ifdef HAVE_LIBGNUTLS +#include <stdio.h> +#include <gcrypt.h> +#include <gnutls/x509.h> +#include <gnutls/openssl.h> + +#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 +#define SSL_LIB_INIT gnutls_global_init +#else +#define SSL_CIPHER_CTX void* +#define SSL_PRIVATE_KEY void +#define SSL_LIB_INIT() +#endif + +#include <netinet/in.h> + +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 decrypted_data; + StringInfo mac_key; + SSL_CIPHER_CTX evp; + u_int32_t 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; + u_int32_t version; + u_int16_t version_netorder; +} SslDecryptSession; + +SslDecryptSession* ssl_alloc_session(void); +int ssl_data_alloc(StringInfo* str, unsigned int len); +int ssl_data_set(StringInfo* data, unsigned char* src, unsigned int len); +int ssl_data_init(StringInfo* data, unsigned char* src, unsigned int len); + + +SSL_PRIVATE_KEY* ssl_load_key(FILE* fp); + +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(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(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 -uNr ethereal-0.10.13/Makefile.am ethereal-0.10.13-patch/Makefile.am --- ethereal-0.10.13/Makefile.am 2005-10-10 15:23:13.000000000 +0200 +++ ethereal-0.10.13-patch/Makefile.am 2005-12-07 09:51:53.000000000 +0100 @@ -281,7 +281,8 @@ @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. @@ -303,7 +304,8 @@ $(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 @@ -419,7 +421,8 @@ $(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
- Follow-Ups:
- [Ethereal-dev] extrernal plugin and patch for ssl decryption
- From: Paolo Abeni
- [Ethereal-dev] extrernal plugin and patch for ssl decryption
- References:
- Re: [Ethereal-dev] SSL decryption patch for ethereal 0.10.13
- From: Joerg Mayer
- Re: [Ethereal-dev] SSL decryption patch for ethereal 0.10.13
- From: Paolo Abeni
- Re: [Ethereal-dev] SSL decryption patch for ethereal 0.10.13
- From: Joerg Mayer
- Re: [Ethereal-dev] SSL decryption patch for ethereal 0.10.13
- Prev by Date: [Ethereal-dev] Re: [Ethereal-cvs] rev 16708: /trunk/epan/: oid_resolv.c
- Next by Date: [Ethereal-dev] Patch for packet-ospf.c
- Previous by thread: Re: [Ethereal-dev] SSL decryption patch for ethereal 0.10.13
- Next by thread: [Ethereal-dev] extrernal plugin and patch for ssl decryption
- Index(es):