Coverage Report

Created: 2026-07-12 07:10

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
2
#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
static int
45
dissect_fortinet_sso(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
46
0
{
47
0
    proto_tree *ti;
48
0
    proto_tree *fsso_tree;
49
0
    uint32_t payload_length, client_ip;
50
0
    unsigned string_length;
51
0
    const char *string;
52
0
    uint32_t len;
53
0
    unsigned offset = 0;
54
55
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "FSSO");
56
0
    col_set_str(pinfo->cinfo, COL_INFO, "Fortinet Single Sign-On");
57
58
0
    ti = proto_tree_add_item(tree, proto_fortinet_sso, tvb, 0, -1, ENC_NA);
59
0
    fsso_tree = proto_item_add_subtree(ti, ett_fortinet_sso);
60
61
0
    proto_tree_add_item(fsso_tree, hf_fsso_length, tvb, offset, 2, ENC_BIG_ENDIAN);
62
0
    offset += 2;
63
64
0
    proto_tree_add_item(fsso_tree, hf_fsso_timestamp, tvb, offset, 4, ENC_BIG_ENDIAN);
65
0
    offset += 4;
66
67
0
    proto_tree_add_item(fsso_tree, hf_fsso_client_ip, tvb, offset, 4, ENC_BIG_ENDIAN);
68
0
    client_ip = tvb_get_ipv4(tvb, offset);
69
0
    offset += 4;
70
71
0
    proto_tree_add_item_ret_uint(fsso_tree, hf_fsso_payload_length, tvb, offset, 2, ENC_BIG_ENDIAN, &payload_length);
72
0
    offset += 2;
73
74
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);
75
0
    col_set_str(pinfo->cinfo, COL_INFO, string);
76
77
0
    if(client_ip == 0xFFFFFFFF) { //if client_ip equal 255.255.255.255 (0xFFFFFFFF) is KeepAlive packet
78
        /* Domain / KeepAlive (User) / Version */
79
0
        tvb_find_uint8_length(tvb, offset, string_length, '/', &len);
80
0
        len = len -offset;
81
0
        proto_tree_add_item(fsso_tree, hf_fsso_domain, tvb, offset, len, ENC_ASCII);
82
0
        offset += (len + 1);
83
0
        string_length -= (len + 1);
84
85
0
        tvb_find_uint8_length(tvb, offset, string_length, '/', &len);
86
0
        len = len - offset;
87
0
        proto_tree_add_item(fsso_tree, hf_fsso_user, tvb, offset, len, ENC_ASCII);
88
0
        offset += (len + 1);
89
0
        string_length -= (len + 1);
90
91
0
        proto_tree_add_item(fsso_tree, hf_fsso_version, tvb, offset, string_length, ENC_ASCII);
92
0
        offset += (string_length);
93
94
0
    } else {
95
        /* Host / Domain / User */
96
0
        tvb_find_uint8_length(tvb, offset, string_length, '/', &len);
97
0
        len = len - offset;
98
0
        proto_tree_add_item(fsso_tree, hf_fsso_host, tvb, offset, len, ENC_ASCII);
99
0
        offset += (len + 1);
100
0
        string_length -= (len + 1);
101
102
0
        tvb_find_uint8_length(tvb, offset, string_length, '/', &len);
103
0
        len = len - offset;
104
0
        proto_tree_add_item(fsso_tree, hf_fsso_domain, tvb, offset, len, ENC_ASCII);
105
0
        offset += (len + 1);
106
0
        string_length -= (len + 1);
107
108
0
        proto_tree_add_item(fsso_tree, hf_fsso_user, tvb, offset, string_length, ENC_ASCII);
109
0
        offset += (string_length);
110
0
    }
111
112
0
    if(tvb_reported_length_remaining(tvb, offset) == 4) {
113
114
        /* There is some packet with extra IPv4 address... */
115
0
        proto_tree_add_item(fsso_tree, hf_fsso_unknown_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN);
116
0
        offset += 4;
117
118
0
    } else {
119
120
0
        if(tvb_reported_length_remaining(tvb, offset)) {
121
0
            uint16_t value;
122
0
            uint32_t number_port_range;
123
0
            value = tvb_get_ntohs(tvb, offset);
124
125
0
            if(value == 0x2002) { /* Not a TS Agent additional Data */
126
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown, tvb, offset, 2, ENC_NA);
127
0
                offset += 2;
128
129
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN);
130
0
                offset += 4;
131
132
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown, tvb, offset, 6, ENC_NA);
133
0
                offset += 6;
134
135
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN);
136
0
                offset += 4;
137
138
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown, tvb, offset, 1, ENC_NA);
139
0
                offset += 1;
140
0
            } else {
141
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown, tvb, offset, 15, ENC_NA);
142
0
                offset += 15;
143
144
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown, tvb, offset, 5, ENC_NA);
145
0
                offset += 5;
146
147
0
                proto_tree_add_item(fsso_tree, hf_fsso_unknown, tvb, offset, 6, ENC_NA);
148
0
                offset += 6;
149
150
                /* Port Range assigned to user for TS Agent (RDP/Citrix) */
151
0
                proto_tree_add_item_ret_uint(fsso_tree, hf_fsso_tsagent_number_port_range, tvb, offset, 2, ENC_BIG_ENDIAN, &number_port_range);
152
0
                offset += 2;
153
154
0
                while (number_port_range) {
155
156
0
                    proto_tree_add_item(fsso_tree, hf_fsso_tsagent_port_range_min, tvb, offset, 2, ENC_BIG_ENDIAN);
157
0
                    offset += 2;
158
159
0
                    proto_tree_add_item(fsso_tree, hf_fsso_tsagent_port_range_max, tvb, offset, 2, ENC_BIG_ENDIAN);
160
0
                    offset += 2;
161
162
0
                    number_port_range --;
163
0
                }
