NTP: fix detection of larger MAC sizes, remove Autokey support

Autokey was not properly supported, the v2 version check looked at the
wrong field (Code instead of Field Type). Since nobody noticed it, let's
remove it to simplify the code.

Improve the Extension Field (EF) heuristics to ensure that larger digest
sizes such as SHA-512 are recognized, and to support messages without
MAC. Previously only MD5 and SHA-1 were supported as these are the only
ones that are defined by the RFCs.

The ntp_ext_field_types array was generated by:

    curl -s https://www.iana.org/assignments/ntp-parameters/ntp-parameters-3.csv |
    awk -F, 'NR>=2{printf "{ %s, \"%s\" },\n", $1, $2}' | sort -n

Tested with md5_dgrams.pcapng and sha1_dgrams.pcapng (Bug 11580) and
NTP-with-mac.pcap (Bug 16640). Also checked against the NTS capture
(go_embeded.pcapng, bug 16222), but TCP reassembly is not supported so
the last part of the first segment is wrongly dissected as MAC.

Bug: 16640
Change-Id: I07fc46c6d8995e6c791952dd7cd84d798cddd21a
Reviewed-on: https://code.wireshark.org/review/38037
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Peter Wu 2020-08-03 13:39:54 +02:00 committed by Anders Broman
parent e6b5bd0976
commit e1c10c5856
1 changed files with 117 additions and 155 deletions

View File

