wireshark/packet-smb-pipe.c

2963 lines
80 KiB
C
Raw Normal View History

/*
XXX Fixme : shouldnt show [malformed frame] for long packets
*/
/* packet-smb-pipe.c
* Routines for SMB named pipe packet dissection
* Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
* significant rewrite to tvbuffify the dissector, Ronnie Sahlberg and
* Guy Harris 2001
*
* $Id: packet-smb-pipe.c,v 1.50 2001/11/20 07:47:41 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
* Copyright 1998 Gerald Combs
*
* Copied from packet-pop.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#include <time.h>
#include <string.h>
#include <glib.h>
#include <ctype.h>
#include "packet.h"
#include "conversation.h"
#include "smb.h"
#include "packet-smb-pipe.h"
#include "packet-smb-browse.h"
#include "packet-dcerpc.h"
static int proto_smb_pipe = -1;
static int hf_pipe_function = -1;
static int hf_pipe_priority = -1;
static int hf_pipe_peek_available = -1;
static int hf_pipe_peek_remaining = -1;
static int hf_pipe_peek_status = -1;
static int hf_pipe_getinfo_info_level = -1;
static int hf_pipe_getinfo_output_buffer_size = -1;
static int hf_pipe_getinfo_input_buffer_size = -1;
static int hf_pipe_getinfo_maximum_instances = -1;
static int hf_pipe_getinfo_current_instances = -1;
static int hf_pipe_getinfo_pipe_name_length = -1;
static int hf_pipe_getinfo_pipe_name = -1;
static int hf_pipe_write_raw_bytes_written = -1;
static gint ett_smb_pipe = -1;
static int proto_smb_lanman = -1;
static int hf_function_code = -1;
static int hf_param_desc = -1;
static int hf_return_desc = -1;
static int hf_aux_data_desc = -1;
static int hf_detail_level = -1;
static int hf_recv_buf_len = -1;
static int hf_send_buf_len = -1;
static int hf_continuation_from = -1;
static int hf_status = -1;
static int hf_convert = -1;
static int hf_ecount = -1;
static int hf_acount = -1;
static int hf_share_name = -1;
static int hf_share_type = -1;
static int hf_share_comment = -1;
static int hf_share_permissions = -1;
static int hf_share_max_uses = -1;
static int hf_share_current_uses = -1;
static int hf_share_path = -1;
static int hf_share_password = -1;
static int hf_server_name = -1;
static int hf_server_major = -1;
static int hf_server_minor = -1;
static int hf_server_comment = -1;
static int hf_abytes = -1;
static int hf_current_time = -1;
static int hf_msecs = -1;
static int hf_hour = -1;
static int hf_minute = -1;
static int hf_second = -1;
static int hf_hundredths = -1;
static int hf_tzoffset = -1;
static int hf_timeinterval = -1;
static int hf_day = -1;
static int hf_month = -1;
static int hf_year = -1;
static int hf_weekday = -1;
static int hf_enumeration_domain = -1;
static int hf_computer_name = -1;
static int hf_user_name = -1;
static int hf_workstation_domain = -1;
static int hf_workstation_major = -1;
static int hf_workstation_minor = -1;
static int hf_logon_domain = -1;
static int hf_other_domains = -1;
static int hf_password = -1;
static int hf_workstation_name = -1;
static int hf_ustruct_size = -1;
static int hf_logon_code = -1;
static int hf_privilege_level = -1;
static int hf_operator_privileges = -1;
static int hf_num_logons = -1;
static int hf_bad_pw_count = -1;
static int hf_last_logon = -1;
static int hf_last_logoff = -1;
static int hf_logoff_time = -1;
static int hf_kickoff_time = -1;
static int hf_password_age = -1;
static int hf_password_can_change = -1;
static int hf_password_must_change = -1;
static int hf_script_path = -1;
static int hf_logoff_code = -1;
static int hf_duration = -1;
static int hf_user_comment = -1;
static int hf_full_name = -1;
static int hf_homedir = -1;
static int hf_parameters = -1;
static int hf_logon_server = -1;
static int hf_country_code = -1;
static int hf_workstations = -1;
static int hf_max_storage = -1;
static int hf_units_per_week = -1;
static int hf_logon_hours = -1;
static int hf_code_page = -1;
static int hf_new_password = -1;
static int hf_old_password = -1;
static int hf_reserved = -1;
static gint ett_lanman = -1;
static gint ett_lanman_shares = -1;
static gint ett_lanman_share = -1;
static gint ett_lanman_servers = -1;
static gint ett_lanman_server = -1;
/*
* See
*
* ftp://ftp.microsoft.com/developr/drg/CIFS/cifsrap2.txt
*
* among other documents.
*/
static const value_string status_vals[] = {
{0, "Success"},
{5, "User has insufficient privilege"},
{65, "Network access is denied"},
{86, "The specified password is invalid"},
{SMBE_moredata, "Additional data is available"},
{2114, "Service is not running on the remote computer"},
{2123, "Supplied buffer is too small"},
{2141, "Server is not configured for transactions (IPC$ not shared)"},
{2212, "An error occurred while loading or running the logon script"},
{2214, "The logon was not validated by any server"},
{2217, "The logon server is running an older software version"},
{2221, "The user name was not found"},
{2240, "The user is not allowed to logon from this computer"},
{2241, "The user is not allowed to logon at this time"},
{2242, "The user password has expired"},
{2243, "The password cannot be changed"},
{2246, "The password is too short"},
{0, NULL}
};
static const value_string share_type_vals[] = {
{0, "Directory tree"},
{1, "Printer queue"},
{2, "Communications device"},
{3, "IPC"},
{0, NULL}
};
static const value_string privilege_vals[] = {
{0, "Guest"},
{1, "User"},
{2, "Administrator"},
{0, NULL}
};
static const value_string op_privilege_vals[] = {
{0, "Print operator"},
{1, "Communications operator"},
{2, "Server operator"},
{3, "Accounts operator"},
{0, NULL}
};
static const value_string weekday_vals[] = {
{0, "Sunday"},
{1, "Monday"},
{2, "Tuesday"},
{3, "Wednesday"},
{4, "Thursday"},
{5, "Friday"},
{6, "Saturday"},
{0, NULL}
};
static int
add_word_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint16 WParam;
if (hf_index != -1)
proto_tree_add_item(tree, hf_index, tvb, offset, 2, TRUE);
else {
WParam = tvb_get_letohs(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"Word Param: %u (0x%04X)", WParam, WParam);
}
offset += 2;
return offset;
}
static int
add_dword_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint32 LParam;
if (hf_index != -1)
proto_tree_add_item(tree, hf_index, tvb, offset, 4, TRUE);
else {
LParam = tvb_get_letohl(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 4,
"Doubleword Param: %u (0x%08X)", LParam, LParam);
}
offset += 4;
return offset;
}
static int
add_byte_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint8 BParam;
if (hf_index != -1)
proto_tree_add_item(tree, hf_index, tvb, offset, count, TRUE);
else {
if (count == 1) {
BParam = tvb_get_guint8(tvb, offset);
proto_tree_add_text(tree, tvb, offset, count,
"Byte Param: %u (0x%02X)",
BParam, BParam);
} else {
proto_tree_add_text(tree, tvb, offset, count,
"Bytes Param: %s, type is wrong",
tvb_bytes_to_str(tvb, offset, count));
}
}
offset += count;
return offset;
}
static int
add_pad_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
/*
* This is for parameters that have descriptor entries but that
* are, in practice, just padding.
*/
offset += count;
return offset;
}
static void
add_null_pointer_param(tvbuff_t *tvb, int offset, int count,
packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
if (hf_index != -1) {
proto_tree_add_text(tree, tvb, offset, 0,
"%s (Null pointer)",
proto_registrar_get_name(hf_index));
} else {
proto_tree_add_text(tree, tvb, offset, 0,
"String Param (Null pointer)");
}
}
static int
add_string_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint string_len;
string_len = tvb_strsize(tvb, offset);
if (hf_index != -1) {
proto_tree_add_item(tree, hf_index, tvb, offset, string_len,
TRUE);
} else {
proto_tree_add_text(tree, tvb, offset, string_len,
"String Param: %s",
tvb_format_text(tvb, offset, string_len));
}
offset += string_len;
return offset;
}
static const char *
get_pointer_value(tvbuff_t *tvb, int offset, int convert, int *cptrp, int *lenp)
{
int cptr;
gint string_len;
/* pointer to string */
cptr = (tvb_get_letohl(tvb, offset)&0xffff)-convert;
*cptrp = cptr;
/* string */
if (tvb_offset_exists(tvb, cptr) &&
(string_len = tvb_strnlen(tvb, cptr, -1)) != -1) {
string_len++; /* include the terminating '\0' */
*lenp = string_len;
return tvb_format_text(tvb, cptr, string_len - 1);
} else
return NULL;
}
static int
add_pointer_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
int cptr;
const char *string;
gint string_len;
string = get_pointer_value(tvb, offset, convert, &cptr, &string_len);
offset += 4;
/* string */
if (string != NULL) {
if (hf_index != -1) {
proto_tree_add_item(tree, hf_index, tvb, cptr,
string_len, TRUE);
} else {
proto_tree_add_text(tree, tvb, cptr, string_len,
"String Param: %s", string);
}
} else {
if (hf_index != -1) {
proto_tree_add_text(tree, tvb, 0, 0,
"%s: <String goes past end of frame>",
proto_registrar_get_name(hf_index));
} else {
proto_tree_add_text(tree, tvb, 0, 0,
"String Param: <String goes past end of frame>");
}
}
return offset;
}
static int
add_detail_level(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
struct smb_info *smb_info = pinfo->private_data;
smb_transact_info_t *trp = smb_info->sip->extra_info;
guint16 level;
level = tvb_get_letohs(tvb, offset);
if (!pinfo->fd->flags.visited)
trp->info_level = level; /* remember this for the response */
proto_tree_add_uint(tree, hf_index, tvb, offset, 2, level);
offset += 2;
return offset;
}
static int
add_max_uses(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint16 WParam;
WParam = tvb_get_letohs(tvb, offset);
if (WParam == 0xffff) { /* -1 */
proto_tree_add_uint_format(tree, hf_index, tvb,
offset, 2, WParam,
"%s: No limit",
proto_registrar_get_name(hf_index));
} else {
proto_tree_add_uint(tree, hf_index, tvb,
offset, 2, WParam);
}
offset += 2;
return offset;
}
static int
add_server_type(tvbuff_t *tvb, int offset, int count,
packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
dissect_smb_server_type_flags(tvb, pinfo, tree, offset, FALSE);
offset += 4;
return offset;
}
static int
add_server_type_info(tvbuff_t *tvb, int offset, int count,
packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
dissect_smb_server_type_flags(tvb, pinfo, tree, offset, TRUE);
offset += 4;
return offset;
}
static int
add_reltime(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
nstime_t nstime;
nstime.secs = tvb_get_letohl(tvb, offset);
nstime.nsecs = 0;
proto_tree_add_time_format(tree, hf_index, tvb, offset, 4,
&nstime, "%s: %s", proto_registrar_get_name(hf_index),
time_secs_to_str(nstime.secs));
offset += 4;
return offset;
}
/*
* Sigh. These are for handling Microsoft's annoying almost-UNIX-time-but-
* it's-local-time-not-UTC time.
*/
static int
add_abstime_common(tvbuff_t *tvb, int offset, int count,
packet_info *pinfo, proto_tree *tree, int convert, int hf_index,
const char *absent_name)
{
nstime_t nstime;
struct tm *tmp;
nstime.secs = tvb_get_letohl(tvb, offset);
nstime.nsecs = 0;
if (nstime.secs == -1) {
proto_tree_add_time_format(tree, hf_index, tvb, offset, 4,
&nstime, "%s: %s", proto_registrar_get_name(hf_index),
absent_name);
} else {
/*
* Run it through "gmtime()" to break it down, and then
* run it through "mktime()" to put it back together
* as UTC.
*/
tmp = gmtime(&nstime.secs);
tmp->tm_isdst = -1; /* we don't know if it's DST or not */
nstime.secs = mktime(tmp);
proto_tree_add_time(tree, hf_index, tvb, offset, 4,
&nstime);
}
offset += 4;
return offset;
}
static int
add_abstime_absent_never(tvbuff_t *tvb, int offset, int count,
packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
return add_abstime_common(tvb, offset, count, pinfo, tree,
convert, hf_index, "Never");
}
static int
add_abstime_absent_unknown(tvbuff_t *tvb, int offset, int count,
packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
return add_abstime_common(tvb, offset, count, pinfo, tree,
convert, hf_index, "Unknown");
}
static int
add_nlogons(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint16 nlogons;
nlogons = tvb_get_letohs(tvb, offset);
if (nlogons == 0xffff) /* -1 */
proto_tree_add_uint_format(tree, hf_index, tvb, offset, 2,
nlogons, "%s: Unknown",
proto_registrar_get_name(hf_index));
else
proto_tree_add_uint(tree, hf_index, tvb, offset, 2,
nlogons);
offset += 2;
return offset;
}
static int
add_max_storage(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint32 max_storage;
max_storage = tvb_get_letohl(tvb, offset);
if (max_storage == 0xffffffff)
proto_tree_add_uint_format(tree, hf_index, tvb, offset, 4,
max_storage, "%s: No limit",
proto_registrar_get_name(hf_index));
else
proto_tree_add_uint(tree, hf_index, tvb, offset, 4,
max_storage);
offset += 4;
return offset;
}
static int
add_logon_hours(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
int cptr;
/* pointer to string */
cptr = (tvb_get_letohl(tvb, offset)&0xffff)-convert;
offset += 4;
/* string */
/* XXX - should actually carve up the bits */
proto_tree_add_item(tree, hf_index, tvb, cptr, 21, TRUE);
return offset;
}
static int
add_tzoffset(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
gint16 tzoffset;
tzoffset = tvb_get_letohs(tvb, offset);
if (tzoffset < 0) {
proto_tree_add_int_format(tree, hf_tzoffset, tvb, offset, 2,
tzoffset, "%s: %s east of UTC",
proto_registrar_get_name(hf_index),
time_secs_to_str(-tzoffset*60));
} else if (tzoffset > 0) {
proto_tree_add_int_format(tree, hf_tzoffset, tvb, offset, 2,
tzoffset, "%s: %s west of UTC",
proto_registrar_get_name(hf_index),
time_secs_to_str(tzoffset*60));
} else {
proto_tree_add_int_format(tree, hf_tzoffset, tvb, offset, 2,
tzoffset, "%s: at UTC",
proto_registrar_get_name(hf_index));
}
offset += 2;
return offset;
}
static int
add_timeinterval(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint16 timeinterval;
timeinterval = tvb_get_letohs(tvb, offset);
proto_tree_add_uint_format(tree, hf_timeinterval, tvb, offset, 2,
timeinterval, "%s: %f seconds", proto_registrar_get_name(hf_index),
timeinterval*.0001);
offset += 2;
return offset;
}
static int
add_logon_args(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
if (count != 54) {
proto_tree_add_text(tree, tvb, offset, count,
"Bogus NetWkstaUserLogon parameters: length is %d, should be 54",
count);
offset += count;
return offset;
}
/* user name */
proto_tree_add_item(tree, hf_user_name, tvb, offset, 21, TRUE);
offset += 21;
/* pad1 */
offset += 1;
/* password */
proto_tree_add_item(tree, hf_password, tvb, offset, 15, TRUE);
offset += 15;
/* pad2 */
offset += 1;
/* workstation name */
proto_tree_add_item(tree, hf_workstation_name, tvb, offset, 16, TRUE);
offset += 16;
return offset;
}
/*
* The following data structure describes the Remote API requests we
* understand.
*
* Simply fill in the number and parameter information.
* Try to keep them in order.
*
* We will extend this data structure as we try to decode more.
*/
/*
* This is a pointer to a function to process an item.
*/
typedef int (*item_func)(tvbuff_t *, int, int, packet_info *, proto_tree *,
int, int);
/*
* Type of an item; determines what parameter strings are valid for
* the item.
*/
typedef enum {
PARAM_NONE, /* for the end-of-list stopper */
PARAM_WORD, /* 'W' or 'h' - 16-bit word */
PARAM_DWORD, /* 'D' or 'i' - 32-bit word */
PARAM_BYTES, /* 'B' or 'b' or 'g' or 'O' - one or more bytes */
PARAM_STRINGZ, /* 'z' or 'O' - null-terminated string */
} param_type_t;
/*
* This structure describes an item; "hf_index" points to the index
* for the field corresponding to that item, "func" points to the
* function to use to add that item to the tree, and "type" is the
* type that the item is supposed to have.
*/
typedef struct {
int *hf_index;
item_func func;
param_type_t type;
} item_t;
/*
* This structure describes a list of items; each list of items
* has a corresponding detail level.
*/
typedef struct {
int level;
const item_t *item_list;
} item_list_t;
struct lanman_desc {
int lanman_num;
const item_t *req;
proto_item *(*req_data_item)(tvbuff_t *, packet_info *,
proto_tree *, int);
gint *ett_req_data;
const item_t *req_data;
const item_t *req_aux_data;
const item_t *resp;
proto_item *(*resp_data_item)(tvbuff_t *, packet_info *,
proto_tree *, int);
gint *ett_resp_data;
proto_item *(*resp_data_element_item)(tvbuff_t *, packet_info *,
proto_tree *, int);
gint *ett_resp_data_element_item;
const item_list_t *resp_data_list;
const item_t *resp_aux_data;
};
static int no_hf = -1; /* for padding crap */
static const item_t lm_params_req_netshareenum[] = {
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ &hf_recv_buf_len, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netshareenum[] = {
{ &hf_acount, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
/*
* Create a subtree for all available shares.
*/
static proto_item *
netshareenum_shares_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
int offset)
{
if (tree) {
return proto_tree_add_text(tree, tvb, offset,
tvb_length_remaining(tvb, offset),
"Available Shares");
} else
return NULL;
}
/*
* Create a subtree for a share.
*/
static proto_item *
netshareenum_share_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
int offset)
{
if (tree) {
return proto_tree_add_text(tree, tvb, offset,
tvb_length_remaining(tvb, offset),
"Share %.13s", tvb_get_ptr(tvb, offset, 13));
} else
return NULL;
}
static const item_t lm_null[] = {
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_null_list[] = {
{ 0, lm_null }
};
static const item_t lm_data_resp_netshareenum_1[] = {
{ &hf_share_name, add_byte_param, PARAM_BYTES },
{ &no_hf, add_pad_param, PARAM_BYTES },
{ &hf_share_type, add_word_param, PARAM_WORD },
{ &hf_share_comment, add_pointer_param, PARAM_STRINGZ },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netshareenum[] = {
{ 1, lm_data_resp_netshareenum_1 },
{ -1, lm_null }
};
static const item_t lm_params_req_netsharegetinfo[] = {
{ &hf_share_name, add_string_param, PARAM_STRINGZ },
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netsharegetinfo[] = {
{ &hf_abytes, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netsharegetinfo_0[] = {
{ &hf_share_name, add_byte_param, PARAM_BYTES },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netsharegetinfo_1[] = {
{ &hf_share_name, add_byte_param, PARAM_BYTES },
{ &no_hf, add_pad_param, PARAM_BYTES },
{ &hf_share_type, add_word_param, PARAM_WORD },
{ &hf_share_comment, add_pointer_param, PARAM_STRINGZ },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netsharegetinfo_2[] = {
{ &hf_share_name, add_byte_param, PARAM_BYTES },
{ &no_hf, add_pad_param, PARAM_BYTES },
{ &hf_share_type, add_word_param, PARAM_WORD },
{ &hf_share_comment, add_pointer_param, PARAM_STRINGZ },
{ &hf_share_permissions, add_word_param, PARAM_WORD }, /* XXX - do as bit fields */
{ &hf_share_max_uses, add_max_uses, PARAM_WORD },
{ &hf_share_current_uses, add_word_param, PARAM_WORD },
{ &hf_share_path, add_pointer_param, PARAM_STRINGZ },
{ &hf_share_password, add_byte_param, PARAM_BYTES },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netsharegetinfo[] = {
{ 0, lm_data_resp_netsharegetinfo_0 },
{ 1, lm_data_resp_netsharegetinfo_1 },
{ 2, lm_data_resp_netsharegetinfo_2 },
{ -1, lm_null }
};
static const item_t lm_params_req_netservergetinfo[] = {
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netservergetinfo[] = {
{ &hf_abytes, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_serverinfo_0[] = {
{ &hf_server_name, add_byte_param, PARAM_BYTES },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_serverinfo_1[] = {
{ &hf_server_name, add_byte_param, PARAM_BYTES },
{ &hf_server_major, add_byte_param, PARAM_BYTES },
{ &hf_server_minor, add_byte_param, PARAM_BYTES },
{ &no_hf, add_server_type, PARAM_DWORD },
{ &hf_server_comment, add_pointer_param, PARAM_STRINGZ },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_serverinfo[] = {
{ 0, lm_data_serverinfo_0 },
{ 1, lm_data_serverinfo_1 },
{ -1, lm_null }
};
static const item_t lm_params_req_netusergetinfo[] = {
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netusergetinfo[] = {
{ &hf_abytes, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netusergetinfo_11[] = {
{ &hf_user_name, add_byte_param, PARAM_BYTES },
{ &no_hf, add_pad_param, PARAM_BYTES },
{ &hf_user_comment, add_pointer_param, PARAM_STRINGZ },
{ &hf_full_name, add_pointer_param, PARAM_STRINGZ },
{ &hf_privilege_level, add_word_param, PARAM_WORD },
{ &hf_operator_privileges, add_dword_param, PARAM_DWORD },
{ &hf_password_age, add_reltime, PARAM_DWORD },
{ &hf_homedir, add_pointer_param, PARAM_STRINGZ },
{ &hf_parameters, add_pointer_param, PARAM_STRINGZ },
{ &hf_last_logon, add_abstime_absent_unknown, PARAM_DWORD },
{ &hf_last_logoff, add_abstime_absent_unknown, PARAM_DWORD },
{ &hf_bad_pw_count, add_word_param, PARAM_WORD },
{ &hf_num_logons, add_nlogons, PARAM_WORD },
{ &hf_logon_server, add_pointer_param, PARAM_STRINGZ },
{ &hf_country_code, add_word_param, PARAM_WORD },
{ &hf_workstations, add_pointer_param, PARAM_STRINGZ },
{ &hf_max_storage, add_max_storage, PARAM_DWORD },
{ &hf_logon_hours, add_logon_hours, PARAM_DWORD },
{ &hf_code_page, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netusergetinfo[] = {
{ 11, lm_data_resp_netusergetinfo_11 },
{ -1, lm_null }
};
/*
* Has no detail level; make it the default.
*/
static const item_t lm_data_resp_netremotetod_nolevel[] = {
{ &hf_current_time, add_abstime_absent_unknown, PARAM_DWORD },
{ &hf_msecs, add_dword_param, PARAM_DWORD },
{ &hf_hour, add_byte_param, PARAM_BYTES },
{ &hf_minute, add_byte_param, PARAM_BYTES },
{ &hf_second, add_byte_param, PARAM_BYTES },
{ &hf_hundredths, add_byte_param, PARAM_BYTES },
{ &hf_tzoffset, add_tzoffset, PARAM_WORD },
{ &hf_timeinterval, add_timeinterval, PARAM_WORD },
{ &hf_day, add_byte_param, PARAM_BYTES },
{ &hf_month, add_byte_param, PARAM_BYTES },
{ &hf_year, add_word_param, PARAM_WORD },
{ &hf_weekday, add_byte_param, PARAM_BYTES },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netremotetod[] = {
{ -1, lm_data_resp_netremotetod_nolevel },
};
static const item_t lm_params_req_netserverenum2[] = {
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ &no_hf, add_server_type_info, PARAM_DWORD },
{ &hf_enumeration_domain, add_string_param, PARAM_STRINGZ },
{ NULL, NULL, PARAM_NONE }
};
/*
* Create a subtree for all servers.
*/
static proto_item *
netserverenum2_servers_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
int offset)
{
if (tree) {
return proto_tree_add_text(tree, tvb, offset,
tvb_length_remaining(tvb, offset), "Servers");
} else
return NULL;
}
/*
* Create a subtree for a share.
*/
static proto_item *
netserverenum2_server_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
int offset)
{
if (tree) {
return proto_tree_add_text(tree, tvb, offset,
tvb_length_remaining(tvb, offset),
"Server %.16s", tvb_get_ptr(tvb, offset, 16));
} else
return NULL;
}
static const item_t lm_params_resp_netserverenum2[] = {
{ &hf_acount, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_req_netwkstagetinfo[] = {
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netwkstagetinfo[] = {
{ &hf_abytes, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netwkstagetinfo_10[] = {
{ &hf_computer_name, add_pointer_param, PARAM_STRINGZ },
{ &hf_user_name, add_pointer_param, PARAM_STRINGZ },
{ &hf_workstation_domain, add_pointer_param, PARAM_STRINGZ },
{ &hf_workstation_major, add_byte_param, PARAM_BYTES },
{ &hf_workstation_minor, add_byte_param, PARAM_BYTES },
{ &hf_logon_domain, add_pointer_param, PARAM_STRINGZ },
{ &hf_other_domains, add_pointer_param, PARAM_STRINGZ },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netwkstagetinfo[] = {
{ 10, lm_data_resp_netwkstagetinfo_10 },
{ -1, lm_null }
};
static const item_t lm_params_req_netwkstauserlogon[] = {
{ &no_hf, add_pointer_param, PARAM_STRINGZ },
{ &no_hf, add_pointer_param, PARAM_STRINGZ },
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ &no_hf, add_logon_args, PARAM_BYTES },
{ &hf_ustruct_size, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netwkstauserlogon[] = {
{ &hf_abytes, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netwkstauserlogon_1[] = {
{ &hf_logon_code, add_word_param, PARAM_WORD },
{ &hf_user_name, add_byte_param, PARAM_BYTES },
{ &no_hf, add_pad_param, PARAM_BYTES },
{ &hf_privilege_level, add_word_param, PARAM_WORD },
{ &hf_operator_privileges, add_dword_param, PARAM_DWORD },
{ &hf_num_logons, add_nlogons, PARAM_WORD },
{ &hf_bad_pw_count, add_word_param, PARAM_WORD },
{ &hf_last_logon, add_abstime_absent_unknown, PARAM_DWORD },
{ &hf_last_logoff, add_abstime_absent_unknown, PARAM_DWORD },
{ &hf_logoff_time, add_abstime_absent_never, PARAM_DWORD },
{ &hf_kickoff_time, add_abstime_absent_never, PARAM_DWORD },
{ &hf_password_age, add_reltime, PARAM_DWORD },
{ &hf_password_can_change, add_abstime_absent_never, PARAM_DWORD },
{ &hf_password_must_change, add_abstime_absent_never, PARAM_DWORD },
{ &hf_server_name, add_pointer_param, PARAM_STRINGZ },
{ &hf_logon_domain, add_pointer_param, PARAM_STRINGZ },
{ &hf_script_path, add_pointer_param, PARAM_STRINGZ },
{ &hf_reserved, add_dword_param, PARAM_DWORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netwkstauserlogon[] = {
{ 1, lm_data_resp_netwkstauserlogon_1 },
{ -1, lm_null }
};
static const item_t lm_params_req_netwkstauserlogoff[] = {
{ &hf_user_name, add_byte_param, PARAM_BYTES },
{ &no_hf, add_pad_param, PARAM_BYTES },
{ &hf_workstation_name, add_byte_param, PARAM_BYTES },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netwkstauserlogoff[] = {
{ &hf_abytes, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netwkstauserlogoff_1[] = {
{ &hf_logoff_code, add_word_param, PARAM_WORD },
{ &hf_duration, add_reltime, PARAM_DWORD },
{ &hf_num_logons, add_nlogons, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netwkstauserlogoff[] = {
{ 1, lm_data_resp_netwkstauserlogoff_1 },
{ -1, lm_null }
};
static const item_t lm_params_req_samoemchangepassword[] = {
{ &hf_user_name, add_string_param, PARAM_STRINGZ },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_req_samoemchangepassword[] = {
{ &hf_new_password, add_byte_param, PARAM_BYTES },
{ &hf_old_password, add_byte_param, PARAM_BYTES },
{ NULL, NULL, PARAM_NONE }
};
#define LANMAN_NETSHAREENUM 0
#define LANMAN_NETSHAREGETINFO 1
#define LANMAN_NETSERVERGETINFO 13
#define LANMAN_NETGROUPGETUSERS 52
#define LANMAN_NETUSERGETINFO 56
#define LANMAN_NETUSERGETGROUPS 59
#define LANMAN_NETWKSTAGETINFO 63
#define LANMAN_DOSPRINTQENUM 69
#define LANMAN_DOSPRINTQGETINFO 70
#define LANMAN_WPRINTQUEUEPAUSE 74
#define LANMAN_WPRINTQUEUERESUME 75
#define LANMAN_WPRINTJOBENUMERATE 76
#define LANMAN_WPRINTJOBGETINFO 77
#define LANMAN_RDOSPRINTJOBDEL 81
#define LANMAN_RDOSPRINTJOBPAUSE 82
#define LANMAN_RDOSPRINTJOBRESUME 83
#define LANMAN_WPRINTDESTENUM 84
#define LANMAN_WPRINTDESTGETINFO 85
#define LANMAN_NETREMOTETOD 91
#define LANMAN_WPRINTQUEUEPURGE 103
#define LANMAN_NETSERVERENUM2 104
#define LANMAN_WACCESSGETUSERPERMS 105
#define LANMAN_SETUSERPASSWORD 115
#define LANMAN_NETWKSTAUSERLOGON 132
#define LANMAN_NETWKSTAUSERLOGOFF 133
#define LANMAN_PRINTJOBINFO 147
#define LANMAN_WPRINTDRIVERENUM 205
#define LANMAN_WPRINTQPROCENUM 206
#define LANMAN_WPRINTPORTENUM 207
#define LANMAN_SAMOEMCHANGEPASSWORD 214
static const struct lanman_desc lmd[] = {
{ LANMAN_NETSHAREENUM,
lm_params_req_netshareenum,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netshareenum,
netshareenum_shares_list,
&ett_lanman_shares,
netshareenum_share_entry,
&ett_lanman_share,
lm_data_resp_netshareenum,
lm_null },
{ LANMAN_NETSHAREGETINFO,
lm_params_req_netsharegetinfo,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netsharegetinfo,
NULL,
NULL,
NULL,
NULL,
lm_data_resp_netsharegetinfo,
lm_null },
{ LANMAN_NETSERVERGETINFO,
lm_params_req_netservergetinfo,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netservergetinfo,
NULL,
NULL,
NULL,
NULL,
lm_data_serverinfo,
lm_null },
{ LANMAN_NETUSERGETINFO,
lm_params_req_netusergetinfo,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netusergetinfo,
NULL,
NULL,
NULL,
NULL,
lm_data_resp_netusergetinfo,
lm_null },
{ LANMAN_NETREMOTETOD,
lm_null,
NULL,
NULL,
lm_null,
lm_null,
lm_null,
NULL,
NULL,
NULL,
NULL,
lm_data_resp_netremotetod,
lm_null },
{ LANMAN_NETSERVERENUM2,
lm_params_req_netserverenum2,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netserverenum2,
netserverenum2_servers_list,
&ett_lanman_servers,
netserverenum2_server_entry,
&ett_lanman_server,
lm_data_serverinfo,
lm_null },
{ LANMAN_NETWKSTAGETINFO,
lm_params_req_netwkstagetinfo,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netwkstagetinfo,
NULL,
NULL,
NULL,
NULL,
lm_data_resp_netwkstagetinfo,
lm_null },
{ LANMAN_NETWKSTAUSERLOGON,
lm_params_req_netwkstauserlogon,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netwkstauserlogon,
NULL,
NULL,
NULL,
NULL,
lm_data_resp_netwkstauserlogon,
lm_null },
{ LANMAN_NETWKSTAUSERLOGOFF,
lm_params_req_netwkstauserlogoff,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netwkstauserlogoff,
NULL,
NULL,
NULL,
NULL,
lm_data_resp_netwkstauserlogoff,
lm_null },
{ LANMAN_SAMOEMCHANGEPASSWORD,
lm_params_req_samoemchangepassword,
NULL,
NULL,
lm_data_req_samoemchangepassword,
lm_null,
lm_null,
NULL,
NULL,
NULL,
NULL,
lm_null_list,
lm_null },
{ -1,
lm_null,
NULL,
NULL,
lm_null,
lm_null,
lm_null,
NULL,
NULL,
NULL,
NULL,
lm_null_list,
lm_null }
};
static const struct lanman_desc *
find_lanman(int lanman_num)
{
int i;
for (i = 0; lmd[i].lanman_num != -1; i++) {
if (lmd[i].lanman_num == lanman_num)
break;
}
return &lmd[i];
}
static const guchar *
get_count(const guchar *desc, int *countp)
{
int count = 0, off = 0;
guchar c;
if (!isdigit(*desc)) {
*countp = 1; /* no count was supplied */
return desc;
}
while ((c = *desc) != '\0' && isdigit(c)) {
count = (count * 10) + c - '0';
desc++;
}
*countp = count; /* XXX - what if it's 0? */
return desc;
}
static int
dissect_request_parameters(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, const guchar *desc, const item_t *items,
gboolean *has_data_p)
{
guint c;
guint16 WParam;
guint32 LParam;
guint string_len;
int count;
*has_data_p = FALSE;
while ((c = *desc++) != '\0') {
switch (c) {
case 'W':
/*
* A 16-bit word value in the request.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_word_param(tvb, offset, 0, pinfo,
tree, 0, -1);
} else if (items->type != PARAM_WORD) {
/*
* Descriptor character is 'W', but this
* isn't a word parameter.
*/
WParam = tvb_get_letohs(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: Value is %u (0x%04X), type is wrong (W)",
(*items->hf_index == -1) ?
"Word Param" :
proto_registrar_get_name(*items->hf_index),
WParam, WParam);
offset += 2;
items++;
} else {
offset = (*items->func)(tvb, offset, 0, pinfo,
tree, 0, *items->hf_index);
items++;
}
break;
case 'D':
/*
* A 32-bit doubleword value in the request.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_dword_param(tvb, offset, 0, pinfo,
tree, 0, -1);
} else if (items->type != PARAM_DWORD) {
/*
* Descriptor character is 'D', but this
* isn't a doubleword parameter.
*/
LParam = tvb_get_letohl(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: Value is %u (0x%08X), type is wrong (D)",
(*items->hf_index == -1) ?
"Doubleword Param" :
proto_registrar_get_name(*items->hf_index),
LParam, LParam);
offset += 4;
items++;
} else {
offset = (*items->func)(tvb, offset, 0, pinfo,
tree, 0, *items->hf_index);
items++;
}
break;
case 'b':
/*
* A byte or multi-byte value in the request.
*/
desc = get_count(desc, &count);
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_byte_param(tvb, offset, count,
pinfo, tree, 0, -1);
} else if (items->type != PARAM_BYTES) {
/*
* Descriptor character is 'b', but this
* isn't a byte/bytes parameter.
*/
proto_tree_add_text(tree, tvb, offset, count,
"%s: Value is %s, type is wrong (b)",
(*items->hf_index == -1) ?
"Byte Param" :
proto_registrar_get_name(*items->hf_index),
tvb_bytes_to_str(tvb, offset, count));
offset += count;
items++;
} else {
offset = (*items->func)(tvb, offset, count,
pinfo, tree, 0, *items->hf_index);
items++;
}
break;
case 'O':
/*
* A null pointer.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
add_null_pointer_param(tvb, offset, 0,
pinfo, tree, 0, -1);
} else {
/*
* If "*items->hf_index" is -1, this is
* a reserved must-be-null field; don't
* clutter the protocol tree by putting
* it in.
*/
if (*items->hf_index != -1) {
add_null_pointer_param(tvb,
offset, 0, pinfo, tree, 0,
*items->hf_index);
}
items++;
}
break;
case 'z':
/*
* A null-terminated ASCII string.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_string_param(tvb, offset, 0,
pinfo, tree, 0, -1);
} else if (items->type != PARAM_STRINGZ) {
/*
* Descriptor character is 'z', but this
* isn't a string parameter.
*/
string_len = tvb_strsize(tvb, offset);
proto_tree_add_text(tree, tvb, offset, string_len,
"%s: Value is %s, type is wrong (z)",
(*items->hf_index == -1) ?
"String Param" :
proto_registrar_get_name(*items->hf_index),
tvb_format_text(tvb, offset, string_len));
offset += string_len;
items++;
} else {
offset = (*items->func)(tvb, offset, 0,
pinfo, tree, 0, *items->hf_index);
items++;
}
break;
case 'F':
/*
* One or more pad bytes.
*/
desc = get_count(desc, &count);
proto_tree_add_text(tree, tvb, offset, count,
"%s", "Padding");
offset += count;
break;
case 'L':
/*
* 16-bit receive buffer length.
*/
proto_tree_add_item(tree, hf_recv_buf_len, tvb,
offset, 2, TRUE);
offset += 2;
break;
case 's':
/*
* 32-bit send buffer offset.
* This appears not to be sent over the wire.
*/
*has_data_p = TRUE;
break;
case 'T':
/*
* 16-bit send buffer length.
* This also appears not to be sent over the wire.
*/
break;
default:
break;
}
}
return offset;
}
static int
dissect_response_parameters(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, const guchar *desc, const item_t *items,
gboolean *has_data_p, gboolean *has_ent_count_p, guint16 *ent_count_p)
{
guint c;
guint16 WParam;
guint32 LParam;
int count;
*has_data_p = FALSE;
*has_ent_count_p = FALSE;
while ((c = *desc++) != '\0') {
switch (c) {
case 'r':
/*
* 32-bit receive buffer offset.
*/
*has_data_p = TRUE;
break;
case 'g':
/*
* A byte or series of bytes is returned.
*/
desc = get_count(desc, &count);
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_byte_param(tvb, offset, count,
pinfo, tree, 0, -1);
} else if (items->type != PARAM_BYTES) {
/*
* Descriptor character is 'b', but this
* isn't a byte/bytes parameter.
*/
proto_tree_add_text(tree, tvb, offset, count,
"%s: Value is %s, type is wrong (g)",
(*items->hf_index == -1) ?
"Byte Param" :
proto_registrar_get_name(*items->hf_index),
tvb_bytes_to_str(tvb, offset, count));
offset += count;
items++;
} else {
offset = (*items->func)(tvb, offset, count,
pinfo, tree, 0, *items->hf_index);
items++;
}
break;
case 'h':
/*
* A 16-bit word is received.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_word_param(tvb, offset, 0, pinfo,
tree, 0, -1);
} else if (items->type != PARAM_WORD) {
/*
* Descriptor character is 'h', but this
* isn't a word parameter.
*/
WParam = tvb_get_letohs(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: Value is %u (0x%04X), type is wrong (W)",
(*items->hf_index == -1) ?
"Word Param" :
proto_registrar_get_name(*items->hf_index),
WParam, WParam);
offset += 2;
items++;
} else {
offset = (*items->func)(tvb, offset, 0, pinfo,
tree, 0, *items->hf_index);
items++;
}
break;
case 'i':
/*
* A 32-bit doubleword is received.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_dword_param(tvb, offset, 0, pinfo,
tree, 0, -1);
} else if (items->type != PARAM_DWORD) {
/*
* Descriptor character is 'i', but this
* isn't a doubleword parameter.
*/
LParam = tvb_get_letohl(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: Value is %u (0x%08X), type is wrong (i)",
(*items->hf_index == -1) ?
"Doubleword Param" :
proto_registrar_get_name(*items->hf_index),
LParam, LParam);
offset += 4;
items++;
} else {
offset = (*items->func)(tvb, offset, 0, pinfo,
tree, 0, *items->hf_index);
items++;
}
break;
case 'e':
/*
* A 16-bit entry count is returned.
*/
WParam = tvb_get_letohs(tvb, offset);
proto_tree_add_uint(tree, hf_ecount, tvb, offset, 2,
WParam);
offset += 2;
*has_ent_count_p = TRUE;
*ent_count_p = WParam; /* Save this for later retrieval */
break;
default:
break;
}
}
return offset;
}
static int
dissect_transact_data(tvbuff_t *tvb, int offset, int convert,
packet_info *pinfo, proto_tree *tree, const guchar *desc,
const item_t *items, guint16 *aux_count_p)
{
guint c;
guint16 WParam;
guint32 LParam;
int count;
int cptr;
const char *string;
gint string_len;
if (aux_count_p != NULL)
*aux_count_p = 0;
while ((c = *desc++) != '\0') {
switch (c) {
case 'W':
/*
* A 16-bit word value.
* XXX - handle the count?
*/
desc = get_count(desc, &count);
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_word_param(tvb, offset, 0, pinfo,
tree, convert, -1);
} else if (items->type != PARAM_WORD) {
/*
* Descriptor character is 'W', but this
* isn't a word parameter.
*/
WParam = tvb_get_letohs(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: Value is %u (0x%04X), type is wrong (W)",
(*items->hf_index == -1) ?
"Word Param" :
proto_registrar_get_name(*items->hf_index),
WParam, WParam);
offset += 2;
items++;
} else {
offset = (*items->func)(tvb, offset, 0, pinfo,
tree, convert, *items->hf_index);
items++;
}
break;
case 'D':
/*
* A 32-bit doubleword value.
* XXX - handle the count?
*/
desc = get_count(desc, &count);
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_dword_param(tvb, offset, 0, pinfo,
tree, convert, -1);
} else if (items->type != PARAM_DWORD) {
/*
* Descriptor character is 'D', but this
* isn't a doubleword parameter.
*/
LParam = tvb_get_letohl(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: Value is %u (0x%08X), type is wrong (D)",
(*items->hf_index == -1) ?
"Doubleword Param" :
proto_registrar_get_name(*items->hf_index),
LParam, LParam);
offset += 4;
items++;
} else {
offset = (*items->func)(tvb, offset, 0, pinfo,
tree, convert, *items->hf_index);
items++;
}
break;
case 'B':
/*
* A byte or multi-byte value.
*/
desc = get_count(desc, &count);
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_byte_param(tvb, offset, count,
pinfo, tree, convert, -1);
} else if (items->type != PARAM_BYTES) {
/*
* Descriptor character is 'B', but this
* isn't a byte/bytes parameter.
*/
proto_tree_add_text(tree, tvb, offset, count,
"%s: Value is %s, type is wrong (B)",
(*items->hf_index == -1) ?
"Byte Param" :
proto_registrar_get_name(*items->hf_index),
tvb_bytes_to_str(tvb, offset, count));
offset += count;
items++;
} else {
offset = (*items->func)(tvb, offset, count,
pinfo, tree, convert, *items->hf_index);
items++;
}
break;
case 'O':
/*
* A null pointer.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
add_null_pointer_param(tvb, offset, 0,
pinfo, tree, convert, -1);
} else {
/*
* If "*items->hf_index" is -1, this is
* a reserved must-be-null field; don't
* clutter the protocol tree by putting
* it in.
*/
if (*items->hf_index != -1) {
add_null_pointer_param(tvb,
offset, 0, pinfo, tree, convert,
*items->hf_index);
}
items++;
}
break;
case 'z':
/*
* A pointer to a null-terminated ASCII string.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_pointer_param(tvb, offset, 0,
pinfo, tree, convert, -1);
} else if (items->type != PARAM_STRINGZ) {
/*
* Descriptor character is 'z', but this
* isn't a string parameter.
*/
string = get_pointer_value(tvb, offset,
convert, &cptr, &string_len);
offset += 4;
proto_tree_add_text(tree, tvb, cptr, string_len,
"%s: Value is %s, type is wrong (z)",
(*items->hf_index == -1) ?
"String Param" :
proto_registrar_get_name(*items->hf_index),
string);
items++;
} else {
offset = (*items->func)(tvb, offset, 0,
pinfo, tree, convert, *items->hf_index);
items++;
}
break;
case 'N':
/*
* 16-bit auxiliary data structure count.
* XXX - hf_acount?
*/
WParam = tvb_get_letohs(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: %u (0x%04X)",
"Auxiliary data structure count",
WParam, WParam);
offset += 2;
if (aux_count_p != NULL)
*aux_count_p = WParam; /* Save this for later retrieval */
break;
default:
break;
}
}
return offset;
}
static const value_string commands[] = {
{LANMAN_NETSHAREENUM, "NetShareEnum"},
{LANMAN_NETSHAREGETINFO, "NetShareGetInfo"},
{LANMAN_NETSERVERGETINFO, "NetServerGetInfo"},
{LANMAN_NETGROUPGETUSERS, "NetGroupGetUsers"},
{LANMAN_NETUSERGETINFO, "NetUserGetInfo"},
{LANMAN_NETUSERGETGROUPS, "NetUserGetGroups"},
{LANMAN_NETWKSTAGETINFO, "NetWkstaGetInfo"},
{LANMAN_DOSPRINTQENUM, "DOSPrintQEnum"},
{LANMAN_DOSPRINTQGETINFO, "DOSPrintQGetInfo"},
{LANMAN_WPRINTQUEUEPAUSE, "WPrintQueuePause"},
{LANMAN_WPRINTQUEUERESUME, "WPrintQueueResume"},
{LANMAN_WPRINTJOBENUMERATE, "WPrintJobEnumerate"},
{LANMAN_WPRINTJOBGETINFO, "WPrintJobGetInfo"},
{LANMAN_RDOSPRINTJOBDEL, "RDOSPrintJobDel"},
{LANMAN_RDOSPRINTJOBPAUSE, "RDOSPrintJobPause"},
{LANMAN_RDOSPRINTJOBRESUME, "RDOSPrintJobResume"},
{LANMAN_WPRINTDESTENUM, "WPrintDestEnum"},
{LANMAN_WPRINTDESTGETINFO, "WPrintDestGetInfo"},
{LANMAN_NETREMOTETOD, "NetRemoteTOD"},
{LANMAN_WPRINTQUEUEPURGE, "WPrintQueuePurge"},
{LANMAN_NETSERVERENUM2, "NetServerEnum2"},
{LANMAN_WACCESSGETUSERPERMS, "WAccessGetUserPerms"},
{LANMAN_SETUSERPASSWORD, "SetUserPassword"},
{LANMAN_NETWKSTAUSERLOGON, "NetWkstaUserLogon"},
{LANMAN_NETWKSTAUSERLOGOFF, "NetWkstaUserLogoff"},
{LANMAN_PRINTJOBINFO, "PrintJobInfo"},
{LANMAN_WPRINTDRIVERENUM, "WPrintDriverEnum"},
{LANMAN_WPRINTQPROCENUM, "WPrintQProcEnum"},
{LANMAN_WPRINTPORTENUM, "WPrintPortEnum"},
{LANMAN_SAMOEMCHANGEPASSWORD, "SamOEMChangePassword"},
{0, NULL}
};
static void
dissect_response_data(tvbuff_t *tvb, packet_info *pinfo, int convert,
proto_tree *tree, struct smb_info *smb_info,
const struct lanman_desc *lanman, gboolean has_ent_count,
guint16 ent_count)
{
smb_transact_info_t *trp = smb_info->sip->extra_info;
const item_list_t *resp_data_list;
int offset, start_offset;
const item_t *resp_data;
proto_item *data_item;
proto_tree *data_tree;
proto_item *entry_item;
proto_tree *entry_tree;
guint i, j;
guint16 aux_count;
/*
* Find the item table for the matching request's detail level.
*/
for (resp_data_list = lanman->resp_data_list;
resp_data_list->level != -1; resp_data_list++) {
if (resp_data_list->level == trp->info_level)
break;
}
resp_data = resp_data_list->item_list;
offset = 0;
if (lanman->resp_data_item != NULL) {
/*
* Create a protocol tree item for the data.
*/
data_item = (*lanman->resp_data_item)(tvb,
pinfo, tree, offset);
data_tree = proto_item_add_subtree(data_item,
*lanman->ett_resp_data);
} else {
/*
* Just leave it at the top level.
*/
data_item = NULL;
data_tree = tree;
}
if (trp->data_descrip == NULL) {
/*
* This could happen if we only dissected
* part of the request to which this is a
* reply, e.g. if the request was split
* across TCP segments and we weren't doing
* TCP desegmentation, or if we had a snapshot
* length that was too short.
*
* We can't dissect the data; just show it
* as raw data.
*/
proto_tree_add_text(tree, tvb, offset,
tvb_length_remaining(tvb, offset),
"Data (no descriptor available)");
offset += tvb_length_remaining(tvb, offset);
} else {
/*
* If we have an entry count, show all the entries,
* with each one having a protocol tree item.
*
* Otherwise, we just show one returned item, with
* no protocol tree item.
*/
if (!has_ent_count)
ent_count = 1;
for (i = 0; i < ent_count; i++) {
start_offset = offset;
if (has_ent_count) {
/*
* Create a protocol tree item for the
* entry.
*/
entry_item =
(*lanman->resp_data_element_item)
(tvb, pinfo, data_tree, offset);
entry_tree = proto_item_add_subtree(
entry_item,
*lanman->ett_resp_data_element_item);
} else {
/*
* Just leave it at the current
* level.
*/
entry_item = NULL;
entry_tree = data_tree;
}
offset = dissect_transact_data(tvb, offset,
convert, pinfo, entry_tree,
trp->data_descrip, resp_data, &aux_count);
/* auxiliary data */
if (trp->aux_data_descrip != NULL) {
for (j = 0; j < aux_count; j++) {
offset = dissect_transact_data(
tvb, offset, convert,
pinfo, entry_tree,
trp->data_descrip,
lanman->resp_aux_data, NULL);
}
}
if (entry_item != NULL) {
/*
* Set the length of the protocol tree
* item for the entry.
*/
proto_item_set_len(entry_item,
offset - start_offset);
}
}
}
if (data_item != NULL) {
/*
* Set the length of the protocol tree item
* for the data.
*/
proto_item_set_len(data_item, offset);
}
}
static gboolean
dissect_pipe_lanman(tvbuff_t *pd_tvb, tvbuff_t *p_tvb, tvbuff_t *d_tvb,
packet_info *pinfo, proto_tree *parent_tree)
{
smb_info_t *smb_info = pinfo->private_data;
smb_transact_info_t *trp = smb_info->sip->extra_info;
int offset = 0, start_offset;
guint16 cmd;
guint16 status;
int convert;
const struct lanman_desc *lanman;
proto_item *item = NULL;
proto_tree *tree = NULL;
guint descriptor_len;
const gchar *param_descrip, *data_descrip, *aux_data_descrip = NULL;
gboolean has_data;
gboolean has_ent_count;
guint16 ent_count, aux_count;
guint i, j;
proto_item *data_item;
proto_tree *data_tree;
proto_item *entry_item;
proto_tree *entry_tree;
Pass, as the first tvbuff argument to "dissect_mailslot_smb()" and "dissect_pipe_smb()", a tvbuff containing the setup words and the pipe/mailslot pathname, as those are arguably the part of the packet that contains the "mailslot protocol" and the "pipe protocol", as opposed to the protocol running atop mailslots or pipes. Pass a setup tvbuff to "dissect_pipe_smb()" for it to pass on to the MSRPC-over-named-pipe dissector, and have the setup tvbuff passed to it and "dissect_mailslot_smb()" contain *only* the setup words; don't extract anything other than the setup words from it. Declare "register_proto_smb_mailslot()" in "packet-smb-mailslot.h" rather than "packet-smb.c", and declare "register_proto_smb_pipe()" in "packet-smb-pipe.h" rather than "packet-smb.c". Add a protocol for MSRPC-over-named-pipes. Move the stuff to handle the FID in the setup words of MSRPC-over-named-pipe transactions out of the SMB Transaction dissector into the MSRPC dissector. Add a routine to "packet-smb.c", callable from outside "packet-smb.c", to put an "smb.fid" field into the protocol tree, and to add ", FID: XXXX" to the Info column, for use by the MSRPC-over-named-pipe dissector; use it in the SMB dissector as well, in all the places where we put a FID into the protocol tree. Move the stuff to check whether the LANMAN protocol is enabled, and to set "pinfo->current_proto" to "LANMAN" if it is, into the LANMAN API-over-named-pipe dissector out of the named pipe protocol dissector. If we didn't dissect a Transaction request or reply as a named pipe or mailslot message, put any setup words, parameters, and data it has into the protocol tree as separate items. Don't put a "Response in" item into the protocol tree for an NT Cancel request, as there are no responses to NT Cancel requests. svn path=/trunk/; revision=4221
2001-11-19 10:06:42 +00:00
if (!proto_is_protocol_enabled(proto_smb_lanman))
return FALSE;
pinfo->current_proto = "LANMAN";
if (check_col(pinfo->fd, COL_PROTOCOL)) {
col_set_str(pinfo->fd, COL_PROTOCOL, "LANMAN");
}
if (parent_tree) {
item = proto_tree_add_item(parent_tree, proto_smb_lanman,
pd_tvb, 0, tvb_length(pd_tvb), FALSE);
tree = proto_item_add_subtree(item, ett_lanman);
}
if (smb_info->request) { /* this is a request */
/* function code */
cmd = tvb_get_letohs(p_tvb, offset);
if (check_col(pinfo->fd, COL_INFO)) {
col_add_fstr(pinfo->fd, COL_INFO, "%s Request", val_to_str(cmd, commands, "Unknown Command:0x%02x"));
}
proto_tree_add_uint(tree, hf_function_code, p_tvb, offset, 2,
cmd);
offset += 2;
/*
* If we haven't already done so, save the function code in
* the structure we were handed, so that it's available to
* the code parsing the reply, and initialize the detail
* level to -1, meaning "unknown".
*/
if (!pinfo->fd->flags.visited) {
trp->lanman_cmd = cmd;
trp->info_level = -1;
trp->param_descrip=NULL;
trp->data_descrip=NULL;
trp->aux_data_descrip=NULL;
}
/* parameter descriptor */
descriptor_len = tvb_strsize(p_tvb, offset);
proto_tree_add_item(tree, hf_param_desc, p_tvb, offset,
descriptor_len, TRUE);
param_descrip = tvb_get_ptr(p_tvb, offset, descriptor_len);
if (!pinfo->fd->flags.visited) {
/*
* Save the parameter descriptor for future use.
*/
g_assert(trp->param_descrip == NULL);
trp->param_descrip = g_strdup(param_descrip);
}
offset += descriptor_len;
/* return descriptor */
descriptor_len = tvb_strsize(p_tvb, offset);
proto_tree_add_item(tree, hf_return_desc, p_tvb, offset,
descriptor_len, TRUE);
data_descrip = tvb_get_ptr(p_tvb, offset, descriptor_len);
if (!pinfo->fd->flags.visited) {
/*
* Save the return descriptor for future use.
*/
g_assert(trp->data_descrip == NULL);
trp->data_descrip = g_strdup(data_descrip);
}
offset += descriptor_len;
lanman = find_lanman(cmd);
/* request parameters */
start_offset = offset;
offset = dissect_request_parameters(p_tvb, offset, pinfo, tree,
param_descrip, lanman->req, &has_data);
/* auxiliary data descriptor */
if (tvb_reported_length_remaining(p_tvb, offset) > 0){
/*
* There are more parameters left, so the next
* item is the auxiliary data descriptor.
*/
descriptor_len = tvb_strsize(p_tvb, offset);
proto_tree_add_item(tree, hf_return_desc, p_tvb, offset,
descriptor_len, TRUE);
aux_data_descrip = tvb_get_ptr(p_tvb, offset, descriptor_len);
if (!pinfo->fd->flags.visited) {
/*
* Save the auxiliary data descriptor for
* future use.
*/
g_assert(trp->aux_data_descrip == NULL);
trp->aux_data_descrip =
g_strdup(aux_data_descrip);
}
offset += descriptor_len;
}
/* reset offset, we now start dissecting the data area */
offset = 0;
if (has_data && d_tvb && tvb_reported_length(d_tvb) != 0) {
/*
* There's a send buffer item in the descriptor
* string, and the data count in the transaction
* is non-zero, so there's data to dissect.
*/
if (lanman->req_data_item != NULL) {
/*
* Create a protocol tree item for the data.
*/
data_item = (*lanman->req_data_item)(d_tvb,
pinfo, tree, offset);
data_tree = proto_item_add_subtree(data_item,
*lanman->ett_req_data);
} else {
/*
* Just leave it at the top level.
*/
data_item = NULL;
data_tree = tree;
}
/* data */
offset = dissect_transact_data(d_tvb, offset, -1,
pinfo, data_tree, data_descrip, lanman->req_data,
&aux_count); /* XXX - what about strings? */
/* auxiliary data */
if (aux_data_descrip != NULL) {
for (i = 0; i < aux_count; i++) {
offset = dissect_transact_data(d_tvb,
offset, -1, pinfo, data_tree,
aux_data_descrip,
lanman->req_aux_data, NULL);
}
}
if (data_item != NULL) {
/*
* Set the length of the protocol tree item
* for the data.
*/
proto_item_set_len(data_item, offset);
}
}
} else {
/*
* This is a response.
* Have we seen the request to which it's a response?
*/
if (trp == NULL)
return FALSE; /* no - can't dissect it */
/* ok we have seen this one before */
/* if it looks like an interim response, update COL_INFO and return */
if( ( (p_tvb==NULL) || (tvb_reported_length(p_tvb)==0) )
&& ( (d_tvb==NULL) || (tvb_reported_length(d_tvb)==0) ) ){
/* command */
if (check_col(pinfo->fd, COL_INFO)) {
col_add_fstr(pinfo->fd, COL_INFO, "%s Interim Response",
val_to_str(trp->lanman_cmd, commands, "Unknown Command (0x%02x)"));
}
proto_tree_add_uint(tree, hf_function_code, p_tvb, 0, 0, trp->lanman_cmd);
return TRUE;
}
/* command */
if (check_col(pinfo->fd, COL_INFO)) {
col_add_fstr(pinfo->fd, COL_INFO, "%s Response",
val_to_str(trp->lanman_cmd, commands, "Unknown Command (0x%02x)"));
}
proto_tree_add_uint(tree, hf_function_code, p_tvb, 0, 0,
trp->lanman_cmd);
lanman = find_lanman(trp->lanman_cmd);
/* response parameters */
/* status */
status = tvb_get_letohs(p_tvb, offset);
proto_tree_add_uint(tree, hf_status, p_tvb, offset, 2, status);
offset += 2;
/* convert */
convert = tvb_get_letohs(p_tvb, offset);
proto_tree_add_uint(tree, hf_convert, p_tvb, offset, 2, convert);
offset += 2;
/* rest of the parameters */
offset = dissect_response_parameters(p_tvb, offset, pinfo, tree,
trp->param_descrip, lanman->resp, &has_data,
&has_ent_count, &ent_count);
/* reset offset, we now start dissecting the data area */
offset = 0;
/* data */
if (has_data && d_tvb && tvb_reported_length(d_tvb) > 0) {
/*
* There's a receive buffer item in the descriptor
* string, and the data count in the transaction
* is non-zero, so there's data to dissect.
*/
dissect_response_data(d_tvb, pinfo, convert, tree,
smb_info, lanman, has_ent_count, ent_count);
}
}
return TRUE;
}
void
proto_register_pipe_lanman(void)
{
static hf_register_info hf[] = {
{ &hf_function_code,
{ "Function Code", "lanman.function_code", FT_UINT16, BASE_DEC,
VALS(commands), 0, "LANMAN Function Code/Command", HFILL }},
{ &hf_param_desc,
{ "Parameter Descriptor", "lanman.param_desc", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Parameter Descriptor", HFILL }},
{ &hf_return_desc,
{ "Return Descriptor", "lanman.ret_desc", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Return Descriptor", HFILL }},
{ &hf_aux_data_desc,
{ "Auxiliary Data Descriptor", "lanman.aux_data_desc", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Auxiliary Data Descriptor", HFILL }},
{ &hf_detail_level,
{ "Detail Level", "lanman.level", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Detail Level", HFILL }},
{ &hf_recv_buf_len,
{ "Receive Buffer Length", "lanman.recv_buf_len", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Receive Buffer Length", HFILL }},
{ &hf_send_buf_len,
{ "Send Buffer Length", "lanman.send_buf_len", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Send Buffer Length", HFILL }},
{ &hf_continuation_from,
{ "Continuation from message in frame", "lanman.continuation_from", FT_UINT32, BASE_DEC,
NULL, 0, "This is a LANMAN continuation from the message in the frame in question", HFILL }},
{ &hf_status,
{ "Status", "lanman.status", FT_UINT16, BASE_DEC,
VALS(status_vals), 0, "LANMAN Return status", HFILL }},
{ &hf_convert,
{ "Convert", "lanman.convert", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Convert", HFILL }},
{ &hf_ecount,
{ "Entry Count", "lanman.entry_count", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Number of Entries", HFILL }},
{ &hf_acount,
{ "Available Entries", "lanman.available_count", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Number of Available Entries", HFILL }},
{ &hf_share_name,
{ "Share Name", "lanman.share.name", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Name of Share", HFILL }},
{ &hf_share_type,
{ "Share Type", "lanman.share.type", FT_UINT16, BASE_DEC,
VALS(share_type_vals), 0, "LANMAN Type of Share", HFILL }},
{ &hf_share_comment,
{ "Share Comment", "lanman.share.comment", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Share Comment", HFILL }},
{ &hf_share_permissions,
{ "Share Permissions", "lanman.share.permissions", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Permissions on share", HFILL }},
{ &hf_share_max_uses,
{ "Share Max Uses", "lanman.share.max_uses", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Max connections allowed to share", HFILL }},
{ &hf_share_current_uses,
{ "Share Current Uses", "lanman.share.current_uses", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Current connections to share", HFILL }},
{ &hf_share_path,
{ "Share Path", "lanman.share.path", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Share Path", HFILL }},
{ &hf_share_password,
{ "Share Password", "lanman.share.password", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Share Password", HFILL }},
{ &hf_server_name,
{ "Server Name", "lanman.server.name", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Name of Server", HFILL }},
{ &hf_server_major,
{ "Major Version", "lanman.server.major", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Server Major Version", HFILL }},
{ &hf_server_minor,
{ "Minor Version", "lanman.server.minor", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Server Minor Version", HFILL }},
{ &hf_server_comment,
{ "Server Comment", "lanman.server.comment", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Server Comment", HFILL }},
{ &hf_abytes,
{ "Available Bytes", "lanman.available_bytes", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Number of Available Bytes", HFILL }},
{ &hf_current_time,
{ "Current Date/Time", "lanman.current_time", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Current date and time, in seconds since 00:00:00, January 1, 1970", HFILL }},
{ &hf_msecs,
{ "Milliseconds", "lanman.msecs", FT_UINT32, BASE_DEC,
NULL, 0, "LANMAN Milliseconds since arbitrary time in the past (typically boot time)", HFILL }},
{ &hf_hour,
{ "Hour", "lanman.hour", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Current hour", HFILL }},
{ &hf_minute,
{ "Minute", "lanman.minute", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Current minute", HFILL }},
{ &hf_second,
{ "Second", "lanman.second", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Current second", HFILL }},
{ &hf_hundredths,
{ "Hundredths of a second", "lanman.hundredths", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Current hundredths of a second", HFILL }},
{ &hf_tzoffset,
{ "Time Zone Offset", "lanman.tzoffset", FT_INT16, BASE_DEC,
NULL, 0, "LANMAN Offset of time zone from GMT, in minutes", HFILL }},
{ &hf_timeinterval,
{ "Time Interval", "lanman.timeinterval", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN .0001 second units per clock tick", HFILL }},
{ &hf_day,
{ "Day", "lanman.day", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Current day", HFILL }},
{ &hf_month,
{ "Month", "lanman.month", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Current month", HFILL }},
{ &hf_year,
{ "Year", "lanman.year", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Current year", HFILL }},
{ &hf_weekday,
{ "Weekday", "lanman.weekday", FT_UINT8, BASE_DEC,
VALS(weekday_vals), 0, "LANMAN Current day of the week", HFILL }},
{ &hf_enumeration_domain,
{ "Enumeration Domain", "lanman.enumeration_domain", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Domain in which to enumerate servers", HFILL }},
{ &hf_computer_name,
{ "Computer Name", "lanman.computer_name", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Computer Name", HFILL }},
{ &hf_user_name,
{ "User Name", "lanman.user_name", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN User Name", HFILL }},
{ &hf_workstation_domain,
{ "Workstation Domain", "lanman.workstation_domain", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Workstation Domain", HFILL }},
{ &hf_workstation_major,
{ "Workstation Major Version", "lanman.workstation_major", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Workstation Major Version", HFILL }},
{ &hf_workstation_minor,
{ "Workstation Minor Version", "lanman.workstation_minor", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Workstation Minor Version", HFILL }},
{ &hf_logon_domain,
{ "Logon Domain", "lanman.logon_domain", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Logon Domain", HFILL }},
{ &hf_other_domains,
{ "Other Domains", "lanman.other_domains", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Other Domains", HFILL }},
{ &hf_password,
{ "Password", "lanman.password", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Password", HFILL }},
{ &hf_workstation_name,
{ "Workstation Name", "lanman.workstation_name", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Workstation Name", HFILL }},
{ &hf_ustruct_size,
{ "Length of UStruct", "lanman.ustruct_size", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN UStruct Length", HFILL }},
{ &hf_logon_code,
{ "Logon Code", "lanman.logon_code", FT_UINT16, BASE_DEC,
VALS(status_vals), 0, "LANMAN Logon Code", HFILL }},
{ &hf_privilege_level,
{ "Privilege Level", "lanman.privilege_level", FT_UINT16, BASE_DEC,
VALS(privilege_vals), 0, "LANMAN Privilege Level", HFILL }},
{ &hf_operator_privileges,
{ "Operator Privileges", "lanman.operator_privileges", FT_UINT32, BASE_DEC,
VALS(op_privilege_vals), 0, "LANMAN Operator Privileges", HFILL }},
{ &hf_num_logons,
{ "Number of Logons", "lanman.num_logons", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Number of Logons", HFILL }},
{ &hf_bad_pw_count,
{ "Bad Password Count", "lanman.bad_pw_count", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Number of incorrect passwords entered since last successful login", HFILL }},
{ &hf_last_logon,
{ "Last Logon Date/Time", "lanman.last_logon", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Date and time of last logon", HFILL }},
{ &hf_last_logoff,
{ "Last Logoff Date/Time", "lanman.last_logoff", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Date and time of last logoff", HFILL }},
{ &hf_logoff_time,
{ "Logoff Date/Time", "lanman.logoff_time", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Date and time when user should log off", HFILL }},
{ &hf_kickoff_time,
{ "Kickoff Date/Time", "lanman.kickoff_time", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Date and time when user will be logged off", HFILL }},
{ &hf_password_age,
{ "Password Age", "lanman.password_age", FT_RELATIVE_TIME, BASE_NONE,
NULL, 0, "LANMAN Time since user last changed his/her password", HFILL }},
{ &hf_password_can_change,
{ "Password Can Change", "lanman.password_can_change", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Date and time when user can change their password", HFILL }},
{ &hf_password_must_change,
{ "Password Must Change", "lanman.password_must_change", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Date and time when user must change their password", HFILL }},
{ &hf_script_path,
{ "Script Path", "lanman.script_path", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Pathname of user's logon script", HFILL }},
{ &hf_logoff_code,
{ "Logoff Code", "lanman.logoff_code", FT_UINT16, BASE_DEC,
VALS(status_vals), 0, "LANMAN Logoff Code", HFILL }},
{ &hf_duration,
{ "Duration of Session", "lanman.duration", FT_RELATIVE_TIME, BASE_NONE,
NULL, 0, "LANMAN Number of seconds the user was logged on", HFILL }},
{ &hf_user_comment,
{ "User Comment", "lanman.user_comment", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN User Comment", HFILL }},
{ &hf_full_name,
{ "Full Name", "lanman.full_name", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Full Name", HFILL }},
{ &hf_homedir,
{ "Home Directory", "lanman.homedir", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Home Directory", HFILL }},
{ &hf_parameters,
{ "Parameters", "lanman.parameters", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Parameters", HFILL }},
{ &hf_logon_server,
{ "Logon Server", "lanman.logon_server", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Logon Server", HFILL }},
/* XXX - we should have a value_string table for this */
{ &hf_country_code,
{ "Country Code", "lanman.country_code", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Country Code", HFILL }},
{ &hf_workstations,
{ "Workstations", "lanman.workstations", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Workstations", HFILL }},
{ &hf_max_storage,
{ "Max Storage", "lanman.max_storage", FT_UINT32, BASE_DEC,
NULL, 0, "LANMAN Max Storage", HFILL }},
{ &hf_units_per_week,
{ "Units Per Week", "lanman.units_per_week", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Units Per Week", HFILL }},
{ &hf_logon_hours,
{ "Logon Hours", "lanman.logon_hours", FT_BYTES, BASE_NONE,
NULL, 0, "LANMAN Logon Hours", HFILL }},
/* XXX - we should have a value_string table for this */
{ &hf_code_page,
{ "Code Page", "lanman.code_page", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Code Page", HFILL }},
{ &hf_new_password,
{ "New Password", "lanman.new_password", FT_BYTES, BASE_HEX,
NULL, 0, "LANMAN New Password (encrypted)", HFILL }},
{ &hf_old_password,
{ "Old Password", "lanman.old_password", FT_BYTES, BASE_HEX,
NULL, 0, "LANMAN Old Password (encrypted)", HFILL }},
{ &hf_reserved,
{ "Reserved", "lanman.reserved", FT_UINT32, BASE_HEX,
NULL, 0, "LANMAN Reserved", HFILL }},
};
static gint *ett[] = {
&ett_lanman,
&ett_lanman_servers,
&ett_lanman_server,
&ett_lanman_shares,
&ett_lanman_share,
};
proto_smb_lanman = proto_register_protocol(
"Microsoft Windows Lanman Remote API Protocol", "LANMAN", "lanman");
proto_register_field_array(proto_smb_lanman, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
static heur_dissector_list_t msrpc_heur_subdissector_list;
static gboolean
dissect_pipe_msrpc(tvbuff_t *d_tvb, packet_info *pinfo, proto_tree *parent_tree,
guint16 fid)
{
dcerpc_private_info dcerpc_priv;
smb_info_t *smb_priv = (smb_info_t *)pinfo->private_data;
gboolean result;
dcerpc_priv.transport_type = DCERPC_TRANSPORT_SMB;
dcerpc_priv.data.smb.fid = fid;
pinfo->private_data = &dcerpc_priv;
result = dissector_try_heuristic(msrpc_heur_subdissector_list, d_tvb,
pinfo, parent_tree);
pinfo->private_data = smb_priv;
if (!result)
dissect_data(d_tvb, 0, pinfo, parent_tree);
return TRUE;
}
void
proto_register_pipe_msrpc(void)
{
register_heur_dissector_list("msrpc", &msrpc_heur_subdissector_list);
}
#define CALL_NAMED_PIPE 0x54
#define WAIT_NAMED_PIPE 0x53
#define PEEK_NAMED_PIPE 0x23
#define Q_NM_P_HAND_STATE 0x21
#define SET_NM_P_HAND_STATE 0x01
#define Q_NM_PIPE_INFO 0x22
#define TRANSACT_NM_PIPE 0x26
#define RAW_READ_NM_PIPE 0x11
#define RAW_WRITE_NM_PIPE 0x31
static const value_string functions[] = {
{CALL_NAMED_PIPE, "CallNamedPipe"},
{WAIT_NAMED_PIPE, "WaitNamedPipe"},
{PEEK_NAMED_PIPE, "PeekNamedPipe"},
{Q_NM_P_HAND_STATE, "QNmPHandState"},
{SET_NM_P_HAND_STATE, "SetNmPHandState"},
{Q_NM_PIPE_INFO, "QNmPipeInfo"},
{TRANSACT_NM_PIPE, "TransactNmPipe"},
{RAW_READ_NM_PIPE, "RawReadNmPipe"},
{RAW_WRITE_NM_PIPE, "RawWriteNmPipe"},
{0, NULL}
};
static const value_string pipe_status[] = {
{1, "Disconnected by server"},
{2, "Listening"},
{3, "Connection to server is OK"},
{4, "Server end of pipe is closed"},
{0, NULL}
};
#define PIPE_LANMAN 1
#define PIPE_MSRPC 2
/* decode the SMB pipe protocol
for requests
pipe is the name of the pipe, e.g. LANMAN
smb_info->trans_subcmd is set to the symbolic constant matching the mailslot name
for responses
pipe is NULL
smb_info->trans_subcmd gives us which pipe this response is for
*/
gboolean
dissect_pipe_smb(tvbuff_t *sp_tvb, tvbuff_t *s_tvb, tvbuff_t *pd_tvb,
tvbuff_t *p_tvb, tvbuff_t *d_tvb, const char *pipe,
packet_info *pinfo, proto_tree *tree)
{
smb_info_t *smb_info;
smb_transact_info_t *tri;
proto_item *pipe_item = NULL;
proto_tree *pipe_tree = NULL;
int offset;
int function;
int fid = -1;
int len;
guint16 info_level;
if (!proto_is_protocol_enabled(proto_smb_pipe))
return FALSE;
pinfo->current_proto = "SMB Pipe";
smb_info = pinfo->private_data;
/*
* Set the columns.
*/
if (check_col(pinfo->fd, COL_PROTOCOL)) {
col_set_str(pinfo->fd, COL_PROTOCOL, "SMB Pipe");
}
if (check_col(pinfo->fd, COL_INFO)) {
col_set_str(pinfo->fd, COL_INFO,
smb_info->request ? "Request" : "Response");
}
if (smb_info->sip != NULL)
tri = smb_info->sip->extra_info;
else
tri = NULL;
/*
* Set up a subtree for the pipe data, if there is any.
*/
if (s_tvb != NULL || tvb_length(sp_tvb) != 0 ||
(tri != NULL && tri->function != -1)) {
if (tree) {
pipe_item = proto_tree_add_item(tree, proto_smb_pipe,
sp_tvb, 0, tvb_length(sp_tvb), FALSE);
pipe_tree = proto_item_add_subtree(pipe_item, ett_smb_pipe);
}
}
offset = 0;
/*
* Do we have any setup words at all?
*/
if (s_tvb != NULL && tvb_length(s_tvb) != 0) {
/*
* Yes. The first of them is the function.
*/
function = tvb_get_letohs(s_tvb, offset);
proto_tree_add_uint(pipe_tree, hf_pipe_function, s_tvb,
offset, 2, function);
offset += 2;
if (check_col(pinfo->fd, COL_INFO)) {
col_add_fstr(pinfo->fd, COL_INFO, "%s %s",
val_to_str(function, functions, "Unknown function (0x%04x)"),
smb_info->request ? "Request" : "Response");
}
if (tri != NULL)
tri->function = function;
/*
* The second of them depends on the function.
*/
switch (function) {
case CALL_NAMED_PIPE:
case WAIT_NAMED_PIPE:
/*
* It's a priority.
*/
proto_tree_add_item(pipe_tree, hf_pipe_priority, s_tvb,
offset, 2, TRUE);
break;
case PEEK_NAMED_PIPE:
case Q_NM_P_HAND_STATE:
case SET_NM_P_HAND_STATE:
case Q_NM_PIPE_INFO:
case TRANSACT_NM_PIPE:
case RAW_READ_NM_PIPE:
case RAW_WRITE_NM_PIPE:
/*
* It's a FID.
*/
fid = tvb_get_letohs(s_tvb, 2);
add_fid(s_tvb, pinfo, pipe_tree, offset, 2, fid);
if (tri != NULL)
tri->fid = fid;
break;
default:
/*
* It's something unknown.
* XXX - put it into the tree?
*/
break;
}
offset += 2;
} else {
/*
* This is either a pipe transaction with no setup
* information or a response.
*
* In the former case, there is no function or FID.
*
* In the latter case, we could get that information from
* the matching request, if we saw it. (XXX - do that.)
*/
if (tri != NULL && tri->function != -1) {
function = tri->function;
proto_tree_add_uint(pipe_tree, hf_pipe_function, sp_tvb,
0, 0, function);
if (check_col(pinfo->fd, COL_INFO)) {
col_add_fstr(pinfo->fd, COL_INFO, "%s %s",
val_to_str(function, functions, "Unknown function (0x%04x)"),
smb_info->request ? "Request" : "Response");
}
fid = tri->fid;
if (fid != -1)
add_fid(sp_tvb, pinfo, pipe_tree, 0, 0, fid);
} else {
function = -1;
fid = -1;
}
}
/*
* XXX - put the byte count and the pipe name into the tree as well;
* that requires us to fetch a possibly-Unicode string.
*/
if(smb_info->request){
if(strncmp(pipe,"LANMAN",6) == 0){
tri->trans_subcmd=PIPE_LANMAN;
} else {
/* assume it is MSRPC*/
tri->trans_subcmd=PIPE_MSRPC;
}
}
if (tri == NULL) {
/*
* We don't know what type of pipe transaction this
* was, so indicate that we didn't dissect it.
*/
return FALSE;
}
switch (function) {
case CALL_NAMED_PIPE:
case TRANSACT_NM_PIPE:
switch(tri->trans_subcmd){
case PIPE_LANMAN:
return dissect_pipe_lanman(pd_tvb, p_tvb, d_tvb, pinfo,
tree);
break;
case PIPE_MSRPC:
/*
* Only dissect this if we know the FID.
*/
if (fid != -1) {
return dissect_pipe_msrpc(d_tvb, pinfo, tree,
fid);
}
break;
}
break;
case -1:
/*
* We don't know the function; we dissect only LANMAN
* pipe messages, not RPC pipe messages, in that case.
*/
switch(tri->trans_subcmd){
case PIPE_LANMAN:
return dissect_pipe_lanman(pd_tvb, p_tvb, d_tvb, pinfo,
tree);
break;
}
break;
case WAIT_NAMED_PIPE:
break;
case PEEK_NAMED_PIPE:
/*
* Request contains no parameters or data.
*/
if (!smb_info->request) {
offset = 0;
proto_tree_add_item(pipe_tree, hf_pipe_peek_available,
p_tvb, offset, 2, TRUE);
offset += 2;
proto_tree_add_item(pipe_tree, hf_pipe_peek_remaining,
p_tvb, offset, 2, TRUE);
offset += 2;
proto_tree_add_item(pipe_tree, hf_pipe_peek_status,
p_tvb, offset, 2, TRUE);
offset += 2;
}
break;
case Q_NM_P_HAND_STATE:
/*
* Request contains no parameters or data.
*/
if (!smb_info->request) {
offset = dissect_ipc_state(p_tvb, pinfo, pipe_tree, 0,
FALSE);
}
break;
case SET_NM_P_HAND_STATE:
/*
* Response contains no parameters or data.
*/
if (smb_info->request) {
offset = dissect_ipc_state(p_tvb, pinfo, pipe_tree, 0,
TRUE);
}
break;
case Q_NM_PIPE_INFO:
offset = 0;
if (smb_info->request) {
/*
* Request contains an information level.
*/
info_level = tvb_get_letohs(p_tvb, offset);
proto_tree_add_uint(pipe_tree, hf_pipe_getinfo_info_level,
p_tvb, offset, 2, info_level);
offset += 2;
tri->info_level = info_level;
} else {
guint8 pipe_namelen;
switch (tri->info_level) {
case 1:
proto_tree_add_item(pipe_tree,
hf_pipe_getinfo_output_buffer_size,
d_tvb, offset, 2, TRUE);
offset += 2;
proto_tree_add_item(pipe_tree,
hf_pipe_getinfo_input_buffer_size,
d_tvb, offset, 2, TRUE);
offset += 2;
proto_tree_add_item(pipe_tree,
hf_pipe_getinfo_maximum_instances,
d_tvb, offset, 1, TRUE);
offset += 1;
proto_tree_add_item(pipe_tree,
hf_pipe_getinfo_current_instances,
d_tvb, offset, 1, TRUE);
offset += 1;
pipe_namelen = tvb_get_guint8(d_tvb, offset);
proto_tree_add_uint(pipe_tree,
hf_pipe_getinfo_pipe_name_length,
d_tvb, offset, 1, pipe_namelen);
offset += 1;
/* XXX - can this be Unicode? */
proto_tree_add_item(pipe_tree,
hf_pipe_getinfo_pipe_name,
d_tvb, offset, pipe_namelen, TRUE);
break;
}
}
break;
case RAW_READ_NM_PIPE:
/*
* Request contains no parameters or data.
*/
if (!smb_info->request) {
offset = dissect_file_data(d_tvb, pinfo, pipe_tree, 0,
tvb_reported_length(d_tvb),
tvb_reported_length(d_tvb));
}
break;
case RAW_WRITE_NM_PIPE:
offset = 0;
if (smb_info->request) {
offset = dissect_file_data(d_tvb, pinfo, pipe_tree,
offset, tvb_reported_length(d_tvb),
tvb_reported_length(d_tvb));
} else {
proto_tree_add_item(pipe_tree,
hf_pipe_write_raw_bytes_written,
p_tvb, offset, 2, TRUE);
offset += 2;
}
break;
}
return TRUE;
}
void
proto_register_smb_pipe(void)
{
static hf_register_info hf[] = {
{ &hf_pipe_function,
{ "Function", "pipe.function", FT_UINT16, BASE_HEX,
VALS(functions), 0, "SMB Pipe Function Code", HFILL }},
{ &hf_pipe_priority,
{ "Priority", "pipe.priority", FT_UINT16, BASE_DEC,
NULL, 0, "SMB Pipe Priority", HFILL }},
{ &hf_pipe_peek_available,
{ "Available Bytes", "pipe.peek.available_bytes", FT_UINT16, BASE_DEC,
NULL, 0, "Total number of bytes available to be read from the pipe", HFILL }},
{ &hf_pipe_peek_remaining,
{ "Bytes Remaining", "pipe.peek.remaining_bytes", FT_UINT16, BASE_DEC,
NULL, 0, "Total number of bytes remaining in the message at the head of the pipe", HFILL }},
{ &hf_pipe_peek_status,
{ "Pipe Status", "pipe.peek.status", FT_UINT16, BASE_DEC,
VALS(pipe_status), 0, "Pipe status", HFILL }},
{ &hf_pipe_getinfo_info_level,
{ "Information Level", "pipe.getinfo.info_level", FT_UINT16, BASE_DEC,
NULL, 0, "Information level of information to return", HFILL }},
{ &hf_pipe_getinfo_output_buffer_size,
{ "Output Buffer Size", "pipe.getinfo.output_buffer_size", FT_UINT16, BASE_DEC,
NULL, 0, "Actual size of buffer for outgoing (server) I/O", HFILL }},
{ &hf_pipe_getinfo_input_buffer_size,
{ "Input Buffer Size", "pipe.getinfo.input_buffer_size", FT_UINT16, BASE_DEC,
NULL, 0, "Actual size of buffer for incoming (client) I/O", HFILL }},
{ &hf_pipe_getinfo_maximum_instances,
{ "Maximum Instances", "pipe.getinfo.maximum_instances", FT_UINT8, BASE_DEC,
NULL, 0, "Maximum allowed number of instances", HFILL }},
{ &hf_pipe_getinfo_current_instances,
{ "Current Instances", "pipe.getinfo.current_instances", FT_UINT8, BASE_DEC,
NULL, 0, "Current number of instances", HFILL }},
{ &hf_pipe_getinfo_pipe_name_length,
{ "Pipe Name Length", "pipe.getinfo.pipe_name_length", FT_UINT8, BASE_DEC,
NULL, 0, "Length of pipe name", HFILL }},
{ &hf_pipe_getinfo_pipe_name,
{ "Pipe Name", "pipe.getinfo.pipe_name", FT_STRING, BASE_NONE,
NULL, 0, "Name of pipe", HFILL }},
{ &hf_pipe_write_raw_bytes_written,
{ "Bytes Written", "pipe.write_raw.bytes_written", FT_UINT16, BASE_DEC,
NULL, 0, "Number of bytes written to the pipe", HFILL }},
};
static gint *ett[] = {
&ett_smb_pipe,
};
proto_smb_pipe = proto_register_protocol(
"SMB Pipe Protocol", "SMB Pipe", "pipe");
proto_register_field_array(proto_smb_pipe, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}