1 /* packet-ssyncp.c
2  * Routines for dissecting mosh's State Synchronization Protocol
3  * Copyright 2020 Google LLC
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 
12 /*
13  * State Synchronization Protocol is the protocol used by mosh:
14  * <https://mosh.org/mosh-paper-draft.pdf>
15  *
16  * The protocol name is abbreviated as SSyncP to avoid conflict with the
17  * "Scripting Service Protocol".
18  *
19  * The protocol is based on UDP, with a plaintext header followed by an
20  * encrypted payload. For now we just support decrypting a single connection at
21  * a time, using the MOSH_KEY dumped from the environment variables
22  * (`cat /proc/$pid/environ | tr '\0' '\n' | grep MOSH_KEY` on Linux).
23  * Note that to display the embedded protobuf properly, you'll have to add
24  * src/protobufs/ from mosh's source code to the ProtoBuf search path.
25  * For now we stop decoding after reaching the first level of protobufs; in
26  * them, a second layer of protobufs is sometimes embedded (e.g. for
27  * transmitting screen contents and such). Implementing that is left as an
28  * exercise for the reader.
29  */
30 
31 #include <config.h>
32 
33 #include <epan/packet.h>   /* Should be first Wireshark include (other than config.h) */
34 #include <epan/conversation.h>
35 #include <epan/wmem_scopes.h>
36 #include <epan/proto_data.h>
37 #include <epan/prefs.h>
38 #include <epan/expert.h>
39 #include <wsutil/report_message.h>
40 #include <wsutil/wsgcrypt.h>
41 
42 void proto_reg_handoff_ssyncp(void);
43 void proto_register_ssyncp(void);
44 
45 static int proto_ssyncp = -1;
46 static int hf_ssyncp_direction = -1;
47 static int hf_ssyncp_seq = -1;
48 static int hf_ssyncp_encrypted = -1;
49 static int hf_ssyncp_seq_delta = -1;
50 static int hf_ssyncp_timestamp = -1;
51 static int hf_ssyncp_timestamp_reply = -1;
52 static int hf_ssyncp_frag_seq = -1;
53 static int hf_ssyncp_frag_final = -1;
54 static int hf_ssyncp_frag_idx = -1;
55 static int hf_ssyncp_rtt_to_server = -1;
56 static int hf_ssyncp_rtt_to_client = -1;
57 
58 /* Initialize the subtree pointers */
59 static gint ett_ssyncp = -1;
60 static gint ett_ssyncp_decrypted = -1;
61 
62 static expert_field ei_ssyncp_fragmented = EI_INIT;
63 static expert_field ei_ssyncp_bad_key = EI_INIT;
64 
65 static const char *pref_ssyncp_key;
66 static char ssyncp_raw_aes_key[16];
67 static gboolean have_ssyncp_key;
68 
69 static dissector_handle_t dissector_protobuf;
70 
71 typedef struct _ssyncp_conv_info_t {
72     /* last sequence numbers per direction */
73     guint64 last_seq[2];
74     /* for each direction, have we seen any traffic yet? */
75     gboolean seen_packet[2];
76 
77     guint16 clock_offset[2];
78     gboolean clock_seen[2];
79 } ssyncp_conv_info_t;
80 
81 typedef struct _ssyncp_packet_info_t {
82     gboolean first_packet;
83     gint64 seq_delta;
84     gboolean have_rtt_estimate;
85     gint16 rtt_estimate;
86 } ssyncp_packet_info_t;
87 
88 #define SSYNCP_IV_PAD 4
89 #define SSYNCP_SEQ_LEN 8
90 #define SSYNCP_DATAGRAM_HEADER_LEN (SSYNCP_SEQ_LEN + 2 + 2) /* 64-bit IV and two 16-bit timestamps */
91 #define SSYNCP_TRANSPORT_HEADER_LEN (8 + 2)
92 #define SSYNCP_AUTHTAG_LEN 16 /* 128-bit auth tag */
93 
94 /*
95  * We only match on 60001, which mosh uses for its first connection.
96  * If there are more connections in the range 60002-61000, the user will have to
97  * mark those as ssyncp traffic manually - we'd have too many false positives
98  * otherwise.
99  */
100 #define SSYNCP_UDP_PORT 60001
101 
102 static int
dissect_ssyncp(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)103 dissect_ssyncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
104         void *data _U_)
105 {
106     /* Check that we have at least a datagram plus an OCB auth tag. */
107     if (tvb_reported_length(tvb) < SSYNCP_DATAGRAM_HEADER_LEN + SSYNCP_TRANSPORT_HEADER_LEN + SSYNCP_AUTHTAG_LEN)
108         return 0;
109 
110     guint64 direction_and_seq = tvb_get_guint64(tvb, 0, ENC_BIG_ENDIAN);
111     guint direction = direction_and_seq >> 63;
112     guint64 seq = direction_and_seq & ~(1ULL << 63);
113 
114     /* Heuristic: The 63-bit sequence number starts from zero and increments
115      * from there. Even if you send 1000 packets per second over 10 years, you
116      * won't reach 2^35. So check that the sequence number is not outrageously
117      * high.
118      */
119     if (seq > (1ULL << 35))
120         return 0;
121 
122     /* On the first pass, track the previous sequence numbers per direction,
123      * compute deltas between sequence numbers, and save those deltas.
124      * On subsequent passes, use the computed deltas.
125      */
126     ssyncp_packet_info_t *ssyncp_pinfo;
127     ssyncp_conv_info_t *ssyncp_info = NULL;
128     if (pinfo->fd->visited) {
129         ssyncp_pinfo = (ssyncp_packet_info_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_ssyncp, 0);
130     } else {
131         conversation_t *conversation = find_or_create_conversation(pinfo);
132         ssyncp_info = (ssyncp_conv_info_t *)conversation_get_proto_data(conversation, proto_ssyncp);
133         if (!ssyncp_info) {
134             ssyncp_info = wmem_new(wmem_file_scope(), ssyncp_conv_info_t);
135             conversation_add_proto_data(conversation, proto_ssyncp, ssyncp_info);
136             ssyncp_info->seen_packet[0] = FALSE;
137             ssyncp_info->seen_packet[1] = FALSE;
138             ssyncp_info->clock_seen[0] = FALSE;
139             ssyncp_info->clock_seen[1] = FALSE;
140         }
141 
142         ssyncp_pinfo = wmem_new(wmem_file_scope(), ssyncp_packet_info_t);
143         ssyncp_pinfo->first_packet = !ssyncp_info->seen_packet[direction];
144         if (ssyncp_pinfo->first_packet) {
145             ssyncp_info->seen_packet[direction] = TRUE;
146         } else {
147             ssyncp_pinfo->seq_delta = seq - ssyncp_info->last_seq[direction];
148         }
149         ssyncp_pinfo->have_rtt_estimate = FALSE;
150         p_add_proto_data(wmem_file_scope(), pinfo, proto_ssyncp, 0, ssyncp_pinfo);
151 
152         ssyncp_info->last_seq[direction] = seq;
153     }
154 
155     /*** COLUMN DATA ***/
156 
157     col_set_str(pinfo->cinfo, COL_PROTOCOL, "ssyncp");
158 
159     col_clear(pinfo->cinfo, COL_INFO);
160 
161     char *direction_str = direction ? "Server->Client" : "Client->Server";
162     col_set_str(pinfo->cinfo, COL_INFO, direction_str);
163 
164     /*** PROTOCOL TREE ***/
165 
166     /* create display subtree for the protocol */
167     proto_item *ti = proto_tree_add_item(tree, proto_ssyncp, tvb, 0, -1, ENC_NA);
168 
169     proto_tree *ssyncp_tree = proto_item_add_subtree(ti, ett_ssyncp);
170 
171     /* Add an item to the subtree, see section 1.5 of README.dissector for more
172      * information. */
173     proto_tree_add_item(ssyncp_tree, hf_ssyncp_direction, tvb,
174             0, 1, ENC_BIG_ENDIAN);
175     proto_tree_add_item(ssyncp_tree, hf_ssyncp_seq, tvb,
176             0, 8, ENC_BIG_ENDIAN);
177 #ifdef GCRY_OCB_BLOCK_LEN
178     proto_item *encrypted_item =
179 #endif
180        proto_tree_add_item(ssyncp_tree, hf_ssyncp_encrypted,
181             tvb, 8, -1, ENC_NA);
182 
183     if (!ssyncp_pinfo->first_packet) {
184         proto_item *delta_item =
185                 proto_tree_add_int64(ssyncp_tree, hf_ssyncp_seq_delta, tvb, 0, 0,
186                         ssyncp_pinfo->seq_delta);
187         proto_item_set_generated(delta_item);
188     }
189 
190     unsigned char *decrypted = NULL;
191     guint decrypted_len = 0;
192 
193     /* avoid build failure on ancient libgcrypt without OCB support */
194 #ifdef GCRY_OCB_BLOCK_LEN
195     if (have_ssyncp_key) {
196         gcry_error_t gcry_err;
197 
198         /* try to decrypt the rest of the packet */
199         gcry_cipher_hd_t gcry_hd;
200         gcry_err = gcry_cipher_open(&gcry_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_OCB, 0);
201         if (gcry_err_code(gcry_err)) {
202             /* this shouldn't happen (even if the packet is garbage) */
203             report_failure("ssyncp: unable to initialize cipher???");
204             return tvb_captured_length(tvb);
205         }
206         gcry_err = gcry_cipher_setkey(gcry_hd, ssyncp_raw_aes_key, sizeof(ssyncp_raw_aes_key));
207         if (gcry_err_code(gcry_err)) {
208             /* this shouldn't happen (even if the packet is garbage) */
209             report_failure("ssyncp: unable to set key???");
210             gcry_cipher_close(gcry_hd);
211             return tvb_captured_length(tvb);
212         }
213         char nonce[SSYNCP_IV_PAD + SSYNCP_SEQ_LEN];
214         memset(nonce, 0, SSYNCP_IV_PAD);
215         tvb_memcpy(tvb, nonce + SSYNCP_IV_PAD, 0, SSYNCP_SEQ_LEN);
216         gcry_err = gcry_cipher_setiv(gcry_hd, nonce, sizeof(nonce));
217         if (gcry_err_code(gcry_err)) {
218             /* this shouldn't happen (even if the packet is garbage) */
219             report_failure("ssyncp: unable to set iv???");
220             gcry_cipher_close(gcry_hd);
221             return tvb_captured_length(tvb);
222         }
223         decrypted_len = tvb_captured_length(tvb) - SSYNCP_SEQ_LEN - SSYNCP_AUTHTAG_LEN;
224         decrypted = (unsigned char *)tvb_memdup(pinfo->pool, tvb,
225                     SSYNCP_SEQ_LEN, decrypted_len);
226         gcry_cipher_final(gcry_hd);
227         gcry_err = gcry_cipher_decrypt(gcry_hd, decrypted, decrypted_len, NULL, 0);
228         if (gcry_err_code(gcry_err)) {
229             /* this shouldn't happen (even if the packet is garbage) */
230             report_failure("ssyncp: unable to decrypt???");
231             gcry_cipher_close(gcry_hd);
232             return tvb_captured_length(tvb);
233         }
234         gcry_err = gcry_cipher_checktag(gcry_hd,
235             tvb_get_ptr(tvb, SSYNCP_SEQ_LEN+decrypted_len, SSYNCP_AUTHTAG_LEN),
236             SSYNCP_AUTHTAG_LEN);
237         if (gcry_err_code(gcry_err) && gcry_err_code(gcry_err) != GPG_ERR_CHECKSUM) {
238             /* this shouldn't happen (even if the packet is garbage) */
239             report_failure("ssyncp: unable to check auth tag???");
240             gcry_cipher_close(gcry_hd);
241             return tvb_captured_length(tvb);
242         }
243         if (gcry_err_code(gcry_err)) {
244             /* if the tag is wrong, the key was wrong and the decrypted data is useless */
245             decrypted = NULL;
246             expert_add_info(pinfo, encrypted_item, &ei_ssyncp_bad_key);
247         }
248         gcry_cipher_close(gcry_hd);
249     }
250 #endif
251 
252     if (decrypted) {
253         tvbuff_t *decrypted_tvb = tvb_new_child_real_data(tvb, decrypted, decrypted_len, decrypted_len);
254         add_new_data_source(pinfo, decrypted_tvb, "Decrypted data");
255 
256         if (!pinfo->fd->visited) {
257             guint16 our_clock16 = ((guint64)pinfo->abs_ts.secs * 1000 + pinfo->abs_ts.nsecs / 1000000) & 0xffff;
258             guint16 sender_ts = tvb_get_guint16(decrypted_tvb, 0, ENC_BIG_ENDIAN);
259             guint16 reply_ts = tvb_get_guint16(decrypted_tvb, 2, ENC_BIG_ENDIAN);
260             ssyncp_info->clock_offset[direction] = sender_ts - our_clock16;
261             ssyncp_info->clock_seen[direction] = TRUE;
262             if (reply_ts != 0xffff && ssyncp_info->clock_seen[1-direction]) {
263                 guint16 projected_send_time_our_clock = reply_ts - ssyncp_info->clock_offset[1-direction];
264                 ssyncp_pinfo->rtt_estimate = our_clock16 - projected_send_time_our_clock;
265                 ssyncp_pinfo->have_rtt_estimate = TRUE;
266             }
267         }
268 
269         proto_tree *dec_tree = proto_tree_add_subtree(ssyncp_tree, decrypted_tvb,
270                 0, -1, ett_ssyncp_decrypted, NULL, "Decrypted data");
271 
272         proto_tree_add_item(dec_tree, hf_ssyncp_timestamp, decrypted_tvb,
273                 0, 2, ENC_BIG_ENDIAN);
274         proto_tree_add_item(dec_tree, hf_ssyncp_timestamp_reply, decrypted_tvb,
275                 2, 2, ENC_BIG_ENDIAN);
276 
277         if (ssyncp_pinfo->have_rtt_estimate) {
278             int rtt_id = direction ? hf_ssyncp_rtt_to_server : hf_ssyncp_rtt_to_client;
279             proto_item *rtt_item = proto_tree_add_int(dec_tree, rtt_id, decrypted_tvb, 2, 2, ssyncp_pinfo->rtt_estimate);
280             proto_item_set_generated(rtt_item);
281         }
282 
283         proto_tree_add_item(dec_tree, hf_ssyncp_frag_seq, decrypted_tvb,
284                 4, 8, ENC_BIG_ENDIAN);
285         proto_tree_add_item(dec_tree, hf_ssyncp_frag_final, decrypted_tvb,
286                 12, 2, ENC_BIG_ENDIAN);
287         proto_item *frag_idx_item = proto_tree_add_item(dec_tree,
288                 hf_ssyncp_frag_idx, decrypted_tvb, 12, 2, ENC_BIG_ENDIAN);
289 
290         /* TODO actually handle fragmentation; for now just bail out on fragmentation */
291         if (tvb_get_guint16(decrypted_tvb, 12, ENC_BIG_ENDIAN) != 0x8000) {
292             expert_add_info(pinfo, frag_idx_item, &ei_ssyncp_fragmented);
293             return tvb_captured_length(tvb);
294         }
295 
296         tvbuff_t *inflated_tvb = tvb_child_uncompress(decrypted_tvb, decrypted_tvb, 14, decrypted_len - 14);
297         if (inflated_tvb == NULL)
298             return tvb_captured_length(tvb);
299         add_new_data_source(pinfo, inflated_tvb, "Inflated data");
300 
301         if (dissector_protobuf) {
302             call_dissector_with_data(dissector_protobuf, inflated_tvb, pinfo,
303                     dec_tree, "message,TransportBuffers.Instruction");
304         }
305     }
306 
307     return tvb_captured_length(tvb);
308 }
309 
310 /* Register the protocol with Wireshark.
311  *
312  * This format is required because a script is used to build the C function that
313  * calls all the protocol registration.
314  */
315 void
proto_register_ssyncp(void)316 proto_register_ssyncp(void)
317 {
318     static const true_false_string direction_name = {
319         "Server->Client",
320         "Client->Server"
321     };
322 
323     /* Setup list of header fields  See Section 1.5 of README.dissector for
324      * details. */
325     static hf_register_info hf[] = {
326         { &hf_ssyncp_direction,
327           { "Direction", "ssyncp.direction",
328             FT_BOOLEAN, 8, TFS(&direction_name), 0x80,
329             "Direction of packet", HFILL }
330         },
331         { &hf_ssyncp_seq,
332           { "Sequence number", "ssyncp.seq",
333             FT_UINT64, BASE_HEX, NULL, 0x7fffffffffffffff,
334             "Monotonically incrementing packet sequence number", HFILL }
335         },
336         { &hf_ssyncp_encrypted,
337           { "Encrypted data", "ssyncp.enc_data",
338             FT_BYTES, BASE_NONE, NULL, 0,
339             "Encrypted RTT estimation fields and Transport Layer payload, encrypted with AES-128-OCB",
340             HFILL }
341         },
342         { &hf_ssyncp_seq_delta,
343           { "Sequence number delta", "ssyncp.seq_delta",
344             FT_INT64, BASE_DEC, NULL, 0,
345             "Delta from last sequence number; 1 is normal, 0 is duplicated packet, <0 is reordering, >1 is reordering or packet loss", HFILL }
346         },
347         { &hf_ssyncp_timestamp,
348           { "Truncated timestamp", "ssyncp.timestamp",
349             FT_UINT16, BASE_HEX, NULL, 0,
350             "Low 16 bits of sender's time in milliseconds", HFILL }
351         },
352         { &hf_ssyncp_timestamp_reply,
353           { "Last timestamp received", "ssyncp.timestamp_reply",
354             FT_UINT16, BASE_HEX, NULL, 0,
355             "Low 16 bits of timestamp of last received packet plus time since it was received (for RTT estimation)", HFILL }
356         },
357         { &hf_ssyncp_frag_seq,
358           { "Fragment ID", "ssyncp.frag_seq",
359             FT_UINT64, BASE_HEX, NULL, 0,
360             "Transport-level sequence number, used for fragment reassembly", HFILL }
361         },
362         { &hf_ssyncp_frag_final,
363           { "Final fragment", "ssyncp.frag_final",
364             FT_BOOLEAN, 16, NULL, 0x8000,
365             "Is this the last fragment?", HFILL }
366         },
367         { &hf_ssyncp_frag_idx,
368           { "Fragment Index", "ssyncp.frag_idx",
369             FT_UINT16, BASE_HEX, NULL, 0x7fff,
370             "Index of this fragment in the list of fragments of the transport-level message", HFILL }
371         },
372         { &hf_ssyncp_rtt_to_server,
373           { "RTT estimate to server (in ms)", "ssyncp.rtt_est_to_server",
374             FT_INT16, BASE_DEC, NULL, 0,
375             "Estimated round trip time from point of capture to server", HFILL }
376         },
377         { &hf_ssyncp_rtt_to_client,
378           { "RTT estimate to client (in ms)", "ssyncp.rtt_est_to_client",
379             FT_INT16, BASE_DEC, NULL, 0,
380             "Estimated round trip time from point of capture to client", HFILL }
381         }
382     };
383 
384     /* Setup protocol subtree array */
385     static gint *ett[] = {
386         &ett_ssyncp,
387         &ett_ssyncp_decrypted
388     };
389 
390     /* Setup protocol expert items */
391     static ei_register_info ei[] = {
392         { &ei_ssyncp_fragmented,
393           { "ssyncp.fragmented", PI_REASSEMBLE, PI_WARN,
394             "SSYNCP-level fragmentation, dissector can't handle that", EXPFILL }
395         },
396         { &ei_ssyncp_bad_key,
397           { "ssyncp.badkey", PI_DECRYPTION, PI_WARN,
398             "Encrypted data could not be decrypted with the provided key", EXPFILL }
399         }
400     };
401 
402     /* Register the protocol name and description */
403     proto_ssyncp = proto_register_protocol("State Synchronization Protocol", "SSyncP", "ssyncp");
404 
405     /* Required function calls to register the header fields and subtrees */
406     proto_register_field_array(proto_ssyncp, hf, array_length(hf));
407     proto_register_subtree_array(ett, array_length(ett));
408 
409     expert_module_t *expert_ssyncp = expert_register_protocol(proto_ssyncp);
410     expert_register_field_array(expert_ssyncp, ei, array_length(ei));
411 
412     module_t *ssyncp_module = prefs_register_protocol(proto_ssyncp, proto_reg_handoff_ssyncp);
413 
414     prefs_register_string_preference(ssyncp_module, "key",
415         "ssyncp MOSH_KEY",
416         "MOSH_KEY AES key (from mosh-{client,server} environment variable)",
417         &pref_ssyncp_key);
418 }
419 
420 void
proto_reg_handoff_ssyncp(void)421 proto_reg_handoff_ssyncp(void)
422 {
423     static dissector_handle_t ssyncp_handle;
424     static gboolean initialized = FALSE;
425 
426     if (!initialized) {
427         ssyncp_handle = create_dissector_handle(dissect_ssyncp, proto_ssyncp);
428         dissector_add_uint("udp.port", SSYNCP_UDP_PORT, ssyncp_handle);
429         initialized = TRUE;
430     }
431 
432     dissector_protobuf = find_dissector("protobuf");
433     if (dissector_protobuf == NULL) {
434         report_failure("unable to find protobuf dissector");
435     }
436 
437     have_ssyncp_key = FALSE;
438     if (strlen(pref_ssyncp_key) != 0) {
439         if (strlen(pref_ssyncp_key) != 22) {
440             report_failure("ssyncp: invalid key, must be 22 characters long");
441             return;
442         }
443         char base64_key[25];
444         memcpy(base64_key, pref_ssyncp_key, 22);
445         memcpy(base64_key+22, "==\0", 3);
446         gsize out_len;
447         if (g_base64_decode_inplace(base64_key, &out_len) == NULL || out_len != sizeof(ssyncp_raw_aes_key)) {
448             report_failure("ssyncp: invalid key, base64 decoding (with \"==\" appended) failed");
449             return;
450         }
451         memcpy(ssyncp_raw_aes_key, base64_key, sizeof(ssyncp_raw_aes_key));
452         have_ssyncp_key = TRUE;
453     }
454 }
455 
456 /*
457  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
458  *
459  * Local variables:
460  * c-basic-offset: 4
461  * tab-width: 8
462  * indent-tabs-mode: nil
463  * End:
464  *
465  * vi: set shiftwidth=4 tabstop=8 expandtab:
466  * :indentSize=4:tabSize=8:noTabs=true:
467  */
468