aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomas Kukosa <tomas.kukosa@siemens.com>2007-04-10 15:12:48 +0000
committerTomas Kukosa <tomas.kukosa@siemens.com>2007-04-10 15:12:48 +0000
commit9f2ccb7fc0eb675448fdf375177fe523aa21747c (patch)
tree899feb88f2e2768d7c9d58a9b1f80ed06f443544
parent4d21c49929c53bf12d789a830b0662835ee0d4bc (diff)
- SSL desegmentation support
- SSL DEFLATE compression method support (RFC3749) - fix for Bugzilla Bug 1515: SSL bug with mutual authentication svn path=/trunk/; revision=21368
-rw-r--r--epan/dissectors/Makefile.nmake2
-rw-r--r--epan/dissectors/packet-dtls.c21
-rw-r--r--epan/dissectors/packet-ssl-utils.c229
-rw-r--r--epan/dissectors/packet-ssl-utils.h43
-rw-r--r--epan/dissectors/packet-ssl.c708
5 files changed, 780 insertions, 223 deletions
diff --git a/epan/dissectors/Makefile.nmake b/epan/dissectors/Makefile.nmake
index 5fe3a4eec5..c382ba2780 100644
--- a/epan/dissectors/Makefile.nmake
+++ b/epan/dissectors/Makefile.nmake
@@ -10,7 +10,7 @@ include ..\..\config.nmake
include Makefile.common
CFLAGS=-WX -DHAVE_CONFIG_H /I. /I.. /I../.. $(GLIB_CFLAGS) \
- $(NET_SNMP_CFLAGS) \
+ $(NET_SNMP_CFLAGS) $(ZLIB_CFLAGS) \
$(PCRE_CFLAGS) $(GNUTLS_CFLAGS) $(NETTLE_CFLAGS) \
$(KFW_CFLAGS) \
/I$(PCAP_DIR)\include -D_U_="" $(LOCAL_CFLAGS)
diff --git a/epan/dissectors/packet-dtls.c b/epan/dissectors/packet-dtls.c
index 3cee4b0b4f..a5a489325c 100644
--- a/epan/dissectors/packet-dtls.c
+++ b/epan/dissectors/packet-dtls.c
@@ -149,6 +149,7 @@ static GHashTable *dtls_session_hash = NULL;
static GHashTable *dtls_key_hash = NULL;
static GTree* dtls_associations = NULL;
static dissector_handle_t dtls_handle = NULL;
+static StringInfo dtls_compressed_data = {NULL, 0};
static StringInfo dtls_decrypted_data = {NULL, 0};
static gint dtls_decrypted_data_avail = 0;
@@ -161,7 +162,7 @@ static gchar* dtls_debug_file_name = NULL;
static void
dtls_init(void)
{
- ssl_common_init(&dtls_session_hash, &dtls_decrypted_data);
+ ssl_common_init(&dtls_session_hash, &dtls_decrypted_data, &dtls_compressed_data);
}
/* parse dtls related preferences (private keys and ports association strings) */
@@ -454,11 +455,11 @@ decrypt_dtls_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
/* retrive decoder for this packet direction */
if ((direction = ssl_packet_from_server(dtls_associations, pinfo->srcport, pinfo->ptype == PT_TCP)) != 0) {
ssl_debug_printf("decrypt_dtls_record: using server decoder\n");
- decoder = &ssl->server;
+ decoder = ssl->server;
}
else {
ssl_debug_printf("decrypt_dtls_record: using client decoder\n");
- decoder = &ssl->client;
+ decoder = ssl->client;
}
/* ensure we have enough storage space for decrypted data */
@@ -477,7 +478,7 @@ decrypt_dtls_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
dtls_decrypted_data_avail = dtls_decrypted_data.data_len;
if (ssl_decrypt_record(ssl, decoder,
content_type, tvb_get_ptr(tvb, offset, record_length),
- record_length, dtls_decrypted_data.data, &dtls_decrypted_data_avail) == 0)
+ record_length, &dtls_compressed_data, &dtls_decrypted_data, &dtls_decrypted_data_avail) == 0)
ret = 1;
if (ret && save_plaintext) {
@@ -553,12 +554,16 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo,
if(ssl){
if(ssl_packet_from_server(dtls_associations, pinfo->srcport, pinfo->ptype == PT_TCP)){
- ssl->server.seq=(guint32)sequence_number;
- ssl->server.epoch=epoch;
+ if (ssl->server) {
+ ssl->server->seq=(guint32)sequence_number;
+ ssl->server->epoch=epoch;
+ }
}
else{
- ssl->client.seq=(guint32)sequence_number;
- ssl->client.epoch=epoch;
+ if (ssl->client) {
+ ssl->client->seq=(guint32)sequence_number;
+ ssl->client->epoch=epoch;
+ }
}
}
if (!ssl_is_valid_content_type(content_type)) {
diff --git a/epan/dissectors/packet-ssl-utils.c b/epan/dissectors/packet-ssl-utils.c
index 5e6e621105..8948337d8c 100644
--- a/epan/dissectors/packet-ssl-utils.c
+++ b/epan/dissectors/packet-ssl-utils.c
@@ -27,6 +27,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif
#include "packet-ssl-utils.h"
#include <epan/emem.h>
@@ -368,6 +371,28 @@ ssl_data_alloc(StringInfo* str, guint len)
return 0;
}
+static gint
+ssl_data_realloc(StringInfo* str, guint len)
+{
+ str->data = g_realloc(str->data, len);
+ if (!str->data)
+ return -1;
+ str->data_len = len;
+ return 0;
+}
+
+static gint
+ssl_data_copy(StringInfo* dst, StringInfo* src)
+{
+ if (dst->data_len < src->data_len) {
+ if (ssl_data_realloc(dst, src->data_len))
+ return -1;
+ }
+ memcpy(dst->data, src->data, src->data_len);
+ dst->data_len = src->data_len;
+ 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))
@@ -625,12 +650,71 @@ ssl3_prf(StringInfo* secret, const gchar* usage,
return(0);
}
-static gint
-ssl_create_decoder(SslDecoder *dec, SslCipherSuite *cipher_suite,
+static SslFlow*
+ssl_create_flow(void)
+{
+ SslFlow *flow;
+
+ flow = se_alloc(sizeof(SslFlow));
+ flow->byte_seq = 0;
+ flow->flags = 0;
+ flow->multisegment_pdus = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "ssl_multisegment_pdus");
+ return flow;
+}
+
+/* memory allocations functions for zlib intialization */
+static void* ssl_zalloc(void* opaque, unsigned int no, unsigned int size)
+{
+ return g_malloc0(no*size);
+}
+static void ssl_zfree(void* opaque, void* address)
+{
+ g_free(address);
+}
+
+static SslDecompress*
+ssl_create_decompressor(gint compression)
+{
+ SslDecompress *decomp;
+ int err;
+
+ if (compression == 0) return NULL;
+ ssl_debug_printf("ssl_create_decompressor: compression method %d\n", compression);
+ decomp = se_alloc(sizeof(SslDecompress));
+ decomp->compression = compression;
+ switch (decomp->compression) {
+#ifdef HAVE_LIBZ
+ case 1: /* DEFLATE */
+ decomp->istream.zalloc = ssl_zalloc;
+ decomp->istream.zfree = ssl_zfree;
+ decomp->istream.opaque = Z_NULL;
+ decomp->istream.next_in = Z_NULL;
+ decomp->istream.next_out = Z_NULL;
+ decomp->istream.avail_in = 0;
+ decomp->istream.avail_out = 0;
+ err = inflateInit_(&decomp->istream, ZLIB_VERSION, sizeof(z_stream));
+ if (err != Z_OK) {
+ ssl_debug_printf("ssl_create_decompressor: inflateInit_() failed - %d\n", err);
+ return NULL;
+ }
+ break;
+#endif
+ default:
+ ssl_debug_printf("ssl_create_decompressor: unsupported compression method %d\n", decomp->compression);
+ return NULL;
+ }
+ return decomp;
+}
+
+static SslDecoder*
+ssl_create_decoder(SslCipherSuite *cipher_suite, gint compression,
guint8 *mk, guint8 *sk, guint8 *iv)
{
+ SslDecoder *dec;
gint ciph;
ciph=0;
+
+ dec = se_alloc0(sizeof(SslDecoder));
/* Find the SSLeay cipher */
if(cipher_suite->enc!=ENC_NULL) {
ssl_debug_printf("ssl_create_decoder CIPHER: %s\n", ciphers[cipher_suite->enc-0x30]);
@@ -639,16 +723,18 @@ ssl_create_decoder(SslDecoder *dec, SslCipherSuite *cipher_suite,
if (ciph == 0) {
ssl_debug_printf("ssl_create_decoder can't find cipher %s\n",
ciphers[(cipher_suite->enc-0x30) > 7 ? 7 : (cipher_suite->enc-0x30)]);
- return -1;
+ return NULL;
}
/* 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->compression = compression;
dec->mac_key.data = dec->_mac_key;
ssl_data_set(&dec->mac_key, mk, cipher_suite->dig_len);
dec->seq = 0;
- dec->byte_seq = 0;
+ dec->decomp = ssl_create_decompressor(compression);
+ dec->flow = ssl_create_flow();
if (dec->evp)
ssl_cipher_cleanup(&dec->evp);
@@ -656,11 +742,11 @@ ssl_create_decoder(SslDecoder *dec, SslCipherSuite *cipher_suite,
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;
+ return NULL;
}
ssl_debug_printf("decoder initialized (digest len %d)\n", cipher_suite->dig_len);
- return 0;
+ return dec;
}
int
@@ -853,20 +939,20 @@ ssl_generate_keyring_material(SslDecryptSession*ssl_session)
/* 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_session->client_new = ssl_create_decoder(&ssl_session->cipher_suite, ssl_session->compression, c_mk, c_wk, c_iv);
+ if (!ssl_session->client_new) {
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_session->server_new = ssl_create_decoder(&ssl_session->cipher_suite, ssl_session->compression, s_mk, s_wk, s_iv);
+ if (!ssl_session->server_new) {
ssl_debug_printf("ssl_generate_keyring_material can't init client decoder\n");
goto fail;
}
- ssl_debug_printf("ssl_generate_keyring_material client seq %d server seq %d\n",
- ssl_session->client.seq, ssl_session->server.seq);
+ ssl_debug_printf("ssl_generate_keyring_material: client seq %d, server seq %d\n",
+ ssl_session->client_new->seq, ssl_session->server_new->seq);
g_free(key_block.data);
return 0;
@@ -875,6 +961,19 @@ fail:
return -1;
}
+void
+ssl_change_cipher(SslDecryptSession *ssl_session, gboolean server)
+{
+ ssl_debug_printf("ssl_change_cipher %s\n", (server)?"SERVER":"CLIENT");
+ if (server) {
+ ssl_session->server = ssl_session->server_new;
+ ssl_session->server_new = NULL;
+ } else {
+ ssl_session->client = ssl_session->client_new;
+ ssl_session->client_new = NULL;
+ }
+}
+
int
ssl_decrypt_pre_master_secret(SslDecryptSession*ssl_session,
StringInfo* entrypted_pre_master, SSL_PRIVATE_KEY *pk)
@@ -1075,27 +1174,68 @@ dtls_check_mac(SslDecoder*decoder, gint ct,int ver, guint8* data,
int
+ssl_decompress_record(SslDecompress* decomp, const guchar* in, guint inl, StringInfo* out_str, guint* outl)
+{
+ gint err;
+
+ switch (decomp->compression) {
+#ifdef HAVE_LIBZ
+ case 1: /* DEFLATE */
+ err = Z_OK;
+ if (out_str->data_len < 16384) { /* maximal plain length */
+ ssl_data_realloc(out_str, 16384);
+ }
+ decomp->istream.next_in = (guchar*)in;
+ decomp->istream.avail_in = inl;
+ decomp->istream.next_out = out_str->data;
+ decomp->istream.avail_out = out_str->data_len;
+ if (inl > 0)
+ err = inflate(&decomp->istream, Z_SYNC_FLUSH);
+ if (err != Z_OK) {
+ ssl_debug_printf("ssl_decompress_record: inflate() failed - %d\n", err);
+ return -1;
+ }
+ *outl = out_str->data_len - decomp->istream.avail_out;
+ break;
+#endif /* HAVE_LIBZ */
+ default:
+ ssl_debug_printf("ssl_decompress_record: unsupported compression method %d\n", decomp->compression);
+ return -1;
+ }
+ return 0;
+}
+
+int
ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, gint ct,
- const guchar* in, gint inl, guchar*out, gint* outl)
+ const guchar* in, guint inl, StringInfo* comp_str, StringInfo* out_str, guint* outl)
{
- gint pad, worklen;
+ guint pad, worklen, uncomplen;
guint8 *mac;
-
ssl_debug_printf("ssl_decrypt_record ciphertext len %d\n", inl);
ssl_print_data("Ciphertext",in, inl);
+
+ /* ensure we have enough storage space for decrypted data */
+ if (inl > out_str->data_len)
+ {
+ ssl_debug_printf("ssl_decrypt_record: allocating %d bytes for decrypt data (old len %d)\n",
+ inl + 32, out_str->data_len);
+ ssl_data_realloc(out_str, inl + 32);
+ }
/* First decrypt*/
- if ((pad = ssl_cipher_decrypt(&decoder->evp,out,*outl,in,inl))!= 0)
- ssl_debug_printf("ssl_decrypt_record: %s %s\n", gcry_strsource (pad),
+ if ((pad = ssl_cipher_decrypt(&decoder->evp, out_str->data, out_str->data_len, in, inl))!= 0) {
+ ssl_debug_printf("ssl_decrypt_record failed: ssl_cipher_decrypt: %s %s\n", gcry_strsource (pad),
gcry_strerror (pad));
+ return -1;
+ }
- ssl_print_data("Plaintext",out,inl);
+ ssl_print_data("Plaintext", out_str->data, inl);
worklen=inl;
/* Now strip off the padding*/
- if(decoder->cipher_suite->block!=1){
- pad=out[inl-1];
+ if(decoder->cipher_suite->block!=1) {
+ pad=out_str->data[inl-1];
worklen-=(pad+1);
ssl_debug_printf("ssl_decrypt_record found padding %d final len %d\n",
pad, worklen);
@@ -1108,42 +1248,56 @@ ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, gint ct,
ssl_debug_printf("ssl_decrypt_record wrong record len/padding outlen %d\n work %d\n",*outl, worklen);
return -1;
}
- mac=out+worklen;
+ mac = out_str->data + worklen;
/* if TLS 1.1 we use the transmitted IV and remove it after (to not modify dissector in others parts)*/
if(ssl->version_netorder==TLSV1DOT1_VERSION){
worklen=worklen-decoder->cipher_suite->block;
- memcpy(out,out+decoder->cipher_suite->block,worklen);
+ memcpy(out_str->data,out_str->data+decoder->cipher_suite->block,worklen);
}
if(ssl->version_netorder==DTLSV1DOT0_VERSION){
worklen=worklen-decoder->cipher_suite->block;
- memcpy(out,out+decoder->cipher_suite->block,worklen);
+ memcpy(out_str->data,out_str->data+decoder->cipher_suite->block,worklen);
}
/* Now check the MAC */
ssl_debug_printf("checking mac (len %d, version %X, ct %d seq %d)\n",
worklen, ssl->version_netorder, ct, decoder->seq);
if(ssl->version_netorder==SSLV3_VERSION){
- if(ssl3_check_mac(decoder,ct,out,worklen,mac) < 0) {
+ if(ssl3_check_mac(decoder,ct,out_str->data,worklen,mac) < 0) {
ssl_debug_printf("ssl_decrypt_record: mac failed\n");
return -1;
}
}
else if(ssl->version_netorder==TLSV1_VERSION || ssl->version_netorder==TLSV1DOT1_VERSION){
- if(tls_check_mac(decoder,ct,ssl->version_netorder,out,worklen,mac)< 0) {
+ if(tls_check_mac(decoder,ct,ssl->version_netorder,out_str->data,worklen,mac)< 0) {
ssl_debug_printf("ssl_decrypt_record: mac failed\n");
return -1;
}
}
else if(ssl->version_netorder==DTLSV1DOT0_VERSION){
/* follow the openssl dtls errors the rigth test is : dtls_check_mac(decoder,ct,ssl->version_netorder,out,worklen,mac)< 0 */
- if(tls_check_mac(decoder,ct,TLSV1_VERSION,out,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");
return -1;
}
}
ssl_debug_printf("ssl_decrypt_record: mac ok\n");
*outl = worklen;
- return(0);
+
+ if (decoder->compression > 0) {
+ ssl_debug_printf("ssl_decrypt_record: compression method %d\n", decoder->compression);
+ ssl_data_copy(comp_str, out_str);
+ ssl_print_data("Plaintext compressed", comp_str->data, worklen);
+ if (!decoder->decomp) {
+ ssl_debug_printf("decrypt_ssl3_record: no decoder available\n");
+ return -1;
+ }
+ if (ssl_decompress_record(decoder->decomp, comp_str->data, worklen, out_str, &uncomplen) < 0) return -1;
+ ssl_print_data("Plaintext uncompressed", out_str->data, uncomplen);
+ *outl = uncomplen;
+ }
+
+ return 0;
}
static void
@@ -1541,7 +1695,7 @@ ssl_packet_from_server(GTree* associations, guint port, gboolean tcp)
register gint ret;
ret = ssl_association_find(associations, port, tcp) != 0;
- ssl_debug_printf("packet_from_server: is from server %d\n", ret);
+ ssl_debug_printf("packet_from_server: is from server - %s\n", (ret)?"TRUE":"FALSE");
return ret;
}
@@ -1591,7 +1745,7 @@ ssl_get_record_info(int proto, packet_info *pinfo, gint record_id)
}
void
-ssl_add_data_info(gint proto, packet_info *pinfo, guchar* data, gint data_len, gint key, guint32 seq)
+ssl_add_data_info(gint proto, packet_info *pinfo, guchar* data, gint data_len, gint key, SslFlow *flow)
{
SslDataInfo *rec, **prec;
SslPacketInfo *pi;
@@ -1608,10 +1762,13 @@ ssl_add_data_info(gint proto, packet_info *pinfo, guchar* data, gint data_len, g
rec->plain_data.data = (guchar*)(rec + 1);
memcpy(rec->plain_data.data, data, data_len);
rec->plain_data.data_len = data_len;
- rec->seq = seq;
- rec->nxtseq = seq + data_len;
+ rec->seq = flow->byte_seq;
+ rec->nxtseq = flow->byte_seq + data_len;
+ rec->flow = flow;
rec->next = NULL;
+ flow->byte_seq += data_len;
+
/* insertion */
prec = &pi->appl_data;
while (*prec) prec = &(*prec)->next;
@@ -1641,15 +1798,19 @@ ssl_get_data_info(int proto, packet_info *pinfo, gint key)
/* initialize/reset per capture state data (ssl sessions cache) */
void
-ssl_common_init(GHashTable **session_hash , StringInfo * decrypted_data)
+ssl_common_init(GHashTable **session_hash, StringInfo *decrypted_data, StringInfo *compressed_data)
{
if (*session_hash)
g_hash_table_destroy(*session_hash);
*session_hash = g_hash_table_new(ssl_hash, ssl_equal);
+
if (decrypted_data->data)
g_free(decrypted_data->data);
- decrypted_data->data = g_malloc0(32);
- decrypted_data->data_len = 32;
+ ssl_data_alloc(decrypted_data, 32);
+
+ if (compressed_data->data)
+ g_free(compressed_data->data);
+ ssl_data_alloc(compressed_data, 32);
}
/* parse ssl related preferences (private keys and ports association strings) */
diff --git a/epan/dissectors/packet-ssl-utils.h b/epan/dissectors/packet-ssl-utils.h
index 139e37fab2..9f6cb3037f 100644
--- a/epan/dissectors/packet-ssl-utils.h
+++ b/epan/dissectors/packet-ssl-utils.h
@@ -25,9 +25,14 @@
#include <glib.h>
#include <epan/packet.h>
+#include <epan/emem.h>
#include <epan/gnuc_format_check.h>
#include <epan/value_string.h>
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif
+
#ifdef HAVE_LIBGNUTLS
#ifdef _WIN32
#include <winposixtype.h>
@@ -346,7 +351,7 @@ static const value_string ssl_31_handshake_type[] = {
static const value_string ssl_31_compression_method[] = {
{ 0, "null" },
- { 1, "ZLIB" },
+ { 1, "DEFLATE" },
{ 64, "LZS" },
{ 0x00, NULL }
};
@@ -580,14 +585,29 @@ typedef struct _SslCipherSuite {
gint mode;
} SslCipherSuite;
+typedef struct _SslFlow {
+ guint32 byte_seq;
+ guint16 flags;
+ emem_tree_t *multisegment_pdus;
+} SslFlow;
+
+typedef struct _SslDecompress {
+ gint compression;
+#ifdef HAVE_LIBZ
+ z_stream istream;
+#endif
+} SslDecompress;
+
typedef struct _SslDecoder {
SslCipherSuite* cipher_suite;
+ gint compression;
guchar _mac_key[20];
StringInfo mac_key;
- SSL_CIPHER_CTX evp;
+ SSL_CIPHER_CTX evp;
+ SslDecompress *decomp;
guint32 seq;
guint16 epoch;
- guint32 byte_seq;
+ SslFlow *flow;
} SslDecoder;
#define KEX_RSA 0x10
@@ -622,6 +642,7 @@ typedef struct _SslDataInfo {
StringInfo plain_data;
guint32 seq;
guint32 nxtseq;
+ SslFlow *flow;
struct _SslDataInfo *next;
} SslDataInfo;
@@ -646,10 +667,13 @@ typedef struct _SslDecryptSession {
StringInfo client_data_for_iv;
gint cipher;
+ gint compression;
gint state;
SslCipherSuite cipher_suite;
- SslDecoder server;
- SslDecoder client;
+ SslDecoder *server;
+ SslDecoder *client;
+ SslDecoder *server_new;
+ SslDecoder *client_new;
SSL_PRIVATE_KEY* private_key;
guint32 version;
guint16 version_netorder;
@@ -716,6 +740,9 @@ ssl_find_cipher(int num,SslCipherSuite* cs);
extern gint
ssl_generate_keyring_material(SslDecryptSession*ssl_session);
+extern void
+ssl_change_cipher(SslDecryptSession *ssl_session, gboolean server);
+
/* Try to decrypt in place the encrypted pre_master_secret
@param ssl_session the store for the decrypted pre_master_secret
@param entrypted_pre_master the rsa encrypted pre_master_secret
@@ -736,7 +763,7 @@ ssl_decrypt_pre_master_secret(SslDecryptSession*ssl_session,
@return 0 on success */
extern gint
ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, gint ct,
- const guchar* in, gint inl,guchar*out,gint* outl);
+ const guchar* in, guint inl, StringInfo* comp_str, StringInfo* out_str, guint* outl);
/* Common part bitween SSL and DTLS dissectors */
@@ -786,14 +813,14 @@ extern tvbuff_t*
ssl_get_record_info(gint proto, packet_info *pinfo, gint record_id);
void
-ssl_add_data_info(gint proto, packet_info *pinfo, guchar* data, gint data_len, gint key, guint32 seq);
+ssl_add_data_info(gint proto, packet_info *pinfo, guchar* data, gint data_len, gint key, SslFlow *flow);
SslDataInfo*
ssl_get_data_info(int proto, packet_info *pinfo, gint key);
/* initialize/reset per capture state data (ssl sessions cache) */
extern void
-ssl_common_init(GHashTable **session_hash , StringInfo * decrypted_data);
+ssl_common_init(GHashTable **session_hash, StringInfo *decrypted_data, StringInfo *compressed_data);
/* parse ssl related preferences (private keys and ports association strings) */
extern void
diff --git a/epan/dissectors/packet-ssl.c b/epan/dissectors/packet-ssl.c
index b0132cba85..befba6425a 100644
--- a/epan/dissectors/packet-ssl.c
+++ b/epan/dissectors/packet-ssl.c
@@ -106,10 +106,12 @@
#include <glib.h>
#include <epan/conversation.h>
+#include <epan/reassemble.h>
#include <epan/prefs.h>
+#include <epan/emem.h>
#include <epan/inet_v6defs.h>
+#include <epan/dissectors/packet-tcp.h>
#include <epan/dissectors/packet-x509af.h>
-#include <epan/emem.h>
#include <epan/tap.h>
#include <epan/filesystem.h>
#include <epan/report_err.h>
@@ -203,6 +205,14 @@ static gint hf_pct_handshake_cipher = -1;
static gint hf_pct_handshake_exch = -1;
static gint hf_pct_handshake_sig = -1;
static gint hf_pct_msg_error_type = -1;
+static int hf_ssl_reassembled_in = -1;
+static int hf_ssl_segments = -1;
+static int hf_ssl_segment = -1;
+static int hf_ssl_segment_overlap = -1;
+static int hf_ssl_segment_overlap_conflict = -1;
+static int hf_ssl_segment_multiple_tails = -1;
+static int hf_ssl_segment_too_long_fragment = -1;
+static int hf_ssl_segment_error = -1;
/* Initialize the subtree pointers */
static gint ett_ssl = -1;
@@ -220,11 +230,32 @@ static gint ett_pct_cipher_suites = -1;
static gint ett_pct_hash_suites = -1;
static gint ett_pct_cert_suites = -1;
static gint ett_pct_exch_suites = -1;
+static gint ett_ssl_segments = -1;
+static gint ett_ssl_segment = -1;
+
+
+/* not all of the hf_fields below make sense for SSL but we have to provide
+ them anyways to comply with the api (which was aimed for ip fragment
+ reassembly) */
+static const fragment_items ssl_segment_items = {
+ &ett_ssl_segment,
+ &ett_ssl_segments,
+ &hf_ssl_segments,
+ &hf_ssl_segment,
+ &hf_ssl_segment_overlap,
+ &hf_ssl_segment_overlap_conflict,
+ &hf_ssl_segment_multiple_tails,
+ &hf_ssl_segment_too_long_fragment,
+ &hf_ssl_segment_error,
+ &hf_ssl_reassembled_in,
+ "Segments"
+};
static GHashTable *ssl_session_hash = NULL;
static GHashTable *ssl_key_hash = NULL;
static GTree* ssl_associations = NULL;
static dissector_handle_t ssl_handle = NULL;
+static StringInfo ssl_compressed_data = {NULL, 0};
static StringInfo ssl_decrypted_data = {NULL, 0};
static gint ssl_decrypted_data_avail = 0;
@@ -244,11 +275,21 @@ const gchar* ssl_version_short_names[] = {
/* Forward declaration we need below */
void proto_reg_handoff_ssl(void);
+/* Desegmentation of SSL streams */
+/* table to hold defragmented SSL streams */
+static GHashTable *ssl_fragment_table = NULL;
+static void
+ssl_fragment_init(void)
+{
+ fragment_table_init(&ssl_fragment_table);
+}
+
/* initialize/reset per capture state data (ssl sessions cache) */
static void
ssl_init(void)
{
- ssl_common_init(&ssl_session_hash, &ssl_decrypted_data);
+ ssl_common_init(&ssl_session_hash, &ssl_decrypted_data, &ssl_compressed_data);
+ ssl_fragment_init();
}
/* parse ssl related preferences (private keys and ports association strings) */
@@ -302,135 +343,6 @@ ssl_parse(void)
}
-#if 0
-/* function that save app_data during sub protocol reassembling */
-static void
-ssl_add_app_data(SslDecryptSession* ssl, guchar* data, gint data_len){
- StringInfo * app;
- app=&ssl->app_data_segment;
-
- if(app->data_len!=0){
- guchar* tmp;
- gint tmp_len;
- tmp=g_malloc(app->data_len);
- tmp_len=app->data_len;
- memcpy(tmp,app->data,app->data_len);
- if(app->data!=NULL)
- g_free(app->data);
- app->data_len=0;
- app->data=g_malloc(tmp_len+data_len);
- app->data_len=tmp_len+data_len;
- memcpy(app->data,tmp,tmp_len);
- g_free(tmp);
- memcpy(app->data+tmp_len, data,data_len);
- }
- else{
- /* it's new */
- if(app->data!=NULL)
- g_free(app->data);
- app->data=g_malloc(data_len);
- app->data_len=data_len;
- memcpy(app->data,data,data_len);
- }
-}
-#endif
-
-#if 0
-static void
-ssl_desegment_ssl_app_data(SslDecryptSession * ssl, packet_info *pinfo){
- SslPacketInfo* pi;
- SslAssociation* association;
- SslPacketInfo* pi2;
- pi = p_get_proto_data(pinfo->fd, proto_ssl);
- if (pi && pi->app_data.data)
- {
- tvbuff_t* new_tvb;
- packet_info * pp;
- /* find out a dissector using server port*/
- association = ssl_association_find(ssl_associations, pinfo->srcport, pinfo->ptype == PT_TCP);
- association = association ? association: ssl_association_find(ssl_associations, pinfo->destport, pinfo->ptype == PT_TCP);
- /* create a copy of packet_info */
- pp=g_malloc(sizeof(packet_info));
- memcpy(pp, pinfo, sizeof(packet_info));
-
- if (association && association->handle) {
- /* it's the first SS segmented packet */
- if(ssl->app_data_segment.data==NULL){
- /* create new tvbuff for the decrypted data */
- new_tvb = tvb_new_real_data(pi->app_data.data,
- pi->app_data.data_len, pi->app_data.data_len);
- tvb_set_free_cb(new_tvb, g_free);
- /* we allow subdissector to tell us more bytes */
- pp->can_desegment=2;
- /* subdissector call */
- call_dissector(association->handle, new_tvb, pp, NULL);
- /* if the dissector need more bytes */
- if(pp->desegment_len>0){
- /* we save the actual data to reuse them later */
- ssl_add_app_data(ssl, pi->app_data.data, pi->app_data.data_len);
- /* we remove data to forbid subdissection */
- p_remove_proto_data(pinfo->fd, proto_ssl);
- /* update of COL_INFO */
- if (check_col(pinfo->cinfo, COL_INFO)){
- col_append_str(pinfo->cinfo, COL_INFO, "[SSL segment of a reassembled PDU]");
- pinfo->cinfo->writable=FALSE;
- }
- return;
- }
- }
- else
- {
- /* it isn't the first SSL segmented packet */
- /* we add actual data to reuse them later */
- ssl_add_app_data(ssl, pi->app_data.data, pi->app_data.data_len);
- /* create new tvbuff for the decrypted data */
- new_tvb = tvb_new_real_data(ssl->app_data_segment.data,
- ssl->app_data_segment.data_len,
- ssl->app_data_segment.data_len);
- tvb_set_free_cb(new_tvb, g_free);
- /* we allow subdissector to tell us more bytes */
- pp->can_desegment=2;
- /* subdissector call */
- call_dissector(association->handle, new_tvb, pp, NULL);
- /* if the dissector need more bytes */
- if(pp->desegment_len>0){
- /* we remove data to forbid subdissection */
- p_remove_proto_data(pinfo->fd, proto_ssl);
- /* update of COL_INFO */
- if (check_col(pinfo->cinfo, COL_INFO)){
- col_append_str(pinfo->cinfo, COL_INFO, "[SSL segment of a reassembled PDU]");
- pinfo->cinfo->writable=FALSE;
- }
- return;
- }
- else
- {
- /* we create SslPacketInfo to save data */
- pi2=g_malloc(sizeof(SslPacketInfo));
- pi2->app_data.data=g_malloc(ssl->app_data_segment.data_len);
- memcpy(pi2->app_data.data,ssl->app_data_segment.data,ssl->app_data_segment.data_len);
- pi2->app_data.data_len=ssl->app_data_segment.data_len;
-
- /* we remove data if it's useful */
- p_remove_proto_data(pinfo->fd, proto_ssl);
- /* we add reassembled subprotocol data */
- p_add_proto_data(pinfo->fd, proto_ssl, pi2);
- /* we delete saved app_data */
- if(ssl->app_data_segment.data)
- g_free(ssl->app_data_segment.data);
- ssl->app_data_segment.data=NULL;
- ssl->app_data_segment.data_len=0;
- }
- }
- /* we delete pp structure */
- g_free(pp);
-
- }
- }
-
-
-}
-#endif /* 0 */
/*********************************************************************
*
* Forward Declarations
@@ -803,34 +715,24 @@ decrypt_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
ssl_debug_printf("decrypt_ssl3_record: app_data len %d ssl, state 0x%02X\n",
record_length, ssl->state);
direction = ssl_packet_from_server(ssl_associations, pinfo->srcport, pinfo->ptype == PT_TCP);
- if (!(ssl->state & SSL_HAVE_SESSION_KEY)) {
- ssl_debug_printf("decrypt_ssl3_record: no session key\n");
- /* save data to update IV if session key is obtained later */
- data_for_iv = (direction != 0) ? &ssl->server_data_for_iv : &ssl->client_data_for_iv;
- data_for_iv_len = (record_length < 24) ? record_length : 24;
- ssl_data_set(data_for_iv, (guchar*)tvb_get_ptr(tvb, offset + record_length - data_for_iv_len, data_for_iv_len), data_for_iv_len);
- return ret;
- }
/* retrive decoder for this packet direction*/
if (direction != 0) {
ssl_debug_printf("decrypt_ssl3_record: using server decoder\n");
- decoder = &ssl->server;
+ decoder = ssl->server;
}
else {
ssl_debug_printf("decrypt_ssl3_record: using client decoder\n");
- decoder = &ssl->client;
+ decoder = ssl->client;
}
- /* ensure we have enough storage space for decrypted data */
- if (record_length > ssl_decrypted_data.data_len)
- {
- ssl_debug_printf("decrypt_ssl3_record: allocating %d bytes"
- " for decrypt data (old len %d)\n",
- record_length + 32, ssl_decrypted_data.data_len);
- ssl_decrypted_data.data = g_realloc(ssl_decrypted_data.data,
- record_length + 32);
- ssl_decrypted_data.data_len = record_length + 32;
+ if (!decoder) {
+ ssl_debug_printf("decrypt_ssl3_record: no decoder available\n");
+ /* save data to update IV if decoder is available later */
+ data_for_iv = (direction != 0) ? &ssl->server_data_for_iv : &ssl->client_data_for_iv;
+ data_for_iv_len = (record_length < 24) ? record_length : 24;
+ ssl_data_set(data_for_iv, (guchar*)tvb_get_ptr(tvb, offset + record_length - data_for_iv_len, data_for_iv_len), data_for_iv_len);
+ return ret;
}
/* run decryption and add decrypted payload to protocol data, if decryption
@@ -838,7 +740,7 @@ decrypt_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
ssl_decrypted_data_avail = ssl_decrypted_data.data_len;
if (ssl_decrypt_record(ssl, decoder,
content_type, tvb_get_ptr(tvb, offset, record_length),
- record_length, ssl_decrypted_data.data, &ssl_decrypted_data_avail) == 0)
+ record_length, &ssl_compressed_data, &ssl_decrypted_data, &ssl_decrypted_data_avail) == 0)
ret = 1;
/* */
if (!ret) {
@@ -848,12 +750,434 @@ decrypt_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
ssl_data_set(data_for_iv, (guchar*)tvb_get_ptr(tvb, offset + record_length - data_for_iv_len, data_for_iv_len), data_for_iv_len);
}
if (ret && save_plaintext) {
- ssl_add_data_info(proto_ssl, pinfo, ssl_decrypted_data.data, ssl_decrypted_data_avail, TVB_RAW_OFFSET(tvb)+offset, decoder->byte_seq);
- decoder->byte_seq += ssl_decrypted_data_avail;
+ ssl_add_data_info(proto_ssl, pinfo, ssl_decrypted_data.data, ssl_decrypted_data_avail, TVB_RAW_OFFSET(tvb)+offset, decoder->flow);
}
return ret;
}
+static void
+process_ssl_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
+ proto_tree *root_tree, proto_tree *tree, SslAssociation* association,
+ guint32 seq, guint32 nxtseq, gboolean is_ssl_segment,
+ SslFlow *flow);
+
+static void
+desegment_ssl(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ guint32 seq, guint32 nxtseq,
+ SslAssociation* association,
+ proto_tree *root_tree, proto_tree *tree,
+ SslFlow *flow)
+{
+ fragment_data *ipfd_head;
+ gboolean must_desegment;
+ gboolean called_dissector;
+ int another_pdu_follows;
+ int deseg_offset;
+ guint32 deseg_seq;
+ gint nbytes;
+ proto_item *item;
+ proto_item *frag_tree_item;
+ proto_item *ssl_tree_item;
+ struct tcp_multisegment_pdu *msp;
+
+again:
+ ipfd_head=NULL;
+ must_desegment = FALSE;
+ called_dissector = FALSE;
+ another_pdu_follows = 0;
+ msp=NULL;
+
+ /*
+ * Initialize these to assume no desegmentation.
+ * If that's not the case, these will be set appropriately
+ * by the subdissector.
+ */
+ pinfo->desegment_offset = 0;
+ pinfo->desegment_len = 0;
+
+ /*
+ * Initialize this to assume that this segment will just be
+ * added to the middle of a desegmented chunk of data, so
+ * that we should show it all as data.
+ * If that's not the case, it will be set appropriately.
+ */
+ deseg_offset = offset;
+
+ /* find the most previous PDU starting before this sequence number */
+ msp=se_tree_lookup32_le(flow->multisegment_pdus, seq-1);
+ if(msp && msp->seq<=seq && msp->nxtpdu>seq){
+ int len;
+
+ if(!pinfo->fd->flags.visited){
+ msp->last_frame=pinfo->fd->num;
+ msp->last_frame_time=pinfo->fd->abs_ts;
+ }
+
+ /* OK, this PDU was found, which means the segment continues
+ a higher-level PDU and that we must desegment it.
+ */
+ if(msp->flags&MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT){
+ /* The dissector asked for the entire segment */
+ len=tvb_length_remaining(tvb, offset);
+ } else {
+ len=MIN(nxtseq, msp->nxtpdu) - seq;
+ }
+
+ ipfd_head = fragment_add(tvb, offset, pinfo, msp->first_frame,
+ ssl_fragment_table,
+ seq - msp->seq,
+ len,
+ (LT_SEQ (nxtseq,msp->nxtpdu)) );
+
+ if(msp->flags&MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT){
+ msp->flags&=(~MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT);
+
+ /* If we consumed the entire segment there is no
+ * other pdu starting anywhere inside this segment.
+ * So update nxtpdu to point at least to the start
+ * of the next segment.
+ * (If the subdissector asks for even more data we
+ * will advance nxtpdu even furhter later down in
+ * the code.)
+ */
+ msp->nxtpdu=nxtseq;
+ }
+
+ if( (msp->nxtpdu<nxtseq)
+ && (msp->nxtpdu>=seq)
+ && (len>0) ){
+ another_pdu_follows=msp->nxtpdu-seq;
+ }
+ } else {
+ /* This segment was not found in our table, so it doesn't
+ contain a continuation of a higher-level PDU.
+ Call the normal subdissector.
+ */
+ process_ssl_payload(tvb, offset, pinfo, root_tree, tree,
+ association, 0, 0, FALSE, flow);
+ called_dissector = TRUE;
+
+ /* Did the subdissector ask us to desegment some more data
+ before it could handle the packet?
+ If so we have to create some structures in our table but
+ this is something we only do the first time we see this
+ packet.
+ */
+ if(pinfo->desegment_len) {
+ if (!pinfo->fd->flags.visited)
+ must_desegment = TRUE;
+
+ /*
+ * Set "deseg_offset" to the offset in "tvb"
+ * of the first byte of data that the
+ * subdissector didn't process.
+ */
+ deseg_offset = offset + pinfo->desegment_offset;
+ }
+
+ /* Either no desegmentation is necessary, or this is
+ segment contains the beginning but not the end of
+ a higher-level PDU and thus isn't completely
+ desegmented.
+ */
+ ipfd_head = NULL;
+ }
+
+
+ /* is it completely desegmented? */
+ if(ipfd_head){
+ /*
+ * Yes, we think it is.
+ * We only call subdissector for the last segment.
+ * Note that the last segment may include more than what
+ * we needed.
+ */
+ if(ipfd_head->reassembled_in==pinfo->fd->num){
+ /*
+ * OK, this is the last segment.
+ * Let's call the subdissector with the desegmented
+ * data.
+ */
+ tvbuff_t *next_tvb;
+ int old_len;
+
+ /* create a new TVB structure for desegmented data */
+ next_tvb = tvb_new_real_data(ipfd_head->data,
+ ipfd_head->datalen, ipfd_head->datalen);
+
+ /* add this tvb as a child to the original one */
+ tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+
+ /* add desegmented data to the data source list */
+ add_new_data_source(pinfo, next_tvb, "Reassembled SSL");
+
+ /* call subdissector */
+ process_ssl_payload(next_tvb, 0, pinfo, root_tree,
+ tree, association, 0, 0, FALSE, flow);
+ called_dissector = TRUE;
+
+ /*
+ * OK, did the subdissector think it was completely
+ * desegmented, or does it think we need even more
+ * data?
+ */
+ old_len=(int)(tvb_reported_length(next_tvb)-tvb_reported_length_remaining(tvb, offset));
+ if(pinfo->desegment_len &&
+ pinfo->desegment_offset<=old_len){
+ /*
+ * "desegment_len" isn't 0, so it needs more
+ * data for something - and "desegment_offset"
+ * is before "old_len", so it needs more data
+ * to dissect the stuff we thought was
+ * completely desegmented (as opposed to the
+ * stuff at the beginning being completely
+ * desegmented, but the stuff at the end
+ * being a new higher-level PDU that also
+ * needs desegmentation).
+ */
+ fragment_set_partial_reassembly(pinfo,msp->first_frame,ssl_fragment_table);
+ /* Update msp->nxtpdu to point to the new next
+ * pdu boundary.
+ */
+ if(pinfo->desegment_len==DESEGMENT_ONE_MORE_SEGMENT){
+ /* We want reassembly of at least one
+ * more segment so set the nxtpdu
+ * boundary to one byte into the next
+ * segment.
+ * This means that the next segment
+ * will complete reassembly even if it
+ * is only one single byte in length.
+ */
+ msp->nxtpdu=seq+tvb_reported_length_remaining(tvb, offset) + 1;
+ msp->flags|=MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
+ } else {
+ msp->nxtpdu=seq+tvb_reported_length_remaining(tvb, offset) + pinfo->desegment_len;
+ }
+ /* Since we need at least some more data
+ * there can be no pdu following in the
+ * tail of this segment.
+ */
+ another_pdu_follows=0;
+ } else {
+ /*
+ * Show the stuff in this TCP segment as
+ * just raw TCP segment data.
+ */
+ nbytes =
+ tvb_reported_length_remaining(tvb, offset);
+ proto_tree_add_text(tree, tvb, offset, -1,
+ "SSL segment data (%u byte%s)", nbytes,
+ plurality(nbytes, "", "s"));
+
+ /*
+ * The subdissector thought it was completely
+ * desegmented (although the stuff at the
+ * end may, in turn, require desegmentation),
+ * so we show a tree with all segments.
+ */
+ show_fragment_tree(ipfd_head, &ssl_segment_items,
+ root_tree, pinfo, next_tvb, &frag_tree_item);
+ /*
+ * The toplevel fragment subtree is now
+ * behind all desegmented data; move it
+ * right behind the TCP tree.
+ */
+ ssl_tree_item = proto_tree_get_parent(tree);
+ if(frag_tree_item && ssl_tree_item) {
+ proto_tree_move_item(root_tree, ssl_tree_item, frag_tree_item);
+ }
+
+ /* Did the subdissector ask us to desegment
+ some more data? This means that the data
+ at the beginning of this segment completed
+ a higher-level PDU, but the data at the
+ end of this segment started a higher-level
+ PDU but didn't complete it.
+
+ If so, we have to create some structures
+ in our table, but this is something we
+ only do the first time we see this packet.
+ */
+ if(pinfo->desegment_len) {
+ if (!pinfo->fd->flags.visited)
+ must_desegment = TRUE;
+
+ /* The stuff we couldn't dissect
+ must have come from this segment,
+ so it's all in "tvb".
+
+ "pinfo->desegment_offset" is
+ relative to the beginning of
+ "next_tvb"; we want an offset
+ relative to the beginning of "tvb".
+
+ First, compute the offset relative
+ to the *end* of "next_tvb" - i.e.,
+ the number of bytes before the end
+ of "next_tvb" at which the
+ subdissector stopped. That's the
+ length of "next_tvb" minus the
+ offset, relative to the beginning
+ of "next_tvb, at which the
+ subdissector stopped.
+ */
+ deseg_offset =
+ ipfd_head->datalen - pinfo->desegment_offset;
+
+ /* "tvb" and "next_tvb" end at the
+ same byte of data, so the offset
+ relative to the end of "next_tvb"
+ of the byte at which we stopped
+ is also the offset relative to
+ the end of "tvb" of the byte at
+ which we stopped.
+
+ Convert that back into an offset
+ relative to the beginninng of
+ "tvb", by taking the length of
+ "tvb" and subtracting the offset
+ relative to the end.
+ */
+ deseg_offset=tvb_reported_length(tvb) - deseg_offset;
+ }
+ }
+ }
+ }
+
+ if (must_desegment) {
+ /* If the dissector requested "reassemble until FIN"
+ * just set this flag for the flow and let reassembly
+ * proceed at normal. We will check/pick up these
+ * reassembled PDUs later down in dissect_tcp() when checking
+ * for the FIN flag.
+ */
+ if(pinfo->desegment_len==DESEGMENT_UNTIL_FIN){
+ flow->flags|=TCP_FLOW_REASSEMBLE_UNTIL_FIN;
+ }
+ /*
+ * The sequence number at which the stuff to be desegmented
+ * starts is the sequence number of the byte at an offset
+ * of "deseg_offset" into "tvb".
+ *
+ * The sequence number of the byte at an offset of "offset"
+ * is "seq", i.e. the starting sequence number of this
+ * segment, so the sequence number of the byte at
+ * "deseg_offset" is "seq + (deseg_offset - offset)".
+ */
+ deseg_seq = seq + (deseg_offset - offset);
+
+ if( ((nxtseq - deseg_seq) <= 1024*1024)
+ && (!pinfo->fd->flags.visited) ){
+ if(pinfo->desegment_len==DESEGMENT_ONE_MORE_SEGMENT){
+ /* The subdissector asked to reassemble using the
+ * entire next segment.
+ * Just ask reassembly for one more byte
+ * but set this msp flag so we can pick it up
+ * above.
+ */
+ msp = pdu_store_sequencenumber_of_next_pdu(pinfo,
+ deseg_seq, nxtseq+1, flow->multisegment_pdus);
+ msp->flags|=MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
+ } else {
+ msp = pdu_store_sequencenumber_of_next_pdu(pinfo,
+ deseg_seq, nxtseq+pinfo->desegment_len, flow->multisegment_pdus);
+ }
+
+ /* add this segment as the first one for this new pdu */
+ fragment_add(tvb, deseg_offset, pinfo, msp->first_frame,
+ ssl_fragment_table,
+ 0,
+ nxtseq - deseg_seq,
+ LT_SEQ(nxtseq, msp->nxtpdu));
+ }
+ }
+
+ if (!called_dissector || pinfo->desegment_len != 0) {
+ if (ipfd_head != NULL && ipfd_head->reassembled_in != 0 &&
+ !(ipfd_head->flags & FD_PARTIAL_REASSEMBLY)) {
+ /*
+ * We know what frame this PDU is reassembled in;
+ * let the user know.
+ */
+ item=proto_tree_add_uint(tree, *ssl_segment_items.hf_reassembled_in,
+ tvb, 0, 0, ipfd_head->reassembled_in);
+ PROTO_ITEM_SET_GENERATED(item);
+ }
+
+ /*
+ * Either we didn't call the subdissector at all (i.e.,
+ * this is a segment that contains the middle of a
+ * higher-level PDU, but contains neither the beginning
+ * nor the end), or the subdissector couldn't dissect it
+ * all, as some data was missing (i.e., it set
+ * "pinfo->desegment_len" to the amount of additional
+ * data it needs).
+ */
+ if (pinfo->desegment_offset == 0) {
+ /*
+ * It couldn't, in fact, dissect any of it (the
+ * first byte it couldn't dissect is at an offset
+ * of "pinfo->desegment_offset" from the beginning
+ * of the payload, and that's 0).
+ * Just mark this as SSL.
+ */
+ if (check_col(pinfo->cinfo, COL_PROTOCOL)){
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "SSL");
+ }
+ if (check_col(pinfo->cinfo, COL_INFO)){
+ col_set_str(pinfo->cinfo, COL_INFO, "[SSL segment of a reassembled PDU]");
+ }
+ }
+
+ /*
+ * Show what's left in the packet as just raw TCP segment
+ * data.
+ * XXX - remember what protocol the last subdissector
+ * was, and report it as a continuation of that, instead?
+ */
+ nbytes = tvb_reported_length_remaining(tvb, deseg_offset);
+ proto_tree_add_text(tree, tvb, deseg_offset, -1,
+ "SSL segment data (%u byte%s)", nbytes,
+ plurality(nbytes, "", "s"));
+ }
+ pinfo->can_desegment=0;
+ pinfo->desegment_offset = 0;
+ pinfo->desegment_len = 0;
+
+ if(another_pdu_follows){
+ /* there was another pdu following this one. */
+ pinfo->can_desegment=2;
+ /* we also have to prevent the dissector from changing the
+ * PROTOCOL and INFO colums since what follows may be an
+ * incomplete PDU and we dont want it be changed back from
+ * <Protocol> to <TCP>
+ * XXX There is no good way to block the PROTOCOL column
+ * from being changed yet so we set the entire row unwritable.
+ */
+ col_set_fence(pinfo->cinfo, COL_INFO);
+ col_set_writable(pinfo->cinfo, FALSE);
+ offset += another_pdu_follows;
+ seq += another_pdu_follows;
+ goto again;
+ }
+}
+
+static void
+process_ssl_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
+ proto_tree *root_tree, proto_tree *tree, SslAssociation* association,
+ guint32 seq, guint32 nxtseq, gboolean is_ssl_segment,
+ SslFlow *flow)
+{
+ tvbuff_t *next_tvb;
+
+ next_tvb = tvb_new_subset(tvb, offset, -1, -1);
+
+ if (association && association->handle) {
+ ssl_debug_printf("dissect_ssl3_record found association %p\n", association);
+ call_dissector(association->handle, next_tvb, pinfo, proto_tree_get_root(tree));
+ }
+}
+
void
dissect_ssl_payload(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, SslAssociation* association)
{
@@ -867,6 +1191,7 @@ dissect_ssl_payload(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *t
/* try to dissect decrypted data*/
ssl_debug_printf("dissect_ssl3_record decrypted len %d\n", appl_data->plain_data.data_len);
+ ssl_print_text_data("decrypted app data fragment", appl_data->plain_data.data, appl_data->plain_data.data_len);
/* create a new TVB structure for desegmented data */
next_tvb = tvb_new_real_data(appl_data->plain_data.data, appl_data->plain_data.data_len, appl_data->plain_data.data_len);
@@ -878,19 +1203,18 @@ dissect_ssl_payload(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *t
add_new_data_source(pinfo, next_tvb, "Decrypted SSL data");
/* Can we desegment this segment? */
- if (FALSE /*ssl_desegment_app_data ignore till implemented well */) {
+ if (ssl_desegment_app_data) {
/* Yes. */
- /*desegment_ssl(next_tvb, pinfo, offset, seq, nxtseq, sport, dport, tree,
- tcp_tree, tcpd);*/
+ pinfo->can_desegment = 2;
+ desegment_ssl(next_tvb, pinfo, 0, appl_data->seq, appl_data->nxtseq, association, proto_tree_get_root(tree), tree, appl_data->flow);
} else if (association && association->handle) {
/* No - just call the subdissector.
Mark this as fragmented, so if somebody throws an exception,
we don't report it as a malformed frame. */
+ pinfo->can_desegment = 0;
save_fragmented = pinfo->fragmented;
pinfo->fragmented = TRUE;
- ssl_debug_printf("dissect_ssl3_record found association %p\n", association);
- ssl_print_text_data("decrypted app data fragment", appl_data->plain_data.data, appl_data->plain_data.data_len);
- call_dissector(association->handle, next_tvb, pinfo, proto_tree_get_root(tree));
+ process_ssl_payload(next_tvb, 0, pinfo, proto_tree_get_root(tree), tree, association, appl_data->seq, appl_data->nxtseq, TRUE, appl_data->flow);
pinfo->fragmented = save_fragmented;
}
}
@@ -1113,11 +1437,12 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
switch (content_type) {
case SSL_ID_CHG_CIPHER_SPEC:
+ ssl_debug_printf("dissect_ssl3_change_cipher_spec\n");
if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO, "Change Cipher Spec");
dissect_ssl3_change_cipher_spec(tvb, ssl_record_tree,
offset, conv_version, content_type);
- ssl_debug_printf("dissect_ssl3_change_cipher_spec\n");
+ if (ssl) ssl_change_cipher(ssl, ssl_packet_from_server(ssl_associations, pinfo->srcport, pinfo->ptype == PT_TCP));
break;
case SSL_ID_ALERT:
{
@@ -1858,14 +2183,16 @@ dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb,
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);
offset += 2;
+ if (ssl) {
+ /* store selected compression method for decryption */
+ ssl->compression = tvb_get_guint8(tvb, offset);
+ }
/* and the server-selected compression method */
proto_tree_add_item(tree, hf_ssl_handshake_comp_method,
tvb, offset, 1, FALSE);
@@ -3146,24 +3473,28 @@ void ssl_set_master_secret(guint32 frame_num, address *addr_srv, address *addr_c
}
ssl->state |= SSL_HAVE_SESSION_KEY;
+ /* chenge ciphers immediately */
+ ssl_change_cipher(ssl, TRUE);
+ ssl_change_cipher(ssl, FALSE);
+
/* update seq numbers is available */
- if (client_seq != (guint32)-1) {
- ssl->client.seq = client_seq;
- ssl_debug_printf("ssl_set_master_secret client.seq updated to %u\n", ssl->client.seq);
+ if (ssl->client && (client_seq != (guint32)-1)) {
+ ssl->client->seq = client_seq;
+ ssl_debug_printf("ssl_set_master_secret client->seq updated to %u\n", ssl->client->seq);
}
- if (server_seq != (guint32)-1) {
- ssl->server.seq = server_seq;
- ssl_debug_printf("ssl_set_master_secret server.seq updated to %u\n", ssl->server.seq);
+ if (ssl->server && (server_seq != (guint32)-1)) {
+ ssl->server->seq = server_seq;
+ ssl_debug_printf("ssl_set_master_secret server->seq updated to %u\n", ssl->server->seq);
}
/* update IV from last data */
iv_len = (ssl->cipher_suite.block>1) ? ssl->cipher_suite.block : 8;
- if ((ssl->client.seq > 0) || (ssl->client_data_for_iv.data_len > iv_len)) {
- ssl_cipher_setiv(&ssl->client.evp, ssl->client_data_for_iv.data + ssl->client_data_for_iv.data_len - iv_len, iv_len);
+ if (ssl->client && ((ssl->client->seq > 0) || (ssl->client_data_for_iv.data_len > iv_len))) {
+ ssl_cipher_setiv(&ssl->client->evp, ssl->client_data_for_iv.data + ssl->client_data_for_iv.data_len - iv_len, iv_len);
ssl_print_data("ssl_set_master_secret client IV updated",ssl->client_data_for_iv.data + ssl->client_data_for_iv.data_len - iv_len, iv_len);
}
- if ((ssl->server.seq > 0) || (ssl->server_data_for_iv.data_len > iv_len)) {
- ssl_cipher_setiv(&ssl->server.evp, ssl->server_data_for_iv.data + ssl->server_data_for_iv.data_len - iv_len, iv_len);
+ if (ssl->server && ((ssl->server->seq > 0) || (ssl->server_data_for_iv.data_len > iv_len))) {
+ ssl_cipher_setiv(&ssl->server->evp, ssl->server_data_for_iv.data + ssl->server_data_for_iv.data_len - iv_len, iv_len);
ssl_print_data("ssl_set_master_secret server IV updated",ssl->server_data_for_iv.data + ssl->server_data_for_iv.data_len - iv_len, iv_len);
}
}
@@ -3856,6 +4187,37 @@ proto_register_ssl(void)
FT_NONE, BASE_NONE, NULL , 0x0,
"PCT Server Certificate", HFILL }
},
+ { &hf_ssl_segment_overlap,
+ { "Segment overlap", "ssl.segment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Segment overlaps with other segments", HFILL }},
+
+ { &hf_ssl_segment_overlap_conflict,
+ { "Conflicting data in segment overlap", "ssl.segment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Overlapping segments contained conflicting data", HFILL }},
+
+ { &hf_ssl_segment_multiple_tails,
+ { "Multiple tail segments found", "ssl.segment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Several tails were found when reassembling the pdu", HFILL }},
+
+ { &hf_ssl_segment_too_long_fragment,
+ { "Segment too long", "ssl.segment.toolongfragment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Segment contained data past end of the pdu", HFILL }},
+
+ { &hf_ssl_segment_error,
+ { "Reassembling error", "ssl.segment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "Reassembling error due to illegal segments", HFILL }},
+
+ { &hf_ssl_segment,
+ { "SSL Segment", "ssl.segment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "SSL Segment", HFILL }},
+
+ { &hf_ssl_segments,
+ { "Reassembled SSL Segments", "ssl.segments", FT_NONE, BASE_NONE, NULL, 0x0,
+ "SSL Segments", HFILL }},
+
+ { &hf_ssl_reassembled_in,
+ { "Reassembled PDU in frame", "ssl.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "The PDU that doesn't end in this segment is reassembled in this frame", HFILL }},
};
/* Setup protocol subtree array */
@@ -3875,6 +4237,8 @@ proto_register_ssl(void)
&ett_pct_hash_suites,
&ett_pct_cert_suites,
&ett_pct_exch_suites,
+ &ett_ssl_segments,
+ &ett_ssl_segment,
};
/* Register the protocol name and description */