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-roughtime.c
Line
Count
Source
1
/* packet-roughtime.c
2
 * Dissector for Roughtime Time Synchronization
3
 *
4
 * Copyright (c) 2024-2026 by Martin Mayer <martin.mayer@m2-it-solutions.de>
5
 *
6
 * Wireshark - Network traffic analyzer
7
 * By Gerald Combs <gerald@wireshark.org>
8
 * Copyright 1998 Gerald Combs
9
 *
10
 * SPDX-License-Identifier: GPL-2.0-or-later
11
 */
12
13
/*
14
 * Based on:
15
 *
16
 * - Google Roughtime Protocol
17
 *   https://roughtime.googlesource.com/roughtime/+/HEAD/PROTOCOL.md
18
 *
19
 * - IETF Roughtime (RFC 10049)
20
 *   https://datatracker.ietf.org/doc/html/rfc10049
21
 */
22
23
#include "config.h"
24
#include <epan/packet.h>
25
#include <epan/expert.h>
26
#include <epan/conversation.h>
27
#include "packet-tcp.h"
28
29
/* Roughtime tags
30
 * Ref.: https://www.iana.org/assignments/roughtime#roughtime-tags
31
 */
32
0
#define TAG_TYPE_SIG    0x00474953  // SIG\x00
33
0
#define TAG_TYPE_VER    0x00524556  // VER\x00
34
0
#define TAG_TYPE_SRV    0x00565253  // SRV\x00
35
0
#define TAG_TYPE_NONC   0x434E4F4E  // NONC
36
0
#define TAG_TYPE_DELE   0x454C4544  // DELE
37
0
#define TAG_TYPE_TYPE   0x45505954  // TYPE
38
0
#define TAG_TYPE_PATH   0x48544150  // PATH
39
0
#define TAG_TYPE_RADI   0x49444152  // RADI
40
0
#define TAG_TYPE_PUBK   0x4B425550  // PUBK
41
0
#define TAG_TYPE_MIDP   0x5044494D  // MIDP
42
0
#define TAG_TYPE_SREP   0x50455253  // SREP
43
0
#define TAG_TYPE_VERS   0x53524556  // VERS
44
0
#define TAG_TYPE_MINT   0x544E494D  // MINT
45
0
#define TAG_TYPE_ROOT   0x544F4F52  // ROOT
46
0
#define TAG_TYPE_CERT   0x54524543  // CERT
47
0
#define TAG_TYPE_MAXT   0x5458414D  // MAXT
48
0
#define TAG_TYPE_INDX   0x58444E49  // INDX
49
0
#define TAG_TYPE_ZZZZ   0x5A5A5A5A  // ZZZZ
50
51
/* Additional tags (Google) */
52
0
#define TAG_TYPE_PAD    0xFF444150  // PAD\xFF
53
54
/* Roughtime IETF Header Magic Word */
55
0
#define HDR_IETF        0x4D49544847554F52 // ROUGHTIM
56
57
/* Min. header length to determine PDU length (Magic + Len) */
58
0
#define HDR_IETF_MINLEN 12
59
60
0
#define PROTO_TEXT_GOOGLE   "Google"
61
0
#define PROTO_TEXT_IETF     "IETF"
62
63
void proto_register_roughtime(void);
64
void proto_reg_handoff_roughtime(void);
65
66
static dissector_handle_t roughtime_udp_handle;
67
static dissector_handle_t roughtime_tcp_handle;
68
69
static int proto_roughtime;
70
71
/* Fields */
72
static int hf_roughtime_proto;
73
static int hf_roughtime_hdr;
74
static int hf_roughtime_msg_len;
75
static int hf_roughtime_srep;
76
static int hf_roughtime_cert;
77
static int hf_roughtime_dele;
78
static int hf_roughtime_num_tags;
79
static int hf_roughtime_offset;
80
static int hf_roughtime_tag;
81
static int hf_roughtime_nonce;
82
static int hf_roughtime_type;
83
static int hf_roughtime_ver;
84
static int hf_roughtime_sig;
85
static int hf_roughtime_srv;
86
static int hf_roughtime_pad;
87
static int hf_roughtime_index;
88
static int hf_roughtime_path;
89
static int hf_roughtime_radius;
90
static int hf_roughtime_midp;
91
static int hf_roughtime_mint;
92
static int hf_roughtime_maxt;
93
static int hf_roughtime_root;
94
static int hf_roughtime_pubk;
95
static int hf_roughtime_value;
96
static int hf_roughtime_time;
97
static int hf_roughtime_time_low;
98
static int hf_roughtime_time_up;
99
static int hf_roughtime_response_in;
100
static int hf_roughtime_response_to;
101
102
/* Map struct */
103
typedef struct _roughtime_map_t {
104
    uint32_t value_offset;
105
    uint32_t type;
106
    uint32_t length;
107
} roughtime_map_t;
108
109
/* Type enum */
110
typedef enum _roughtime_proto_type_e {
111
    PROTO_TYPE_UNDEFINED,
112
    PROTO_TYPE_GOOGLE,
113
    PROTO_TYPE_IETF
114
} roughtime_proto_type_e;
115
116
/* Expert fields */
117
static expert_field ei_roughtime_illegal_length;
118
static expert_field ei_roughtime_response_too_large;
119
static expert_field ei_roughtime_path_too_large;
120
static expert_field ei_roughtime_nesting_too_deep;
121
122
/* Trees */
123
static int ett_roughtime;
124
static int ett_roughtime_srep;
125
static int ett_roughtime_cert;
126
static int ett_roughtime_dele;
127
128
/* Request/response tracking */
129
typedef struct _roughtime_req_resp_t {
130
    uint32_t req_frame;
131
    uint32_t resp_frame;
132
    uint32_t req_length;
133
    uint32_t resp_length;
134
} roughtime_req_resp_t;
135
136
/* Roughtime versions
137
 * Ref.: https://www.iana.org/assignments/roughtime#roughtime-versions
138
 */
