From f3e01d1c101f53849d51279ebdba1e3b51cc81ea Mon Sep 17 00:00:00 2001 From: John Thacker Date: Sat, 12 Oct 2024 02:04:43 -0400 Subject: [PATCH] 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 --- doc/release-notes.adoc | 4 + .../asn1/x509af/packet-x509af-template.c | 46 +++++++++++ epan/dissectors/asn1/x509af/x509af.cnf | 33 +++++++- epan/dissectors/packet-x509af.c | 77 ++++++++++++++++++- 4 files changed, 158 insertions(+), 2 deletions(-) diff --git a/doc/release-notes.adoc b/doc/release-notes.adoc index be3a2aa9c5..ae4c1af593 100644 --- a/doc/release-notes.adoc +++ b/doc/release-notes.adoc @@ -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 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. They previously shipped with Npcap 1.79. diff --git a/epan/dissectors/asn1/x509af/packet-x509af-template.c b/epan/dissectors/asn1/x509af/packet-x509af-template.c index ac46594a0c..f2dce2920f 100644 --- a/epan/dissectors/asn1/x509af/packet-x509af-template.c +++ b/epan/dissectors/asn1/x509af/packet-x509af-template.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include "packet-ber.h" @@ -37,6 +39,8 @@ void proto_reg_handoff_x509af(void); static dissector_handle_t pkix_crl_handle; +static int x509af_eo_tap; + /* Initialize the protocol and registered fields */ static int proto_x509af; static int hf_x509af_algorithm_id; @@ -49,8 +53,48 @@ static int ett_pkix_crl; static const char *algorithm_id; static void 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" +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. * actx->private_data is assumed to be a gnutls_datum_t pointer which will be * 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_subtree_array(ett, array_length(ett)); + x509af_eo_tap = register_export_object(proto_x509af, x509af_eo_packet, NULL); + register_cleanup_routine(&x509af_cleanup_protocol); pkix_crl_handle = register_dissector(PFNAME, dissect_pkix_crl, proto_x509af); diff --git a/epan/dissectors/asn1/x509af/x509af.cnf b/epan/dissectors/asn1/x509af/x509af.cnf index 0bda7a1f77..924efc869a 100644 --- a/epan/dissectors/asn1/x509af/x509af.cnf +++ b/epan/dissectors/asn1/x509af/x509af.cnf @@ -67,6 +67,22 @@ IssuerSerial/issuer issuerName CertificateList/signedCertificateList/revokedCertificates/_item/userCertificate revokedUserCertificate #.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_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(); 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 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/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 diff --git a/epan/dissectors/packet-x509af.c b/epan/dissectors/packet-x509af.c index d8e46f3c0c..4e3ef955ea 100644 --- a/epan/dissectors/packet-x509af.c +++ b/epan/dissectors/packet-x509af.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include "packet-ber.h" @@ -42,6 +44,8 @@ void proto_reg_handoff_x509af(void); static dissector_handle_t pkix_crl_handle; +static int x509af_eo_tap; + /* Initialize the protocol and registered fields */ static int proto_x509af; static int hf_x509af_algorithm_id; @@ -161,6 +165,13 @@ static const char *algorithm_id; static void 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[] = { { 0, "v1" }, { 1, "v2" }, @@ -181,9 +192,19 @@ dissect_x509af_Version(bool implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, 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_) { + int start_offset = offset; offset = dissect_ber_integer64(implicit_tag, actx, tree, tvb, offset, hf_index, 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; } @@ -325,6 +346,10 @@ dissect_x509af_SubjectName(bool implicit_tag _U_, tvbuff_t *tvb _U_, int offset str = x509if_get_last_dn(); 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; @@ -488,9 +513,24 @@ static const ber_sequence_t Certificate_sequence[] = { 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_) { - 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); + + 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; } @@ -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. * actx->private_data is assumed to be a gnutls_datum_t pointer which will be * 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_subtree_array(ett, array_length(ett)); + x509af_eo_tap = register_export_object(proto_x509af, x509af_eo_packet, NULL); + register_cleanup_routine(&x509af_cleanup_protocol); pkix_crl_handle = register_dissector(PFNAME, dissect_pkix_crl, proto_x509af);