Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-fortinet-sso.c
Line
Count
Source
1
/* packet-fortinet-sso.c
2
 * Routines for Fortinet Single Sign-On
3
 * Copyright 2020, Alexis La Goutte <alexis.lagoutte at gmail dot com>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 *
11
 * No spec/doc is available based on reverse/analysis of protocol...
12
 */
13
14
#include "config.h"
15
16
#include <epan/packet.h>
17
#include <epan/addr_resolv.h>
18
19
0
#define UDP_FSSO        8002
20
21
void proto_register_fortinet_sso(void);
22
void proto_reg_handoff_fortinet_sso(void);
23
24
static int proto_fortinet_sso;
25
static int ett_fortinet_sso;
26
27
static int hf_fsso_length;
28
static int hf_fsso_timestamp;
29
static int hf_fsso_client_ip;
30
static int hf_fsso_payload_length;
31
static int hf_fsso_string;
32
static int hf_fsso_domain;
33
static int hf_fsso_user;
34
static int hf_fsso_host;
35
static int hf_fsso_version;
36
static int hf_fsso_tsagent_number_port_range;
37
static int hf_fsso_tsagent_port_range_min;
38
static int hf_fsso_tsagent_port_range_max;
39
static int hf_fsso_unknown;
40
static int hf_fsso_unknown_ipv4;
41
42
static dissector_handle_t fortinet_sso_handle;
43
44
/* Add the '/' delimited field at offset (limited to *remaining bytes) and
45
 * advance past it. If there is no delimiter the field takes all that is left. */
46
static void
47
fsso_add_field(proto_tree *tree, int hf, tvbuff_t *tvb, unsigned *offset, unsigned *remaining)
48
0
{
49
0
    unsigned found_offset;
50
0
    unsigned len;
51
0
    bool found = false;
52
53
0
    if (*remaining > 0)
54
0
        found = tvb_find_uint8_length(tvb, *offset, *remaining, '/', &found_offset);
55
0
    if (found) {
56
0
        len = found_offset - *offset;
57
0
        proto_tree_add_item(tree, hf, tvb, *offset, len, ENC_ASCII);
58
0
        *offset += len + 1;
59
0
        *remaining -= len + 1;
60
0
    } else {
61
0
        proto_tree_add_item(tree, hf, tvb, *offset, *remaining, ENC_ASCII);
62
0
        *offset += *remaining;
63
0
        *remaining = 0;
64
0
    }
65
0
}
66
67
static int
68
dissect_fortinet_sso(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
69
0
{
70
0
    proto_tree *ti;
71
0
    proto_tree *fsso_tree;
72
0
    uint32_t payload_length, client_ip;
73
0
    unsigned string_length;
74
0
    const char *string;
75
0
    unsigned offset = 0;
76
77
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "FSSO");
78
0
    col_set_str(pinfo->cinfo, COL_INFO, "Fortinet Single Sign-On");
79
80
0
    ti = proto_tree_add_item(tree, proto_fortinet_sso, tvb, 0, -1, ENC_NA);
81
0
    fsso_tree = proto_item_add_subtree(ti, ett_fortinet_sso);
82
83
0
    proto_tree_add_item(fsso_tree, hf_fsso_length, tvb, offset, 2, ENC_BIG_ENDIAN);
84
0
    offset += 2;
85
86
0
    proto_tree_add_item(fsso_tree, hf_fsso_timestamp, tvb, offset, 4, ENC_BIG_ENDIAN);
87
0
    offset += 4;
88
89
0
    proto_tree_add_item(fsso_tree, hf_fsso_client_ip, tvb, offset, 4, ENC_BIG_ENDIAN);
90
0
    client_ip = tvb_get_ipv4(tvb, offset);
91
0
    offset += 4;
92
93
0
    proto_tree_add_item_ret_uint(fsso_tree, hf_fsso_payload_length, tvb, offset, 2, ENC_BIG_ENDIAN, &payload_length);
94
0
    offset += 2;
95
96
0
    proto_tree_add_item_ret_string_and_length(fsso_tree, hf_fsso_string, tvb, offset, -1, ENC_ASCII, pinfo->pool, (const uint8_t**)&string, &string_length);
97
0
    col_add_str(pinfo->cinfo, COL_INFO, string);
98
99
0
    if(client_ip == 0xFFFFFFFF) { //if client_ip equal 255.255.255.255 (0xFFFFFFFF) is KeepAlive packet
100
        /* Domain / KeepAlive (User) / Version */
101
0
        fsso_add_field(fsso_tree, hf_fsso_domain, tvb, &offset, &string_length);
102
0
        fsso_add_field(fsso_tree, hf_fsso_user, tvb, &offset, &string_length);
103
0
        fsso_add_field(fsso_tree, hf_fsso_version, tvb, &offset, &string_length);
104
0
    } else {
105
        /* Host / Domain / User */
106
0
        fsso_add_field(fsso_tree, hf_fsso_host, tvb, &offset, &string_length);
107
0
        fsso_add_field(fsso_tree, hf_fsso_domain, tvb, &offset, &string_length);
108
0
        fsso_add_field(fsso_tree, hf_fsso_user, tvb, &offset, &string_length);
109
0
    }
110
111
0
    if(tvb_reported_length_remaining(tvb, offset) == 4) {
112
113
        /* There is some packet with extra IPv4 address... */
114
0
        proto_tree_add_item(fsso_tree, hf_fsso_unknown_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN);
115
0
        offset += 4;
116
117
0
    } else {
118
119
0
        if(tvb_reported_length_remaining(tvb, offset)) {
120
0
            uint16_t value;
121
0
            uint32_t number_port_range;
122
0
            value = tvb_get_ntohs(tvb, offset);
123
124
0
            if(value == 0x2002) { /* Not a TS Agent additional Data */
125
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown, tvb, offset, 2, ENC_NA);
126
0
                offset += 2;
127
128
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN);
129
0
                offset += 4;
130
131
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown, tvb, offset, 6, ENC_NA);
132
0
                offset += 6;
133
134
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN);
135
0
                offset += 4;
