Index: epan/dissectors/packet-ssl-utils.c =================================================================== --- epan/dissectors/packet-ssl-utils.c (revision 27128) +++ epan/dissectors/packet-ssl-utils.c (working copy) @@ -979,6 +979,7 @@ {47,KEX_RSA,SIG_RSA,ENC_AES,16,128,128,DIG_SHA,20,0, SSL_CIPHER_MODE_CBC}, {51,KEX_DH, 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}, + {57,KEX_DH,SIG_RSA,ENC_AES256,16,256,256,DIG_SHA,20,0, SSL_CIPHER_MODE_CBC}, /* for tor */ {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}, @@ -1314,17 +1315,28 @@ ssl_debug_printf("ssl_generate_keyring_material not enough data to generate key " "(0x%02X required 0x%02X or 0x%02X)\n", ssl_session->state, need_all|SSL_MASTER_SECRET, need_all|SSL_PRE_MASTER_SECRET); + ssl_debug_printf("need_all: 0x%02X\n",need_all); + ssl_debug_printf("need_any: 0x%02X\n",need_any); + ssl_debug_printf("SSL_MASTER_SECRET: 0x%02X\n",SSL_MASTER_SECRET); + ssl_debug_printf("SSL_PRE_MASTER_SECRET: 0x%02X\n",SSL_PRE_MASTER_SECRET); + ssl_debug_printf("SSL_VERSION: 0x%02X\n",SSL_VERSION); + ssl_debug_printf("SSL_HAVE_SESSION_KEY: 0x%02X\n",SSL_HAVE_SESSION_KEY); + ssl_debug_printf("SSL_CIPHER: 0x%02X\n",SSL_CIPHER); + ssl_debug_printf("SSL_SERVER_RANDOM: 0x%02X\n",SSL_SERVER_RANDOM); + ssl_debug_printf("SSL_CLIENT_RANDOM: 0x%02X\n",SSL_CLIENT_RANDOM); return -1; } /* 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; + if (!ssl_session->master_key_session) { + 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); @@ -1836,26 +1848,27 @@ worklen, ssl->version_netorder, ct, decoder->seq); if(ssl->version_netorder==SSLV3_VERSION){ if(ssl3_check_mac(decoder,ct,out_str->data,worklen,mac) < 0) { - ssl_debug_printf("ssl_decrypt_record: mac failed\n"); + ssl_debug_printf("sslv3_decrypt_record: mac failed\n"); return -1; } + ssl_debug_printf("sslv3_decrypt_record: mac ok\n"); } else if(ssl->version_netorder==TLSV1_VERSION || ssl->version_netorder==TLSV1DOT1_VERSION){ if(tls_check_mac(decoder,ct,ssl->version_netorder,out_str->data,worklen,mac)< 0) { - ssl_debug_printf("ssl_decrypt_record: mac failed\n"); + ssl_debug_printf("tlsv1_decrypt_record: mac failed\n"); return -1; } + ssl_debug_printf("tlsv1_decrypt_record: mac ok\n"); } else if(ssl->version_netorder==DTLSV1DOT0_VERSION || ssl->version_netorder==DTLSV1DOT0_VERSION_NOT){ /* following the openssl dtls errors the right test is: if(dtls_check_mac(decoder,ct,ssl->version_netorder,out_str->data,worklen,mac)< 0) { */ if(tls_check_mac(decoder,ct,TLSV1_VERSION,out_str->data,worklen,mac)< 0) { - ssl_debug_printf("ssl_decrypt_record: mac failed\n"); + ssl_debug_printf("dtls_decrypt_record: mac failed\n"); return -1; } } - ssl_debug_printf("ssl_decrypt_record: mac ok\n"); *outl = worklen; if (decoder->compression > 0) { @@ -1978,6 +1991,28 @@ } Ssl_private_key_t * +ssl_load_master_key(gchar *master_key) +{ + GByteArray *bytes = NULL; + gboolean res; + Ssl_private_key_t *private_key = g_malloc(sizeof(Ssl_private_key_t)); + private_key->x509_cert = 0; + private_key->x509_pkey = 0; + private_key->sexp_pkey = 0; + private_key->_master_secret = 0; + private_key->_second_master_secret = 0; + bytes = g_byte_array_new(); + res = hex_str_to_bytes(master_key, bytes,FALSE); + + if (res && bytes->len == 48) { + private_key->_master_secret=g_malloc(bytes->len); + memcpy(private_key->_master_secret,bytes->data,bytes->len); + } + g_byte_array_free(bytes, TRUE); + return private_key; +} + +Ssl_private_key_t * ssl_load_key(FILE* fp) { /* gnutls make our work much harded, since we have to work internally with @@ -2325,10 +2360,14 @@ (void *)ssl_session, (gulong)sizeof(SslDecryptSession)); ssl_session->master_secret.data = ssl_session->_master_secret; + ssl_session->primary_master_secret.data = ssl_session->_primary_master_secret; + ssl_session->secondary_master_secret.data = ssl_session->_secondary_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; + ssl_session->primary_master_secret.data_len = 48; + ssl_session->secondary_master_secret.data_len = 48; ssl_session->server_data_for_iv.data_len = 0; ssl_session->server_data_for_iv.data = ssl_session->_server_data_for_iv; ssl_session->client_data_for_iv.data_len = 0; @@ -2722,36 +2761,52 @@ ssl_debug_printf("ssl_init addr '%hhu.%hhu.%hhu.%hhu' port '%d' filename '%s' password(only for p12 file) '%s'\n", ip[0], ip[1], ip[2], ip[3], service->port, filename, cert_passwd); - /* try to load pen or p12 file*/ - fp = ws_fopen(filename, "rb"); - if (!fp) { - fprintf(stderr, "can't open file %s \n",filename); - continue; - } + if (strlen(filename)==96) { + ssl_debug_printf("using master secret"); + private_key = ssl_load_master_key(filename); + /* !!! */ + if (!private_key) { + fprintf(stderr,"can't load private key from %s\n", + filename); + continue; + } + }else{ + /* try to load pen or p12 file*/ + fp = ws_fopen(filename, "rb"); + if (!fp) { + fprintf(stderr, "can't open file %s \n",filename); + continue; + } - if (!cert_passwd) { - private_key = ssl_load_key(fp); + if (!cert_passwd) { + private_key = ssl_load_key(fp); + } + else + { + private_key = ssl_load_pkcs12(fp,cert_passwd); + } + /* !!! */ + if (!private_key) { + fprintf(stderr,"can't load private key from %s\n", + filename); + continue; + } + + fclose(fp); } - else - { - private_key = ssl_load_pkcs12(fp,cert_passwd); - } - /* !!! */ - if (!private_key) { - fprintf(stderr,"can't load private key from %s\n", - filename); - continue; - } - - fclose(fp); - ssl_debug_printf("ssl_init private key file %s successfully loaded\n",filename); /* if item exists, remove first */ tmp_private_key = g_hash_table_lookup(key_hash, service); - if (tmp_private_key) { - g_hash_table_remove(key_hash, service); - ssl_free_key(tmp_private_key); + if ((tmp_private_key) && (strlen(filename)==96)) { + /* if we are loading master secrets and already have this item, then + this must be a renegotiate master secret */ + tmp_private_key->_second_master_secret=g_malloc(48); + memcpy(tmp_private_key->_second_master_secret,private_key->_master_secret,48); + continue; + }else if (tmp_private_key){ + g_hash_table_remove(key_hash, service); + ssl_free_key(tmp_private_key); } g_hash_table_insert(key_hash, service, private_key); Index: epan/dissectors/packet-ssl-utils.h =================================================================== --- epan/dissectors/packet-ssl-utils.h (revision 27128) +++ epan/dissectors/packet-ssl-utils.h (working copy) @@ -279,6 +279,8 @@ typedef struct _SslDecryptSession { guchar _master_secret[48]; + guchar _primary_master_secret[48]; + guchar _secondary_master_secret[48]; guchar _session_id[256]; guchar _client_random[32]; guchar _server_random[32]; @@ -286,6 +288,8 @@ StringInfo server_random; StringInfo client_random; StringInfo master_secret; + StringInfo primary_master_secret; + StringInfo secondary_master_secret; /* the data store for this StringInfo must be allocated explicitly with a capture lifetime scope */ StringInfo pre_master_secret; guchar _server_data_for_iv[24]; @@ -306,6 +310,8 @@ guint16 version_netorder; StringInfo app_data_segment; + guint16 master_key_session; + } SslDecryptSession; typedef struct _SslAssociation { @@ -326,7 +332,9 @@ gnutls_x509_crt_t x509_cert; gnutls_x509_privkey_t x509_pkey; #endif - SSL_PRIVATE_KEY *sexp_pkey; + SSL_PRIVATE_KEY *sexp_pkey; + gchar *_master_secret; + gchar *_second_master_secret; } Ssl_private_key_t; /** Initialize decryption engine/ssl layer. To be called once per execution */ Index: epan/dissectors/Makefile.common =================================================================== --- epan/dissectors/Makefile.common (revision 27128) +++ epan/dissectors/Makefile.common (working copy) @@ -797,6 +797,7 @@ packet-tivoconnect.c \ packet-tnef.c \ packet-tns.c \ + packet-tor.c \ packet-tpkt.c \ packet-tpncp.c \ packet-tr.c \ Index: epan/dissectors/packet-ssl.c =================================================================== --- epan/dissectors/packet-ssl.c (revision 27128) +++ epan/dissectors/packet-ssl.c (working copy) @@ -267,6 +267,7 @@ static gint ssl_decrypted_data_avail = 0; static gchar* ssl_keys_list = NULL; +static gchar* ssl_master_keys_list = NULL; #if defined(SSL_DECRYPT_DEBUG) || defined(HAVE_LIBGNUTLS) static gchar* ssl_debug_file_name = NULL; @@ -303,18 +304,45 @@ ssl_debug_flush(); } +static void +ssl_parse_keys_file(gchar* ssl_tmp_keys_list) +{ + FILE *ssl_keys_file; + struct stat statb; + size_t size; + gchar *tmp_buf; + size_t nbytes; + gboolean read_failed; + + if (file_exists(ssl_tmp_keys_list)) { + if ((ssl_keys_file = ws_fopen(ssl_tmp_keys_list, "r"))) { + read_failed = FALSE; + fstat(fileno(ssl_keys_file), &statb); + size = statb.st_size; + tmp_buf = ep_alloc0(size + 1); + nbytes = fread(tmp_buf, 1, size, ssl_keys_file); + if (ferror(ssl_keys_file)) { + report_read_failure(ssl_tmp_keys_list, errno); + read_failed = TRUE; + } + fclose(ssl_keys_file); + tmp_buf[nbytes] = '\0'; + if (!read_failed) + ssl_parse_key_list(tmp_buf,ssl_key_hash,ssl_associations,ssl_handle,TRUE); + } else { + report_open_failure(ssl_tmp_keys_list, errno, FALSE); + } + } else { + ssl_parse_key_list(ssl_tmp_keys_list,ssl_key_hash,ssl_associations,ssl_handle,TRUE); + } + +} /* parse ssl related preferences (private keys and ports association strings) */ static void ssl_parse(void) { ep_stack_t tmp_stack; SslAssociation *tmp_assoc; - FILE *ssl_keys_file; - struct stat statb; - size_t size; - gchar *tmp_buf; - size_t nbytes; - gboolean read_failed; ssl_set_debug(ssl_debug_file_name); @@ -336,28 +364,12 @@ if (ssl_keys_list && (ssl_keys_list[0] != 0)) { - if (file_exists(ssl_keys_list)) { - if ((ssl_keys_file = ws_fopen(ssl_keys_list, "r"))) { - read_failed = FALSE; - fstat(fileno(ssl_keys_file), &statb); - size = statb.st_size; - tmp_buf = ep_alloc0(size + 1); - nbytes = fread(tmp_buf, 1, size, ssl_keys_file); - if (ferror(ssl_keys_file)) { - report_read_failure(ssl_keys_list, errno); - read_failed = TRUE; - } - fclose(ssl_keys_file); - tmp_buf[nbytes] = '\0'; - if (!read_failed) - ssl_parse_key_list(tmp_buf,ssl_key_hash,ssl_associations,ssl_handle,TRUE); - } else { - report_open_failure(ssl_keys_list, errno, FALSE); - } - } else { - ssl_parse_key_list(ssl_keys_list,ssl_key_hash,ssl_associations,ssl_handle,TRUE); - } + ssl_parse_keys_file(ssl_keys_list); } + if (ssl_master_keys_list && (ssl_master_keys_list[0] != 0)) + { + ssl_parse_keys_file(ssl_master_keys_list); + } ssl_debug_flush(); } @@ -377,7 +389,8 @@ guint *conv_version, gboolean *need_desegmentation, SslDecryptSession *conv_data, - gboolean first_record_in_frame); + gboolean first_record_in_frame, + Ssl_private_key_t * private_key); /* change cipher spec dissector */ static void dissect_ssl3_change_cipher_spec(tvbuff_t *tvb, @@ -507,7 +520,7 @@ gboolean need_desegmentation; SslDecryptSession* ssl_session; guint* conv_version; - Ssl_private_key_t * private_key; + Ssl_private_key_t * private_key=NULL; guint32 port; ti = NULL; @@ -570,7 +583,7 @@ /* try to retrieve 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 + * Note that without HAVE_LIBGNUTLS undefined private_key is allways 0 * and thus decryption never engaged*/ @@ -598,7 +611,15 @@ if (!private_key) { ssl_debug_printf("dissect_ssl can't find any private key!\n"); } else { - ssl_session->private_key = private_key->sexp_pkey; + if (private_key->_master_secret) { + ssl_debug_printf("dissect_ssl found master_secret!\n"); + ssl_data_set(&ssl_session->primary_master_secret, private_key->_master_secret, 48); + if (private_key->_second_master_secret) + ssl_data_set(&ssl_session->secondary_master_secret, private_key->_second_master_secret, 48); + ssl_session->master_key_session=1; + }else{ + ssl_session->private_key = private_key->sexp_pkey; + } } } @@ -694,7 +715,8 @@ offset, conv_version, &need_desegmentation, ssl_session, - first_record_in_frame); + first_record_in_frame, + private_key); } break; @@ -717,7 +739,8 @@ offset, conv_version, &need_desegmentation, ssl_session, - first_record_in_frame); + first_record_in_frame, + private_key); } else { @@ -1290,7 +1313,8 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, guint *conv_version, gboolean *need_desegmentation, - SslDecryptSession* ssl, gboolean first_record_in_frame _U_) + SslDecryptSession* ssl, gboolean first_record_in_frame _U_, + Ssl_private_key_t * private_key) { /* @@ -1522,7 +1546,7 @@ case SSL_ID_HANDSHAKE: { tvbuff_t* decrypted; - + if (private_key){}; /* try to decrypt handshake record, if possible. Store decrypted * record for later usage. The offset is used as 'key' to identify * this record in the packet (we can have multiple handshake records @@ -1532,14 +1556,26 @@ ssl_add_record_info(proto_ssl, pinfo, ssl_decrypted_data.data, ssl_decrypted_data_avail, offset); + ssl_debug_printf("dissect_ssl3_record: decrypting handshake\n"); /* try to retrieve and use decrypted handshake record, if any. */ decrypted = ssl_get_record_info(proto_ssl, pinfo, offset); if (decrypted) { /* add desegmented data to the data source list */ add_new_data_source(pinfo, decrypted, "Decrypted SSL record"); + /* if we are using user-supplied master_keys we need to switch + to the renegotiated master-key now. */ + ssl_debug_printf("dissect_ssl3_record: should i change master secret?\n"); + if (ssl && &ssl->master_key_session && &ssl->secondary_master_secret) { + ssl_debug_printf("dissect_ssl3_record: using secondary master_secret!\n"); + ssl->master_secret = ssl->secondary_master_secret; + } dissect_ssl3_handshake(decrypted, pinfo, ssl_record_tree, 0, decrypted->length, conv_version, ssl, content_type); } else { + if (ssl && &ssl->master_key_session) { + ssl_debug_printf("dissect_ssl3_record: using primary master_secret!\n"); + ssl->master_secret = ssl->primary_master_secret; + } dissect_ssl3_handshake(tvb, pinfo, ssl_record_tree, offset, record_length, conv_version, ssl, content_type); } @@ -1867,6 +1903,7 @@ if (!ssl) break; + ssl_debug_printf("dissect_ssl3_handshake SSL_HND_CLIENT_KEY_EXCHG\n"); /* get encrypted data, on tls1 we have to skip two bytes * (it's the encrypted len and should be equal to record len - 2) @@ -1886,18 +1923,23 @@ encrypted_pre_master.data_len = encrlen; tvb_memcpy(tvb, encrypted_pre_master.data, offset+skip, encrlen); - if (!ssl->private_key) { + if (!ssl->private_key && !ssl->master_key_session) { ssl_debug_printf("dissect_ssl3_handshake can't find private key\n"); break; } /* go with ssl key processessing; encrypted_pre_master * will be used for master secret store*/ - ret = ssl_decrypt_pre_master_secret(ssl, &encrypted_pre_master, ssl->private_key); - if (ret < 0) { - ssl_debug_printf("dissect_ssl3_handshake can't decrypt pre master secret\n"); - break; + if (!ssl->master_key_session) { + ret = ssl_decrypt_pre_master_secret(ssl, &encrypted_pre_master, ssl->private_key); + if (ret < 0) { + ssl_debug_printf("dissect_ssl3_handshake can't decrypt pre master secret\n"); + break; + } + }else{ + ssl->state |= SSL_MASTER_SECRET; } + if (ssl_generate_keyring_material(ssl)<0) { ssl_debug_printf("dissect_ssl3_handshake can't generate keyring material\n"); break; @@ -1956,7 +1998,7 @@ 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) && + if (session_id_length && from_server && (session_id_length == ssl->session_id.data_len) && (tvb_memeql(tvb, offset+33, ssl->session_id.data, session_id_length) == 0)) { /* client/server id match: try to restore a previous cached session*/ @@ -4352,6 +4394,11 @@ " is the local file name of the RSA private key used by the specified server " "(or name of the file containing such a list)", (const gchar **)&ssl_keys_list); + prefs_register_string_preference(ssl_module, "master_keys_list", "SSL master keys list", + "Semicolon-separated list of private SSL/TLS master keys used for SSL decryption; " + "each list entry must be in the form of ,,,. " + " is the master key used during the TLS/SSL session", + (const gchar **)&ssl_master_keys_list); prefs_register_string_preference(ssl_module, "debug_file", "SSL debug file", "Redirect SSL debug to file name; leave empty to disable debugging, " "or use \"" SSL_DEBUG_USE_STDERR "\" to redirect output to stderr\n", Index: epan/dissectors/packet-tor.c =================================================================== --- epan/dissectors/packet-tor.c (revision 0) +++ epan/dissectors/packet-tor.c (revision 0) @@ -0,0 +1,1302 @@ +/* packet-tor.c + * Routines for tor.packet dissection + * https://svn.torproject.org/svn/tor/trunk/doc/spec/tor-spec.txt + * Copyright 2009, Robert Hogan + * + * $Id: packet-tor.c 26356 2008-10-05 23:08:54Z wmeier $ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Copied from packet-pop.c and packet-ssl.c + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "packet-ssl.h" +#include "packet-ssl-utils.h" + +#define REVERSE 0 +#define FORWARD 1 + +#define PADDING 0x00 +#define CREATE 0x01 +#define CREATED 0x02 +#define RELAY 0x03 +#define DESTROY 0x04 +#define CREATE_FAST 0x05 +#define CREATED_FAST 0x06 +#define VERSIONS 0x07 +#define NETINFO 0x08 +#define RELAY_EARLY 0x09 + +static const value_string vals_circuit_commands[] = { + { PADDING , "(Padding)"}, + { CREATE , "(Create a circuit)"}, + { CREATED , "(Acknowledge create)"}, + { RELAY , "(End-to-end data)"}, + { DESTROY , "(Stop using a circuit)"}, + { CREATE_FAST , "(Create a circuit, no PK)"}, + { CREATED_FAST, "(Circuit created, no PK)"}, + { VERSIONS , "(Negotiate proto version)"}, + { NETINFO , "(Time and address info)"}, + { RELAY_EARLY , "(End-to-end data (Relay Early).)"}, + { 0x00, NULL } +}; + +#define NONE 0x00 +#define PROTOCOL 0x01 +#define INTERNAL 0x02 +#define REQUESTED 0x03 +#define HIBERNATING 0x04 +#define RESOURCELIMIT 0x05 +#define CONNECTFAILED 0x06 +#define OR_IDENTITY 0x07 +#define OR_CONN_CLOSED 0x08 +#define FINISHED 0x09 +#define TIMEOUT 0x0A +#define DESTROYED 0x0B +#define NOSUCHSERVICE 0x0C + +static const value_string vals_destroy_reason[] = { + { NONE , "(No reason given.)" }, + { PROTOCOL , "(Tor protocol violation.)" }, + { INTERNAL , "(Internal error.)" }, + { REQUESTED , "(A client sent a TRUNCATE command.)" }, + { HIBERNATING , "(Not currently operating; trying to save bandwidth.)" }, + { RESOURCELIMIT , "(Out of memory, sockets, or circuit IDs.)" }, + { CONNECTFAILED , "(Unable to reach server.)" }, + { OR_IDENTITY , "(Connected to server, but its OR identity was not as expected.)" }, + { OR_CONN_CLOSED, "(The OR connection that was carrying this circuit died.)" }, + { FINISHED , "(The circuit has expired for being dirty or old.)" }, + { TIMEOUT , "(Circuit construction took too long)" }, + { DESTROYED , "(The circuit was destroyed w/o client TRUNCATE)" }, + { NOSUCHSERVICE , "(Request for unknown hidden service)"}, + { 0x00, NULL } +}; + +#define RELAY_BEGIN 0x01 +#define RELAY_DATA 0x02 +#define RELAY_END 0x03 +#define RELAY_CONNECTED 0x04 +#define RELAY_SENDME 0x05 +#define RELAY_EXTEND 0x06 +#define RELAY_EXTENDED 0x07 +#define RELAY_TRUNCATE 0x08 +#define RELAY_TRUNCATED 0x09 +#define RELAY_DROP 0x0A +#define RELAY_RESOLVE 0x0B +#define RELAY_RESOLVED 0x0C +#define RELAY_BEGIN_DIR 0x0D + +static const value_string vals_relay_commands[] = { + { RELAY_BEGIN , "RELAY_BEGIN "}, + { RELAY_DATA , "RELAY_DATA "}, + { RELAY_END , "RELAY_END "}, + { RELAY_CONNECTED, "RELAY_CONNECTED"}, + { RELAY_SENDME , "RELAY_SENDME "}, + { RELAY_EXTEND , "RELAY_EXTEND "}, + { RELAY_EXTENDED , "RELAY_EXTENDED "}, + { RELAY_TRUNCATE , "RELAY_TRUNCATE "}, + { RELAY_TRUNCATED, "RELAY_TRUNCATED"}, + { RELAY_DROP , "RELAY_DROP "}, + { RELAY_RESOLVE , "RELAY_RESOLVE "}, + { RELAY_RESOLVED , "RELAY_RESOLVED "}, + { RELAY_BEGIN_DIR, "RELAY_BEGIN_DIR"}, + { 0x00, NULL } +}; + +#define HOSTNAME 0x00 +#define IPV4_ADDRESS 0x04 +#define IPV6_ADDRESS 0x06 +#define ERROR_TRANSIENT 0xF0 +#define ERROR_NONTRANSIENT 0xF1 + +static const value_string vals_address_types[] = { + { HOSTNAME , "HOSTNAME "}, + { IPV4_ADDRESS , "IPV4_ADDRESS "}, + { IPV6_ADDRESS , "IPV6_ADDRESS "}, + { ERROR_TRANSIENT , "ERROR_TRANSIENT "}, + { ERROR_NONTRANSIENT, "ERROR_NONTRANSIENT"}, + { 0x00, NULL } +}; + +#define CELL_LEN 512 +#define PAYLOAD_LEN 509 +#define HASH_LEN 20 + +#define TCP_PORT_SSL_TOR 9001 + +static int proto_tor = -1; + +static int hf_tor_circid = -1; +static int hf_tor_command = -1; +static int hf_tor_payload = -1; +static int hf_tor_length = -1; +static int hf_tor_timestamp = -1; +static int hf_tor_other_or_address = -1; +static int hf_tor_number_of_addresses = -1; +static int hf_tor_this_or_address = -1; +static int hf_tor_address_type = -1; +static int hf_tor_address_length = -1; +static int hf_tor_hostname = -1; +static int hf_tor_ttl = -1; +static int hf_tor_key_material_y = -1; +static int hf_tor_key_material_x = -1; +static int hf_tor_dh_data = -1; +static int hf_tor_derivative_key_data = -1; +static int hf_tor_create = -1; +static int hf_tor_destroy_reason = -1; +static int hf_tor_relay_command = -1; +static int hf_tor_relay_data = -1; +static int hf_tor_relay_data_length = -1; +static int hf_tor_relay_digest = -1; +static int hf_tor_streamid = -1; +static int hf_tor_recognized = -1; +static int hf_tor_padding = -1; +static int hf_tor_encrypted_cell = -1; + +static int hf_tor_data_fragments = -1; +static int hf_tor_data_fragment = -1; +static int hf_tor_data_fragment_overlap = -1; +static int hf_tor_data_fragment_overlap_conflicts = -1; +static int hf_tor_data_fragment_multiple_tails = -1; +static int hf_tor_data_fragment_too_long_fragment = -1; +static int hf_tor_data_fragment_error = -1; +static int hf_tor_data_reassembled_in = -1; + +static gint ett_tor = -1; + +static gint ett_tor_data_fragment = -1; +static gint ett_tor_data_fragments = -1; + +static dissector_handle_t data_handle; +// static dissector_handle_t imf_handle = NULL; + +static GHashTable *tor_data_segment_table = NULL; +static GHashTable *tor_data_reassembled_table = NULL; + +static gchar* cell_keys_list = NULL; +static GHashTable *tor_key_hash = NULL; +static GHashTable *tor_relay_data_hash = NULL; + +static const fragment_items tor_data_frag_items = { + /* Fragment subtrees */ + &ett_tor_data_fragment, + &ett_tor_data_fragments, + /* Fragment fields */ + &hf_tor_data_fragments, + &hf_tor_data_fragment, + &hf_tor_data_fragment_overlap, + &hf_tor_data_fragment_overlap_conflicts, + &hf_tor_data_fragment_multiple_tails, + &hf_tor_data_fragment_too_long_fragment, + &hf_tor_data_fragment_error, + /* Reassembled in field */ + &hf_tor_data_reassembled_in, + /* Tag */ + "DATA fragments" +}; + +struct tor_proto_data { + guint16 conversation_id; + gboolean more_frags; +}; + +struct tor_data_val { + gboolean msg_request; + guint32 msg_read_len; /* Length of RETR message read so far */ + guint32 msg_tot_len; /* Total length of RETR message */ +}; + +typedef struct _cell_key { + gchar *key; + gint no_of_hops; + gcry_cipher_hd_t cipher; + guchar *ctr; + guint hop; + struct _cell_key *next; +} cell_key_t; + +typedef struct _TorService { + guint16 circid; + guint8 source[4]; + guint direction; +} TorService; + +void proto_reg_handoff_tor(void); + +static void +tor_add_cell_key(cell_key_t *key, cell_key_t *newkey) +{ + cell_key_t *first_key = key; + + newkey->hop++; + while (key->next) { + newkey->hop++; + key = key->next; + } + key->next = newkey; + first_key->no_of_hops = newkey->hop; + +} + +static cell_key_t * +tor_get_cell_key(cell_key_t *rec, guint hop) +{ + while (rec) { + if (rec->hop == hop) return rec; + rec = rec->next; + } + return NULL; +} + +/* store master secret into session data cache */ +void +tor_save_decrypted(StringInfo* data_key, const guint8 *decrypted) +{ + /* allocate stringinfo chunks for session id and master secret data*/ + StringInfo* decrypted_data; + decrypted_data = se_alloc0(PAYLOAD_LEN + sizeof(StringInfo)); + + decrypted_data->data = ((guchar*)decrypted_data+sizeof(StringInfo)); + + /* ssl_hash() depends on data_key->data being aligned for guint access + * so be careful in changing how it is allocated. + */ + memcpy(decrypted_data->data, decrypted, PAYLOAD_LEN); + decrypted_data->data_len = PAYLOAD_LEN; + + g_hash_table_insert(tor_relay_data_hash, data_key, decrypted_data); +} + +static gint +tor_relay_data_equal (gconstpointer v, gconstpointer v2) +{ + const StringInfo *val1; + const StringInfo *val2; + val1 = (const StringInfo *)v; + 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 +tor_relay_data_key_hash (gconstpointer v) +{ + guint l,hash; + const StringInfo* id; + const guint* cur; + hash = 0; + id = (const StringInfo*) v; + + /* id and id->data are mallocated in ssl_save_session(). As such 'data' + * should be aligned for any kind of access (for example as a guint as + * is done below). The intermediate void* cast is to prevent "cast + * increases required alignment of target type" warnings on CPUs (such + * as SPARCs) that do not allow misaligned memory accesses. + */ + cur = (const guint*)(void*) id->data; + + for (l=4; (l < id->data_len); l+=4, cur++) + hash = hash ^ (*cur); + + return hash; +} + +/* private key table entries have a scope 'larger' then packet capture, + * so we can't relay on se_alloc** function */ +static void +tor_relay_data_free(gpointer id, gpointer key, gpointer dummy _U_) +{ + g_free(id); + g_free((guint8 *)key); +} + +static gint +tor_key_equal (gconstpointer v, gconstpointer v2) +{ + const TorService *val1; + const TorService *val2; + val1 = (const TorService *)v; + val2 = (const TorService *)v2; + + if ((val1->circid == val2->circid) && + (val1->direction == val2->direction) && + !memcmp(val1->source, val2->source, 4)) { + return 1; + } + return 0; +} + +static guint +tor_cell_key_hash (gconstpointer v) +{ + const TorService *key; + const guint* cr; + guint hash; + key = (const TorService *)v; + hash = key->circid; + hash = hash ^ key->direction; + cr = (const guint*) key->source; + hash = hash ^ (*cr); + return hash; +} + +/* private key table entries have a scope 'larger' then packet capture, + * so we can't relay on se_alloc** function */ +static void +tor_key_free(gpointer id, gpointer key, gpointer dummy _U_) +{ + g_free(id); + g_free((cell_key_t*)key); +} + +/* stream cipher abstraction layer*/ +static gint +tor_cipher_init(gcry_cipher_hd_t *cipher, guchar* sk) +{ + gint err; + + err = gcry_cipher_open(cipher, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, 0); + if (err !=0) + return -1; + err = gcry_cipher_setkey(*(cipher), sk, gcry_cipher_get_algo_blklen (GCRY_CIPHER_AES128)); + if (err != 0) + return -1; + gcry_cipher_setctr(*(cipher), NULL, 0); + if (err != 0) + return -1; + return 0; +} + +static inline gint +tor_cipher_decrypt(gcry_cipher_hd_t *cipher, guchar * out, gint outl) +{ + return gcry_cipher_encrypt ( *(cipher), out, outl, NULL, 0); +} + +static guint8 * +tor_decrypt_cell(guint8 *encrypted_data, cell_key_t *cell_key) +{ + guint8 *decrypted_data = NULL; + + if (!cell_key->cipher) { + tor_cipher_init(&cell_key->cipher,cell_key->key); + } + + decrypted_data = (guint8 *) g_malloc ((PAYLOAD_LEN)* sizeof(guint8)); + tor_cipher_decrypt(&cell_key->cipher, encrypted_data, PAYLOAD_LEN); + + return decrypted_data; +} + +static cell_key_t * +tor_load_cell_key(gchar *key) +{ + GByteArray *bytes = NULL; + gboolean res; + cell_key_t *cell_key = g_malloc(sizeof(cell_key_t)); + cell_key->key = 0; + cell_key->hop=1; + cell_key->no_of_hops=1; + cell_key->next = NULL; + bytes = g_byte_array_new(); + res = hex_str_to_bytes(key, bytes,FALSE); + + if (res && bytes->len == 16) { + cell_key->key=g_malloc(bytes->len); + memcpy(cell_key->key,bytes->data,bytes->len); + } + g_byte_array_free(bytes, TRUE); + tor_cipher_init(&cell_key->cipher,cell_key->key); + + + return cell_key; +} + +/* parse tor related preferences (cell keys and ports association strings) */ +static void +tor_parse_key_list(const gchar * keys_list, GHashTable *key_hash) +{ + gchar* end; + gchar* start; + gchar* tmp; + TorService* service; + cell_key_t *cell_key=NULL, *tmp_cell_key=NULL; + + start = g_strdup(keys_list); + tmp = start; + ssl_debug_printf("tor_init keys string:\n%s\n", start); + do { + gchar* circid, *direction, *key; + + circid = start; + /* split ip/file couple with ';' separator*/ + end = strpbrk(start, ";\n\r"); + if (end) { + *end = 0; + start = end+1; + } + + /* skip comments (in file) */ + if (circid[0] == '#') continue; + + /* for each entry split ip, direction, key, filename with ',' separator */ + ssl_debug_printf("tor_init found circid entry %s\n", circid); + direction = strchr(circid, ','); + if (!direction || (strncmp(",forward", direction,8) + && (strncmp(",reverse", direction,8)))) + { + ssl_debug_printf("tor_init entry malformed can't find direction in '%s'\n", circid); + continue; + } + *direction = 0; + direction++; + + key = strchr(direction,','); + if (!key) + { + ssl_debug_printf("tor_init entry malformed can't find key in %s\n", direction); + continue; + } + *key=0; + key++; + + /* convert ip and direction string to network rappresentation*/ + service = g_malloc(sizeof(TorService) + 8); + service->circid = atoi(circid); + if(!strcmp("forward", direction)) { + service->direction = FORWARD; + }else{ + service->direction = REVERSE; + } + ssl_debug_printf("tor_init found direction %s\n", direction); + memset(service->source,0,4); + if (strlen(key)==32) { + cell_key = tor_load_cell_key(key); + /* !!! */ + if (!cell_key) { + ssl_debug_printf("can't load cell key from %s\n", key); + continue; + } + } + ssl_debug_printf("tor_init cell key file %s successfully loaded\n",key); + + /* if item exists, remove first */ + tmp_cell_key = g_hash_table_lookup(key_hash, service); + if (tmp_cell_key){ + tor_add_cell_key(tmp_cell_key, cell_key); + } else { + g_hash_table_insert(key_hash, service, cell_key); + } + + } while (end != NULL); + g_free(tmp); +} + +static void +tor_parse_keys_file(gchar* tor_tmp_keys_list) +{ + FILE *tor_keys_file; + struct stat statb; + size_t size; + gchar *tmp_buf; + size_t nbytes; + gboolean read_failed; + + if (file_exists(tor_tmp_keys_list)) { + if ((tor_keys_file = ws_fopen(tor_tmp_keys_list, "r"))) { + read_failed = FALSE; + fstat(fileno(tor_keys_file), &statb); + size = statb.st_size; + tmp_buf = ep_alloc0(size + 1); + nbytes = fread(tmp_buf, 1, size, tor_keys_file); + if (ferror(tor_keys_file)) { + report_read_failure(tor_tmp_keys_list, errno); + read_failed = TRUE; + } + fclose(tor_keys_file); + tmp_buf[nbytes] = '\0'; + if (!read_failed) + tor_parse_key_list(tmp_buf,tor_key_hash); + } else { + report_open_failure(tor_tmp_keys_list, errno, FALSE); + } + } else { + tor_parse_key_list(tor_tmp_keys_list,tor_key_hash); + } + +} + +/* parse tor related preferences (cell keys and ports association strings) */ +static void +tor_parse(void) +{ + if (tor_key_hash) + { + g_hash_table_foreach(tor_key_hash, tor_key_free, NULL); + g_hash_table_destroy(tor_key_hash); + } + + /* parse cell keys string, load available keys and put them in key hash*/ + tor_key_hash = g_hash_table_new(tor_cell_key_hash,tor_key_equal); + + if (tor_relay_data_hash) + { + g_hash_table_foreach(tor_relay_data_hash, tor_relay_data_free, NULL); + g_hash_table_destroy(tor_relay_data_hash); + } + + /* parse cell relay_datas string, load available relay_datas and put them in relay_data hash*/ + tor_relay_data_hash = g_hash_table_new(tor_relay_data_key_hash,tor_relay_data_equal); + + if (cell_keys_list && (cell_keys_list[0] != 0)) + { + tor_parse_keys_file(cell_keys_list); + } +} + +static gint +add_tor_address(proto_tree *payload_tree, tvbuff_t *tvb, gint offset, gboolean other) +{ + gint address_type, address_length, start; + struct e_in6_addr address_ipv6; + + start=offset; + + address_type=tvb_get_guint8(tvb, offset); + proto_tree_add_item(payload_tree, + hf_tor_address_type, + tvb, offset, + 1, + FALSE); + offset+=1; + + address_length=tvb_get_guint8(tvb, offset); + proto_tree_add_item(payload_tree, + hf_tor_address_length, + tvb, offset, + 1, + FALSE); + offset+=1; + + switch(address_type) { + case HOSTNAME: + proto_tree_add_item(payload_tree, + hf_tor_hostname, + tvb, offset, + address_length, + FALSE); + break; + case IPV4_ADDRESS: + proto_tree_add_ipv4(payload_tree, + (other)?hf_tor_other_or_address:hf_tor_this_or_address, + tvb, offset, + address_length, + tvb_get_ipv4(tvb,offset)); + break; + case IPV6_ADDRESS: + tvb_get_ipv6(tvb, offset, &address_ipv6); + proto_tree_add_ipv6(payload_tree, + (other)?hf_tor_other_or_address:hf_tor_this_or_address, + tvb, offset, + 16, (guint8 *)&address_ipv6); + break; + default: + proto_tree_add_item(payload_tree, + hf_tor_hostname, + tvb, offset, + address_length, + FALSE); + } + offset+=address_length; +/* + proto_tree_add_item(payload_tree, + hf_tor_ttl, + tvb, offset, + 4, + FALSE); + offset+=4; +*/ + return (offset-start); +} + +static gint +dissect_relay_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset, guint circid, packet_info *pinfo) +{ + guint8 relay_command; + TorService* service; + cell_key_t *cell_key; + guint8 *decrypted_data = NULL; + guint8 *encrypted_data = NULL; + StringInfo *data_key = NULL; + StringInfo *stored_data = NULL; + tvbuff_t *tvb_decrypted; + gint d_offset=0, i; + gboolean found_payload=FALSE; + + service = g_malloc(sizeof(TorService) + 8); + service->circid = circid; + /*FIXME: get correct direction */ + service->direction = REVERSE; + memcpy(service->source, pinfo->src.data, pinfo->src.len); + + cell_key = g_hash_table_lookup(tor_key_hash, service); + if (!cell_key) { + service->direction = FORWARD; + cell_key = g_hash_table_lookup(tor_key_hash, service); + if ((!cell_key)){ + return 0; + } + } + + + encrypted_data = (guint8 *) g_malloc ((PAYLOAD_LEN) * sizeof(guint8)); + memset(encrypted_data,0,PAYLOAD_LEN); + tvb_memcpy(tvb, encrypted_data , offset, PAYLOAD_LEN); + data_key = se_alloc0(8 + sizeof(StringInfo)); + data_key->data = ((guchar*)data_key+sizeof(StringInfo)); + memcpy(data_key->data, encrypted_data,8); + data_key->data_len = 8; + + if ((stored_data = g_hash_table_lookup(tor_relay_data_hash, data_key)) == NULL) { + /* Allocate Buffers for Encrypted and Decrypted data */ + + for (i=1; i <= cell_key->no_of_hops; i++) { + cell_key_t *ckey; + if ((ckey = tor_get_cell_key(cell_key,i)) == 0) + break; + ssl_print_data("Encrypted Data", encrypted_data, PAYLOAD_LEN); + ssl_print_data("Cell Key", ckey->key, 16); + tor_decrypt_cell(encrypted_data,ckey); + ssl_print_data("Decrypted Data", encrypted_data, PAYLOAD_LEN); + if (!encrypted_data[1] && !encrypted_data[2]) { + found_payload=TRUE; + decrypted_data = encrypted_data; + tor_save_decrypted(data_key, encrypted_data); + break; + } + } + } else { + memcpy(encrypted_data,stored_data->data, stored_data->data_len); + decrypted_data=encrypted_data; + found_payload=TRUE; + } + + if (!found_payload) + return 0; + + ssl_print_data("Found Decrypted Data", decrypted_data, PAYLOAD_LEN); + + tvb_decrypted = tvb_new_real_data(decrypted_data, PAYLOAD_LEN, PAYLOAD_LEN); + tvb_set_child_real_data_tvbuff(tvb, tvb_decrypted); + + add_new_data_source(pinfo, tvb_decrypted, "Decrypted Relay Cell"); + + /* Handler to free the Decrypted Data Buffer. */ + tvb_set_free_cb(tvb_decrypted,g_free); + + /* Dissect the Decrypted Relay Cell */ + relay_command = tvb_get_guint8(tvb_decrypted, d_offset); + proto_tree_add_item(payload_tree, + hf_tor_relay_command, + tvb_decrypted, d_offset, + 1, + FALSE); + d_offset+=1; + + proto_tree_add_item(payload_tree, + hf_tor_recognized, + tvb_decrypted, d_offset, + 2, + FALSE); + d_offset+=2; + + proto_tree_add_item(payload_tree, + hf_tor_streamid, + tvb_decrypted, d_offset, + 2, + FALSE); + d_offset+=2; + + proto_tree_add_item(payload_tree, + hf_tor_relay_digest, + tvb_decrypted, d_offset, + 4, + FALSE); + d_offset+=4; + + proto_tree_add_item(payload_tree, + hf_tor_relay_data_length, + tvb_decrypted, d_offset, + 2, + FALSE); + d_offset+=2; + + proto_tree_add_item(payload_tree, + hf_tor_relay_data, + tvb_decrypted, d_offset, + (CELL_LEN-14), + FALSE); + d_offset+=(CELL_LEN-14); + + + return PAYLOAD_LEN; +} + +static gint +dissect_destroy_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint start=offset; + + proto_tree_add_item(payload_tree, + hf_tor_destroy_reason, + tvb, offset, + 1, + FALSE); + offset+=1; + + return (offset-start); +} + +static void +update_cell_directions(guint circid, packet_info *pinfo, guint direction) +{ + TorService* service; + cell_key_t *tmp_cell_key; + service = g_malloc(sizeof(TorService) + 8); + service->circid = circid; + service->direction = direction; + memset(service->source,0,4); + tmp_cell_key = g_hash_table_lookup(tor_key_hash, service); + if (tmp_cell_key) { + g_hash_table_remove(tor_key_hash, service); + memcpy(service->source, pinfo->src.data, pinfo->src.len); + g_hash_table_insert(tor_key_hash, service, tmp_cell_key); + } +} + +static gint +dissect_create_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint start=offset; + proto_tree_add_item(payload_tree, + hf_tor_create, + tvb, offset, + 509, + FALSE); + offset+=509; + + return (offset-start); +} + +static gint +dissect_createfast_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint start=offset; + proto_tree_add_item(payload_tree, + hf_tor_key_material_x, + tvb, offset, + HASH_LEN, + FALSE); + offset+=HASH_LEN; + + return (offset-start); +} + +static gint +dissect_createdfast_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint start=offset; + proto_tree_add_item(payload_tree, + hf_tor_key_material_y, + tvb, offset, + HASH_LEN, + FALSE); + offset+=HASH_LEN; + + proto_tree_add_item(payload_tree, + hf_tor_derivative_key_data, + tvb, offset, + HASH_LEN, + FALSE); + offset+=HASH_LEN; + + return (offset-start); +} + +static gint +dissect_created_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint start=offset; + proto_tree_add_item(payload_tree, + hf_tor_dh_data, + tvb, offset, + HASH_LEN, + FALSE); + offset+=HASH_LEN; + + proto_tree_add_item(payload_tree, + hf_tor_derivative_key_data, + tvb, offset, + HASH_LEN, + FALSE); + offset+=HASH_LEN; + + return (offset-start); +} + +static gint +dissect_netinfo_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset) +{ + gint i, no_of_addresses; + gint start=offset; + proto_tree_add_item(payload_tree, + hf_tor_timestamp, + tvb, offset, + 4, + FALSE); + offset+=4; + + offset+=add_tor_address(payload_tree, tvb, offset,TRUE); + + proto_tree_add_item(payload_tree, + hf_tor_number_of_addresses, + tvb, offset, + 1, + FALSE); + no_of_addresses=tvb_get_guint8(tvb, offset); + offset+=1; + + for (i=0; i < no_of_addresses; i++) { + offset+=add_tor_address(payload_tree, tvb, offset,FALSE); + } + return (offset-start); +} + +static gint +dissect_versions_cell(proto_tree *payload_tree, tvbuff_t *tvb, gint offset, + gint tor_length) +{ + gint i; + proto_tree_add_string_format(payload_tree, + hf_tor_length, + tvb, offset, + 2, + "Length:", "Length: %s", + tvb_bytes_to_str(tvb, offset, 2)); + offset+=2; + for (i=0; icinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "Tor"); + + /* + * Find the end of the first line. + * + * Note that "tvb_find_line_end()" will return a value that is + * not longer than what's in the buffer, so the "tvb_get_ptr()" + * call won't throw an exception. + */ + linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE); + line = tvb_get_ptr(tvb, offset, linelen); + + frame_data = p_get_proto_data(pinfo->fd, proto_tor); + + if (!frame_data) { + + conversation = find_conversation(pinfo->fd->num, + &pinfo->src, &pinfo->dst, + pinfo->ptype, + pinfo->srcport, pinfo->destport, 0); + + if (conversation == NULL) { /* No conversation, create one */ + conversation = conversation_new(pinfo->fd->num, + &pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, 0); + } + + data_val = conversation_get_proto_data(conversation, proto_tor); + + if (!data_val) { + + /* + * No - create one and attach it. + */ + data_val = se_alloc(sizeof(struct tor_data_val)); + data_val->msg_request = FALSE; + data_val->msg_read_len = 0; + data_val->msg_tot_len = 0; + + conversation_add_proto_data(conversation, proto_tor, data_val); + } + } + + + if (tree) { + + ti = proto_tree_add_item(tree, proto_tor, tvb, offset, -1, + FALSE); + tor_tree = proto_item_add_subtree(ti, ett_tor); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + while (tvb_offset_exists(tvb, offset)) { + + /*Inspect the packet. if we don't have enough data to dissect it, + request more. */ + tor_circid=(guint)tvb_get_ntohs(tvb, offset); + tor_cmd = tvb_get_guint8(tvb, offset+2); + switch(tor_cmd) { + case NETINFO: + /* need to calculate length base on no of addresses here */ + required=CELL_LEN; + break; + case VERSIONS: + tor_length=(guint)tvb_get_ntohs(tvb, offset+3); + required=tor_length + 4; + break; + default: + required=CELL_LEN; + } + + available = tvb_reported_length_remaining(tvb, offset); + if (available < required) { + /* we ran out of data: ask for more */ + pinfo->desegment_offset = offset; + pinfo->desegment_len = (required-available); + return; + } + + /* + * Put the line into the protocol tree. + */ + + val_str = match_strval(tor_cmd, vals_circuit_commands); + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Tor Cell: %s", val_str); + } + + if (!tor_cmd) { + ti=proto_tree_add_item(tor_tree, + hf_tor_padding, + tvb, offset, + (tvb_length_remaining(tvb,offset) > CELL_LEN)? + CELL_LEN:tvb_length_remaining(tvb,offset), + FALSE); + offset+=(tvb_length_remaining(tvb,offset) > CELL_LEN)? + CELL_LEN:tvb_length_remaining(tvb,offset); + continue; + } + + /* + * Add the CircID field + */ + ti=proto_tree_add_item(tor_tree, + hf_tor_circid, + tvb, offset, + 2, + FALSE); + offset+=2; + + /* + * Add the Command field + */ + proto_tree_add_item(tor_tree, + hf_tor_command, + tvb, offset, + 1, + FALSE); + offset+=1; + /* + * Add the Payload + */ + ti = proto_tree_add_item(tor_tree, + hf_tor_payload, + tvb, offset, + 0, + FALSE); + payload_tree = proto_item_add_subtree(ti, ett_tor); + + switch(tor_cmd) { + case VERSIONS: + offset+=dissect_versions_cell(payload_tree, tvb, offset, tor_length); + break; + case NETINFO: + offset+=dissect_netinfo_cell(payload_tree, tvb, offset); + break; + case CREATED: + update_cell_directions(tor_circid, pinfo, REVERSE); + offset+=dissect_created_cell(payload_tree, tvb, offset); + break; + case CREATED_FAST: + update_cell_directions(tor_circid, pinfo, REVERSE); + offset+=dissect_createdfast_cell(payload_tree, tvb, offset); + break; + case CREATE_FAST: + update_cell_directions(tor_circid, pinfo, FORWARD); + offset+=dissect_createfast_cell(payload_tree, tvb, offset); + break; + case CREATE: + update_cell_directions(tor_circid, pinfo, FORWARD); + offset+=dissect_create_cell(payload_tree, tvb, offset); + break; + case DESTROY: + offset+=dissect_destroy_cell(payload_tree, tvb, offset); + break; + case RELAY_EARLY: + case RELAY: + if (!dissect_relay_cell(payload_tree, tvb, offset, tor_circid, pinfo)) { + proto_tree_add_item(payload_tree, + hf_tor_encrypted_cell, + tvb, offset, + PAYLOAD_LEN, + FALSE); + } + offset+=PAYLOAD_LEN; + break; + default: + break; + } + + /*Add any Padding*/ + t_offset = offset; + while (tvb_offset_exists(tvb, t_offset) && !tvb_get_guint8(tvb, t_offset)) + ++t_offset; + if (t_offset - offset > 2) { + proto_tree_add_item(tor_tree, + hf_tor_padding, + tvb, offset, + t_offset - offset, + FALSE); + offset=t_offset; + } + + } + + } +} + +static void tor_data_reassemble_init (void) +{ + fragment_table_init (&tor_data_segment_table); + reassembled_table_init (&tor_data_reassembled_table); +} + +void +proto_register_tor(void) +{ + static hf_register_info hf[] = { + { &hf_tor_circid, + { "CircID", "tor.circid", + FT_UINT16, BASE_DEC, NULL, 0x0, + "CircID", HFILL }}, + { &hf_tor_command, + { "Command", "tor.command", + FT_UINT8, BASE_HEX, VALS(vals_circuit_commands), 0x0, + "Command", HFILL }}, + { &hf_tor_length, + { "Length", "tor.length", + FT_STRING, BASE_NONE, NULL, 0x0, + "Length", HFILL }}, + { &hf_tor_payload, + { "Payload", "tor.payload", + FT_STRING, BASE_NONE, NULL, 0x0, + "Payload", HFILL }}, + { &hf_tor_timestamp, + { "Timestamp", "tor.timestamp", + FT_UINT64, BASE_DEC, NULL, 0x0, + "Timestamp", HFILL }}, + { &hf_tor_other_or_address, + { "Other OR's Address", "tor.otheroraddress", + FT_IPv4, BASE_NONE, NULL, 0x0, + "Other OR's Address", HFILL }}, + { &hf_tor_number_of_addresses, + { "Number of Addresses", "tor.numberofaddresses", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Number of Addresses", HFILL }}, + { &hf_tor_this_or_address, + { "This OR's Address", "tor.thisoraddress", + FT_IPv4, BASE_NONE, NULL, 0x0, + "This OR's Address", HFILL }}, + { &hf_tor_ttl, + { "TTL", "tor.ttl", + FT_UINT64, BASE_DEC, NULL, 0x0, + "TTL", HFILL }}, + { &hf_tor_address_type, + { "Address Type", "tor.addresstype", + FT_UINT8, BASE_HEX, VALS(vals_address_types), 0x0, + "Address Type", HFILL }}, + { &hf_tor_address_length, + { "Address Length", "tor.addresslength", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Address Length", HFILL }}, + { &hf_tor_hostname, + { "Hostname", "tor.hostname", + FT_STRING, BASE_NONE, NULL, 0x0, + "Hostname", HFILL }}, + { &hf_tor_key_material_y, + { "Key Material (Y)", "tor.keymaterialy", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Key Material (Y)", HFILL }}, + { &hf_tor_key_material_x, + { "Key Material (X)", "tor.keymaterialx", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Key Material (X)", HFILL }}, + { &hf_tor_dh_data, + { "Diffie Hellman data", "tor.dhdata", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Diffie Hellman data", HFILL }}, + { &hf_tor_derivative_key_data, + { "Derivative key data", "tor.derivativekeydata", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Derivative key data", HFILL }}, + { &hf_tor_create, + { "Create Payload", "tor.create", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Create Payload", HFILL }}, + { &hf_tor_destroy_reason, + { "Destroy Reason", "tor.destroyreason", + FT_UINT8, BASE_HEX, VALS(vals_destroy_reason), 0x0, + "Destroy Reason", HFILL }}, + { &hf_tor_relay_command, + { "Relay Command", "tor.relay_command", + FT_UINT8, BASE_HEX, VALS(vals_relay_commands), 0x0, + "Relay Command", HFILL }}, + { &hf_tor_recognized, + { "Recognized", "tor.recognized", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Recognized", HFILL }}, + { &hf_tor_streamid, + { "Stream ID", "tor.streamid", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Stream ID", HFILL }}, + { &hf_tor_relay_digest, + { "Digest", "tor.relay_digest", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Digest", HFILL }}, + { &hf_tor_relay_data_length, + { "Relay Data Length", "tor.relay_data_length", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Relay Data Length", HFILL }}, + { &hf_tor_relay_data, + { "Relay Data", "tor.relay_data", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Relay Data", HFILL }}, + { &hf_tor_encrypted_cell, + { "Encrypted Cell", "tor.ecnrypted_cell", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Encrypted Cell", HFILL }}, + { &hf_tor_padding, + { "Padding", "tor.padding", + FT_BYTES, BASE_HEX, NULL, 0x0, + "Padding", HFILL }}, + }; + + static gint *ett[] = { + &ett_tor, + &ett_tor_data_fragment, + &ett_tor_data_fragments + }; + module_t *tor_module; + + + proto_tor = proto_register_protocol("Tor Protocol", "Tor", "tor"); + register_dissector("tor", dissect_tor, proto_tor); + proto_register_field_array(proto_tor, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + register_init_routine (&tor_data_reassemble_init); + + /* Preferences */ + tor_module = prefs_register_protocol(proto_tor, proto_reg_handoff_tor); + + prefs_register_string_preference(tor_module, "cell_keys_list", "Tor cell keys list", + "Semicolon-separated list of AES keys used for Tor cell decryption; " + "each list entry must be in the form of ,,. " + " is the key used to encrypt cells", + (const gchar **)&cell_keys_list); +} + +void +proto_reg_handoff_tor(void) +{ + dissector_handle_t tor_handle; + + tor_handle = find_dissector("tor"); + ssl_dissector_add(TCP_PORT_SSL_TOR, "tor", TRUE); + data_handle = find_dissector("data"); + + tor_parse(); + +}