forked from osmocom/wireshark
editcap: add --inject-secrets option
Add a new option to insert decryption secrets into a pcapng file. Change-Id: I0e024585cac9a8a328e88d32f9eb03d37d350e2a Ping-Bug: 15252 Reviewed-on: https://code.wireshark.org/review/30693 Petri-Dish: Peter Wu <peter@lekensteyn.nl> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
parent
52a6671439
commit
e2e0fd1dbd
|
@ -28,6 +28,7 @@ S<[ B<-S> E<lt>strict time adjustmentE<gt> ]>
|
|||
S<[ B<-t> E<lt>time adjustmentE<gt> ]>
|
||||
S<[ B<-T> E<lt>encapsulation typeE<gt> ]>
|
||||
S<[ B<-v> ]>
|
||||
S<[ B<--inject-secrets> E<lt>secrets typeE<gt>,E<lt>fileE<gt> ]>
|
||||
I<infile>
|
||||
I<outfile>
|
||||
S<[ I<packet#>[-I<packet#>] ... ]>
|
||||
|
@ -335,6 +336,20 @@ NOTE: The B<-w> option assumes that the packets are in chronological order.
|
|||
If the packets are NOT in chronological order then the B<-w> duplication
|
||||
removal option may not identify some duplicates.
|
||||
|
||||
=item --inject-secrets E<lt>secrets typeE<gt>,E<lt>fileE<gt>
|
||||
|
||||
Inserts the contents of E<lt>fileE<gt> into a Decryption Secrets Block (DSB)
|
||||
within the pcapng output file. This enables decryption without requiring
|
||||
additional configuration in protocol preferences.
|
||||
|
||||
The file format is described by E<lt>secrets typeE<gt> which can be one of:
|
||||
|
||||
I<tls> TLS Key Log as described at
|
||||
L<https://developer.mozilla.org/NSS_Key_Log_Format>
|
||||
|
||||
This option may be specified multiple times. The available options for
|
||||
E<lt>secrets typeE<gt> can be listed with B<--inject-secrets help>.
|
||||
|
||||
=back
|
||||
|
||||
=head1 EXAMPLES
|
||||
|
|
103
editcap.c
103
editcap.c
|
@ -43,6 +43,7 @@
|
|||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#include <wiretap/secrets-types.h>
|
||||
#include <wiretap/wtap.h>
|
||||
|
||||
#include "epan/etypes.h"
|
||||
|
@ -175,6 +176,13 @@ static int do_strict_time_adjustment = FALSE;
|
|||
static struct time_adjustment strict_time_adj = {NSTIME_INIT_ZERO, 0}; /* strict time adjustment */
|
||||
static nstime_t previous_time = NSTIME_INIT_ZERO; /* previous time */
|
||||
|
||||
static const struct {
|
||||
const char *str;
|
||||
guint32 id;
|
||||
} secrets_types[] = {
|
||||
{ "tls", SECRETS_TYPE_TLS },
|
||||
};
|
||||
|
||||
static int find_dct2000_real_data(guint8 *buf);
|
||||
static void handle_chopping(chop_t chop, wtap_packet_header *out_phdr,
|
||||
const wtap_packet_header *in_phdr, guint8 **buf,
|
||||
|
@ -900,6 +908,25 @@ list_encap_types(FILE *stream) {
|
|||
g_free(encaps);
|
||||
}
|
||||
|
||||
static void
|
||||
list_secrets_types(FILE *stream)
|
||||
{
|
||||
for (guint i = 0; i < G_N_ELEMENTS(secrets_types); i++) {
|
||||
fprintf(stream, " %s\n", secrets_types[i].str);
|
||||
}
|
||||
}
|
||||
|
||||
static guint32
|
||||
lookup_secrets_type(const char *type)
|
||||
{
|
||||
for (guint i = 0; i < G_N_ELEMENTS(secrets_types); i++) {
|
||||
if (!strcmp(secrets_types[i].str, type)) {
|
||||
return secrets_types[i].id;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
framenum_compare(gconstpointer a, gconstpointer b, gpointer user_data _U_)
|
||||
{
|
||||
|
@ -965,6 +992,7 @@ real_main(int argc, char *argv[])
|
|||
{"novlan", no_argument, NULL, 0x8100},
|
||||
{"skip-radiotap-header", no_argument, NULL, 0x8101},
|
||||
{"seed", required_argument, NULL, 0x8102},
|
||||
{"inject-secrets", required_argument, NULL, 0x8103},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"version", no_argument, NULL, 'V'},
|
||||
{0, 0, 0, 0 }
|
||||
|
@ -992,6 +1020,8 @@ real_main(int argc, char *argv[])
|
|||
gchar *fsuffix = NULL;
|
||||
guint32 change_offset = 0;
|
||||
guint max_packet_number = 0;
|
||||
GArray *dsb_types = NULL;
|
||||
GPtrArray *dsb_filenames = NULL;
|
||||
const wtap_rec *rec;
|
||||
wtap_rec temp_rec;
|
||||
wtap_dump_params params = WTAP_DUMP_PARAMS_INIT;
|
||||
|
@ -1073,6 +1103,36 @@ real_main(int argc, char *argv[])
|
|||
break;
|
||||
}
|
||||
|
||||
case 0x8103: /* --inject-secrets */
|
||||
{
|
||||
guint32 secrets_type_id = 0;
|
||||
const char *secrets_filename = NULL;
|
||||
if (strcmp("help", optarg) == 0) {
|
||||
list_secrets_types(stdout);
|
||||
goto clean_exit;
|
||||
}
|
||||
gchar **splitted = g_strsplit(optarg, ",", 2);
|
||||
if (splitted[0]) {
|
||||
secrets_type_id = lookup_secrets_type(splitted[0]);
|
||||
secrets_filename = splitted[1];
|
||||
}
|
||||
|
||||
if (secrets_type_id == 0) {
|
||||
fprintf(stderr, "editcap: \"%s\" isn't a valid secrets type\n", secrets_filename);
|
||||
g_strfreev(splitted);
|
||||
ret = INVALID_OPTION;
|
||||
goto clean_exit;
|
||||
}
|
||||
if (!dsb_filenames) {
|
||||
dsb_types = g_array_new(FALSE, FALSE, sizeof(guint32));
|
||||
dsb_filenames = g_ptr_array_new_with_free_func(g_free);
|
||||
}
|
||||
g_array_append_val(dsb_types, secrets_type_id);
|
||||
g_ptr_array_add(dsb_filenames, g_strdup(secrets_filename));
|
||||
g_strfreev(splitted);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'a':
|
||||
{
|
||||
guint frame_number;
|
||||
|
@ -1400,6 +1460,45 @@ real_main(int argc, char *argv[])
|
|||
|
||||
wtap_dump_params_init(¶ms, wth);
|
||||
|
||||
if (dsb_filenames) {
|
||||
for (guint k = 0; k < dsb_filenames->len; k++) {
|
||||
guint32 secrets_type_id = g_array_index(dsb_types, guint32, k);
|
||||
const char *secrets_filename = (const char *)g_ptr_array_index(dsb_filenames, k);
|
||||
char *data;
|
||||
gsize data_len;
|
||||
wtap_block_t block;
|
||||
wtapng_dsb_mandatory_t *dsb;
|
||||
GError *err = NULL;
|
||||
|
||||
if (!g_file_get_contents(secrets_filename, &data, &data_len, &err)) {
|
||||
fprintf(stderr, "editcap: \"%s\" could not be read: %s\n", secrets_filename, err->message);
|
||||
g_clear_error(&err);
|
||||
ret = INVALID_OPTION;
|
||||
goto clean_exit;
|
||||
}
|
||||
if (data_len == 0) {
|
||||
fprintf(stderr, "editcap: \"%s\" is an empty file, ignoring\n", secrets_filename);
|
||||
g_free(data);
|
||||
continue;
|
||||
}
|
||||
if (data_len >= G_MAXINT) {
|
||||
fprintf(stderr, "editcap: \"%s\" is too large, ignoring\n", secrets_filename);
|
||||
g_free(data);
|
||||
continue;
|
||||
}
|
||||
|
||||
block = wtap_block_create(WTAP_BLOCK_DSB);
|
||||
dsb = (wtapng_dsb_mandatory_t *)wtap_block_get_mandatory_data(block);
|
||||
dsb->secrets_type = secrets_type_id;
|
||||
dsb->secrets_len = (guint)data_len;
|
||||
dsb->secrets_data = data;
|
||||
if (params.dsbs_initial == NULL) {
|
||||
params.dsbs_initial = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
|
||||
}
|
||||
g_array_append_val(params.dsbs_initial, block);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If an encapsulation type was specified, override the encapsulation
|
||||
* type of the input file.
|
||||
|
@ -1935,6 +2034,10 @@ real_main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
clean_exit:
|
||||
if (dsb_filenames) {
|
||||
g_array_free(dsb_types, TRUE);
|
||||
g_ptr_array_free(dsb_filenames, TRUE);
|
||||
}
|
||||
g_free(params.idb_inf);
|
||||
wtap_dump_params_cleanup(¶ms);
|
||||
if (wth != NULL)
|
||||
|
|
|
@ -111,6 +111,11 @@ def cmd_text2pcap(program):
|
|||
return program('text2pcap')
|
||||
|
||||
|
||||
@fixtures.fixture(scope='session')
|
||||
def cmd_editcap(program):
|
||||
return program('editcap')
|
||||
|
||||
|
||||
@fixtures.fixture(scope='session')
|
||||
def cmd_wireshark(program):
|
||||
return program('wireshark')
|
||||
|
|
|
@ -157,6 +157,64 @@ class case_fileformat_pcapng_dsb(subprocesstest.SubprocessTestCase):
|
|||
(0x544c534b, len(dsb2_contents), dsb2_contents),
|
||||
))
|
||||
|
||||
def test_pcapng_dsb_2(self, cmd_editcap, dirs, capture_file, check_pcapng_dsb_fields):
|
||||
'''Insert a single DSB into a pcapng file.'''
|
||||
key_file = os.path.join(dirs.key_dir, 'dhe1_keylog.dat')
|
||||
outfile = self.filename_from_id('dhe1-dsb.pcapng')
|
||||
self.runProcess((cmd_editcap,
|
||||
'--inject-secrets', 'tls,%s' % key_file,
|
||||
capture_file('dhe1.pcapng.gz'), outfile
|
||||
))
|
||||
with open(key_file, 'rb') as f:
|
||||
keylog_contents = f.read()
|
||||
check_pcapng_dsb_fields(outfile, (
|
||||
(0x544c534b, len(keylog_contents), keylog_contents),
|
||||
))
|
||||
|
||||
def test_pcapng_dsb_3(self, cmd_editcap, dirs, capture_file, check_pcapng_dsb_fields):
|
||||
'''Insert two DSBs into a pcapng file.'''
|
||||
key_file1 = os.path.join(dirs.key_dir, 'dhe1_keylog.dat')
|
||||
key_file2 = os.path.join(dirs.key_dir, 'http2-data-reassembly.keys')
|
||||
outfile = self.filename_from_id('dhe1-dsb.pcapng')
|
||||
self.runProcess((cmd_editcap,
|
||||
'--inject-secrets', 'tls,%s' % key_file1,
|
||||
'--inject-secrets', 'tls,%s' % key_file2,
|
||||
capture_file('dhe1.pcapng.gz'), outfile
|
||||
))
|
||||
with open(key_file1, 'rb') as f:
|
||||
keylog1_contents = f.read()
|
||||
with open(key_file2, 'rb') as f:
|
||||
keylog2_contents = f.read()
|
||||
check_pcapng_dsb_fields(outfile, (
|
||||
(0x544c534b, len(keylog1_contents), keylog1_contents),
|
||||
(0x544c534b, len(keylog2_contents), keylog2_contents),
|
||||
))
|
||||
|
||||
def test_pcapng_dsb_4(self, cmd_editcap, dirs, capture_file, check_pcapng_dsb_fields):
|
||||
'''Insert a single DSB into a pcapng file with existing DSBs.'''
|
||||
dsb_keys1 = os.path.join(dirs.key_dir, 'tls12-dsb-1.keys')
|
||||
dsb_keys2 = os.path.join(dirs.key_dir, 'tls12-dsb-2.keys')
|
||||
key_file = os.path.join(dirs.key_dir, 'dhe1_keylog.dat')
|
||||
outfile = self.filename_from_id('tls12-dsb-extra.pcapng')
|
||||
self.runProcess((cmd_editcap,
|
||||
'--inject-secrets', 'tls,%s' % key_file,
|
||||
capture_file('tls12-dsb.pcapng'), outfile
|
||||
))
|
||||
with open(dsb_keys1, 'r') as f:
|
||||
dsb1_contents = f.read().encode('utf8')
|
||||
with open(dsb_keys2, 'r') as f:
|
||||
dsb2_contents = f.read().encode('utf8')
|
||||
with open(key_file, 'rb') as f:
|
||||
keylog_contents = f.read()
|
||||
# New DSBs are inserted before the first record. Due to the current
|
||||
# implementation, this is inserted before other (existing) DSBs. This
|
||||
# might change in the future if it is deemed more logical.
|
||||
check_pcapng_dsb_fields(outfile, (
|
||||
(0x544c534b, len(keylog_contents), keylog_contents),
|
||||
(0x544c534b, len(dsb1_contents), dsb1_contents),
|
||||
(0x544c534b, len(dsb2_contents), dsb2_contents),
|
||||
))
|
||||
|
||||
|
||||
@fixtures.mark_usefixtures('test_env')
|
||||
@fixtures.uses_fixtures
|
||||
|
|
Loading…
Reference in New Issue