139
static const range_string roughtime_version_rvals[] = {
140
    { 0x00000000, 0x00000000, "Reserved" },
141
    { 0x00000001, 0x00000001, "Roughtime version 1" },
142
    { 0x80000000, 0xBFFFFFFF, "Reserved for Experimental Use" },
143
    { 0xC0000000, 0xFFFFFFFF, "Reserved for Private Use" },
144
    {          0,          0, NULL }
145
};
146
147
0
static proto_item* add_generated_time_item(tvbuff_t *tvb, proto_tree *tree, int hf, nstime_t *ts) {
148
0
    proto_item *pi = proto_tree_add_time(tree, hf, tvb, 0, 0, ts);
149
0
    proto_item_set_generated(pi);
150
0
    return pi;
151
0
}
152
153
0
#define ROUGHTIME_MAX_NESTING 8
154
155
static int
156
// NOLINTNEXTLINE(misc-no-recursion)
157
0
dissect_roughtime_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, roughtime_proto_type_e proto_type, unsigned depth) {
158
159
0
    uint32_t num_tags, tag, tag_offset = 0, type, radius_i = 0, prev_offset = 0, value_block_len;
160
0
    nstime_t time_midp, time_radius, time_lower, time_upper;
161
0
    proto_item *pi;
162
0
    proto_tree *msg_tree;
163
0
    wmem_array_t *map_arr = wmem_array_new(pinfo->pool, sizeof(roughtime_map_t));
164
0
    roughtime_map_t *map, map_init = {.type = 0, .length = 0};
165
0
    conversation_t *conv;
166
0
    roughtime_req_resp_t *conv_data;
167
168
    /* This function will get called recursively, guard it with an explicit
169
     * counter (an exception would leak pinfo->dissection_depth). */
170
0
    if (depth > ROUGHTIME_MAX_NESTING) {
171
0
        proto_tree_add_expert_remaining(tree, pinfo, &ei_roughtime_nesting_too_deep, tvb, 0);
172
0
        return tvb_reported_length(tvb);
173
0
    }
174
175
    /* Presence of fields of interest for req/resp tracking */
176
0
    bool fa_srep = false, fa_nonc = false;
177
178
0
    unsigned offset = 0;
179
0
    nstime_set_unset(&time_midp);
180
0
    nstime_set_unset(&time_radius);
181
182
0
    proto_tree_add_item_ret_uint(tree, hf_roughtime_num_tags, tvb, 0, 4, ENC_LITTLE_ENDIAN, &num_tags);
183
0
    offset += 4;
184
185
    /* The value block starts after the num_tags field, the num_tags - 1
186
     * offsets and the num_tags tags: 8 * num_tags octets (4 with no tags). */
187
0
    uint64_t header_len = num_tags ? 8 * (uint64_t)num_tags : 4;
188
0
    if (header_len > tvb_reported_length(tvb)) {
189
0
        proto_tree_add_expert_remaining(tree, pinfo, &ei_roughtime_illegal_length, tvb, 0);
190
0
        return tvb_reported_length(tvb);
191
0
    }
192
0
    value_block_len = tvb_reported_length(tvb) - (uint32_t)header_len;
193
194
0
    conv = find_or_create_conversation(pinfo);
195
0
    conv_data = (roughtime_req_resp_t *)conversation_get_proto_data(conv, proto_roughtime);
196
197
    /*
198
     * Value Offset Block
199
     *
200
     * Listed after Num. Tags in 4-octet chunks
201
     */
202
0
    tag = 0;
203
0
    while(tag < num_tags) {
204
0
        if(tag == 0) {
205
0
            pi = proto_tree_add_uint(tree, hf_roughtime_offset, tvb, offset, 0, 0);
206
0
            proto_item_set_generated(pi);
207
0
        } else {
208
0
            proto_tree_add_item_ret_uint(tree, hf_roughtime_offset, tvb, offset, 4, ENC_LITTLE_ENDIAN, &tag_offset);
209
0
            offset += 4;
210
0
            if (tag_offset < prev_offset || (tag_offset & 3) || tag_offset > value_block_len) {
211
0
                proto_tree_add_expert_remaining(tree, pinfo, &ei_roughtime_illegal_length, tvb, offset - 4);
212
0
                return tvb_reported_length(tvb);
213
0
            }
214
0
            prev_offset = tag_offset;
215
0
        }
216
0
        map_init.value_offset = tag_offset;
217
0
        wmem_array_append_one(map_arr, map_init);
218
219
        // Calculate the value lengths of the preceding entry
220
0
        if(tag > 0) {
221
0
            map = (roughtime_map_t*)wmem_array_index(map_arr, tag-1);
222
0
            map->length = tag_offset - map->value_offset;
223
0
        }
224
        // Calculate the value lengths of the last entry
225
0
        if(tag + 1 == num_tags) {
226
0
            map = (roughtime_map_t*)wmem_array_index(map_arr, tag);
227
0
            map->length = tag_offset - map->value_offset;
228
0
            map->length = tvb_reported_length_remaining(tvb, offset + map->value_offset + 4*num_tags);
229
0
        }
230
231
0
        tag++;
232
0
    }
233
234
    /*
235
     * Tag Block
236
     *
237
     * Listed after Value Offset Block in 4-octet chunks
238
     */
239
0
    tag = 0;
240
0
    while(tag < num_tags) {
241
242
0
        map = (roughtime_map_t*)wmem_array_index(map_arr, tag);
243
244
0
        type = tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN);
245
0
        const char *type_str;
246
247
        // If we reached the PAD-tag which is terminated by 0xFF, read only 3 octets and extend the item length.
248
        // All other 3-char tags are null-terminated.
249
0
        if(type == TAG_TYPE_PAD) {
250
0
            pi = proto_tree_add_item_ret_string(tree, hf_roughtime_tag, tvb, offset, 3, ENC_ASCII, pinfo->pool, (const uint8_t**)&type_str);
251
0
            proto_item_set_len(pi, 4);
252
0
        } else {
253
0
            proto_tree_add_item_ret_string(tree, hf_roughtime_tag, tvb, offset, 4, ENC_ASCII, pinfo->pool, (const uint8_t**)&type_str);
254
0
        }
255
256
0
        col_append_sep_str(pinfo->cinfo, COL_INFO, " ", type_str);
257
258
0
        map->type = type;
259
0
        offset += 4;
260
0
        tag++;
261
0
    }