136
137
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown, tvb, offset, 1, ENC_NA);
138
0
                offset += 1;
139
0
            } else {
140
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown, tvb, offset, 15, ENC_NA);
141
0
                offset += 15;
142
143
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown, tvb, offset, 5, ENC_NA);
144
0
                offset += 5;
145
146
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown, tvb, offset, 6, ENC_NA);
147
0
                offset += 6;
148
149
                /* Port Range assigned to user for TS Agent (RDP/Citrix) */
150
0
                proto_tree_add_item_ret_uint(fsso_tree, hf_fsso_tsagent_number_port_range, tvb, offset, 2, ENC_BIG_ENDIAN, &number_port_range);
151
0
                offset += 2;
152
153
0
                while (number_port_range) {
154
155
0
                    proto_tree_add_item(fsso_tree, hf_fsso_tsagent_port_range_min, tvb, offset, 2, ENC_BIG_ENDIAN);
156
0
                    offset += 2;
157
158
0
                    proto_tree_add_item(fsso_tree, hf_fsso_tsagent_port_range_max, tvb, offset, 2, ENC_BIG_ENDIAN);
159
0
                    offset += 2;
160
161
0
                    number_port_range --;
162
0
                }
163
0
            }
164
0
        }
165
166
0
    }
167
168
0
    return offset;
