diff options
author | Tomas Kukosa <tomas.kukosa@siemens.com> | 2007-04-10 15:12:48 +0000 |
---|---|---|
committer | Tomas Kukosa <tomas.kukosa@siemens.com> | 2007-04-10 15:12:48 +0000 |
commit | 9f2ccb7fc0eb675448fdf375177fe523aa21747c (patch) | |
tree | 899feb88f2e2768d7c9d58a9b1f80ed06f443544 | |
parent | 4d21c49929c53bf12d789a830b0662835ee0d4bc (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.nmake | 2 | ||||
-rw-r--r-- | epan/dissectors/packet-dtls.c | 21 | ||||
-rw-r--r-- | epan/dissectors/packet-ssl-utils.c | 229 | ||||
-rw-r--r-- | epan/dissectors/packet-ssl-utils.h | 43 | ||||
-rw-r--r-- | epan/dissectors/packet-ssl.c | 708 |
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 */ |