262
263
    /*
264
     * Value Block
265
     *
266
     * Listed after Tag Block, each with variable length
267
     */
268
0
    tag = 0;
269
0
    while(tag < num_tags) {
270
271
0
        map = (roughtime_map_t*)wmem_array_index(map_arr, tag);
272
273
0
        switch (map->type)
274
0
        {
275
0
            case TAG_TYPE_SREP:
276
0
                fa_srep = true;
277
0
                pi = proto_tree_add_item(tree, hf_roughtime_srep, tvb, offset, map->length, ENC_NA);
278
0
                msg_tree = proto_item_add_subtree(pi, ett_roughtime_srep);
279
0
                dissect_roughtime_msg(tvb_new_subset_length(tvb, offset, map->length), pinfo, msg_tree, proto_type, depth + 1);
280
0
                break;
281
282
0
            case TAG_TYPE_NONC:
283
0
                fa_nonc = true;
284
0
                proto_tree_add_item(tree, hf_roughtime_nonce, tvb, offset, map->length, ENC_NA);
285
0
                break;
286
287
0
            case TAG_TYPE_VER:
288
0
            case TAG_TYPE_VERS:
289
0
                if(map->length == 0) {
290
                    // Add empty item for informational purpose
291
0
                    proto_tree_add_item(tree, hf_roughtime_ver, tvb, offset, 0, ENC_LITTLE_ENDIAN);
292
0
                }
293
0
                else if(map->length % 4 == 0) {
294
0
                    for(uint32_t i = 0; i < map->length; i += 4) {
295
0
                        proto_tree_add_item(tree, hf_roughtime_ver, tvb, offset + i, 4, ENC_LITTLE_ENDIAN);
296
0
                    }
297
0
                }
298
0
                else {
299
0
                    pi = proto_tree_add_expert(tree, pinfo, &ei_roughtime_illegal_length, tvb, offset, map->length);
300
0
                    proto_item_prepend_text(pi, "Version: ");
301
0
                }
302
0
                break;
303
304
0
            case TAG_TYPE_ZZZZ:
305
0
            case TAG_TYPE_PAD:
306
0
                proto_tree_add_item(tree, hf_roughtime_pad, tvb, offset, map->length, ENC_NA);
307
0
                break;
308
309
0
            case TAG_TYPE_SIG:
310
0
                pi = proto_tree_add_item(tree, hf_roughtime_sig, tvb, offset, map->length, ENC_NA);
311
0
                if(map->length != 64) {
312
0
                    expert_add_info(pinfo, pi, &ei_roughtime_illegal_length);
313
0
                }
314
0
                break;
315
316
0
            case TAG_TYPE_SRV:
317
0
                proto_tree_add_item(tree, hf_roughtime_srv, tvb, offset, map->length, ENC_NA);
318
0
                break;
319
320
0
            case TAG_TYPE_CERT:
321
0
                pi = proto_tree_add_item(tree, hf_roughtime_cert, tvb, offset, map->length, ENC_NA);
322
0
                msg_tree = proto_item_add_subtree(pi, ett_roughtime_cert);
323
0
                dissect_roughtime_msg(tvb_new_subset_length(tvb, offset, map->length), pinfo, msg_tree, proto_type, depth + 1);
324
0
                break;
325
326
0
            case TAG_TYPE_INDX:
327
0
                pi = proto_tree_add_item(tree, hf_roughtime_index, tvb, offset, map->length, ENC_LITTLE_ENDIAN);
328
0
                if(map->length != 4) {
329
0
                    expert_add_info(pinfo, pi, &ei_roughtime_illegal_length);
330
0
                }
331
0
                break;
332
333
0
            case TAG_TYPE_PATH:
334
0
                if(map->length == 0) {
335
                    // Add empty item for informational purpose
336
0
                    proto_tree_add_item(tree, hf_roughtime_path, tvb, offset, 0, ENC_NA);
337
0
                }
338
0
                else if(proto_type == PROTO_TYPE_GOOGLE && map->length % 64 == 0) {
339
0
                    for(uint32_t i = 0; i < map->length; i += 64) {
340
0
                        proto_tree_add_item(tree, hf_roughtime_path, tvb, offset + i, 64, ENC_NA);
341
0
                    }
342
0
                }
343
0
                else if(proto_type == PROTO_TYPE_IETF && map->length % 32 == 0) {
344
0
                    for(uint32_t i = 0; i < map->length; i += 32) {
345
0
                        proto_tree_add_item(tree, hf_roughtime_path, tvb, offset + i, 32, ENC_NA);
346
0
                    }
347
0
                    if(map->length > 32*32) {
348
0
                        pi = proto_tree_add_expert(tree, pinfo, &ei_roughtime_path_too_large, tvb, offset, map->length);
349
0
                        proto_item_prepend_text(pi, "Path: ");
350
0
                    }
351
0
                }
352
0
                else {
353
0
                    pi = proto_tree_add_item(tree, hf_roughtime_path, tvb, offset, map->length, ENC_NA);
354
0
                    expert_add_info(pinfo, pi, &ei_roughtime_illegal_length);
355
0
                }
356
0
                break;
357
358
0
            case TAG_TYPE_RADI:
359
0
                if(map->length != 4) {
360
0
                    pi = proto_tree_add_expert(tree, pinfo, &ei_roughtime_illegal_length, tvb, offset, map->length);
361
0
                    proto_item_prepend_text(pi, "Radius: ");
362
0
                    break;
363
0
                }
364
365
0
                if(proto_type == PROTO_TYPE_GOOGLE) {
366
0
                    pi = proto_tree_add_item_ret_uint(tree, hf_roughtime_radius, tvb, offset, 4, ENC_LITTLE_ENDIAN, &radius_i);
367
0
                    proto_item_append_text(pi, "µs");
368
369
0
                    nstime_set_zero(&time_radius);
370
0
                    time_radius.nsecs = radius_i*1000;
371
0
                }
372
0
                else if(proto_type == PROTO_TYPE_IETF) {
373
0
                    pi = proto_tree_add_item_ret_uint(tree, hf_roughtime_radius, tvb, offset, 4, ENC_LITTLE_ENDIAN, &radius_i);
374
0
                    proto_item_append_text(pi, "s");
375
376
0
                    nstime_set_zero(&time_radius);
377
0
                    time_radius.secs = radius_i;
378
0
                }
379
0
                break;
380
381
0
            case TAG_TYPE_MIDP:
382
0
                if(map->length != 8) {
383
0
                    pi = proto_tree_add_expert(tree, pinfo, &ei_roughtime_illegal_length, tvb, offset, map->length);
384
0
                    proto_item_prepend_text(pi, "Midpoint: ");
385
0
                    break;
386
0
                }
387
388
0
                if(proto_type == PROTO_TYPE_GOOGLE) {
389
0
                    proto_tree_add_time_item(tree, hf_roughtime_midp, tvb, offset, 8, ENC_LITTLE_ENDIAN|ENC_TIME_USECS, &time_midp, NULL, NULL);
390
0
                    pi = proto_tree_add_item(tree, hf_roughtime_time, tvb, offset, 8, ENC_LITTLE_ENDIAN|ENC_TIME_USECS);
391
0
                    proto_item_set_hidden(pi); // available for filtering
392
393
0
                }
394
0
                else if(proto_type == PROTO_TYPE_IETF) {
395
0
                    proto_tree_add_time_item(tree, hf_roughtime_midp, tvb, offset, 8, ENC_LITTLE_ENDIAN|ENC_TIME_SECS, &time_midp, NULL, NULL);
396
0
                    pi = proto_tree_add_item(tree, hf_roughtime_time, tvb, offset, 8, ENC_LITTLE_ENDIAN|ENC_TIME_SECS);
397
0
                    proto_item_set_hidden(pi); // available for filtering
398
0
                }
399
0
                break;
400
401
0
            case TAG_TYPE_ROOT:
402
0
                proto_tree_add_item(tree, hf_roughtime_root, tvb, offset, map->length, ENC_NA);
403
0
                break;
404
405
0
            case TAG_TYPE_PUBK:
406
0
                proto_tree_add_item(tree, hf_roughtime_pubk, tvb, offset, map->length, ENC_NA);
407
0
                break;
408
409
0
            case TAG_TYPE_MINT:
410
0
                if(map->length != 8) {
411
0
                    pi = proto_tree_add_expert(tree, pinfo, &ei_roughtime_illegal_length, tvb, offset, map->length);
412
0
                    proto_item_prepend_text(pi, "Min. Time: ");
413
0
                    break;
414
0
                }
415
416
0
                if(proto_type == PROTO_TYPE_GOOGLE) {
417
0
                    proto_tree_add_item(tree, hf_roughtime_mint, tvb, offset, 8, ENC_LITTLE_ENDIAN|ENC_TIME_USECS);
418
0
                }
419
0
                else if(proto_type == PROTO_TYPE_IETF) {
420
0
                    proto_tree_add_item(tree, hf_roughtime_mint, tvb, offset, 8, ENC_LITTLE_ENDIAN|ENC_TIME_SECS);
421
0
                }
422
0
                break;
423
424
0
            case TAG_TYPE_MAXT:
425
0
                if(map->length != 8) {
426
0
                    pi = proto_tree_add_expert(tree, pinfo, &ei_roughtime_illegal_length, tvb, offset, map->length);
427
0
                    proto_item_prepend_text(pi, "Max. Time: ");
428
0
                    break;
429
0
                }
430
431
0
                if(proto_type == PROTO_TYPE_GOOGLE) {
432
0
                    proto_tree_add_item(tree, hf_roughtime_maxt, tvb, offset, 8, ENC_LITTLE_ENDIAN|ENC_TIME_USECS);
433
0
                }
434
0
                else if(proto_type == PROTO_TYPE_IETF) {
435
0
                    proto_tree_add_item(tree, hf_roughtime_maxt, tvb, offset, 8, ENC_LITTLE_ENDIAN|ENC_TIME_SECS);
436
0
                }
437
0
                break;
438
439
0
            case TAG_TYPE_DELE:
440
0
                pi = proto_tree_add_item(tree, hf_roughtime_dele, tvb, offset, map->length, ENC_NA);
441
0
                msg_tree = proto_item_add_subtree(pi, ett_roughtime_dele);
442
0
                dissect_roughtime_msg(tvb_new_subset_length(tvb, offset, map->length), pinfo, msg_tree, proto_type, depth + 1);
443
0
                break;
444
445
0
            case TAG_TYPE_TYPE:
446
0
                proto_tree_add_item(tree, hf_roughtime_type, tvb, offset, map->length, ENC_NA);
447
0
                break;
448
449
0
            default:
450
0
                proto_tree_add_item(tree, hf_roughtime_value, tvb, offset, map->length, ENC_NA);
451
0
                break;
452
0
        }
453
454
0
        offset += map->length;
455
0
        tag++;
456
0
    }