169
0
}
170
171
static bool
172
dissect_fortinet_fsso_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
173
103
{
174
103
    uint32_t length_remaining, length;
175
176
103
    if (tvb_captured_length(tvb) < 2) {
177
2
        return false;
178
2
    }
179
180
101
    length_remaining = tvb_reported_length_remaining(tvb, 0);
181
    //first bytes is the length of payload
182
101
    length = tvb_get_ntohs(tvb, 0);
183
101
    if(length_remaining != length)
184
101
    {
185
101
        return false;
186
101
    }
187
188
    //always send with UDP Destination Port 80002
189
0
    if(pinfo->destport != UDP_FSSO)
190
0
    {
191
0
        return false;
192
0
    }
193
194
0
    dissect_fortinet_sso(tvb, pinfo, tree, data);
195
0
    return true;
196
0
}
197
198
void
199
proto_register_fortinet_sso(void)
200
16
{
201
16
    static hf_register_info hf[] = {
202
16
        { &hf_fsso_length,
203
16
        { "Length", "fortinet_sso.length", FT_UINT16, BASE_DEC, NULL, 0x0,
204
16
        NULL, HFILL}},
205
206
16
        { &hf_fsso_timestamp,
207
16
        { "Timestamp", "fortinet_sso.timestamp", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0,
208
16
        NULL, HFILL}},
209
210
16
        { &hf_fsso_client_ip,
211
16
        { "Client IP", "fortinet_sso.client_ip", FT_IPv4, BASE_NONE, NULL, 0x0,
212
16
        NULL, HFILL}},
213
214
16
        { &hf_fsso_payload_length,
215
16
        { "Payload Length", "fortinet_sso.payload_length", FT_UINT16, BASE_DEC, NULL, 0x0,
216
16
        NULL, HFILL}},
217
218
16
        { &hf_fsso_string,
219
16
        { "String", "fortinet_sso.string", FT_STRINGZ, BASE_NONE, NULL, 0x0,
220
16
        NULL, HFILL}},
221
222
16
        { &hf_fsso_user,
223
16
        { "User", "fortinet_sso.user", FT_STRING, BASE_NONE, NULL, 0x0,
224
16
        NULL, HFILL}},
225
226
16
        { &hf_fsso_domain,
227
16
        { "Domain", "fortinet_sso.domain", FT_STRING, BASE_NONE, NULL, 0x0,
228
16
        NULL, HFILL}},
229
230
16
        { &hf_fsso_host,
231
16
        { "Host", "fortinet_sso.host", FT_STRING, BASE_NONE, NULL, 0x0,
232
16
        NULL, HFILL}},
233
234
16
        { &hf_fsso_version,
235
16
        { "Version", "fortinet_sso.version", FT_STRING, BASE_NONE, NULL, 0x0,
236
16
        NULL, HFILL}},
237
238
16
        { &hf_fsso_tsagent_number_port_range,
239
16
        { "Number of Port Range", "fortinet_sso.tsagent.port_range.number", FT_UINT16, BASE_DEC, NULL, 0x0,
240
16
        NULL, HFILL}},
241
242
16
        { &hf_fsso_tsagent_port_range_min,
243
16
        { "Port Range (Min)", "fortinet_sso.tsagent.port_range.min", FT_UINT16, BASE_DEC, NULL, 0x0,
244
16
        NULL, HFILL}},
245
246
16
        { &hf_fsso_tsagent_port_range_max,
247
16
        { "Port Range (Max)", "fortinet_sso.tsagent.port_range.max", FT_UINT16, BASE_DEC, NULL, 0x0,
248
16
        NULL, HFILL}},
249
250
16
        { &hf_fsso_unknown,
251
16
        { "Unknown", "fortinet_sso.unknown", FT_BYTES, BASE_NONE, NULL, 0x0,
252
16
        "Unknown Data...", HFILL}},
253
254
16
        { &hf_fsso_unknown_ipv4,
255
16
        { "Unknown IPv4", "fortinet_sso.unknown.ipv4", FT_IPv4, BASE_NONE, NULL, 0x0,
256
16
        "Unknown Data...", HFILL}},
257
258
16
    };
259
260
16
    static int *ett[] = {
261
16
        &ett_fortinet_sso,
262
16
    };
263
264
16
    proto_fortinet_sso = proto_register_protocol("Fortinet Single Sign On", "fortinet_sso", "fortinet_sso");
265
16
    fortinet_sso_handle = register_dissector("fortinet_sso", dissect_fortinet_sso, proto_fortinet_sso);
266
267
16
    proto_register_field_array(proto_fortinet_sso, hf, array_length(hf));
268
16
    proto_register_subtree_array(ett, array_length(ett));
269
16
}
270
271
272
void
273
proto_reg_handoff_fortinet_sso(void)
274
16
{
275
16
    dissector_add_uint_with_preference("udp.port", 0, fortinet_sso_handle);
276
16
    heur_dissector_add("udp", dissect_fortinet_fsso_heur, "Fortinet SSO over UDP", "fortinet_sso", proto_fortinet_sso, HEURISTIC_ENABLE);
277
16
}
278
279
/*
280
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
281
 *
282
 * Local variables:
283
 * c-basic-offset: 4
284
 * tab-width: 8
285
 * indent-tabs-mode: nil
286
 * End:
287
 *
288
 * vi: set shiftwidth=4 tabstop=8 expandtab:
289
 * :indentSize=4:tabSize=8:noTabs=true:
290
 */