/* packet-sftp.c * Routines for ssh packet dissection * * Jérôme Hamm * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * Copied from packet-ssh.c * * SPDX-License-Identifier: GPL-2.0-or-later * * * Note: support for SFTP. * */ /* SFTP is defined in: * * draft-ietf-secsh-filexfer-02 - SSH File Transfer Protocol * */ #include "config.h" /* Start with WIRESHARK_LOG_DOMAINS=sftp and WIRESHARK_LOG_LEVEL=debug to see messages. */ #define WS_LOG_DOMAIN "sftp" #include #include void proto_register_sftp(void); static int proto_sftp; static int hf_ssh_sftp_len; static int hf_ssh_sftp_type; static int hf_ssh_sftp_version; static int hf_ssh_sftp_id; static int hf_ssh_sftp_path_len; static int hf_ssh_sftp_path; static int hf_ssh_sftp_pflags; static int hf_ssh_sftp_name_count; static int hf_ssh_sftp_name_fn_len; static int hf_ssh_sftp_name_fn; static int hf_ssh_sftp_name_ln_len; static int hf_ssh_sftp_name_ln; static int hf_ssh_sftp_attrs_flags; static int hf_ssh_sftp_attrs_size; static int hf_ssh_sftp_attrs_uid; static int hf_ssh_sftp_attrs_gid; static int hf_ssh_sftp_attrs_permissions; static int hf_ssh_sftp_attrs_atime; static int hf_ssh_sftp_attrs_mtime; static int hf_ssh_sftp_attrs_extended_count; static int hf_ssh_sftp_handle_len; static int hf_ssh_sftp_handle; static int hf_ssh_sftp_status; static int hf_ssh_sftp_error_message_len; static int hf_ssh_sftp_error_message; static int hf_ssh_sftp_offset; static int hf_ssh_sftp_length; static int hf_ssh_sftp_data_len; static int hf_ssh_sftp_data; static int hf_ssh_lang_tag_length; static int hf_ssh_lang_tag; static int ett_sftp; static int ett_sftp_attrs; static dissector_handle_t sftp_handle; #define SSH_FXP_INIT 1 #define SSH_FXP_VERSION 2 #define SSH_FXP_OPEN 3 #define SSH_FXP_CLOSE 4 #define SSH_FXP_READ 5 #define SSH_FXP_WRITE 6 #define SSH_FXP_LSTAT 7 #define SSH_FXP_FSTAT 8 #define SSH_FXP_SETSTAT 9 #define SSH_FXP_FSETSTAT 10 #define SSH_FXP_OPENDIR 11 #define SSH_FXP_READDIR 12 #define SSH_FXP_REMOVE 13 #define SSH_FXP_MKDIR 14 #define SSH_FXP_RMDIR 15 #define SSH_FXP_REALPATH 16 #define SSH_FXP_STAT 17 #define SSH_FXP_RENAME 18 #define SSH_FXP_READLINK 19 #define SSH_FXP_LINK 21 #define SSH_FXP_BLOCK 22 #define SSH_FXP_UNBLOCK 23 #define SSH_FXP_STATUS 101 #define SSH_FXP_HANDLE 102 #define SSH_FXP_DATA 103 #define SSH_FXP_NAME 104 #define SSH_FXP_ATTRS 105 #define SSH_FXP_EXTENDED 200 #define SSH_FXP_EXTENDED_REPLY 201 #define SSH_FILEXFER_ATTR_SIZE 0x00000001 #define SSH_FILEXFER_ATTR_UIDGID 0x00000002 #define SSH_FILEXFER_ATTR_PERMISSIONS 0x00000004 #define SSH_FILEXFER_ATTR_ACMODTIME 0x00000008 #define SSH_FILEXFER_ATTR_EXTENDED 0x80000000 static const value_string ssh2_sftp_vals[] = { {SSH_FXP_INIT, "SSH_FXP_INIT"}, {SSH_FXP_VERSION, "SSH_FXP_VERSION"}, {SSH_FXP_OPEN, "SSH_FXP_OPEN"}, {SSH_FXP_CLOSE, "SSH_FXP_CLOSE"}, {SSH_FXP_READ, "SSH_FXP_READ"}, {SSH_FXP_WRITE, "SSH_FXP_WRITE"}, {SSH_FXP_LSTAT, "SSH_FXP_LSTAT"}, {SSH_FXP_FSTAT, "SSH_FXP_FSTAT"}, {SSH_FXP_SETSTAT, "SSH_FXP_SETSTAT"}, {SSH_FXP_FSETSTAT, "SSH_FXP_FSETSTAT"}, {SSH_FXP_OPENDIR, "SSH_FXP_OPENDIR"}, {SSH_FXP_READDIR, "SSH_FXP_READDIR"}, {SSH_FXP_REMOVE, "SSH_FXP_REMOVE"}, {SSH_FXP_MKDIR, "SSH_FXP_MKDIR"}, {SSH_FXP_RMDIR, "SSH_FXP_RMDIR"}, {SSH_FXP_REALPATH, "SSH_FXP_REALPATH"}, {SSH_FXP_STAT, "SSH_FXP_STAT"}, {SSH_FXP_RENAME, "SSH_FXP_RENAME"}, {SSH_FXP_READLINK, "SSH_FXP_READLINK"}, {SSH_FXP_LINK, "SSH_FXP_LINK"}, {SSH_FXP_BLOCK, "SSH_FXP_BLOCK"}, {SSH_FXP_UNBLOCK, "SSH_FXP_UNBLOCK"}, {SSH_FXP_STATUS, "SSH_FXP_STATUS"}, {SSH_FXP_HANDLE, "SSH_FXP_HANDLE"}, {SSH_FXP_DATA, "SSH_FXP_DATA"}, {SSH_FXP_NAME, "SSH_FXP_NAME"}, {SSH_FXP_ATTRS, "SSH_FXP_ATTRS"}, {SSH_FXP_EXTENDED, "SSH_FXP_EXTENDED"}, {SSH_FXP_EXTENDED_REPLY, "SSH_FXP_EXTENDED_REPLY"}, {0, NULL} }; static int dissect_sftp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_); static int dissect_sftp_attrs(tvbuff_t *packet_tvb, packet_info *pinfo, int offset, proto_item *msg_type_tree); static int dissect_sftp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { int offset = 0; unsigned plen; unsigned slen; if (pinfo->can_desegment) { if (tvb_captured_length_remaining(tvb, offset) < 4) { pinfo->desegment_offset = offset; pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT; return tvb_captured_length(tvb); } } plen = tvb_get_ntohl(tvb, offset); if (pinfo->can_desegment) { unsigned length_remaining = tvb_captured_length_remaining(tvb, offset + 4); if (length_remaining < plen) { pinfo->desegment_offset = offset; pinfo->desegment_len = plen - length_remaining; return tvb_captured_length(tvb); } } wmem_strbuf_t *title = wmem_strbuf_new(pinfo->pool, ""); proto_item *ti = proto_tree_add_item(tree, proto_sftp, tvb, offset, -1, ENC_NA); proto_tree *sftp_tree = proto_item_add_subtree(ti, ett_sftp); proto_tree_add_item(sftp_tree, hf_ssh_sftp_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; uint8_t typ; typ = tvb_get_uint8(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_type, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(typ, ssh2_sftp_vals, "Unknown (%u)")); switch(typ){ case SSH_FXP_INIT:{ int ver = tvb_get_ntohl(tvb, offset) ; wmem_strbuf_append_printf(title, " SSH_FXP_INIT (%d) version %d", typ, ver); proto_tree_add_item(sftp_tree, hf_ssh_sftp_version, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; break; } case SSH_FXP_VERSION:{ int ver = tvb_get_ntohl(tvb, offset) ; wmem_strbuf_append_printf(title, " SSH_FXP_VERSION (%d) version %d", typ, ver); proto_tree_add_item(sftp_tree, hf_ssh_sftp_version, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; break; } case SSH_FXP_OPEN:{ int id = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_path_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; uint8_t * path = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, slen, ENC_UTF_8); proto_tree_add_item(sftp_tree, hf_ssh_sftp_path, tvb, offset, slen, ENC_UTF_8); offset += slen; // int pflags = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_pflags, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = dissect_sftp_attrs(tvb, pinfo, offset, sftp_tree); offset += slen; wmem_strbuf_append_printf(title, " SSH_FXP_OPEN (%d) id=%d [%s]", typ, id, path); break; } case SSH_FXP_CLOSE:{ int id = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_handle_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; char * handle = tvb_bytes_to_str(wmem_packet_scope(), tvb, offset, slen); proto_tree_add_item(sftp_tree, hf_ssh_sftp_handle, tvb, offset, slen, ENC_NA); offset += slen; wmem_strbuf_append_printf(title, " SSH_FXP_CLOSE (%d) id=%d {%s}", typ, id, handle); break; } case SSH_FXP_READ:{ int id = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_handle_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; char * handle = tvb_bytes_to_str(wmem_packet_scope(), tvb, offset, slen); proto_tree_add_item(sftp_tree, hf_ssh_sftp_handle, tvb, offset, slen, ENC_NA); offset += slen; proto_tree_add_item(sftp_tree, hf_ssh_sftp_offset, tvb, offset, 8, ENC_BIG_ENDIAN); offset += 8; proto_tree_add_item(sftp_tree, hf_ssh_sftp_length, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; wmem_strbuf_append_printf(title, " SSH_FXP_READ (%d) id=%d {%s}", typ, id, handle); break; } case SSH_FXP_WRITE:{ int id = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_handle_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; char * handle = tvb_bytes_to_str(wmem_packet_scope(), tvb, offset, slen); proto_tree_add_item(sftp_tree, hf_ssh_sftp_handle, tvb, offset, slen, ENC_NA); offset += slen; proto_tree_add_item(sftp_tree, hf_ssh_sftp_offset, tvb, offset, 8, ENC_BIG_ENDIAN); offset += 8; int dlen = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_data_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(sftp_tree, hf_ssh_sftp_data, tvb, offset, dlen, ENC_NA); offset += dlen; wmem_strbuf_append_printf(title, " SSH_FXP_WRITE (%d) id=%d {%s} len=%d", typ, id, handle, dlen); break; } case SSH_FXP_LSTAT:{ int id = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_path_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; uint8_t * path = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, slen, ENC_UTF_8); wmem_strbuf_append_printf(title, " SSH_FXP_LSTAT (%d) id=%d [%s]", typ, id, path); proto_tree_add_item(sftp_tree, hf_ssh_sftp_path, tvb, offset, slen, ENC_UTF_8); offset += slen; break; } case SSH_FXP_FSTAT:{ int id = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_handle_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; char * handle = tvb_bytes_to_str(wmem_packet_scope(), tvb, offset, slen); proto_tree_add_item(sftp_tree, hf_ssh_sftp_handle, tvb, offset, slen, ENC_NA); offset += slen; wmem_strbuf_append_printf(title, " SSH_FXP_FSTAT (%d) id=%d {%s}", typ, id, handle); break; } case SSH_FXP_SETSTAT:{ int id = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_path_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; uint8_t * path = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, slen, ENC_UTF_8); proto_tree_add_item(sftp_tree, hf_ssh_sftp_path, tvb, offset, slen, ENC_UTF_8); offset += slen; slen = dissect_sftp_attrs(tvb, pinfo, offset, sftp_tree); proto_item_set_len(sftp_tree, slen); offset += slen; wmem_strbuf_append_printf(title, " SSH_FXP_SETSTAT (%d) id=%d [%s]", typ, id, path); break; } // case SSH_FXP_FSETSTAT):{ // break; // } case SSH_FXP_OPENDIR:{ int id = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_path_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; uint8_t * path = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, slen, ENC_UTF_8); proto_tree_add_item(sftp_tree, hf_ssh_sftp_path, tvb, offset, slen, ENC_UTF_8); offset += slen; wmem_strbuf_append_printf(title, " SSH_FXP_OPENDIR (%d) id=%d [%s]", typ, id, path); break; } case SSH_FXP_READDIR:{ int id = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_handle_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; char * handle = tvb_bytes_to_str(wmem_packet_scope(), tvb, offset, slen); proto_tree_add_item(sftp_tree, hf_ssh_sftp_handle, tvb, offset, slen, ENC_NA); offset += slen; wmem_strbuf_append_printf(title, " SSH_FXP_READDIR (%d) id=%d {%s}", typ, id, handle); break; } case SSH_FXP_REMOVE:{ int id = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_path_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; uint8_t * path = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, slen, ENC_UTF_8); wmem_strbuf_append_printf(title, " SSH_FXP_REMOVE (%d) id=%d [%s]", typ, id, path); proto_tree_add_item(sftp_tree, hf_ssh_sftp_path, tvb, offset, slen, ENC_UTF_8); offset += slen; break; } // case SSH_FXP_MKDIR:{ // break; // } // case SSH_FXP_RMDIR:{ // break; // } case SSH_FXP_REALPATH:{ int id = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_path_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; uint8_t * path = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, slen, ENC_UTF_8); wmem_strbuf_append_printf(title, " SSH_FXP_REALPATH (%d) id=%d [%s]", typ, id, path); proto_tree_add_item(sftp_tree, hf_ssh_sftp_path, tvb, offset, slen, ENC_UTF_8); offset += slen; break; } case SSH_FXP_STAT:{ int id = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_path_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; uint8_t * path = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, slen, ENC_UTF_8); wmem_strbuf_append_printf(title, " SSH_FXP_STAT (%d) id=%d [%s]", typ, id, path); proto_tree_add_item(sftp_tree, hf_ssh_sftp_path, tvb, offset, slen, ENC_UTF_8); offset += slen; break; } case SSH_FXP_RENAME:{ int id = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_path_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; uint8_t * oldpath = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, slen, ENC_UTF_8); proto_tree_add_item(sftp_tree, hf_ssh_sftp_path, tvb, offset, slen, ENC_UTF_8); offset += slen; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_path_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; uint8_t * newpath = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, slen, ENC_UTF_8); proto_tree_add_item(sftp_tree, hf_ssh_sftp_path, tvb, offset, slen, ENC_UTF_8); offset += slen; wmem_strbuf_append_printf(title, " SSH_FXP_STAT (%d) id=%d [%s] > [%s]", typ, id, oldpath, newpath); break; } // case SSH_FXP_READLINK:{ // break; // } // case SSH_FXP_SYMLINK:{ // break; // } case SSH_FXP_STATUS:{ int id = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; int code = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_status, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_error_message_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; uint8_t * err_msg = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, slen, ENC_UTF_8); proto_tree_add_item(sftp_tree, hf_ssh_sftp_error_message, tvb, offset, slen, ENC_UTF_8); offset += slen; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_lang_tag_length, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(sftp_tree, hf_ssh_lang_tag, tvb, offset, slen, ENC_UTF_8); offset += slen; wmem_strbuf_append_printf(title, " SSH_FXP_STATUS (%d) id=%d code=%d [%s]", typ, id, code, err_msg); break; } case SSH_FXP_HANDLE:{ int id = tvb_get_ntohl(tvb, offset); proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; slen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_handle_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; char * handle = tvb_bytes_to_str(wmem_packet_scope(), tvb, offset, slen); proto_tree_add_item(sftp_tree, hf_ssh_sftp_handle, tvb, offset, slen, ENC_NA); offset += slen; wmem_strbuf_append_printf(title, " SSH_FXP_HANDLE (%d) id=%d {%s}", typ, id, handle); break; } case SSH_FXP_DATA:{ int id = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; int dlen = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_data_len, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(sftp_tree, hf_ssh_sftp_data, tvb, offset, dlen, ENC_NA); offset += dlen; wmem_strbuf_append_printf(title, " SSH_FXP_DATA (%d) id=%d len=%d", typ, id, dlen); break; } case SSH_FXP_NAME:{ wmem_strbuf_append_printf(title, " SSH_FXP_NAME (%d)", typ); proto_tree_add_item(sftp_tree, hf_ssh_sftp_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; unsigned count = tvb_get_ntohl(tvb, offset) ; proto_tree_add_item(sftp_tree, hf_ssh_sftp_name_count, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; unsigned cnt; for(cnt=0;cnt