aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-ssh.c
diff options
context:
space:
mode:
authorJeff Morriss <jeff.morriss.ws@gmail.com>2013-08-07 22:20:49 +0000
committerJeff Morriss <jeff.morriss.ws@gmail.com>2013-08-07 22:20:49 +0000
commitc44883cb1aa0f9dcdbd681d31894941dc2ea71b1 (patch)
tree909a61731f9d746900fb1f4303dd8bc9af50e942 /epan/dissectors/packet-ssh.c
parent07ee14d5c1cbe0328f46a174331bf245996d9918 (diff)
From Роман Донченко via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9018 :
This patch enables Wireshark to correctly decode messages specific to the selected key exchange method by determining and remembering that method. It's a little incomplete in that it doesn't support the case where first_kex_packet_follows is true, but then the current implementation doesn't either. It also adds/fixes dissection for the plain Diffie-Hellman key exchange and DH group exchange messages that were broken/missing, completing support for both of these key exchange methods. svn path=/trunk/; revision=51197
Diffstat (limited to 'epan/dissectors/packet-ssh.c')
-rw-r--r--epan/dissectors/packet-ssh.c247
1 files changed, 180 insertions, 67 deletions
diff --git a/epan/dissectors/packet-ssh.c b/epan/dissectors/packet-ssh.c
index 9c61fa8cc5..3e2e8e9ffd 100644
--- a/epan/dissectors/packet-ssh.c
+++ b/epan/dissectors/packet-ssh.c
@@ -40,6 +40,11 @@
* RFC 4254: The Secure Shell (SSH) Connection Protocol
*
* SSH versions under 2 were never officially standardized.
+ *
+ * Diffie-Hellman Group Exchange is defined in:
+ *
+ * RFC 4419: Diffie-Hellman Group Exchange for
+ * the Secure Shell (SSH) Transport Layer Protocol
*/
/* "SSH" prefixes are for version 2, whereas "SSH1" is for version 1 */
@@ -80,7 +85,10 @@ struct ssh_peer_data {
guint32 frame_key_start;
guint32 frame_key_end;
- /* For all proposals, [0] is client-to-server and [1] is server-to-client. */
+ gchar* kex_proposal;
+
+ /* For all subsequent proposals,
+ [0] is client-to-server and [1] is server-to-client. */
#define CLIENT_TO_SERVER_PROPOSAL 0
#define SERVER_TO_CLIENT_PROPOSAL 1
@@ -98,6 +106,9 @@ struct ssh_peer_data {
struct ssh_flow_data {
guint version;
+ gchar* kex;
+ int (*kex_specific_dissector)(guint8 msg_code, tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree);
+
/* [0] is client's, [1] is server's */
#define CLIENT_PEER_DATA 0
#define SERVER_PEER_DATA 1
@@ -117,6 +128,8 @@ static int hf_ssh_padding_string= -1;
static int hf_ssh_mac_string= -1;
static int hf_ssh_msg_code = -1;
static int hf_ssh2_msg_code = -1;
+static int hf_ssh2_kex_dh_msg_code = -1;
+static int hf_ssh2_kex_dh_gex_msg_code = -1;
static int hf_ssh_cookie = -1;
static int hf_ssh_mpint_g= -1;
static int hf_ssh_mpint_p= -1;
@@ -180,9 +193,12 @@ static gboolean ssh_desegment = TRUE;
/* Transport layer: Key exchange method specific (reusable) (30-49) */
#define SSH_MSG_KEXDH_INIT 30
#define SSH_MSG_KEXDH_REPLY 31
-#define SSH_MSG_KEX_DH_GEX_INIT 32
-#define SSH_MSG_KEX_DH_GEX_REPLY 33
-#define SSH_MSG_KEX_DH_GEX_REQUEST 34
+
+#define SSH_MSG_KEX_DH_GEX_REQUEST_OLD 30
+#define SSH_MSG_KEX_DH_GEX_GROUP 31
+#define SSH_MSG_KEX_DH_GEX_INIT 32
+#define SSH_MSG_KEX_DH_GEX_REPLY 33
+#define SSH_MSG_KEX_DH_GEX_REQUEST 34
/* User authentication protocol: generic (50-59) */
#define SSH_MSG_USERAUTH_REQUEST 50
@@ -222,11 +238,6 @@ static const value_string ssh2_msg_vals[] = {
{ SSH_MSG_SERVICE_ACCEPT, "Service Accept" },
{ SSH_MSG_KEXINIT, "Key Exchange Init" },
{ SSH_MSG_NEWKEYS, "New Keys" },
- { SSH_MSG_KEXDH_INIT, "Diffie-Hellman Key Exchange Init" },
- { SSH_MSG_KEXDH_REPLY, "Diffie-Hellman Key Exchange Reply" },
- { SSH_MSG_KEX_DH_GEX_INIT, "Diffie-Hellman GEX Init" },
- { SSH_MSG_KEX_DH_GEX_REPLY, "Diffie-Hellman GEX Reply" },
- { SSH_MSG_KEX_DH_GEX_REQUEST, "Diffie-Hellman GEX Request" },
{ SSH_MSG_USERAUTH_REQUEST, "User Authentication Request" },
{ SSH_MSG_USERAUTH_FAILURE, "User Authentication Failure" },
{ SSH_MSG_USERAUTH_SUCCESS, "User Authentication Success" },
@@ -248,6 +259,21 @@ static const value_string ssh2_msg_vals[] = {
{ 0, NULL }
};
+static const value_string ssh2_kex_dh_msg_vals[] = {
+ { SSH_MSG_KEXDH_INIT, "Diffie-Hellman Key Exchange Init" },
+ { SSH_MSG_KEXDH_REPLY, "Diffie-Hellman Key Exchange Reply" },
+ { 0, NULL }
+};
+
+static const value_string ssh2_kex_dh_gex_msg_vals[] = {
+ { SSH_MSG_KEX_DH_GEX_REQUEST_OLD, "Diffie-Hellman Group Exchange Request (Old)" },
+ { SSH_MSG_KEX_DH_GEX_GROUP, "Diffie-Hellman Group Exchange Group" },
+ { SSH_MSG_KEX_DH_GEX_INIT, "Diffie-Hellman Group Exchange Init" },
+ { SSH_MSG_KEX_DH_GEX_REPLY, "Diffie-Hellman Group Exchange Reply" },
+ { SSH_MSG_KEX_DH_GEX_REQUEST, "Diffie-Hellman Group Exchange Request" },
+ { 0, NULL }
+};
+
static const value_string ssh1_msg_vals[] = {
{SSH1_MSG_NONE, "No Message"},
{SSH1_MSG_DISCONNECT, "Disconnect"},
@@ -274,6 +300,10 @@ static int ssh_dissect_key_exchange(tvbuff_t *tvb, packet_info *pinfo,
struct ssh_flow_data *global_data,
int offset, proto_tree *tree, int is_response,
gboolean *need_desegmentation);
+static int ssh_dissect_kex_dh(guint8 msg_code, tvbuff_t *tvb,
+ packet_info *pinfo, int offset, proto_tree *tree);
+static int ssh_dissect_kex_dh_gex(guint8 msg_code, tvbuff_t *tvb,
+ packet_info *pinfo, int offset, proto_tree *tree);
static int ssh_dissect_protocol(tvbuff_t *tvb, packet_info *pinfo,
struct ssh_flow_data *global_data,
int offset, proto_tree *tree, int is_response, guint *version,
@@ -285,6 +315,7 @@ static proto_item *ssh_proto_tree_add_item(proto_tree *tree, int hfindex, tvbuff
gint start, gint length, guint encoding);
static void ssh_choose_algo(gchar *client, gchar *server, gchar **result);
static void ssh_set_mac_length(struct ssh_peer_data *peer_data);
+static void ssh_set_kex_specific_dissector(struct ssh_flow_data *global_data);
static void
@@ -309,6 +340,7 @@ dissect_ssh(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
if (!global_data) {
global_data = (struct ssh_flow_data *)se_alloc0(sizeof(struct ssh_flow_data));
global_data->version=SSH_VERSION_UNKNOWN;
+ global_data->kex_specific_dissector=ssh_dissect_kex_dh;
global_data->peer_data[CLIENT_PEER_DATA].mac_length=-1;
global_data->peer_data[SERVER_PEER_DATA].mac_length=-1;
@@ -669,76 +701,121 @@ ssh_dissect_key_exchange(tvbuff_t *tvb, packet_info *pinfo,
/* msg_code */
msg_code = tvb_get_guint8(tvb, offset);
- proto_tree_add_item(key_ex_tree, hf_ssh2_msg_code, tvb, offset, 1, ENC_NA);
- col_append_sep_str(pinfo->cinfo, COL_INFO, NULL,
- val_to_str(msg_code, ssh2_msg_vals, "Unknown (%u)"));
- offset += 1;
+ if (msg_code >= 30 && msg_code < 40) {
+ offset = global_data->kex_specific_dissector(msg_code, tvb, pinfo, offset, key_ex_tree);
+ } else {
+ proto_tree_add_item(key_ex_tree, hf_ssh2_msg_code, tvb, offset, 1, ENC_NA);
+ offset += 1;
- /* 16 bytes cookie */
- switch(msg_code)
- {
- case SSH_MSG_KEXINIT:
- if ((peer_data->frame_key_start == 0) || (peer_data->frame_key_start == pinfo->fd->num)) {
- offset = ssh_dissect_key_init(tvb, offset, key_ex_tree, is_response, global_data);
- peer_data->frame_key_start = pinfo->fd->num;
+ col_append_sep_str(pinfo->cinfo, COL_INFO, NULL,
+ val_to_str(msg_code, ssh2_msg_vals, "Unknown (%u)"));
+
+ /* 16 bytes cookie */
+ switch(msg_code)
+ {
+ case SSH_MSG_KEXINIT:
+ if ((peer_data->frame_key_start == 0) || (peer_data->frame_key_start == pinfo->fd->num)) {
+ offset = ssh_dissect_key_init(tvb, offset, key_ex_tree, is_response, global_data);
+ peer_data->frame_key_start = pinfo->fd->num;
+ }
+ break;
+ case SSH_MSG_NEWKEYS:
+ if (peer_data->frame_key_end == 0) {
+ peer_data->frame_key_end = pinfo->fd->num;
+ ssh_choose_algo(global_data->peer_data[CLIENT_PEER_DATA].mac_proposals[is_response],
+ global_data->peer_data[SERVER_PEER_DATA].mac_proposals[is_response],
+ &peer_data->mac);
+ ssh_set_mac_length(peer_data);
+ ssh_choose_algo(global_data->peer_data[CLIENT_PEER_DATA].enc_proposals[is_response],
+ global_data->peer_data[SERVER_PEER_DATA].enc_proposals[is_response],
+ &peer_data->enc);
+ ssh_choose_algo(global_data->peer_data[CLIENT_PEER_DATA].comp_proposals[is_response],
+ global_data->peer_data[SERVER_PEER_DATA].comp_proposals[is_response],
+ &peer_data->comp);
+ }
+ break;
}
+ }
+
+ len = plen+4-padding_length-(offset-last_offset);
+ ssh_proto_tree_add_item(key_ex_tree, hf_ssh_payload, tvb, offset, len, ENC_NA);
+ offset +=len;
+
+ /* padding */
+ ssh_proto_tree_add_item(key_ex_tree, hf_ssh_padding_string, tvb, offset, padding_length, ENC_NA);
+ offset+= padding_length;
+
+ return offset;
+}
+
+static int ssh_dissect_kex_dh(guint8 msg_code, tvbuff_t *tvb,
+ packet_info *pinfo, int offset, proto_tree *tree)
+{
+ proto_tree_add_item(tree, hf_ssh2_kex_dh_msg_code, tvb, offset, 1, ENC_NA);
+ offset += 1;
+
+ col_append_sep_str(pinfo->cinfo, COL_INFO, NULL,
+ val_to_str(msg_code, ssh2_kex_dh_msg_vals, "Unknown (%u)"));
+
+ switch (msg_code) {
+ case SSH_MSG_KEXDH_INIT:
+ offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_mpint_e);
break;
- case SSH_MSG_NEWKEYS:
- if (peer_data->frame_key_end == 0) {
- peer_data->frame_key_end = pinfo->fd->num;
- ssh_choose_algo(global_data->peer_data[CLIENT_PEER_DATA].mac_proposals[is_response],
- global_data->peer_data[SERVER_PEER_DATA].mac_proposals[is_response],
- &peer_data->mac);
- ssh_set_mac_length(peer_data);
- ssh_choose_algo(global_data->peer_data[CLIENT_PEER_DATA].enc_proposals[is_response],
- global_data->peer_data[SERVER_PEER_DATA].enc_proposals[is_response],
- &peer_data->enc);
- ssh_choose_algo(global_data->peer_data[CLIENT_PEER_DATA].comp_proposals[is_response],
- global_data->peer_data[SERVER_PEER_DATA].comp_proposals[is_response],
- &peer_data->comp);
- }
+
+ case SSH_MSG_KEXDH_REPLY:
+ offset += ssh_tree_add_string(tvb, offset, tree, hf_ssh_kexdh_host_key, hf_ssh_kexdh_host_key_length);
+ offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_mpint_f);
+ offset += ssh_tree_add_string(tvb, offset, tree, hf_ssh_kexdh_h_sig, hf_ssh_kexdh_h_sig_length);
break;
- /* DH GEX Request (min/nbits/max) */
- case SSH_MSG_KEX_DH_GEX_REQUEST:
- ssh_proto_tree_add_item(key_ex_tree, hf_ssh_dh_gex_min,
- tvb, offset, 4, ENC_NA);
- offset+=4;
- ssh_proto_tree_add_item(key_ex_tree, hf_ssh_dh_gex_nbits,
- tvb, offset, 4, ENC_NA);
- offset+=4;
- ssh_proto_tree_add_item(key_ex_tree, hf_ssh_dh_gex_max,
- tvb, offset, 4, ENC_NA);
- offset+=4;
+ }
+
+ return offset;
+}
+
+static int ssh_dissect_kex_dh_gex(guint8 msg_code, tvbuff_t *tvb,
+ packet_info *pinfo, int offset, proto_tree *tree)
+{
+ proto_tree_add_item(tree, hf_ssh2_kex_dh_gex_msg_code, tvb, offset, 1, ENC_NA);
+ offset += 1;
+
+ col_append_sep_str(pinfo->cinfo, COL_INFO, NULL,
+ val_to_str(msg_code, ssh2_kex_dh_gex_msg_vals, "Unknown (%u)"));
+
+ switch (msg_code) {
+ case SSH_MSG_KEX_DH_GEX_REQUEST_OLD:
+ ssh_proto_tree_add_item(tree, hf_ssh_dh_gex_nbits, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset += 4;
break;
- /* DH Key Exchange Reply (g/p) */
- case SSH_MSG_KEXDH_REPLY:
- offset+=ssh_tree_add_mpint(tvb, offset, key_ex_tree, hf_ssh_mpint_p);
- offset+=ssh_tree_add_mpint(tvb, offset, key_ex_tree, hf_ssh_mpint_g);
+
+ case SSH_MSG_KEX_DH_GEX_GROUP:
+ offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_mpint_p);
+ offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_mpint_g);
break;
- /* DH GEX Init (e) */
case SSH_MSG_KEX_DH_GEX_INIT:
- offset+=ssh_tree_add_mpint(tvb, offset, key_ex_tree, hf_ssh_mpint_e);
+ offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_mpint_e);
break;
- /* DH GEX Reply (f) */
+
case SSH_MSG_KEX_DH_GEX_REPLY:
- offset+=ssh_tree_add_string(tvb, offset, key_ex_tree, hf_ssh_kexdh_host_key, hf_ssh_kexdh_host_key_length);
- offset+=ssh_tree_add_mpint(tvb, offset, key_ex_tree, hf_ssh_mpint_f);
- offset+=ssh_tree_add_string(tvb, offset, key_ex_tree, hf_ssh_kexdh_h_sig, hf_ssh_kexdh_h_sig_length);
+ offset += ssh_tree_add_string(tvb, offset, tree, hf_ssh_kexdh_host_key, hf_ssh_kexdh_host_key_length);
+ offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_mpint_f);
+ offset += ssh_tree_add_string(tvb, offset, tree, hf_ssh_kexdh_h_sig, hf_ssh_kexdh_h_sig_length);
break;
- }
-
- len = plen+4-padding_length-(offset-last_offset);
- ssh_proto_tree_add_item(key_ex_tree, hf_ssh_payload, tvb, offset, len, ENC_NA);
- offset +=len;
- /* padding */
- ssh_proto_tree_add_item(key_ex_tree, hf_ssh_padding_string, tvb, offset, padding_length, ENC_NA);
- offset+= padding_length;
+ case SSH_MSG_KEX_DH_GEX_REQUEST:
+ ssh_proto_tree_add_item(tree, hf_ssh_dh_gex_min, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset += 4;
+ ssh_proto_tree_add_item(tree, hf_ssh_dh_gex_nbits, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset += 4;
+ ssh_proto_tree_add_item(tree, hf_ssh_dh_gex_max, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset += 4;
+ break;
+ }
return offset;
}
+
static int
ssh_dissect_encrypted_packet(tvbuff_t *tvb, packet_info *pinfo,
struct ssh_peer_data *peer_data,
@@ -861,6 +938,19 @@ ssh_set_mac_length(struct ssh_peer_data *peer_data)
}
}
+static void ssh_set_kex_specific_dissector(struct ssh_flow_data *global_data)
+{
+ const char *kex_name = global_data->kex;
+
+ if (!kex_name) return;
+
+ if (strcmp(kex_name, "diffie-hellman-group-exchange-sha1") == 0 ||
+ strcmp(kex_name, "diffie-hellman-group-exchange-sha256") == 0)
+ {
+ global_data->kex_specific_dissector = ssh_dissect_kex_dh_gex;
+ }
+}
+
static gint
ssh_gslist_compare_strings(gconstpointer a, gconstpointer b)
{
@@ -924,7 +1014,8 @@ ssh_dissect_key_init(tvbuff_t *tvb, int offset, proto_tree *tree,
offset += 16;
offset = ssh_dissect_proposal(tvb, offset, key_init_tree,
- hf_ssh_kex_algorithms_length, hf_ssh_kex_algorithms, NULL);
+ hf_ssh_kex_algorithms_length, hf_ssh_kex_algorithms,
+ &peer_data->kex_proposal);
offset = ssh_dissect_proposal(tvb, offset, key_init_tree,
hf_ssh_server_host_key_algorithms_length,
hf_ssh_server_host_key_algorithms, NULL);
@@ -967,6 +1058,18 @@ ssh_dissect_key_init(tvbuff_t *tvb, int offset, proto_tree *tree,
tvb, offset, 4, ENC_NA);
offset+=4;
+ if (global_data->peer_data[CLIENT_PEER_DATA].kex_proposal &&
+ global_data->peer_data[SERVER_PEER_DATA].kex_proposal &&
+ !global_data->kex)
+ {
+ /* Note: we're ignoring first_kex_packet_follows. */
+ ssh_choose_algo(
+ global_data->peer_data[CLIENT_PEER_DATA].kex_proposal,
+ global_data->peer_data[SERVER_PEER_DATA].kex_proposal,
+ &global_data->kex);
+ ssh_set_kex_specific_dissector(global_data);
+ }
+
if (tf != NULL) {
proto_item_set_len(tf, offset-start_offset);
}
@@ -1025,6 +1128,16 @@ proto_register_ssh(void)
FT_UINT8, BASE_DEC, VALS(ssh2_msg_vals), 0x0,
"SSH Message Code", HFILL }},
+ { &hf_ssh2_kex_dh_msg_code,
+ { "Message Code", "ssh.message_code",
+ FT_UINT8, BASE_DEC, VALS(ssh2_kex_dh_msg_vals), 0x0,
+ "SSH Message Code", HFILL }},
+
+ { &hf_ssh2_kex_dh_gex_msg_code,
+ { "Message Code", "ssh.message_code",
+ FT_UINT8, BASE_DEC, VALS(ssh2_kex_dh_gex_msg_vals), 0x0,
+ "SSH Message Code", HFILL }},
+
{ &hf_ssh_mpint_g,
{ "DH base (G)", "ssh.dh.g",
FT_BYTES, BASE_NONE, NULL, 0x0,
@@ -1097,17 +1210,17 @@ proto_register_ssh(void)
{ &hf_ssh_dh_gex_min,
{ "DH GEX Min", "ssh.dh_gex.min",
- FT_BYTES, BASE_NONE, NULL, 0x0,
+ FT_UINT32, BASE_DEC, NULL, 0x0,
"SSH DH GEX Minimum", HFILL }},
{ &hf_ssh_dh_gex_nbits,
{ "DH GEX Numbers of Bits", "ssh.dh_gex.nbits",
- FT_BYTES, BASE_NONE, NULL, 0x0,
+ FT_UINT32, BASE_DEC, NULL, 0x0,
"SSH DH GEX Number of Bits", HFILL }},
{ &hf_ssh_dh_gex_max,
{ "DH GEX Max", "ssh.dh_gex.max",
- FT_BYTES, BASE_NONE, NULL, 0x0,
+ FT_UINT32, BASE_DEC, NULL, 0x0,
"SSH DH GEX Maximum", HFILL }},
{ &hf_ssh_payload,