ntp: add support for interleaved client/server mode
Adapt the interleaved symmetric mode for client/server associations. On server, save the state needed for detection and responding in the interleaved mode in the client log. On client, enable the interleaved mode when the server is specified with the xleave option. Always accept responses in basic mode to allow synchronization with servers that don't support the interleaved mode, have too many clients, or have multiple clients behing the same IP address. This is also necessary to prevent DoS attacks on the client by overwriting or flushing the server state. Protect the client's state variables against replay attacks as the timestamps are now needed when processing the subsequent packet.
This commit is contained in:
parent
bd736f9234
commit
96d652e5bd
4 changed files with 98 additions and 19 deletions
36
clientlog.c
36
clientlog.c
|
@ -39,6 +39,7 @@
|
|||
#include "clientlog.h"
|
||||
#include "conf.h"
|
||||
#include "memory.h"
|
||||
#include "ntp.h"
|
||||
#include "reports.h"
|
||||
#include "util.h"
|
||||
#include "logging.h"
|
||||
|
@ -57,6 +58,8 @@ typedef struct {
|
|||
int8_t cmd_rate;
|
||||
int8_t ntp_timeout_rate;
|
||||
uint8_t flags;
|
||||
NTP_int64 ntp_rx_ts;
|
||||
NTP_int64 ntp_tx_ts;
|
||||
} Record;
|
||||
|
||||
/* Hash table of records, there is a fixed number of records per slot */
|
||||
|
@ -206,6 +209,8 @@ get_record(IPAddr *ip)
|
|||
record->ntp_rate = record->cmd_rate = INVALID_RATE;
|
||||
record->ntp_timeout_rate = INVALID_RATE;
|
||||
record->flags = 0;
|
||||
record->ntp_rx_ts.hi = record->ntp_rx_ts.lo = 0;
|
||||
record->ntp_tx_ts.hi = record->ntp_tx_ts.lo = 0;
|
||||
|
||||
return record;
|
||||
}
|
||||
|
@ -405,6 +410,23 @@ get_index(Record *record)
|
|||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CLG_GetClientIndex(IPAddr *client)
|
||||
{
|
||||
Record *record;
|
||||
|
||||
if (!active)
|
||||
return -1;
|
||||
|
||||
record = get_record(client);
|
||||
if (record == NULL)
|
||||
return -1;
|
||||
|
||||
return get_index(record);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CLG_LogNTPAccess(IPAddr *client, struct timespec *now)
|
||||
{
|
||||
|
@ -553,7 +575,19 @@ CLG_LimitCommandResponseRate(int index)
|
|||
|
||||
/* ================================================== */
|
||||
|
||||
extern int
|
||||
void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
|
||||
{
|
||||
Record *record;
|
||||
|
||||
record = ARR_GetElement(records, index);
|
||||
|
||||
*rx_ts = &record->ntp_rx_ts;
|
||||
*tx_ts = &record->ntp_tx_ts;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CLG_GetNumberOfIndices(void)
|
||||
{
|
||||
if (!active)
|
||||
|
|
|
@ -33,10 +33,12 @@
|
|||
|
||||
extern void CLG_Initialise(void);
|
||||
extern void CLG_Finalise(void);
|
||||
extern int CLG_GetClientIndex(IPAddr *client);
|
||||
extern int CLG_LogNTPAccess(IPAddr *client, struct timespec *now);
|
||||
extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now);
|
||||
extern int CLG_LimitNTPResponseRate(int index);
|
||||
extern int CLG_LimitCommandResponseRate(int index);
|
||||
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
|
||||
|
||||
/* And some reporting functions, for use by chronyc. */
|
||||
|
||||
|
|
77
ntp_core.c
77
ntp_core.c
|
@ -471,17 +471,17 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
|
|||
/* Client socket will be obtained when sending request */
|
||||
result->local_addr.sock_fd = INVALID_SOCK_FD;
|
||||
result->mode = MODE_CLIENT;
|
||||
result->interleaved = 0;
|
||||
break;
|
||||
case NTP_PEER:
|
||||
result->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr);
|
||||
result->mode = MODE_ACTIVE;
|
||||
result->interleaved = params->interleaved;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
result->interleaved = params->interleaved;
|
||||
|
||||
result->minpoll = params->minpoll;
|
||||
if (result->minpoll < MIN_POLL)
|
||||
result->minpoll = SRC_DEFAULT_MINPOLL;
|
||||
|
@ -1446,10 +1446,11 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
|
||||
/* Update the NTP timestamps. If it's a valid packet from a synchronised
|
||||
source, the timestamps may be used later when processing a packet in the
|
||||
interleaved mode. The authentication test (test5) is required to prevent
|
||||
denial-of-service attacks using unauthenticated packets on authenticated
|
||||
symmetric associations. */
|
||||
if (test5) {
|
||||
interleaved mode. Protect the timestamps against replay attacks in client
|
||||
mode. The authentication test (test5) is required to prevent DoS attacks
|
||||
using unauthenticated packets on authenticated symmetric associations. */
|
||||
if ((inst->mode == MODE_CLIENT && valid_packet && !inst->valid_rx) ||
|
||||
(inst->mode == MODE_ACTIVE && test5)) {
|
||||
inst->remote_ntp_rx = message->receive_ts;
|
||||
inst->remote_ntp_tx = message->transmit_ts;
|
||||
inst->local_rx = *rx_ts;
|
||||
|
@ -1738,7 +1739,9 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
|
|||
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
|
||||
{
|
||||
NTP_Mode pkt_mode, my_mode;
|
||||
int valid_auth, log_index;
|
||||
NTP_int64 *local_ntp_rx, *local_ntp_tx;
|
||||
NTP_Local_Timestamp local_tx, *tx_ts;
|
||||
int valid_auth, log_index, interleaved;
|
||||
AuthenticationMode auth_mode;
|
||||
uint32_t key_id;
|
||||
|
||||
|
@ -1803,15 +1806,41 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
|
|||
}
|
||||
}
|
||||
|
||||
/* Send a reply.
|
||||
- copy the poll value as the client may use it to control its polling
|
||||
interval
|
||||
- originate timestamp is the client's transmit time
|
||||
- don't save our transmit timestamp as we aren't maintaining state about
|
||||
this client */
|
||||
transmit_packet(my_mode, 0, message->poll, NTP_LVM_TO_VERSION(message->lvm),
|
||||
local_ntp_rx = local_ntp_tx = NULL;
|
||||
tx_ts = NULL;
|
||||
interleaved = 0;
|
||||
|
||||
/* Check if the client is using the interleaved mode. If it is, save the
|
||||
new transmit timestamp and if the old transmit timestamp is valid, respond
|
||||
in the interleaved mode. This means the third reply to a new client is
|
||||
the earliest one that can be interleaved. We don't want to waste time
|
||||
on clients that are not using the interleaved mode. */
|
||||
if (log_index >= 0) {
|
||||
CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
|
||||
interleaved = (local_ntp_rx->hi || local_ntp_rx->lo) &&
|
||||
message->originate_ts.hi == local_ntp_rx->hi &&
|
||||
message->originate_ts.lo == local_ntp_rx->lo;
|
||||
|
||||
if (interleaved) {
|
||||
if (local_ntp_tx->hi || local_ntp_tx->lo)
|
||||
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
|
||||
else
|
||||
interleaved = 0;
|
||||
tx_ts = &local_tx;
|
||||
} else {
|
||||
local_ntp_tx->hi = local_ntp_tx->lo = 0;
|
||||
local_ntp_tx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send a reply */
|
||||
transmit_packet(my_mode, interleaved, message->poll, NTP_LVM_TO_VERSION(message->lvm),
|
||||
auth_mode, key_id, &message->receive_ts, &message->transmit_ts,
|
||||
rx_ts, NULL, NULL, NULL, remote_addr, local_addr);
|
||||
rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr);
|
||||
|
||||
/* Save the transmit timestamp */
|
||||
if (tx_ts)
|
||||
UTI_TimespecToNtp64(&tx_ts->ts, local_ntp_tx, NULL);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
@ -1877,8 +1906,22 @@ void
|
|||
NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
||||
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
|
||||
{
|
||||
/* Nothing to do yet */
|
||||
DEBUG_LOG(LOGF_NtpCore, "Process TX unknown");
|
||||
NTP_int64 *local_ntp_rx, *local_ntp_tx;
|
||||
NTP_Local_Timestamp local_tx;
|
||||
int log_index;
|
||||
|
||||
if (!check_packet_format(message, length))
|
||||
return;
|
||||
|
||||
log_index = CLG_GetClientIndex(&remote_addr->ip_addr);
|
||||
if (log_index < 0)
|
||||
return;
|
||||
|
||||
CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
|
||||
|
||||
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
|
||||
update_tx_timestamp(&local_tx, tx_ts, local_ntp_rx, NULL, message);
|
||||
UTI_TimespecToNtp64(&local_tx.ts, local_ntp_tx, NULL);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
|
|
@ -66,7 +66,7 @@ test_unit(void)
|
|||
}
|
||||
|
||||
DEBUG_LOG(0, "records %d", ARR_GetSize(records));
|
||||
TEST_CHECK(ARR_GetSize(records) == 128);
|
||||
TEST_CHECK(ARR_GetSize(records) == 64);
|
||||
|
||||
for (i = j = 0; i < 10000; i++) {
|
||||
ts.tv_sec += 1;
|
||||
|
|
Loading…
Reference in a new issue