0% found this document useful (0 votes)
46 views

Appendix C - Wireshark Code Sample

Uploaded by

john arias
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
46 views

Appendix C - Wireshark Code Sample

Uploaded by

john arias
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 26

WSU04: Wireshark Network Forensics

and Security

Appendix C:
Wireshark Code
Wireshark University

SSL Dissector Code


The following code is from the packet-ssl.c which can be found at
http://anonsvn.wireshark.org/wireshark/trunk/epan/dissectors.

Note: This is only a portion of the code. See ssl-c.txt in the \misc directory on the
Student Supplement DVD.

/* packet-ssl.c
* Routines for ssl dissection
* Copyright (c) 2000-2001, Scott Renfro <scott@renfro.org>
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*

References
• See http://www.netscape.com/eng/security/SSL_2.html for SSL 2.0 specs.
• See http://www.netscape.com/eng/ssl3/for SSL 3.0 specs.
• See RFC 2246 for SSL 3.1/TLS 1.0 specs.
• See (among other places) http://www.graphcomp.com/info/specs/ms/pct.htm for PCT
1 draft specs.
• See http://research.sun.com/projects/crypto/draft-ietf-tls-ecc-05.txt for Elliptic
Curve Cryptography cipher suites.
• See http://www.ietf.org/internet-drafts/draft-ietf-tls-camellia-04.txt for
Camellia-based cipher suites.

WSU04: Network Forensics and Security – Appendix C Page C-2


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

Notes Regarding Dissection


*
* - Does not support dissection
* of frames that would require state maintained between frames
* (e.g., single ssl records spread across multiple tcp frames)
*
* - Identifies, but does not fully dissect the following messages:
*
* - SSLv3/TLS (These need more state from previous handshake msgs)
* - Server Key Exchange
* - Client Key Exchange
* - Certificate Verify
*
* - SSLv2 (These don't appear in the clear)
* - Error
* - Client Finished
* - Server Verify
* - Server Finished
* - Request Certificate
* - Client Certificate
*
* - Decryption is supported only for session that use RSA key exchange,
* if the host private key is provided via preference.
*
* - Decryption need to be performed 'sequentially', so it's done
* at packet reception time. This may cause a significative packet capture
* slow down. This also cause do dissect some ssl info that in previous
* dissector version were dissected only when a proto_tree context was
* available
*
* We are at Packet reception if time pinfo->fd->flags.visited == 0
*
*/

[Portion of code removed by course developer; see ssl-c.txt in the \misc directory of the Student
Supplement DVD.]

WSU04: Network Forensics and Security – Appendix C Page C-3


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

/*********************************************************************
*

* Main dissector
*
*********************************************************************/
/*
* Code to actually dissect the packets
*/
static void
dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{

conversation_t *conversation;
void *conv_data;
proto_item *ti;
proto_tree *ssl_tree;
guint32 offset;
gboolean first_record_in_frame;
gboolean need_desegmentation;
SslDecryptSession* ssl_session;
guint* conv_version;
ti = NULL;
ssl_tree = NULL;
offset = 0;
first_record_in_frame = TRUE;
ssl_session = NULL;

ssl_debug_printf("\ndissect_ssl enter frame #%u (%s)\n", pinfo->fd->num, (pinfo->fd-


>flags.visited)?"already visited":"first time");

/* Track the version using conversations to reduce the


* chance that a packet that simply *looks* like a v2 or
* v3 packet is dissected improperly. This also allows
* us to more frequently set the protocol column properly
* for continuation data frames.
*
* Also: We use the copy in conv_version as our cached copy,
* so that we don't have to search the conversation
* table every time we want the version; when setting
* the conv_version, must set the copy in the conversation
* in addition to conv_version
*/
conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo-
>ptype,
pinfo->srcport, pinfo->destport, 0);

if (!conversation)
{
/* create a new conversation */
conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo-
>ptype,
pinfo->srcport, pinfo->destport, 0);
}
conv_data = conversation_get_proto_data(conversation, proto_ssl);

/* PAOLO: manage ssl decryption data */


/*get a valid ssl session pointer*/
if (conv_data != NULL)
ssl_session = conv_data;
else {
SslService dummy;

ssl_session = se_alloc0(sizeof(SslDecryptSession));
ssl_session_init(ssl_session);
ssl_session->version = SSL_VER_UNKNOWN;
conversation_add_proto_data(conversation, proto_ssl, ssl_session);

WSU04: Network Forensics and Security – Appendix C Page C-4


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

/* we need to know witch side of conversation is speaking*/


if (ssl_packet_from_server(ssl_associations, pinfo->srcport, pinfo->ptype ==
PT_TCP)) {
dummy.addr = pinfo->src;
dummy.port = pinfo->srcport;
}
else {
dummy.addr = pinfo->dst;
dummy.port = pinfo->destport;
}
ssl_debug_printf("dissect_ssl server %hhu.%hhu.%hhu.%hhu:%u\n",
dummy.addr.data[0],
dummy.addr.data[1],dummy.addr.data[2],
dummy.addr.data[3],dummy.port);

/* try to retrive private key for this service. Do it now 'cause pinfo
* is not always available
* Note that with HAVE_LIBGNUTLS undefined private_key is allways 0
* and thus decryption never engaged*/
ssl_session->private_key = g_hash_table_lookup(ssl_key_hash, &dummy);
if (!ssl_session->private_key)
ssl_debug_printf("dissect_ssl can't find private key for this server!\n");
}
conv_version= & ssl_session->version;

/* try decryption only the first time we see this packet


* (to keep cipher syncronized)and only if we have
* the server private key*/
if (pinfo->fd->flags.visited)
ssl_session = NULL;

/* Initialize the protocol column; we'll set it later when we


* figure out what flavor of SSL it is (assuming we don't
* throw an exception before we get the chance to do so). */
if (check_col(pinfo->cinfo, COL_PROTOCOL))
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SSL");
}
/* clear the the info column */
if (check_col(pinfo->cinfo, COL_INFO))
col_clear(pinfo->cinfo, COL_INFO);

