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);