@ -218,31 +218,12 @@ static const struct {
{ NULL, NULL}
};
#define NTP_EXT_R_MASK 0x80
static const value_string ext_r_types[] = {
{ 0, "Request" },
{ 1, "Response" },
{ 0, NULL}
};
#define NTP_EXT_ERROR_MASK 0x40
#define NTP_EXT_VN_MASK 0x3f
static const value_string ext_op_types[] = {
{ 0, "NULL" },
{ 1, "ASSOC" },
{ 2, "CERT" },
{ 3, "COOK" },
{ 4, "AUTO" },
{ 5, "TAI" },
{ 6, "SIGN" },
{ 7, "IFF" },
{ 8, "GQ" },
{ 9, "MV" },
{ 0, NULL}
};
#define NTPCTRL_R_MASK 0x80
#define ctrl_r_types ext_r_types
@ -533,6 +514,7 @@ static value_string_ext priv_rc_types_ext = VALUE_STRING_EXT_INIT(priv_rc_types)
#define PRIV_INFO_FLAG_AUTHENABLE 0x20
#define PRIV_INFO_FLAG_SEL_CANDIDATE 0x40
#define PRIV_INFO_FLAG_SHORTLIST 0x80
/* XXX PRIV_INFO_FLAG_IBURST is unused, is a field needed? */
#define PRIV_INFO_FLAG_IBURST 0x100
#define PRIV_CONF_FLAG_AUTHENABLE 0x01
@ -577,6 +559,49 @@ static const value_string authentication_types[] = {
{ 0, NULL}
};
/*
* NTP Extension Field Types.
* https://www.iana.org/assignments/ntp-parameters/ntp-parameters.xhtml#ntp-parameters-3
*/
static const value_string ntp_ext_field_types[] = {
{ 0x0002, "No-Operation Request" },
{ 0x0102, "Association Message Request" },
{ 0x0104, "Unique Identifier" },
{ 0x0202, "Certificate Message Request" },
{ 0x0204, "NTS Cookie" },
{ 0x0302, "Cookie Message Request" },
{ 0x0304, "NTS Cookie Placeholder" },
{ 0x0402, "Autokey Message Request" },
{ 0x0404, "NTS Authenticator and Encrypted Extension Fields" },
{ 0x0502, "Leapseconds Message Request" },
{ 0x0602, "Sign Message Request" },
{ 0x0702, "IFF Identity Message Request" },
{ 0x0802, "GQ Identity Message Request" },
{ 0x0902, "MV Identity Message Request" },
{ 0x2005, "Checksum Complement" },
{ 0x8002, "No-Operation Response" },
{ 0x8102, "Association Message Response" },
{ 0x8202, "Certificate Message Response" },
{ 0x8302, "Cookie Message Response" },
{ 0x8402, "Autokey Message Response" },
{ 0x8502, "Leapseconds Message Response" },
{ 0x8602, "Sign Message Response" },
{ 0x8702, "IFF Identity Message Response" },
{ 0x8802, "GQ Identity Message Response" },
{ 0x8902, "MV Identity Message Response" },
{ 0xC002, "No-Operation Error Response" },
{ 0xC102, "Association Message Error Response" },
{ 0xC202, "Certificate Message Error Response" },
{ 0xC302, "Cookie Message Error Response" },
{ 0xC402, "Autokey Message Error Response" },
{ 0xC502, "Leapseconds Message Error Response" },
{ 0xC602, "Sign Message Error Response" },
{ 0xC702, "IFF Identity Message Error Response" },
{ 0xC802, "GQ Identity Message Error Response" },
{ 0xC902, "MV Identity Message Error Response" },
{ 0, NULL }
};
typedef struct {
guint32 req_frame;
@ -590,11 +615,6 @@ typedef struct {
} ntp_conv_info_t;
/*
* Maximum MAC length : 160 bits MAC + 32 bits Key ID
*/
#define MAX_MAC_LEN (6 * sizeof (guint32))
static int proto_ntp = -1;
static int hf_ntp_flags = -1;
@ -622,19 +642,9 @@ static int hf_ntp_request_in = -1;
static int hf_ntp_delta_time = -1;
static int hf_ntp_ext = -1;
static int hf_ntp_ext_flags = -1;
static int hf_ntp_ext_flags_r = -1;
static int hf_ntp_ext_flags_error = -1;
static int hf_ntp_ext_flags_vn = -1;
static int hf_ntp_ext_op = -1;
static int hf_ntp_ext_len = -1;
static int hf_ntp_ext_associd = -1;
static int hf_ntp_ext_tstamp = -1;
static int hf_ntp_ext_fstamp = -1;
static int hf_ntp_ext_vallen = -1;
static int hf_ntp_ext_val = -1;
static int hf_ntp_ext_siglen = -1;
static int hf_ntp_ext_sig = -1;
static int hf_ntp_ext_type = -1;
static int hf_ntp_ext_length = -1;
static int hf_ntp_ext_value = -1;
static int hf_ntpctrl_flags2 = -1;
static int hf_ntpctrl_flags2_r = -1;
@ -1083,17 +1093,21 @@ ntp_to_nstime(tvbuff_t *tvb, gint offset, nstime_t *nstime)
static int
dissect_ntp_ext(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ntp_tree, int offset)
{
proto_tree *ext_tree, *flags_tree;
proto_item *tf, *ext_item;
proto_tree *ext_tree;
proto_item *tf;
guint16 extlen;
int endoffset;
guint8 flags;
guint32 vallen, vallen_round, siglen;
int value_length;
extlen = tvb_get_ntohs(tvb, offset+2);
tf = proto_tree_add_item(ntp_tree, hf_ntp_ext, tvb, offset, extlen, ENC_NA);
ext_tree = proto_item_add_subtree(tf, ett_ntp_ext);
proto_tree_add_item(ext_tree, hf_ntp_ext_type, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
tf = proto_tree_add_item(ext_tree, hf_ntp_ext_length, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
if (extlen < 8) {
/* Extension length isn't enough for the extension header.
* Report the error, and return an offset that goes to
@ -1111,76 +1125,12 @@ dissect_ntp_ext(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ntp_tree, int off
extlen);
return tvb_reported_length(tvb);
}
endoffset = offset + extlen;
flags = tvb_get_guint8(tvb, offset);
tf = proto_tree_add_uint(ext_tree, hf_ntp_ext_flags, tvb, offset, 1, flags);
flags_tree = proto_item_add_subtree(tf, ett_ntp_ext_flags);
proto_tree_add_uint(flags_tree, hf_ntp_ext_flags_r, tvb, offset, 1, flags);
proto_tree_add_uint(flags_tree, hf_ntp_ext_flags_error, tvb, offset, 1, flags);
proto_tree_add_uint(flags_tree, hf_ntp_ext_flags_vn, tvb, offset, 1, flags);
offset += 1;
value_length = extlen - 4;
proto_tree_add_item(ext_tree, hf_ntp_ext_value, tvb, offset, value_length, ENC_NA);
offset += value_length;
proto_tree_add_item(ext_tree, hf_ntp_ext_op, tvb, offset, 1, ENC_BIG_ENDIAN);
offset += 1;
proto_tree_add_uint(ext_tree, hf_ntp_ext_len, tvb, offset, 2, extlen);
offset += 2;
if ((flags & NTP_EXT_VN_MASK) != 2) {
/* don't care about autokey v1 */
return endoffset;
}
proto_tree_add_item(ext_tree, hf_ntp_ext_associd, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
/* check whether everything up to "vallen" is present */
if (extlen < MAX_MAC_LEN) {
/* XXX - report as error? */
return endoffset;
}
proto_tree_add_item(ext_tree, hf_ntp_ext_tstamp, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
proto_tree_add_item(ext_tree, hf_ntp_ext_fstamp, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
/* XXX fstamp can be server flags */
vallen = tvb_get_ntohl(tvb, offset);
ext_item = proto_tree_add_uint(ext_tree, hf_ntp_ext_vallen, tvb, offset, 4, vallen);
offset += 4;
vallen_round = (vallen + 3) & (-4);
if (vallen != 0) {
if ((guint32)(endoffset - offset) < vallen_round) {
/*
* Value goes past the length of the extension
* field.
*/
expert_add_info_format(pinfo, ext_item, &ei_ntp_ext,
"Value length makes value go past the end of the extension field");
return endoffset;
}
proto_tree_add_item(ext_tree, hf_ntp_ext_val, tvb, offset, vallen, ENC_NA);
}
offset += vallen_round;
siglen = tvb_get_ntohl(tvb, offset);
ext_item = proto_tree_add_uint(ext_tree, hf_ntp_ext_siglen, tvb, offset, 4, siglen);
offset += 4;
if (siglen != 0) {
if (offset + (int)siglen > endoffset) {
/*
* Value goes past the length of the extension
* field.
*/
expert_add_info_format(pinfo, ext_item, &ei_ntp_ext,
"Signature length makes value go past the end of the extension field");
return endoffset;
}
proto_tree_add_item(ext_tree, hf_ntp_ext_sig, tvb, offset, siglen, ENC_NA);
}
return endoffset;
return offset;
}
static void
@ -1196,8 +1146,10 @@ dissect_ntp_std(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ntp_tree, ntp_con
guint32 refid_addr;
gchar *buff;
int i;
int efs_end;
guint16 last_extlen = 0;
int macofs;
gint maclen;
guint maclen;
ntp_trans_info_t *ntp_trans;
wmem_tree_key_t key[3];
guint64 flags;
@ -1345,14 +1297,54 @@ dissect_ntp_std(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ntp_tree, ntp_con
*/
proto_tree_add_item(ntp_tree, hf_ntp_xmt, tvb, 40, 8, ENC_TIME_NTP|ENC_BIG_ENDIAN);
/* MAX_MAC_LEN is the largest message authentication code
* (MAC) length. If we have more data left in the packet
* after the header than that, the extra data is NTP4
* extensions; parse them as such.
/*
* Optional fields:
*
* - Optional Extension fields (EFs), at minimum 16 bytes each.
* Used for Autokey (RFC 5906, requires a MAC) and others.
*
* - Optional Message Authentication Codes (MACs), consisting of a
* 32-bit key ID concatenated with the digest. Per RFC 7822, this MAC
* can be 24 bytes (SHA-1, AES-CMAC from RFC 8573), 20 octets (MD5),
* or 4 bytes (crypto-NAK, MAC contains four zeroes). However,
* implementations such as chrony and NTPsec support additional hash
* algorithms such as SHA-512 which result in a MAC of 68 bytes.
*
* Since MACs cannot unambiguously be recognized from EFs based on size
* alone due to the larger, non-standard MAC algorithms, follow this:
*
* 1. Find the end of EFs, stopping as soon as it looks invalid (too
* small or large Length field).
* 2. If there is any trailing data, assume a MAC is present. If it is
* too small, remove a field that was assumed to be an EF.
*/
efs_end = 48;
while (tvb_reported_length_remaining(tvb, efs_end) >= 16) {
guint16 extlen = tvb_get_ntohs(tvb, efs_end + 2);
if (extlen < 16) {
break;
}
if (tvb_reported_length_remaining(tvb, efs_end) < extlen) {
break;
}
efs_end += extlen;
last_extlen = extlen;
}
maclen = tvb_reported_length_remaining(tvb, efs_end);
if (maclen == 0) {
/* MAC is missing. */
} else if (maclen == 4 && tvb_get_ntohl(tvb, efs_end) == 0) {
/* crypto-NAK - continue as normal. */
} else if (maclen < 20) {
/* last field was most likely not an EF, remove it. */
efs_end -= last_extlen;
}
macofs = 48;
while (tvb_reported_length_remaining(tvb, macofs) > (gint)MAX_MAC_LEN)
while (macofs < efs_end) {
macofs = dissect_ntp_ext(tvb, pinfo, ntp_tree, macofs);
}
/* When the NTP authentication scheme is implemented, the
* Key Identifier and Message Digest fields contain the
@ -2711,45 +2703,15 @@ proto_register_ntp(void)
{ &hf_ntp_ext, {
"Extension", "ntp.ext", FT_NONE, BASE_NONE,
NULL, 0, NULL, HFILL }},
{ &hf_ntp_ext_flags, {
"Flags", "ntp.ext.flags", FT_UINT8, BASE_HEX,
NULL, 0, "Flags (Response/Error/Version)", HFILL }},
{ &hf_ntp_ext_flags_r, {
"Response bit", "ntp.ext.flags.r", FT_UINT8, BASE_DEC,
VALS(ext_r_types), NTP_EXT_R_MASK, NULL, HFILL }},
{ &hf_ntp_ext_flags_error, {
"Error bit", "ntp.ext.flags.error", FT_UINT8, BASE_DEC,
NULL, NTP_EXT_ERROR_MASK, NULL, HFILL }},
{ &hf_ntp_ext_flags_vn, {
"Version", "ntp.ext.flags.vn", FT_UINT8, BASE_DEC,
NULL, NTP_EXT_VN_MASK, NULL, HFILL }},
{ &hf_ntp_ext_op, {
"Opcode", "ntp.ext.op", FT_UINT8, BASE_DEC,
VALS(ext_op_types), 0, NULL, HFILL }},
{ &hf_ntp_ext_len, {
"Extension length", "ntp.ext.len", FT_UINT16, BASE_DEC,
NULL, 0, NULL, HFILL }},
{ &hf_ntp_ext_associd, {
"Association ID", "ntp.ext.associd", FT_UINT32, BASE_DEC,
NULL, 0, NULL, HFILL }},
{ &hf_ntp_ext_tstamp, {
"Timestamp", "ntp.ext.tstamp", FT_UINT32, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_ntp_ext_fstamp, {
"File Timestamp", "ntp.ext.fstamp", FT_UINT32, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_ntp_ext_vallen, {
"Value length", "ntp.ext.vallen", FT_UINT32, BASE_DEC,
NULL, 0, NULL, HFILL }},
{ &hf_ntp_ext_val, {
"Value", "ntp.ext.val", FT_BYTES, BASE_NONE,
NULL, 0, NULL, HFILL }},
{ &hf_ntp_ext_siglen, {
"Signature length", "ntp.ext.siglen", FT_UINT32, BASE_DEC,
NULL, 0, NULL, HFILL }},
{ &hf_ntp_ext_sig, {
"Signature", "ntp.ext.sig", FT_BYTES, BASE_NONE,
NULL, 0, NULL, HFILL }},
{ &hf_ntp_ext_type, {
"Field Type", "ntp.ext.type", FT_UINT16, BASE_HEX,
VALS(ntp_ext_field_types), 0, NULL, HFILL }},
{ &hf_ntp_ext_length, {
"Length", "ntp.ext.length", FT_UINT16, BASE_DEC,
NULL, 0, "Entire extension length including padding", HFILL }},
{ &hf_ntp_ext_value, {
"Value", "ntp.ext.value", FT_BYTES, BASE_NONE,
NULL, 0, "Type-specific value", HFILL }},
{ &hf_ntpctrl_flags2, {
"Flags 2", "ntp.ctrl.flags2", FT_UINT8, BASE_HEX,