/* TCP packets and SSL records are orthogonal.


* A tcp packet may contain multiple ssl records and an ssl
* record may be spread across multiple tcp packets.
*
* This loop accounts for multiple ssl records in a single
* frame, but not a single ssl record across multiple tcp
* packets.
*
* Handling the single ssl record across multiple packets
* may be possible using wireshark conversations, but
* probably not cleanly. May have to wait for tcp stream
* reassembly.
*/

/* Create display subtree for SSL as a whole */


if (tree)
{
ti = proto_tree_add_item(tree, proto_ssl, tvb, 0, -1, FALSE);
ssl_tree = proto_item_add_subtree(ti, ett_ssl);
}
/* iterate through the records in this tvbuff */
while (tvb_reported_length_remaining(tvb, offset) != 0)
{
/* on second and subsequent records per frame
* add a delimiter on info column
*/
if (!first_record_in_frame
&& check_col(pinfo->cinfo, COL_INFO))
{

WSU04: Network Forensics and Security – Appendix C Page C-5


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

col_append_str(pinfo->cinfo, COL_INFO, ", ");


}

/*
* Assume, for now, that this doesn't need desegmentation.
*/
need_desegmentation = FALSE;

/* first try to dispatch off the cached version


* known to be associated with the conversation
*/
switch(*conv_version) {
case SSL_VER_SSLv2:
case SSL_VER_PCT:
offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
offset, conv_version,
&need_desegmentation,
ssl_session);
break;

case SSL_VER_SSLv3:
case SSL_VER_TLS:
/* the version tracking code works too well ;-)
* at times, we may visit a v2 client hello after
* we already know the version of the connection;
* work around that here by detecting and calling
* the v2 dissector instead
*/
if (ssl_is_v2_client_hello(tvb, offset))
{
offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
offset, conv_version,
&need_desegmentation,
ssl_session);
}
else
{
offset = dissect_ssl3_record(tvb, pinfo, ssl_tree,
offset, conv_version,
&need_desegmentation,
ssl_session,
first_record_in_frame);
}
break;

/* that failed, so apply some heuristics based


* on this individual packet
*/
default:
if (ssl_looks_like_sslv2(tvb, offset))
{
/* looks like sslv2 or pct client hello */
offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
offset, conv_version,
&need_desegmentation,
ssl_session);
}
else if (ssl_looks_like_sslv3(tvb, offset))
{
/* looks like sslv3 or tls */
offset = dissect_ssl3_record(tvb, pinfo, ssl_tree,
offset, conv_version,
&need_desegmentation,
ssl_session,
first_record_in_frame);
}
else
{
/* looks like something unknown, so lump into
* continuation data
*/

WSU04: Network Forensics and Security – Appendix C Page C-6


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

offset = tvb_length(tvb);
if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO,
"Continuation Data");

/* Set the protocol column */


if (check_col(pinfo->cinfo, COL_PROTOCOL))
{
col_set_str(pinfo->cinfo, COL_PROTOCOL,
ssl_version_short_names[*conv_version]);
}
}
break;
}

/* Desegmentation return check */


if (need_desegmentation)
return;
/* set up for next record in frame, if any */
first_record_in_frame = FALSE;
}
if (check_col(pinfo->cinfo, COL_INFO))
col_set_fence(pinfo->cinfo, COL_INFO);
tap_queue_packet(ssl_tap, pinfo, (gpointer)proto_ssl);
}

static gint
decrypt_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
guint32 record_length, guint8 content_type, SslDecryptSession* ssl,
gboolean save_plaintext)
{
gint ret;
gint direction;
StringInfo* data_for_iv;
gint data_for_iv_len;
SslDecoder* decoder;
ret = 0;
/* if we can decrypt and decryption have success
* add decrypted data to this packet info*/
ssl_debug_printf("decrypt_ssl3_record: app_data len %d ssl, state 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;
}
else {
ssl_debug_printf("decrypt_ssl3_record: using client decoder\n");
decoder = &ssl->client;
}

/* ensure we have enough storage space for decrypted data */


if (record_length > ssl_decrypted_data.data_len)
{
ssl_debug_printf("decrypt_ssl3_record: allocating %d bytes"
" for decrypt data (old len %d)\n",
record_length + 32, ssl_decrypted_data.data_len);
ssl_decrypted_data.data = g_realloc(ssl_decrypted_data.data,

WSU04: Network Forensics and Security – Appendix C Page C-7


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

record_length + 32);
ssl_decrypted_data.data_len = record_length + 32;
}

/* run decryption and add decrypted payload to protocol data, if decryption


* is successful*/
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)
ret = 1;
/* */
if (!ret) {
/* save data to update IV if valid 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);
}
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;
}
return ret;
}

void
dissect_ssl_payload(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree,
SslAssociation* association)
{
gboolean save_fragmented;
SslDataInfo *appl_data;
proto_tree *ti;
tvbuff_t *next_tvb;

/* show decrypted data info, if available */


appl_data = ssl_get_data_info(proto_ssl, pinfo, TVB_RAW_OFFSET(tvb)+offset);
if (!appl_data || !appl_data->plain_data.data_len) return;

/* try to dissect decrypted data*/


ssl_debug_printf("dissect_ssl3_record decrypted len %d\n", 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);

/* 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, "Decrypted SSL data");

/* Can we desegment this segment? */


if (FALSE /*ssl_desegment_app_data ignore till implemented well */) {
/* Yes. */
/*desegment_ssl(next_tvb, pinfo, offset, seq, nxtseq, sport, dport, tree,
tcp_tree, tcpd);*/
} 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. */
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));
pinfo->fragmented = save_fragmented;

WSU04: Network Forensics and Security – Appendix C Page C-8


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

}
}

