From e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 22:34:10 +0200 Subject: Adding upstream version 4.2.2. Signed-off-by: Daniel Baumann --- epan/dissectors/packet-ssh.c | 4968 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4968 insertions(+) create mode 100644 epan/dissectors/packet-ssh.c (limited to 'epan/dissectors/packet-ssh.c') diff --git a/epan/dissectors/packet-ssh.c b/epan/dissectors/packet-ssh.c new file mode 100644 index 00000000..1b919590 --- /dev/null +++ b/epan/dissectors/packet-ssh.c @@ -0,0 +1,4968 @@ +/* packet-ssh.c + * Routines for ssh packet dissection + * + * Huagang XIE + * Kees Cook + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Copied from packet-mysql.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * + * Note: support SSH v1 and v2 now. + * + */ + +/* SSH version 2 is defined in: + * + * RFC 4250: The Secure Shell (SSH) Protocol Assigned Numbers + * RFC 4251: The Secure Shell (SSH) Protocol Architecture + * RFC 4252: The Secure Shell (SSH) Authentication Protocol + * RFC 4253: The Secure Shell (SSH) Transport Layer Protocol + * 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 */ + +#include "config.h" +/* Start with WIRESHARK_LOG_DOMAINS=packet-ssh and WIRESHARK_LOG_LEVEL=debug to see messages. */ +#define WS_LOG_DOMAIN "packet-ssh" + +// Define this to get hex dumps more similar to what you get in openssh. If not defined, dumps look more like what you get with other dissectors. +#define OPENSSH_STYLE + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_LIBGNUTLS) +#include +#endif + +#include "packet-tcp.h" + +void proto_register_ssh(void); +void proto_reg_handoff_ssh(void); + +/* SSH Version 1 definition , from openssh ssh1.h */ +#define SSH1_MSG_NONE 0 /* no message */ +#define SSH1_MSG_DISCONNECT 1 /* cause (string) */ +#define SSH1_SMSG_PUBLIC_KEY 2 /* ck,msk,srvk,hostk */ +#define SSH1_CMSG_SESSION_KEY 3 /* key (BIGNUM) */ +#define SSH1_CMSG_USER 4 /* user (string) */ + + +#define SSH_VERSION_UNKNOWN 0 +#define SSH_VERSION_1 1 +#define SSH_VERSION_2 2 + +/* proto data */ + +typedef struct { + guint8 *data; + guint length; +} ssh_bignum; + +#define SSH_KEX_CURVE25519 0x00010000 +#define SSH_KEX_DH_GEX 0x00020000 +#define SSH_KEX_DH_GROUP1 0x00030001 +#define SSH_KEX_DH_GROUP14 0x00030014 +#define SSH_KEX_DH_GROUP16 0x00030016 +#define SSH_KEX_DH_GROUP18 0x00030018 + +#define SSH_KEX_HASH_SHA1 1 +#define SSH_KEX_HASH_SHA256 2 +#define SSH_KEX_HASH_SHA512 4 + +#define DIGEST_MAX_SIZE 48 + +typedef struct _ssh_message_info_t { + guint32 sequence_number; + guint32 offset; + guchar *plain_data; /**< Decrypted data. */ + guint data_len; /**< Length of decrypted data. */ + gint id; /**< Identifies the exact message within a frame + (there can be multiple records in a frame). */ + struct _ssh_message_info_t* next; + guint8 calc_mac[DIGEST_MAX_SIZE]; +} ssh_message_info_t; + +typedef struct { + gboolean from_server; + ssh_message_info_t * messages; +} ssh_packet_info_t; + +typedef struct _ssh_channel_info_t { + guint client_channel_number; + guint server_channel_number; + dissector_handle_t subdissector_handle; + struct _ssh_channel_info_t* next; +} ssh_channel_info_t; + +struct ssh_peer_data { + guint counter; + + guint32 frame_version_start; + guint32 frame_version_end; + + guint32 frame_key_start; + guint32 frame_key_end; + int frame_key_end_offset; + + 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 + + gchar* mac_proposals[2]; + gchar* mac; + gint mac_length; + + gchar* enc_proposals[2]; + gchar* enc; + + gchar* comp_proposals[2]; + gchar* comp; + + gint length_is_plaintext; + + // see libgcrypt source, gcrypt.h:gcry_cipher_algos + guint cipher_id; + guint mac_id; + // chacha20 needs two cipher handles + gcry_cipher_hd_t cipher, cipher_2; + guint sequence_number; + guint32 seq_num_kex_init; +// union ??? -- begin + guint32 seq_num_gex_req; + guint32 seq_num_gex_grp; + guint32 seq_num_gex_ini; + guint32 seq_num_gex_rep; +// -- + guint32 seq_num_ecdh_ini; + guint32 seq_num_ecdh_rep; +// -- + guint32 seq_num_dh_ini; + guint32 seq_num_dh_rep; +// union ??? -- end + guint32 seq_num_new_key; + ssh_bignum *bn_cookie; + guint8 iv[12]; + guint8 hmac_iv[DIGEST_MAX_SIZE]; + guint hmac_iv_len; + struct ssh_flow_data * global_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, + struct ssh_flow_data *global_data, guint *seq_num); + + /* [0] is client's, [1] is server's */ +#define CLIENT_PEER_DATA 0 +#define SERVER_PEER_DATA 1 + struct ssh_peer_data peer_data[2]; + + gchar *session_id; + guint session_id_length; + ssh_bignum *kex_e; + ssh_bignum *kex_f; + ssh_bignum *kex_gex_p; // Group modulo + ssh_bignum *kex_gex_g; // Group generator + ssh_bignum *secret; + wmem_array_t *kex_client_version; + wmem_array_t *kex_server_version; + wmem_array_t *kex_client_key_exchange_init; + wmem_array_t *kex_server_key_exchange_init; + wmem_array_t *kex_server_host_key_blob; + wmem_array_t *kex_gex_bits_min; + wmem_array_t *kex_gex_bits_req; + wmem_array_t *kex_gex_bits_max; + wmem_array_t *kex_shared_secret; + gboolean do_decrypt; + ssh_bignum new_keys[6]; + ssh_channel_info_t *channel_info; +}; + +typedef struct { + gchar *type; + ssh_bignum *key_material; +} ssh_key_map_entry_t; + +static GHashTable * ssh_master_key_map = NULL; + +static int proto_ssh = -1; + +/* Version exchange */ +static int hf_ssh_protocol = -1; + +/* Framing */ +static int hf_ssh_packet_length = -1; +static int hf_ssh_packet_length_encrypted = -1; +static int hf_ssh_padding_length = -1; +static int hf_ssh_payload = -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_mac_status = -1; +static int hf_ssh_seq_num = -1; +static int hf_ssh_direction = -1; + +/* Message codes */ +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_ssh2_kex_ecdh_msg_code = -1; + +/* Algorithm negotiation */ +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 int hf_ssh_first_kex_packet_follows = -1; +static int hf_ssh_kex_reserved = -1; +static int hf_ssh_kex_hassh_algo = -1; +static int hf_ssh_kex_hassh = -1; +static int hf_ssh_kex_hasshserver_algo = -1; +static int hf_ssh_kex_hasshserver = -1; + +/* Key exchange common elements */ +static int hf_ssh_hostkey_length = -1; +static int hf_ssh_hostkey_type_length = -1; +static int hf_ssh_hostkey_type = -1; +static int hf_ssh_hostkey_data = -1; +static int hf_ssh_hostkey_rsa_n = -1; +static int hf_ssh_hostkey_rsa_e = -1; +static int hf_ssh_hostkey_dsa_p = -1; +static int hf_ssh_hostkey_dsa_q = -1; +static int hf_ssh_hostkey_dsa_g = -1; +static int hf_ssh_hostkey_dsa_y = -1; +static int hf_ssh_hostkey_ecdsa_curve_id = -1; +static int hf_ssh_hostkey_ecdsa_curve_id_length = -1; +static int hf_ssh_hostkey_ecdsa_q = -1; +static int hf_ssh_hostkey_ecdsa_q_length = -1; +static int hf_ssh_hostkey_eddsa_key = -1; +static int hf_ssh_hostkey_eddsa_key_length = -1; +static int hf_ssh_hostsig_length = -1; +static int hf_ssh_hostsig_type_length = -1; +static int hf_ssh_hostsig_type = -1; +static int hf_ssh_hostsig_rsa = -1; +static int hf_ssh_hostsig_dsa = -1; +static int hf_ssh_hostsig_data = -1; + +/* Key exchange: Diffie-Hellman */ +static int hf_ssh_dh_e = -1; +static int hf_ssh_dh_f = -1; + +/* Key exchange: Diffie-Hellman Group Exchange */ +static int hf_ssh_dh_gex_min = -1; +static int hf_ssh_dh_gex_nbits = -1; +static int hf_ssh_dh_gex_max = -1; +static int hf_ssh_dh_gex_p = -1; +static int hf_ssh_dh_gex_g = -1; + +/* Key exchange: Elliptic Curve Diffie-Hellman */ +static int hf_ssh_ecdh_q_c = -1; +static int hf_ssh_ecdh_q_c_length = -1; +static int hf_ssh_ecdh_q_s = -1; +static int hf_ssh_ecdh_q_s_length = -1; + +/* Miscellaneous */ +static int hf_ssh_mpint_length = -1; + +static int hf_ssh_ignore_data_length = -1; +static int hf_ssh_ignore_data = -1; +static int hf_ssh_debug_always_display = -1; +static int hf_ssh_debug_message_length = -1; +static int hf_ssh_debug_message = -1; +static int hf_ssh_service_name_length = -1; +static int hf_ssh_service_name = -1; +static int hf_ssh_userauth_user_name_length = -1; +static int hf_ssh_userauth_user_name = -1; +static int hf_ssh_userauth_change_password = -1; +static int hf_ssh_userauth_service_name_length = -1; +static int hf_ssh_userauth_service_name = -1; +static int hf_ssh_userauth_method_name_length = -1; +static int hf_ssh_userauth_method_name = -1; +static int hf_ssh_userauth_have_signature = -1; +static int hf_ssh_userauth_password_length = -1; +static int hf_ssh_userauth_password = -1; +static int hf_ssh_userauth_new_password_length = -1; +static int hf_ssh_userauth_new_password = -1; +static int hf_ssh_auth_failure_list_length = -1; +static int hf_ssh_auth_failure_list = -1; +static int hf_ssh_userauth_partial_success = -1; +static int hf_ssh_userauth_pka_name_len = -1; +static int hf_ssh_userauth_pka_name = -1; +static int hf_ssh_pk_blob_name_length = -1; +static int hf_ssh_pk_blob_name = -1; +static int hf_ssh_blob_length = -1; +static int hf_ssh_signature_length = -1; +static int hf_ssh_pk_sig_blob_name_length = -1; +static int hf_ssh_pk_sig_blob_name = -1; +static int hf_ssh_connection_type_name_len = -1; +static int hf_ssh_connection_type_name = -1; +static int hf_ssh_connection_sender_channel = -1; +static int hf_ssh_connection_recipient_channel = -1; +static int hf_ssh_connection_initial_window = -1; +static int hf_ssh_connection_maximum_packet_size = -1; +static int hf_ssh_global_request_name_len = -1; +static int hf_ssh_global_request_name = -1; +static int hf_ssh_global_request_want_reply = -1; +static int hf_ssh_global_request_hostkeys_array_len = -1; +static int hf_ssh_channel_request_name_len = -1; +static int hf_ssh_channel_request_name = -1; +static int hf_ssh_channel_request_want_reply = -1; +static int hf_ssh_subsystem_name_len = -1; +static int hf_ssh_subsystem_name = -1; +static int hf_ssh_channel_window_adjust = -1; +static int hf_ssh_channel_data_len = -1; +static int hf_ssh_exit_status = -1; +static int hf_ssh_disconnect_reason = -1; +static int hf_ssh_disconnect_description_length = -1; +static int hf_ssh_disconnect_description = -1; +static int hf_ssh_lang_tag_length = -1; +static int hf_ssh_lang_tag = -1; + +static int hf_ssh_blob_p = -1; +static int hf_ssh_blob_e = -1; + +static int hf_ssh_pk_sig_s_length = -1; +static int hf_ssh_pk_sig_s = -1; + +static gint ett_ssh = -1; +static gint ett_key_exchange = -1; +static gint ett_key_exchange_host_key = -1; +static gint ett_key_exchange_host_sig = -1; +static gint ett_userauth_pk_blob = -1; +static gint ett_userauth_pk_signautre = -1; +static gint ett_key_init = -1; +static gint ett_ssh1 = -1; +static gint ett_ssh2 = -1; + +static expert_field ei_ssh_packet_length = EI_INIT; +static expert_field ei_ssh_packet_decode = EI_INIT; +static expert_field ei_ssh_invalid_keylen = EI_INIT; +static expert_field ei_ssh_mac_bad = EI_INIT; + +static gboolean ssh_desegment = TRUE; + +static dissector_handle_t ssh_handle; +static dissector_handle_t sftp_handle=NULL; + +static const char *pref_keylog_file; +static FILE *ssh_keylog_file; + +#define SSH_DECRYPT_DEBUG + +#ifdef SSH_DECRYPT_DEBUG +static const gchar *ssh_debug_file_name = NULL; +#endif + +// 29418/tcp: Gerrit Code Review +#define TCP_RANGE_SSH "22,29418" +#define SCTP_PORT_SSH 22 + +/* Message Numbers (from RFC 4250) (1-255) */ + +/* Transport layer protocol: generic (1-19) */ +#define SSH_MSG_DISCONNECT 1 +#define SSH_MSG_IGNORE 2 +#define SSH_MSG_UNIMPLEMENTED 3 +#define SSH_MSG_DEBUG 4 +#define SSH_MSG_SERVICE_REQUEST 5 +#define SSH_MSG_SERVICE_ACCEPT 6 + +/* Transport layer protocol: Algorithm negotiation (20-29) */ +#define SSH_MSG_KEXINIT 20 +#define SSH_MSG_NEWKEYS 21 + +/* 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_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 + +#define SSH_MSG_KEX_ECDH_INIT 30 +#define SSH_MSG_KEX_ECDH_REPLY 31 + +/* User authentication protocol: generic (50-59) */ +#define SSH_MSG_USERAUTH_REQUEST 50 +#define SSH_MSG_USERAUTH_FAILURE 51 +#define SSH_MSG_USERAUTH_SUCCESS 52 +#define SSH_MSG_USERAUTH_BANNER 53 + +/* User authentication protocol: method specific (reusable) (50-79) */ +#define SSH_MSG_USERAUTH_PK_OK 60 + +/* Connection protocol: generic (80-89) */ +#define SSH_MSG_GLOBAL_REQUEST 80 +#define SSH_MSG_REQUEST_SUCCESS 81 +#define SSH_MSG_REQUEST_FAILURE 82 + +/* Connection protocol: channel related messages (90-127) */ +#define SSH_MSG_CHANNEL_OPEN 90 +#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 91 +#define SSH_MSG_CHANNEL_OPEN_FAILURE 92 +#define SSH_MSG_CHANNEL_WINDOW_ADJUST 93 +#define SSH_MSG_CHANNEL_DATA 94 +#define SSH_MSG_CHANNEL_EXTENDED_DATA 95 +#define SSH_MSG_CHANNEL_EOF 96 +#define SSH_MSG_CHANNEL_CLOSE 97 +#define SSH_MSG_CHANNEL_REQUEST 98 +#define SSH_MSG_CHANNEL_SUCCESS 99 +#define SSH_MSG_CHANNEL_FAILURE 100 + +/* 128-191 reserved for client protocols */ +/* 192-255 local extensions */ + +#define CIPHER_AES128_CTR 0x00010001 +#define CIPHER_AES192_CTR 0x00010003 +#define CIPHER_AES256_CTR 0x00010004 +#define CIPHER_AES128_CBC 0x00020001 +#define CIPHER_AES192_CBC 0x00020002 +#define CIPHER_AES256_CBC 0x00020004 +#define CIPHER_AES128_GCM 0x00040001 +//#define CIPHER_AES192_GCM 0x00040002 -- does not exist +#define CIPHER_AES256_GCM 0x00040004 + +#define CIPHER_MAC_SHA2_256 0x00020001 + +static const value_string ssh_direction_vals[] = { + { CLIENT_TO_SERVER_PROPOSAL, "client-to-server" }, + { SERVER_TO_CLIENT_PROPOSAL, "server-to-client" }, + { 0, NULL } +}; + +static const value_string ssh2_msg_vals[] = { + { SSH_MSG_DISCONNECT, "Disconnect" }, + { SSH_MSG_IGNORE, "Ignore" }, + { SSH_MSG_UNIMPLEMENTED, "Unimplemented" }, + { SSH_MSG_DEBUG, "Debug" }, + { SSH_MSG_SERVICE_REQUEST, "Service Request" }, + { SSH_MSG_SERVICE_ACCEPT, "Service Accept" }, + { SSH_MSG_KEXINIT, "Key Exchange Init" }, + { SSH_MSG_NEWKEYS, "New Keys" }, + { SSH_MSG_USERAUTH_REQUEST, "User Authentication Request" }, + { SSH_MSG_USERAUTH_FAILURE, "User Authentication Failure" }, + { SSH_MSG_USERAUTH_SUCCESS, "User Authentication Success" }, + { SSH_MSG_USERAUTH_BANNER, "User Authentication Banner" }, + { SSH_MSG_GLOBAL_REQUEST, "Global Request" }, + { SSH_MSG_REQUEST_SUCCESS, "Request Success" }, + { SSH_MSG_REQUEST_FAILURE, "Request Failure" }, + { SSH_MSG_CHANNEL_OPEN, "Channel Open" }, + { SSH_MSG_CHANNEL_OPEN_CONFIRMATION, "Channel Open Confirmation" }, + { SSH_MSG_CHANNEL_OPEN_FAILURE, "Channel Open Failure" }, + { SSH_MSG_CHANNEL_WINDOW_ADJUST, "Window Adjust" }, + { SSH_MSG_CHANNEL_DATA, "Channel Data" }, + { SSH_MSG_CHANNEL_EXTENDED_DATA, "Channel Extended Data" }, + { SSH_MSG_CHANNEL_EOF, "Channel EOF" }, + { SSH_MSG_CHANNEL_CLOSE, "Channel Close" }, + { SSH_MSG_CHANNEL_REQUEST, "Channel Request" }, + { SSH_MSG_CHANNEL_SUCCESS, "Channel Success" }, + { SSH_MSG_CHANNEL_FAILURE, "Channel Failure" }, + { SSH_MSG_USERAUTH_PK_OK, "Public Key algorithm accepted" }, + { 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 ssh2_kex_ecdh_msg_vals[] = { + { SSH_MSG_KEX_ECDH_INIT, "Elliptic Curve Diffie-Hellman Key Exchange Init" }, + { SSH_MSG_KEX_ECDH_REPLY, "Elliptic Curve Diffie-Hellman Key Exchange Reply" }, + { 0, NULL } +}; + +static const value_string ssh1_msg_vals[] = { + {SSH1_MSG_NONE, "No Message"}, + {SSH1_MSG_DISCONNECT, "Disconnect"}, + {SSH1_SMSG_PUBLIC_KEY, "Public Key"}, + {SSH1_CMSG_SESSION_KEY, "Session Key"}, + {SSH1_CMSG_USER, "User"}, + {0, NULL} +}; + +static int ssh_dissect_key_init(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, + int is_response, + struct ssh_flow_data *global_data); +static int ssh_dissect_proposal(tvbuff_t *tvb, int offset, proto_tree *tree, + int hf_index_length, int hf_index_value, char **store); +static int ssh_dissect_ssh1(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_ssh2(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_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, + struct ssh_flow_data *global_data, guint *seq_num); +static int ssh_dissect_kex_dh_gex(guint8 msg_code, tvbuff_t *tvb, + packet_info *pinfo, int offset, proto_tree *tree, + struct ssh_flow_data *global_data, guint *seq_num); +static int ssh_dissect_kex_ecdh(guint8 msg_code, tvbuff_t *tvb, + packet_info *pinfo, int offset, proto_tree *tree, + struct ssh_flow_data *global_data, guint *seq_num); +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, + gboolean *need_desegmentation); +static int ssh_try_dissect_encrypted_packet(tvbuff_t *tvb, packet_info *pinfo, + struct ssh_peer_data *peer_data, int offset, proto_tree *tree); +static int ssh_dissect_encrypted_packet(tvbuff_t *tvb, packet_info *pinfo, + struct ssh_peer_data *peer_data, + int offset, proto_tree *tree); +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 ssh_keylog_read_file(void); +static void ssh_keylog_process_line(const char *line); +static void ssh_keylog_process_lines(const guint8 *data, guint datalen); +static void ssh_keylog_reset(void); +static ssh_bignum *ssh_kex_make_bignum(const guint8 *data, guint length); +static gboolean ssh_read_e(tvbuff_t *tvb, int offset, + struct ssh_flow_data *global_data); +static gboolean ssh_read_f(tvbuff_t *tvb, int offset, + struct ssh_flow_data *global_data); +#ifdef SSH_DECRYPTION_SUPPORTED +static ssh_bignum * ssh_read_mpint(tvbuff_t *tvb, int offset); +#endif +static void ssh_keylog_hash_write_secret(struct ssh_flow_data *global_data); +static ssh_bignum *ssh_kex_shared_secret(gint kex_type, ssh_bignum *pub, ssh_bignum *priv, ssh_bignum *modulo); +static void ssh_hash_buffer_put_string(wmem_array_t *buffer, const gchar *string, + guint len); +static void ssh_hash_buffer_put_uint32(wmem_array_t *buffer, guint val); +static gchar *ssh_string(const gchar *string, guint len); +static void ssh_derive_symmetric_keys(ssh_bignum *shared_secret, + gchar *exchange_hash, guint hash_length, + struct ssh_flow_data *global_data); +static void ssh_derive_symmetric_key(ssh_bignum *shared_secret, + gchar *exchange_hash, guint hash_length, gchar id, + ssh_bignum *result_key, struct ssh_flow_data *global_data, guint we_need); + +static void ssh_choose_enc_mac(struct ssh_flow_data *global_data); +static void ssh_decryption_set_cipher_id(struct ssh_peer_data *peer); +static void ssh_decryption_setup_cipher(struct ssh_peer_data *peer, + ssh_bignum *iv, ssh_bignum *key); +static void ssh_decryption_set_mac_id(struct ssh_peer_data *peer); +static void ssh_decryption_setup_mac(struct ssh_peer_data *peer, + ssh_bignum *iv); +static void ssh_increment_message_number(packet_info *pinfo, + struct ssh_flow_data *global_data, gboolean is_response); +static guint ssh_decrypt_packet(tvbuff_t *tvb, packet_info *pinfo, + struct ssh_peer_data *peer_data, int offset, proto_tree *tree); +static gboolean ssh_decrypt_chacha20(gcry_cipher_hd_t hd, guint32 seqnr, + guint32 counter, const guchar *ctext, guint ctext_len, + guchar *plain, guint plain_len); +static proto_item * ssh_tree_add_mac(proto_tree *tree, tvbuff_t *tvb, const guint offset, const guint mac_len, + const int hf_mac, const int hf_mac_status, struct expert_field* bad_checksum_expert, + packet_info *pinfo, const guint8 * calc_mac, const guint flags); + +static int ssh_dissect_decrypted_packet(tvbuff_t *tvb, packet_info *pinfo, + struct ssh_peer_data *peer_data, proto_tree *tree, + gchar *plaintext, guint plaintext_len); +static int ssh_dissect_transport_generic(tvbuff_t *packet_tvb, packet_info *pinfo, + int offset, proto_item *msg_type_tree, guint msg_code); +static int ssh_dissect_userauth_generic(tvbuff_t *packet_tvb, packet_info *pinfo, + int offset, proto_item *msg_type_tree, guint msg_code); +static int ssh_dissect_userauth_specific(tvbuff_t *packet_tvb, packet_info *pinfo, + int offset, proto_item *msg_type_tree, guint msg_code); +static int ssh_dissect_connection_specific(tvbuff_t *packet_tvb, packet_info *pinfo, + struct ssh_peer_data *peer_data, int offset, proto_item *msg_type_tree, + guint msg_code); +static int ssh_dissect_connection_generic(tvbuff_t *packet_tvb, packet_info *pinfo, + int offset, proto_item *msg_type_tree, guint msg_code); +static int ssh_dissect_public_key_blob(tvbuff_t *packet_tvb, packet_info *pinfo, + int offset, proto_item *msg_type_tree); +static int ssh_dissect_public_key_signature(tvbuff_t *packet_tvb, packet_info *pinfo, + int offset, proto_item *msg_type_tree); + +static dissector_handle_t get_subdissector_for_channel(struct ssh_peer_data *peer_data, guint uiNumChannel); +static void set_subdissector_for_channel(struct ssh_peer_data *peer_data, guint uiNumChannel, guint8* subsystem_name); + +#define SSH_DEBUG_USE_STDERR "-" + +#ifdef SSH_DECRYPT_DEBUG +static void +ssh_debug_printf(const gchar* fmt,...) G_GNUC_PRINTF(1,2); +static void +ssh_print_data(const gchar* name, const guchar* data, size_t len); +static void +ssh_set_debug(const gchar* name); +static void +ssh_debug_flush(void); +#else + +/* No debug: nullify debug operation*/ +static inline void G_GNUC_PRINTF(1,2) +ssh_debug_printf(const gchar* fmt _U_,...) +{ +} +#define ssh_print_data(a, b, c) +#define ssh_print_string(a, b) +#define ssh_set_debug(name) +#define ssh_debug_flush() + +#endif /* SSH_DECRYPT_DEBUG */ + +static int +dissect_ssh(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + proto_tree *ssh_tree; + proto_item *ti; + conversation_t *conversation; + int last_offset, offset = 0; + + gboolean is_response = (pinfo->destport != pinfo->match_uint), + need_desegmentation; + guint version; + + struct ssh_flow_data *global_data = NULL; + struct ssh_peer_data *peer_data; + + ssh_debug_printf("\ndissect_ssh enter frame #%u (%s)\n", pinfo->num, (pinfo->fd->visited)?"already visited":"first time"); + + conversation = find_or_create_conversation(pinfo); + + global_data = (struct ssh_flow_data *)conversation_get_proto_data(conversation, proto_ssh); + if (!global_data) { + global_data = wmem_new0(wmem_file_scope(), 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; + global_data->peer_data[CLIENT_PEER_DATA].sequence_number = 0; + global_data->peer_data[SERVER_PEER_DATA].sequence_number = 0; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_kex_init = 0; + global_data->peer_data[SERVER_PEER_DATA].seq_num_kex_init = 0; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_gex_req = 0; + global_data->peer_data[SERVER_PEER_DATA].seq_num_gex_req = 0; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_gex_grp = 0; + global_data->peer_data[SERVER_PEER_DATA].seq_num_gex_grp = 0; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_gex_ini = 0; + global_data->peer_data[SERVER_PEER_DATA].seq_num_gex_ini = 0; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_gex_rep = 0; + global_data->peer_data[SERVER_PEER_DATA].seq_num_gex_rep = 0; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_ecdh_ini = 0; + global_data->peer_data[SERVER_PEER_DATA].seq_num_ecdh_ini = 0; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_ecdh_rep = 0; + global_data->peer_data[SERVER_PEER_DATA].seq_num_ecdh_rep = 0; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_dh_ini = 0; + global_data->peer_data[SERVER_PEER_DATA].seq_num_dh_ini = 0; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_dh_rep = 0; + global_data->peer_data[SERVER_PEER_DATA].seq_num_dh_rep = 0; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_new_key = 0; + global_data->peer_data[SERVER_PEER_DATA].seq_num_new_key = 0; + global_data->peer_data[CLIENT_PEER_DATA].bn_cookie = NULL; + global_data->peer_data[SERVER_PEER_DATA].bn_cookie = NULL; + global_data->peer_data[CLIENT_PEER_DATA].global_data = global_data; + global_data->peer_data[SERVER_PEER_DATA].global_data = global_data; + global_data->channel_info = NULL; + global_data->kex_client_version = wmem_array_new(wmem_file_scope(), 1); + global_data->kex_server_version = wmem_array_new(wmem_file_scope(), 1); + global_data->kex_client_key_exchange_init = wmem_array_new(wmem_file_scope(), 1); + global_data->kex_server_key_exchange_init = wmem_array_new(wmem_file_scope(), 1); + global_data->kex_server_host_key_blob = wmem_array_new(wmem_file_scope(), 1); + global_data->kex_gex_bits_min = wmem_array_new(wmem_file_scope(), 1); + global_data->kex_gex_bits_req = wmem_array_new(wmem_file_scope(), 1); + global_data->kex_gex_bits_max = wmem_array_new(wmem_file_scope(), 1); + global_data->kex_shared_secret = wmem_array_new(wmem_file_scope(), 1); + global_data->do_decrypt = TRUE; + + conversation_add_proto_data(conversation, proto_ssh, global_data); + } + + peer_data = &global_data->peer_data[is_response]; + + ti = proto_tree_add_item(tree, proto_ssh, tvb, offset, -1, ENC_NA); + ssh_tree = proto_item_add_subtree(ti, ett_ssh); + + version = global_data->version; + + 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; + + } + + col_clear(pinfo->cinfo, COL_INFO); + + while(tvb_reported_length_remaining(tvb, offset)> 0) { + gboolean after_version_start = (peer_data->frame_version_start == 0 || + pinfo->num >= peer_data->frame_version_start); + gboolean before_version_end = (peer_data->frame_version_end == 0 || + pinfo->num <= peer_data->frame_version_end); + + need_desegmentation = FALSE; + last_offset = offset; + + peer_data->counter++; + + if (after_version_start && before_version_end && + (tvb_strncaseeql(tvb, offset, "SSH-", 4) == 0)) { + if (peer_data->frame_version_start == 0) + peer_data->frame_version_start = pinfo->num; + + offset = ssh_dissect_protocol(tvb, pinfo, + global_data, + offset, ssh_tree, is_response, + &version, &need_desegmentation); + + if (!need_desegmentation) { + peer_data->frame_version_end = pinfo->num; + global_data->version = version; + } + } else { + switch(version) { + + case SSH_VERSION_UNKNOWN: + offset = ssh_try_dissect_encrypted_packet(tvb, pinfo, + &global_data->peer_data[is_response], offset, ssh_tree); + break; + + case SSH_VERSION_1: + offset = ssh_dissect_ssh1(tvb, pinfo, global_data, + offset, ssh_tree, is_response, + &need_desegmentation); + break; + + case SSH_VERSION_2: + offset = ssh_dissect_ssh2(tvb, pinfo, global_data, + offset, ssh_tree, is_response, + &need_desegmentation); + break; + } + } + + if (need_desegmentation) + return tvb_captured_length(tvb); + if (offset <= last_offset) { + /* XXX - add an expert info in the function + that decrements offset */ + break; + } + } + + col_prepend_fstr(pinfo->cinfo, COL_INFO, "%s: ", is_response ? "Server" : "Client"); + ti = proto_tree_add_boolean_format_value(ssh_tree, hf_ssh_direction, tvb, 0, 0, is_response, "%s", + try_val_to_str(is_response, ssh_direction_vals)); + proto_item_set_generated(ti); + + ssh_debug_flush(); + + return tvb_captured_length(tvb); +} + +static int +ssh_dissect_ssh2(tvbuff_t *tvb, packet_info *pinfo, + struct ssh_flow_data *global_data, + int offset, proto_tree *tree, int is_response, + gboolean *need_desegmentation) +{ + proto_item *ssh2_tree = NULL; + gint remain_length; + + struct ssh_peer_data *peer_data = &global_data->peer_data[is_response]; + + remain_length = tvb_captured_length_remaining(tvb, offset); + + while(remain_length>0){ + int last_offset = offset; + if (tree) { + wmem_strbuf_t *title = wmem_strbuf_new(wmem_packet_scope(), "SSH Version 2"); + + if (peer_data->enc || peer_data->mac || peer_data->comp) { + wmem_strbuf_append_printf(title, " ("); + if (peer_data->enc) + wmem_strbuf_append_printf(title, "encryption:%s%s", + peer_data->enc, + peer_data->mac || peer_data->comp + ? " " : ""); + if (peer_data->mac) + wmem_strbuf_append_printf(title, "mac:%s%s", + peer_data->mac, + peer_data->comp ? " " : ""); + if (peer_data->comp) + wmem_strbuf_append_printf(title, "compression:%s", + peer_data->comp); + wmem_strbuf_append_printf(title, ")"); + } + + ssh2_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_ssh2, NULL, wmem_strbuf_get_str(title)); + } + ws_noisy("....ssh_dissect_ssh2[%c]: frame_key_start=%d, pinfo->num=%d, frame_key_end=%d, offset=%d, frame_key_end_offset=%d ", is_response==SERVER_PEER_DATA?'S':'C', peer_data->frame_key_start, pinfo->num, peer_data->frame_key_end, offset, peer_data->frame_key_end_offset); + if ((peer_data->frame_key_start == 0) || + ((peer_data->frame_key_start <= pinfo->num) && + ((peer_data->frame_key_end == 0) || (pinfo->num < peer_data->frame_key_end) || + ((pinfo->num == peer_data->frame_key_end) && (offset < peer_data->frame_key_end_offset))))) { + offset = ssh_dissect_key_exchange(tvb, pinfo, global_data, + offset, ssh2_tree, is_response, + need_desegmentation); + + if (!*need_desegmentation) { + ssh_increment_message_number(pinfo, global_data, is_response); + }else{ + break; + } + } else { + if(!*need_desegmentation){ + offset = ssh_try_dissect_encrypted_packet(tvb, pinfo, + &global_data->peer_data[is_response], offset, ssh2_tree); + }else{ + break; + } + } + + if (ssh2_tree) { + proto_item_set_len(ssh2_tree, offset - last_offset); + } + + remain_length = tvb_captured_length_remaining(tvb, offset); + } + + return offset; +} +static int +ssh_dissect_ssh1(tvbuff_t *tvb, packet_info *pinfo, + struct ssh_flow_data *global_data, + int offset, proto_tree *tree, int is_response, + gboolean *need_desegmentation) +{ + guint plen, padding_length, len; + guint8 msg_code; + guint remain_length; + + proto_item *ssh1_tree; + + struct ssh_peer_data *peer_data = &global_data->peer_data[is_response]; + + ssh1_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_ssh1, NULL, "SSH Version 1"); + + /* + * We use "tvb_ensure_captured_length_remaining()" to make sure there + * actually *is* data remaining. + * + * This means we're guaranteed that "remain_length" is positive. + */ + remain_length = tvb_ensure_captured_length_remaining(tvb, offset); + /* + * Can we do reassembly? + */ + if (ssh_desegment && pinfo->can_desegment) { + /* + * Yes - would an SSH header starting at this offset be split + * across segment boundaries? + */ + if (remain_length < 4) { + /* + * Yes. Tell the TCP dissector where the data for + * this message starts in the data it handed us and + * that we need "some more data." Don't tell it + * exactly how many bytes we need because if/when we + * ask for even more (after the header) that will + * break reassembly. + */ + pinfo->desegment_offset = offset; + pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT; + *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 (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 */ + + proto_tree_add_uint(ssh1_tree, hf_ssh_padding_length, tvb, + offset, padding_length, padding_length); + offset += padding_length; + + /* msg_code */ + if ((peer_data->frame_key_start == 0) || + ((peer_data->frame_key_start >= pinfo->num) && (pinfo->num <= peer_data->frame_key_end))) { + msg_code = tvb_get_guint8(tvb, offset); + + proto_tree_add_item(ssh1_tree, hf_ssh_msg_code, tvb, offset, 1, ENC_BIG_ENDIAN); + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, + val_to_str(msg_code, ssh1_msg_vals, "Unknown (%u)")); + offset += 1; + len = plen -1; + if (!pinfo->fd->visited) { + if (peer_data->frame_key_start == 0) + peer_data->frame_key_start = pinfo->num; + peer_data->frame_key_end = pinfo->num; + } + } else { + len = plen; + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Encrypted packet (len=%d)", len); + } + /* payload */ + if (ssh1_tree) { + proto_tree_add_item(ssh1_tree, hf_ssh_payload, + tvb, offset, len, ENC_NA); + } + offset += len; + + return offset; +} + +static int +ssh_tree_add_mpint(tvbuff_t *tvb, int offset, proto_tree *tree, + int hf_ssh_mpint_selection) +{ + guint len = tvb_get_ntohl(tvb, offset); + proto_tree_add_uint(tree, hf_ssh_mpint_length, tvb, + offset, 4, len); + offset+=4; + proto_tree_add_item(tree, hf_ssh_mpint_selection, + tvb, offset, len, ENC_NA); + return 4+len; +} + +static int +ssh_tree_add_string(tvbuff_t *tvb, int offset, proto_tree *tree, + int hf_ssh_string, int hf_ssh_string_length) +{ + guint len = tvb_get_ntohl(tvb, offset); + proto_tree_add_uint(tree, hf_ssh_string_length, tvb, + offset, 4, len); + offset+=4; + proto_tree_add_item(tree, hf_ssh_string, + tvb, offset, len, ENC_NA); + return 4+len; +} + +static guint +ssh_tree_add_hostkey(tvbuff_t *tvb, int offset, proto_tree *parent_tree, + const char *tree_name, int ett_idx, + struct ssh_flow_data *global_data) +{ + proto_tree *tree = NULL; + int last_offset; + int remaining_len; + guint key_len, type_len; + char* key_type; + gchar *tree_title; + + last_offset = offset; + + key_len = tvb_get_ntohl(tvb, offset); + offset += 4; + + /* Read the key type before creating the tree so we can append it as info. */ + type_len = tvb_get_ntohl(tvb, offset); + offset += 4; + key_type = (char *) tvb_get_string_enc(wmem_packet_scope(), tvb, offset, type_len, ENC_ASCII|ENC_NA); + + tree_title = wmem_strdup_printf(wmem_packet_scope(), "%s (type: %s)", tree_name, key_type); + tree = proto_tree_add_subtree(parent_tree, tvb, last_offset, key_len + 4, ett_idx, NULL, + tree_title); + + proto_tree_add_uint(tree, hf_ssh_hostkey_length, tvb, last_offset, 4, key_len); + + // server host key (K_S / Q) + gchar *data = (gchar *)tvb_memdup(wmem_packet_scope(), tvb, last_offset + 4, key_len); + ssh_hash_buffer_put_string(global_data->kex_server_host_key_blob, data, key_len); + + last_offset += 4; + proto_tree_add_uint(tree, hf_ssh_hostkey_type_length, tvb, last_offset, 4, type_len); + proto_tree_add_string(tree, hf_ssh_hostkey_type, tvb, offset, type_len, key_type); + offset += type_len; + + if (0 == strcmp(key_type, "ssh-rsa")) { + offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_hostkey_rsa_e); + ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_hostkey_rsa_n); + } else if (0 == strcmp(key_type, "ssh-dss")) { + offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_hostkey_dsa_p); + offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_hostkey_dsa_q); + offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_hostkey_dsa_g); + ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_hostkey_dsa_y); + } else if (g_str_has_prefix(key_type, "ecdsa-sha2-")) { + offset += ssh_tree_add_string(tvb, offset, tree, + hf_ssh_hostkey_ecdsa_curve_id, hf_ssh_hostkey_ecdsa_curve_id_length); + ssh_tree_add_string(tvb, offset, tree, + hf_ssh_hostkey_ecdsa_q, hf_ssh_hostkey_ecdsa_q_length); + } else if (g_str_has_prefix(key_type, "ssh-ed")) { + ssh_tree_add_string(tvb, offset, tree, + hf_ssh_hostkey_eddsa_key, hf_ssh_hostkey_eddsa_key_length); + } else { + remaining_len = key_len - (type_len + 4); + proto_tree_add_item(tree, hf_ssh_hostkey_data, tvb, offset, remaining_len, ENC_NA); + } + + return 4+key_len; +} + +static guint +ssh_tree_add_hostsignature(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *parent_tree, + const char *tree_name, int ett_idx, + struct ssh_flow_data *global_data) +{ + (void)global_data; + proto_tree *tree = NULL; + proto_item* ti = NULL; + int last_offset; + int offset0 = offset; + int remaining_len; + guint sig_len, type_len; + guint8* sig_type; + gchar *tree_title; + + last_offset = offset; + + sig_len = tvb_get_ntohl(tvb, offset); + offset += 4; + + /* Read the signature type before creating the tree so we can append it as info. */ + type_len = tvb_get_ntohl(tvb, offset); + offset += 4; + sig_type = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, type_len, ENC_ASCII|ENC_NA); + + tree_title = wmem_strdup_printf(wmem_packet_scope(), "%s (type: %s)", tree_name, sig_type); + tree = proto_tree_add_subtree(parent_tree, tvb, last_offset, sig_len + 4, ett_idx, NULL, + tree_title); + + ti = proto_tree_add_uint(tree, hf_ssh_hostsig_length, tvb, last_offset, 4, sig_len); + + last_offset += 4; + proto_tree_add_uint(tree, hf_ssh_hostsig_type_length, tvb, last_offset, 4, type_len); + proto_tree_add_string(tree, hf_ssh_hostsig_type, tvb, offset, type_len, sig_type); + offset += type_len; + + if (0 == strcmp(sig_type, "ssh-rsa")) { + offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_hostsig_rsa); + } else if (0 == strcmp(sig_type, "ssh-dss")) { + offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_hostsig_dsa); +// } else if (g_str_has_prefix(sig_type, "ecdsa-sha2-")) { +// offset += ssh_tree_add_string(tvb, offset, tree, +// hf_ssh_hostkey_ecdsa_curve_id, hf_ssh_hostkey_ecdsa_curve_id_length); +// ssh_tree_add_string(tvb, offset, tree, +// hf_ssh_hostkey_ecdsa_q, hf_ssh_hostkey_ecdsa_q_length); +// } else if (g_str_has_prefix(sig_type, "ssh-ed")) { +// ssh_tree_add_string(tvb, offset, tree, +// hf_ssh_hostkey_eddsa_key, hf_ssh_hostkey_eddsa_key_length); + } else { + remaining_len = sig_len - (type_len + 4); + proto_tree_add_item(tree, hf_ssh_hostsig_data, tvb, offset, remaining_len, ENC_NA); + offset += remaining_len; + } + + if(offset-offset0!=(int)(4+sig_len)){ + expert_add_info_format(pinfo, ti, &ei_ssh_packet_decode, "Decoded %d bytes, but packet length is %d bytes", offset-offset0, sig_len); + } + + return 4+sig_len; +} + +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) +{ + guint plen, len; + guint8 padding_length; + guint remain_length; + int last_offset = offset; + guint msg_code; + guint seq_num = 0; + + proto_item *ti; + proto_item *key_ex_tree = NULL; + const gchar *key_ex_title = "Key Exchange"; + + struct ssh_peer_data *peer_data = &global_data->peer_data[is_response]; + + /* + * We use "tvb_ensure_captured_length_remaining()" to make sure there + * actually *is* data remaining. + * + * This means we're guaranteed that "remain_length" is positive. + */ + remain_length = tvb_ensure_captured_length_remaining(tvb, offset); + /* + * Can we do reassembly? + */ + if (ssh_desegment && pinfo->can_desegment) { + /* + * Yes - would an SSH header starting at this offset + * be split across segment boundaries? + */ + if (remain_length < 4) { + /* + * Yes. Tell the TCP dissector where the data for + * this message starts in the data it handed us and + * that we need "some more data." Don't tell it + * exactly how many bytes we need because if/when we + * ask for even more (after the header) that will + * break reassembly. + */ + pinfo->desegment_offset = offset; + pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT; + *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 + */ + + ti = proto_tree_add_uint(tree, hf_ssh_packet_length, tvb, + offset, 4, plen); + if (plen >= 0xffff) { + expert_add_info_format(pinfo, ti, &ei_ssh_packet_length, "Overly large number %d", plen); + plen = remain_length-4; + } + offset+=4; + + /* padding length */ + padding_length = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(tree, hf_ssh_padding_length, tvb, offset, 1, padding_length); + offset += 1; + + if (global_data->kex) + key_ex_title = wmem_strdup_printf(wmem_packet_scope(), "%s (method:%s)", key_ex_title, global_data->kex); + key_ex_tree = proto_tree_add_subtree(tree, tvb, offset, plen-1, ett_key_exchange, NULL, key_ex_title); + + /* msg_code */ + msg_code = tvb_get_guint8(tvb, offset); + + if (msg_code >= 30 && msg_code < 40) { + offset = global_data->kex_specific_dissector(msg_code, tvb, pinfo, + offset, key_ex_tree, global_data, &seq_num); + } else { + proto_tree_add_item(key_ex_tree, hf_ssh2_msg_code, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + 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: + offset = ssh_dissect_key_init(tvb, pinfo, offset, key_ex_tree, is_response, global_data); + if ((peer_data->frame_key_start == 0) || (peer_data->frame_key_start == pinfo->num)) { + if (!PINFO_FD_VISITED(pinfo)) { + peer_data->frame_key_start = pinfo->num; + if(global_data->peer_data[is_response].seq_num_kex_init == 0){ + global_data->peer_data[is_response].seq_num_kex_init = global_data->peer_data[is_response].sequence_number; + global_data->peer_data[is_response].sequence_number++; + ssh_debug_printf("%s->sequence_number{SSH_MSG_KEXINIT=%d}++ > %d\n", is_response?"server":"client", global_data->peer_data[is_response].seq_num_kex_init, global_data->peer_data[is_response].sequence_number); + } + } + } + seq_num = global_data->peer_data[is_response].seq_num_kex_init; + break; + case SSH_MSG_NEWKEYS: + if (peer_data->frame_key_end == 0) { + peer_data->frame_key_end = pinfo->num; + peer_data->frame_key_end_offset = offset; + + if(global_data->peer_data[is_response].seq_num_new_key == 0){ + global_data->peer_data[is_response].seq_num_new_key = global_data->peer_data[is_response].sequence_number; + global_data->peer_data[is_response].sequence_number++; + ssh_debug_printf("%s->sequence_number{SSH_MSG_NEWKEYS=%d}++ > %d\n", is_response?"server":"client", global_data->peer_data[is_response].seq_num_new_key, global_data->peer_data[is_response].sequence_number); + } + + // the client sent SSH_MSG_NEWKEYS + if (!is_response) { + ssh_debug_printf("Activating new keys for CLIENT => SERVER\n"); + ssh_decryption_setup_cipher(&global_data->peer_data[CLIENT_PEER_DATA], &global_data->new_keys[0], &global_data->new_keys[2]); + ssh_decryption_setup_mac(&global_data->peer_data[CLIENT_PEER_DATA], &global_data->new_keys[4]); + }else{ + ssh_debug_printf("Activating new keys for SERVER => CLIENT\n"); + ssh_decryption_setup_cipher(&global_data->peer_data[SERVER_PEER_DATA], &global_data->new_keys[1], &global_data->new_keys[3]); + ssh_decryption_setup_mac(&global_data->peer_data[SERVER_PEER_DATA], &global_data->new_keys[5]); + } + } + seq_num = global_data->peer_data[is_response].seq_num_new_key; + + break; + } + } + + len = plen+4-padding_length-(offset-last_offset); + if (len > 0) { + proto_tree_add_item(key_ex_tree, hf_ssh_payload, tvb, offset, len, ENC_NA); + } + offset += len; + + /* padding */ + proto_tree_add_item(tree, hf_ssh_padding_string, tvb, offset, padding_length, ENC_NA); + offset+= padding_length; + proto_tree_add_uint(tree, hf_ssh_seq_num, tvb, offset, 0, seq_num); + + return offset; +} + +static int ssh_dissect_kex_dh(guint8 msg_code, tvbuff_t *tvb, + packet_info *pinfo, int offset, proto_tree *tree, + struct ssh_flow_data *global_data, guint *seq_num) +{ + *seq_num = 0; + proto_tree_add_item(tree, hf_ssh2_kex_dh_msg_code, tvb, offset, 1, ENC_BIG_ENDIAN); + 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: + // e (client ephemeral key public part) + if (!ssh_read_e(tvb, offset, global_data)) { + proto_tree_add_expert_format(tree, pinfo, &ei_ssh_invalid_keylen, tvb, offset, 2, + "Invalid key length: %u", tvb_get_ntohl(tvb, offset)); + } + + offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_dh_e); + if(global_data->peer_data[CLIENT_PEER_DATA].seq_num_dh_ini == 0){ + global_data->peer_data[CLIENT_PEER_DATA].sequence_number++; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_dh_ini = global_data->peer_data[CLIENT_PEER_DATA].sequence_number; + ssh_debug_printf("%s->sequence_number{SSH_MSG_KEXDH_INIT}++ > %d\n", CLIENT_PEER_DATA?"serveur":"client", global_data->peer_data[CLIENT_PEER_DATA].sequence_number); + } + *seq_num = global_data->peer_data[CLIENT_PEER_DATA].seq_num_dh_ini; + break; + + case SSH_MSG_KEXDH_REPLY: + offset += ssh_tree_add_hostkey(tvb, offset, tree, "KEX host key", + ett_key_exchange_host_key, global_data); + + // f (server ephemeral key public part), K_S (host key) + if (!ssh_read_f(tvb, offset, global_data)) { + proto_tree_add_expert_format(tree, pinfo, &ei_ssh_invalid_keylen, tvb, offset, 2, + "Invalid key length: %u", tvb_get_ntohl(tvb, offset)); + } + ssh_choose_enc_mac(global_data); + ssh_keylog_hash_write_secret(global_data); + + offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_dh_f); + offset += ssh_tree_add_hostsignature(tvb, pinfo, offset, tree, "KEX host signature", + ett_key_exchange_host_sig, global_data); + if(global_data->peer_data[SERVER_PEER_DATA].seq_num_dh_rep == 0){ + global_data->peer_data[SERVER_PEER_DATA].sequence_number++; + global_data->peer_data[SERVER_PEER_DATA].seq_num_dh_rep = global_data->peer_data[SERVER_PEER_DATA].sequence_number; + ssh_debug_printf("%s->sequence_number{SSH_MSG_KEXDH_REPLY}++ > %d\n", SERVER_PEER_DATA?"serveur":"client", global_data->peer_data[SERVER_PEER_DATA].sequence_number); + } + *seq_num = global_data->peer_data[SERVER_PEER_DATA].seq_num_dh_rep; + break; + } + + return offset; +} + +static int ssh_dissect_kex_dh_gex(guint8 msg_code, tvbuff_t *tvb, + packet_info *pinfo, int offset, proto_tree *tree, + struct ssh_flow_data *global_data, guint *seq_num) +{ + *seq_num = 0; + proto_tree_add_item(tree, hf_ssh2_kex_dh_gex_msg_code, tvb, offset, 1, ENC_BIG_ENDIAN); + 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: + proto_tree_add_item(tree, hf_ssh_dh_gex_nbits, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + break; + + case SSH_MSG_KEX_DH_GEX_GROUP: +#ifdef SSH_DECRYPTION_SUPPORTED + // p (Group modulo) + global_data->kex_gex_p = ssh_read_mpint(tvb, offset); +#endif + offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_dh_gex_p); +#ifdef SSH_DECRYPTION_SUPPORTED + // g (Group generator) + global_data->kex_gex_g = ssh_read_mpint(tvb, offset); +#endif + offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_dh_gex_g); + if(global_data->peer_data[SERVER_PEER_DATA].seq_num_gex_grp == 0){ + global_data->peer_data[SERVER_PEER_DATA].sequence_number++; + global_data->peer_data[SERVER_PEER_DATA].seq_num_gex_grp = global_data->peer_data[SERVER_PEER_DATA].sequence_number; + ssh_debug_printf("%s->sequence_number{SSH_MSG_KEX_DH_GEX_GROUP}++ > %d\n", SERVER_PEER_DATA?"serveur":"client", global_data->peer_data[SERVER_PEER_DATA].sequence_number); + } + *seq_num = global_data->peer_data[SERVER_PEER_DATA].seq_num_gex_grp; + break; + + case SSH_MSG_KEX_DH_GEX_INIT: +#ifdef SSH_DECRYPTION_SUPPORTED + // e (Client public key) + if (!ssh_read_e(tvb, offset, global_data)) { + proto_tree_add_expert_format(tree, pinfo, &ei_ssh_invalid_keylen, tvb, offset, 2, + "Invalid key length: %u", tvb_get_ntohl(tvb, offset)); + } +#endif + offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_dh_e); + if(global_data->peer_data[CLIENT_PEER_DATA].seq_num_gex_ini == 0){ + global_data->peer_data[CLIENT_PEER_DATA].sequence_number++; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_gex_ini = global_data->peer_data[CLIENT_PEER_DATA].sequence_number; + ssh_debug_printf("%s->sequence_number{SSH_MSG_KEX_DH_GEX_INIT}++ > %d\n", CLIENT_PEER_DATA?"serveur":"client", global_data->peer_data[CLIENT_PEER_DATA].sequence_number); + } + *seq_num = global_data->peer_data[CLIENT_PEER_DATA].seq_num_gex_ini; + break; + + case SSH_MSG_KEX_DH_GEX_REPLY: + offset += ssh_tree_add_hostkey(tvb, offset, tree, "KEX host key", + ett_key_exchange_host_key, global_data); +#ifdef SSH_DECRYPTION_SUPPORTED + if (!PINFO_FD_VISITED(pinfo)) { + ssh_read_f(tvb, offset, global_data); + // f (server ephemeral key public part), K_S (host key) + ssh_keylog_hash_write_secret(global_data); + } + ssh_choose_enc_mac(global_data); + ssh_keylog_hash_write_secret(global_data); +#endif + offset += ssh_tree_add_mpint(tvb, offset, tree, hf_ssh_dh_f); + offset += ssh_tree_add_hostsignature(tvb, pinfo, offset, tree, "KEX host signature", + ett_key_exchange_host_sig, global_data); + if(global_data->peer_data[SERVER_PEER_DATA].seq_num_gex_rep == 0){ + global_data->peer_data[SERVER_PEER_DATA].sequence_number++; + global_data->peer_data[SERVER_PEER_DATA].seq_num_gex_rep = global_data->peer_data[SERVER_PEER_DATA].sequence_number; + ssh_debug_printf("%s->sequence_number{SSH_MSG_KEX_DH_GEX_REPLY}++ > %d\n", SERVER_PEER_DATA?"serveur":"client", global_data->peer_data[SERVER_PEER_DATA].sequence_number); + } + *seq_num = global_data->peer_data[SERVER_PEER_DATA].seq_num_gex_rep; + break; + + case SSH_MSG_KEX_DH_GEX_REQUEST:{ + + if (!PINFO_FD_VISITED(pinfo)) { + ssh_hash_buffer_put_uint32(global_data->kex_gex_bits_min, tvb_get_ntohl(tvb, offset)); + } + proto_tree_add_item(tree, hf_ssh_dh_gex_min, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + if (!PINFO_FD_VISITED(pinfo)) { + ssh_hash_buffer_put_uint32(global_data->kex_gex_bits_req, tvb_get_ntohl(tvb, offset)); + } + proto_tree_add_item(tree, hf_ssh_dh_gex_nbits, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + if (!PINFO_FD_VISITED(pinfo)) { + ssh_hash_buffer_put_uint32(global_data->kex_gex_bits_max, tvb_get_ntohl(tvb, offset)); + } + proto_tree_add_item(tree, hf_ssh_dh_gex_max, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + if(global_data->peer_data[CLIENT_PEER_DATA].seq_num_gex_req == 0){ + global_data->peer_data[CLIENT_PEER_DATA].sequence_number++; + global_data->peer_data[CLIENT_PEER_DATA].seq_num_gex_req = global_data->peer_data[CLIENT_PEER_DATA].sequence_number; + ssh_debug_printf("%s->sequence_number{SSH_MSG_KEX_DH_GEX_REQUEST}++ > %d\n", CLIENT_PEER_DATA?"serveur":"client", global_data->peer_data[CLIENT_PEER_DATA].sequence_number); + } + *seq_num = global_data->peer_data[CLIENT_PEER_DATA].seq_num_gex_req; + break; + } + } + + return offset; +} + +static int +ssh_dissect_kex_ecdh(guint8 msg_code, tvbuff_t *tvb, + packet_info *pinfo, int offset, proto_tree *tree, + struct ssh_flow_data *global_data, guint *seq_num) +{ + proto_tree_add_item(tree, hf_ssh2_kex_ecdh_msg_code, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, + val_to_str(msg_code, ssh2_kex_ecdh_msg_vals, "Unknown (%u)")); + + switch (msg_code) { + case SSH_MSG_KEX_ECDH_INIT: + if (!ssh_read_e(tvb, offset, global_data)) { + proto_tree_add_expert_format(tree, pinfo, &ei_ssh_invalid_keylen, tvb, offset, 2, + "Invalid key length: %u", tvb_get_ntohl(tvb, offset)); + } + + if (!PINFO_FD_VISITED(pinfo)) { + if(global_data->peer_data[CLIENT_PEER_DATA].seq_num_ecdh_ini == 0){ + global_data->peer_data[CLIENT_PEER_DATA].seq_num_ecdh_ini = global_data->peer_data[CLIENT_PEER_DATA].sequence_number; + global_data->peer_data[CLIENT_PEER_DATA].sequence_number++; + ssh_debug_printf("%s->sequence_number{SSH_MSG_KEX_ECDH_INIT=%d}++ > %d\n", CLIENT_PEER_DATA?"server":"client", global_data->peer_data[CLIENT_PEER_DATA].seq_num_ecdh_ini, global_data->peer_data[CLIENT_PEER_DATA].sequence_number); + } + } + *seq_num = global_data->peer_data[CLIENT_PEER_DATA].seq_num_ecdh_ini; + + offset += ssh_tree_add_string(tvb, offset, tree, hf_ssh_ecdh_q_c, hf_ssh_ecdh_q_c_length); + break; + + case SSH_MSG_KEX_ECDH_REPLY: + offset += ssh_tree_add_hostkey(tvb, offset, tree, "KEX host key", + ett_key_exchange_host_key, global_data); + + if (!ssh_read_f(tvb, offset, global_data)){ + proto_tree_add_expert_format(tree, pinfo, &ei_ssh_invalid_keylen, tvb, offset, 2, + "Invalid key length: %u", tvb_get_ntohl(tvb, offset)); + } + + ssh_choose_enc_mac(global_data); + ssh_keylog_hash_write_secret(global_data); + if(global_data->peer_data[SERVER_PEER_DATA].seq_num_ecdh_rep == 0){ + global_data->peer_data[SERVER_PEER_DATA].seq_num_ecdh_rep = global_data->peer_data[SERVER_PEER_DATA].sequence_number; + global_data->peer_data[SERVER_PEER_DATA].sequence_number++; + ssh_debug_printf("%s->sequence_number{SSH_MSG_KEX_ECDH_REPLY=%d}++ > %d\n", SERVER_PEER_DATA?"server":"client", global_data->peer_data[SERVER_PEER_DATA].seq_num_ecdh_rep, global_data->peer_data[SERVER_PEER_DATA].sequence_number); + } + *seq_num = global_data->peer_data[SERVER_PEER_DATA].seq_num_ecdh_rep; + + offset += ssh_tree_add_string(tvb, offset, tree, hf_ssh_ecdh_q_s, hf_ssh_ecdh_q_s_length); + offset += ssh_tree_add_hostsignature(tvb, pinfo, offset, tree, "KEX host signature", + ett_key_exchange_host_sig, global_data); + break; + } + + return offset; +} + +static int +ssh_try_dissect_encrypted_packet(tvbuff_t *tvb, packet_info *pinfo, + struct ssh_peer_data *peer_data, int offset, proto_tree *tree) +{ + gboolean can_decrypt = peer_data->cipher != NULL; + + if (can_decrypt) { + return ssh_decrypt_packet(tvb, pinfo, peer_data, offset, tree); + } + + return ssh_dissect_encrypted_packet(tvb, pinfo, peer_data, offset, tree); +} + +static int +ssh_dissect_encrypted_packet(tvbuff_t *tvb, packet_info *pinfo, + struct ssh_peer_data *peer_data, + int offset, proto_tree *tree) +{ + gint len; + guint plen; + + len = tvb_reported_length_remaining(tvb, offset); + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Encrypted packet (len=%d)", len); + + if (tree) { + gint encrypted_len = len; + + if (len > 4 && peer_data->length_is_plaintext) { + plen = tvb_get_ntohl(tvb, offset) ; + proto_tree_add_uint(tree, hf_ssh_packet_length, tvb, offset, 4, plen); + encrypted_len -= 4; + } + else if (len > 4) { + proto_tree_add_item(tree, hf_ssh_packet_length_encrypted, tvb, offset, 4, ENC_NA); + encrypted_len -= 4; + } + + if (peer_data->mac_length>0) + encrypted_len -= peer_data->mac_length; + + proto_tree_add_item(tree, hf_ssh_encrypted_packet, + tvb, offset+4, encrypted_len, ENC_NA); + + if (peer_data->mac_length>0) + proto_tree_add_item(tree, hf_ssh_mac_string, + tvb, offset+4+encrypted_len, + peer_data->mac_length, ENC_NA); + } + offset += len; + return offset; +} + +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, + 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, + &global_data->peer_data[is_response], offset, tree); + 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_captured_length_remaining()" to make sure there + * actually *is* data remaining. + * + * This means we're guaranteed that "remain_length" is positive. + */ + remain_length = tvb_ensure_captured_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; + + if (linelen > 1 && tvb_get_guint8(tvb, offset + linelen - 2) == '\r') + protolen = linelen - 2; + else + protolen = linelen - 1; + } + + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Protocol (%s)", + tvb_format_text(pinfo->pool, tvb, offset, protolen)); + + // V_C / V_S (client and server identification strings) RFC4253 4.2 + // format: SSH-protoversion-softwareversion SP comments [CR LF not incl.] + if (!PINFO_FD_VISITED(pinfo)) { + gchar *data = (gchar *)tvb_memdup(wmem_packet_scope(), tvb, offset, protolen); + if(!is_response){ + ssh_hash_buffer_put_string(global_data->kex_client_version, data, protolen); + }else{ + ssh_hash_buffer_put_string(global_data->kex_server_version, data, protolen); + } + } + + proto_tree_add_item(tree, hf_ssh_protocol, + tvb, offset, protolen, ENC_ASCII); + offset += linelen; + return offset; +} + +static void +ssh_set_mac_length(struct ssh_peer_data *peer_data) +{ + char *size_str; + guint32 size = 0; + char *mac_name = peer_data->mac; + char *strip; + + if (!mac_name) + return; + + /* wmem_strdup() never returns NULL */ + mac_name = wmem_strdup(NULL, (const gchar *)mac_name); + + /* strip trailing "-etm@openssh.com" or "@openssh.com" */ + strip = strstr(mac_name, "-etm@openssh.com"); + if (strip) { + peer_data->length_is_plaintext = 1; + *strip = '\0'; + } + else { + strip = strstr(mac_name, "@openssh.com"); + if (strip) *strip = '\0'; + } + + size_str = g_strrstr(mac_name, "-"); + if (size_str && ws_strtou32(size_str + 1, NULL, &size) && size > 0 && size % 8 == 0) { + peer_data->mac_length = size / 8; + } + else if (strcmp(mac_name, "hmac-sha1") == 0) { + peer_data->mac_length = 20; + } + else if (strcmp(mac_name, "hmac-md5") == 0) { + peer_data->mac_length = 16; + } + else if (strcmp(mac_name, "hmac-ripemd160") == 0) { + peer_data->mac_length = 20; + } + else if (strcmp(mac_name, "none") == 0) { + peer_data->mac_length = 0; + } + + wmem_free(NULL, mac_name); +} + +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; + } + else if (g_str_has_prefix(kex_name, "ecdh-sha2-") || + strcmp(kex_name, "curve25519-sha256@libssh.org") == 0 || + strcmp(kex_name, "curve25519-sha256") == 0 || + strcmp(kex_name, "curve448-sha512") == 0) + { + global_data->kex_specific_dissector = ssh_dissect_kex_ecdh; + } + else if (strcmp(kex_name, "diffie-hellman-group14-sha256") == 0 || + strcmp(kex_name, "diffie-hellman-group16-sha512") == 0 || + strcmp(kex_name, "diffie-hellman-group18-sha512") == 0 || + strcmp(kex_name, "diffie-hellman-group1-sha1") == 0 || + strcmp(kex_name, "diffie-hellman-group14-sha1") == 0) + { + global_data->kex_specific_dissector = ssh_dissect_kex_dh; + } +} + +static gint +ssh_gslist_compare_strings(gconstpointer a, gconstpointer b) +{ + if (a == NULL && b == NULL) + return 0; + if (a == NULL) + return -1; + if (b == NULL) + return 1; + return strcmp((const char*)a, (const char*)b); +} + +/* expects that *result is NULL */ +static void +ssh_choose_algo(gchar *client, gchar *server, gchar **result) +{ + gchar **server_strings = NULL; + gchar **client_strings = NULL; + gchar **step; + GSList *server_list = NULL; + + if (!client || !server || !result || *result) + return; + + server_strings = g_strsplit(server, ",", 0); + for (step = server_strings; *step; step++) { + server_list = g_slist_append(server_list, *step); + } + + client_strings = g_strsplit(client, ",", 0); + for (step = client_strings; *step; step++) { + GSList *agreed; + if ((agreed = g_slist_find_custom(server_list, *step, ssh_gslist_compare_strings))) { + *result = wmem_strdup(wmem_file_scope(), (const gchar *)agreed->data); + break; + } + } + + g_strfreev(client_strings); + g_slist_free(server_list); + g_strfreev(server_strings); +} + +static int +ssh_dissect_key_init(tvbuff_t *tvb, packet_info *pinfo, int offset, + proto_tree *tree, int is_response, struct ssh_flow_data *global_data) +{ + int start_offset = offset; + int payload_length; + wmem_strbuf_t *hassh_algo; + gchar *hassh; + + proto_item *tf, *ti; + proto_tree *key_init_tree; + + struct ssh_peer_data *peer_data = &global_data->peer_data[is_response]; + + key_init_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_key_init, &tf, "Algorithms"); + if (!PINFO_FD_VISITED(pinfo)) { + peer_data->bn_cookie = ssh_kex_make_bignum(tvb_get_ptr(tvb, offset, 16), 16); + } + proto_tree_add_item(key_init_tree, hf_ssh_cookie, + tvb, offset, 16, ENC_NA); + offset += 16; + + offset = ssh_dissect_proposal(tvb, offset, key_init_tree, + 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); + offset = ssh_dissect_proposal(tvb, offset, key_init_tree, + hf_ssh_encryption_algorithms_client_to_server_length, + hf_ssh_encryption_algorithms_client_to_server, + &peer_data->enc_proposals[CLIENT_TO_SERVER_PROPOSAL]); + offset = ssh_dissect_proposal(tvb, offset, key_init_tree, + hf_ssh_encryption_algorithms_server_to_client_length, + hf_ssh_encryption_algorithms_server_to_client, + &peer_data->enc_proposals[SERVER_TO_CLIENT_PROPOSAL]); + offset = ssh_dissect_proposal(tvb, offset, key_init_tree, + hf_ssh_mac_algorithms_client_to_server_length, + hf_ssh_mac_algorithms_client_to_server, + &peer_data->mac_proposals[CLIENT_TO_SERVER_PROPOSAL]); + offset = ssh_dissect_proposal(tvb, offset, key_init_tree, + hf_ssh_mac_algorithms_server_to_client_length, + hf_ssh_mac_algorithms_server_to_client, + &peer_data->mac_proposals[SERVER_TO_CLIENT_PROPOSAL]); + offset = ssh_dissect_proposal(tvb, offset, key_init_tree, + hf_ssh_compression_algorithms_client_to_server_length, + hf_ssh_compression_algorithms_client_to_server, + &peer_data->comp_proposals[CLIENT_TO_SERVER_PROPOSAL]); + offset = ssh_dissect_proposal(tvb, offset, key_init_tree, + hf_ssh_compression_algorithms_server_to_client_length, + hf_ssh_compression_algorithms_server_to_client, + &peer_data->comp_proposals[SERVER_TO_CLIENT_PROPOSAL]); + offset = ssh_dissect_proposal(tvb, offset, key_init_tree, + hf_ssh_languages_client_to_server_length, + hf_ssh_languages_client_to_server, NULL); + offset = ssh_dissect_proposal(tvb, offset, key_init_tree, + hf_ssh_languages_server_to_client_length, + hf_ssh_languages_server_to_client, NULL); + + proto_tree_add_item(key_init_tree, hf_ssh_first_kex_packet_follows, + tvb, offset, 1, ENC_BIG_ENDIAN); + offset+=1; + + proto_tree_add_item(key_init_tree, hf_ssh_kex_reserved, + tvb, offset, 4, ENC_NA); + offset+=4; + + hassh_algo = wmem_strbuf_new(wmem_packet_scope(), ""); + if(!is_response) { + wmem_strbuf_append_printf(hassh_algo, "%s;%s;%s;%s", peer_data->kex_proposal, peer_data->enc_proposals[CLIENT_TO_SERVER_PROPOSAL], + peer_data->mac_proposals[CLIENT_TO_SERVER_PROPOSAL], peer_data->comp_proposals[CLIENT_TO_SERVER_PROPOSAL]); + hassh = g_compute_checksum_for_string(G_CHECKSUM_MD5, wmem_strbuf_get_str(hassh_algo), wmem_strbuf_get_len(hassh_algo)); + ti = proto_tree_add_string(key_init_tree, hf_ssh_kex_hassh_algo, tvb, offset, 0, wmem_strbuf_get_str(hassh_algo)); + proto_item_set_generated(ti); + ti = proto_tree_add_string(key_init_tree, hf_ssh_kex_hassh, tvb, offset, 0, hassh); + proto_item_set_generated(ti); + g_free(hassh); + } else { + wmem_strbuf_append_printf(hassh_algo, "%s;%s;%s;%s", peer_data->kex_proposal, peer_data->enc_proposals[SERVER_TO_CLIENT_PROPOSAL], + peer_data->mac_proposals[SERVER_TO_CLIENT_PROPOSAL], peer_data->comp_proposals[SERVER_TO_CLIENT_PROPOSAL]); + hassh = g_compute_checksum_for_string(G_CHECKSUM_MD5, wmem_strbuf_get_str(hassh_algo), wmem_strbuf_get_len(hassh_algo)); + ti = proto_tree_add_string(key_init_tree, hf_ssh_kex_hasshserver_algo, tvb, offset, 0, wmem_strbuf_get_str(hassh_algo)); + proto_item_set_generated(ti); + ti = proto_tree_add_string(key_init_tree, hf_ssh_kex_hasshserver, tvb, offset, 0, hassh); + proto_item_set_generated(ti); + g_free(hassh); + } + + 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); + } + + payload_length = offset - start_offset; + + if (tf != NULL) { + proto_item_set_len(tf, payload_length); + } + + // I_C / I_S (client and server SSH_MSG_KEXINIT payload) RFC4253 4.2 + if (!PINFO_FD_VISITED(pinfo)) { + gchar *data = (gchar *)wmem_alloc(wmem_packet_scope(), payload_length + 1); + tvb_memcpy(tvb, data + 1, start_offset, payload_length); + data[0] = SSH_MSG_KEXINIT; + if(is_response){ + ssh_hash_buffer_put_string(global_data->kex_server_key_exchange_init, data, payload_length + 1); + }else{ + ssh_hash_buffer_put_string(global_data->kex_client_key_exchange_init, data, payload_length + 1); + } + } + + return offset; +} + +static int +ssh_dissect_proposal(tvbuff_t *tvb, int offset, proto_tree *tree, + int hf_index_length, int hf_index_value, char **store) +{ + guint32 len = tvb_get_ntohl(tvb, offset); + proto_tree_add_uint(tree, hf_index_length, tvb, offset, 4, len); + offset += 4; + + proto_tree_add_item(tree, hf_index_value, tvb, offset, len, + ENC_ASCII); + if (store) + *store = (char *) tvb_get_string_enc(wmem_file_scope(), tvb, offset, len, ENC_ASCII); + offset += len; + + return offset; +} + +static void +ssh_keylog_read_file(void) +{ + if (!pref_keylog_file || !*pref_keylog_file) { + ws_debug("no keylog file preference set"); + return; + } + + if (ssh_keylog_file && file_needs_reopen(ws_fileno(ssh_keylog_file), + pref_keylog_file)) { + ssh_keylog_reset(); + g_hash_table_remove_all(ssh_master_key_map); + } + + if (!ssh_keylog_file) { + ssh_keylog_file = ws_fopen(pref_keylog_file, "r"); + if (!ssh_keylog_file) { + ws_debug("ssh: failed to open key log file %s: %s", + pref_keylog_file, g_strerror(errno)); + return; + } + } + + /* File format: each line follows the format " ". + * is the hex-encoded (client or server) 16 bytes cookie + * (32 characters) found in the SSH_MSG_KEXINIT of the endpoint whose + * private random is disclosed. + * is either SHARED_SECRET or PRIVATE_KEY depending on the + * type of key provided. PRIVAT_KEY is only supported for DH, + * DH group exchange, and ECDH (including Curve25519) key exchanges. + * is the private random number that is used to generate the DH + * negotiation (length depends on algorithm). In RFC4253 it is called + * x for the client and y for the server. + * For openssh and DH group exchange, it can be retrieved using + * DH_get0_key(kex->dh, NULL, &server_random) + * for groupN in file kexdh.c function kex_dh_compute_key + * for custom group in file kexgexs.c function input_kex_dh_gex_init + * For openssh and curve25519, it can be found in function kex_c25519_enc + * in variable server_key. One may also provide the shared secret + * directly if is set to SHARED_SECRET. + * + * Example: + * 90d886612f9c35903db5bb30d11f23c2 PRIVATE_KEY DEF830C22F6C927E31972FFB20B46C96D0A5F2D5E7BE5A3A8804D6BFC431619ED10AF589EEDFF4750DEA00EFD7AFDB814B6F3528729692B1F2482041521AE9DC + */ + for (;;) { + char buf[512]; + buf[0] = 0; + + if (!fgets(buf, sizeof(buf), ssh_keylog_file)) { + if (ferror(ssh_keylog_file)) { + ws_debug("Error while reading %s, closing it.", pref_keylog_file); + ssh_keylog_reset(); + g_hash_table_remove_all(ssh_master_key_map); + } + break; + } + + size_t len = strlen(buf); + while(len>0 && (buf[len-1]=='\r' || buf[len-1]=='\n')){len-=1;buf[len]=0;} + + ssh_keylog_process_line(buf); + } +} + +static void +ssh_keylog_process_lines(const guint8 *data, guint datalen) +{ + const char *next_line = (const char *)data; + const char *line_end = next_line + datalen; + while (next_line && next_line < line_end) { + const char *line = next_line; + next_line = (const char *)memchr(line, '\n', line_end - line); + gssize linelen; + + if (next_line) { + linelen = next_line - line; + next_line++; /* drop LF */ + } else { + linelen = (gssize)(line_end - line); + } + if (linelen > 0 && line[linelen - 1] == '\r') { + linelen--; /* drop CR */ + } + + ssh_debug_printf(" checking keylog line: %.*s\n", (int)linelen, line); + + gchar * strippedline = g_strndup(line, linelen); + ssh_keylog_process_line(strippedline); + g_free(strippedline); + } +} + +static void +ssh_keylog_process_line(const char *line) +{ + ws_noisy("ssh: process line: %s", line); + + gchar **split = g_strsplit(line, " ", 3); + gchar *cookie, *type, *key; + size_t cookie_len, key_len; + + if (g_strv_length(split) == 3) { + // New format: [hex-encoded cookie] [key type] [hex-encoded key material] + cookie = split[0]; + type = split[1]; + key = split[2]; + } else if (g_strv_length(split) == 2) { + // Old format: [hex-encoded cookie] [hex-encoded private key] + ws_debug("ssh keylog: detected old keylog format without explicit key type"); + type = "PRIVATE_KEY"; + cookie = split[0]; + key = split[1]; + } else { + ws_debug("ssh keylog: invalid format"); + g_strfreev(split); + return; + } + + key_len = strlen(key); + cookie_len = strlen(cookie); + if(key_len & 1){ + ws_debug("ssh keylog: invalid format (key could at least be even!)"); + g_strfreev(split); + return; + } + if(cookie_len & 1){ + ws_debug("ssh keylog: invalid format (cookie could at least be even!)"); + g_strfreev(split); + return; + } + ssh_bignum * bn_cookie = ssh_kex_make_bignum(NULL, (guint)(cookie_len/2)); + ssh_bignum * bn_priv = ssh_kex_make_bignum(NULL, (guint)(key_len/2)); + guint8 c; + for (size_t i = 0; i < key_len/2; i ++) { + gchar v0 = key[i * 2]; + gint8 h0 = (v0>='0' && v0<='9')?v0-'0':(v0>='a' && v0<='f')?v0-'a'+10:(v0>='A' && v0<='F')?v0-'A'+10:-1; + gchar v1 = key[i * 2 + 1]; + gint8 h1 = (v1>='0' && v1<='9')?v1-'0':(v1>='a' && v1<='f')?v1-'a'+10:(v1>='A' && v1<='F')?v1-'A'+10:-1; + + if (h0==-1 || h1==-1) { + ws_debug("ssh: can't process key, invalid hex number: %c%c", v0, v1); + g_strfreev(split); + return; + } + + c = (h0 << 4) | h1; + + bn_priv->data[i] = c; + } + for (size_t i = 0; i < cookie_len/2; i ++) { + gchar v0 = cookie[i * 2]; + gint8 h0 = (v0>='0' && v0<='9')?v0-'0':(v0>='a' && v0<='f')?v0-'a'+10:(v0>='A' && v0<='F')?v0-'A'+10:-1; + gchar v1 = cookie[i * 2 + 1]; + gint8 h1 = (v1>='0' && v1<='9')?v1-'0':(v1>='a' && v1<='f')?v1-'a'+10:(v1>='A' && v1<='F')?v1-'A'+10:-1; + + if (h0==-1 || h1==-1) { + ws_debug("ssh: can't process cookie, invalid hex number: %c%c", v0, v1); + g_strfreev(split); + return; + } + + c = (h0 << 4) | h1; + + bn_cookie->data[i] = c; + } + ssh_bignum * bn_priv_ht = g_new(ssh_bignum, 1); + bn_priv_ht->length = bn_priv->length; + bn_priv_ht->data = (guint8 *) g_memdup2(bn_priv->data, bn_priv->length); + ssh_bignum * bn_cookie_ht = g_new(ssh_bignum, 1); + bn_cookie_ht->length = bn_cookie->length; + bn_cookie_ht->data = (guint8 *) g_memdup2(bn_cookie->data, bn_cookie->length); + + gchar * type_ht = (gchar *) g_memdup2(type, strlen(type) + 1); + ssh_key_map_entry_t * entry_ht = g_new(ssh_key_map_entry_t, 1); + entry_ht->type = type_ht; + entry_ht->key_material = bn_priv_ht; + g_hash_table_insert(ssh_master_key_map, bn_cookie_ht, entry_ht); + g_strfreev(split); +} + +static void +ssh_keylog_reset(void) +{ + if (ssh_keylog_file) { + fclose(ssh_keylog_file); + ssh_keylog_file = NULL; + } +} + +static guint +ssh_kex_type(gchar *type) +{ + if (type) { + if (g_str_has_prefix(type, "curve25519")) { + return SSH_KEX_CURVE25519; + }else if (g_str_has_prefix(type, "diffie-hellman-group-exchange")) { + return SSH_KEX_DH_GEX; + }else if (g_str_has_prefix(type, "diffie-hellman-group14")) { + return SSH_KEX_DH_GROUP14; + }else if (g_str_has_prefix(type, "diffie-hellman-group16")) { + return SSH_KEX_DH_GROUP16; + }else if (g_str_has_prefix(type, "diffie-hellman-group18")) { + return SSH_KEX_DH_GROUP18; + }else if (g_str_has_prefix(type, "diffie-hellman-group1")) { + return SSH_KEX_DH_GROUP1; + } + } + + return 0; +} + +static guint +ssh_kex_hash_type(gchar *type_string) +{ + if (type_string && g_str_has_suffix(type_string, "sha1")) { + return SSH_KEX_HASH_SHA1; + }else if (type_string && g_str_has_suffix(type_string, "sha256")) { + return SSH_KEX_HASH_SHA256; + }else if (type_string && g_str_has_suffix(type_string, "sha512")) { + return SSH_KEX_HASH_SHA512; + } else { + ws_debug("hash type %s not supported", type_string); + return 0; + } +} + +static ssh_bignum * +ssh_kex_make_bignum(const guint8 *data, guint length) +{ + // 512 bytes (4096 bits) is the maximum bignum size we're supporting + // Actually we need 513 bytes, to make provision for signed values + // Diffie-Hellman group 18 has 8192 bits + if (length == 0 || length > 1025) { + return NULL; + } + + ssh_bignum *bn = wmem_new0(wmem_file_scope(), ssh_bignum); + bn->data = (guint8 *)wmem_alloc0(wmem_file_scope(), length); + + if (data) { + memcpy(bn->data, data, length); + } + + bn->length = length; + return bn; +} + +static gboolean +ssh_read_e(tvbuff_t *tvb, int offset, struct ssh_flow_data *global_data) +{ + // store the client's public part (e) for later usage + guint32 length = tvb_get_ntohl(tvb, offset); + global_data->kex_e = ssh_kex_make_bignum(NULL, length); + if (!global_data->kex_e) { + return false; + } + tvb_memcpy(tvb, global_data->kex_e->data, offset + 4, length); + return true; +} + +static gboolean +ssh_read_f(tvbuff_t *tvb, int offset, struct ssh_flow_data *global_data) +{ + // store the server's public part (f) for later usage + guint32 length = tvb_get_ntohl(tvb, offset); + global_data->kex_f = ssh_kex_make_bignum(NULL, length); + if (!global_data->kex_f) { + return false; + } + tvb_memcpy(tvb, global_data->kex_f->data, offset + 4, length); + return true; +} + +#ifdef SSH_DECRYPTION_SUPPORTED +static ssh_bignum * +ssh_read_mpint(tvbuff_t *tvb, int offset) +{ + // store the DH group modulo (p) for later usage + int length = tvb_get_ntohl(tvb, offset); + ssh_bignum * bn = ssh_kex_make_bignum(NULL, length); + if (!bn) { + ws_debug("invalid bignum length %u", length); + return NULL; + } + tvb_memcpy(tvb, bn->data, offset + 4, length); + return bn; +} +#endif //def SSH_DECRYPTION_SUPPORTED + +static void +ssh_keylog_hash_write_secret(struct ssh_flow_data *global_data) +{ + /* + * This computation is defined differently for each key exchange method: + * https://tools.ietf.org/html/rfc4253#page-23 + * https://tools.ietf.org/html/rfc5656#page-8 + * https://tools.ietf.org/html/rfc4419#page-4 + * All key exchange methods: + * https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xhtml#ssh-parameters-16 + */ + + gcry_md_hd_t hd; + ssh_key_map_entry_t *entry; + ssh_bignum *secret = NULL; + int length; + gboolean client_cookie = FALSE; + + ssh_keylog_read_file(); + + guint kex_type = ssh_kex_type(global_data->kex); + guint kex_hash_type = ssh_kex_hash_type(global_data->kex); + + entry = (ssh_key_map_entry_t *)g_hash_table_lookup(ssh_master_key_map, global_data->peer_data[SERVER_PEER_DATA].bn_cookie); + if (!entry) { + entry = (ssh_key_map_entry_t *)g_hash_table_lookup(ssh_master_key_map, global_data->peer_data[CLIENT_PEER_DATA].bn_cookie); + client_cookie = TRUE; + } + if (!entry) { + ws_debug("ssh decryption: no entry in keylog file for this session"); + global_data->do_decrypt = FALSE; + return; + } + + if (!strcmp(entry->type, "PRIVATE_KEY")) { + if (client_cookie) { + secret = ssh_kex_shared_secret(kex_type, global_data->kex_f, entry->key_material, global_data->kex_gex_p); + } else { + secret = ssh_kex_shared_secret(kex_type, global_data->kex_e, entry->key_material, global_data->kex_gex_p); + } + } else if (!strcmp(entry->type, "SHARED_SECRET")) { + secret = ssh_kex_make_bignum(entry->key_material->data, entry->key_material->length); + } else { + ws_debug("ssh decryption: unknown key type in keylog file"); + global_data->do_decrypt = FALSE; + return; + } + + if (!secret) { + ws_debug("ssh decryption: no key material for this session"); + global_data->do_decrypt = FALSE; + return; + } + + // shared secret data needs to be written as an mpint, and we need it later + if (secret->data[0] & 0x80) { // Stored in Big endian + length = secret->length + 1; + gchar *tmp = (gchar *)wmem_alloc0(wmem_packet_scope(), length); + memcpy(tmp + 1, secret->data, secret->length); + tmp[0] = 0; + secret->data = tmp; + secret->length = length; + } + ssh_hash_buffer_put_string(global_data->kex_shared_secret, secret->data, secret->length); + + wmem_array_t * kex_gex_p = wmem_array_new(wmem_packet_scope(), 1); + if(global_data->kex_gex_p){ssh_hash_buffer_put_string(kex_gex_p, global_data->kex_gex_p->data, global_data->kex_gex_p->length);} + wmem_array_t * kex_gex_g = wmem_array_new(wmem_packet_scope(), 1); + if(global_data->kex_gex_g){ssh_hash_buffer_put_string(kex_gex_g, global_data->kex_gex_g->data, global_data->kex_gex_g->length);} + wmem_array_t * kex_e = wmem_array_new(wmem_packet_scope(), 1); + if(global_data->kex_e){ssh_hash_buffer_put_string(kex_e, global_data->kex_e->data, global_data->kex_e->length);} + wmem_array_t * kex_f = wmem_array_new(wmem_packet_scope(), 1); + if(global_data->kex_f){ssh_hash_buffer_put_string(kex_f, global_data->kex_f->data, global_data->kex_f->length);} + + wmem_array_t * kex_hash_buffer = wmem_array_new(wmem_packet_scope(), 1); + ssh_print_data("client_version", (const guchar *)wmem_array_get_raw(global_data->kex_client_version), wmem_array_get_count(global_data->kex_client_version)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(global_data->kex_client_version), wmem_array_get_count(global_data->kex_client_version)); + ssh_print_data("server_version", (const guchar *)wmem_array_get_raw(global_data->kex_server_version), wmem_array_get_count(global_data->kex_server_version)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(global_data->kex_server_version), wmem_array_get_count(global_data->kex_server_version)); + ssh_print_data("client_key_exchange_init", (const guchar *)wmem_array_get_raw(global_data->kex_client_key_exchange_init), wmem_array_get_count(global_data->kex_client_key_exchange_init)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(global_data->kex_client_key_exchange_init), wmem_array_get_count(global_data->kex_client_key_exchange_init)); + ssh_print_data("server_key_exchange_init", (const guchar *)wmem_array_get_raw(global_data->kex_server_key_exchange_init), wmem_array_get_count(global_data->kex_server_key_exchange_init)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(global_data->kex_server_key_exchange_init), wmem_array_get_count(global_data->kex_server_key_exchange_init)); + ssh_print_data("kex_server_host_key_blob", (const guchar *)wmem_array_get_raw(global_data->kex_server_host_key_blob), wmem_array_get_count(global_data->kex_server_host_key_blob)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(global_data->kex_server_host_key_blob), wmem_array_get_count(global_data->kex_server_host_key_blob)); + if(kex_type==SSH_KEX_DH_GEX){ + ssh_print_data("kex_gex_bits_min", (const guchar *)wmem_array_get_raw(global_data->kex_gex_bits_min), wmem_array_get_count(global_data->kex_gex_bits_min)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(global_data->kex_gex_bits_min), wmem_array_get_count(global_data->kex_gex_bits_min)); + ssh_print_data("kex_gex_bits_req", (const guchar *)wmem_array_get_raw(global_data->kex_gex_bits_req), wmem_array_get_count(global_data->kex_gex_bits_req)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(global_data->kex_gex_bits_req), wmem_array_get_count(global_data->kex_gex_bits_req)); + ssh_print_data("kex_gex_bits_max", (const guchar *)wmem_array_get_raw(global_data->kex_gex_bits_max), wmem_array_get_count(global_data->kex_gex_bits_max)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(global_data->kex_gex_bits_max), wmem_array_get_count(global_data->kex_gex_bits_max)); + ssh_print_data("key modulo (p)", (const guchar *)wmem_array_get_raw(kex_gex_p), wmem_array_get_count(kex_gex_p)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(kex_gex_p), wmem_array_get_count(kex_gex_p)); + ssh_print_data("key base (g)", (const guchar *)wmem_array_get_raw(kex_gex_g), wmem_array_get_count(kex_gex_g)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(kex_gex_g), wmem_array_get_count(kex_gex_g)); + ssh_print_data("key client (e)", (const guchar *)wmem_array_get_raw(kex_e), wmem_array_get_count(kex_e)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(kex_e), wmem_array_get_count(kex_e)); + ssh_print_data("key server (f)", (const guchar *)wmem_array_get_raw(kex_f), wmem_array_get_count(kex_f)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(kex_f), wmem_array_get_count(kex_f)); + } + if(kex_type==SSH_KEX_DH_GROUP1 || kex_type==SSH_KEX_DH_GROUP14 || kex_type==SSH_KEX_DH_GROUP16 || kex_type==SSH_KEX_DH_GROUP18){ + ssh_print_data("key client (e)", (const guchar *)wmem_array_get_raw(kex_e), wmem_array_get_count(kex_e)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(kex_e), wmem_array_get_count(kex_e)); + ssh_print_data("key server (f)", (const guchar *)wmem_array_get_raw(kex_f), wmem_array_get_count(kex_f)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(kex_f), wmem_array_get_count(kex_f)); + } + if(kex_type==SSH_KEX_CURVE25519){ + ssh_print_data("key client (Q_C)", (const guchar *)wmem_array_get_raw(kex_e), wmem_array_get_count(kex_e)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(kex_e), wmem_array_get_count(kex_e)); + ssh_print_data("key server (Q_S)", (const guchar *)wmem_array_get_raw(kex_f), wmem_array_get_count(kex_f)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(kex_f), wmem_array_get_count(kex_f)); + } + ssh_print_data("shared secret", (const guchar *)wmem_array_get_raw(global_data->kex_shared_secret), wmem_array_get_count(global_data->kex_shared_secret)); + wmem_array_append(kex_hash_buffer, wmem_array_get_raw(global_data->kex_shared_secret), wmem_array_get_count(global_data->kex_shared_secret)); + + ssh_print_data("exchange", (const guchar *)wmem_array_get_raw(kex_hash_buffer), wmem_array_get_count(kex_hash_buffer)); + + guint hash_len = 32; + if(kex_hash_type==SSH_KEX_HASH_SHA1) { + gcry_md_open(&hd, GCRY_MD_SHA1, 0); + hash_len = 20; + } else if(kex_hash_type==SSH_KEX_HASH_SHA256) { + gcry_md_open(&hd, GCRY_MD_SHA256, 0); + hash_len = 32; + } else if(kex_hash_type==SSH_KEX_HASH_SHA512) { + gcry_md_open(&hd, GCRY_MD_SHA512, 0); + hash_len = 64; + } else { + ws_debug("kex_hash_type type %d not supported", kex_hash_type); + return; + } + gchar *exchange_hash = (gchar *)wmem_alloc0(wmem_file_scope(), hash_len); + gcry_md_write(hd, wmem_array_get_raw(kex_hash_buffer), wmem_array_get_count(kex_hash_buffer)); + memcpy(exchange_hash, gcry_md_read(hd, 0), hash_len); + gcry_md_close(hd); + ssh_print_data("hash", exchange_hash, hash_len); + global_data->secret = secret; + ssh_derive_symmetric_keys(secret, exchange_hash, hash_len, global_data); +} + +// the purpose of this function is to deal with all different kex methods +static ssh_bignum * +ssh_kex_shared_secret(gint kex_type, ssh_bignum *pub, ssh_bignum *priv, ssh_bignum *modulo) +{ + DISSECTOR_ASSERT(pub != NULL); + DISSECTOR_ASSERT(priv != NULL); + + ssh_bignum *secret = ssh_kex_make_bignum(NULL, pub->length); + if (!secret) { + ws_debug("invalid key length %u", pub->length); + return NULL; + } + + if(kex_type==SSH_KEX_DH_GEX){ + gcry_mpi_t b = NULL; + gcry_mpi_scan(&b, GCRYMPI_FMT_USG, pub->data, pub->length, NULL); + gcry_mpi_t d = NULL, e = NULL, m = NULL; + size_t result_len = 0; + d = gcry_mpi_new(pub->length*8); + gcry_mpi_scan(&e, GCRYMPI_FMT_USG, priv->data, priv->length, NULL); + gcry_mpi_scan(&m, GCRYMPI_FMT_USG, modulo->data, modulo->length, NULL); + gcry_mpi_powm(d, b, e, m); // gcry_mpi_powm(d, b, e, m) => d = b^e % m + gcry_mpi_print(GCRYMPI_FMT_USG, secret->data, secret->length, &result_len, d); + secret->length = (guint)result_len; // Should not be larger than what fits in a 32-bit unsigned integer... + gcry_mpi_release(d); + gcry_mpi_release(b); + gcry_mpi_release(e); + gcry_mpi_release(m); + + }else if(kex_type==SSH_KEX_DH_GROUP1 || kex_type==SSH_KEX_DH_GROUP14 || kex_type==SSH_KEX_DH_GROUP16 || kex_type==SSH_KEX_DH_GROUP18){ + gcry_mpi_t m = NULL; + if(kex_type==SSH_KEX_DH_GROUP1){ + static const guint8 p[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,}; + gcry_mpi_scan(&m, GCRYMPI_FMT_USG, p, sizeof(p), NULL); + }else if(kex_type==SSH_KEX_DH_GROUP14){ +//p:FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF + static const guint8 p[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + gcry_mpi_scan(&m, GCRYMPI_FMT_USG, p, sizeof(p), NULL); + }else if(kex_type==SSH_KEX_DH_GROUP16){ +//p:FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF + static const guint8 p[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, + 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, + 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, + 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, + 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, + 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,}; + gcry_mpi_scan(&m, GCRYMPI_FMT_USG, p, sizeof(p), NULL); + }else if(kex_type==SSH_KEX_DH_GROUP18){ +//p:FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF + static const guint8 p[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, + 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, + 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, + 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, + 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, + 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92, 0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26, + 0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE, 0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD, + 0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E, 0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE, + 0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31, 0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18, + 0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED, 0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B, + 0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B, 0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42, + 0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF, 0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC, + 0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03, 0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6, + 0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82, 0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E, + 0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3, 0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE, + 0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5, 0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA, + 0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8, 0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0, + 0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28, 0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76, + 0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0, 0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C, + 0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32, 0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68, + 0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE, 0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6, + 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xBE, 0x11, 0x59, 0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4, + 0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C, 0xD8, 0xBE, 0xC4, 0xD0, 0x73, 0xB9, 0x31, 0xBA, + 0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00, 0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED, + 0x25, 0x76, 0xF6, 0x93, 0x6B, 0xA4, 0x24, 0x66, 0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68, + 0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78, 0x23, 0x8F, 0x16, 0xCB, 0xE3, 0x9D, 0x65, 0x2D, + 0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9, 0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07, + 0x13, 0xEB, 0x57, 0xA8, 0x1A, 0x23, 0xF0, 0xC7, 0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B, + 0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD, 0xFA, 0x9D, 0x4B, 0x7F, 0xA2, 0xC0, 0x87, 0xE8, + 0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A, 0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6, + 0x6D, 0x2A, 0x13, 0xF8, 0x3F, 0x44, 0xF8, 0x2D, 0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36, + 0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1, 0x64, 0xF3, 0x1C, 0xC5, 0x08, 0x46, 0x85, 0x1D, + 0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1, 0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73, + 0xFA, 0xF3, 0x6B, 0xC3, 0x1E, 0xCF, 0xA2, 0x68, 0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92, + 0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7, 0x88, 0x9A, 0x00, 0x2E, 0xD5, 0xEE, 0x38, 0x2B, + 0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47, 0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA, + 0x9E, 0x30, 0x50, 0xE2, 0x76, 0x56, 0x94, 0xDF, 0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71, + 0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,}; + gcry_mpi_scan(&m, GCRYMPI_FMT_USG, p, sizeof(p), NULL); + } + + gcry_mpi_t b = NULL; + gcry_mpi_scan(&b, GCRYMPI_FMT_USG, pub->data, pub->length, NULL); + gcry_mpi_t d = NULL, e = NULL; + size_t result_len = 0; + d = gcry_mpi_new(pub->length*8); + gcry_mpi_scan(&e, GCRYMPI_FMT_USG, priv->data, priv->length, NULL); + gcry_mpi_powm(d, b, e, m); // gcry_mpi_powm(d, b, e, m) => d = b^e % m + gcry_mpi_print(GCRYMPI_FMT_USG, secret->data, secret->length, &result_len, d); + secret->length = (guint)result_len; // Should not be larger than what fits in a 32-bit unsigned integer... + gcry_mpi_release(d); + gcry_mpi_release(b); + gcry_mpi_release(e); + gcry_mpi_release(m); + }else if(kex_type==SSH_KEX_CURVE25519){ + if (crypto_scalarmult_curve25519(secret->data, priv->data, pub->data)) { + ws_debug("curve25519: can't compute shared secret"); + return NULL; + } + } else { + ws_debug("kex_type type %d not supported", kex_type); + return 0; + } + + return secret; +} + +static gchar * +ssh_string(const gchar *string, guint length) +{ + gchar *ssh_string = (gchar *)wmem_alloc(wmem_packet_scope(), length + 4); + ssh_string[0] = (length >> 24) & 0xff; + ssh_string[1] = (length >> 16) & 0xff; + ssh_string[2] = (length >> 8) & 0xff; + ssh_string[3] = length & 0xff; + memcpy(ssh_string + 4, string, length); + return ssh_string; +} + +static void +ssh_hash_buffer_put_string(wmem_array_t *buffer, const gchar *string, + guint length) +{ + if (!buffer) { + return; + } + + gchar *string_with_length = ssh_string(string, length); + wmem_array_append(buffer, string_with_length, length + 4); +} + +static void +ssh_hash_buffer_put_uint32(wmem_array_t *buffer, guint val) +{ + if (!buffer) { + return; + } + + gchar buf[4]; + buf[0] = (val >> 24); buf[1] = (val >> 16); buf[2] = (val >> 8); buf[3] = (val >> 0); + wmem_array_append(buffer, buf, 4); +} + +static void ssh_derive_symmetric_keys(ssh_bignum *secret, gchar *exchange_hash, + guint hash_length, struct ssh_flow_data *global_data) +{ + if (!global_data->session_id) { + global_data->session_id = exchange_hash; + global_data->session_id_length = hash_length; + } + + unsigned int we_need = 0; + for(int peer_cnt=0;peer_cnt<2;peer_cnt++){ + struct ssh_peer_data * peer_data = &global_data->peer_data[peer_cnt]; + // required size of key depends on cipher used. chacha20 wants 64 bytes + guint need = 0; + if (GCRY_CIPHER_CHACHA20 == peer_data->cipher_id) { + need = 64; + } else if (CIPHER_AES128_CBC == peer_data->cipher_id || CIPHER_AES128_CTR == peer_data->cipher_id || CIPHER_AES128_GCM == peer_data->cipher_id) { + need = 16; + } else if (CIPHER_AES192_CBC == peer_data->cipher_id || CIPHER_AES192_CTR == peer_data->cipher_id) { + need = 24; + } else if (CIPHER_AES256_CBC == peer_data->cipher_id || CIPHER_AES256_CTR == peer_data->cipher_id || CIPHER_AES256_GCM == peer_data->cipher_id) { + need = 32; + } else { + ssh_debug_printf("ssh: cipher (%d) is unknown or not set\n", peer_data->cipher_id); + ssh_debug_flush(); + } + if(peer_data->mac_id == CIPHER_MAC_SHA2_256){ + need = 32; + }else{ + ssh_debug_printf("ssh: MAC (%d) is unknown or not set\n", peer_data->mac_id); + ssh_debug_flush(); + } + if (we_neednew_keys[i], global_data, we_need); + if(i==0){ ssh_print_data("Initial IV client to server", global_data->new_keys[i].data, global_data->new_keys[i].length); + }else if(i==1){ ssh_print_data("Initial IV server to client", global_data->new_keys[i].data, global_data->new_keys[i].length); + }else if(i==2){ ssh_print_data("Encryption key client to server", global_data->new_keys[i].data, global_data->new_keys[i].length); + }else if(i==3){ ssh_print_data("Encryption key server to client", global_data->new_keys[i].data, global_data->new_keys[i].length); + }else if(i==4){ ssh_print_data("Integrity key client to server", global_data->new_keys[i].data, global_data->new_keys[i].length); + }else if(i==5){ ssh_print_data("Integrity key server to client", global_data->new_keys[i].data, global_data->new_keys[i].length); + } + } +} + +static void ssh_derive_symmetric_key(ssh_bignum *secret, gchar *exchange_hash, + guint hash_length, gchar id, ssh_bignum *result_key, + struct ssh_flow_data *global_data, guint we_need) +{ + gcry_md_hd_t hd; + + guint kex_hash_type = ssh_kex_hash_type(global_data->kex); + int algo = GCRY_MD_SHA256; + if(kex_hash_type==SSH_KEX_HASH_SHA1){ + algo = GCRY_MD_SHA1; + }else if(kex_hash_type==SSH_KEX_HASH_SHA256){ + algo = GCRY_MD_SHA256; + }else if(kex_hash_type==SSH_KEX_HASH_SHA512){ + algo = GCRY_MD_SHA512; + } + guint len = gcry_md_get_algo_dlen(algo); + + result_key->data = (guchar *)wmem_alloc(wmem_file_scope(), we_need); + + gchar *secret_with_length = ssh_string(secret->data, secret->length); + + if (gcry_md_open(&hd, algo, 0) == 0) { + gcry_md_write(hd, secret_with_length, secret->length + 4); + gcry_md_write(hd, exchange_hash, hash_length); + gcry_md_putc(hd, id); + gcry_md_write(hd, global_data->session_id, hash_length); + guint add_length = MIN(len, we_need); + memcpy(result_key->data, gcry_md_read(hd, 0), add_length); + gcry_md_close(hd); + } + + // expand key + for (guint have = len; have < we_need; have += len) { + if (gcry_md_open(&hd, algo, 0) == 0) { + gcry_md_write(hd, secret_with_length, secret->length + 4); + gcry_md_write(hd, exchange_hash, hash_length); + gcry_md_write(hd, result_key->data+have-len, len); + guint add_length = MIN(len, we_need - have); + memcpy(result_key->data+have, gcry_md_read(hd, 0), add_length); + gcry_md_close(hd); + } + } + + result_key->length = we_need; +} + +static void +ssh_choose_enc_mac(struct ssh_flow_data *global_data) +{ + for(int peer_cnt=0;peer_cnt<2;peer_cnt++){ + struct ssh_peer_data * peer_data = &global_data->peer_data[peer_cnt]; + ssh_choose_algo(global_data->peer_data[CLIENT_PEER_DATA].enc_proposals[peer_cnt], + global_data->peer_data[SERVER_PEER_DATA].enc_proposals[peer_cnt], + &peer_data->enc); + /* some ciphers have their own MAC so the "negotiated" one is meaningless */ + if(peer_data->enc && (0 == strcmp(peer_data->enc, "aes128-gcm@openssh.com") || + 0 == strcmp(peer_data->enc, "aes256-gcm@openssh.com"))) { + peer_data->mac = wmem_strdup(wmem_file_scope(), (const gchar *)""); + peer_data->mac_length = 16; + peer_data->length_is_plaintext = 1; + } + else if(peer_data->enc && 0 == strcmp(peer_data->enc, "chacha20-poly1305@openssh.com")) { + peer_data->mac = wmem_strdup(wmem_file_scope(), (const gchar *)""); + peer_data->mac_length = 16; + } + else { + ssh_choose_algo(global_data->peer_data[CLIENT_PEER_DATA].mac_proposals[peer_cnt], + global_data->peer_data[SERVER_PEER_DATA].mac_proposals[peer_cnt], + &peer_data->mac); + ssh_set_mac_length(peer_data); + } + ssh_choose_algo(global_data->peer_data[CLIENT_PEER_DATA].comp_proposals[peer_cnt], + global_data->peer_data[SERVER_PEER_DATA].comp_proposals[peer_cnt], + &peer_data->comp); + } + + ssh_decryption_set_cipher_id(&global_data->peer_data[CLIENT_PEER_DATA]); + ssh_decryption_set_mac_id(&global_data->peer_data[CLIENT_PEER_DATA]); + ssh_decryption_set_cipher_id(&global_data->peer_data[SERVER_PEER_DATA]); + ssh_decryption_set_mac_id(&global_data->peer_data[SERVER_PEER_DATA]); +} + +static void +ssh_decryption_set_cipher_id(struct ssh_peer_data *peer) +{ + gchar *cipher_name = peer->enc; + + if (!cipher_name) { + peer->cipher = NULL; + ws_debug("ERROR: cipher_name is NULL"); + } else if (0 == strcmp(cipher_name, "chacha20-poly1305@openssh.com")) { + peer->cipher_id = GCRY_CIPHER_CHACHA20; + } else if (0 == strcmp(cipher_name, "aes128-gcm@openssh.com")) { + peer->cipher_id = CIPHER_AES128_GCM; + } else if (0 == strcmp(cipher_name, "aes128-gcm")) { + peer->cipher_id = CIPHER_AES128_GCM; + } else if (0 == strcmp(cipher_name, "aes256-gcm@openssh.com")) { + peer->cipher_id = CIPHER_AES256_GCM; + } else if (0 == strcmp(cipher_name, "aes256-gcm")) { + peer->cipher_id = CIPHER_AES256_GCM; + } else if (0 == strcmp(cipher_name, "aes128-cbc")) { + peer->cipher_id = CIPHER_AES128_CBC; + } else if (0 == strcmp(cipher_name, "aes192-cbc")) { + peer->cipher_id = CIPHER_AES192_CBC; + } else if (0 == strcmp(cipher_name, "aes256-cbc")) { + peer->cipher_id = CIPHER_AES256_CBC; + } else if (0 == strcmp(cipher_name, "aes128-ctr")) { + peer->cipher_id = CIPHER_AES128_CTR; + } else if (0 == strcmp(cipher_name, "aes192-ctr")) { + peer->cipher_id = CIPHER_AES192_CTR; + } else if (0 == strcmp(cipher_name, "aes256-ctr")) { + peer->cipher_id = CIPHER_AES256_CTR; + } else { + peer->cipher = NULL; + ws_debug("decryption not supported: %s", cipher_name); + } +} + +static void +ssh_decryption_set_mac_id(struct ssh_peer_data *peer) +{ + gchar *mac_name = peer->mac; + + if (!mac_name) { + peer->mac = NULL; + ws_debug("ERROR: mac_name is NULL"); + } else if (0 == strcmp(mac_name, "hmac-sha2-256")) { + peer->mac_id = CIPHER_MAC_SHA2_256; + } else { + peer->mac = NULL; + ws_debug("decryption MAC not supported: %s", mac_name); + } +} + +static bool +gcry_cipher_destroy_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_, void *user_data) +{ + gcry_cipher_hd_t hd = (gcry_cipher_hd_t)user_data; + + gcry_cipher_close(hd); + + return FALSE; +} + +static void +ssh_decryption_setup_cipher(struct ssh_peer_data *peer_data, + ssh_bignum *iv, ssh_bignum *key) +{ + gcry_error_t err; + gcry_cipher_hd_t *hd1, *hd2; + + hd1 = &peer_data->cipher; + hd2 = &peer_data->cipher_2; + + if (GCRY_CIPHER_CHACHA20 == peer_data->cipher_id) { + if (gcry_cipher_open(hd1, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_STREAM, 0) || + gcry_cipher_open(hd2, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_STREAM, 0)) { + gcry_cipher_close(*hd1); + gcry_cipher_close(*hd2); + ws_debug("ssh: can't open chacha20 cipher handles"); + return; + } + + gchar k1[32]; + gchar k2[32]; + if(key->data){ + memcpy(k1, key->data, 32); + memcpy(k2, key->data + 32, 32); + }else{ + memset(k1, 0, 32); + memset(k2, 0, 32); + } + + ssh_debug_printf("ssh: cipher is chacha20\n"); + ssh_print_data("key 1", k1, 32); + ssh_print_data("key 2", k2, 32); + + if ((err = gcry_cipher_setkey(*hd1, k1, 32))) { + gcry_cipher_close(*hd1); + ws_debug("ssh: can't set chacha20 cipher key %s", gcry_strerror(err)); + return; + } + + if ((err = gcry_cipher_setkey(*hd2, k2, 32))) { + gcry_cipher_close(*hd1); + gcry_cipher_close(*hd2); + ws_debug("ssh: can't set chacha20 cipher key %s", gcry_strerror(err)); + return; + } + + wmem_register_callback(wmem_file_scope(), gcry_cipher_destroy_cb, *hd1); + wmem_register_callback(wmem_file_scope(), gcry_cipher_destroy_cb, *hd2); + + } else if (CIPHER_AES128_CBC == peer_data->cipher_id || CIPHER_AES192_CBC == peer_data->cipher_id || CIPHER_AES256_CBC == peer_data->cipher_id) { + gint iKeyLen = CIPHER_AES128_CBC == peer_data->cipher_id?16:CIPHER_AES192_CBC == peer_data->cipher_id?24:32; + if (gcry_cipher_open(hd1, CIPHER_AES128_CBC == peer_data->cipher_id?GCRY_CIPHER_AES128:CIPHER_AES192_CBC == peer_data->cipher_id?GCRY_CIPHER_AES192:GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0)) { + gcry_cipher_close(*hd1); + ws_debug("ssh: can't open aes%d cipher handle", iKeyLen*8); + return; + } + gchar k1[32], iv1[16]; + if(key->data){ + memcpy(k1, key->data, iKeyLen); + }else{ + memset(k1, 0, iKeyLen); + } + if(iv->data){ + memcpy(iv1, iv->data, 16); + }else{ + memset(iv1, 0, 16); + } + + ssh_debug_printf("ssh: cipher is aes%d-cbc\n", iKeyLen*8); + ssh_print_data("key", k1, iKeyLen); + ssh_print_data("iv", iv1, 16); + + if ((err = gcry_cipher_setkey(*hd1, k1, iKeyLen))) { + gcry_cipher_close(*hd1); + ws_debug("ssh: can't set aes%d cipher key", iKeyLen*8); + ws_debug("libgcrypt: %d %s %s", gcry_err_code(err), gcry_strsource(err), gcry_strerror(err)); + return; + } + + if ((err = gcry_cipher_setiv(*hd1, iv1, 16))) { + gcry_cipher_close(*hd1); + ws_debug("ssh: can't set aes%d cipher iv", iKeyLen*8); + ws_debug("libgcrypt: %d %s %s", gcry_err_code(err), gcry_strsource(err), gcry_strerror(err)); + return; + } + + wmem_register_callback(wmem_file_scope(), gcry_cipher_destroy_cb, *hd1); + + } else if (CIPHER_AES128_CTR == peer_data->cipher_id || CIPHER_AES192_CTR == peer_data->cipher_id || CIPHER_AES256_CTR == peer_data->cipher_id) { + gint iKeyLen = CIPHER_AES128_CTR == peer_data->cipher_id?16:CIPHER_AES192_CTR == peer_data->cipher_id?24:32; + if (gcry_cipher_open(hd1, CIPHER_AES128_CTR == peer_data->cipher_id?GCRY_CIPHER_AES128:CIPHER_AES192_CTR == peer_data->cipher_id?GCRY_CIPHER_AES192:GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CTR, 0)) { + gcry_cipher_close(*hd1); + ws_debug("ssh: can't open aes%d cipher handle", iKeyLen*8); + return; + } + gchar k1[32], iv1[16]; + if(key->data){ + memcpy(k1, key->data, iKeyLen); + }else{ + memset(k1, 0, iKeyLen); + } + if(iv->data){ + memcpy(iv1, iv->data, 16); + }else{ + memset(iv1, 0, 16); + } + + ssh_debug_printf("ssh: cipher is aes%d-ctr\n", iKeyLen*8); + ssh_print_data("key", k1, iKeyLen); + ssh_print_data("iv", iv1, 16); + + if ((err = gcry_cipher_setkey(*hd1, k1, iKeyLen))) { + gcry_cipher_close(*hd1); + ws_debug("ssh: can't set aes%d cipher key", iKeyLen*8); + ws_debug("libgcrypt: %d %s %s", gcry_err_code(err), gcry_strsource(err), gcry_strerror(err)); + return; + } + + if ((err = gcry_cipher_setctr(*hd1, iv1, 16))) { + gcry_cipher_close(*hd1); + ws_debug("ssh: can't set aes%d cipher iv", iKeyLen*8); + ws_debug("libgcrypt: %d %s %s", gcry_err_code(err), gcry_strsource(err), gcry_strerror(err)); + return; + } + + wmem_register_callback(wmem_file_scope(), gcry_cipher_destroy_cb, *hd1); + + } else if (CIPHER_AES128_GCM == peer_data->cipher_id || CIPHER_AES256_GCM == peer_data->cipher_id) { + gint iKeyLen = CIPHER_AES128_GCM == peer_data->cipher_id?16:32; + if (gcry_cipher_open(hd1, CIPHER_AES128_GCM == peer_data->cipher_id?GCRY_CIPHER_AES128:GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_GCM, 0)) { + gcry_cipher_close(*hd1); + ws_debug("ssh: can't open aes%d cipher handle", iKeyLen*8); + return; + } + + gchar k1[32], iv2[12]; + if(key->data){ + memcpy(k1, key->data, iKeyLen); + }else{ + memset(k1, 0, iKeyLen); + } + if(iv->data){ + memcpy(peer_data->iv, iv->data, 12); + }else{ + memset(iv2, 0, 12); + } + + ssh_debug_printf("ssh: cipher is aes%d-gcm\n", iKeyLen*8); + ssh_print_data("key", k1, iKeyLen); + ssh_print_data("iv", peer_data->iv, 12); + + if ((err = gcry_cipher_setkey(*hd1, k1, iKeyLen))) { + gcry_cipher_close(*hd1); + ws_debug("ssh: can't set aes%d cipher key", iKeyLen*8); + ws_debug("libgcrypt: %d %s %s", gcry_err_code(err), gcry_strsource(err), gcry_strerror(err)); + return; + } + + wmem_register_callback(wmem_file_scope(), gcry_cipher_destroy_cb, *hd1); + + } else { + ssh_debug_printf("ssh: cipher (%d) is unknown or not set\n", peer_data->cipher_id); + } +} + +static void +ssh_decryption_setup_mac(struct ssh_peer_data *peer_data, + ssh_bignum *iv) +{ + if(peer_data->mac_id == CIPHER_MAC_SHA2_256){ + if(iv->data){ + memcpy(peer_data->hmac_iv, iv->data, 32); + }else{ + memset(peer_data->hmac_iv, 0, 32); + } + peer_data->hmac_iv_len = 32; + ssh_debug_printf("ssh: mac is hmac-sha2-256\n"); + ssh_print_data("iv", peer_data->hmac_iv, peer_data->hmac_iv_len); + }else{ + ws_debug("ssh: unsupported MAC"); + } +} + +/* libgcrypt wrappers for HMAC/message digest operations {{{ */ +/* hmac abstraction layer */ +#define SSH_HMAC gcry_md_hd_t + +static inline gint +ssh_hmac_init(SSH_HMAC* md, const void * key, gint len, gint algo) +{ + gcry_error_t err; + const char *err_str, *err_src; + + err = gcry_md_open(md,algo, GCRY_MD_FLAG_HMAC); + if (err != 0) { + err_str = gcry_strerror(err); + err_src = gcry_strsource(err); + ssh_debug_printf("ssh_hmac_init(): gcry_md_open failed %s/%s", err_str, err_src); + return -1; + } + gcry_md_setkey (*(md), key, len); + return 0; +} + +static inline void +ssh_hmac_update(SSH_HMAC* md, const void* data, gint len) +{ + gcry_md_write(*(md), data, len); +} + +static inline void +ssh_hmac_final(SSH_HMAC* md, guchar* data, guint* datalen) +{ + gint algo; + guint len; + + algo = gcry_md_get_algo (*(md)); + len = gcry_md_get_algo_dlen(algo); + DISSECTOR_ASSERT(len <= *datalen); + memcpy(data, gcry_md_read(*(md), algo), len); + *datalen = len; +} + +static inline void +ssh_hmac_cleanup(SSH_HMAC* md) +{ + gcry_md_close(*(md)); +} +/* libgcrypt wrappers for HMAC/message digest operations }}} */ + +/* Decryption integrity check {{{ */ + +static gint +ssh_get_digest_by_id(guint mac_id) +{ + if(mac_id==CIPHER_MAC_SHA2_256){ + return GCRY_MD_SHA256; + } + return -1; +} + +static void +ssh_calc_mac(struct ssh_peer_data *peer_data, guint32 seqnr, guint8* data, guint32 datalen, guint8* calc_mac) +{ + SSH_HMAC hm; + gint md; + guint32 len; + guint8 buf[DIGEST_MAX_SIZE]; + + md=ssh_get_digest_by_id(peer_data->mac_id); +// ssl_debug_printf("ssh_check_mac mac type:%s md %d\n", +// ssl_cipher_suite_dig(decoder->cipher_suite)->name, md); + + memset(calc_mac, 0, DIGEST_MAX_SIZE); + + if (ssh_hmac_init(&hm, peer_data->hmac_iv, peer_data->hmac_iv_len,md) != 0) + return; + + /* hash sequence number */ + phton32(buf, seqnr); + + ssh_print_data("Mac IV", peer_data->hmac_iv, peer_data->hmac_iv_len); + ssh_print_data("Mac seq", buf, 4); + ssh_print_data("Mac data", data, datalen); + + ssh_hmac_update(&hm,buf,4); + + ssh_hmac_update(&hm,data,datalen); + + /* get digest and digest len*/ + len = sizeof(buf); + ssh_hmac_final(&hm,buf,&len); + ssh_hmac_cleanup(&hm); + ssh_print_data("Mac", buf, len); + memcpy(calc_mac, buf, len); + + return; +} +/* Decryption integrity check }}} */ + +static void +ssh_increment_message_number(packet_info *pinfo, struct ssh_flow_data *global_data, + gboolean is_response) +{ + if (!PINFO_FD_VISITED(pinfo)) { + ssh_packet_info_t * packet = (ssh_packet_info_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_ssh, 0); + if(!packet){ + packet = wmem_new0(wmem_file_scope(), ssh_packet_info_t); + packet->from_server = is_response; + packet->messages = NULL; + p_add_proto_data(wmem_file_scope(), pinfo, proto_ssh, 0, packet); + } + (void)global_data; + } +} + +static guint +ssh_decrypt_packet(tvbuff_t *tvb, packet_info *pinfo, + struct ssh_peer_data *peer_data, int offset, proto_tree *tree) +{ + gboolean is_response = (pinfo->destport != pinfo->match_uint); + ssh_packet_info_t *packet = (ssh_packet_info_t *)p_get_proto_data( + wmem_file_scope(), pinfo, proto_ssh, 0); + if(!packet){ + packet = wmem_new0(wmem_file_scope(), ssh_packet_info_t); + packet->from_server = is_response; + packet->messages = NULL; + p_add_proto_data(wmem_file_scope(), pinfo, proto_ssh, 0, packet); + } + + gint record_id = tvb_raw_offset(tvb)+offset; + ssh_message_info_t *message = NULL; + ssh_message_info_t **pmessage = &packet->messages; + while(*pmessage){ +// ws_debug("looking for message %d now %d", record_id, (*pmessage)->id); + if ((*pmessage)->id == record_id) { + message = *pmessage; + break; + } + pmessage = &(*pmessage)->next; + } + if(!message){ + message = wmem_new(wmem_file_scope(), ssh_message_info_t); + message->plain_data = NULL; + message->data_len = 0; + message->id = record_id; + message->next = NULL; + message->sequence_number = peer_data->sequence_number; + peer_data->sequence_number++; + ssh_debug_printf("%s->sequence_number++ > %d\n", is_response?"server":"client", peer_data->sequence_number); + *pmessage = message; + } + + gcry_error_t err; + guint message_length = 0, seqnr; + gchar *plain = NULL, *mac; + guint mac_len; + + seqnr = message->sequence_number; + + if (GCRY_CIPHER_CHACHA20 == peer_data->cipher_id) { + const gchar *ctext = (const gchar *)tvb_get_ptr(tvb, offset, 4); + guint8 plain_length_buf[4]; + + if (!ssh_decrypt_chacha20(peer_data->cipher_2, seqnr, 0, ctext, 4, + plain_length_buf, 4)) { + ws_debug("ERROR: could not decrypt packet len"); + return tvb_captured_length(tvb); + } + + message_length = pntoh32(plain_length_buf); + + ssh_debug_printf("chachapoly_crypt seqnr=%d [%u]\n", seqnr, message_length); + + ssh_debug_printf("%s plain for seq = %d len = %u\n", is_response?"s2c":"c2s", seqnr, message_length); + if(message_length>32768){ + ws_debug("ssh: unreasonable message length %u", message_length); + return tvb_captured_length(tvb); + } + + plain = (gchar *)wmem_alloc0(pinfo->pool, message_length+4); + plain[0] = plain_length_buf[0]; plain[1] = plain_length_buf[1]; plain[2] = plain_length_buf[2]; plain[3] = plain_length_buf[3]; + const gchar *ctext2 = (const gchar *)tvb_get_ptr(tvb, offset+4, + message_length); + + if (!ssh_decrypt_chacha20(peer_data->cipher, seqnr, 1, ctext2, + message_length, plain+4, message_length)) { + ws_debug("ERROR: could not decrypt packet payload"); + return tvb_captured_length(tvb); + } + + mac_len = 16; + mac = (gchar *)tvb_get_ptr(tvb, offset + 4 + message_length, mac_len); + gchar poly_key[32], iv[16]; + + memset(poly_key, 0, 32); + memset(iv, 0, 8); + phton64(iv+8, (guint64)seqnr); + gcry_cipher_setiv(peer_data->cipher, iv, mac_len); + gcry_cipher_encrypt(peer_data->cipher, poly_key, 32, poly_key, 32); + + gcry_mac_hd_t mac_hd; + gcry_mac_open(&mac_hd, GCRY_MAC_POLY1305, 0, NULL); + gcry_mac_setkey(mac_hd, poly_key, 32); + gcry_mac_write(mac_hd, ctext, 4); + gcry_mac_write(mac_hd, ctext2, message_length); + if (gcry_mac_verify(mac_hd, mac, mac_len)) { + ws_debug("ssh: MAC does not match"); + } + size_t buflen = DIGEST_MAX_SIZE; + gcry_mac_read(mac_hd, message->calc_mac, &buflen); + gcry_mac_close(mac_hd); + + message->plain_data = plain; + message->data_len = message_length + 4; + +// ssh_print_data(is_response?"s2c encrypted":"c2s encrypted", ctext2, message_length+4+mac_len); + ssh_debug_printf("%s plain text seq=%d", is_response?"s2c":"c2s",seqnr); + ssh_print_data("", plain, message_length+4); + } else if (CIPHER_AES128_GCM == peer_data->cipher_id || CIPHER_AES256_GCM == peer_data->cipher_id) { + + mac_len = peer_data->mac_length; + + /* AES GCM for Secure Shell [RFC 5647] */ + /* The message length is Additional Authenticated Data */ + /* XXX: If there are fewer than 4 octets available, we need to + * ask the TCP dissector for DESEGMENT_ONE_MORE_SEGMENT instead + * of throwing an exception here, if we're desegmenting. + */ + message_length = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN); + guint remaining = tvb_reported_length_remaining(tvb, offset); + ssh_debug_printf("length: %d, remaining: %d\n", message_length, remaining); + /* The minimum size of a packet (not counting mac) is 16. */ + if (message_length < 16) { + ws_debug("ssh: unreasonable message length %u", message_length); + return tvb_captured_length(tvb); + } + + /* SSH requires that the data to be encrypted (not including the AAD, + * so message_length) be a multiple of the block size, 16 octets */ + if (message_length % 16 != 0) { + ssh_debug_printf("length not a multiple of block length (16)!\n"); + } + + if(message->plain_data && message->data_len){ + }else{ + + /* If tvb_reported_length_remaining(tvb, offset + 4) is less + * than message_length + mac_len, then we should ask the TCP + * dissector for more data if we're desegmenting. That is + * simpler than trying to handle fragmentation ourselves. + */ + const gchar *ctext = (const gchar *)tvb_get_ptr(tvb, offset + 4, + message_length); + plain = (gchar *)wmem_alloc(wmem_file_scope(), message_length+4); + phton32(plain, message_length); + + /* gcry_cipher_setiv(peer_data->cipher, iv, 12); */ + if ((err = gcry_cipher_setiv(peer_data->cipher, peer_data->iv, 12))) { + //gcry_cipher_close(peer_data->cipher); + //Don't close this unless we also remove the wmem callback +// TODO: temporary work-around as long as a Windows python bug is triggered by automated tests +#ifndef _WIN32 + ws_debug("ssh: can't set aes128 cipher iv"); + ws_debug("libgcrypt: %d %s %s", gcry_err_code(err), gcry_strsource(err), gcry_strerror(err)); +#endif //ndef _WIN32 + return tvb_captured_length(tvb); + } + int idx = 12; + do{ + idx -= 1; + peer_data->iv[idx] += 1; + }while(idx>4 && peer_data->iv[idx]==0); + + if ((err = gcry_cipher_authenticate(peer_data->cipher, plain, 4))) { +// TODO: temporary work-around as long as a Windows python bug is triggered by automated tests +#ifndef _WIN32 + ws_debug("can't authenticate using aes128-gcm: %s\n", gpg_strerror(err)); +#endif //ndef _WIN32 + return tvb_captured_length(tvb); + } + + if ((err = gcry_cipher_decrypt(peer_data->cipher, plain+4, message_length, + ctext, message_length))) { +// TODO: temporary work-around as long as a Windows python bug is triggered by automated tests +#ifndef _WIN32 + ws_debug("can't decrypt aes-gcm %d %s %s", gcry_err_code(err), gcry_strsource(err), gcry_strerror(err)); + +#endif //ndef _WIN32 + return tvb_captured_length(tvb); + } + + if (gcry_cipher_gettag (peer_data->cipher, message->calc_mac, 16)) { +// TODO: temporary work-around as long as a Windows python bug is triggered by automated tests +#ifndef _WIN32 + ws_debug ("aes128-gcm, gcry_cipher_gettag() failed\n"); +#endif //ndef _WIN32 + return tvb_captured_length(tvb); + } + + if ((err = gcry_cipher_reset(peer_data->cipher))) { +// TODO: temporary work-around as long as a Windows python bug is triggered by automated tests +#ifndef _WIN32 + ws_debug("aes-gcm, gcry_cipher_reset failed: %s\n", gpg_strerror (err)); +#endif //ndef _WIN32 + return tvb_captured_length(tvb); + } + + message->plain_data = plain; + message->data_len = message_length + 4; + +// ssh_print_data(is_response?"s2c encrypted":"c2s encrypted", ctl, message_length+4+mac_len); + ssh_debug_printf("%s plain text seq=%d", is_response?"s2c":"c2s",seqnr); + ssh_print_data("", plain, message_length+4); + } + + plain = message->plain_data; + message_length = message->data_len - 4; + + + } else if (CIPHER_AES128_CBC == peer_data->cipher_id || CIPHER_AES128_CTR == peer_data->cipher_id || + CIPHER_AES192_CBC == peer_data->cipher_id || CIPHER_AES192_CTR == peer_data->cipher_id || + CIPHER_AES256_CBC == peer_data->cipher_id || CIPHER_AES256_CTR == peer_data->cipher_id) { + + mac_len = peer_data->mac_length; + message_length = tvb_reported_length_remaining(tvb, offset) - 4 - mac_len; + + if(message->plain_data && message->data_len){ + }else{ +// TODO: see how to handle fragmentation... +// const gchar *ctext = NULL; + ws_noisy("Getting raw bytes of length %d", tvb_reported_length_remaining(tvb, offset)); + /* In CBC and CTR mode, the message length is encrypted as well. + * We need to decrypt one block to get the length. + * If we have fewer than 16 octets, and we're doing desegmentation, + * we should tell the TCP dissector we need ONE_MORE_SEGMENT. + */ + const gchar *cypher_buf0 = (const gchar *)tvb_get_ptr(tvb, offset, 16); + + gchar plain0[16]; + if (gcry_cipher_decrypt(peer_data->cipher, plain0, 16, cypher_buf0, 16)) + { + ws_debug("can\'t decrypt aes128"); + return tvb_captured_length(tvb); + } + + guint message_length_decrypted = pntoh32(plain0); + guint remaining = tvb_reported_length_remaining(tvb, offset); + + /* The message_length value doesn't include the length of the + * message_length field itself, so it must be at least 12 bytes. + */ + if(message_length_decrypted>32768 || message_length_decrypted < 12){ + ws_debug("ssh: unreasonable message length %u/%u", message_length_decrypted, message_length); + return tvb_captured_length(tvb); + }else{ + + message_length = message_length_decrypted; + /* SSH requires that the data to be encrypted (message_length+4) + * be a multiple of the block size, 16 octets. */ + if (message_length % 16 != 12) { + ssh_debug_printf("total length not a multiple of block length (16)!\n"); + } + message->plain_data = (gchar *)wmem_alloc(wmem_file_scope(), message_length+4); + memcpy(message->plain_data, plain0, 16); + plain = message->plain_data; + + /* If we're desegmenting, we want to test if we have enough + * remaining bytes here. It's easier to have the TCP + * dissector put together a PDU based on our length. + */ + + if (message_length - 12 > 0) { + /* All of these functions actually do handle the case where + * there is no data left, so the check is unnecessary. + */ + gchar *ct = (gchar *)tvb_get_ptr(tvb, offset + 16, message_length - 12); + if ((err = gcry_cipher_decrypt(peer_data->cipher, plain + 16, message_length - 12, ct, message_length - 12))) + { + ws_debug("can't decrypt aes-cbc/ctr %d %s %s", gcry_err_code(err), gcry_strsource(err), gcry_strerror(err)); + return tvb_captured_length(tvb); + } + } + + /* XXX: Need to test if we have enough data above if we're + * doing desegmentation; the tvb_get_ptr() calls will throw + * exceptions if there's not enough data before we get here. + */ + if(message_length_decrypted>remaining){ + // Need desegmentation + ws_noisy(" need_desegmentation: offset = %d, reported_length_remaining = %d\n", + offset, tvb_reported_length_remaining(tvb, offset)); + /* Make data available to ssh_follow_tap_listener */ + return tvb_captured_length(tvb); + } + +// ssh_print_data(is_response?"s2c encrypted":"c2s encrypted", ctext, message_length+4+mac_len); + ssh_debug_printf("%s plain text seq=%d", is_response?"s2c":"c2s",seqnr); + ssh_print_data("", plain, message_length+4); + +// TODO: process fragments + message->plain_data = plain; + message->data_len = message_length + 4; + + ssh_calc_mac(peer_data, message->sequence_number, message->plain_data, message->data_len, message->calc_mac); + } + } + plain = message->plain_data; + message_length = message->data_len - 4; + mac = (gchar *)tvb_get_ptr(tvb, offset + 4 + message_length, mac_len); + if(!memcmp(mac, message->calc_mac, mac_len)){ws_noisy("MAC OK");}else{ws_debug("MAC ERR");} + /* XXX: The TLS dissector, by default, when the MAC is invalid regards + * the packet as having failed decryption, and does not display it + * as here. (There is a preference to disable that for testing.) + */ + } + + if(plain){ + ssh_dissect_decrypted_packet(tvb, pinfo, peer_data, tree, plain, message_length+4); + ssh_tree_add_mac(tree, tvb, offset + 4 + message_length, mac_len, hf_ssh_mac_string, hf_ssh_mac_status, &ei_ssh_mac_bad, pinfo, message->calc_mac, + PROTO_CHECKSUM_VERIFY|PROTO_CHECKSUM_IN_CKSUM); + proto_tree_add_uint(tree, hf_ssh_seq_num, tvb, offset + 4 + message_length, mac_len, message->sequence_number); + } + /* If decryption fails, we should present it as a still encrypted packet. + * We also should not try to decrypt on future passes if it failed the + * first pass, because the cipher state will be wrong anyway. + */ + + offset += message_length + peer_data->mac_length + 4; + return offset; +} + +proto_item * +ssh_tree_add_mac(proto_tree *tree, tvbuff_t *tvb, const guint offset, const guint mac_len, + const int hf_mac, const int hf_mac_status, struct expert_field* bad_checksum_expert, + packet_info *pinfo, const guint8 * calc_mac, const guint flags) +{ +// header_field_info *hfinfo = proto_registrar_get_nth(hf_checksum); + proto_item* ti = NULL; + proto_item* ti2; + gboolean incorrect_mac = TRUE; + gchar *mac; + +// DISSECTOR_ASSERT_HINT(hfinfo != NULL, "Not passed hfi!"); +/* + if (flags & PROTO_CHECKSUM_NOT_PRESENT) { + ti = proto_tree_add_uint_format_value(tree, hf_checksum, tvb, offset, len, 0, "[missing]"); + proto_item_set_generated(ti); + if (hf_checksum_status != -1) { + ti2 = proto_tree_add_uint(tree, hf_checksum_status, tvb, offset, len, PROTO_CHECKSUM_E_NOT_PRESENT); + proto_item_set_generated(ti2); + } + return ti; + } +*/ + mac = (gchar *)tvb_get_ptr(tvb, offset, mac_len); + if (flags & PROTO_CHECKSUM_GENERATED) { +// ti = proto_tree_add_uint(tree, hf_checksum, tvb, offset, len, computed_checksum); +// proto_item_set_generated(ti); + } else { + ti = proto_tree_add_item(tree, hf_mac, tvb, offset, mac_len, ENC_NA); + if (flags & PROTO_CHECKSUM_VERIFY) { + if (flags & (PROTO_CHECKSUM_IN_CKSUM|PROTO_CHECKSUM_ZERO)) { + if (!memcmp(mac, calc_mac, mac_len)) { + proto_item_append_text(ti, " [correct]"); + if (hf_mac_status != -1) { + ti2 = proto_tree_add_uint(tree, hf_mac_status, tvb, offset, 0, PROTO_CHECKSUM_E_GOOD); + proto_item_set_generated(ti2); + } + incorrect_mac = FALSE; + } else if (flags & PROTO_CHECKSUM_IN_CKSUM) { +// computed_checksum = in_cksum_shouldbe(checksum, computed_checksum); + } + } else { + if (!memcmp(mac, calc_mac, mac_len)) { + proto_item_append_text(ti, " [correct]"); + if (hf_mac_status != -1) { + ti2 = proto_tree_add_uint(tree, hf_mac_status, tvb, offset, 0, PROTO_CHECKSUM_E_GOOD); + proto_item_set_generated(ti2); + } + incorrect_mac = FALSE; + } + } + + if (incorrect_mac) { + if (hf_mac_status != -1) { + ti2 = proto_tree_add_uint(tree, hf_mac_status, tvb, offset, 0, PROTO_CHECKSUM_E_BAD); + proto_item_set_generated(ti2); + } + if (flags & PROTO_CHECKSUM_ZERO) { + proto_item_append_text(ti, " [incorrect]"); + if (bad_checksum_expert != NULL) + expert_add_info_format(pinfo, ti, bad_checksum_expert, "%s", expert_get_summary(bad_checksum_expert)); + } else { + gchar *data = (gchar *)wmem_alloc(wmem_packet_scope(), mac_len*2 + 1); + *bytes_to_hexstr(data, calc_mac, mac_len) = 0; + proto_item_append_text(ti, " incorrect, computed %s", data); + if (bad_checksum_expert != NULL) + expert_add_info_format(pinfo, ti, bad_checksum_expert, "%s", expert_get_summary(bad_checksum_expert)); + } + } + } else { + if (hf_mac_status != -1) { + proto_item_append_text(ti, " [unverified]"); + ti2 = proto_tree_add_uint(tree, hf_mac_status, tvb, offset, 0, PROTO_CHECKSUM_E_UNVERIFIED); + proto_item_set_generated(ti2); + } + } + } + + return ti; +} + +static gboolean +ssh_decrypt_chacha20(gcry_cipher_hd_t hd, + guint32 seqnr, guint32 counter, const guchar *ctext, guint ctext_len, + guchar *plain, guint plain_len) +{ + guchar seq[8]; + guchar iv[16]; + + phton64(seq, (guint64)seqnr); + + // chacha20 uses a different cipher handle for the packet payload & length + // the payload uses a block counter + if (counter) { + guchar ctr[8] = {1,0,0,0,0,0,0,0}; + memcpy(iv, ctr, 8); + memcpy(iv+8, seq, 8); + } + + return ((!counter && gcry_cipher_setiv(hd, seq, 8) == 0) || + (counter && gcry_cipher_setiv(hd, iv, 16) == 0)) && + gcry_cipher_decrypt(hd, plain, plain_len, ctext, ctext_len) == 0; +} + +static int +ssh_dissect_decrypted_packet(tvbuff_t *tvb, packet_info *pinfo, + struct ssh_peer_data *peer_data, proto_tree *tree, + gchar *plaintext, guint plaintext_len) +{ + int offset = 0; // TODO: + int dissected_len = 0; + + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Encrypted packet (plaintext_len=%d)", plaintext_len); + + tvbuff_t *packet_tvb = tvb_new_child_real_data(tvb, plaintext, plaintext_len, plaintext_len); + add_new_data_source(pinfo, packet_tvb, "Decrypted Packet"); + + guint plen, len; + guint8 padding_length; + guint remain_length; + int last_offset=offset; + guint msg_code; + + proto_item *ti; + proto_item *msg_type_tree = NULL; + + /* + * We use "tvb_ensure_captured_length_remaining()" to make sure there + * actually *is* data remaining. + * + * This means we're guaranteed that "remain_length" is positive. + */ + remain_length = tvb_ensure_captured_length_remaining(packet_tvb, offset); + /* + * Can we do reassembly? + */ + if (ssh_desegment && pinfo->can_desegment) { + /* + * Yes - would an SSH header starting at this offset + * be split across segment boundaries? + */ + if (remain_length < 4) { + /* + * Yes. Tell the TCP dissector where the data for + * this message starts in the data it handed us and + * that we need "some more data." Don't tell it + * exactly how many bytes we need because if/when we + * ask for even more (after the header) that will + * break reassembly. + */ + pinfo->desegment_offset = offset; + pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT; + return offset; + } + } + plen = tvb_get_ntohl(packet_tvb, offset) ; + + if (ssh_desegment && pinfo->can_desegment) { + if (plen +4 > remain_length) { + pinfo->desegment_offset = offset; + pinfo->desegment_len = plen+4 - remain_length; + return offset; + } + } + /* + * Need to check plen > 0x80000000 here + */ + + ti = proto_tree_add_uint(tree, hf_ssh_packet_length, packet_tvb, + offset, 4, plen); + if (plen >= 0xffff) { + expert_add_info_format(pinfo, ti, &ei_ssh_packet_length, "Overly large number %d", plen); + plen = remain_length-4; + } + offset+=4; + + /* padding length */ + padding_length = tvb_get_guint8(packet_tvb, offset); + proto_tree_add_uint(tree, hf_ssh_padding_length, packet_tvb, offset, 1, padding_length); + offset += 1; + + /* msg_code */ + msg_code = tvb_get_guint8(packet_tvb, offset); + + /* Transport layer protocol */ + /* Generic (1-19) */ + if(msg_code >= 1 && msg_code <= 19) { + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(msg_code, ssh2_msg_vals, "Unknown (%u)")); + msg_type_tree = proto_tree_add_subtree(tree, packet_tvb, offset, plen-1, ett_key_exchange, NULL, "Message: Transport (generic)"); + proto_tree_add_item(msg_type_tree, hf_ssh2_msg_code, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + offset+=1; + dissected_len = ssh_dissect_transport_generic(packet_tvb, pinfo, offset, msg_type_tree, msg_code) - offset; + // offset = ssh_dissect_transport_generic(packet_tvb, pinfo, global_data, offset, msg_type_tree, is_response, msg_code); + } + /* Algorithm negotiation (20-29) */ + else if(msg_code >=20 && msg_code <= 29) { + msg_type_tree = proto_tree_add_subtree(tree, packet_tvb, offset, plen-1, ett_key_exchange, NULL, "Message: Transport (algorithm negotiation)"); +//TODO: See if the complete dissector should be refactored to always got through here first offset = ssh_dissect_transport_algorithm_negotiation(packet_tvb, pinfo, global_data, offset, msg_type_tree, is_response, msg_code); + } + /* Key exchange method specific (reusable) (30-49) */ + else if (msg_code >=30 && msg_code <= 49) { + msg_type_tree = proto_tree_add_subtree(tree, packet_tvb, offset, plen-1, ett_key_exchange, NULL, "Message: Transport (key exchange method specific)"); +//TODO: See if the complete dissector should be refactored to always got through here first offset = global_data->kex_specific_dissector(msg_code, packet_tvb, pinfo, offset, msg_type_tree); + } + + /* User authentication protocol */ + /* Generic (50-59) */ + else if (msg_code >= 50 && msg_code <= 59) { + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(msg_code, ssh2_msg_vals, "Unknown (%u)")); + msg_type_tree = proto_tree_add_subtree(tree, packet_tvb, offset, plen-1, ett_key_exchange, NULL, "Message: User Authentication (generic)"); + proto_tree_add_item(msg_type_tree, hf_ssh2_msg_code, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + dissected_len = ssh_dissect_userauth_generic(packet_tvb, pinfo, offset+1, msg_type_tree, msg_code) - offset; + // TODO: offset = ssh_dissect_userauth_generic(packet_tvb, pinfo, global_data, offset, msg_type_tree, is_response, msg_code); + } + /* User authentication method specific (reusable) (60-79) */ + else if (msg_code >= 60 && msg_code <= 79) { + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(msg_code, ssh2_msg_vals, "Unknown (%u)")); + msg_type_tree = proto_tree_add_subtree(tree, packet_tvb, offset, plen-1, ett_key_exchange, NULL, "Message: User Authentication: (method specific)"); + proto_tree_add_item(msg_type_tree, hf_ssh2_msg_code, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + // TODO: offset = ssh_dissect_userauth_specific(packet_tvb, pinfo, global_data, offset, msg_type_tree, is_response, msg_code); + dissected_len = ssh_dissect_userauth_specific(packet_tvb, pinfo, offset+1, msg_type_tree, msg_code) - offset; + } + + /* Connection protocol */ + /* Generic (80-89) */ + else if (msg_code >= 80 && msg_code <= 89) { + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(msg_code, ssh2_msg_vals, "Unknown (%u)")); + msg_type_tree = proto_tree_add_subtree(tree, packet_tvb, offset, plen-1, ett_key_exchange, NULL, "Message: Connection (generic)"); + proto_tree_add_item(msg_type_tree, hf_ssh2_msg_code, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + // TODO: offset = ssh_dissect_connection_generic(packet_tvb, pinfo, global_data, offset, msg_type_tree, is_response, msg_code); + dissected_len = ssh_dissect_connection_generic(packet_tvb, pinfo, offset+1, msg_type_tree, msg_code) - offset; + } + /* Channel related messages (90-127) */ + else if (msg_code >= 90 && msg_code <= 127) { + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(msg_code, ssh2_msg_vals, "Unknown (%u)")); + msg_type_tree = proto_tree_add_subtree(tree, packet_tvb, offset, plen-1, ett_key_exchange, NULL, "Message: Connection: (channel related message)"); + proto_tree_add_item(msg_type_tree, hf_ssh2_msg_code, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + // TODO: offset = ssh_dissect_connection_channel(packet_tvb, pinfo, global_data, offset, msg_type_tree, is_response, msg_code); + dissected_len = ssh_dissect_connection_specific(packet_tvb, pinfo, peer_data, offset+1, msg_type_tree, msg_code) - offset; + } + + /* Reserved for client protocols (128-191) */ + else if (msg_code >= 128 && msg_code <= 191) { + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(msg_code, ssh2_msg_vals, "Unknown (%u)")); + msg_type_tree = proto_tree_add_subtree(tree, packet_tvb, offset, plen-1, ett_key_exchange, NULL, "Message: Client protocol"); + proto_tree_add_item(msg_type_tree, hf_ssh2_msg_code, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + offset+=1; + // TODO: offset = ssh_dissect_client(packet_tvb, pinfo, global_data, offset, msg_type_tree, is_response, msg_code); + } + + /* Local extensions (192-255) */ + else if (msg_code >= 192 && msg_code <= 255) { + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(msg_code, ssh2_msg_vals, "Unknown (%u)")); + msg_type_tree = proto_tree_add_subtree(tree, packet_tvb, offset, plen-1, ett_key_exchange, NULL, "Message: Local extension"); + proto_tree_add_item(msg_type_tree, hf_ssh2_msg_code, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + offset+=1; + // TODO: offset = ssh_dissect_local_extention(packet_tvb, pinfo, global_data, offset, msg_type_tree, is_response, msg_code); + } + + len = plen+4-padding_length-(offset-last_offset); + if (len > 0) { + proto_tree_add_item(msg_type_tree, hf_ssh_payload, packet_tvb, offset, len, ENC_NA); + } + if(dissected_len!=(int)len){ +// expert_add_info_format(pinfo, ti, &ei_ssh_packet_decode, "Decoded %d bytes, but packet length is %d bytes", dissected_len, len); + expert_add_info_format(pinfo, ti, &ei_ssh_packet_decode, "Decoded %d bytes, but packet length is %d bytes [%d]", dissected_len, len, msg_code); + } + offset +=len; + + /* padding */ + proto_tree_add_item(tree, hf_ssh_padding_string, packet_tvb, offset, padding_length, ENC_NA); + offset+= padding_length; + + return offset; +} + +static int +ssh_dissect_transport_generic(tvbuff_t *packet_tvb, packet_info *pinfo, + int offset, proto_item *msg_type_tree, guint msg_code) +{ + (void)pinfo; + if(msg_code==SSH_MSG_DISCONNECT){ + proto_tree_add_item(msg_type_tree, hf_ssh_disconnect_reason, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + guint nlen; + nlen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_disconnect_description_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_disconnect_description, packet_tvb, offset, nlen, ENC_ASCII); + offset += nlen; + nlen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_lang_tag_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_lang_tag, packet_tvb, offset, nlen, ENC_ASCII); + offset += nlen; + }else if(msg_code==SSH_MSG_IGNORE){ + offset += ssh_tree_add_string(packet_tvb, offset, msg_type_tree, hf_ssh_ignore_data, hf_ssh_ignore_data_length); + }else if(msg_code==SSH_MSG_DEBUG){ + guint slen; + proto_tree_add_item(msg_type_tree, hf_ssh_debug_always_display, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_debug_message_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_debug_message, packet_tvb, offset, slen, ENC_UTF_8); + offset += slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_lang_tag_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_lang_tag, packet_tvb, offset, slen, ENC_ASCII); + offset += slen; + }else if(msg_code==SSH_MSG_SERVICE_REQUEST){ + guint nlen; + nlen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_service_name_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_service_name, packet_tvb, offset, nlen, ENC_ASCII); + offset += nlen; + }else if(msg_code==SSH_MSG_SERVICE_ACCEPT){ + guint nlen; + nlen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_service_name_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_service_name, packet_tvb, offset, nlen, ENC_ASCII); + offset += nlen; + } + return offset; +} + +static int +ssh_dissect_userauth_generic(tvbuff_t *packet_tvb, packet_info *pinfo, + int offset, proto_item *msg_type_tree, guint msg_code) +{ + if(msg_code==SSH_MSG_USERAUTH_REQUEST){ + guint slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_user_name_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_user_name, packet_tvb, offset, slen, ENC_ASCII); + offset += slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_service_name_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_service_name, packet_tvb, offset, slen, ENC_ASCII); + offset += slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_method_name_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_method_name, packet_tvb, offset, slen, ENC_ASCII); + + guint8* key_type; + key_type = tvb_get_string_enc(wmem_packet_scope(), packet_tvb, offset, slen, ENC_ASCII|ENC_NA); + offset += slen; + if (0 == strcmp(key_type, "none")) { + }else if (0 == strcmp(key_type, "publickey")) { + guint8 bHaveSignature = tvb_get_guint8(packet_tvb, offset); + int dissected_len = 0; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_have_signature, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_pka_name_len, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_pka_name, packet_tvb, offset, slen, ENC_ASCII); + offset += slen; + proto_item *blob_tree = NULL; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_blob_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + blob_tree = proto_tree_add_subtree(msg_type_tree, packet_tvb, offset, slen, ett_userauth_pk_blob, NULL, "Public key blob"); +// proto_tree_add_item(blob_tree, hf_ssh2_msg_code, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + dissected_len = ssh_dissect_public_key_blob(packet_tvb, pinfo, offset, blob_tree) - offset; + if(dissected_len!=(int)slen){ + expert_add_info_format(pinfo, blob_tree, &ei_ssh_packet_decode, "Decoded %d bytes, but packet length is %d bytes", dissected_len, slen); + } + offset += slen; + if(bHaveSignature){ + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_signature_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_item *signature_tree = NULL; + signature_tree = proto_tree_add_subtree(msg_type_tree, packet_tvb, offset, slen, ett_userauth_pk_signautre, NULL, "Public key signature"); + dissected_len = ssh_dissect_public_key_signature(packet_tvb, pinfo, offset, signature_tree) - offset; + if(dissected_len!=(int)slen){ + expert_add_info_format(pinfo, signature_tree, &ei_ssh_packet_decode, "Decoded %d bytes, but packet length is %d bytes", dissected_len, slen); + } + offset += slen; + } + }else if (0 == strcmp(key_type, "password")) { + guint8 bChangePassword = tvb_get_guint8(packet_tvb, offset); + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_change_password, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_password_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_password, packet_tvb, offset, slen, ENC_ASCII); + offset += slen; + if(bChangePassword){ + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_new_password_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_new_password, packet_tvb, offset, slen, ENC_ASCII); + offset += slen; + } + }else{ + } + + }else if(msg_code==SSH_MSG_USERAUTH_FAILURE){ + guint slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_auth_failure_list_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_auth_failure_list, packet_tvb, offset, slen, ENC_ASCII); + offset += slen; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_partial_success, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + } + return offset; +} + +static int +ssh_dissect_userauth_specific(tvbuff_t *packet_tvb, packet_info *pinfo, + int offset, proto_item *msg_type_tree, guint msg_code) +{ + if(msg_code==SSH_MSG_USERAUTH_PK_OK){ + proto_item *ti; + int dissected_len = 0; + guint slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_pka_name_len, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_userauth_pka_name, packet_tvb, offset, slen, ENC_ASCII); + offset += slen; + proto_item *blob_tree = NULL; + slen = tvb_get_ntohl(packet_tvb, offset) ; + ti = proto_tree_add_item(msg_type_tree, hf_ssh_blob_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + blob_tree = proto_tree_add_subtree(msg_type_tree, packet_tvb, offset, slen, ett_userauth_pk_blob, NULL, "Public key blob"); + dissected_len = ssh_dissect_public_key_blob(packet_tvb, pinfo, offset, blob_tree) - offset; + if(dissected_len!=(int)slen){ + expert_add_info_format(pinfo, ti, &ei_ssh_packet_decode, "Decoded %d bytes, but packet length is %d bytes", dissected_len, slen); + } + offset += slen; + } + return offset; +} + +static int +ssh_dissect_connection_specific(tvbuff_t *packet_tvb, packet_info *pinfo, + struct ssh_peer_data *peer_data, int offset, proto_item *msg_type_tree, + guint msg_code) +{ + if(msg_code==SSH_MSG_CHANNEL_OPEN){ + guint slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_connection_type_name_len, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_connection_type_name, packet_tvb, offset, slen, ENC_UTF_8); + offset += slen; + proto_tree_add_item(msg_type_tree, hf_ssh_connection_sender_channel, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_connection_initial_window, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_connection_maximum_packet_size, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + }else if(msg_code==SSH_MSG_CHANNEL_OPEN_CONFIRMATION){ + proto_tree_add_item(msg_type_tree, hf_ssh_connection_recipient_channel, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_connection_sender_channel, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_connection_initial_window, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_connection_maximum_packet_size, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + }else if(msg_code==SSH_MSG_CHANNEL_WINDOW_ADJUST){ + proto_tree_add_item(msg_type_tree, hf_ssh_connection_recipient_channel, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_channel_window_adjust, packet_tvb, offset, 4, ENC_BIG_ENDIAN); // TODO: maintain count of transfered bytes and window size + offset += 4; + }else if(msg_code==SSH_MSG_CHANNEL_DATA){ + guint uiNumChannel; + uiNumChannel = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_connection_recipient_channel, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; +// TODO: process according to the type of channel + guint slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_channel_data_len, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + tvbuff_t *next_tvb = tvb_new_subset_remaining(packet_tvb, offset); + dissector_handle_t subdissector_handle = get_subdissector_for_channel(peer_data, uiNumChannel); + if(subdissector_handle){ + call_dissector(subdissector_handle, next_tvb, pinfo, msg_type_tree); + } + offset += slen; + }else if(msg_code==SSH_MSG_CHANNEL_EOF){ + proto_tree_add_item(msg_type_tree, hf_ssh_connection_recipient_channel, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + }else if(msg_code==SSH_MSG_CHANNEL_CLOSE){ + proto_tree_add_item(msg_type_tree, hf_ssh_connection_recipient_channel, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + }else if(msg_code==SSH_MSG_CHANNEL_REQUEST){ + guint uiNumChannel; + uiNumChannel = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_connection_recipient_channel, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + guint8* request_name; + guint slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_channel_request_name_len, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + request_name = tvb_get_string_enc(wmem_packet_scope(), packet_tvb, offset, slen, ENC_ASCII|ENC_NA); + proto_tree_add_item(msg_type_tree, hf_ssh_channel_request_name, packet_tvb, offset, slen, ENC_UTF_8); + offset += slen; + proto_tree_add_item(msg_type_tree, hf_ssh_channel_request_want_reply, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + if (0 == strcmp(request_name, "subsystem")) { + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_subsystem_name_len, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + guint8* subsystem_name = tvb_get_string_enc(wmem_packet_scope(), packet_tvb, offset, slen, ENC_ASCII|ENC_NA); + set_subdissector_for_channel(peer_data, uiNumChannel, subsystem_name); + proto_tree_add_item(msg_type_tree, hf_ssh_subsystem_name, packet_tvb, offset, slen, ENC_UTF_8); + offset += slen; + }else if (0 == strcmp(request_name, "exit-status")) { + proto_tree_add_item(msg_type_tree, hf_ssh_exit_status, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + } + }else if(msg_code==SSH_MSG_CHANNEL_SUCCESS){ + proto_tree_add_item(msg_type_tree, hf_ssh_connection_recipient_channel, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + } + return offset; +} + +static dissector_handle_t +get_subdissector_for_channel(struct ssh_peer_data *peer_data, guint uiNumChannel) +{ + ssh_channel_info_t *ci = peer_data->global_data->channel_info; + while(ci){ + guint channel_number = &peer_data->global_data->peer_data[SERVER_PEER_DATA]==peer_data?ci->client_channel_number:ci->server_channel_number; + if(channel_number==uiNumChannel){return ci->subdissector_handle;} + ci = ci->next; + } + ws_debug("Error lookin up channel %d", uiNumChannel); + return NULL; +} + +static void +set_subdissector_for_channel(struct ssh_peer_data *peer_data, guint uiNumChannel, guint8* subsystem_name) +{ + ssh_channel_info_t *ci = NULL; + ssh_channel_info_t **pci = &peer_data->global_data->channel_info; + int is_server = &peer_data->global_data->peer_data[SERVER_PEER_DATA]==peer_data; + while(*pci){ + guint channel_number = is_server?(*pci)->client_channel_number:(*pci)->server_channel_number; + if (channel_number == uiNumChannel) { + ci = *pci; + break; + } + pci = &(*pci)->next; + } + if(!ci){ + ci = wmem_new(wmem_file_scope(), ssh_channel_info_t); + *pci = ci; + } + if(0 == strcmp(subsystem_name, "sftp")) { + ci->subdissector_handle = sftp_handle; + } else { + ci->subdissector_handle = NULL; + } +} + +static int +ssh_dissect_connection_generic(tvbuff_t *packet_tvb, packet_info *pinfo, + int offset, proto_item *msg_type_tree, guint msg_code) +{ + (void)pinfo; + if(msg_code==SSH_MSG_GLOBAL_REQUEST){ + guint8* request_name; + guint slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_global_request_name_len, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + request_name = tvb_get_string_enc(wmem_packet_scope(), packet_tvb, offset, slen, ENC_ASCII|ENC_NA); + proto_tree_add_item(msg_type_tree, hf_ssh_global_request_name, packet_tvb, offset, slen, ENC_ASCII); + offset += slen; + proto_tree_add_item(msg_type_tree, hf_ssh_global_request_want_reply, packet_tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + if (0 == strcmp(request_name, "hostkeys-00@openssh.com")) { + guint alen; + proto_item *ti; + int dissected_len = 0; + alen = tvb_get_ntohl(packet_tvb, offset) ; + ti = proto_tree_add_item(msg_type_tree, hf_ssh_global_request_hostkeys_array_len, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_item *blob_tree = NULL; + blob_tree = proto_tree_add_subtree(msg_type_tree, packet_tvb, offset, alen, ett_userauth_pk_blob, NULL, "Public key blob"); + dissected_len = ssh_dissect_public_key_blob(packet_tvb, pinfo, offset, blob_tree) - offset; + if(dissected_len!=(int)alen){ + expert_add_info_format(pinfo, ti, &ei_ssh_packet_decode, "Decoded %d bytes, but packet length is %d bytes", dissected_len, alen); + } + offset += alen; + } + } + return offset; +} + +static int +ssh_dissect_public_key_blob(tvbuff_t *packet_tvb, packet_info *pinfo, + int offset, proto_item *msg_type_tree) +{ + (void)pinfo; + guint slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_pk_blob_name_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_pk_blob_name, packet_tvb, offset, slen, ENC_ASCII); + offset += slen; + offset += ssh_tree_add_mpint(packet_tvb, offset, msg_type_tree, hf_ssh_blob_e); + offset += ssh_tree_add_mpint(packet_tvb, offset, msg_type_tree, hf_ssh_blob_p); + return offset; +} + +static int +ssh_dissect_public_key_signature(tvbuff_t *packet_tvb, packet_info *pinfo, + int offset, proto_item *msg_type_tree) +{ + (void)pinfo; + guint slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_pk_sig_blob_name_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_pk_sig_blob_name, packet_tvb, offset, slen, ENC_ASCII); + offset += slen; + slen = tvb_get_ntohl(packet_tvb, offset) ; + proto_tree_add_item(msg_type_tree, hf_ssh_pk_sig_s_length, packet_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(msg_type_tree, hf_ssh_pk_sig_s, packet_tvb, offset, slen, ENC_NA); + offset += slen; + return offset; +} + +#ifdef SSH_DECRYPT_DEBUG /* {{{ */ + +static FILE* ssh_debug_file=NULL; + +static void +ssh_prefs_apply_cb(void) +{ + ssh_set_debug(ssh_debug_file_name); +} + +static void +ssh_set_debug(const gchar* name) +{ + static gint debug_file_must_be_closed; + gint use_stderr; + + use_stderr = name?(strcmp(name, SSH_DEBUG_USE_STDERR) == 0):0; + + if (debug_file_must_be_closed) + fclose(ssh_debug_file); + + if (use_stderr) + ssh_debug_file = stderr; + else if (!name || (strcmp(name, "") ==0)) + ssh_debug_file = NULL; + else + ssh_debug_file = ws_fopen(name, "w"); + + if (!use_stderr && ssh_debug_file) + debug_file_must_be_closed = 1; + else + debug_file_must_be_closed = 0; + + ssh_debug_printf("Wireshark SSH debug log \n\n"); +#ifdef HAVE_LIBGNUTLS + ssh_debug_printf("GnuTLS version: %s\n", gnutls_check_version(NULL)); +#endif + ssh_debug_printf("Libgcrypt version: %s\n", gcry_check_version(NULL)); + ssh_debug_printf("\n"); +} + +static void +ssh_debug_flush(void) +{ + if (ssh_debug_file) + fflush(ssh_debug_file); +} + +static void +ssh_debug_printf(const gchar* fmt, ...) +{ + va_list ap; + + if (!ssh_debug_file) + return; + + va_start(ap, fmt); + vfprintf(ssh_debug_file, fmt, ap); + va_end(ap); +} + +static void +ssh_print_data(const gchar* name, const guchar* data, size_t len) +{ + size_t i, j, k; + if (!ssh_debug_file) + return; +#ifdef OPENSSH_STYLE + fprintf(ssh_debug_file,"%s[%d]\n",name, (int) len); +#else + fprintf(ssh_debug_file,"%s[%d]:\n",name, (int) len); +#endif + for (i=0; ilength == val2->length && + !memcmp(val1->data, val2->data, val2->length)) { + return 1; + } + return 0; +} + +static guint +ssh_hash (gconstpointer v) +{ + guint l,hash; + const ssh_bignum* id; + const guint* cur; + + if (v == NULL) { + return 0; + } + + hash = 0; + id = (const ssh_bignum*) v; + + /* id and id->data are mallocated in ssh_save_master_key(). As such 'data' + * should be aligned for any kind of access (for example as a guint as + * is done below). The intermediate void* cast is to prevent "cast + * increases required alignment of target type" warnings on CPUs (such + * as SPARCs) that do not allow misaligned memory accesses. + */ + cur = (const guint*)(void*) id->data; + + for (l=4; (l < id->length); l+=4, cur++) + hash = hash ^ (*cur); + + return hash; +} + +static void +ssh_free_glib_allocated_bignum(gpointer data) +{ + ssh_bignum * bignum; + if (data == NULL) { + return; + } + + bignum = (ssh_bignum *) data; + g_free(bignum->data); + g_free(bignum); +} + +static void +ssh_free_glib_allocated_entry(gpointer data) +{ + ssh_key_map_entry_t * entry; + if (data == NULL) { + return; + } + + entry = (ssh_key_map_entry_t *) data; + g_free(entry->type); + ssh_free_glib_allocated_bignum(entry->key_material); + g_free(entry); +} +/* Functions for SSH random hashtables. }}} */ + +static void +ssh_shutdown(void) { + g_hash_table_destroy(ssh_master_key_map); +} + +void +proto_register_ssh(void) +{ + static hf_register_info hf[] = { + { &hf_ssh_protocol, + { "Protocol", "ssh.protocol", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_packet_length, + { "Packet Length", "ssh.packet_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_packet_length_encrypted, + { "Packet Length (encrypted)", "ssh.packet_length_encrypted", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_padding_length, + { "Padding Length", "ssh.padding_length", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_payload, + { "Payload", "ssh.payload", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_encrypted_packet, + { "Encrypted Packet", "ssh.encrypted_packet", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_padding_string, + { "Padding String", "ssh.padding_string", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_seq_num, + { "Sequence number", "ssh.seq_num", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_mac_string, + { "MAC", "ssh.mac", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Message authentication code", HFILL }}, + + { &hf_ssh_mac_status, + { "MAC Status", "ssh.mac.status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0, + NULL, HFILL }}, + + { &hf_ssh_direction, + { "Direction", "ssh.direction", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, + "Message direction", HFILL }}, + + { &hf_ssh_msg_code, + { "Message Code", "ssh.message_code", + FT_UINT8, BASE_DEC, VALS(ssh1_msg_vals), 0x0, + NULL, HFILL }}, + + { &hf_ssh2_msg_code, + { "Message Code", "ssh.message_code", + FT_UINT8, BASE_DEC, VALS(ssh2_msg_vals), 0x0, + NULL, HFILL }}, + + { &hf_ssh2_kex_dh_msg_code, + { "Message Code", "ssh.message_code", + FT_UINT8, BASE_DEC, VALS(ssh2_kex_dh_msg_vals), 0x0, + NULL, 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, + NULL, HFILL }}, + + { &hf_ssh2_kex_ecdh_msg_code, + { "Message Code", "ssh.message_code", + FT_UINT8, BASE_DEC, VALS(ssh2_kex_ecdh_msg_vals), 0x0, + NULL, HFILL }}, + + { &hf_ssh_cookie, + { "Cookie", "ssh.cookie", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_kex_algorithms, + { "kex_algorithms string", "ssh.kex_algorithms", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_server_host_key_algorithms, + { "server_host_key_algorithms string", "ssh.server_host_key_algorithms", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_encryption_algorithms_client_to_server, + { "encryption_algorithms_client_to_server string", "ssh.encryption_algorithms_client_to_server", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_encryption_algorithms_server_to_client, + { "encryption_algorithms_server_to_client string", "ssh.encryption_algorithms_server_to_client", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_mac_algorithms_client_to_server, + { "mac_algorithms_client_to_server string", "ssh.mac_algorithms_client_to_server", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_mac_algorithms_server_to_client, + { "mac_algorithms_server_to_client string", "ssh.mac_algorithms_server_to_client", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_compression_algorithms_client_to_server, + { "compression_algorithms_client_to_server string", "ssh.compression_algorithms_client_to_server", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_compression_algorithms_server_to_client, + { "compression_algorithms_server_to_client string", "ssh.compression_algorithms_server_to_client", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_languages_client_to_server, + { "languages_client_to_server string", "ssh.languages_client_to_server", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_languages_server_to_client, + { "languages_server_to_client string", "ssh.languages_server_to_client", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_kex_algorithms_length, + { "kex_algorithms length", "ssh.kex_algorithms_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, 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, + NULL, 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, + NULL, 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, + NULL, 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, + NULL, 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, + NULL, 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, + NULL, 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, + NULL, 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, + NULL, 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, + NULL, HFILL }}, + + { &hf_ssh_first_kex_packet_follows, + { "First KEX Packet Follows", "ssh.first_kex_packet_follows", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_kex_reserved, + { "Reserved", "ssh.kex.reserved", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_kex_hassh_algo, + { "hasshAlgorithms", "ssh.kex.hassh_algorithms", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_kex_hassh, + { "hassh", "ssh.kex.hassh", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_kex_hasshserver_algo, + { "hasshServerAlgorithms", "ssh.kex.hasshserver_algorithms", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_kex_hasshserver, + { "hasshServer", "ssh.kex.hasshserver", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_length, + { "Host key length", "ssh.host_key.length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_type_length, + { "Host key type length", "ssh.host_key.type_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_type, + { "Host key type", "ssh.host_key.type", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_data, + { "Host key data", "ssh.host_key.data", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_rsa_n, + { "RSA modulus (N)", "ssh.host_key.rsa.n", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_rsa_e, + { "RSA public exponent (e)", "ssh.host_key.rsa.e", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_dsa_p, + { "DSA prime modulus (p)", "ssh.host_key.dsa.p", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_dsa_q, + { "DSA prime divisor (q)", "ssh.host_key.dsa.q", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_dsa_g, + { "DSA subgroup generator (g)", "ssh.host_key.dsa.g", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_dsa_y, + { "DSA public key (y)", "ssh.host_key.dsa.y", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_ecdsa_curve_id, + { "ECDSA elliptic curve identifier", "ssh.host_key.ecdsa.id", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_ecdsa_curve_id_length, + { "ECDSA elliptic curve identifier length", "ssh.host_key.ecdsa.id_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_ecdsa_q, + { "ECDSA public key (Q)", "ssh.host_key.ecdsa.q", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_ecdsa_q_length, + { "ECDSA public key length", "ssh.host_key.ecdsa.q_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_eddsa_key, + { "EdDSA public key", "ssh.host_key.eddsa.key", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostkey_eddsa_key_length, + { "EdDSA public key length", "ssh.host_key.eddsa.key_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostsig_length, + { "Host signature length", "ssh.host_sig.length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostsig_type_length, + { "Host signature type length", "ssh.host_sig.type_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostsig_type, + { "Host signature type", "ssh.host_sig.type", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostsig_data, + { "Host signature data", "ssh.host_sig.data", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostsig_rsa, + { "RSA signature", "ssh.host_sig.rsa", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_hostsig_dsa, + { "DSA signature", "ssh.host_sig.dsa", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_dh_e, + { "DH client e", "ssh.dh.e", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_dh_f, + { "DH server f", "ssh.dh.f", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_dh_gex_min, + { "DH GEX Min", "ssh.dh_gex.min", + FT_UINT32, BASE_DEC, NULL, 0x0, + "Minimal acceptable group size", HFILL }}, + + { &hf_ssh_dh_gex_nbits, + { "DH GEX Number of Bits", "ssh.dh_gex.nbits", + FT_UINT32, BASE_DEC, NULL, 0x0, + "Preferred group size", HFILL }}, + + { &hf_ssh_dh_gex_max, + { "DH GEX Max", "ssh.dh_gex.max", + FT_UINT32, BASE_DEC, NULL, 0x0, + "Maximal acceptable group size", HFILL }}, + + { &hf_ssh_dh_gex_p, + { "DH GEX modulus (P)", "ssh.dh_gex.p", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_dh_gex_g, + { "DH GEX base (G)", "ssh.dh_gex.g", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_ecdh_q_c, + { "ECDH client's ephemeral public key (Q_C)", "ssh.ecdh.q_c", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_ecdh_q_c_length, + { "ECDH client's ephemeral public key length", "ssh.ecdh.q_c_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_ecdh_q_s, + { "ECDH server's ephemeral public key (Q_S)", "ssh.ecdh.q_s", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_ecdh_q_s_length, + { "ECDH server's ephemeral public key length", "ssh.ecdh.q_s_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_mpint_length, + { "Multi Precision Integer Length", "ssh.mpint_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_ignore_data_length, + { "Debug message length", "ssh.ignore_data_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_ignore_data, + { "Ignore data", "ssh.ignore_data", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_debug_always_display, + { "Always Display", "ssh.debug_always_display", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_debug_message_length, + { "Debug message length", "ssh.debug_name_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_debug_message, + { "Debug message", "ssh.debug_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_service_name_length, + { "Service Name length", "ssh.service_name_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_service_name, + { "Service Name", "ssh.service_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_disconnect_reason, + { "Disconnect reason", "ssh.disconnect_reason", + FT_UINT32, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_disconnect_description_length, + { "Disconnect description length", "ssh.disconnect_description_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_disconnect_description, + { "Disconnect description", "ssh.disconnect_description", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_lang_tag_length, + { "Language tag length", "ssh.lang_tag_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_lang_tag, + { "Language tag", "ssh.lang_tag", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_user_name_length, + { "User Name length", "ssh.userauth_user_name_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_user_name, + { "User Name", "ssh.userauth_user_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_change_password, + { "Change password", "ssh.userauth.change_password", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_service_name_length, + { "Service Name length", "ssh.userauth_service_name_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_service_name, + { "Service Name", "ssh.userauth_service_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_method_name_length, + { "Method Name length", "ssh.userauth_method_name_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_method_name, + { "Method Name", "ssh.userauth_method_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_have_signature, + { "Have signature", "ssh.userauth.have_signature", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_password_length, + { "Password length", "ssh.userauth_password_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_password, + { "Password", "ssh.userauth_password", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_new_password_length, + { "New password length", "ssh.userauth_new_password_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_new_password, + { "New password", "ssh.userauth_new_password", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_auth_failure_list_length, + { "Authentications that can continue list len", "ssh.auth_failure_cont_list_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_auth_failure_list, + { "Authentications that can continue list", "ssh.auth_failure_cont_list", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_partial_success, + { "Partial success", "ssh.userauth.partial_success", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_pka_name_len, + { "Public key algorithm name length", "ssh.userauth_pka_name_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_userauth_pka_name, + { "Public key algorithm name", "ssh.userauth_pka_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_pk_blob_name_length, + { "Public key blob algorithm name length", "ssh.pk_blob_name_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_pk_blob_name, + { "Public key blob algorithm name", "ssh.pk_blob_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_blob_length, + { "Public key blob length", "ssh.pk_blob_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_blob_p, + { "ssh-rsa modulus (n)", "ssh.blob.ssh-rsa.n", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_blob_e, + { "ssh-rsa public exponent (e)", "ssh.blob.ssh-rsa.e", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_signature_length, + { "Public key signature blob length", "ssh.pk_sig_blob_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_pk_sig_blob_name_length, + { "Public key signature blob algorithm name length", "ssh.pk_sig_blob_name_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_pk_sig_blob_name, + { "Public key signature blob algorithm name", "ssh.pk_sig_blob_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_pk_sig_s_length, + { "ssh-rsa signature length", "ssh.sig.ssh-rsa.length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_pk_sig_s, + { "ssh-rsa signature (s)", "ssh.sig.ssh-rsa.s", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_connection_type_name_len, + { "Channel type name length", "ssh.connection_type_name_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_connection_type_name, + { "Channel type name", "ssh.connection_type_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_connection_sender_channel, + { "Sender channel", "ssh.connection_sender_channel", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_connection_recipient_channel, + { "Recipient channel", "ssh.connection_recipient_channel", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_connection_initial_window, + { "Initial window size", "ssh.connection_initial_window_size", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_connection_maximum_packet_size, + { "Maximum packet size", "ssh.userauth_maximum_packet_size", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_global_request_name_len, + { "Global request name length", "ssh.global_request_name_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_global_request_name, + { "Global request name", "ssh.global_request_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_global_request_want_reply, + { "Global request want reply", "ssh.global_request_want_reply", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_global_request_hostkeys_array_len, + { "Host keys array length", "ssh.global_request_hostkeys", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_channel_request_name_len, + { "Channel request name length", "ssh.global_request_name_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_channel_request_name, + { "Channel request name", "ssh.global_request_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_channel_request_want_reply, + { "Channel request want reply", "ssh.channel_request_want_reply", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_subsystem_name_len, + { "Subsystem name length", "ssh.subsystem_name_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_subsystem_name, + { "Subsystem name", "ssh.subsystem_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_exit_status, + { "Exit status", "ssh.exit_status", + FT_UINT32, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_channel_window_adjust, + { "Bytes to add", "ssh.channel_window_adjust", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_ssh_channel_data_len, + { "Data length", "ssh.channel_data_length", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + }; + + static gint *ett[] = { + &ett_ssh, + &ett_key_exchange, + &ett_key_exchange_host_key, + &ett_key_exchange_host_sig, + &ett_userauth_pk_blob, + &ett_userauth_pk_signautre, + &ett_ssh1, + &ett_ssh2, + &ett_key_init + }; + + static ei_register_info ei[] = { + { &ei_ssh_packet_length, { "ssh.packet_length.error", PI_PROTOCOL, PI_WARN, "Overly large number", EXPFILL }}, + { &ei_ssh_packet_decode, { "ssh.packet_decode.error", PI_PROTOCOL, PI_WARN, "Packet decoded length not equal to packet length", EXPFILL }}, + { &ei_ssh_invalid_keylen, { "ssh.key_length.error", PI_PROTOCOL, PI_ERROR, "Invalid key length", EXPFILL }}, + { &ei_ssh_mac_bad, { "ssh.mac_bad.expert", PI_CHECKSUM, PI_ERROR, "Bad MAC", EXPFILL }}, + }; + + module_t *ssh_module; + expert_module_t *expert_ssh; + + 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)); + expert_ssh = expert_register_protocol(proto_ssh); + expert_register_field_array(expert_ssh, ei, array_length(ei)); + +#ifdef SSH_DECRYPT_DEBUG + ssh_module = prefs_register_protocol(proto_ssh, ssh_prefs_apply_cb); +#else + ssh_module = prefs_register_protocol(proto_ssh, NULL); +#endif + prefs_register_bool_preference(ssh_module, "desegment_buffers", + "Reassemble SSH buffers spanning multiple TCP segments", + "Whether the SSH dissector should reassemble SSH buffers spanning multiple TCP segments. " + "To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.", + &ssh_desegment); + + ssh_master_key_map = g_hash_table_new_full(ssh_hash, ssh_equal, ssh_free_glib_allocated_bignum, ssh_free_glib_allocated_entry); + prefs_register_filename_preference(ssh_module, "keylog_file", "Key log filename", + "The path to the file which contains a list of key exchange secrets in the following format:\n" + "\" \" (without quotes or leading spaces).\n", + &pref_keylog_file, FALSE); + + prefs_register_filename_preference(ssh_module, "debug_file", "SSH debug file", + "Redirect SSH debug to the file specified. Leave empty to disable debugging " + "or use \"" SSH_DEBUG_USE_STDERR "\" to redirect output to stderr.", + &ssh_debug_file_name, TRUE); + + secrets_register_type(SECRETS_TYPE_SSH, ssh_secrets_block_callback); + + ssh_handle = register_dissector("ssh", dissect_ssh, proto_ssh); + register_shutdown_routine(ssh_shutdown); +} + +void +proto_reg_handoff_ssh(void) +{ +#ifdef SSH_DECRYPT_DEBUG + ssh_set_debug(ssh_debug_file_name); +#endif + dissector_add_uint_range_with_preference("tcp.port", TCP_RANGE_SSH, ssh_handle); + dissector_add_uint("sctp.port", SCTP_PORT_SSH, ssh_handle); + dissector_add_uint("sctp.ppi", SSH_PAYLOAD_PROTOCOL_ID, ssh_handle); + sftp_handle = find_dissector("sftp"); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ -- cgit v1.2.3