* Routines for ssl dissection
* Copyright (c) 2000-2001, Scott Renfro <scott@renfro.org>
*
- * $Id: packet-ssl.c,v 1.5 2001/07/16 05:17:30 guy Exp $
+ * $Id: packet-ssl.c,v 1.22 2002/04/11 09:43:22 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
# include "snprintf.h"
#endif
-#include "conversation.h"
+#include <epan/conversation.h>
+#include "prefs.h"
+
+static gboolean ssl_desegment = TRUE;
+
/*********************************************************************
*
static int hf_ssl2_record_is_escape = -1;
static int hf_ssl2_record_padding_length = -1;
static int hf_ssl2_msg_type = -1;
+static int hf_pct_msg_type = -1;
static int hf_ssl_change_cipher_spec = -1;
static int hf_ssl_alert_message = -1;
static int hf_ssl_alert_message_level = -1;
/* The TCP port to associate with by default */
#define TCP_PORT_SSL 443
+#define TCP_PORT_SSL_LDAP 636
+#define TCP_PORT_SSL_IMAP 993
+#define TCP_PORT_SSL_POP 995
/* version state tables */
#define SSL_VER_UNKNOWN 0
#define SSL_VER_SSLv2 1
#define SSL_VER_SSLv3 2
#define SSL_VER_TLS 3
+#define SSL_VER_PCT 4
/* corresponds to the #defines above */
static gchar* ssl_version_short_names[] = {
"SSLv2",
"SSLv3",
"TLS",
+ "PCT"
};
/* other defines */
#define SSL2_HND_REQUEST_CERTIFICATE 0x07
#define SSL2_HND_CLIENT_CERTIFICATE 0x08
+#define PCT_VERSION_1 0x8001
+
+#define PCT_MSG_CLIENT_HELLO 0x01
+#define PCT_MSG_SERVER_HELLO 0x02
+#define PCT_MSG_CLIENT_MASTER_KEY 0x03
+#define PCT_MSG_SERVER_VERIFY 0x04
+#define PCT_MSG_ERROR 0x05
+
/*
* Lookup tables
*
{ 0x050080, "SSL2_IDEA_128_CBC_WITH_MD5" },
{ 0x060040, "SSL2_DES_64_CBC_WITH_MD5" },
{ 0x0700c0, "SSL2_DES_192_EDE3_CBC_WITH_MD5" },
+ { 0x080080, "SSL2_RC4_64_WITH_MD5" },
{ 0x000000, "TLS_NULL_WITH_NULL_NULL" },
{ 0x000001, "TLS_RSA_WITH_NULL_MD5" },
{ 0x000002, "TLS_RSA_WITH_NULL_SHA" },
{ 0x00001c, "SSL_FORTEZZA_KEA_WITH_NULL_SHA" },
{ 0x00001d, "SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA" },
{ 0x00001e, "SSL_FORTEZZA_KEA_WITH_RC4_128_SHA" },
+ { 0x000060, "TLS_RSA_EXPORT1024_WITH_RC4_56_MD5" },
+ { 0x000061, "TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5" },
{ 0x000062, "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA" },
{ 0x000063, "TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA" },
{ 0x000064, "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA" },
{ 0x000065, "TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA" },
{ 0x000066, "TLS_DHE_DSS_WITH_RC4_128_SHA" },
+ /* these from http://www.mozilla.org/projects/
+ security/pki/nss/ssl/fips-ssl-ciphersuites.html */
+ { 0x00fefe, "SSL_RSA_FIPS_WITH_DES_CBC_SHA"},
+ { 0x00feff, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA" },
+ { 0x00ffe0, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA" },
+ { 0x00ffe1, "SSL_RSA_FIPS_WITH_DES_CBC_SHA"},
+ /* Microsoft's old PCT protocol. These are from Eric Rescorla's
+ book "SSL and TLS" */
+ { 0x8f8001, "PCT_SSL_COMPAT | PCT_VERSION_1" },
+ { 0x800003, "PCT_SSL_CERT_TYPE | PCT1_CERT_X509_CHAIN" },
+ { 0x800001, "PCT_SSL_CERT_TYPE | PCT1_CERT_X509" },
+ { 0x810001, "PCT_SSL_HASH_TYPE | PCT1_HASH_MD5" },
+ { 0x810003, "PCT_SSL_HASH_TYPE | PCT1_HASH_SHA" },
+ { 0x820001, "PCT_SSL_EXCH_TYPE | PCT1_EXCH_RSA_PKCS1" },
+ { 0x830004, "PCT_SSL_CIPHER_TYPE_1ST_HALF | PCT1_CIPHER_RC4" },
+ { 0x848040, "PCT_SSL_CIPHER_TYPE_2ND_HALF | PCT1_ENC_BITS_128 | PCT1_MAC_BITS_128" },
+ { 0x842840, "PCT_SSL_CIPHER_TYPE_2ND_HALF | PCT1_ENC_BITS_40 | PCT1_MAC_BITS_128" },
/* note that ciphersuites of {0x00????} are TLS cipher suites in
* a sslv2 client hello message; the ???? above is the two-byte
* tls cipher suite id
{ 0x0064, "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA" },
{ 0x0065, "TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA" },
{ 0x0066, "TLS_DHE_DSS_WITH_RC4_128_SHA" },
+ /* these from http://www.mozilla.org/projects/
+ security/pki/nss/ssl/fips-ssl-ciphersuites.html */
+ { 0xfefe, "SSL_RSA_FIPS_WITH_DES_CBC_SHA"},
+ { 0xfeff, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA" },
+ { 0xffe0, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA" },
+ { 0xffe1, "SSL_RSA_FIPS_WITH_DES_CBC_SHA"},
/* note that ciphersuites 0xff00 - 0xffff are private */
{ 0x00, NULL }
};
+static const value_string pct_msg_types[] = {
+ { PCT_MSG_CLIENT_HELLO, "Client Hello" },
+ { PCT_MSG_SERVER_HELLO, "Server Hello" },
+ { PCT_MSG_CLIENT_MASTER_KEY, "Client Master Key" },
+ { PCT_MSG_SERVER_VERIFY, "Server Verify" },
+ { PCT_MSG_ERROR, "Error" },
+ { 0x00, NULL },
+};
+
+
/*********************************************************************
*
* Forward Declarations
/* record layer dissector */
static int dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, guint32 offset,
- guint *conv_version);
+ guint *conv_version,
+ gboolean *need_desegmentation);
/* change cipher spec dissector */
-static void dissect_ssl3_change_cipher_spec(tvbuff_t *tvb, packet_info *pinfo,
+static void dissect_ssl3_change_cipher_spec(tvbuff_t *tvb,
proto_tree *tree,
guint32 offset,
guint *conv_version);
guint *conv_version);
-static void dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb, packet_info *pinfo,
+static void dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
proto_tree *tree,
guint32 offset);
-static void dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb, packet_info *pinfo,
+static void dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb,
proto_tree *tree,
guint32 offset);
-static void dissect_ssl3_hnd_cert(tvbuff_t *tvb, packet_info *pinfo,
+static void dissect_ssl3_hnd_cert(tvbuff_t *tvb,
proto_tree *tree, guint32 offset);
-static void dissect_ssl3_hnd_cert_req(tvbuff_t *tvb, packet_info *pinfo,
+static void dissect_ssl3_hnd_cert_req(tvbuff_t *tvb,
proto_tree *tree,
guint32 offset);
-static void dissect_ssl3_hnd_finished(tvbuff_t *tvb, packet_info *pinfo,
+static void dissect_ssl3_hnd_finished(tvbuff_t *tvb,
proto_tree *tree,
guint32 offset,
guint *conv_version);
/* record layer dissector */
static int dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, guint32 offset,
- guint *conv_version);
+ guint *conv_version,
+ gboolean *need_desegmentation);
/* client hello dissector */
-static void dissect_ssl2_hnd_client_hello(tvbuff_t *tvb, packet_info *pinfo,
+static void dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
proto_tree *tree,
guint32 offset);
/* client master key dissector */
static void dissect_ssl2_hnd_client_master_key(tvbuff_t *tvb,
- packet_info *pinfo,
proto_tree *tree,
guint32 offset);
/* server hello dissector */
static void dissect_ssl2_hnd_server_hello(tvbuff_t *tvb,
- packet_info *pinfo,
proto_tree *tree,
guint32 offset);
static int ssl_looks_like_valid_v2_handshake(tvbuff_t *tvb,
guint32 offset,
guint32 record_length);
+static int ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb,
+ guint32 offset,
+ guint32 record_length);
/*********************************************************************
*
{
conversation_t *conversation;
+ void *conv_data;
guint conv_version = SSL_VER_UNKNOWN;
proto_item *ti = NULL;
proto_tree *ssl_tree = NULL;
guint32 offset = 0;
gboolean first_record_in_frame = TRUE;
+ gboolean need_desegmentation;
/* Track the version using conversations to reduce the
* chance that a packet that simply *looks* like a v2 or
{
/* create a new conversation */
conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
- pinfo->srcport, pinfo->destport,
- (void*)SSL_VER_UNKNOWN, 0);
+ pinfo->srcport, pinfo->destport, 0);
}
- if (conversation)
+ conv_data = conversation_get_proto_data(conversation, proto_ssl);
+ if (conv_data != NULL)
{
- conv_version = (guint)conversation->data;
+ conv_version = (guint)conv_data;
}
/* 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->fd, COL_PROTOCOL))
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
{
- col_set_str(pinfo->fd, COL_PROTOCOL, "SSL");
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "SSL");
}
/* clear the the info column */
- if (check_col(pinfo->fd, COL_INFO))
- col_clear(pinfo->fd, COL_INFO);
+ 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
/* Create display subtree for SSL as a whole */
if (tree)
{
- ti = proto_tree_add_item(tree, proto_ssl, tvb,
- 0, tvb_length(tvb), FALSE);
+ 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 frame */
- while (offset < tvb_length(tvb)-1)
+ /* 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->fd, COL_INFO))
+ && check_col(pinfo->cinfo, COL_INFO))
{
- col_append_str(pinfo->fd, COL_INFO, ", ");
+ 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);
+ offset, &conv_version,
+ &need_desegmentation);
break;
case SSL_VER_SSLv3:
if (ssl_is_v2_client_hello(tvb, offset))
{
offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
- offset, &conv_version);
+ offset, &conv_version,
+ &need_desegmentation);
}
else
{
offset = dissect_ssl3_record(tvb, pinfo, ssl_tree,
- offset, &conv_version);
+ offset, &conv_version,
+ &need_desegmentation);
}
break;
default:
if (ssl_looks_like_sslv2(tvb, offset))
{
- /* looks like sslv2 client hello */
+ /* looks like sslv2 or pct client hello */
offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
- offset, &conv_version);
+ offset, &conv_version,
+ &need_desegmentation);
}
else if (ssl_looks_like_sslv3(tvb, offset))
{
/* looks like sslv3 or tls */
offset = dissect_ssl3_record(tvb, pinfo, ssl_tree,
- offset, &conv_version);
+ offset, &conv_version,
+ &need_desegmentation);
}
else
{
* continuation data
*/
offset = tvb_length(tvb);
- if (check_col(pinfo->fd, COL_INFO))
- col_append_str(pinfo->fd, COL_INFO,
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO,
"Continuation Data");
/* Set the protocol column */
- if (check_col(pinfo->fd, COL_PROTOCOL))
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
{
- col_set_str(pinfo->fd, COL_PROTOCOL,
+ col_set_str(pinfo->cinfo, COL_PROTOCOL,
ssl_version_short_names[conv_version]);
}
}
break;
}
+ /* Desegmentation return check */
+ if (need_desegmentation)
+ return;
+
+ /* If we haven't already set the version information for
+ * this conversation, do so. */
+ if (conv_data == NULL)
+ {
+ conv_data = (void *)conv_version;
+ conversation_add_proto_data(conversation, proto_ssl, conv_data);
+ }
+
/* set up for next record in frame, if any */
- conversation->data = (void*)conv_version;
first_record_in_frame = FALSE;
}
static int
dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, guint32 offset,
- guint *conv_version)
+ guint *conv_version, gboolean *need_desegmentation)
{
/*
guint8 next_byte;
proto_tree *ti = NULL;
proto_tree *ssl_record_tree = NULL;
+ guint32 available_bytes = 0;
+
+ available_bytes = tvb_length_remaining(tvb, offset);
+
+ /*
+ * Can we do reassembly?
+ */
+ if (ssl_desegment && pinfo->can_desegment) {
+ /*
+ * Yes - is the record header split across segment boundaries?
+ */
+ if (available_bytes < 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 = 5 - available_bytes;
+ *need_desegmentation = TRUE;
+ return offset;
+ }
+ }
/*
* Get the record layer fields of interest
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 (!ssl_is_valid_content_type(content_type))
- {
- if (check_col(pinfo->fd, COL_INFO))
- col_append_str(pinfo->fd, COL_INFO, "Continuation Data");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO, "Continuation Data");
/* Set the protocol column */
- if (check_col(pinfo->fd, COL_PROTOCOL))
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
{
- col_set_str(pinfo->fd, COL_PROTOCOL,
+ col_set_str(pinfo->cinfo, COL_PROTOCOL,
ssl_version_short_names[*conv_version]);
}
return offset + 5 + record_length;
ssl_set_conv_version(pinfo, *conv_version);
}
}
- if (check_col(pinfo->fd, COL_PROTOCOL))
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
{
if (version == 0x0300)
{
- col_set_str(pinfo->fd, COL_PROTOCOL,
+ col_set_str(pinfo->cinfo, COL_PROTOCOL,
ssl_version_short_names[SSL_VER_SSLv3]);
}
else if (version == 0x0301)
{
- col_set_str(pinfo->fd, COL_PROTOCOL,
+ col_set_str(pinfo->cinfo, COL_PROTOCOL,
ssl_version_short_names[SSL_VER_TLS]);
}
else
{
- col_set_str(pinfo->fd, COL_PROTOCOL,
+ col_set_str(pinfo->cinfo, COL_PROTOCOL,
ssl_version_short_names[*conv_version]);
}
}
*/
switch (content_type) {
case SSL_ID_CHG_CIPHER_SPEC:
- if (check_col(pinfo->fd, COL_INFO))
- col_append_str(pinfo->fd, COL_INFO, "Change Cipher Spec");
- dissect_ssl3_change_cipher_spec(tvb, pinfo, ssl_record_tree,
+ 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);
break;
case SSL_ID_ALERT:
record_length, conv_version);
break;
case SSL_ID_APP_DATA:
- if (check_col(pinfo->fd, COL_INFO))
- col_append_str(pinfo->fd, COL_INFO, "Application Data");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO, "Application Data");
if (ssl_record_tree)
{
proto_item_set_text(ssl_record_tree,
default:
/* shouldn't get here since we check above for valid types */
- if (check_col(pinfo->fd, COL_INFO))
- col_append_str(pinfo->fd, COL_INFO, "Bad SSLv3 Content Type");
+ 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 */
/* dissects the change cipher spec procotol, filling in the tree */
static void
-dissect_ssl3_change_cipher_spec(tvbuff_t *tvb, packet_info *pinfo,
+dissect_ssl3_change_cipher_spec(tvbuff_t *tvb,
proto_tree *tree, guint32 offset,
guint *conv_version)
{
/* now set the text in the record layer line */
if (level && desc)
{
- if (check_col(pinfo->fd, COL_INFO))
- col_append_fstr(pinfo->fd, COL_INFO,
+ 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->fd, COL_INFO))
- col_append_str(pinfo->fd, COL_INFO, "Encrypted Alert");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO, "Encrypted Alert");
}
if (tree)
/* on second and later iterations, add comma to info col */
if (!first_iteration)
{
- if (check_col(pinfo->fd, COL_INFO))
- col_append_fstr(pinfo->fd, COL_INFO, ", ");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_fstr(pinfo->cinfo, COL_INFO, ", ");
}
/*
* Update our info string
*/
- if (check_col(pinfo->fd, COL_INFO))
- col_append_fstr(pinfo->fd, COL_INFO, "%s", (msg_type_str != NULL)
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_fstr(pinfo->cinfo, COL_INFO, "%s", (msg_type_str != NULL)
? msg_type_str : "Encrypted Handshake Message");
if (tree)
break;
case SSL_HND_CLIENT_HELLO:
- dissect_ssl3_hnd_cli_hello(tvb, pinfo, ssl_hand_tree, offset);
+ dissect_ssl3_hnd_cli_hello(tvb, ssl_hand_tree, offset);
break;
case SSL_HND_SERVER_HELLO:
- dissect_ssl3_hnd_srv_hello(tvb, pinfo, ssl_hand_tree, offset);
+ dissect_ssl3_hnd_srv_hello(tvb, ssl_hand_tree, offset);
break;
case SSL_HND_CERTIFICATE:
- dissect_ssl3_hnd_cert(tvb, pinfo, ssl_hand_tree, offset);
+ dissect_ssl3_hnd_cert(tvb, ssl_hand_tree, offset);
break;
case SSL_HND_CERT_REQUEST:
- dissect_ssl3_hnd_cert_req(tvb, pinfo, ssl_hand_tree, offset);
+ dissect_ssl3_hnd_cert_req(tvb, ssl_hand_tree, offset);
break;
case SSL_HND_SVR_HELLO_DONE:
break;
case SSL_HND_FINISHED:
- dissect_ssl3_hnd_finished(tvb, pinfo, ssl_hand_tree,
+ dissect_ssl3_hnd_finished(tvb, ssl_hand_tree,
offset, conv_version);
break;
{
/* show the client's random challenge */
guint32 initial_offset = offset;
- struct timeval gmt_unix_time;
+ nstime_t gmt_unix_time;
guint8 session_id_length = 0;
if (tree)
{
/* show the time */
- gmt_unix_time.tv_sec = tvb_get_ntohl(tvb, offset);
- gmt_unix_time.tv_usec = 0;
+ gmt_unix_time.secs = tvb_get_ntohl(tvb, offset);
+ gmt_unix_time.nsecs = 0;
proto_tree_add_time(tree, hf_ssl_handshake_random_time,
tvb, offset, 4, &gmt_unix_time);
offset += 4;
}
static void
-dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb, packet_info *pinfo,
+dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
proto_tree *tree, guint32 offset)
{
/* struct {
}
static void
-dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb, packet_info *pinfo,
+dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb,
proto_tree *tree, guint32 offset)
{
/* struct {
}
static void
-dissect_ssl3_hnd_cert(tvbuff_t *tvb, packet_info *pinfo,
+dissect_ssl3_hnd_cert(tvbuff_t *tvb,
proto_tree *tree, guint32 offset)
{
}
static void
-dissect_ssl3_hnd_cert_req(tvbuff_t *tvb, packet_info *pinfo,
+dissect_ssl3_hnd_cert_req(tvbuff_t *tvb,
proto_tree *tree, guint32 offset)
{
/*
}
static void
-dissect_ssl3_hnd_finished(tvbuff_t *tvb, packet_info *pinfo,
+dissect_ssl3_hnd_finished(tvbuff_t *tvb,
proto_tree *tree, guint32 offset,
guint *conv_version)
{
/* record layer dissector */
static int
-dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree
- *tree, guint32 offset, guint *conv_version)
+dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ guint32 offset, guint *conv_version,
+ gboolean *need_desegmentation)
{
guint32 initial_offset = offset;
guint8 byte = 0;
guint8 record_length_length = 0;
- gint32 record_length = -1;
+ guint32 record_length = 0;
gint is_escape = -1;
gint16 padding_length = -1;
guint8 msg_type = 0;
gchar *msg_type_str = NULL;
+ guint32 available_bytes = 0;
proto_tree *ti;
proto_tree *ssl_record_tree = NULL;
- /* if we get here, but don't have a version set for the
- * conversation, then set a version for just this frame
- * (e.g., on a client hello)
- */
- if (check_col(pinfo->fd, COL_PROTOCOL))
- {
- col_set_str(pinfo->fd, COL_PROTOCOL, "SSLv2");
- }
-
/* pull first byte; if high bit is set, then record
* length is three bytes due to padding; otherwise
* record length is two bytes
*/
- byte = tvb_get_guint8(tvb, offset++);
+ byte = tvb_get_guint8(tvb, offset);
record_length_length = (byte & 0x80) ? 2 : 3;
+ /*
+ * Can we do reassembly?
+ */
+ available_bytes = tvb_length_remaining(tvb, offset);
+
+ if (ssl_desegment && pinfo->can_desegment) {
+ /*
+ * Yes - is the record header split across segment boundaries?
+ */
+ if (available_bytes < record_length_length) {
+ /*
+ * 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_length - available_bytes;
+ *need_desegmentation = TRUE;
+ return offset;
+ }
+ }
+
/* parse out the record length */
switch(record_length_length) {
case 2: /* two-byte record length */
record_length = (byte & 0x7f) << 8;
- byte = tvb_get_guint8(tvb, offset++);
+ byte = tvb_get_guint8(tvb, offset + 1);
record_length += byte;
break;
case 3: /* three-byte record length */
is_escape = (byte & 0x40) ? TRUE : FALSE;
record_length = (byte & 0x3f) << 8;
- byte = tvb_get_guint8(tvb, offset++);
+ byte = tvb_get_guint8(tvb, offset + 1);
record_length += byte;
- byte = tvb_get_guint8(tvb, offset++);
+ byte = tvb_get_guint8(tvb, offset + 2);
padding_length = byte;
}
+ /*
+ * Can we do reassembly?
+ */
+ if (ssl_desegment && pinfo->can_desegment) {
+ /*
+ * Yes - is the record split across segment boundaries?
+ */
+ if (available_bytes < (record_length_length + record_length)) {
+ /*
+ * 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_length + record_length)
+ - available_bytes;
+ *need_desegmentation = TRUE;
+ return offset;
+ }
+ }
+ offset += record_length_length;
+
/* add the record layer subtree header */
ti = proto_tree_add_item(tree, hf_ssl2_record, tvb, initial_offset,
record_length_length + record_length, 0);
/* if we get a server_hello or later handshake in v2, then set
* this to sslv2
*/
- if (*conv_version == SSL_VER_UNKNOWN
- && msg_type >= 2 && msg_type <= 8)
+ if (*conv_version == SSL_VER_UNKNOWN)
+ {
+ if (ssl_looks_like_valid_pct_handshake(tvb,
+ (initial_offset +
+ record_length_length),
+ record_length)) {
+ *conv_version = SSL_VER_PCT;
+ ssl_set_conv_version(pinfo, *conv_version);
+ }
+ else if (msg_type >= 2 && msg_type <= 8)
+ {
+ *conv_version = SSL_VER_SSLv2;
+ ssl_set_conv_version(pinfo, *conv_version);
+ }
+ }
+
+ /* if we get here, but don't have a version set for the
+ * conversation, then set a version for just this frame
+ * (e.g., on a client hello)
+ */
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
{
- *conv_version = SSL_VER_SSLv2;
- ssl_set_conv_version(pinfo, *conv_version);
+ col_set_str(pinfo->cinfo, COL_PROTOCOL,
+ (*conv_version == SSL_VER_PCT) ? "PCT" : "SSLv2");
}
/* see if the msg_type is valid; if not the payload is
* probably encrypted, so note that fact and bail
*/
- msg_type_str = match_strval(msg_type, ssl_20_msg_types);
+ msg_type_str = match_strval(msg_type,
+ (*conv_version == SSL_VER_PCT)
+ ? pct_msg_types : ssl_20_msg_types);
if (!msg_type_str
- || !ssl_looks_like_valid_v2_handshake(tvb, initial_offset
- + record_length_length,
- record_length))
+ || ((*conv_version != SSL_VER_PCT) &&
+ !ssl_looks_like_valid_v2_handshake(tvb, initial_offset
+ + record_length_length,
+ record_length))
+ || ((*conv_version == SSL_VER_PCT) &&
+ !ssl_looks_like_valid_pct_handshake(tvb, initial_offset
+ + record_length_length,
+ record_length)))
{
if (ssl_record_tree)
{
- proto_item_set_text(ssl_record_tree, "SSLv2 Record Layer: %s",
+ proto_item_set_text(ssl_record_tree, "%s Record Layer: %s",
+ (*conv_version == SSL_VER_PCT)
+ ? "PCT" : "SSLv2",
"Encrypted Data");
}
- if (check_col(pinfo->fd, COL_INFO))
- col_append_str(pinfo->fd, COL_INFO, "Encrypted Data");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO, "Encrypted Data");
return initial_offset + record_length_length + record_length;
}
else
{
- if (check_col(pinfo->fd, COL_INFO))
- col_append_str(pinfo->fd, COL_INFO, msg_type_str);
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO, msg_type_str);
if (ssl_record_tree)
{
- proto_item_set_text(ssl_record_tree, "SSLv2 Record Layer: %s",
+ proto_item_set_text(ssl_record_tree, "%s Record Layer: %s",
+ (*conv_version == SSL_VER_PCT)
+ ? "PCT" : "SSLv2",
msg_type_str);
}
}
* tree by adding the length, is_escape boolean and padding_length,
* if present in the original packet
*/
- if (ssl_record_tree && record_length != -1)
+ if (ssl_record_tree)
{
/* add the record length */
ti = proto_tree_add_uint (ssl_record_tree,
/* add the message type */
if (ssl_record_tree)
{
- proto_tree_add_item(ssl_record_tree, hf_ssl2_msg_type, tvb,
- offset, 1, 0);
+ proto_tree_add_item(ssl_record_tree,
+ (*conv_version == SSL_VER_PCT)
+ ? hf_pct_msg_type : hf_ssl2_msg_type,
+ tvb, offset, 1, 0);
}
offset++; /* move past msg_type byte */
+ if (*conv_version != SSL_VER_PCT)
+ {
+ /* dissect the message (only handle client hello right now) */
+ switch (msg_type) {
+ case SSL2_HND_CLIENT_HELLO:
+ dissect_ssl2_hnd_client_hello(tvb, ssl_record_tree, offset);
+ break;
- /* dissect the message (only handle client hello right now) */
- switch (msg_type) {
- case SSL2_HND_CLIENT_HELLO:
- dissect_ssl2_hnd_client_hello(tvb, pinfo, ssl_record_tree, offset);
- break;
-
- case SSL2_HND_CLIENT_MASTER_KEY:
- dissect_ssl2_hnd_client_master_key(tvb, pinfo, ssl_record_tree, offset);
- break;
+ case SSL2_HND_CLIENT_MASTER_KEY:
+ dissect_ssl2_hnd_client_master_key(tvb, ssl_record_tree, offset);
+ break;
- case SSL2_HND_SERVER_HELLO:
- dissect_ssl2_hnd_server_hello(tvb, pinfo, ssl_record_tree, offset);
- break;
+ case SSL2_HND_SERVER_HELLO:
+ dissect_ssl2_hnd_server_hello(tvb, ssl_record_tree, offset);
+ break;
- case SSL2_HND_ERROR:
- case SSL2_HND_CLIENT_FINISHED:
- case SSL2_HND_SERVER_VERIFY:
- case SSL2_HND_SERVER_FINISHED:
- case SSL2_HND_REQUEST_CERTIFICATE:
- case SSL2_HND_CLIENT_CERTIFICATE:
- /* unimplemented */
- break;
+ case SSL2_HND_ERROR:
+ case SSL2_HND_CLIENT_FINISHED:
+ case SSL2_HND_SERVER_VERIFY:
+ case SSL2_HND_SERVER_FINISHED:
+ case SSL2_HND_REQUEST_CERTIFICATE:
+ case SSL2_HND_CLIENT_CERTIFICATE:
+ /* unimplemented */
+ break;
- default: /* unknown */
- break;
+ default: /* unknown */
+ break;
+ }
}
+ else
+ {
+ /* dissect the message */
+ switch (msg_type) {
+ case PCT_MSG_CLIENT_HELLO:
+ case PCT_MSG_SERVER_HELLO:
+ case PCT_MSG_CLIENT_MASTER_KEY:
+ case PCT_MSG_SERVER_VERIFY:
+ case PCT_MSG_ERROR:
+ /* unimplemented */
+ break;
-
+ default: /* unknown */
+ break;
+ }
+ }
return (initial_offset + record_length_length + record_length);
}
static void
-dissect_ssl2_hnd_client_hello(tvbuff_t *tvb, packet_info *pinfo,
+dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
proto_tree *tree, guint32 offset)
{
/* struct {
}
static void
-dissect_ssl2_hnd_client_master_key(tvbuff_t *tvb, packet_info *pinfo,
+dissect_ssl2_hnd_client_master_key(tvbuff_t *tvb,
proto_tree *tree, guint32 offset)
{
/* struct {
}
static void
-dissect_ssl2_hnd_server_hello(tvbuff_t *tvb, packet_info *pinfo,
+dissect_ssl2_hnd_server_hello(tvbuff_t *tvb,
proto_tree *tree, guint32 offset)
{
/* struct {
ssl_set_conv_version(packet_info *pinfo, guint version)
{
conversation_t *conversation;
+
+ if (pinfo->fd->flags.visited)
+ {
+ /* We've already processed this frame; no need to do any more
+ * work on it.
+ */
+ return;
+ }
+
conversation = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype,
pinfo->srcport, pinfo->destport, 0);
- if (conversation)
+ if (conversation == NULL)
{
- conversation->data = (void*)version;
+ /* create a new conversation */
+ conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
}
- else
+
+ if (conversation_get_proto_data(conversation, proto_ssl) != NULL)
{
- /* create a new conversation */
- conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
- pinfo->srcport, pinfo->destport, (void*)version, 0);
+ /* get rid of the current data */
+ conversation_delete_proto_data(conversation, proto_ssl);
}
+ conversation_add_proto_data(conversation, proto_ssl, (void *)version);
}
static int
case SSL2_HND_CLIENT_HELLO:
case SSL2_HND_CLIENT_MASTER_KEY:
case SSL2_HND_SERVER_HELLO:
+ case PCT_MSG_CLIENT_MASTER_KEY:
+ case PCT_MSG_ERROR:
return 1;
}
return 0;
return 0;
}
+/* applies a heuristic to determine whether
+ * or not the data beginning at offset looks
+ * like a valid, unencrypted v2 handshake message.
+ * since it isn't possible to completely tell random
+ * data apart from a valid message without state,
+ * we try to help the odds.
+ */
+static int
+ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb, guint32 offset,
+ guint32 record_length)
+{
+ /* first byte should be a msg_type.
+ *
+ * - we know we only see client_hello, client_master_key,
+ * and server_hello in the clear, so check to see if
+ * msg_type is one of those (this gives us a 3 in 2^8
+ * chance of saying yes with random payload)
+ *
+ * - for those three types that we know about, do some
+ * further validation to reduce the chance of an error
+ */
+ guint8 msg_type;
+ guint16 version;
+ guint32 sum;
+
+ /* fetch the msg_type */
+ msg_type = tvb_get_guint8(tvb, offset);
+
+ switch (msg_type) {
+ case PCT_MSG_CLIENT_HELLO:
+ /* version follows msg byte, so verify that this is valid */
+ version = tvb_get_ntohs(tvb, offset+1);
+ return version == PCT_VERSION_1;
+ break;
+
+ case PCT_MSG_SERVER_HELLO:
+ /* version is one byte after msg_type */
+ version = tvb_get_ntohs(tvb, offset+2);
+ return version == PCT_VERSION_1;
+ break;
+
+ case PCT_MSG_CLIENT_MASTER_KEY:
+ /* sum of various length fields must be less than record length */
+ sum = tvb_get_ntohs(tvb, offset + 6); /* clear_key_length */
+ sum += tvb_get_ntohs(tvb, offset + 8); /* encrypted_key_length */
+ sum += tvb_get_ntohs(tvb, offset + 10); /* key_arg_length */
+ sum += tvb_get_ntohs(tvb, offset + 12); /* verify_prelude_length */
+ sum += tvb_get_ntohs(tvb, offset + 14); /* client_cert_length */
+ sum += tvb_get_ntohs(tvb, offset + 16); /* response_length */
+ if (sum > record_length)
+ {
+ return 0;
+ }
+ return 1;
+ break;
+
+ case PCT_MSG_SERVER_VERIFY:
+ /* record is 36 bytes longer than response_length */
+ sum = tvb_get_ntohs(tvb, offset + 34); /* response_length */
+ if ((sum + 36) == record_length)
+ return 1;
+ else
+ return 0;
+ break;
+
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+
/*********************************************************************
*
* Standard Ethereal Protocol Registration and housekeeping
FT_UINT8, BASE_DEC, VALS(ssl_20_msg_types), 0x0,
"SSLv2 handshake message type", HFILL}
},
+ { &hf_pct_msg_type,
+ { "Handshake Message Type", "ssl.pct_handshake.type",
+ FT_UINT8, BASE_DEC, VALS(pct_msg_types), 0x0,
+ "PCT handshake message type", HFILL}
+ },
{ &hf_ssl_record_version,
{ "Version", "ssl.record.version",
FT_UINT16, BASE_HEX, VALS(ssl_versions), 0x0,
"Payload is application data", HFILL }
},
{ & hf_ssl2_record,
- { "SSLv2 Record Header", "ssl.record",
+ { "SSLv2/PCT Record Header", "ssl.record",
FT_NONE, BASE_DEC, NULL, 0x0,
- "SSLv2 record data", HFILL }
+ "SSLv2/PCT record data", HFILL }
},
{ &hf_ssl2_record_is_escape,
{ "Is Escape", "ssl.record.is_escape",
* subtrees used */
proto_register_field_array(proto_ssl, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
+
+ {
+ module_t *ssl_module = prefs_register_protocol(proto_ssl, NULL);
+ prefs_register_bool_preference(ssl_module,
+ "desegment_ssl_records",
+ "Desegment SSL records",
+ "When enabled, SSL records that span multiple TCP segments are desegmented",
+ &ssl_desegment);
+ }
+
+ register_dissector("ssl", dissect_ssl, proto_ssl);
+
}
/* If this dissector uses sub-dissector registration add a registration
void
proto_reg_handoff_ssl(void)
{
- dissector_add("tcp.port", TCP_PORT_SSL, dissect_ssl, proto_ssl);
+ dissector_handle_t ssl_handle;
+
+ ssl_handle = find_dissector("ssl");
+ dissector_add("tcp.port", TCP_PORT_SSL, ssl_handle);
+ dissector_add("tcp.port", TCP_PORT_SSL_LDAP, ssl_handle);
+ dissector_add("tcp.port", TCP_PORT_SSL_IMAP, ssl_handle);
+ dissector_add("tcp.port", TCP_PORT_SSL_POP, ssl_handle);
}