From 56a102ed4d45ee5652193716ee6f7210e70da742 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Thu, 15 Aug 2019 11:20:36 +0200 Subject: [PATCH] ntp: move auth parsing to ntp_auth Move the remaining authentication-specific code to the new file. --- ntp_auth.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++ ntp_auth.h | 3 ++ ntp_core.c | 110 ++------------------------------------------------ 3 files changed, 122 insertions(+), 106 deletions(-) diff --git a/ntp_auth.c b/ntp_auth.c index 8a64b62..d6a4959 100644 --- a/ntp_auth.c +++ b/ntp_auth.c @@ -32,6 +32,7 @@ #include "logging.h" #include "memory.h" #include "ntp_auth.h" +#include "ntp_ext.h" #include "ntp_signd.h" #include "srcparams.h" #include "util.h" @@ -109,6 +110,19 @@ adjust_timestamp(NTP_AuthMode mode, uint32_t key_id, struct timespec *ts) /* ================================================== */ +static int +is_zero_data(unsigned char *data, int length) +{ + int i; + + for (i = 0; i < length; i++) + if (data[i]) + return 0; + return 1; +} + +/* ================================================== */ + static NAU_Instance create_instance(NTP_AuthMode mode) { @@ -220,6 +234,107 @@ NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketIn /* ================================================== */ +int +NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info) +{ + int parsed, remainder, ef_length, ef_type; + unsigned char *data; + + data = (void *)packet; + parsed = NTP_HEADER_LENGTH; + remainder = info->length - parsed; + + info->ext_fields = 0; + + /* Check if this is a plain NTP packet with no extension fields or MAC */ + if (remainder <= 0) + return 1; + + /* In NTPv3 and older packets don't have extension fields. Anything after + the header is assumed to be a MAC. */ + if (info->version <= 3) { + info->auth.mode = AUTH_SYMMETRIC; + info->auth.mac.start = parsed; + info->auth.mac.length = remainder; + info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed)); + + /* Check if it is an MS-SNTP authenticator field or extended authenticator + field with zeroes as digest */ + if (info->version == 3 && info->auth.mac.key_id) { + if (remainder == 20 && is_zero_data(data + parsed + 4, remainder - 4)) + info->auth.mode = AUTH_MSSNTP; + else if (remainder == 72 && is_zero_data(data + parsed + 8, remainder - 8)) + info->auth.mode = AUTH_MSSNTP_EXT; + } + + return 1; + } + + /* Check for a crypto NAK */ + if (remainder == 4 && ntohl(*(uint32_t *)(data + parsed)) == 0) { + info->auth.mode = AUTH_SYMMETRIC; + info->auth.mac.start = parsed; + info->auth.mac.length = remainder; + info->auth.mac.key_id = 0; + return 1; + } + + /* Parse the rest of the NTPv4 packet */ + + while (remainder > 0) { + /* 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. */ + + if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_MAC_LENGTH) { + info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed)); + if (remainder <= NTP_MAX_V4_MAC_LENGTH || + KEY_CheckAuth(info->auth.mac.key_id, data, parsed, (void *)(data + parsed + 4), + remainder - 4, NTP_MAX_MAC_LENGTH - 4)) + break; + } + + /* Check if this is a valid NTPv4 extension field and skip it */ + if (!NEF_ParseField(packet, info->length, parsed, &ef_length, &ef_type, NULL, NULL)) { + /* Invalid MAC or format error */ + DEBUG_LOG("Invalid format or MAC"); + return 0; + } + + assert(ef_length > 0); + + switch (ef_type) { + default: + DEBUG_LOG("Unknown extension field type=%x", (unsigned int)ef_type); + } + + info->ext_fields++; + parsed += ef_length; + remainder = info->length - parsed; + } + + if (remainder == 0) { + /* No MAC */ + return 1; + } else if (remainder >= NTP_MIN_MAC_LENGTH) { + /* This is not 100% reliable as a MAC could fail to authenticate and could + pass as an extension field, leaving reminder smaller than the minimum MAC + length */ + info->auth.mode = AUTH_SYMMETRIC; + info->auth.mac.start = parsed; + info->auth.mac.length = remainder; + info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed)); + return 1; + } + + DEBUG_LOG("Invalid format"); + return 0; +} + +/* ================================================== */ + int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info) { diff --git a/ntp_auth.h b/ntp_auth.h index 603ac17..7537238 100644 --- a/ntp_auth.h +++ b/ntp_auth.h @@ -56,6 +56,9 @@ extern void NAU_AdjustRequestTimestamp(NAU_Instance instance, struct timespec *t extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketInfo *info); +/* Parse a request or response to detect the authentication mode */ +extern int NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info); + /* Verify that a request is authentic */ extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info); diff --git a/ntp_core.c b/ntp_core.c index fe1bb8a..9ce133a 100644 --- a/ntp_core.c +++ b/ntp_core.c @@ -32,7 +32,6 @@ #include "array.h" #include "ntp_auth.h" #include "ntp_core.h" -#include "ntp_ext.h" #include "ntp_io.h" #include "memory.h" #include "sched.h" @@ -44,7 +43,6 @@ #include "util.h" #include "conf.h" #include "logging.h" -#include "keys.h" #include "addrfilt.h" #include "clientlog.h" @@ -1268,25 +1266,9 @@ transmit_timeout(void *arg) /* ================================================== */ -static int -is_zero_data(unsigned char *data, int length) -{ - int i; - - for (i = 0; i < length; i++) - if (data[i]) - return 0; - return 1; -} - -/* ================================================== */ - static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info) { - int parsed, remainder, ef_length, ef_type; - unsigned char *data; - if (length < NTP_HEADER_LENGTH || length % 4U != 0) { DEBUG_LOG("NTP packet has invalid length %d", length); return 0; @@ -1303,95 +1285,11 @@ parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info) return 0; } - data = (void *)packet; - parsed = NTP_HEADER_LENGTH; - remainder = length - parsed; + /* Parse authentication extension fields or MAC */ + if (!NAU_ParsePacket(packet, info)) + return 0; - /* Check if this is a plain NTP packet with no extension fields or MAC */ - if (remainder == 0) - return 1; - - /* In NTPv3 and older packets don't have extension fields. Anything after - the header is assumed to be a MAC. */ - if (info->version <= 3) { - info->auth.mode = AUTH_SYMMETRIC; - info->auth.mac.start = parsed; - info->auth.mac.length = remainder; - info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed)); - - /* Check if it is an MS-SNTP authenticator field or extended authenticator - field with zeroes as digest */ - if (info->version == 3 && info->auth.mac.key_id) { - if (remainder == 20 && is_zero_data(data + parsed + 4, remainder - 4)) - info->auth.mode = AUTH_MSSNTP; - else if (remainder == 72 && is_zero_data(data + parsed + 8, remainder - 8)) - info->auth.mode = AUTH_MSSNTP_EXT; - } - - return 1; - } - - /* Check for a crypto NAK */ - if (remainder == 4 && ntohl(*(uint32_t *)(data + parsed)) == 0) { - info->auth.mode = AUTH_SYMMETRIC; - info->auth.mac.start = parsed; - info->auth.mac.length = remainder; - info->auth.mac.key_id = 0; - return 1; - } - - /* Parse the rest of the NTPv4 packet */ - - while (remainder > 0) { - /* 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. */ - - if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_MAC_LENGTH) { - info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed)); - if (remainder <= NTP_MAX_V4_MAC_LENGTH || - KEY_CheckAuth(info->auth.mac.key_id, data, parsed, (void *)(data + parsed + 4), - remainder - 4, NTP_MAX_MAC_LENGTH - 4)) - break; - } - - /* Check if this is a valid NTPv4 extension field and skip it */ - if (!NEF_ParseField(packet, length, parsed, &ef_length, &ef_type, NULL, NULL)) { - /* Invalid MAC or format error */ - DEBUG_LOG("Invalid format or MAC"); - return 0; - } - - assert(ef_length > 0); - - switch (ef_type) { - default: - DEBUG_LOG("Unknown extension field type=%x", (unsigned int)ef_type); - } - - info->ext_fields++; - parsed += ef_length; - remainder = length - parsed; - } - - if (remainder == 0) { - /* No MAC */ - return 1; - } else if (remainder >= NTP_MIN_MAC_LENGTH) { - /* This is not 100% reliable as a MAC could fail to authenticate and could - pass as an extension field, leaving reminder smaller than the minimum MAC - length */ - info->auth.mode = AUTH_SYMMETRIC; - info->auth.mac.start = parsed; - info->auth.mac.length = remainder; - info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed)); - return 1; - } - - DEBUG_LOG("Invalid format"); - return 0; + return 1; } /* ================================================== */