aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/editcap.pod15
-rw-r--r--editcap.c103
-rw-r--r--test/fixtures_ws.py5
-rw-r--r--test/suite_fileformats.py58
4 files changed, 181 insertions, 0 deletions
diff --git a/doc/editcap.pod b/doc/editcap.pod
index 7f65c6268b..0be66ce95e 100644
--- a/doc/editcap.pod
+++ b/doc/editcap.pod
@@ -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
diff --git a/editcap.c b/editcap.c
index 18523a1c23..389266e05e 100644
--- a/editcap.c
+++ b/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(&params, 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(&params);
if (wth != NULL)
diff --git a/test/fixtures_ws.py b/test/fixtures_ws.py
index 053d89e0ce..df9b9149ac 100644
--- a/test/fixtures_ws.py
+++ b/test/fixtures_ws.py
@@ -112,6 +112,11 @@ def cmd_text2pcap(program):
@fixtures.fixture(scope='session')
+def cmd_editcap(program):
+ return program('editcap')
+
+
+@fixtures.fixture(scope='session')
def cmd_wireshark(program):
return program('wireshark')
diff --git a/test/suite_fileformats.py b/test/suite_fileformats.py
index 66c9880929..8bf341c1cd 100644
--- a/test/suite_fileformats.py
+++ b/test/suite_fileformats.py
@@ -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