164
0
            }
165
0
        }
166
167
0
    }
168
169
0
    return offset;
170
0
}
171
172
static bool
173
dissect_fortinet_fsso_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
174
1.41k
{
175
1.41k
    uint32_t length_remaining, length;
176
177
1.41k
    if (tvb_captured_length(tvb) < 2) {
178
98
        return false;
179
98
    }
180
181
1.31k
    length_remaining = tvb_reported_length_remaining(tvb, 0);
182
    //first bytes is the length of payload
183
1.31k
    length = tvb_get_ntohs(tvb, 0);
184
1.31k
    if(length_remaining != length)
185
1.31k
    {
186
1.31k
        return false;
187
1.31k
    }
188
189
    //always send with UDP Destination Port 80002
190
2
    if(pinfo->destport != UDP_FSSO)
191
2
    {
192
2
        return false;
193
2
    }
194
195
0
    dissect_fortinet_sso(tvb, pinfo, tree, data);
196
0
    return true;
197
2
}
198
199
void
200
proto_register_fortinet_sso(void)
201
15
{
202
15
    static hf_register_info hf[] = {
203
15
        { &hf_fsso_length,
204
15
        { "Length", "fortinet_sso.length", FT_UINT16, BASE_DEC, NULL, 0x0,
205
15
        NULL, HFILL}},
206
207
15
        { &hf_fsso_timestamp,
208
15
        { "Timestamp", "fortinet_sso.timestamp", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0,
209
15
        NULL, HFILL}},
210
211
15
        { &hf_fsso_client_ip,
212
15
        { "Client IP", "fortinet_sso.client_ip", FT_IPv4, BASE_NONE, NULL, 0x0,
213
15
        NULL, HFILL}},
214
215
15
        { &hf_fsso_payload_length,
216
15
        { "Payload Length", "fortinet_sso.payload_length", FT_UINT16, BASE_DEC, NULL, 0x0,
217
15
        NULL, HFILL}},
218
219
15
        { &hf_fsso_string,
220
15
        { "String", "fortinet_sso.string", FT_STRINGZ, BASE_NONE, NULL, 0x0,
221
15
        NULL, HFILL}},
222
223
15
        { &hf_fsso_user,
224
15
        { "User", "fortinet_sso.user", FT_STRING, BASE_NONE, NULL, 0x0,
225
15
        NULL, HFILL}},
226
227
15
        { &hf_fsso_domain,
228
15
        { "Domain", "fortinet_sso.domain", FT_STRING, BASE_NONE, NULL, 0x0,
229
15
        NULL, HFILL}},
230
231
15
        { &hf_fsso_host,
232
15
        { "Host", "fortinet_sso.host", FT_STRING, BASE_NONE, NULL, 0x0,
233
15
        NULL, HFILL}},
234
235
15
        { &hf_fsso_version,
236
15
        { "Version", "fortinet_sso.version", FT_STRING, BASE_NONE, NULL, 0x0,
237
15
        NULL, HFILL}},
238
239
15
        { &hf_fsso_tsagent_number_port_range,
240
15
        { "Number of Port Range", "fortinet_sso.tsagent.port_range.number", FT_UINT16, BASE_DEC, NULL, 0x0,
241
15
        NULL, HFILL}},
242
243
15
        { &hf_fsso_tsagent_port_range_min,
244
15
        { "Port Range (Min)", "fortinet_sso.tsagent.port_range.min", FT_UINT16, BASE_DEC, NULL, 0x0,
245
15
        NULL, HFILL}},
246
247
15
        { &hf_fsso_tsagent_port_range_max,
248
15
        { "Port Range (Max)", "fortinet_sso.tsagent.port_range.max", FT_UINT16, BASE_DEC, NULL, 0x0,
249
15
        NULL, HFILL}},
250
251
15
        { &hf_fsso_unknown,
252
15
        { "Unknown", "fortinet_sso.unknown", FT_BYTES, BASE_NONE, NULL, 0x0,
253
15
        "Unknown Data...", HFILL}},
254
255
15
        { &hf_fsso_unknown_ipv4,
256
15
        { "Unknown IPv4", "fortinet_sso.unknown.ipv4", FT_IPv4, BASE_NONE, NULL, 0x0,
257
15
        "Unknown Data...", HFILL}},
258
259
15
    };
260
261
15
    static int *ett[] = {
262
15
        &ett_fortinet_sso,
263
15
    };
264
265
15
    proto_fortinet_sso = proto_register_protocol("Fortinet Single Sign On", "fortinet_sso", "fortinet_sso");
266
15
    fortinet_sso_handle = register_dissector("fortinet_sso", dissect_fortinet_sso, proto_fortinet_sso);
267
268
15
    proto_register_field_array(proto_fortinet_sso, hf, array_length(hf));
269
15
    proto_register_subtree_array(ett, array_length(ett));
270
15
}
271
272
273
void
274
proto_reg_handoff_fortinet_sso(void)
275
15
{
276
15
    dissector_add_uint_with_preference("udp.port", 0, fortinet_sso_handle);
277
15
    heur_dissector_add("udp", dissect_fortinet_fsso_heur, "Fortinet SSO over UDP", "fortinet_sso", proto_fortinet_sso, HEURISTIC_ENABLE);
278
15
}
279
280
/*
281
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
282
 *
283
 * Local variables:
284
 * c-basic-offset: 4
285
 * tab-width: 8
286
 * indent-tabs-mode: nil
287
 * End:
288
 *
289
 * vi: set shiftwidth=4 tabstop=8 expandtab:
290
 * :indentSize=4:tabSize=8:noTabs=true:
291
 */