ntp: move auth parsing to ntp_auth

Move the remaining authentication-specific code to the new file.
This commit is contained in:
Miroslav Lichvar 2019-08-15 11:20:36 +02:00
parent ca28dbd2c3
commit 56a102ed4d
3 changed files with 122 additions and 106 deletions

View file

@ -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)
{

View file

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

View file

@ -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;
}
/* ================================================== */