From 89b127bf6cfacfb2fd19224a94c320d349551104 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Tue, 29 Nov 2016 12:52:20 +0100 Subject: [PATCH] ntp: accept NTPv4 packets with truncated MACs In order to allow deterministic parsing of NTPv4 extension fields, the MAC must not be longer than 192 bits (RFC 7822). One way to get around this limitation when using symmetric keys which produce longer MACs is to truncate them to 192 bits (32-bit key ID and 160-bit hash). Modify the code to accept NTPv4 packets with MACs truncated to 192 bits, but still allow long MACs in NTPv4 packets to not break compatibility with older chrony clients. --- ntp.h | 4 ++++ ntp_core.c | 32 +++++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/ntp.h b/ntp.h index 08a8bf6..e2abd84 100644 --- a/ntp.h +++ b/ntp.h @@ -56,6 +56,10 @@ typedef uint32_t NTP_int32; #define NTP_MIN_MAC_LENGTH (4 + 16) #define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH) +/* The maximum length of MAC in NTPv4 packets which allows deterministic + parsing of extension fields (RFC 7822) */ +#define NTP_MAX_V4_MAC_LENGTH (4 + 20) + /* Type definition for leap bits */ typedef enum { LEAP_Normal = 0, diff --git a/ntp_core.c b/ntp_core.c index fccdb49..6a5701c 100644 --- a/ntp_core.c +++ b/ntp_core.c @@ -1162,7 +1162,7 @@ static int check_packet_auth(NTP_Packet *pkt, int length, AuthenticationMode *auth_mode, uint32_t *key_id) { - int i, version, remainder, ext_length; + int i, version, remainder, ext_length, max_mac_length; unsigned char *data; uint32_t id; @@ -1175,15 +1175,28 @@ check_packet_auth(NTP_Packet *pkt, int length, while (1) { remainder = length - i; - /* Check if the remaining data is a valid MAC. This needs to be done - before trying to parse it as an extension field, because we support - MACs longer than the shortest valid extension field. */ - if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_MAC_LENGTH) { + /* Check if the remaining data is a valid MAC. There is a limit on MAC + length in NTPv4 packets to allow deterministic parsing of extension + fields (RFC 7822), but we need to support longer MACs to not break + compatibility with older chrony clients. This needs to be done before + trying to parse the data as an extension field. */ + + max_mac_length = version == 4 && remainder <= NTP_MAX_V4_MAC_LENGTH ? + NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH; + + if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= max_mac_length) { id = ntohl(*(uint32_t *)(data + i)); if (KEY_CheckAuth(id, (void *)pkt, i, (void *)(data + i + 4), - remainder - 4, NTP_MAX_MAC_LENGTH - 4)) { + remainder - 4, max_mac_length - 4)) { *auth_mode = AUTH_SYMMETRIC; *key_id = id; + + /* If it's an NTPv4 packet with long MAC and no extension fields, + rewrite the version in the packet to respond with long MAC too */ + if (version == 4 && NTP_NORMAL_PACKET_LENGTH + remainder == length && + remainder > NTP_MAX_V4_MAC_LENGTH) + pkt->lvm = NTP_LVM(NTP_LVM_TO_LEAP(pkt->lvm), 3, NTP_LVM_TO_MODE(pkt->lvm)); + return 1; } } @@ -1234,7 +1247,7 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr, { SST_Stats stats; - int pkt_leap; + int pkt_leap, pkt_version; uint32_t pkt_refid, pkt_key_id; double pkt_root_delay; double pkt_root_dispersion; @@ -1292,6 +1305,7 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr, inst->report.total_rx_count++; pkt_leap = NTP_LVM_TO_LEAP(message->lvm); + pkt_version = NTP_LVM_TO_VERSION(message->lvm); pkt_refid = ntohl(message->reference_id); pkt_root_delay = UTI_Ntp32ToDouble(message->root_delay); pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion); @@ -1597,8 +1611,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr, inst->report.remote_addr = inst->remote_addr.ip_addr; inst->report.local_addr = inst->local_addr.ip_addr; inst->report.remote_port = inst->remote_addr.port; - inst->report.leap = NTP_LVM_TO_LEAP(message->lvm); - inst->report.version = NTP_LVM_TO_VERSION(message->lvm); + inst->report.leap = pkt_leap; + inst->report.version = pkt_version; inst->report.mode = NTP_LVM_TO_MODE(message->lvm); inst->report.stratum = message->stratum; inst->report.poll = message->poll;