x509af: Add Export Objects for Certificates

Add X.509 Certificates to Export Objects. It's possible to extract
these from Wireshark via the context menu Export Packet Bytes, but
this allows extracting all the certs in files at once, or through
tshark.

Fix #16726
This commit is contained in:
John Thacker 2024-10-12 02:04:43 -04:00 committed by AndersBroman
parent 5ac7d83590
commit f3e01d1c10
4 changed files with 158 additions and 2 deletions

View File

@ -69,6 +69,10 @@ The following features are either new or have been significantly updated since v
* Custom columns have an option to show the values using the same format as * Custom columns have an option to show the values using the same format as
in Packet Details. in Packet Details.
* X.509 certificates, used in TLS and elsewhere, can be exported via the
File->Export Objects menu in Wireshark (under the name "X509AF") and
`--export-objects` in TShark (with the protocol name `x509af`.)
* The Windows installers now ship with Npcap 1.80. * The Windows installers now ship with Npcap 1.80.
They previously shipped with Npcap 1.79. They previously shipped with Npcap 1.79.

View File

@ -15,6 +15,8 @@
#include <epan/oids.h> #include <epan/oids.h>
#include <epan/asn1.h> #include <epan/asn1.h>
#include <epan/strutil.h> #include <epan/strutil.h>
#include <epan/export_object.h>
#include <epan/proto_data.h>
#include <wsutil/array.h> #include <wsutil/array.h>
#include "packet-ber.h" #include "packet-ber.h"
@ -37,6 +39,8 @@ void proto_reg_handoff_x509af(void);
static dissector_handle_t pkix_crl_handle; static dissector_handle_t pkix_crl_handle;
static int x509af_eo_tap;
/* Initialize the protocol and registered fields */ /* Initialize the protocol and registered fields */
static int proto_x509af; static int proto_x509af;
static int hf_x509af_algorithm_id; static int hf_x509af_algorithm_id;
@ -49,8 +53,48 @@ static int ett_pkix_crl;
static const char *algorithm_id; static const char *algorithm_id;
static void static void
x509af_export_publickey(tvbuff_t *tvb, asn1_ctx_t *actx, int offset, int len); x509af_export_publickey(tvbuff_t *tvb, asn1_ctx_t *actx, int offset, int len);
typedef struct _x509af_eo_t {
const char *subjectname;
char *serialnum;
tvbuff_t *payload;
} x509af_eo_t;
#include "packet-x509af-fn.c" #include "packet-x509af-fn.c"
static tap_packet_status
x509af_eo_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data, tap_flags_t flags _U_)
{
export_object_list_t *object_list = (export_object_list_t *)tapdata;
const x509af_eo_t *eo_info = (const x509af_eo_t *)data;
export_object_entry_t *entry;
if (data) {
entry = g_new0(export_object_entry_t, 1);
entry->pkt_num = pinfo->num;
// There should be a commonName
char *name = strstr(eo_info->subjectname, "id-at-commonName=");
if (name) {
name += strlen("id-at-commonName=");
entry->hostname = g_strndup(name, strcspn(name, ","));
}
entry->content_type = g_strdup("application/pkix-cert");
entry->filename = g_strdup_printf("%s.cer", eo_info->serialnum);
entry->payload_len = tvb_captured_length(eo_info->payload);
entry->payload_data = (uint8_t *)tvb_memdup(NULL, eo_info->payload, 0, entry->payload_len);
object_list->add_entry(object_list->gui_data, entry);
return TAP_PACKET_REDRAW;
} else {
return TAP_PACKET_DONT_REDRAW;
}
}
/* Exports the SubjectPublicKeyInfo structure as gnutls_datum_t. /* Exports the SubjectPublicKeyInfo structure as gnutls_datum_t.
* actx->private_data is assumed to be a gnutls_datum_t pointer which will be * actx->private_data is assumed to be a gnutls_datum_t pointer which will be
* filled in if non-NULL. */ * filled in if non-NULL. */
@ -124,6 +168,8 @@ void proto_register_x509af(void) {
proto_register_field_array(proto_x509af, hf, array_length(hf)); proto_register_field_array(proto_x509af, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett)); proto_register_subtree_array(ett, array_length(ett));
x509af_eo_tap = register_export_object(proto_x509af, x509af_eo_packet, NULL);
register_cleanup_routine(&x509af_cleanup_protocol); register_cleanup_routine(&x509af_cleanup_protocol);
pkix_crl_handle = register_dissector(PFNAME, dissect_pkix_crl, proto_x509af); pkix_crl_handle = register_dissector(PFNAME, dissect_pkix_crl, proto_x509af);

View File