/*********************************************************************
*

* SSL version 3 and TLS Dissection Routines


*
*********************************************************************/
static gint
dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, guint32 offset,
guint *conv_version, gboolean *need_desegmentation,
SslDecryptSession* ssl, gboolean first_record_in_frame)
{

/*
* struct {
* uint8 major, minor;
* } ProtocolVersion;
*
*
* enum {
* change_cipher_spec(20), alert(21), handshake(22),
* application_data(23), (255)
* } ContentType;
*
* struct {
* ContentType type;
* ProtocolVersion version;
* uint16 length;
* opaque fragment[TLSPlaintext.length];
* } TLSPlaintext;
*/
guint32 record_length;
guint16 version;
guint8 content_type;
guint8 next_byte;
proto_tree *ti;
proto_tree *ssl_record_tree;
SslAssociation* association;
guint32 available_bytes;
ti = NULL;
ssl_record_tree = NULL;
available_bytes = 0;

available_bytes = tvb_length_remaining(tvb, offset);

/* TLS 1.0/1.1 just ignores unknown records - RFC 2246 chapter 6. The TLS Record
Protocol */
if ((*conv_version==SSL_VER_TLS || *conv_version==SSL_VER_TLSv1DOT1) &&
(available_bytes >=1 ) && !ssl_is_valid_content_type(tvb_get_guint8(tvb,
offset))) {
proto_tree_add_text(tree, tvb, offset, available_bytes, "Ignored Unknown Record");
if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO, "Ignored Unknown Record");
if (check_col(pinfo->cinfo, COL_PROTOCOL))
col_set_str(pinfo->cinfo, COL_PROTOCOL, ssl_version_short_names[*conv_version]);
return offset + available_bytes;
}

/*
* Can we do reassembly?
*/
if (ssl_desegment && pinfo->can_desegment) {
/*
* Yes - is the record header split across segment boundaries?
*/
if (available_bytes < 5) {
WSU04: Network Forensics and Security – Appendix C Page C-9
© 2007 Protocol Analysis Institute, Inc.
Wireshark University

/*
* Yes. Tell the TCP dissector where the data for this
* message starts in the data it handed us, and how many
* more bytes we need, and return.
*/
pinfo->desegment_offset = offset;
pinfo->desegment_len = 5 - available_bytes;
*need_desegmentation = TRUE;
return offset;
}
}

/*
* Get the record layer fields of interest
*/
content_type = tvb_get_guint8(tvb, offset);
version = tvb_get_ntohs(tvb, offset + 1);
record_length = tvb_get_ntohs(tvb, offset + 3);

if (ssl_is_valid_content_type(content_type)) {

/*
* Can we do reassembly?
*/
if (ssl_desegment && pinfo->can_desegment) {
/*
* Yes - is the record split across segment boundaries?
*/
if (available_bytes < record_length + 5) {
/*
* Yes. Tell the TCP dissector where the data for this
* message starts in the data it handed us, and how many
* more bytes we need, and return.
*/
pinfo->desegment_offset = offset;
pinfo->desegment_len = (record_length + 5) - available_bytes;
*need_desegmentation = TRUE;
return offset;
}
}

} else {

/* if we don't have a valid content_type, there's no sense


* continuing any further
*/
if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO, "Continuation Data");

/* Set the protocol column */


if (check_col(pinfo->cinfo, COL_PROTOCOL))
{
col_set_str(pinfo->cinfo, COL_PROTOCOL,
ssl_version_short_names[*conv_version]);
}
return offset + 5 + record_length;
}

/*
* If GUI, fill in record layer part of tree
*/
if (tree)
{

/* add the record layer subtree header */


tvb_ensure_bytes_exist(tvb, offset, 5 + record_length);
ti = proto_tree_add_item(tree, hf_ssl_record, tvb,
offset, 5 + record_length, 0);
ssl_record_tree = proto_item_add_subtree(ti, ett_ssl_record);
}
if (ssl_record_tree)

WSU04: Network Forensics and Security – Appendix C Page C-10


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

/* show the one-byte content type */


proto_tree_add_item(ssl_record_tree, hf_ssl_record_content_type,
tvb, offset, 1, 0);
offset++;

/* add the version */


proto_tree_add_item(ssl_record_tree, hf_ssl_record_version, tvb,
offset, 2, FALSE);
offset += 2;

/* add the length */


proto_tree_add_uint(ssl_record_tree, hf_ssl_record_length, tvb,
offset, 2, record_length);
offset += 2; /* move past length field itself */
}
else
{
/* if no GUI tree, then just skip over those fields */
offset += 5;
}

