ntp: move auth parsing to ntp_auth
Move the remaining authentication-specific code to the new file.
This commit is contained in:
parent
ca28dbd2c3
commit
56a102ed4d
3 changed files with 122 additions and 106 deletions
115
ntp_auth.c
115
ntp_auth.c
|
@ -32,6 +32,7 @@
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "ntp_auth.h"
|
#include "ntp_auth.h"
|
||||||
|
#include "ntp_ext.h"
|
||||||
#include "ntp_signd.h"
|
#include "ntp_signd.h"
|
||||||
#include "srcparams.h"
|
#include "srcparams.h"
|
||||||
#include "util.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
|
static NAU_Instance
|
||||||
create_instance(NTP_AuthMode mode)
|
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
|
int
|
||||||
NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info)
|
NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info)
|
||||||
{
|
{
|
||||||
|
|
|
@ -56,6 +56,9 @@ extern void NAU_AdjustRequestTimestamp(NAU_Instance instance, struct timespec *t
|
||||||
extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request,
|
extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request,
|
||||||
NTP_PacketInfo *info);
|
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 */
|
/* Verify that a request is authentic */
|
||||||
extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info);
|
extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info);
|
||||||
|
|
||||||
|
|
110
ntp_core.c
110
ntp_core.c
|
@ -32,7 +32,6 @@
|
||||||
#include "array.h"
|
#include "array.h"
|
||||||
#include "ntp_auth.h"
|
#include "ntp_auth.h"
|
||||||
#include "ntp_core.h"
|
#include "ntp_core.h"
|
||||||
#include "ntp_ext.h"
|
|
||||||
#include "ntp_io.h"
|
#include "ntp_io.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "sched.h"
|
#include "sched.h"
|
||||||
|
@ -44,7 +43,6 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "keys.h"
|
|
||||||
#include "addrfilt.h"
|
#include "addrfilt.h"
|
||||||
#include "clientlog.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
|
static int
|
||||||
parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info)
|
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) {
|
if (length < NTP_HEADER_LENGTH || length % 4U != 0) {
|
||||||
DEBUG_LOG("NTP packet has invalid length %d", length);
|
DEBUG_LOG("NTP packet has invalid length %d", length);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1303,95 +1285,11 @@ parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = (void *)packet;
|
/* Parse authentication extension fields or MAC */
|
||||||
parsed = NTP_HEADER_LENGTH;
|
if (!NAU_ParsePacket(packet, info))
|
||||||
remainder = length - parsed;
|
return 0;
|
||||||
|
|
||||||
/* Check if this is a plain NTP packet with no extension fields or MAC */
|
return 1;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
Loading…
Reference in a new issue