@ -67,6 +67,22 @@ IssuerSerial/issuer issuerName
CertificateList/signedCertificateList/revokedCertificates/_item/userCertificate revokedUserCertificate CertificateList/signedCertificateList/revokedCertificates/_item/userCertificate revokedUserCertificate
#.END #.END
#.FN_BODY Certificate
int start_offset = offset;
x509af_eo_t *eo_info = NULL;
if (have_tap_listener(x509af_eo_tap)) {
eo_info = wmem_new0(actx->pinfo->pool, x509af_eo_t);
p_add_proto_data(actx->pinfo->pool, actx->pinfo, proto_x509af, 0, eo_info);
}
%(DEFAULT_BODY)s
if (eo_info) {
eo_info->payload = tvb_new_subset_length(tvb, start_offset, offset - start_offset);
tap_queue_packet(x509af_eo_tap, actx->pinfo, eo_info);
}
#.FN_PARS AlgorithmIdentifier/algorithmId #.FN_PARS AlgorithmIdentifier/algorithmId
FN_VARIANT = _str HF_INDEX = hf_x509af_algorithm_id VAL_PTR = &actx->external.direct_reference FN_VARIANT = _str HF_INDEX = hf_x509af_algorithm_id VAL_PTR = &actx->external.direct_reference
@ -166,6 +182,10 @@ CertificateList/signedCertificateList/revokedCertificates/_item/userCertificate
str = x509if_get_last_dn(); str = x509if_get_last_dn();
proto_item_append_text(proto_item_get_parent(tree), " (%%s)", str?str:""); proto_item_append_text(proto_item_get_parent(tree), " (%%s)", str?str:"");
x509af_eo_t *eo_info = p_get_proto_data(actx->pinfo->pool, actx->pinfo, proto_x509af, 0);
if (eo_info) {
eo_info->subjectname = str;
}
#.TYPE_ATTR #.TYPE_ATTR
CertificateSerialNumber TYPE = FT_BYTES DISPLAY = BASE_NONE CertificateSerialNumber TYPE = FT_BYTES DISPLAY = BASE_NONE
@ -173,6 +193,17 @@ DSS-Params/p TYPE = FT_BYTES DISPLAY = BASE_NONE
DSS-Params/q TYPE = FT_BYTES DISPLAY = BASE_NONE DSS-Params/q TYPE = FT_BYTES DISPLAY = BASE_NONE
DSS-Params/g TYPE = FT_BYTES DISPLAY = BASE_NONE DSS-Params/g TYPE = FT_BYTES DISPLAY = BASE_NONE
#.FN_PARS CertificateSerialNumber FN_VARIANT = 64 # This INTEGER can be up to 20 octets, so extract the bytes manually
#.FN_BODY CertificateSerialNumber FN_VARIANT = 64
int start_offset = offset;
%(DEFAULT_BODY)s
x509af_eo_t *eo_info = p_get_proto_data(actx->pinfo->pool, actx->pinfo, proto_x509af, 0);
if (eo_info) {
uint32_t len;
start_offset = get_ber_identifier(tvb, start_offset, NULL, NULL, NULL);
start_offset = get_ber_length(tvb, start_offset, &len, NULL);
eo_info->serialnum = tvb_bytes_to_str(actx->pinfo->pool, tvb, start_offset, len);
}
#.END #.END

View File