/*
* if we don't already have a version set for this conversation,
* but this message's version is authoritative (i.e., it's
* not client_hello, then save the version to to conversation
* structure and print the column version
*/
next_byte = tvb_get_guint8(tvb, offset);
if (*conv_version == SSL_VER_UNKNOWN
&& ssl_is_authoritative_version_message(content_type, next_byte))
{
if (version == SSLV3_VERSION)
{
*conv_version = SSL_VER_SSLv3;
if (ssl) {
ssl->version_netorder = version;
ssl->state |= SSL_VERSION;
ssl_debug_printf("dissect_ssl3_record found version 0x%04X -> state
0x%02X\n", ssl->version_netorder, ssl->state);
}
/*ssl_set_conv_version(pinfo, ssl->version);*/
}
else if (version == TLSV1_VERSION)
{

*conv_version = SSL_VER_TLS;
if (ssl) {
ssl->version_netorder = version;
ssl->state |= SSL_VERSION;
ssl_debug_printf("dissect_ssl3_record found version 0x%04X -> state
0x%02X\n", ssl->version_netorder, ssl->state);
}
/*ssl_set_conv_version(pinfo, ssl->version);*/
}
else if (version == TLSV1DOT1_VERSION)
{

*conv_version = SSL_VER_TLSv1DOT1;
if (ssl) {
ssl->version_netorder = version;
ssl->state |= SSL_VERSION;
ssl_debug_printf("dissect_ssl3_record found version 0x%04X -> state
0x%02X\n", ssl->version_netorder, ssl->state);
}
/*ssl_set_conv_version(pinfo, ssl->version);*/
}
}
if (check_col(pinfo->cinfo, COL_PROTOCOL))

WSU04: Network Forensics and Security – Appendix C Page C-11


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

{
col_set_str(pinfo->cinfo, COL_PROTOCOL,
ssl_version_short_names[*conv_version]);
}

/*
* now dissect the next layer
*/
ssl_debug_printf("dissect_ssl3_record: content_type %d\n",content_type);

/* PAOLO try to decrypt each record (we must keep ciphers "in sync")
* store plain text only for app data */

switch (content_type) {
case SSL_ID_CHG_CIPHER_SPEC:
if (check_col(pinfo->cinfo, COL_INFO))
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");
break;
case SSL_ID_ALERT:
{
tvbuff_t* decrypted;
decrypted=0;
if (ssl&&decrypt_ssl3_record(tvb, pinfo, offset,
record_length, content_type, ssl, FALSE))
ssl_add_record_info(proto_ssl, pinfo, ssl_decrypted_data.data,
ssl_decrypted_data_avail, offset);

/* try to retrive and use decrypted alert record, if any. */


decrypted = ssl_get_record_info(proto_ssl, pinfo, offset);
if (decrypted)
dissect_ssl3_alert(decrypted, pinfo, ssl_record_tree, 0,
conv_version);
else
dissect_ssl3_alert(tvb, pinfo, ssl_record_tree, offset,
conv_version);
break;
}
case SSL_ID_HANDSHAKE:
{
tvbuff_t* decrypted;
decrypted=0;
/* try to decrypt handshake record, if possible. Store decrypted
* record for later usage. The offset is used as 'key' to itentify
* this record into the packet (we can have multiple handshake records
* in the same frame) */
if (ssl && decrypt_ssl3_record(tvb, pinfo, offset,
record_length, content_type, ssl, FALSE))
ssl_add_record_info(proto_ssl, pinfo, ssl_decrypted_data.data,
ssl_decrypted_data_avail, offset);

/* try to retrive and use decrypted handshake record, if any. */


decrypted = ssl_get_record_info(proto_ssl, pinfo, offset);
if (decrypted) {
/* add desegmented data to the data source list */
add_new_data_source(pinfo, decrypted, "Decrypted SSL record");
dissect_ssl3_handshake(decrypted, pinfo, ssl_record_tree, 0,
decrypted->length, conv_version, ssl, content_type);
} else {
dissect_ssl3_handshake(tvb, pinfo, ssl_record_tree, offset,
record_length, conv_version, ssl, content_type);
}
break;
}
case SSL_ID_APP_DATA:
if (ssl){
decrypt_ssl3_record(tvb, pinfo, offset,
record_length, content_type, ssl, TRUE);
/* if application data desegmentation is allowed and needed */

WSU04: Network Forensics and Security – Appendix C Page C-12


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

/*if(ssl_desegment_app_data && *need_desegmentation)


ssl_desegment_ssl_app_data(ssl,pinfo);
*/
}

/* show on info colum what we are decoding */


if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO, "Application Data");

/* we need dissector information when the selected packet is shown.


* ssl session pointer is NULL at that time, so we can't access
* info cached there*/
association = ssl_association_find(ssl_associations, pinfo->srcport, pinfo->ptype
== PT_TCP);
association = association ? association: ssl_association_find(ssl_associations,
pinfo->destport, pinfo->ptype == PT_TCP);

proto_item_set_text(ssl_record_tree,
"%s Record Layer: %s Protocol: %s",
ssl_version_short_names[*conv_version],
val_to_str(content_type, ssl_31_content_type, "unknown"),
association?association->info:"Application Data");

proto_tree_add_item(ssl_record_tree, hf_ssl_record_appdata, tvb,


offset, record_length, 0);

dissect_ssl_payload(tvb, pinfo, offset, tree, association);

break;

default:
/* shouldn't get here since we check above for valid types */
if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO, "Bad SSLv3 Content Type");
break;
}
offset += record_length; /* skip to end of record */

return offset;
}

/* dissects the change cipher spec procotol, filling in the tree */


static void
dissect_ssl3_change_cipher_spec(tvbuff_t *tvb,
proto_tree *tree, guint32 offset,
guint* conv_version, guint8 content_type)
{
/*
* struct {
* enum { change_cipher_spec(1), (255) } type;
* } ChangeCipherSpec;
*
*/
if (tree)
{
proto_item_set_text(tree,
"%s Record Layer: %s Protocol: Change Cipher Spec",
ssl_version_short_names[*conv_version],
val_to_str(content_type, ssl_31_content_type, "unknown"));
proto_tree_add_item(tree, hf_ssl_change_cipher_spec, tvb,
offset++, 1, FALSE);
}
}

/* dissects the alert message, filling in the tree */


static void
dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, guint32 offset,
guint* conv_version)
{
/* struct {

WSU04: Network Forensics and Security – Appendix C Page C-13


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

* AlertLevel level;
* AlertDescription description;
* } Alert;
*/
proto_tree *ti;
proto_tree *ssl_alert_tree;
const gchar *level;
const gchar *desc;
guint8 byte;
ssl_alert_tree = NULL;
if (tree)
{
ti = proto_tree_add_item(tree, hf_ssl_alert_message, tvb,
offset, 2, 0);
ssl_alert_tree = proto_item_add_subtree(ti, ett_ssl_alert);
}

/*
* set the record layer label
*/

/* first lookup the names for the alert level and description */
byte = tvb_get_guint8(tvb, offset); /* grab the level byte */
level = match_strval(byte, ssl_31_alert_level);

byte = tvb_get_guint8(tvb, offset+1); /* grab the desc byte */


desc = match_strval(byte, ssl_31_alert_description);

/* now set the text in the record layer line */


if (level && desc)
{
if (check_col(pinfo->cinfo, COL_INFO))
col_append_fstr(pinfo->cinfo, COL_INFO,
"Alert (Level: %s, Description: %s)",
level, desc);
}
else
{
if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO, "Encrypted Alert");
}

if (tree)
{
if (level && desc)
{
proto_item_set_text(tree, "%s Record Layer: Alert "
"(Level: %s, Description: %s)",
ssl_version_short_names[*conv_version],
level, desc);
proto_tree_add_item(ssl_alert_tree, hf_ssl_alert_message_level,
tvb, offset++, 1, FALSE);

proto_tree_add_item(ssl_alert_tree, hf_ssl_alert_message_description,
tvb, offset++, 1, FALSE);
}
else
{
proto_item_set_text(tree,
"%s Record Layer: Encrypted Alert",
ssl_version_short_names[*conv_version]);
proto_item_set_text(ssl_alert_tree,
"Alert Message: Encrypted Alert");
}
}
}

/* dissects the handshake protocol, filling the tree */


static void
dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,

WSU04: Network Forensics and Security – Appendix C Page C-14


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

