summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-ms-nns.c
blob: a364dc5bb52e4b2851d42c5d95dec51661d53aeb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/* packet-ms-nns.c
 * Routines for .NET NegotiateStream Protocol (MS-NNS) dissection
 * Copyright 2020, Uli Heilmeier <uh@heilmeier.eu>
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wieshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

/*
 * Basic dissector for .NET NegotiateStream Protocol based on protocol reference found at
 * https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NNS/%5bMS-NNS%5d.pdf
 */

#include <config.h>

#include <epan/packet.h>
#include <epan/conversation.h>

/* Prototypes */
void proto_reg_handoff_nns(void);
void proto_register_nns(void);

static dissector_handle_t gssapi_handle;


#define MS_NNS_MESSAGE_HANDSHAKE_DONE      20
#define MS_NNS_MESSAGE_HANDSHAKE_ERROR     21
#define MS_NNS_MESSAGE_HANDSHAKE_PROGRESS  22

static const value_string nns_message_id_vals[] = {
    { MS_NNS_MESSAGE_HANDSHAKE_DONE, "Handshake Done" },
    { MS_NNS_MESSAGE_HANDSHAKE_ERROR, "Handshake Error" },
    { MS_NNS_MESSAGE_HANDSHAKE_PROGRESS, "Handshake In Progress" },
    { 0, NULL}
};

struct nns_session_state {
    guint32   handshake_done;
    gboolean  first_handshake_done;
};

static int proto_nns = -1;
static int hf_nns_message_id = -1;
static int hf_nns_major_version = -1;
static int hf_nns_minor_version = -1;
static int hf_nns_auth_payload_size = -1;
static int hf_nns_auth_payload = -1;
static int hf_nns_payload_size = -1;
static int hf_nns_payload = -1;

static gint ett_nns = -1;
static gint ett_nns_payload = -1;

#define MS_NNS_MIN_LENGTH 4

static int
dissect_nns(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    proto_item      *ti, *pti;
    proto_tree      *nns_tree, *payload_tree;
    guint           offset = 0;
    guint32         message_id;
    guint32         payload_size;
    conversation_t  *conversation;
    tvbuff_t        *nt_tvb;
    gint            remaining;
    struct nns_session_state *session_state;

    if (tvb_reported_length(tvb) < MS_NNS_MIN_LENGTH)
        return 0;

    conversation = find_or_create_conversation(pinfo);

    session_state = (struct nns_session_state *)conversation_get_proto_data(conversation, proto_nns);
    if (!session_state) {
        session_state = wmem_new0(wmem_file_scope(), struct nns_session_state);
        conversation_add_proto_data(conversation, proto_nns, session_state);
    }

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "MS-NNS");
    col_clear(pinfo->cinfo, COL_INFO);

    ti = proto_tree_add_item(tree, proto_nns, tvb, 0, -1, ENC_NA);

    nns_tree = proto_item_add_subtree(ti, ett_nns);

    /* As it is unknown if there is a one-way Handshake Done or a two-way Handshake Done we check the first frame
     * after the first Handshake Done if it looks like a Handshake Done message (0x140100). */

    if ( session_state->handshake_done && session_state->handshake_done < pinfo->num &&
            !(session_state->first_handshake_done && tvb_get_ntoh24(tvb, offset) == 0x140100)) {
        proto_tree_add_item_ret_uint(nns_tree, hf_nns_payload_size, tvb, offset, 4, ENC_LITTLE_ENDIAN, &payload_size);
        offset += 4;
        col_append_str(pinfo->cinfo, COL_INFO, "Data");
        if ( payload_size > 0) {
            remaining = tvb_reported_length_remaining(tvb, offset);
            if ((guint32) remaining < payload_size) {
                pinfo->desegment_offset = offset - 4;
                pinfo->desegment_len = payload_size - remaining;
                return (offset - 4);
            }
            proto_tree_add_item(nns_tree, hf_nns_payload, tvb, offset, payload_size, ENC_NA);
            offset += payload_size;
            session_state->first_handshake_done = FALSE;
        }
    }
    else {
        proto_tree_add_item_ret_uint(nns_tree, hf_nns_message_id, tvb, offset, 1, ENC_NA, &message_id);
        offset += 1;
        col_append_sep_fstr(pinfo->cinfo, COL_INFO, ", ", "%s", val_to_str_const(message_id, nns_message_id_vals, "Unknown Record"));
        proto_tree_add_item(nns_tree, hf_nns_major_version, tvb, offset, 1, ENC_NA);
        offset += 1;
        proto_tree_add_item(nns_tree, hf_nns_minor_version, tvb, offset, 1, ENC_NA);
        offset += 1;
        proto_tree_add_item_ret_uint(nns_tree, hf_nns_auth_payload_size, tvb, offset, 2, ENC_BIG_ENDIAN, &payload_size);
        offset += 2;
        if ( payload_size > 0) {
            if ((guint32) tvb_reported_length_remaining(tvb, offset) < payload_size) {
                pinfo->desegment_offset = offset - 5;
                pinfo->desegment_len = payload_size;
                return (offset - 5);
            }
            pti = proto_tree_add_item(nns_tree, hf_nns_auth_payload, tvb, offset, payload_size, ENC_NA);
            if (message_id == MS_NNS_MESSAGE_HANDSHAKE_DONE || message_id == MS_NNS_MESSAGE_HANDSHAKE_PROGRESS) {
                nt_tvb = tvb_new_subset_length(tvb, offset, payload_size);
                payload_tree = proto_item_add_subtree(pti, ett_nns_payload);
                call_dissector(gssapi_handle, nt_tvb, pinfo, payload_tree);
            }
            offset += payload_size;
        }
        if ( message_id == MS_NNS_MESSAGE_HANDSHAKE_DONE) {
            session_state->handshake_done = pinfo->num;
            session_state->first_handshake_done = (session_state->first_handshake_done ? FALSE : TRUE);
        }
    }
    return offset;
}

void proto_register_nns(void)
{
    static hf_register_info hf[] = {
        { &hf_nns_message_id,
          { "MessageID", "ms-nns.message_id",
            FT_UINT8, BASE_DEC, VALS(nns_message_id_vals), 0x0,
            NULL, HFILL }
        },
        { &hf_nns_major_version,
          { "Major Version", "ms-nns.major_version",
            FT_UINT8, BASE_DEC, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_nns_minor_version,
          { "Minor Version", "ms-nns.minor_version",
            FT_UINT8, BASE_DEC, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_nns_auth_payload_size,
          { "Auth Payload Size", "ms-nns.auth_payload_size",
            FT_UINT16, BASE_DEC, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_nns_auth_payload,
          { "Auth Payload", "ms-nns.known_encoding",
            FT_BYTES, BASE_NONE, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_nns_payload_size,
          { "Payload Size", "ms-nns.payload_size",
            FT_UINT32, BASE_DEC, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_nns_payload,
          { "Payload", "ms-nns.payload",
            FT_BYTES, BASE_NONE, NULL, 0x0,
            NULL, HFILL }
        }
    };

    static gint *ett[] = {
        &ett_nns,
        &ett_nns_payload
    };

    proto_nns = proto_register_protocol(".NET NegotiateStream Protocol", "MS-NNS", "ms-nns");
    proto_register_field_array(proto_nns, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
    register_dissector("ms-nns", dissect_nns, proto_nns);
}

void proto_reg_handoff_nns(void)
{
    gssapi_handle = find_dissector("gssapi");
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=8 expandtab:
 * :indentSize=4:tabSize=8:noTabs=true:
 */