From 3ba56ce586dbad693f1865a7241d4cd1152cd761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=BE=D0=BC=D0=B0=D0=BD=20=D0=94=D0=BE=D0=BD=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=BA=D0=BE?= Date: Mon, 9 Apr 2018 01:15:01 +0300 Subject: wiretap: Add a reader for files in the PEM-like format specified by RFC 7468 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I8109025120d01c915f3a9d5550aa9272ec83893a Reviewed-on: https://code.wireshark.org/review/27334 Petri-Dish: Stig Bjørlykke Reviewed-by: Stig Bjørlykke --- epan/dissectors/packet-ber.c | 2 +- file.c | 3 +- wiretap/CMakeLists.txt | 1 + wiretap/file_access.c | 7 ++ wiretap/pem.c | 203 +++++++++++++++++++++++++++++++++++++++++++ wiretap/pem.h | 27 ++++++ wiretap/wtap.h | 1 + 7 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 wiretap/pem.c create mode 100644 wiretap/pem.h diff --git a/epan/dissectors/packet-ber.c b/epan/dissectors/packet-ber.c index 9f7d358510..03842543a6 100644 --- a/epan/dissectors/packet-ber.c +++ b/epan/dissectors/packet-ber.c @@ -4233,7 +4233,7 @@ dissect_ber(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) col_set_str(pinfo->cinfo, COL_PROTOCOL, "BER"); - col_set_str(pinfo->cinfo, COL_DEF_SRC, "BER encoded file"); + col_set_str(pinfo->cinfo, COL_DEF_SRC, "BER encoded value"); if (!decode_as_syntax) { diff --git a/file.c b/file.c index de1f65af1b..c9b9878f81 100644 --- a/file.c +++ b/file.c @@ -318,7 +318,8 @@ cf_open(capture_file *cf, const char *fname, unsigned int type, gboolean is_temp packet_list_queue_draw(); cf_callback_invoke(cf_cb_file_opened, cf); - if (cf->cd_t == WTAP_FILE_TYPE_SUBTYPE_BER) { + if ((cf->cd_t == WTAP_FILE_TYPE_SUBTYPE_BER) + || (cf->cd_t == WTAP_FILE_TYPE_SUBTYPE_PEM)) { /* tell the BER dissector the file name */ ber_set_filename(cf->filename); } diff --git a/wiretap/CMakeLists.txt b/wiretap/CMakeLists.txt index 35c404155c..3bfc5870b1 100644 --- a/wiretap/CMakeLists.txt +++ b/wiretap/CMakeLists.txt @@ -68,6 +68,7 @@ set(WIRETAP_NONGENERATED_FILES pcapng.c peekclassic.c peektagged.c + pem.c pppdump.c radcom.c snoop.c diff --git a/wiretap/file_access.c b/wiretap/file_access.c index 0c054c4670..2e501dfeef 100644 --- a/wiretap/file_access.c +++ b/wiretap/file_access.c @@ -73,6 +73,7 @@ #include "nettrace_3gpp_32_423.h" #include "mplog.h" #include "dpa400.h" +#include "pem.h" /* * Add an extension, and all compressed versions thereof, to a GSList @@ -353,6 +354,7 @@ static const struct open_info open_info_base[] = { { "MIME Files Format", OPEN_INFO_MAGIC, mime_file_open, NULL, NULL, NULL }, { "Micropross mplog", OPEN_INFO_MAGIC, mplog_open, "mplog", NULL, NULL }, { "Unigraf DPA-400 capture", OPEN_INFO_MAGIC, dpa400_open, "bin", NULL, NULL }, + { "ASN.1 (PEM-like encoding)", OPEN_INFO_MAGIC, pem_open, "pem;crt", NULL, NULL }, { "Novell LANalyzer", OPEN_INFO_HEURISTIC, lanalyzer_open, "tr1", NULL, NULL }, /* * PacketLogger must come before MPEG, because its files @@ -1609,6 +1611,11 @@ static const struct file_type_subtype_info dump_open_table_base[] = { /* WTAP_FILE_TYPE_SUBTYPE_DPA400 */ { "Unigraf DisplayPort AUX channel monitor output parser", "dpa400", "bin", NULL, + FALSE, FALSE, 0, + NULL, NULL, NULL }, + + /* WTAP_FILE_TYPE_SUBTYPE_PEM */ + { "ASN.1 (PEM-like encoding)", "pem", NULL, NULL, FALSE, FALSE, 0, NULL, NULL, NULL } }; diff --git a/wiretap/pem.c b/wiretap/pem.c new file mode 100644 index 0000000000..dd1502795b --- /dev/null +++ b/wiretap/pem.c @@ -0,0 +1,203 @@ +/* pem.c + * + * Implements loading of files in the format specified by RFC 7468. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "pem.h" + +#include "file_wrappers.h" +#include "wtap-int.h" + +#include + +#include + +#include + +/* 128 bytes should be enough to contain any line. Strictly speaking, 64 is + enough, but we provide some leeway to accomodate nonconformant producers and + trailing whitespace. The 2 extra bytes are for the trailing newline and NUL + terminator. */ +#define MAX_LINE_LENGTH (128 + 2) + +/* based on the `label` production in RFC 7468 */ +#define RE_LABEL "([!-,.-~]([-\\s]?[!-,.-~])*)?" + +struct pem_priv { + GRegex *re_blank_line; + GRegex *re_pre_eb; + GRegex *re_post_eb; +}; + +static char *read_complete_text_line(char line[MAX_LINE_LENGTH], FILE_T fh, int *err, gchar **err_info) +{ + char *line_end; + + if (!(line_end = file_getsp(line, MAX_LINE_LENGTH, fh))) { + *err = file_error(fh, err_info); + return NULL; + } + + if (strlen(line) != (size_t)(line_end - line)) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup("unexpected NUL inside a line"); + return NULL; + } + + if (line_end[-1] != '\n' && !file_eof(fh)) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup("overlong line"); + return NULL; + } + + return line_end; +} + +static gboolean pem_read_value(wtap *wth, FILE_T fh, wtap_rec *rec, + Buffer *buf, int *err, gchar **err_info) +{ + struct pem_priv *priv = (struct pem_priv *)wth->priv; + + char line[MAX_LINE_LENGTH]; + + // skip blank lines + do { + if (!read_complete_text_line(line, fh, err, err_info)) return FALSE; + } while (g_regex_match(priv->re_blank_line, line, (GRegexMatchFlags)0, NULL)); + + if (!g_regex_match(priv->re_pre_eb, line, (GRegexMatchFlags)0, NULL)) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup("invalid pre-encapsulation boundary"); + return FALSE; + } + + gint base64_state = 0; + guint base64_save = 0; + + ws_buffer_clean(buf); + + for (; ; ) { + char *line_end = read_complete_text_line(line, fh, err, err_info); + if (!line_end) { + if (*err == 0) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup("missing post-encapsulation boundary"); + } + return FALSE; + } + + if (g_regex_match(priv->re_post_eb, line, (GRegexMatchFlags)0, NULL)) + break; + + guchar decoded[(sizeof(line) / 4) * 3 + 3]; + guint32 decoded_size = (guint32) g_base64_decode_step( + line, line_end - line, decoded, &base64_state, &base64_save); + + if ((guint32)ws_buffer_length(buf) > G_MAXUINT32 - decoded_size) { + // we can't set the packet length if this happens + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup("encoded value too large"); + return FALSE; + } + + ws_buffer_append(buf, decoded, decoded_size); + } + + rec->rec_type = REC_TYPE_PACKET; + rec->presence_flags = 0; + rec->rec_header.packet_header.len = + rec->rec_header.packet_header.caplen = + (guint32)ws_buffer_length(buf); + + return TRUE; +} + +static gboolean pem_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset) +{ + *data_offset = file_tell(wth->fh); + + return pem_read_value(wth, wth->fh, &wth->rec, wth->rec_data, err, err_info); +} + +static gboolean pem_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, + Buffer *buf, int *err, gchar **err_info) +{ + if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) + return FALSE; + + return pem_read_value(wth, wth->random_fh, rec, buf, err, err_info); +} + +static void pem_close(wtap *wth) +{ + struct pem_priv *priv = (struct pem_priv *)wth->priv; + + g_regex_unref(priv->re_pre_eb); + g_regex_unref(priv->re_post_eb); +} + +wtap_open_return_val pem_open(wtap *wth, int *err, gchar **err_info) +{ + static const char expected_magic[] = "-----BEGIN "; + char actual_magic[sizeof(expected_magic) - 1]; + + if (!wtap_read_bytes(wth->fh, &actual_magic, sizeof(actual_magic), err, err_info)) { + if (*err == WTAP_ERR_SHORT_READ) + return WTAP_OPEN_NOT_MINE; + return WTAP_OPEN_ERROR; + } + + if (memcmp(expected_magic, actual_magic, sizeof(actual_magic)) != 0) + return WTAP_OPEN_NOT_MINE; + + if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) + return WTAP_OPEN_ERROR; + + wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_PEM; + wth->file_encap = WTAP_ENCAP_BER; + + wth->snapshot_length = 0; + wth->file_tsprec = WTAP_TSPREC_SEC; + + struct pem_priv *priv = g_new0(struct pem_priv, 1); + + priv->re_blank_line = g_regex_new("^\\s*$", G_REGEX_RAW, (GRegexMatchFlags)0, NULL); + + priv->re_pre_eb = g_regex_new("^-----BEGIN " RE_LABEL "-----\\s*$", + (GRegexCompileFlags)(G_REGEX_RAW | G_REGEX_NO_AUTO_CAPTURE), + (GRegexMatchFlags)0, NULL); + priv->re_post_eb = g_regex_new("^-----END " RE_LABEL "-----\\s*$", + (GRegexCompileFlags)(G_REGEX_RAW | G_REGEX_NO_AUTO_CAPTURE), + (GRegexMatchFlags)0, NULL); + + wth->priv = priv; + + wth->subtype_read = pem_read; + wth->subtype_seek_read = pem_seek_read; + wth->subtype_close = pem_close; + + if (!priv->re_blank_line || !priv->re_pre_eb || !priv->re_post_eb) { + *err = WTAP_ERR_INTERNAL; + *err_info = g_strdup("failed to initialize reader"); + return WTAP_OPEN_ERROR; + } + + return WTAP_OPEN_MINE; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/wiretap/pem.h b/wiretap/pem.h new file mode 100644 index 0000000000..5a6cff58ba --- /dev/null +++ b/wiretap/pem.h @@ -0,0 +1,27 @@ +/* pem.h + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __PEM_H__ +#define __PEM_H__ + +#include +#include "wtap.h" + +wtap_open_return_val pem_open(wtap *wth, int *err, gchar **err_info); + +#endif + +/* +* Editor modelines - http://www.wireshark.org/tools/modelines.html +* +* Local Variables: +* c-basic-offset: 4 +* tab-width: 8 +* indent-tabs-mode: nil +* End: +* +* vi: set shiftwidth=4 tabstop=8 expandtab: +* :indentSize=4:tabSize=8:noTabs=true: +*/ diff --git a/wiretap/wtap.h b/wiretap/wtap.h index d0cc052507..8f3c235990 100644 --- a/wiretap/wtap.h +++ b/wiretap/wtap.h @@ -373,6 +373,7 @@ extern "C" { #define WTAP_FILE_TYPE_SUBTYPE_NETTRACE_3GPP_32_423 79 #define WTAP_FILE_TYPE_SUBTYPE_MPLOG 80 #define WTAP_FILE_TYPE_SUBTYPE_DPA400 81 +#define WTAP_FILE_TYPE_SUBTYPE_PEM 82 #define WTAP_NUM_FILE_TYPES_SUBTYPES wtap_get_num_file_types_subtypes() -- cgit v1.2.3