2018-08-01 04:57:42 +00:00
|
|
|
/* file-rfc7468.c
|
|
|
|
* Routines for dissection of files in the format specified by RFC 7468.
|
|
|
|
*
|
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
|
|
* Copyright 1998 Gerald Combs
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <epan/packet.h>
|
|
|
|
|
|
|
|
void proto_register_rfc7468(void);
|
|
|
|
void proto_reg_handoff_rfc7468(void);
|
|
|
|
|
|
|
|
static int proto_rfc7468 = -1;
|
|
|
|
|
|
|
|
static gint ett_rfc7468 = -1;
|
|
|
|
static gint ett_rfc7468_preeb = -1;
|
|
|
|
static gint ett_rfc7468_data = -1;
|
|
|
|
static gint ett_rfc7468_posteb = -1;
|
|
|
|
|
|
|
|
static int hf_rfc7468_preeb_label = -1;
|
2018-08-01 05:54:51 +00:00
|
|
|
static int hf_rfc7468_ber_data = -1;
|
2018-08-01 04:57:42 +00:00
|
|
|
static int hf_rfc7468_posteb_label = -1;
|
|
|
|
|
|
|
|
static dissector_handle_t ber_handle = NULL;
|
|
|
|
|
|
|
|
static dissector_table_t rfc7468_label_table;
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
line_is_eb(const guchar *line, int linelen, const char *prefix,
|
|
|
|
size_t prefixlen, const guchar **labelpp, int *labellenp)
|
|
|
|
{
|
|
|
|
static const char suffix[] = "-----";
|
|
|
|
#define suffixlen (sizeof suffix - 1)
|
|
|
|
const guchar *labelp;
|
|
|
|
int labellen;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is this line an encapulation boundary of the type specified by the
|
|
|
|
* prefix?
|
|
|
|
*
|
|
|
|
* First, it must be big enough to include the prefix at the beginning
|
|
|
|
* and the suffix at the end.
|
|
|
|
*/
|
|
|
|
if ((size_t)linelen < prefixlen + suffixlen) {
|
|
|
|
/*
|
|
|
|
* No - it's too short.
|
|
|
|
*/
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is, but it must begin with the prefix.
|
|
|
|
*/
|
|
|
|
if (memcmp(line, prefix, prefixlen) != 0) {
|
|
|
|
/*
|
|
|
|
* No - it doesn't begin with the prefix.
|
|
|
|
*/
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It does, but it must also end with the suffix.
|
|
|
|
*/
|
|
|
|
if (memcmp(line + linelen - suffixlen, suffix, suffixlen) != 0) {
|
|
|
|
/*
|
|
|
|
* No - it doesn't end with the suffix.
|
|
|
|
*/
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It begins with the prefix and ends with the suffix. Check
|
|
|
|
* the label, if there is one.
|
|
|
|
*/
|
|
|
|
labelp = line + prefixlen;
|
|
|
|
labellen = (int)(linelen - (prefixlen + suffixlen));
|
|
|
|
*labelpp = labelp;
|
|
|
|
*labellenp = labellen;
|
|
|
|
if (labellen == 0) {
|
|
|
|
/* The label is empty. */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The first character of the label must be 0x21-0x2C or 0x2E-0x7F,
|
|
|
|
* i.e., printable ASCII other than SP or '-'.
|
|
|
|
*/
|
|
|
|
if (*labelp == ' ' || *labelp == '-')
|
|
|
|
return FALSE;
|
|
|
|
labelp++;
|
|
|
|
labellen--;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The rest of the characters must be printable ASCII.
|
|
|
|
*/
|
|
|
|
for (int i = 0; i < labellen; i++, labelp++) {
|
|
|
|
if (*labelp < 0x20 || *labelp > 0x7E) {
|
|
|
|
/* Not printable ASCII. */
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
line_is_blank(const guchar *line, int linelen)
|
|
|
|
{
|
|
|
|
const guchar *p;
|
|
|
|
|
|
|
|
p = line;
|
|
|
|
for (int i = 0; i < linelen; i++, p++) {
|
|
|
|
if (*p != ' ' && *p != '\t') {
|
|
|
|
/* Not space or tab */
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char preeb_prefix[] = "-----BEGIN ";
|
|
|
|
#define preeb_prefix_len (sizeof preeb_prefix - 1)
|
|
|
|
static const char posteb_prefix[] = "-----END ";
|
|
|
|
#define posteb_prefix_len (sizeof posteb_prefix - 1)
|
|
|
|
|
|
|
|
static gint
|
|
|
|
dissect_rfc7468(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
|
|
|
{
|
|
|
|
int offset;
|
|
|
|
int linelen;
|
|
|
|
int next_offset;
|
|
|
|
const guchar *line;
|
|
|
|
const guchar *labelp;
|
|
|
|
int labellen;
|
|
|
|
char *label;
|
|
|
|
proto_tree *rfc7468_tree, *preeb_tree, *posteb_tree;
|
|
|
|
proto_item *rfc7468_item, *ti;
|
|
|
|
|
|
|
|
offset = 0;
|
|
|
|
rfc7468_item = proto_tree_add_item(tree, proto_rfc7468, tvb, offset, -1, ENC_NA);
|
|
|
|
rfc7468_tree = proto_item_add_subtree(rfc7468_item, ett_rfc7468);
|
|
|
|
|
2018-08-01 09:14:30 +00:00
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "rfc7468");
|
|
|
|
|
2018-08-01 04:57:42 +00:00
|
|
|
/*
|
|
|
|
* First, process the text lines prior to the pre-encapsulation
|
|
|
|
* boundary; they're explanatory text lines.
|
|
|
|
*/
|
|
|
|
for (;;) {
|
|
|
|
linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
|
|
|
|
if (linelen == -1) {
|
2018-08-18 07:33:14 +00:00
|
|
|
/* No complete line was found. Nothing more to do. */
|
|
|
|
return tvb_captured_length(tvb);
|
2018-08-01 04:57:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get a buffer that refers to the line.
|
|
|
|
*
|
|
|
|
* Note that "tvb_find_line_end()" will return a value that
|
|
|
|
* is not longer than what's in the buffer, so the
|
|
|
|
* "tvb_get_ptr()" call won't throw an exception.
|
|
|
|
*/
|
|
|
|
line = tvb_get_ptr(tvb, offset, linelen);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is this line a pre-encapulation boundary?
|
|
|
|
*/
|
|
|
|
if (line_is_eb(line, linelen, preeb_prefix, sizeof preeb_prefix - 1,
|
|
|
|
&labelp, &labellen)) {
|
|
|
|
/*
|
|
|
|
* Yes - we're finished with the explanatory text lines.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add this line to the dissection.
|
|
|
|
*/
|
|
|
|
proto_tree_add_format_text(rfc7468_tree, tvb, offset, next_offset - offset);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Step to the next line.
|
|
|
|
*/
|
|
|
|
offset = next_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This line is the pre-encapsulation boundary.
|
|
|
|
* Put it into the protocol tree, and create a subtree under it.
|
|
|
|
*/
|
|
|
|
ti = proto_tree_add_format_text(rfc7468_tree, tvb, offset, next_offset - offset);
|
|
|
|
preeb_tree = proto_item_add_subtree(ti, ett_rfc7468_preeb);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract the label, and put it in that subtree.
|
|
|
|
*/
|
|
|
|
label = wmem_strndup(wmem_packet_scope(), labelp, labellen);
|
|
|
|
proto_tree_add_item(preeb_tree, hf_rfc7468_preeb_label, tvb,
|
2018-09-09 18:56:31 +00:00
|
|
|
offset + (int)preeb_prefix_len, labellen, ENC_ASCII|ENC_NA);
|
2018-08-01 04:57:42 +00:00
|
|
|
|
2018-08-01 09:14:30 +00:00
|
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "Label: %s", label);
|
|
|
|
|
2018-08-01 04:57:42 +00:00
|
|
|
/*
|
|
|
|
* Step to the next line.
|
|
|
|
*/
|
|
|
|
offset = next_offset;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Skip over any blank lines before the base64 information.
|
|
|
|
*/
|
|
|
|
for (;;) {
|
|
|
|
linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
|
|
|
|
if (linelen == -1) {
|
|
|
|
/* No complete line was found. We're done. */
|
|
|
|
return tvb_captured_length(tvb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get a buffer that refers to the line.
|
|
|
|
*
|
|
|
|
* Note that "tvb_find_line_end()" will return a value that
|
|
|
|
* is not longer than what's in the buffer, so the
|
|
|
|
* "tvb_get_ptr()" call won't throw an exception.
|
|
|
|
*/
|
|
|
|
line = tvb_get_ptr(tvb, offset, linelen);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is the line entirely blank (space or tab)?
|
|
|
|
*/
|
|
|
|
if (!line_is_blank(line, linelen)) {
|
|
|
|
/*
|
|
|
|
* No.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add this line to the dissection.
|
|
|
|
*/
|
|
|
|
proto_tree_add_format_text(rfc7468_tree, tvb, offset, next_offset - offset);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Step to the next line.
|
|
|
|
*/
|
|
|
|
offset = next_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, this should be base64-encoded binary data.
|
|
|
|
*/
|
|
|
|
guint8 *databuf = NULL;
|
|
|
|
gsize databufsize = 0;
|
|
|
|
gint base64_state = 0;
|
|
|
|
guint base64_save = 0;
|
|
|
|
guint datasize = 0;
|
|
|
|
for (;;) {
|
|
|
|
linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
|
|
|
|
if (linelen == -1) {
|
|
|
|
/*
|
2018-08-18 07:33:14 +00:00
|
|
|
* No complete line was found. Nothing more to do.
|
2018-08-01 04:57:42 +00:00
|
|
|
*/
|
|
|
|
return tvb_captured_length(tvb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get a buffer that refers to the line.
|
|
|
|
*
|
|
|
|
* Note that "tvb_find_line_end()" will return a value that
|
|
|
|
* is not longer than what's in the buffer, so the
|
|
|
|
* "tvb_get_ptr()" call won't throw an exception.
|
|
|
|
*/
|
|
|
|
line = tvb_get_ptr(tvb, offset, linelen);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is this line a post-encapulation boundary?
|
|
|
|
*/
|
|
|
|
if (line_is_eb(line, linelen, posteb_prefix, sizeof posteb_prefix - 1,
|
|
|
|
&labelp, &labellen)) {
|
|
|
|
/*
|
|
|
|
* Yes - we're done with the base64 data.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add this line to the dissection.
|
|
|
|
*/
|
|
|
|
proto_tree_add_format_text(rfc7468_tree, tvb, offset, next_offset - offset);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode it and add that to the buffer.
|
|
|
|
* First, grow the buffer as needed.
|
|
|
|
*/
|
|
|
|
databufsize += (linelen / 4) * 3 + 3;
|
2018-12-19 07:17:33 +00:00
|
|
|
databuf = (guint8 *)wmem_realloc(pinfo->pool, databuf, databufsize);
|
2018-08-01 04:57:42 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now decode into it.
|
|
|
|
*/
|
|
|
|
guint decodesize = (guint)g_base64_decode_step(line, linelen,
|
|
|
|
&databuf[datasize],
|
|
|
|
&base64_state,
|
|
|
|
&base64_save);
|
|
|
|
datasize += decodesize;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Step to the next line.
|
|
|
|
*/
|
|
|
|
offset = next_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make a tvbuff for the data, and put it into the protocol tree,
|
|
|
|
* if we have any.
|
|
|
|
*/
|
|
|
|
if (datasize != 0) {
|
|
|
|
tvbuff_t *data_tvb;
|
|
|
|
|
|
|
|
data_tvb = tvb_new_child_real_data(tvb, databuf, datasize, datasize);
|
|
|
|
add_new_data_source(pinfo, data_tvb, "Base64-encoded data");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to decode it based on the label.
|
|
|
|
*/
|
|
|
|
if (dissector_try_string(rfc7468_label_table, label, data_tvb, pinfo,
|
2018-08-01 09:14:30 +00:00
|
|
|
tree, NULL) == 0) {
|
2018-08-01 05:54:51 +00:00
|
|
|
proto_tree *data_tree;
|
|
|
|
|
2018-08-01 04:57:42 +00:00
|
|
|
/*
|
|
|
|
* No known dissector; decode it as BER.
|
|
|
|
*/
|
2018-08-01 09:14:30 +00:00
|
|
|
ti = proto_tree_add_item(tree, hf_rfc7468_ber_data, data_tvb, 0, -1, ENC_NA);
|
2018-08-01 05:54:51 +00:00
|
|
|
data_tree = proto_item_add_subtree(ti, ett_rfc7468_data);
|
2018-08-01 04:57:42 +00:00
|
|
|
call_dissector(ber_handle, data_tvb, pinfo, data_tree);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This line is the post-encapsulation boundary.
|
|
|
|
* Put it into the protocol tree, and create a subtree under it.
|
|
|
|
*/
|
|
|
|
ti = proto_tree_add_format_text(rfc7468_tree, tvb, offset, next_offset - offset);
|
|
|
|
posteb_tree = proto_item_add_subtree(ti, ett_rfc7468_posteb);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract the label, and put it in that subtree.
|
|
|
|
*/
|
|
|
|
proto_tree_add_item(posteb_tree, hf_rfc7468_posteb_label, tvb,
|
2018-09-09 18:56:31 +00:00
|
|
|
offset + (int)posteb_prefix_len, labellen, ENC_ASCII|ENC_NA);
|
2018-08-01 04:57:42 +00:00
|
|
|
|
|
|
|
return tvb_captured_length(tvb);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Arbitrary value - we don't want to read all of a huge non-RFC 7468 file
|
|
|
|
// only to find no pre-encapsulation boundary.
|
|
|
|
//
|
|
|
|
#define MAX_EXPLANATORY_TEXT_LINES 20
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
dissect_rfc7468_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
|
|
|
{
|
|
|
|
int offset;
|
|
|
|
int linelen;
|
|
|
|
int next_offset;
|
|
|
|
const guchar *line;
|
|
|
|
const guchar *labelp;
|
|
|
|
int labellen;
|
|
|
|
gboolean found = FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look for a pre-encapsulation boundary.
|
|
|
|
* Process up to MAX_EXPLANATORY_TEXT_LINES worth of lines that don't
|
|
|
|
* look like pre-encapsulation boundaries.
|
|
|
|
*/
|
|
|
|
offset = 0;
|
|
|
|
for (unsigned int i = 0; i < MAX_EXPLANATORY_TEXT_LINES; i++) {
|
|
|
|
linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
|
|
|
|
if (linelen == -1) {
|
|
|
|
/*
|
|
|
|
* No complete line was found; we ran out of file data
|
|
|
|
* and didn't find a pre-encapsulation boundary, so this
|
|
|
|
* isn't an RFC 7468 file.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get a buffer that refers to the line.
|
|
|
|
*
|
|
|
|
* Note that "tvb_find_line_end()" will return a value that
|
|
|
|
* is not longer than what's in the buffer, so the
|
|
|
|
* "tvb_get_ptr()" call won't throw an exception.
|
|
|
|
*/
|
|
|
|
line = tvb_get_ptr(tvb, offset, linelen);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is this line a pre-encapulation boundary?
|
|
|
|
*/
|
|
|
|
if (line_is_eb(line, linelen, preeb_prefix, sizeof preeb_prefix - 1,
|
|
|
|
&labelp, &labellen)) {
|
|
|
|
/*
|
|
|
|
* Yes - we're done looking.
|
|
|
|
*/
|
|
|
|
found = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Step to the next line.
|
|
|
|
*/
|
|
|
|
offset = next_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Did we find a pre-encapsulation boundary?
|
|
|
|
*/
|
|
|
|
if (!found)
|
|
|
|
return FALSE; /* no */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, it's an RFC 7468 file. Dissect it.
|
|
|
|
*/
|
|
|
|
dissect_rfc7468(tvb, pinfo, tree, data);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
proto_register_rfc7468(void)
|
|
|
|
{
|
|
|
|
static hf_register_info hf[] = {
|
|
|
|
{ &hf_rfc7468_preeb_label,
|
|
|
|
{ "Pre-encapsulation boundary label", "rfc7468.preeb_label", FT_STRING, BASE_NONE,
|
|
|
|
NULL, 0, NULL, HFILL } },
|
2018-08-01 05:54:51 +00:00
|
|
|
{ &hf_rfc7468_ber_data,
|
|
|
|
{ "BER data", "rfc7468.ber_data", FT_NONE, BASE_NONE,
|
2018-08-01 04:57:42 +00:00
|
|
|
NULL, 0, NULL, HFILL } },
|
|
|
|
{ &hf_rfc7468_posteb_label,
|
|
|
|
{ "Post-encapsulation boundary label", "rfc7468.posteb_label", FT_STRING, BASE_NONE,
|
|
|
|
NULL, 0, NULL, HFILL } },
|
|
|
|
};
|
|
|
|
|
|
|
|
static gint *ett[] = {
|
|
|
|
&ett_rfc7468,
|
|
|
|
&ett_rfc7468_preeb,
|
|
|
|
&ett_rfc7468_data,
|
|
|
|
&ett_rfc7468_posteb
|
|
|
|
};
|
|
|
|
|
|
|
|
proto_rfc7468 = proto_register_protocol("RFC 7468 file format", "rfc7468", "rfc7468");
|
|
|
|
|
|
|
|
proto_register_field_array(proto_rfc7468, hf, array_length(hf));
|
|
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
|
|
|
|
|
|
rfc7468_label_table = register_dissector_table("rfc7468.preeb_label", "FFF",
|
|
|
|
proto_rfc7468, FT_STRING,
|
|
|
|
TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
proto_reg_handoff_rfc7468(void)
|
|
|
|
{
|
|
|
|
dissector_handle_t rfc7468_handle;
|
|
|
|
|
|
|
|
heur_dissector_add("wtap_file", dissect_rfc7468_heur, "RFC 7468 file", "rfc7468_wtap", proto_rfc7468, HEURISTIC_ENABLE);
|
|
|
|
rfc7468_handle = create_dissector_handle(dissect_rfc7468, proto_rfc7468);
|
|
|
|
dissector_add_uint("wtap_encap", WTAP_ENCAP_RFC7468, rfc7468_handle);
|
|
|
|
|
|
|
|
ber_handle = find_dissector("ber");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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:
|
|
|
|
*/
|