diff options
author | Gilbert Ramirez <gram@alumni.rice.edu> | 2004-07-18 18:06:47 +0000 |
---|---|---|
committer | Gilbert Ramirez <gram@alumni.rice.edu> | 2004-07-18 18:06:47 +0000 |
commit | 669db206cb1f270046ad400fff7655e20c63e723 (patch) | |
tree | 4eff24a2e16c8963e497e1fc575f35e6af59bd26 /epan/dissectors/packet-ssh.c | |
parent | ae46c27a38700af669ef907491081f09df6f6b2c (diff) |
Move dissectors to epan/dissectors directory.
Also move ncp222.py, x11-fields, process-x11-fields.pl,
make-reg-dotc, and make-reg-dotc.py.
Adjust #include lines in files that include packet-*.h
files.
svn path=/trunk/; revision=11410
Diffstat (limited to 'epan/dissectors/packet-ssh.c')
-rw-r--r-- | epan/dissectors/packet-ssh.c | 940 |
1 files changed, 940 insertions, 0 deletions
diff --git a/epan/dissectors/packet-ssh.c b/epan/dissectors/packet-ssh.c new file mode 100644 index 0000000000..9cc34d3b6f --- /dev/null +++ b/epan/dissectors/packet-ssh.c @@ -0,0 +1,940 @@ +/* packet-ssh.c + * Routines for ssh packet dissection + * + * Huagang XIE <huagang@intruvert.com> + * + * $Id$ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * Copied from packet-mysql.c + * + * 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. + * + * + * Note: support SSH v1 and v2 now. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <glib.h> +#include <epan/packet.h> +#include <epan/conversation.h> + +#include "packet-smb-common.h" +#include "packet-tcp.h" +#include "reassemble.h" +#include "prefs.h" + +/* get from openssh ssh2.h */ +#define SSH2_MSG_DISCONNECT 1 +#define SSH2_MSG_IGNORE 2 +#define SSH2_MSG_UNIMPLEMENTED 3 +#define SSH2_MSG_DEBUG 4 +#define SSH2_MSG_SERVICE_REQUEST 5 +#define SSH2_MSG_SERVICE_ACCEPT 6 + +/* transport layer: alg negotiation */ + +#define SSH2_MSG_KEXINIT 20 +#define SSH2_MSG_NEWKEYS 21 + +/* transport layer: kex specific messages, can be reused */ + +#define SSH2_MSG_KEXDH_INIT 30 +#define SSH2_MSG_KEXDH_REPLY 31 + +/* +#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 +#define SSH2_MSG_KEX_DH_GEX_GROUP 31 +*/ +#define SSH2_MSG_KEX_DH_GEX_INIT 32 +#define SSH2_MSG_KEX_DH_GEX_REPLY 33 +#define SSH2_MSG_KEX_DH_GEX_REQUEST 34 + +/* SSH Version 1 definition , from openssh ssh1.h */ + +#define SSH_MSG_NONE 0 /* no message */ +#define SSH_MSG_DISCONNECT 1 /* cause (string) */ +#define SSH_SMSG_PUBLIC_KEY 2 /* ck,msk,srvk,hostk */ +#define SSH_CMSG_SESSION_KEY 3 /* key (BIGNUM) */ +#define SSH_CMSG_USER 4 /* user (string) */ + + +#define SSH_VERSION_UNKNOWN 0 +#define SSH_VERSION_1 1 +#define SSH_VERSION_2 2 + +/* proto data */ + +struct ssh_pdu_data{ + guint counter; + guint number; +}; + +struct ssh_flow_data { + guint req_counter; + guint rsp_counter; + guint version; +}; +static GMemChunk *ssh_this_data=NULL; +static GMemChunk *ssh_global_data = NULL; + +static int proto_ssh = -1; +static int hf_ssh_packet_length= -1; +static int hf_ssh_padding_length= -1; +static int hf_ssh_payload= -1; +static int hf_ssh_protocol= -1; +static int hf_ssh_encrypted_packet= -1; +static int hf_ssh_padding_string= -1; +static int hf_ssh_mac_string= -1; +static int hf_ssh_msg_code = -1; +static int hf_ssh_cookie = -1; +static int hf_ssh_kex_algorithms = -1; +static int hf_ssh_server_host_key_algorithms = -1; +static int hf_ssh_encryption_algorithms_client_to_server = -1; +static int hf_ssh_encryption_algorithms_server_to_client = -1; +static int hf_ssh_mac_algorithms_client_to_server=-1; +static int hf_ssh_mac_algorithms_server_to_client=-1; +static int hf_ssh_compression_algorithms_client_to_server=-1; +static int hf_ssh_compression_algorithms_server_to_client=-1; +static int hf_ssh_languages_client_to_server=-1; +static int hf_ssh_languages_server_to_client=-1; +static int hf_ssh_kex_algorithms_length= -1; +static int hf_ssh_server_host_key_algorithms_length= -1; +static int hf_ssh_encryption_algorithms_client_to_server_length= -1; +static int hf_ssh_encryption_algorithms_server_to_client_length= -1; +static int hf_ssh_mac_algorithms_client_to_server_length= -1; +static int hf_ssh_mac_algorithms_server_to_client_length= -1; +static int hf_ssh_compression_algorithms_client_to_server_length= -1; +static int hf_ssh_compression_algorithms_server_to_client_length= -1; +static int hf_ssh_languages_client_to_server_length= -1; +static int hf_ssh_languages_server_to_client_length= -1; + +static gint ett_ssh = -1; +static gint ett_key_exchange= -1; +static gint ett_key_init= -1; +static gint ett_ssh1= -1; +static gint ett_ssh2= -1; + +static gboolean ssh_desegment = TRUE; + +#define TCP_PORT_SSH 22 + +static const value_string ssh2_msg_vals[] = { + {SSH2_MSG_DISCONNECT, "Disconnect"}, + {SSH2_MSG_IGNORE, "Ignore"}, + {SSH2_MSG_UNIMPLEMENTED, "Unimplemented"}, + {SSH2_MSG_DEBUG, "Debug"}, + {SSH2_MSG_SERVICE_REQUEST, "Service Request"}, + {SSH2_MSG_SERVICE_ACCEPT, "Service Accept"}, + {SSH2_MSG_KEXINIT, "Key Exchange Init"}, + {SSH2_MSG_NEWKEYS, "New Keys"}, + {SSH2_MSG_KEXDH_INIT, "Diffie-Hellman Key Exchange Init"}, + {SSH2_MSG_KEXDH_REPLY, "Diffie-Hellman Key Exchange Reply"}, + {SSH2_MSG_KEX_DH_GEX_INIT, "Diffie-Hellman GEX Init"}, + {SSH2_MSG_KEX_DH_GEX_REPLY, "Diffie-Hellman GEX Reply"}, + {SSH2_MSG_KEX_DH_GEX_REQUEST, "Diffie-Hellman GEX Request"}, + { 0, NULL } +}; + +static const value_string ssh1_msg_vals[] = { + {SSH_MSG_NONE,"No Message"}, + {SSH_MSG_DISCONNECT, "Disconnect"}, + {SSH_SMSG_PUBLIC_KEY,"Public Key"}, + {SSH_CMSG_SESSION_KEY,"Session Key"}, + {SSH_CMSG_USER,"User"}, +}; + + +static const value_string ssh_opcode_vals[] = { + { 0, NULL } +}; + +static int ssh_dissect_key_init(tvbuff_t *tvb, int offset, proto_tree *tree); + +static int ssh_dissect_ssh1(tvbuff_t *tvb, packet_info *pinfo, + int offset, proto_tree *tree,int is_response, + int number, gboolean *need_desegmentation); +static int ssh_dissect_ssh2(tvbuff_t *tvb, packet_info *pinfo, + int offset, proto_tree *tree,int is_response, + int number, gboolean *need_desegmentation ); +static int ssh_dissect_key_exchange(tvbuff_t *tvb, packet_info *pinfo, + int offset, proto_tree *tree,int is_response, + int number, gboolean *need_desegmentation ); +static int ssh_dissect_protocol(tvbuff_t *tvb, packet_info *pinfo, + int offset, proto_tree *tree,int is_response,int *version, + gboolean *need_desegmentation); +static int ssh_dissect_encrypted_packet(tvbuff_t *tvb, packet_info *pinfo, + int offset, proto_tree *tree,int is_response); +proto_item * ssh_proto_tree_add_item(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gboolean little_endian); + +static void +ssh_init_protocol(void) +{ + if (ssh_global_data) + g_mem_chunk_destroy(ssh_global_data); + if (ssh_this_data) + g_mem_chunk_destroy(ssh_this_data); + + ssh_global_data = g_mem_chunk_new("ssh_global_datas", + sizeof(struct ssh_flow_data), + 100* sizeof(struct ssh_flow_data), G_ALLOC_AND_FREE); + ssh_this_data = g_mem_chunk_new("ssh_pku_data", + sizeof(struct ssh_pdu_data), + 100* sizeof(struct ssh_pdu_data), G_ALLOC_AND_FREE); + +} + +static void +dissect_ssh(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + + proto_tree *ssh_tree = NULL; + proto_item *ti; + conversation_t *conversation=NULL; + gint remain_length; + int last_offset; + guint this_number,number; + + int offset = 0; + + gboolean is_response; + gboolean is_newdata; + gboolean need_desegmentation; + guint version; + + struct ssh_pdu_data *this_data=NULL; + struct ssh_flow_data *global_data=NULL; + + is_newdata = FALSE; + this_data = p_get_proto_data(pinfo->fd, proto_ssh); + + conversation = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, 0); + + if (!conversation) { + /* create a new conversation */ + conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, 0); + } + + global_data = conversation_get_proto_data(conversation,proto_ssh); + if(!global_data ) { + global_data = g_mem_chunk_alloc(ssh_global_data); + global_data->req_counter=0; + global_data->rsp_counter=0; + global_data->version=SSH_VERSION_UNKNOWN; + conversation_add_proto_data(conversation,proto_ssh,global_data); + } + +/* + * end of attaching data + */ + if (pinfo->destport == pinfo->match_port) { + is_response=FALSE; + if(!this_data) { + this_data = g_mem_chunk_alloc(ssh_this_data); + this_data->counter = global_data->req_counter++; + p_add_proto_data(pinfo->fd, proto_ssh, this_data); + is_newdata = TRUE; + } + }else { + is_response=TRUE; + if(!this_data) { + this_data = g_mem_chunk_alloc(ssh_global_data); + this_data->counter = global_data->rsp_counter++; + p_add_proto_data(pinfo->fd, proto_ssh, this_data); + is_newdata = TRUE; + } + } + if(tree) { + ti = proto_tree_add_item(tree, proto_ssh, tvb, offset, -1, FALSE); + ssh_tree = proto_item_add_subtree(ti, ett_ssh); + } + number = 0; + + version = global_data->version; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) { + switch(version) { + case SSH_VERSION_UNKNOWN: + col_set_str(pinfo->cinfo, COL_PROTOCOL, "SSH"); + break; + case SSH_VERSION_1: + col_set_str(pinfo->cinfo, COL_PROTOCOL, "SSHv1"); + break; + case SSH_VERSION_2: + col_set_str(pinfo->cinfo, COL_PROTOCOL, "SSHv2"); + break; + + } + } + + if(this_data->counter != 0 && version == SSH_VERSION_UNKNOWN) { + offset = ssh_dissect_encrypted_packet(tvb, pinfo, + offset,ssh_tree,is_response); + return; + } + + while((remain_length = tvb_reported_length_remaining(tvb,offset))> 0 ) { + need_desegmentation = FALSE; + last_offset = offset; + this_number = this_data->counter+number; + + if(number > 1 && is_newdata) { + /* update the this_data and flow_data */ + if(is_response) { + global_data->rsp_counter++; + } else { + global_data->req_counter++; + } + } + + number++; + if(this_number == 0) { + offset = ssh_dissect_protocol(tvb, pinfo, + offset,ssh_tree, is_response, + &version, &need_desegmentation); + if(!is_response) { + global_data->version= version; + } + } else { + switch(version) { + + case SSH_VERSION_UNKNOWN: + /* + * We use "tvb_ensure_length_remaining()" + * to make sure there actually *is* data + * remaining. + * + * This means we're guaranteed that + * "remain_length" is positive. + */ + remain_length = tvb_ensure_length_remaining(tvb, + offset); + proto_tree_add_text(ssh_tree, tvb, offset, + remain_length, + "Unknown SSH version data"); + offset += remain_length; + break; + + case SSH_VERSION_1: + offset = ssh_dissect_ssh1(tvb, pinfo, + offset,ssh_tree,is_response,this_number, + &need_desegmentation); + break; + + case SSH_VERSION_2: + offset = ssh_dissect_ssh2(tvb, pinfo, + offset,ssh_tree,is_response,this_number, + &need_desegmentation); + break; + } + } + + if(need_desegmentation) return; + } +} + +static int +ssh_dissect_ssh2(tvbuff_t *tvb, packet_info *pinfo, + int offset, proto_tree *tree,int is_response, int this_number, + gboolean *need_desegmentation) +{ + proto_item *ti; + proto_item *ssh2_tree=NULL; + + if(tree) { + ti=proto_tree_add_text(tree,tvb,offset,-1,"SSH Version 2"); + ssh2_tree = proto_item_add_subtree(ti ,ett_ssh2); + } + + if((is_response && this_number > 3) || (!is_response && this_number>4)) { + offset = ssh_dissect_encrypted_packet(tvb, pinfo, + offset,ssh2_tree,is_response); + } else { + offset = ssh_dissect_key_exchange(tvb,pinfo, + offset,ssh2_tree,is_response,this_number, + need_desegmentation); + } + + return offset; +} +static int +ssh_dissect_ssh1(tvbuff_t *tvb, packet_info *pinfo, + int offset, proto_tree *tree,int is_response, + int number, gboolean *need_desegmentation) +{ + guint plen, padding_length,len; + guint8 msg_code; + guint remain_length; + + proto_item *ti; + proto_item *ssh1_tree =NULL; + + if(tree) { + ti=proto_tree_add_text(tree,tvb,offset,-1,"SSH Version 1"); + ssh1_tree = proto_item_add_subtree(ti ,ett_ssh1); + } + + /* + * We use "tvb_ensure_length_remaining()" to make sure there + * actually *is* data remaining. + * + * This means we're guaranteed that "remain_length" is positive. + */ + remain_length = tvb_ensure_length_remaining(tvb,offset); + if (ssh_desegment && pinfo->can_desegment) { + if(remain_length < 4) { + pinfo->desegment_offset = offset; + pinfo->desegment_len = 4-remain_length; + *need_desegmentation = TRUE; + return offset; + } + } + plen = tvb_get_ntohl(tvb, offset) ; + padding_length = 8 - plen%8; + + + if (ssh_desegment && pinfo->can_desegment) { + if(plen+4+padding_length > remain_length ) { + pinfo->desegment_offset = offset; + pinfo->desegment_len = plen+padding_length - remain_length; + *need_desegmentation = TRUE; + return offset; + } + } + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s: ", + is_response?"Server":"Client"); + } + + if(plen >= 0xffff) { + if (ssh1_tree && plen > 0) { + proto_tree_add_uint_format(ssh1_tree, hf_ssh_packet_length, tvb, + offset, 4, plen,"Overly large length %x",plen); + } + plen = remain_length-4-padding_length; + } else { + if (ssh1_tree && plen > 0) { + proto_tree_add_uint(ssh1_tree, hf_ssh_packet_length, tvb, + offset, 4, plen); + } + } + offset+=4; +/* padding length */ + + if (tree) { + proto_tree_add_uint(ssh1_tree, hf_ssh_padding_length, tvb, + offset, padding_length, padding_length); + } + offset += padding_length; +/* + if(tree) { + tf=proto_tree_add_text(tree,tvb,offset,-1,"SSH Version 1"); + ssh1_tree = proto_item_add_subtree(tf ,ett_ssh1); + } +*/ + /* msg_code */ + if(number == 1 ) { + msg_code = tvb_get_guint8(tvb, offset); + if (tree) { + proto_tree_add_uint_format(ssh1_tree, hf_ssh_msg_code, tvb, + offset, 1, msg_code,"Msg code: %s (%u)", + val_to_str(msg_code, ssh1_msg_vals, "Unknown (%u)"), + msg_code); + } + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, "%s", + val_to_str(msg_code, ssh1_msg_vals, "Unknown (%u)")); + } + offset += 1; + len = plen -1; + } else { + len = plen; + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, "Encrypted packet len=%d", len); + } + } + /* payload */ + if (ssh1_tree ) { + ssh_proto_tree_add_item(ssh1_tree, hf_ssh_payload, + tvb, offset, len, FALSE); + } + offset+=len; + + return offset; +} + +static int +ssh_dissect_key_exchange(tvbuff_t *tvb, packet_info *pinfo, + int offset, proto_tree *tree,int is_response,int number, + gboolean *need_desegmentation) +{ + guint plen,len; + guint8 padding_length; + guint remain_length; + int last_offset=offset; + guint msg_code; + + proto_item *tf; + proto_item *key_ex_tree =NULL; + + /* + * We use "tvb_ensure_length_remaining()" to make sure there + * actually *is* data remaining. + * + * This means we're guaranteed that "remain_length" is positive. + */ + remain_length = tvb_ensure_length_remaining(tvb,offset); + if (ssh_desegment && pinfo->can_desegment) { + if(remain_length < 4) { + pinfo->desegment_offset = offset; + pinfo->desegment_len = 4-remain_length; + *need_desegmentation = TRUE; + return offset; + } + } + plen = tvb_get_ntohl(tvb, offset) ; + + if (ssh_desegment && pinfo->can_desegment) { + if(plen +4 > remain_length ) { + pinfo->desegment_offset = offset; + pinfo->desegment_len = plen+4 - remain_length; + *need_desegmentation = TRUE; + return offset; + } + } + /* + * Need to check plen > 0x80000000 here + */ + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s: ", + is_response?"Server":"Client"); + } + + if(plen >= 0xffff) { + if (tree) { + proto_tree_add_uint_format(tree, hf_ssh_packet_length, tvb, + offset, 4, plen,"Overly large number 0x%x",plen); + } + plen = remain_length-4; + } else { + if (tree) { + proto_tree_add_uint(tree, hf_ssh_packet_length, tvb, + offset, 4, plen); + } + } + offset+=4; +/* padding length */ + padding_length = tvb_get_guint8(tvb, offset); + if (tree) { + proto_tree_add_uint(tree, hf_ssh_padding_length, tvb, + offset, 1, padding_length); + } + offset += 1; + + if(tree) { + tf=proto_tree_add_text(tree,tvb,offset,-1,"Key Exchange"); + key_ex_tree = proto_item_add_subtree(tf ,ett_key_exchange); + } + /* msg_code */ + msg_code = tvb_get_guint8(tvb, offset); + if (tree) { + proto_tree_add_uint_format(key_ex_tree, hf_ssh_msg_code, tvb, + offset, 1, msg_code,"Msg code: %s (%u)", + val_to_str(msg_code, ssh2_msg_vals, "Unknown (%u)"), + msg_code); + + } + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, "%s", + val_to_str(msg_code, ssh2_msg_vals, "Unknown (%u)")); + } + offset += 1; + + /* 16 bytes cookie */ + if(number == 1) { + offset = ssh_dissect_key_init(tvb, offset,key_ex_tree); + } + + len = plen+4-padding_length-(offset-last_offset); + if (tree ) { + ssh_proto_tree_add_item(key_ex_tree, hf_ssh_payload, + tvb, offset, len, FALSE); + } + offset +=len; + + /* padding */ + if(tree) { + ssh_proto_tree_add_item(key_ex_tree, hf_ssh_padding_string, + tvb, offset, padding_length, FALSE); + } + offset+= padding_length; + + /* MAC , if there is still bytes, treat it as 16bytes MAC*/ + if(msg_code == SSH2_MSG_KEX_DH_GEX_REPLY) { + len = tvb_reported_length_remaining(tvb,offset); + if(len == 16) { + if(tree) { + proto_tree_add_item(key_ex_tree, hf_ssh_mac_string, + tvb, offset, len , FALSE); + } + offset+=len; + } + } + + return offset; +} +static int +ssh_dissect_encrypted_packet(tvbuff_t *tvb, packet_info *pinfo, + int offset, proto_tree *tree,int is_response) +{ + gint len; + + len = tvb_reported_length_remaining(tvb,offset); + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "Encrypted %s packet len=%d", + is_response?"response":"request",len); + } + if (tree ) { + ssh_proto_tree_add_item(tree, hf_ssh_encrypted_packet, + tvb, offset, len, FALSE); + } + offset+=len; + return offset; +} + +static int +ssh_dissect_protocol(tvbuff_t *tvb, packet_info *pinfo, + int offset, proto_tree *tree, int is_response, int * version, + gboolean *need_desegmentation) +{ + guint remain_length; + gint linelen, protolen; + + /* + * If the first packet do not contain the banner, + * it is dump in the middle of a flow or not a ssh at all + */ + if(tvb_strncaseeql(tvb,offset,"SSH-",4) != 0 ) { + offset = ssh_dissect_encrypted_packet(tvb, pinfo, + offset,tree,is_response); + return offset; + } + + if(!is_response) { + if(tvb_strncaseeql(tvb,offset,"SSH-2.",6) == 0 ) { + *(version) = SSH_VERSION_2; + }else if(tvb_strncaseeql(tvb,offset,"SSH-1.99-",9) == 0 ) { + *(version) = SSH_VERSION_2; + }else if(tvb_strncaseeql(tvb,offset,"SSH-1.",6) == 0 ) { + *(version) = SSH_VERSION_1; + } + } + + /* + * We use "tvb_ensure_length_remaining()" to make sure there + * actually *is* data remaining. + * + * This means we're guaranteed that "remain_length" is positive. + */ + remain_length = tvb_ensure_length_remaining(tvb,offset); + /*linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE); + */ + linelen = tvb_find_guint8(tvb, offset, -1, '\n'); + + if (ssh_desegment && pinfo->can_desegment) { + if(linelen == -1 || remain_length < (guint)linelen-offset ) { + pinfo->desegment_offset = offset; + pinfo->desegment_len = linelen-remain_length; + *need_desegmentation = TRUE; + return offset; + } + } + if(linelen == -1 ) { + /* XXX - reassemble across segment boundaries? */ + linelen = remain_length; + protolen = linelen; + } else { + linelen = linelen - offset + 1; + protolen = linelen - 1; + } + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s Protocol: %s", + is_response?"Server":"Client", + tvb_format_text(tvb,offset,protolen)); + } + if (tree ) { + ssh_proto_tree_add_item(tree, hf_ssh_protocol, + tvb, offset, linelen, FALSE); + } + offset+=linelen; + return offset; +} + +#define SSH_PROPOSAL(item)\ + { &hf_ssh_ ## item, &hf_ssh_ ## item ## _length } + +static struct { + int *value, *length; +} ssh_proposals[] = { + SSH_PROPOSAL(kex_algorithms), + SSH_PROPOSAL(server_host_key_algorithms), + SSH_PROPOSAL(encryption_algorithms_client_to_server), + SSH_PROPOSAL(encryption_algorithms_server_to_client), + SSH_PROPOSAL(mac_algorithms_client_to_server), + SSH_PROPOSAL(mac_algorithms_server_to_client), + SSH_PROPOSAL(compression_algorithms_client_to_server), + SSH_PROPOSAL(compression_algorithms_server_to_client), + SSH_PROPOSAL(languages_client_to_server), + SSH_PROPOSAL(languages_server_to_client), + {NULL, NULL} +}; + +static int +ssh_dissect_key_init(tvbuff_t *tvb, int offset, proto_tree *tree ) +{ + guint len; + int i; + + proto_item *tf; + proto_item *key_init_tree=NULL; + + if (tree) { + tf=proto_tree_add_text(tree,tvb,offset,-1,"Algorithms"); + key_init_tree = proto_item_add_subtree(tf, ett_key_init); + proto_tree_add_item(key_init_tree, hf_ssh_cookie, + tvb, offset, 16, FALSE); + } + offset += 16; + + for (i = 0; ssh_proposals[i].value; i++) { + len = tvb_get_ntohl(tvb, offset); + if (key_init_tree) { + proto_tree_add_uint(key_init_tree, + *ssh_proposals[i].length, tvb, offset, 4, len); + } + offset+=4; + if (key_init_tree) { + ssh_proto_tree_add_item(key_init_tree, + *ssh_proposals[i].value, tvb, offset, len, FALSE); + } + offset+=len; + } + return offset; +} +proto_item * +ssh_proto_tree_add_item(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gboolean little_endian) +{ + if (tree && length <0xffff && length > 0) { + return proto_tree_add_item(tree, hfindex, tvb, start, length,little_endian); + } + return NULL; +} + +void +proto_register_ssh(void) +{ + static hf_register_info hf[] = { + { &hf_ssh_packet_length, + { "Packet Length", "ssh.packet_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + "SSH packet length", HFILL }}, + + { &hf_ssh_padding_length, + { "Padding Length", "ssh.padding_length", + FT_UINT8, BASE_DEC, NULL, 0x0, + "SSH Packet Number", HFILL }}, + + { &hf_ssh_msg_code, + { "Message Code", "ssh.message_code", + FT_UINT8, BASE_DEC, NULL, 0x0, + "SSH Message Code", HFILL }}, + + { &hf_ssh_cookie, + { "Cookie", "ssh.cookie", + FT_BYTES, BASE_NONE, NULL, 0x0, + "SSH Cookie", HFILL }}, + + { &hf_ssh_encrypted_packet, + { "Encrypted Packet", "ssh.encrypted_packet", + FT_BYTES, BASE_NONE, NULL, 0x0, + "SSH Protocol Packet", HFILL }}, + + { &hf_ssh_protocol, + { "Protocol", "ssh.protocol", + FT_STRING, BASE_NONE, NULL, 0x0, + "SSH Protocol", HFILL }}, + + { &hf_ssh_payload, + { "Payload", "ssh.payload", + FT_BYTES, BASE_NONE, NULL, 0x0, + "SSH Payload", HFILL }}, + + { &hf_ssh_padding_string, + { "Padding String", "ssh.padding_string", + FT_STRING, BASE_NONE, NULL, 0x0, + "SSH Padding String", HFILL }}, + + { &hf_ssh_mac_string, + { "MAC String", "ssh.mac_string", + FT_STRING, BASE_NONE, NULL, 0x0, + "SSH MAC String", HFILL }}, + + { &hf_ssh_kex_algorithms, + { "kex_algorithms string", "ssh.kex_algorithms", + FT_STRINGZ, BASE_NONE, NULL, 0x0, + "SSH kex_algorithms string", HFILL }}, + + { &hf_ssh_server_host_key_algorithms, + { "server_host_key_algorithms string", "ssh.server_host_key_algorithms", + FT_STRINGZ, BASE_NONE, NULL, 0x0, + "SSH server_host_key_algorithms string", HFILL }}, + + { &hf_ssh_encryption_algorithms_client_to_server, + { "encryption_algorithms_client_to_server string", "ssh.encryption_algorithms_client_to_server", + FT_STRINGZ, BASE_NONE, NULL, 0x0, + "SSH encryption_algorithms_client_to_server string", HFILL }}, + + { &hf_ssh_encryption_algorithms_server_to_client, + { "encryption_algorithms_server_to_client string", "ssh.encryption_algorithms_server_to_client", + FT_STRINGZ, BASE_NONE, NULL, 0x0, + "SSH encryption_algorithms_server_to_client string", HFILL }}, + + { &hf_ssh_mac_algorithms_client_to_server, + { "mac_algorithms_client_to_server string", "ssh.mac_algorithms_client_to_server", + FT_STRINGZ, BASE_NONE, NULL, 0x0, + "SSH mac_algorithms_client_to_server string", HFILL }}, + + { &hf_ssh_mac_algorithms_server_to_client, + { "mac_algorithms_server_to_client string", "ssh.mac_algorithms_server_to_client", + FT_STRINGZ, BASE_NONE, NULL, 0x0, + "SSH mac_algorithms_server_to_client string", HFILL }}, + + { &hf_ssh_compression_algorithms_client_to_server, + { "compression_algorithms_client_to_server string", "ssh.compression_algorithms_client_to_server", + FT_STRINGZ, BASE_NONE, NULL, 0x0, + "SSH compression_algorithms_client_to_server string", HFILL }}, + + { &hf_ssh_compression_algorithms_server_to_client, + { "compression_algorithms_server_to_client string", "ssh.compression_algorithms_server_to_client", + FT_STRINGZ, BASE_NONE, NULL, 0x0, + "SSH compression_algorithms_server_to_client string", HFILL }}, + + { &hf_ssh_languages_client_to_server, + { "languages_client_to_server string", "ssh.languages_client_to_server", + FT_STRINGZ, BASE_NONE, NULL, 0x0, + "SSH languages_client_to_server string", HFILL }}, + + { &hf_ssh_languages_server_to_client, + { "languages_server_to_client string", "ssh.languages_server_to_client", + FT_STRINGZ, BASE_NONE, NULL, 0x0, + "SSH languages_server_to_client string", HFILL }}, + + { &hf_ssh_kex_algorithms_length, + { "kex_algorithms length", "ssh.kex_algorithms_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + "SSH kex_algorithms length", HFILL }}, + + { &hf_ssh_server_host_key_algorithms_length, + { "server_host_key_algorithms length", "ssh.server_host_key_algorithms_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + "SSH server_host_key_algorithms length", HFILL }}, + + { &hf_ssh_encryption_algorithms_client_to_server_length, + { "encryption_algorithms_client_to_server length", "ssh.encryption_algorithms_client_to_server_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + "SSH encryption_algorithms_client_to_server length", HFILL }}, + + { &hf_ssh_encryption_algorithms_server_to_client_length, + { "encryption_algorithms_server_to_client length", "ssh.encryption_algorithms_server_to_client_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + "SSH encryption_algorithms_server_to_client length", HFILL }}, + + { &hf_ssh_mac_algorithms_client_to_server_length, + { "mac_algorithms_client_to_server length", "ssh.mac_algorithms_client_to_server_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + "SSH mac_algorithms_client_to_server length", HFILL }}, + + { &hf_ssh_mac_algorithms_server_to_client_length, + { "mac_algorithms_server_to_client length", "ssh.mac_algorithms_server_to_client_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + "SSH mac_algorithms_server_to_client length", HFILL }}, + + { &hf_ssh_compression_algorithms_client_to_server_length, + { "compression_algorithms_client_to_server length", "ssh.compression_algorithms_client_to_server_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + "SSH compression_algorithms_client_to_server length", HFILL }}, + + { &hf_ssh_compression_algorithms_server_to_client_length, + { "compression_algorithms_server_to_client length", "ssh.compression_algorithms_server_to_client_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + "SSH compression_algorithms_server_to_client length", HFILL }}, + + { &hf_ssh_languages_client_to_server_length, + { "languages_client_to_server length", "ssh.languages_client_to_server_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + "SSH languages_client_to_server length", HFILL }}, + + { &hf_ssh_languages_server_to_client_length, + { "languages_server_to_client length", "ssh.languages_server_to_client_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + "SSH languages_server_to_client length", HFILL }}, + + + }; + static gint *ett[] = { + &ett_ssh, + &ett_key_exchange, + &ett_ssh1, + &ett_ssh2, + &ett_key_init + }; + module_t *ssh_module; + + proto_ssh = proto_register_protocol("SSH Protocol", + "SSH", "ssh"); + proto_register_field_array(proto_ssh, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + register_init_routine(&ssh_init_protocol); + + ssh_module = prefs_register_protocol(proto_ssh, NULL); + prefs_register_bool_preference(ssh_module, "desegment_buffers", + "Desegment all SSH buffers spanning multiple TCP segments", + "Whether the SSH dissector should desegment all SSH buffers spanning multiple TCP segments", + &ssh_desegment); +} + +void +proto_reg_handoff_ssh(void) +{ + dissector_handle_t ssh_handle; + + ssh_handle = create_dissector_handle(dissect_ssh, proto_ssh); + + dissector_add("tcp.port", TCP_PORT_SSH, ssh_handle); +} |