457
458
    /*
459
     * Time calculation based on accuracy (Radius)
460
     */
461
0
    if(!nstime_is_unset(&time_radius) && !nstime_is_unset(&time_midp)) {
462
463
0
        nstime_delta(&time_lower, &time_midp, &time_radius);
464
0
        nstime_sum(&time_upper, &time_midp, &time_radius);
465
466
0
        add_generated_time_item(tvb, tree, hf_roughtime_time_low, &time_lower);
467
0
        add_generated_time_item(tvb, tree, hf_roughtime_time_up, &time_upper);
468
0
    }
469
470
    /*
471
     * Conversation data
472
     *
473
     * Checks must only succeed on top-level message (non-recursive)
474
     */
475
0
    if(conv_data && depth == 0) {
476
477
0
        if(fa_srep && conv_data->resp_frame == 0) {
478
0
            conv_data->resp_frame = pinfo->num;
479
0
            conv_data->resp_length = tvb_reported_length(tvb);
480
0
        }
481
0
        else if (fa_nonc && conv_data->req_frame == 0) {
482
0
            conv_data->req_frame = pinfo->num;
483
0
            conv_data->req_length = tvb_reported_length(tvb);
484
0
        }
485
0
    }
486
487
0
    return offset;
488
0
}
489
490
static bool
491
test_ietf_roughtime(tvbuff_t *tvb)
492
0
{
493
0
    if (!tvb_bytes_exist(tvb, 0, HDR_IETF_MINLEN)) {
494
0
        return false;
495
0
    }
496
497
    /* Check for IETF Magic Word */
498
0
    if(tvb_get_uint64(tvb, 0, ENC_LITTLE_ENDIAN) != HDR_IETF) {
499
0
        return false;
500
0
    }
501
502
0
    return true;
503
0
}
504
505
static int
506
dissect_roughtime(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
507
0
{
508
0
    int offset;
509
0
    uint32_t msg_len;
510
0
    proto_item *ti, *pi, *ci;
511
0
    proto_tree *roughtime_tree;
512
0
    conversation_t *conv;
513
0
    roughtime_req_resp_t *conv_data;
514
0
    roughtime_proto_type_e proto_type = PROTO_TYPE_UNDEFINED;
515
516
0
    offset = 0;
517
518
0
    if (tvb_captured_length(tvb) < 8) {
519
0
        return 0;
520
0
    }
521
522
    /*
523
     * The protocol type
524
     * If we have no IETF header, assume Google's protocol version.
525
     */
526
0
    if(test_ietf_roughtime(tvb)) {
527
0
        proto_type = PROTO_TYPE_IETF;
528
0
    } else if (tvb_get_uint32(tvb, 0, ENC_LITTLE_ENDIAN) < 16) {
529
        /* The Google version starts with the number of outer tags, which
530
         * should never be more than 7. Allow twice as many. */
531
0
        proto_type = PROTO_TYPE_GOOGLE;
532
0
    } else {
533
0
        return 0;
534
0
    }
535
536
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "Roughtime");
537
0
    col_clear(pinfo->cinfo, COL_INFO);
538
539
0
    ti = proto_tree_add_item(tree, proto_roughtime, tvb, 0, -1, ENC_NA);
540
0
    roughtime_tree = proto_item_add_subtree(ti, ett_roughtime);
541
542
    /* Conversation init */
543
0
    conv = find_or_create_conversation(pinfo);
544
0
    conv_data = (roughtime_req_resp_t *)conversation_get_proto_data(conv, proto_roughtime);
545
0
    if (!conv_data) {
546
0
        conv_data = wmem_new(wmem_file_scope(), roughtime_req_resp_t);
547
0
        conv_data->req_frame = 0;
548
0
        conv_data->resp_frame = 0;
549
0
        conv_data->req_length = 0;
550
0
        conv_data->resp_length = 0;
551
0
        conversation_add_proto_data(conv, proto_roughtime, conv_data);
552
0
    }
553
554
0
    switch (proto_type) {
555
0
    case PROTO_TYPE_IETF:
556
0
        pi = proto_tree_add_string(roughtime_tree, hf_roughtime_proto, tvb, 0, 0, PROTO_TEXT_IETF);
557
0
        proto_item_set_generated(pi);
558
0
        proto_item_append_text(ti, ", Proto: %s", PROTO_TEXT_IETF);
559
0
        col_append_fstr(pinfo->cinfo, COL_INFO, "Proto: %s", PROTO_TEXT_IETF);
560
0
        col_append_sep_str(pinfo->cinfo, COL_INFO, ", ", "Tags:");
561
562
0
        proto_tree_add_item(roughtime_tree, hf_roughtime_hdr, tvb, 0, 8, ENC_ASCII);
563
0
        offset += 8;
564
565
0
        proto_tree_add_item_ret_uint(roughtime_tree, hf_roughtime_msg_len, tvb, offset, 4, ENC_LITTLE_ENDIAN, &msg_len);
566
0
        offset += 4;
567
568
0
        offset += dissect_roughtime_msg(
569
0
            tvb_new_subset_length(tvb, offset, msg_len),
570
0
            pinfo, roughtime_tree, proto_type, 0);
571
0
        break;
572
0
    case PROTO_TYPE_GOOGLE:
573
0
        pi = proto_tree_add_string(roughtime_tree, hf_roughtime_proto, tvb, 0, 0, PROTO_TEXT_GOOGLE);
574
0
        proto_item_set_generated(pi);
575
0
        proto_item_append_text(ti, ", Proto: %s", PROTO_TEXT_GOOGLE);
576
0
        col_append_fstr(pinfo->cinfo, COL_INFO, "Proto: %s", PROTO_TEXT_GOOGLE);
577
0
        col_append_sep_str(pinfo->cinfo, COL_INFO, ", ", "Tags:");
578
579
0
        offset += dissect_roughtime_msg(
580
0
            tvb_new_subset_remaining(tvb, offset),
581
0
            pinfo, roughtime_tree, proto_type, 0);
582
0
        break;
583
0
    default:
584
0
        DISSECTOR_ASSERT_NOT_REACHED();
585
0
    }
586
587
    /*
588
     * Request/Response Tracking
589
     *
590
     * Request and response is determined by the tags available in a Roughtime message.
591
     * -> Handled in dissect_roughtime_msg()
592
     *
593
     *   Request:  Never has SREP
594
     *   Response: Has SREP
595
     *
596
     */
597
0
    if(conv_data) {
598
0
        if(conv_data->req_frame > 0 && conv_data->resp_frame > 0 && conv_data->req_frame == pinfo->num) {
599
0
            col_prepend_fstr(pinfo->cinfo, COL_INFO, "%s, ", "Request");
600
0
            ci = proto_tree_add_uint(roughtime_tree, hf_roughtime_response_in, tvb, 0, 0, conv_data->resp_frame);
601
0
            proto_item_set_generated(ci);
602
0
        }
603
0
        else if (conv_data->req_frame > 0 && conv_data->resp_frame > 0 && conv_data->resp_frame == pinfo->num) {
604
0
            col_prepend_fstr(pinfo->cinfo, COL_INFO, "%s, ", "Response");
605
0
            ci = proto_tree_add_uint(roughtime_tree, hf_roughtime_response_to, tvb, 0, 0, conv_data->req_frame);
606
0
            proto_item_set_generated(ci);
607
608
            // The response must be smaller than the request to avoid amplification
609
0
            if( conv_data->req_length > 0 && conv_data->resp_length > 0 &&
610
0
                conv_data->resp_length > conv_data->req_length )
611
0
            {
612
0
                expert_add_info(pinfo, ti, &ei_roughtime_response_too_large);
613
0
            }
614
0
        }
615
0
    }
616
617
0
    proto_item_set_end(ti, tvb, offset);
618
0
    return offset;
619
0
}
620
621
static unsigned
622
get_roughtime_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
623
0
{
624
    /*   Magic Word field     (8 bytes)
625
     * + Message length field (4 bytes)
626
     * ---------------------------------
627
     * = HDR_IETF_MINLEN      (12 bytes)
628
     * + Message length       (var.)
629
     * ---------------------------------
630
     * = PDU length
631
     */
632
0
    return tvb_get_uint32(tvb, offset + 8, ENC_LITTLE_ENDIAN) + HDR_IETF_MINLEN;
633
0
}
634
635
static int
636
dissect_roughtime_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
637
0
{
638
0
    if (!test_ietf_roughtime(tvb))
639
0
        return 0;
640
641
0
    tcp_dissect_pdus(tvb, pinfo, tree, true, HDR_IETF_MINLEN, get_roughtime_pdu_len, dissect_roughtime, data);
642
0
    return tvb_reported_length(tvb);
643
0
}
644
645
void
646
proto_register_roughtime(void)
647
16
{
648
16
    static hf_register_info hf[] = {
649
16
        { &hf_roughtime_proto,
650
16
            { "Protocol", "roughtime.proto",
651
16
            FT_STRING, BASE_NONE,
652
16
            NULL, 0x0,
653
16
            NULL, HFILL }
654
16
        },
655
16
        { &hf_roughtime_hdr,
656
16
            { "Header", "roughtime.hdr",
657
16
            FT_STRING, BASE_NONE,
658
16
            NULL, 0x0,
659
16
            NULL, HFILL }
660
16
        },
661
16
        { &hf_roughtime_msg_len,
662
16
            { "Message Length", "roughtime.msg_len",
663
16
            FT_UINT32, BASE_DEC,
664
16
            NULL, 0x0,
665
16
            NULL, HFILL }
666
16
        },
667
16
        { &hf_roughtime_srep,
668
16
            { "Signed Response", "roughtime.srep",
669
16
            FT_NONE, BASE_NONE,
670
16
            NULL, 0x0,
671
16
            NULL, HFILL }
672
16
        },
673
16
        { &hf_roughtime_cert,
674
16
            { "Certificate", "roughtime.cert",
675
16
            FT_NONE, BASE_NONE,
676
16
            NULL, 0x0,
677
16
            NULL, HFILL }
678
16
        },
679
16
        { &hf_roughtime_dele,
680
16
            { "Delegation", "roughtime.dele",
681
16
            FT_NONE, BASE_NONE,
682
16
            NULL, 0x0,
683
16
            NULL, HFILL }
684
16
        },
685
16
        { &hf_roughtime_num_tags,
686
16
            { "Number of Tags", "roughtime.numtags",
687
16
            FT_UINT32, BASE_DEC,
688
16
            NULL, 0x0,
689
16
            NULL, HFILL }
690
16
        },
691
16
        { &hf_roughtime_offset,
692
16
            { "Value Offset", "roughtime.offset",
693
16
            FT_UINT32, BASE_DEC,
694
16
            NULL, 0x0,
695
16
            NULL, HFILL }
696
16
        },
697
16
        { &hf_roughtime_tag,
698
16
            { "Tag", "roughtime.tag",
699
16
            FT_STRING, BASE_NONE,
700
16
            NULL, 0x0,
701
16
            NULL, HFILL }
702
16
        },
703
16
        { &hf_roughtime_nonce,
704
16
            { "Nonce", "roughtime.nonce",
705
16
            FT_BYTES, BASE_NONE,
706
16
            NULL, 0x0,
707
16
            NULL, HFILL }
708
16
        },
709
16
        { &hf_roughtime_type,
710
16
            { "Type", "roughtime.type",
711
16
            FT_BYTES, BASE_NONE,
712
16
            NULL, 0x0,
713
16
            NULL, HFILL }
714
16
        },
715
16
        { &hf_roughtime_ver,
716
16
            { "Version", "roughtime.ver",
717
16
            FT_UINT32, BASE_HEX | BASE_RANGE_STRING,
718
16
            RVALS(roughtime_version_rvals), 0x0,
719
16
            NULL, HFILL }
720
16
        },
721
16
        { &hf_roughtime_sig,
722
16
            { "Signature", "roughtime.sig",
723
16
            FT_BYTES, BASE_NONE,
724
16
            NULL, 0x0,
725
16
            NULL, HFILL }
726
16
        },
727
16
        { &hf_roughtime_srv,
728
16
            { "Server Key", "roughtime.srv",
729
16
            FT_BYTES, BASE_NONE,
730
16
            NULL, 0x0,
731
16
            NULL, HFILL }
732
16
        },
733
16
        { &hf_roughtime_index,
734
16
            { "Index", "roughtime.index",
735
16
            FT_UINT32, BASE_DEC,
736
16
            NULL, 0x0,
737
16
            NULL, HFILL }
738
16
        },
739
16
        { &hf_roughtime_path,
740
16
            { "Path", "roughtime.path",
741
16
            FT_BYTES, BASE_NONE,
742
16
            NULL, 0x0,
743
16
            NULL, HFILL }
744
16
        },
745
16
        { &hf_roughtime_radius,
746
16
            { "Radius", "roughtime.radius",
747
16
            FT_UINT32, BASE_DEC,
748
16
            NULL, 0x0,
749
16
            NULL, HFILL }
750
16
        },
751
16
        { &hf_roughtime_midp,
752
16
            { "Midpoint", "roughtime.midp",
753
16
            FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC,
754
16
            NULL, 0x0,
755
16
            NULL, HFILL }
756
16
        },
757
16
        { &hf_roughtime_mint,
758
16
            { "Min. Time", "roughtime.mint",
759
16
            FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC,
760
16
            NULL, 0x0,
761
16
            NULL, HFILL }
762
16
        },
763
16
        { &hf_roughtime_maxt,
764
16
            { "Max. Time", "roughtime.maxt",
765
16
            FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC,
766
16
            NULL, 0x0,
767
16
            NULL, HFILL }
768
16
        },
769
16
        { &hf_roughtime_root,
770
16
            { "Root", "roughtime.root",
771
16
            FT_BYTES, BASE_NONE,
772
16
            NULL, 0x0,
773
16
            NULL, HFILL }
774
16
        },
775
16
        { &hf_roughtime_pubk,
776
16
            { "Public Key", "roughtime.pubk",
777
16
            FT_BYTES, BASE_NONE,
778
16
            NULL, 0x0,
779
16
            NULL, HFILL }
780
16
        },
781
16
        { &hf_roughtime_value,
782
16
            { "Value", "roughtime.value",
783
16
            FT_BYTES, BASE_NONE,
784
16
            NULL, 0x0,
785
16
            NULL, HFILL }
786
16
        },
787
16
        { &hf_roughtime_pad,
788
16
            { "Padding", "roughtime.pad",
789
16
            FT_NONE, BASE_NONE,
790
16
            NULL, 0x0,
791
16
            NULL, HFILL }
792
16
        },
793
16
        { &hf_roughtime_time,
794
16
            { "Time", "roughtime.time",
795
16
            FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC,
796
16
            NULL, 0x0,
797
16
            "Midpoint of guaranteed time", HFILL }
798
16
        },
799
16
        { &hf_roughtime_time_low,
800
16
            { "Time (lower)", "roughtime.time.low",
801
16
            FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC,
802
16
            NULL, 0x0,
803
16
            "Calculated lower bound of guaranteed time", HFILL }
804
16
        },
805
16
        { &hf_roughtime_time_up,
806
16
            { "Time (upper)", "roughtime.time.up",
807
16
            FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC,
808
16
            NULL, 0x0,
809
16
            "Calculated upper bound of guaranteed time", HFILL }
810
16
        },
811
16
        { &hf_roughtime_response_in,
812
16
            { "Response In", "roughtime.response_in",
813
16
            FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
814
16
            NULL, HFILL }
815
16
        },
816
16
        { &hf_roughtime_response_to,
817
16
            { "Response To", "roughtime.response_to",
818
16
            FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
819
16
            NULL, HFILL }
820
16
        }
821
16
    };
822
823
16
    static ei_register_info ei[] = {
824
16
        { &ei_roughtime_illegal_length,
825
16
            { "roughtime.length.illegal", PI_MALFORMED, PI_ERROR,
826
16
                "Illegal field length", EXPFILL }
827
16
        },
828
16
        { &ei_roughtime_response_too_large,
829
16
            { "roughtime.response.too_large", PI_MALFORMED, PI_ERROR,
830
16
                "Response is larger than request", EXPFILL }
831
16
        },
832
16
        { &ei_roughtime_nesting_too_deep,
833
16
          { "roughtime.nesting_too_deep", PI_MALFORMED, PI_ERROR,
834
16
            "Roughtime messages nested too deeply", EXPFILL }},
835
16
        { &ei_roughtime_path_too_large,
836
16
            { "roughtime.path.too_large", PI_MALFORMED, PI_ERROR,
837
16
                "Merkle tree height exceeded", EXPFILL }
838
16
        },
839
16
    };
840
841
16
    static int *ett[] = {
842
16
        &ett_roughtime,
843
16
        &ett_roughtime_srep,
844
16
        &ett_roughtime_cert,
845
16
        &ett_roughtime_dele,
846
16
    };
847
848
16
    expert_module_t* expert_roughtime;
849
850
16
    proto_roughtime = proto_register_protocol("Roughtime", "Roughtime", "roughtime");
851
852
16
    proto_register_field_array(proto_roughtime, hf, array_length(hf));
853
16
    proto_register_subtree_array(ett, array_length(ett));
854
855
16
    expert_roughtime = expert_register_protocol(proto_roughtime);
856
16
    expert_register_field_array(expert_roughtime, ei, array_length(ei));
857
858
16
    roughtime_udp_handle = register_dissector("roughtime.udp", dissect_roughtime, proto_roughtime);
859
16
    roughtime_tcp_handle = register_dissector("roughtime.tcp", dissect_roughtime_tcp, proto_roughtime);
860
16
}
861
862
void
863
proto_reg_handoff_roughtime(void)
864
16
{
865
    /*
866
     * Port 5319/tcp/udp is assigned by IANA for IETF's Roughtime.
867
     * Port 2002-2003/udp is commonly used for Google's implementation
868
     * and for IETF-drafts. We may remove or change these default
869
     * port sometime.
870
    .*/
871
16
    dissector_add_uint_range_with_preference("udp.port", "5319,2002-2003", roughtime_udp_handle);
872
16
    dissector_add_uint_range_with_preference("tcp.port", "5319,", roughtime_tcp_handle);
873
16
}
874
875
/*
876
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
877
 *
878
 * Local variables:
879
 * c-basic-offset: 4
880
 * tab-width: 8
881
 * indent-tabs-mode: nil
882
 * End:
883
 *
884
 * vi: set shiftwidth=4 tabstop=8 expandtab:
885
 * :indentSize=4:tabSize=8:noTabs=true:
886
 */