@ -20,6 +20,8 @@
#include <epan/oids.h> #include <epan/oids.h>
#include <epan/asn1.h> #include <epan/asn1.h>
#include <epan/strutil.h> #include <epan/strutil.h>
#include <epan/export_object.h>
#include <epan/proto_data.h>
#include <wsutil/array.h> #include <wsutil/array.h>
#include "packet-ber.h" #include "packet-ber.h"
@ -42,6 +44,8 @@ void proto_reg_handoff_x509af(void);
static dissector_handle_t pkix_crl_handle; static dissector_handle_t pkix_crl_handle;
static int x509af_eo_tap;
/* Initialize the protocol and registered fields */ /* Initialize the protocol and registered fields */
static int proto_x509af; static int proto_x509af;
static int hf_x509af_algorithm_id; static int hf_x509af_algorithm_id;
@ -161,6 +165,13 @@ static const char *algorithm_id;
static void static void
x509af_export_publickey(tvbuff_t *tvb, asn1_ctx_t *actx, int offset, int len); x509af_export_publickey(tvbuff_t *tvb, asn1_ctx_t *actx, int offset, int len);
typedef struct _x509af_eo_t {
const char *subjectname;
char *serialnum;
tvbuff_t *payload;
} x509af_eo_t;
const value_string x509af_Version_vals[] = { const value_string x509af_Version_vals[] = {
{ 0, "v1" }, { 0, "v1" },
{ 1, "v2" }, { 1, "v2" },
@ -181,9 +192,19 @@ dissect_x509af_Version(bool implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_,
int int
dissect_x509af_CertificateSerialNumber(bool implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { dissect_x509af_CertificateSerialNumber(bool implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
int start_offset = offset;
offset = dissect_ber_integer64(implicit_tag, actx, tree, tvb, offset, hf_index, offset = dissect_ber_integer64(implicit_tag, actx, tree, tvb, offset, hf_index,
NULL); NULL);
x509af_eo_t *eo_info = p_get_proto_data(actx->pinfo->pool, actx->pinfo, proto_x509af, 0);
if (eo_info) {
uint32_t len;
start_offset = get_ber_identifier(tvb, start_offset, NULL, NULL, NULL);
start_offset = get_ber_length(tvb, start_offset, &len, NULL);
eo_info->serialnum = tvb_bytes_to_str(actx->pinfo->pool, tvb, start_offset, len);
}
return offset; return offset;
} }
@ -325,6 +346,10 @@ dissect_x509af_SubjectName(bool implicit_tag _U_, tvbuff_t *tvb _U_, int offset
str = x509if_get_last_dn(); str = x509if_get_last_dn();
proto_item_append_text(proto_item_get_parent(tree), " (%s)", str?str:""); proto_item_append_text(proto_item_get_parent(tree), " (%s)", str?str:"");
x509af_eo_t *eo_info = p_get_proto_data(actx->pinfo->pool, actx->pinfo, proto_x509af, 0);
if (eo_info) {
eo_info->subjectname = str;
}
return offset; return offset;
@ -488,9 +513,24 @@ static const ber_sequence_t Certificate_sequence[] = {
int int
dissect_x509af_Certificate(bool implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { dissect_x509af_Certificate(bool implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {
offset = dissect_ber_sequence(implicit_tag, actx, tree, tvb, offset, int start_offset = offset;
x509af_eo_t *eo_info = NULL;
if (have_tap_listener(x509af_eo_tap)) {
eo_info = wmem_new0(actx->pinfo->pool, x509af_eo_t);
p_add_proto_data(actx->pinfo->pool, actx->pinfo, proto_x509af, 0, eo_info);
}
offset = dissect_ber_sequence(implicit_tag, actx, tree, tvb, offset,
Certificate_sequence, hf_index, ett_x509af_Certificate); Certificate_sequence, hf_index, ett_x509af_Certificate);
if (eo_info) {
eo_info->payload = tvb_new_subset_length(tvb, start_offset, offset - start_offset);
tap_queue_packet(x509af_eo_tap, actx->pinfo, eo_info);
}
return offset; return offset;
} }
@ -932,6 +972,39 @@ static int dissect_Userid_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_t
} }
static tap_packet_status
x509af_eo_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data, tap_flags_t flags _U_)
{
export_object_list_t *object_list = (export_object_list_t *)tapdata;
const x509af_eo_t *eo_info = (const x509af_eo_t *)data;
export_object_entry_t *entry;
if (data) {
entry = g_new0(export_object_entry_t, 1);
entry->pkt_num = pinfo->num;
// There should be a commonName
char *name = strstr(eo_info->subjectname, "id-at-commonName=");
if (name) {
name += strlen("id-at-commonName=");
entry->hostname = g_strndup(name, strcspn(name, ","));
}
entry->content_type = g_strdup("application/pkix-cert");
entry->filename = g_strdup_printf("%s.cer", eo_info->serialnum);
entry->payload_len = tvb_captured_length(eo_info->payload);
entry->payload_data = (uint8_t *)tvb_memdup(NULL, eo_info->payload, 0, entry->payload_len);
object_list->add_entry(object_list->gui_data, entry);
return TAP_PACKET_REDRAW;
} else {
return TAP_PACKET_DONT_REDRAW;
}
}
/* Exports the SubjectPublicKeyInfo structure as gnutls_datum_t. /* Exports the SubjectPublicKeyInfo structure as gnutls_datum_t.
* actx->private_data is assumed to be a gnutls_datum_t pointer which will be * actx->private_data is assumed to be a gnutls_datum_t pointer which will be
* filled in if non-NULL. */ * filled in if non-NULL. */
@ -1339,6 +1412,8 @@ void proto_register_x509af(void) {
proto_register_field_array(proto_x509af, hf, array_length(hf)); proto_register_field_array(proto_x509af, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett)); proto_register_subtree_array(ett, array_length(ett));
x509af_eo_tap = register_export_object(proto_x509af, x509af_eo_packet, NULL);
register_cleanup_routine(&x509af_cleanup_protocol); register_cleanup_routine(&x509af_cleanup_protocol);
pkix_crl_handle = register_dissector(PFNAME, dissect_pkix_crl, proto_x509af); pkix_crl_handle = register_dissector(PFNAME, dissect_pkix_crl, proto_x509af);