proto_tree *tree, guint32 offset,


guint32 record_length, guint *conv_version,
SslDecryptSession* ssl, guint8 content_type)
{
/* struct {
* HandshakeType msg_type;
* uint24 length;
* select (HandshakeType) {
* case hello_request: HelloRequest;
* case client_hello: ClientHello;
* case server_hello: ServerHello;
* case certificate: Certificate;
* case server_key_exchange: ServerKeyExchange;
* case certificate_request: CertificateRequest;
* case server_hello_done: ServerHelloDone;
* case certificate_verify: CertificateVerify;
* case client_key_exchange: ClientKeyExchange;
* case finished: Finished;
* } body;
* } Handshake;
*/
proto_tree *ti;
proto_tree *ssl_hand_tree;
const gchar *msg_type_str;
guint8 msg_type;
guint32 length;
gboolean first_iteration;
ti = NULL;
ssl_hand_tree = NULL;
msg_type_str = NULL;
first_iteration = TRUE;

/* just as there can be multiple records per packet, there


* can be multiple messages per record as long as they have
* the same content type
*
* we really only care about this for handshake messages
*/

/* set record_length to the max offset */


record_length += offset;
while (offset < record_length)
{
msg_type = tvb_get_guint8(tvb, offset);
msg_type_str = match_strval(msg_type, ssl_31_handshake_type);
length = tvb_get_ntoh24(tvb, offset + 1);

ssl_debug_printf("dissect_ssl3_handshake iteration %d type %d offset %d length %d


"
"bytes, remaining %d \n", first_iteration, msg_type, offset, length,
record_length);
if (!msg_type_str && !first_iteration)
{
/* only dissect / report messages if they're
* either the first message in this record
* or they're a valid message type
*/
return;
}

/* on second and later iterations, add comma to info col */


if (!first_iteration)
{
if (check_col(pinfo->cinfo, COL_INFO))
col_append_fstr(pinfo->cinfo, COL_INFO, ", ");
}

/*
* Update our info string
*/
if (check_col(pinfo->cinfo, COL_INFO))

WSU04: Network Forensics and Security – Appendix C Page C-15


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

col_append_fstr(pinfo->cinfo, COL_INFO, "%s", (msg_type_str != NULL)


? msg_type_str : "Encrypted Handshake Message");

if (tree)
{
/* set the label text on the record layer expanding node */
if (first_iteration)
{
proto_item_set_text(tree, "%s Record Layer: %s Protocol: %s",
ssl_version_short_names[*conv_version],
val_to_str(content_type, ssl_31_content_type,
"unknown"),
(msg_type_str!=NULL) ? msg_type_str :
"Encrypted Handshake Message");
}
else
{
proto_item_set_text(tree, "%s Record Layer: %s Protocol: %s",
ssl_version_short_names[*conv_version],
val_to_str(content_type, ssl_31_content_type,
"unknown"),
"Multiple Handshake Messages");
}

/* add a subtree for the handshake protocol */


ti = proto_tree_add_item(tree, hf_ssl_handshake_protocol, tvb,
offset, length + 4, 0);
ssl_hand_tree = proto_item_add_subtree(ti, ett_ssl_handshake);

if (ssl_hand_tree)
{
/* set the text label on the subtree node */
proto_item_set_text(ssl_hand_tree, "Handshake Protocol: %s",
(msg_type_str != NULL) ? msg_type_str :
"Encrypted Handshake Message");
}
}

/* if we don't have a valid handshake type, just quit dissecting */


if (!msg_type_str)
return;

/* PAOLO: if we are doing ssl decryption we must dissect some requests type */
if (ssl_hand_tree || ssl)
{
/* add nodes for the message type and message length */
if (ssl_hand_tree)
proto_tree_add_item(ssl_hand_tree, hf_ssl_handshake_type,
tvb, offset, 1, msg_type);
offset++;
if (ssl_hand_tree)
proto_tree_add_uint(ssl_hand_tree, hf_ssl_handshake_length,
tvb, offset, 3, length);
offset += 3;

/* now dissect the handshake message, if necessary */


switch (msg_type) {
case SSL_HND_HELLO_REQUEST:
/* hello_request has no fields, so nothing to do! */
break;

case SSL_HND_CLIENT_HELLO:
dissect_ssl3_hnd_cli_hello(tvb, ssl_hand_tree, offset, length, ssl);
break;

case SSL_HND_SERVER_HELLO:
dissect_ssl3_hnd_srv_hello(tvb, ssl_hand_tree, offset, length, ssl);
break;

case SSL_HND_CERTIFICATE:
dissect_ssl3_hnd_cert(tvb, ssl_hand_tree, offset, pinfo);

WSU04: Network Forensics and Security – Appendix C Page C-16


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

break;

case SSL_HND_SERVER_KEY_EXCHG:
/* unimplemented */
break;

case SSL_HND_CERT_REQUEST:
dissect_ssl3_hnd_cert_req(tvb, ssl_hand_tree, offset);
break;

case SSL_HND_SVR_HELLO_DONE:
/* server_hello_done has no fields, so nothing to do! */
break;

case SSL_HND_CERT_VERIFY:
/* unimplemented */
break;

case SSL_HND_CLIENT_KEY_EXCHG:
{
/* PAOLO: here we can have all the data to build session key*/
StringInfo encrypted_pre_master;
gint ret;
guint encrlen, skip;
encrlen = length;
skip = 0;

if (!ssl)
break;

/* check for required session data */


ssl_debug_printf("dissect_ssl3_handshake found
SSL_HND_CLIENT_KEY_EXCHG state 0x%X\n",
ssl->state);
if ((ssl->state &
(SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION)) !=
(SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION))
{
ssl_debug_printf("dissect_ssl3_handshake not enough data to
generate key (required 0x%02X)\n",

(SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION));
break;
}

/* get encrypted data, on tls1 we have to skip two bytes


* (it's the encrypted len and should be equal to record len - 2)
*/
if (ssl->version == SSL_VER_TLS||ssl->version == SSL_VER_TLSv1DOT1)
{
encrlen = tvb_get_ntohs(tvb, offset);
skip = 2;
if (encrlen > length - 2)
{
ssl_debug_printf("dissect_ssl3_handshake wrong encrypted
length (%d max %d)\n",
encrlen, length);
break;
}
}
encrypted_pre_master.data = se_alloc(encrlen);
encrypted_pre_master.data_len = encrlen;
tvb_memcpy(tvb, encrypted_pre_master.data, offset+skip, encrlen);

if (!ssl->private_key) {
ssl_debug_printf("dissect_ssl3_handshake can't find private
key\n");
break;
}

/* go with ssl key processessing; encrypted_pre_master

WSU04: Network Forensics and Security – Appendix C Page C-17


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

* will be used for master secret store*/


ret = ssl_decrypt_pre_master_secret(ssl, &encrypted_pre_master, ssl-
>private_key);
if (ret < 0) {
ssl_debug_printf("dissect_ssl3_handshake can't decrypt pre master
secret\n");
break;
}
if (ssl_generate_keyring_material(ssl)<0) {
ssl_debug_printf("dissect_ssl3_handshake can't generate keyring
material\n");
break;
}
ssl->state |= SSL_HAVE_SESSION_KEY;
ssl_save_session(ssl, ssl_session_hash);
ssl_debug_printf("dissect_ssl3_handshake session keys succesfully
generated\n");
}
break;

case SSL_HND_FINISHED:
dissect_ssl3_hnd_finished(tvb, ssl_hand_tree,
offset, conv_version);
break;
}

}
else
offset += 4; /* skip the handshake header when handshake is not
processed*/

offset += length;
first_iteration = FALSE; /* set up for next pass, if any */
}
}

static gint
dissect_ssl3_hnd_hello_common(tvbuff_t *tvb, proto_tree *tree,
guint32 offset, SslDecryptSession* ssl, gint from_server)
{
/* show the client's random challenge */
nstime_t gmt_unix_time;
guint8 session_id_length;
proto_item *ti_rnd;
proto_tree *ssl_rnd_tree;

session_id_length = 0;

if (ssl)
{
/* PAOLO: get proper peer information*/
StringInfo* rnd;
if (from_server)
rnd = &ssl->server_random;
else
rnd = &ssl->client_random;

/* get provided random for keyring generation*/


tvb_memcpy(tvb, rnd->data, offset, 32);
rnd->data_len = 32;
if (from_server)
ssl->state |= SSL_SERVER_RANDOM;
else
ssl->state |= SSL_CLIENT_RANDOM;
ssl_debug_printf("dissect_ssl3_hnd_hello_common found %s RANDOM -> state
0x%02X\n",
(from_server)?"SERVER":"CLIENT", ssl->state);

session_id_length = tvb_get_guint8(tvb, offset + 32);


/* check stored session id info */
if (from_server && (session_id_length == ssl->session_id.data_len) &&

WSU04: Network Forensics and Security – Appendix C Page C-18


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

(tvb_memeql(tvb, offset+33, ssl->session_id.data, session_id_length) ==


0))
{
/* clinet/server id match: try to restore a previous cached session*/
ssl_restore_session(ssl, ssl_session_hash);
}
else {
tvb_memcpy(tvb,ssl->session_id.data, offset+33, session_id_length);
ssl->session_id.data_len = session_id_length;
}
}

if (tree)
{
ti_rnd = proto_tree_add_text(tree, tvb, offset, 32, "Random");
ssl_rnd_tree = proto_item_add_subtree(ti_rnd, ett_ssl_random);

/* show the time */


gmt_unix_time.secs = tvb_get_ntohl(tvb, offset);
gmt_unix_time.nsecs = 0;
proto_tree_add_time(ssl_rnd_tree, hf_ssl_handshake_random_time,
tvb, offset, 4, &gmt_unix_time);
offset += 4;

/* show the random bytes */


proto_tree_add_item(ssl_rnd_tree, hf_ssl_handshake_random_bytes,
tvb, offset, 28, FALSE);
offset += 28;

/* show the session id */


session_id_length = tvb_get_guint8(tvb, offset);
proto_tree_add_item(tree, hf_ssl_handshake_session_id_len,
tvb, offset++, 1, 0);
if (session_id_length > 0)
{
tvb_ensure_bytes_exist(tvb, offset, session_id_length);
proto_tree_add_bytes(tree, hf_ssl_handshake_session_id,
tvb, offset, session_id_length,
tvb_get_ptr(tvb, offset, session_id_length));
offset += session_id_length;
}

/* XXXX */
return session_id_length+33;
}

static gint
dissect_ssl3_hnd_hello_ext(tvbuff_t *tvb,
proto_tree *tree, guint32 offset, guint32 left)
{
guint16 extension_length;
guint16 ext_type;
guint16 ext_len;
proto_item *pi;
proto_tree *ext_tree;

if (left < 2)
return offset;

extension_length = tvb_get_ntohs(tvb, offset);


proto_tree_add_uint(tree, hf_ssl_handshake_extensions_len,
tvb, offset, 2, extension_length);
offset += 2;
left -= 2;

while (left >= 4)


{
ext_type = tvb_get_ntohs(tvb, offset);
ext_len = tvb_get_ntohs(tvb, offset + 2);

WSU04: Network Forensics and Security – Appendix C Page C-19


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

pi = proto_tree_add_text(tree, tvb, offset, 4 + ext_len,


"Extension: %s",
val_to_str(ext_type,
tls_hello_extension_types,
"Unknown %u"));
ext_tree = proto_item_add_subtree(pi, ett_ssl_extension);
if (!ext_tree)
ext_tree = tree;

proto_tree_add_uint(ext_tree, hf_ssl_handshake_extension_type,
tvb, offset, 2, ext_type);
offset += 2;

proto_tree_add_uint(ext_tree, hf_ssl_handshake_extension_len,
tvb, offset, 2, ext_len);
offset += 2;

proto_tree_add_bytes_format(ext_tree, hf_ssl_handshake_extension_data,
tvb, offset, ext_len,
tvb_get_ptr(tvb, offset, ext_len),
"Data (%u byte%s)",
ext_len, plurality(ext_len, "", "s"));
offset += ext_len;
left -= 2 + 2 + ext_len;
}

return offset;
}

static void
dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
proto_tree *tree, guint32 offset, guint32 length,
SslDecryptSession*ssl)
{
/* struct {
* ProtocolVersion client_version;
* Random random;
* SessionID session_id;
* CipherSuite cipher_suites<2..2^16-1>;
* CompressionMethod compression_methods<1..2^8-1>;
* Extension client_hello_extension_list<0..2^16-1>;
* } ClientHello;
*
*/
proto_tree *ti;
proto_tree *cs_tree;
guint16 cipher_suite_length;
guint8 compression_methods_length;
guint8 compression_method;
guint16 start_offset;
cipher_suite_length = 0;
compression_methods_length = 0;
start_offset = offset;

if (tree || ssl)
{
/* show the client version */
if (tree)
proto_tree_add_item(tree, hf_ssl_handshake_client_version, tvb,
offset, 2, FALSE);
offset += 2;

/* show the fields in common with server hello */


offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset, ssl, 0);

/* tell the user how many cipher suites there are */


cipher_suite_length = tvb_get_ntohs(tvb, offset);
if (!tree)
return;
proto_tree_add_uint(tree, hf_ssl_handshake_cipher_suites_len,

WSU04: Network Forensics and Security – Appendix C Page C-20


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

tvb, offset, 2, cipher_suite_length);


offset += 2; /* skip opaque length */

if (cipher_suite_length > 0)
{
tvb_ensure_bytes_exist(tvb, offset, cipher_suite_length);
ti = proto_tree_add_none_format(tree,
hf_ssl_handshake_cipher_suites,
tvb, offset, cipher_suite_length,
"Cipher Suites (%u suite%s)",
cipher_suite_length / 2,
plurality(cipher_suite_length/2, "", "s"));

/* make this a subtree */


cs_tree = proto_item_add_subtree(ti, ett_ssl_cipher_suites);
if (!cs_tree)
{
cs_tree = tree; /* failsafe */
}

while (cipher_suite_length > 0)


{
proto_tree_add_item(cs_tree, hf_ssl_handshake_cipher_suite,
tvb, offset, 2, FALSE);
offset += 2;
cipher_suite_length -= 2;
}
}

/* tell the user how man compression methods there are */


compression_methods_length = tvb_get_guint8(tvb, offset);
proto_tree_add_uint(tree, hf_ssl_handshake_comp_methods_len,
tvb, offset, 1, compression_methods_length);
offset++;

if (compression_methods_length > 0)
{
tvb_ensure_bytes_exist(tvb, offset, compression_methods_length);
ti = proto_tree_add_none_format(tree,
hf_ssl_handshake_comp_methods,
tvb, offset, compression_methods_length,
"Compression Methods (%u method%s)",
compression_methods_length,
plurality(compression_methods_length,
"", "s"));

/* make this a subtree */


cs_tree = proto_item_add_subtree(ti, ett_ssl_comp_methods);
if (!cs_tree)
{
cs_tree = tree; /* failsafe */
}

while (compression_methods_length > 0)


{
compression_method = tvb_get_guint8(tvb, offset);
if (compression_method < 64)
proto_tree_add_uint(cs_tree, hf_ssl_handshake_comp_method,
tvb, offset, 1, compression_method);
else if (compression_method > 63 && compression_method < 193)
proto_tree_add_text(cs_tree, tvb, offset, 1,
"Compression Method: Reserved - to be assigned by IANA (%u)",
compression_method);
else
proto_tree_add_text(cs_tree, tvb, offset, 1,
"Compression Method: Private use range (%u)",
compression_method);
offset++;
compression_methods_length--;
}
}

WSU04: Network Forensics and Security – Appendix C Page C-21


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

if (length > offset - start_offset)


{
offset = dissect_ssl3_hnd_hello_ext(tvb, tree, offset,
length -
(offset - start_offset));
}
}
}

static void
dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb,
proto_tree *tree, guint32 offset, guint32 length,
SslDecryptSession* ssl)
{
/* struct {
* ProtocolVersion server_version;
* Random random;
* SessionID session_id;
* CipherSuite cipher_suite;
* CompressionMethod compression_method;
* Extension server_hello_extension_list<0..2^16-1>;
* } ServerHello;
*/
guint16 start_offset;
start_offset = offset;

if (tree || ssl)
{
/* show the server version */
if (tree)
proto_tree_add_item(tree, hf_ssl_handshake_server_version, tvb,
offset, 2, FALSE);
offset += 2;

/* first display the elements conveniently in


* common with client hello
*/
offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset, ssl, 1);

/* PAOLO: handle session cipher suite */


if (ssl) {
/* store selected cipher suite for decryption */
ssl->cipher = tvb_get_ntohs(tvb, offset);
if (ssl_find_cipher(ssl->cipher,&ssl->cipher_suite) < 0) {
ssl_debug_printf("dissect_ssl3_hnd_srv_hello can't find cipher suite
0x%X\n", ssl->cipher);
goto no_cipher;
}

ssl->state |= SSL_CIPHER;
ssl_debug_printf("dissect_ssl3_hnd_srv_hello found CIPHER 0x%04X -> state
0x%02X\n",
ssl->cipher, ssl->state);

/* if we have restored a session now we can have enought material


* to build session key, check it out*/
if ((ssl->state &

(SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET)) !=

(SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET)) {
ssl_debug_printf("dissect_ssl3_hnd_srv_hello not enough data to generate
key (required 0x%02X)\n",

(SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET));
goto no_cipher;
}

ssl_debug_printf("dissect_ssl3_hnd_srv_hello trying to generate keys\n");


if (ssl_generate_keyring_material(ssl)<0) {

WSU04: Network Forensics and Security – Appendix C Page C-22


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

ssl_debug_printf("dissect_ssl3_hnd_srv_hello can't generate keyring


material\n");
goto no_cipher;
}
ssl->state |= SSL_HAVE_SESSION_KEY;
}
no_cipher:
if (!tree)
return;

/* now the server-selected cipher suite */


proto_tree_add_item(tree, hf_ssl_handshake_cipher_suite,
tvb, offset, 2, FALSE);
offset += 2;

/* and the server-selected compression method */


proto_tree_add_item(tree, hf_ssl_handshake_comp_method,
tvb, offset, 1, FALSE);
offset++;

if (length > offset - start_offset)


{
offset = dissect_ssl3_hnd_hello_ext(tvb, tree, offset,
length -
(offset - start_offset));
}
}
}

static void
dissect_ssl3_hnd_cert(tvbuff_t *tvb,
proto_tree *tree, guint32 offset, packet_info *pinfo)
{

/* opaque ASN.1Cert<2^24-1>;
*
* struct {
* ASN.1Cert certificate_list<1..2^24-1>;
* } Certificate;
*/
guint32 certificate_list_length;
proto_tree *ti;
proto_tree *subtree;

if (tree)
{
certificate_list_length = tvb_get_ntoh24(tvb, offset);
proto_tree_add_uint(tree, hf_ssl_handshake_certificates_len,
tvb, offset, 3, certificate_list_length);
offset += 3; /* 24-bit length value */

if (certificate_list_length > 0)
{
tvb_ensure_bytes_exist(tvb, offset, certificate_list_length);
ti = proto_tree_add_none_format(tree,
hf_ssl_handshake_certificates,
tvb, offset, certificate_list_length,
"Certificates (%u byte%s)",
certificate_list_length,
plurality(certificate_list_length,
"", "s"));

/* make it a subtree */
subtree = proto_item_add_subtree(ti, ett_ssl_certs);
if (!subtree)
{
subtree = tree; /* failsafe */
}

/* iterate through each certificate */


while (certificate_list_length > 0)

WSU04: Network Forensics and Security – Appendix C Page C-23


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

{
/* get the length of the current certificate */
guint32 cert_length;
cert_length = tvb_get_ntoh24(tvb, offset);
certificate_list_length -= 3 + cert_length;

proto_tree_add_item(subtree, hf_ssl_handshake_certificate_len,
tvb, offset, 3, FALSE);
offset += 3;

dissect_x509af_Certificate(FALSE, tvb, offset, pinfo, subtree,


hf_ssl_handshake_certificate);
offset += cert_length;
}
}

}
}

static void
dissect_ssl3_hnd_cert_req(tvbuff_t *tvb,
proto_tree *tree, guint32 offset)
{
/*
* enum {
* rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
* (255)
* } ClientCertificateType;
*
* opaque DistinguishedName<1..2^16-1>;
*
* struct {
* ClientCertificateType certificate_types<1..2^8-1>;
* DistinguishedName certificate_authorities<3..2^16-1>;
* } CertificateRequest;
*
*/
proto_tree *ti;
proto_tree *subtree;
guint8 cert_types_count;
gint dnames_length;
cert_types_count = 0;
dnames_length = 0;

if (tree)
{
cert_types_count = tvb_get_guint8(tvb, offset);
proto_tree_add_uint(tree, hf_ssl_handshake_cert_types_count,
tvb, offset, 1, cert_types_count);
offset++;

if (cert_types_count > 0)
{
ti = proto_tree_add_none_format(tree,
hf_ssl_handshake_cert_types,
tvb, offset, cert_types_count,
"Certificate types (%u type%s)",
cert_types_count,
plurality(cert_types_count, "", "s"));
subtree = proto_item_add_subtree(ti, ett_ssl_cert_types);
if (!subtree)
{
subtree = tree;
}

while (cert_types_count > 0)


{
proto_tree_add_item(subtree, hf_ssl_handshake_cert_type,
tvb, offset, 1, FALSE);
offset++;
cert_types_count--;

WSU04: Network Forensics and Security – Appendix C Page C-24


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

}
}

dnames_length = tvb_get_ntohs(tvb, offset);


proto_tree_add_uint(tree, hf_ssl_handshake_dnames_len,
tvb, offset, 2, dnames_length);
offset += 2;

if (dnames_length > 0)
{
tvb_ensure_bytes_exist(tvb, offset, dnames_length);
ti = proto_tree_add_none_format(tree,
hf_ssl_handshake_dnames,
tvb, offset, dnames_length,
"Distinguished Names (%d byte%s)",
dnames_length,
plurality(dnames_length, "", "s"));
subtree = proto_item_add_subtree(ti, ett_ssl_dnames);
if (!subtree)
{
subtree = tree;
}

while (dnames_length > 0)


{
/* get the length of the current certificate */
guint16 name_length;
name_length = tvb_get_ntohs(tvb, offset);
dnames_length -= 2 + name_length;

proto_tree_add_item(subtree, hf_ssl_handshake_dname_len,
tvb, offset, 2, FALSE);
offset += 2;

tvb_ensure_bytes_exist(tvb, offset, name_length);


proto_tree_add_bytes_format(subtree,
hf_ssl_handshake_dname,
tvb, offset, name_length,
tvb_get_ptr(tvb, offset, name_length),
"Distinguished Name (%u byte%s)",
name_length,
plurality(name_length, "", "s"));
offset += name_length;
}
}
}

static void
dissect_ssl3_hnd_finished(tvbuff_t *tvb,
proto_tree *tree, guint32 offset,
guint* conv_version)
{
/* For TLS:
* struct {
* opaque verify_data[12];
* } Finished;
*
* For SSLv3:
* struct {
* opaque md5_hash[16];
* opaque sha_hash[20];
* } Finished;
*/

/* this all needs a tree, so bail if we don't have one */


if (!tree)
{
return;
}

WSU04: Network Forensics and Security – Appendix C Page C-25


© 2007 Protocol Analysis Institute, Inc.
Wireshark University

switch(*conv_version) {
case SSL_VER_TLS:
case SSL_VER_TLSv1DOT1:
proto_tree_add_item(tree, hf_ssl_handshake_finished,
tvb, offset, 12, FALSE);
break;

case SSL_VER_SSLv3:
proto_tree_add_item(tree, hf_ssl_handshake_md5_hash,
tvb, offset, 16, FALSE);
offset += 16;
proto_tree_add_item(tree, hf_ssl_handshake_sha_hash,
tvb, offset, 20, FALSE);
offset += 20;
break;
}
}

Note: This is only a portion of the code. The entire code can be found in the \misc
directory on the Student Supplement DVD.

WSU04: Network Forensics and Security – Appendix C Page C-26


© 2007 Protocol Analysis Institute, Inc.

You might also like