Compare commits

...

28 commits

Author SHA1 Message Date
Miroslav Lichvar
b9b338a8df refclock: rework update of reachability
Update the reachability register of a refclock source by 1 if a valid
measurement is received by the drivers between source polls, and not
only when it is accumulated to sourcestats, similarly to how
reachability works with NTP sources.

This avoids drops in the reported reachability when a PHC refclock is
dropping samples due to significant changes in the measured delay (e.g.
due to high PCIe load), or a PPS refclock dropping samples due to failed
lock.
2024-11-05 15:51:43 +01:00
Vincent Blut
a5d73f692f doc: fix typo in chrony.conf man page 2024-10-09 09:05:41 +02:00
Miroslav Lichvar
b0ac5992fb doc: update NEWS 2024-10-08 14:49:43 +02:00
Miroslav Lichvar
cd65e32cf0 doc: warn about MD5 keys not protecting extension fields
Add a warning to the chrony.conf man page that MD5 keys cannot protect
NTP extension fields due to the length extension attack.
2024-10-08 14:49:41 +02:00
Miroslav Lichvar
b9f5278846 update copyright years 2024-10-08 12:11:32 +02:00
Miroslav Lichvar
b8b166044f nts: don't include compliant-128gcm record for other AEADs
If the client included the NTS-KE record requesting compliant key
exporter context for AES-128-GCM-SIV, but the server doesn't select this
AEAD algorithm (it's not supported by the crypto library or it is
disabled by the ntsaeads directive), don't include the NTS-KE record in
the response. It's not relevant to the other AEAD algorithms.
2024-10-08 12:11:05 +02:00
Miroslav Lichvar
42fbf41686 nts: make server and client AEAD algorithms configurable
Add ntsaeads directive to specify a list of AEAD algorithms enabled for
NTS. The list is shared between the server and client. For the client it
also specifies the order of priority. The default is "30 15", matching
the previously hardcoded preference of AES-128-GCM-SIV (30) over
AES-SIV-CMAC-256 (15).
2024-10-03 16:09:53 +02:00
Miroslav Lichvar
79a790e6b5 test: improve nts_ke_client unit test 2024-10-03 15:02:16 +02:00
Miroslav Lichvar
f5cd79d2df nts: check TLS session in NKSN_GetKeys()
Make sure the TLS session is not NULL in NKSN_GetKeys() before trying to
export the keys in case some future code tried to call the function
outside of the NTS-KE message handler.
2024-10-03 15:02:16 +02:00
Miroslav Lichvar
689605b6a2 nts: switch client to compliant key exporter on NTS NAK
Implement a fallback for the NTS-NTP client to switch to the compliant
AES-128-GCM-SIV exporter context when the server is using the compliant
context, but does not support the new NTS-KE record negotiating its use,
assuming it can respond with an NTS NAK to the request authenticated
with the incorrect key.

Export both sets of keys when processing the NTS-KE response. If an NTS
NAK is the only valid response from the server after the last NTS-KE
session, switch to the keys exported with the compliant context for the
following requests instead of dropping all cookies and restarting
NTS-KE. Don't switch back to the original keys if an NTS NAK is received
again.
2024-10-03 15:02:03 +02:00
Miroslav Lichvar
0707865413 nts: negotiate compliant export of AES-128-GCM-SIV keys
Add client and server support for a new NTS-KE record to negotiate use
of the compliant key exporter context with the AES-128-GCM-SIV AEAD as
specified here:

https://chrony-project.org/doc/spec/nts-compliant-128gcm.html
2024-09-26 16:04:01 +02:00
Miroslav Lichvar
2adda9c12c nts: construct key exporter context
When the NTS client and server negotiated use of AES-128-GCM-SIV keys,
the keys exported from the TLS session and used for authentication and
encryption of NTP messages do not comply to RFC8915. The exporter
context value specified in the section 5.1 of RFC8915 function is
incorrect. It is a hardcoded string which contains 15 (AES-SIV-CMAC-256)
instead of 30 (AES-128-GCM-SIV). This causes chrony to not interoperate
with NTS implementations that follow RFC8915 correctly. (At this time,
there doesn't seem to be another implementation with AES-128-GCM-SIV
support yet.)

Replace the string with a proper construction of the exporter context
from a specified AEAD ID and next protocol.

Keep using the incorrect AEAD ID for AES-128-GCM-SIV to not break
compatibility with existing chrony servers and clients. A new NTS-KE
record will be added to negotiate the compliant exporter context.

Reported-by: Martin Mayer <martin.mayer@m2-it-solutions.de>
2024-09-26 12:45:44 +02:00
Miroslav Lichvar
113d1134d1 doc: update NEWS 2024-08-29 10:28:49 +02:00
Miroslav Lichvar
b363af754d doc: update README 2024-08-29 09:37:50 +02:00
Miroslav Lichvar
0f5cf57bc2 update copyright years 2024-08-29 09:37:50 +02:00
Miroslav Lichvar
5a43f0c39b test: make 110-chronyc more reliable 2024-08-29 09:37:50 +02:00
Miroslav Lichvar
5a6fbe7a4b test: make 106-refclock more reliable 2024-08-29 09:37:50 +02:00
Miroslav Lichvar
bb34e92f96 test: make 108-peer more reliable 2024-08-29 09:37:50 +02:00
Miroslav Lichvar
78b9c13a11 sources: replace unreachable sources before selection
The commit c43efccf02 ("sources: update source selection with
unreachable sources") caused a high rate of failures in the
148-replacement test (1 falseticker vs 2 unreachable sources). This was
due to a larger fraction of the replacement attempts being made for the
source incorrectly marked as a falseticker instead of the second
unreachable source and the random process needed more time to get to the
expected state with both unreachable sources replaced.

When updating reachability of an unreachable source, try to request the
replacement of the source before calling the source selection, where
other sources may be replaced, to better balance the different
replacement attempts.
2024-08-29 09:37:42 +02:00
Luca Boccassi
1ab5b88939 conf: do not check, write and delete PID file if set to '/'
If the pid file path is specified as '/', skip handling it,
as it is not only unnecessary but complicates managing the
service. A systemd unit can manage the program without any
need for this functionality, and it makes process tracking
simpler and more robust.
The implementation matches the bindcmdaddress directive.
2024-08-22 14:24:49 +02:00
Luca Boccassi
e30f937f6a doc: explain how to disable DNSSEC validation with sd-resolved in FAQ
DNSSEC requires the system time to be synced in order to work,
as the signature date and expiration need to be checked by
resolvers. But it is possible that syncing the times requires
doing DNS queries. Add a paragraph to the FAQ explaining how
to break this cycle by asking nss-resolved to always avoid
DNSSEC when chronyd tries to resolve hostnames.
2024-08-22 14:24:49 +02:00
Miroslav Lichvar
08b67dba98 ntp: fix finalization for async resolver
If an attempt to resolve addresses of an NTP server is made right before
starting the termination sequence, the asynchronous resolver thread
could read the server name when it was already freed.

Leave unresolved sources allocated in NSR_Finalise() if the async
resolver did not finish yet, at least for now. Waiting for the resolving
result or cancelling the thread would complicate the code. The scheduler
is not expected to be running at this point.
2024-08-22 09:32:36 +02:00
Miroslav Lichvar
61f15fedcd doc: add new question about accuracy to FAQ 2024-08-21 11:24:24 +02:00
Miroslav Lichvar
6d59234995 doc: clarify hostname with nts option 2024-08-20 14:18:54 +02:00
Miroslav Lichvar
d4a4f89329 conf: don't repeat error message when adding sourcedir source
When a source from a configured sourcedir cannot be added (e.g. it is a
duplicate of another source), log the error message only on the first
attempt adding the source, until the source is removed and added to a
sourcedir again.

This avoids spamming of the system log with error messages if the
reload sources command is called frequently (e.g. from a DHCP renewal
networking script).
2024-08-07 09:48:24 +02:00
Miroslav Lichvar
916ed70c4a conf: save source status in sourcedir reload
Save the NSR status when adding a source from a sourcedir and don't
hide sources that failed the addition by clearing their name.
2024-08-07 09:48:13 +02:00
Miroslav Lichvar
8ba2da52df conf: merge ntp_source_ids with ntp_sources
Keep the configuration IDs of sources loaded from sourcedir in the
NTP_Source structure itself to simplify the code.
2024-08-07 09:46:00 +02:00
Miroslav Lichvar
fd9e956d27 test: extend 008-confload test 2024-08-06 11:27:13 +02:00
41 changed files with 650 additions and 117 deletions

15
NEWS
View file

@ -1,3 +1,17 @@
New in version 4.6.1
====================
Enhancements
------------
* Add ntsaeads directive to enable only selected AEAD algorithms for NTS
Workarounds
-----------
* Negotiate use of compliant NTS keys with AES-128-GCM-SIV AEAD algorithm
(by default the keys are generated differently than in RFC 8915 for
compatibility with chrony server and client versions 4.4, 4.5, and 4.6)
* Switch to compliant NTS keys if first response from server is NTS NAK
New in version 4.6 New in version 4.6
================== ==================
@ -8,6 +22,7 @@ Enhancements
* Add kod option to ratelimit directive for server KoD RATE support * Add kod option to ratelimit directive for server KoD RATE support
* Add leapseclist directive to read NIST/IERS leap-seconds.list file * Add leapseclist directive to read NIST/IERS leap-seconds.list file
* Add ptpdomain directive to set PTP domain for NTP over PTP * Add ptpdomain directive to set PTP domain for NTP over PTP
* Allow disabling pidfile
* Improve copy server option to accept unsynchronised status instantly * Improve copy server option to accept unsynchronised status instantly
* Log one selection failure on start * Log one selection failure on start
* Add offset command to modify source offset correction * Add offset command to modify source offset correction

1
README
View file

@ -75,6 +75,7 @@ Lonnie Abelbeck <lonnie@abelbeck.com>
Benny Lyne Amorsen <benny@amorsen.dk> Benny Lyne Amorsen <benny@amorsen.dk>
Andrew Bishop <amb@gedanken.demon.co.uk> Andrew Bishop <amb@gedanken.demon.co.uk>
Vincent Blut <vincent.debian@free.fr> Vincent Blut <vincent.debian@free.fr>
Luca Boccassi <bluca@debian.org>
Stephan I. Boettcher <stephan@nevis1.columbia.edu> Stephan I. Boettcher <stephan@nevis1.columbia.edu>
David Bohman <debohman@gmail.com> David Bohman <debohman@gmail.com>
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de> Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>

View file

@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016, 2018 * Copyright (C) Lonnie Abelbeck 2016, 2018
* Copyright (C) Miroslav Lichvar 2009-2023 * Copyright (C) Miroslav Lichvar 2009-2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@ -3440,7 +3440,7 @@ static void
display_gpl(void) display_gpl(void)
{ {
printf("chrony version %s\n" printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2023 Richard P. Curnow and others\n" "Copyright (C) 1997-2003, 2007, 2009-2024 Richard P. Curnow and others\n"
"chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n" "chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
"you are welcome to redistribute it under certain conditions. See the\n" "you are welcome to redistribute it under certain conditions. See the\n"
"GNU General Public License version 2 for details.\n\n", "GNU General Public License version 2 for details.\n\n",

View file

@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009, 2015-2017, 2021 * Copyright (C) Miroslav Lichvar 2009, 2015-2017, 2021, 2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as

View file

@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2023 * Copyright (C) Miroslav Lichvar 2009-2016, 2018-2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as

104
conf.c
View file

@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2017, 2020 * Copyright (C) Miroslav Lichvar 2009-2017, 2020, 2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@ -57,6 +57,7 @@ static int parse_string(char *line, char **result);
static int parse_int(char *line, int *result); static int parse_int(char *line, int *result);
static int parse_double(char *line, double *result); static int parse_double(char *line, double *result);
static int parse_null(char *line); static int parse_null(char *line);
static int parse_ints(char *line, ARR_Instance array);
static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow); static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow);
static void parse_authselectmode(char *); static void parse_authselectmode(char *);
@ -78,6 +79,7 @@ static void parse_makestep(char *);
static void parse_maxchange(char *); static void parse_maxchange(char *);
static void parse_ntsserver(char *, ARR_Instance files); static void parse_ntsserver(char *, ARR_Instance files);
static void parse_ntstrustedcerts(char *); static void parse_ntstrustedcerts(char *);
static void parse_pidfile(char *line);
static void parse_ratelimit(char *line, int *enabled, int *interval, static void parse_ratelimit(char *line, int *enabled, int *interval,
int *burst, int *leak, int *kod); int *burst, int *leak, int *kod);
static void parse_refclock(char *); static void parse_refclock(char *);
@ -260,7 +262,10 @@ static char *user;
/* Address refresh interval */ /* Address refresh interval */
static int refresh = 1209600; /* 2 weeks */ static int refresh = 1209600; /* 2 weeks */
#define DEFAULT_NTS_AEADS "30 15"
/* NTS server and client configuration */ /* NTS server and client configuration */
static ARR_Instance nts_aeads; /* array of int */
static char *nts_dump_dir = NULL; static char *nts_dump_dir = NULL;
static char *nts_ntp_server = NULL; static char *nts_ntp_server = NULL;
static ARR_Instance nts_server_cert_files; /* array of (char *) */ static ARR_Instance nts_server_cert_files; /* array of (char *) */
@ -294,15 +299,15 @@ typedef struct {
NTP_Source_Type type; NTP_Source_Type type;
int pool; int pool;
CPS_NTP_Source params; CPS_NTP_Source params;
NSR_Status status;
uint32_t conf_id;
} NTP_Source; } NTP_Source;
/* Array of NTP_Source */ /* Array of NTP_Source */
static ARR_Instance ntp_sources; static ARR_Instance ntp_sources;
/* Array of (char *) */ /* Array of (char *) */
static ARR_Instance ntp_source_dirs; static ARR_Instance ntp_source_dirs;
/* Array of uint32_t corresponding to ntp_sources (for sourcedirs reload) */ /* Flag indicating ntp_sources is used for sourcedirs after config load */
static ARR_Instance ntp_source_ids;
/* Flag indicating ntp_sources and ntp_source_ids are used for sourcedirs */
static int conf_ntp_sources_added = 0; static int conf_ntp_sources_added = 0;
/* Array of RefclockParameters */ /* Array of RefclockParameters */
@ -396,6 +401,8 @@ check_number_of_args(char *line, int num)
void void
CNF_Initialise(int r, int client_only) CNF_Initialise(int r, int client_only)
{ {
char buf[10];
restarted = r; restarted = r;
hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface)); hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface));
@ -403,13 +410,15 @@ CNF_Initialise(int r, int client_only)
init_sources = ARR_CreateInstance(sizeof (IPAddr)); init_sources = ARR_CreateInstance(sizeof (IPAddr));
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source)); ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
ntp_source_dirs = ARR_CreateInstance(sizeof (char *)); ntp_source_dirs = ARR_CreateInstance(sizeof (char *));
ntp_source_ids = ARR_CreateInstance(sizeof (uint32_t));
refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters)); refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters));
broadcasts = ARR_CreateInstance(sizeof (NTP_Broadcast_Destination)); broadcasts = ARR_CreateInstance(sizeof (NTP_Broadcast_Destination));
ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny)); ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny)); cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
nts_aeads = ARR_CreateInstance(sizeof (int));
snprintf(buf, sizeof (buf), DEFAULT_NTS_AEADS);
parse_ints(buf, nts_aeads);
nts_server_cert_files = ARR_CreateInstance(sizeof (char *)); nts_server_cert_files = ARR_CreateInstance(sizeof (char *));
nts_server_key_files = ARR_CreateInstance(sizeof (char *)); nts_server_key_files = ARR_CreateInstance(sizeof (char *));
nts_trusted_certs_paths = ARR_CreateInstance(sizeof (char *)); nts_trusted_certs_paths = ARR_CreateInstance(sizeof (char *));
@ -463,13 +472,13 @@ CNF_Finalise(void)
ARR_DestroyInstance(init_sources); ARR_DestroyInstance(init_sources);
ARR_DestroyInstance(ntp_sources); ARR_DestroyInstance(ntp_sources);
ARR_DestroyInstance(ntp_source_dirs); ARR_DestroyInstance(ntp_source_dirs);
ARR_DestroyInstance(ntp_source_ids);
ARR_DestroyInstance(refclock_sources); ARR_DestroyInstance(refclock_sources);
ARR_DestroyInstance(broadcasts); ARR_DestroyInstance(broadcasts);
ARR_DestroyInstance(ntp_restrictions); ARR_DestroyInstance(ntp_restrictions);
ARR_DestroyInstance(cmd_restrictions); ARR_DestroyInstance(cmd_restrictions);
ARR_DestroyInstance(nts_aeads);
ARR_DestroyInstance(nts_server_cert_files); ARR_DestroyInstance(nts_server_cert_files);
ARR_DestroyInstance(nts_server_key_files); ARR_DestroyInstance(nts_server_key_files);
ARR_DestroyInstance(nts_trusted_certs_paths); ARR_DestroyInstance(nts_trusted_certs_paths);
@ -680,6 +689,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
no_system_cert = parse_null(p); no_system_cert = parse_null(p);
} else if (!strcasecmp(command, "ntpsigndsocket")) { } else if (!strcasecmp(command, "ntpsigndsocket")) {
parse_string(p, &ntp_signd_socket); parse_string(p, &ntp_signd_socket);
} else if (!strcasecmp(command, "ntsaeads")) {
parse_ints(p, nts_aeads);
} else if (!strcasecmp(command, "ntsratelimit")) { } else if (!strcasecmp(command, "ntsratelimit")) {
parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval, parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
&nts_ratelimit_burst, &nts_ratelimit_leak, NULL); &nts_ratelimit_burst, &nts_ratelimit_leak, NULL);
@ -705,7 +716,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "peer")) { } else if (!strcasecmp(command, "peer")) {
parse_source(p, command, 1); parse_source(p, command, 1);
} else if (!strcasecmp(command, "pidfile")) { } else if (!strcasecmp(command, "pidfile")) {
parse_string(p, &pidfile); parse_pidfile(p);
} else if (!strcasecmp(command, "pool")) { } else if (!strcasecmp(command, "pool")) {
parse_source(p, command, 1); parse_source(p, command, 1);
} else if (!strcasecmp(command, "port")) { } else if (!strcasecmp(command, "port")) {
@ -807,6 +818,25 @@ parse_null(char *line)
/* ================================================== */ /* ================================================== */
static int
parse_ints(char *line, ARR_Instance array)
{
char *s;
int v;
ARR_SetSize(array, 0);
while (*line) {
s = line;
line = CPS_SplitWord(line);
parse_int(s, &v);
ARR_AppendElement(array, &v);
}
return 1;
}
/* ================================================== */
static void static void
parse_source(char *line, char *type, int fatal) parse_source(char *line, char *type, int fatal)
{ {
@ -837,6 +867,9 @@ parse_source(char *line, char *type, int fatal)
} }
source.params.name = Strdup(source.params.name); source.params.name = Strdup(source.params.name);
source.status = NSR_NoSuchSource;
source.conf_id = 0;
ARR_AppendElement(ntp_sources, &source); ARR_AppendElement(ntp_sources, &source);
} }
@ -1528,6 +1561,20 @@ parse_hwtimestamp(char *line)
/* ================================================== */ /* ================================================== */
static void
parse_pidfile(char *line)
{
parse_string(line, &pidfile);
/* / disables the PID file handling */
if (strcmp(pidfile, "/") == 0) {
Free(pidfile);
pidfile = NULL;
}
}
/* ================================================== */
static const char * static const char *
get_basename(const char *path) get_basename(const char *path)
{ {
@ -1694,7 +1741,6 @@ reload_source_dirs(void)
{ {
NTP_Source *prev_sources, *new_sources, *source; NTP_Source *prev_sources, *new_sources, *source;
unsigned int i, j, prev_size, new_size, unresolved; unsigned int i, j, prev_size, new_size, unresolved;
uint32_t *prev_ids, *new_ids;
char buf[MAX_LINE_LENGTH]; char buf[MAX_LINE_LENGTH];
NSR_Status s; NSR_Status s;
int d, pass; int d, pass;
@ -1703,13 +1749,9 @@ reload_source_dirs(void)
if (!conf_ntp_sources_added) if (!conf_ntp_sources_added)
return; return;
prev_size = ARR_GetSize(ntp_source_ids); prev_size = ARR_GetSize(ntp_sources);
if (ARR_GetSize(ntp_sources) != prev_size)
assert(0);
/* Save the current sources and their configuration IDs */ /* Save the current sources */
prev_ids = MallocArray(uint32_t, prev_size);
memcpy(prev_ids, ARR_GetElements(ntp_source_ids), prev_size * sizeof (prev_ids[0]));
prev_sources = MallocArray(NTP_Source, prev_size); prev_sources = MallocArray(NTP_Source, prev_size);
memcpy(prev_sources, ARR_GetElements(ntp_sources), prev_size * sizeof (prev_sources[0])); memcpy(prev_sources, ARR_GetElements(ntp_sources), prev_size * sizeof (prev_sources[0]));
@ -1727,8 +1769,6 @@ reload_source_dirs(void)
new_size = ARR_GetSize(ntp_sources); new_size = ARR_GetSize(ntp_sources);
new_sources = ARR_GetElements(ntp_sources); new_sources = ARR_GetElements(ntp_sources);
ARR_SetSize(ntp_source_ids, new_size);
new_ids = ARR_GetElements(ntp_source_ids);
unresolved = 0; unresolved = 0;
LOG_SetContext(LOGC_SourceFile); LOG_SetContext(LOGC_SourceFile);
@ -1743,31 +1783,31 @@ reload_source_dirs(void)
d = i < prev_size ? -1 : 1; d = i < prev_size ? -1 : 1;
/* Remove missing sources before adding others to avoid conflicts */ /* Remove missing sources before adding others to avoid conflicts */
if (pass == 0 && d < 0 && prev_sources[i].params.name[0] != '\0') { if (pass == 0 && d < 0 && prev_sources[i].status == NSR_Success) {
NSR_RemoveSourcesById(prev_ids[i]); NSR_RemoveSourcesById(prev_sources[i].conf_id);
} }
/* Add new sources */ /* Add new sources and sources that could not be added before */
if (pass == 1 && d > 0) { if (pass == 1 && (d > 0 || (d == 0 && prev_sources[i].status != NSR_Success))) {
source = &new_sources[j]; source = &new_sources[j];
s = NSR_AddSourceByName(source->params.name, source->params.family, source->params.port, s = NSR_AddSourceByName(source->params.name, source->params.family, source->params.port,
source->pool, source->type, &source->params.params, source->pool, source->type, &source->params.params,
&new_ids[j]); &source->conf_id);
source->status = s;
if (s == NSR_UnresolvedName) { if (s == NSR_UnresolvedName) {
unresolved++; unresolved++;
} else if (s != NSR_Success) { } else if (s != NSR_Success && (d > 0 || s != prev_sources[i].status)) {
LOG(LOGS_ERR, "Could not add source %s : %s", LOG(LOGS_ERR, "Could not add source %s : %s",
source->params.name, NSR_StatusToString(s)); source->params.name, NSR_StatusToString(s));
/* Mark the source as not present */
source->params.name[0] = '\0';
} }
} }
/* Keep unchanged sources */ /* Keep unchanged sources */
if (pass == 1 && d == 0) if (pass == 1 && d == 0) {
new_ids[j] = prev_ids[i]; new_sources[j].status = prev_sources[i].status;
new_sources[j].conf_id = prev_sources[i].conf_id;
}
} }
} }
@ -1776,7 +1816,6 @@ reload_source_dirs(void)
for (i = 0; i < prev_size; i++) for (i = 0; i < prev_size; i++)
Free(prev_sources[i].params.name); Free(prev_sources[i].params.name);
Free(prev_sources); Free(prev_sources);
Free(prev_ids);
if (unresolved > 0) if (unresolved > 0)
NSR_ResolveSources(); NSR_ResolveSources();
@ -1875,7 +1914,6 @@ CNF_AddSources(void)
/* The arrays will be used for sourcedir (re)loading */ /* The arrays will be used for sourcedir (re)loading */
ARR_SetSize(ntp_sources, 0); ARR_SetSize(ntp_sources, 0);
ARR_SetSize(ntp_source_ids, 0);
conf_ntp_sources_added = 1; conf_ntp_sources_added = 1;
reload_source_dirs(); reload_source_dirs();
@ -2592,6 +2630,14 @@ CNF_GetRefresh(void)
/* ================================================== */ /* ================================================== */
ARR_Instance
CNF_GetNtsAeads(void)
{
return nts_aeads;
}
/* ================================================== */
char * char *
CNF_GetNtsDumpDir(void) CNF_GetNtsDumpDir(void)
{ {

2
conf.h
View file

@ -29,6 +29,7 @@
#define GOT_CONF_H #define GOT_CONF_H
#include "addressing.h" #include "addressing.h"
#include "array.h"
#include "reference.h" #include "reference.h"
#include "sources.h" #include "sources.h"
@ -163,6 +164,7 @@ extern int CNF_GetPtpDomain(void);
extern int CNF_GetRefresh(void); extern int CNF_GetRefresh(void);
extern ARR_Instance CNF_GetNtsAeads(void);
extern char *CNF_GetNtsDumpDir(void); extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void); extern char *CNF_GetNtsNtpServer(void);
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys); extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);

View file

@ -3,7 +3,7 @@
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016 // Copyright (C) Stephen Wadeley 2016
// Copyright (C) Bryan Christianson 2017 // Copyright (C) Bryan Christianson 2017
// Copyright (C) Miroslav Lichvar 2009-2023 // Copyright (C) Miroslav Lichvar 2009-2024
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as // it under the terms of version 2 of the GNU General Public License as
@ -126,6 +126,15 @@ mechanism. Unlike with the *key* option, the server and client do not need to
share a key in a key file. NTS has a Key Establishment (NTS-KE) protocol using share a key in a key file. NTS has a Key Establishment (NTS-KE) protocol using
the Transport Layer Security (TLS) protocol to get the keys and cookies the Transport Layer Security (TLS) protocol to get the keys and cookies
required by NTS for authentication of NTP packets. required by NTS for authentication of NTP packets.
+
With this option, the hostname specified in the server or pool directive is the
NTS-KE server or pool of NTS-KE servers respectively. The NTP server usually
runs on the same host, but it can be separated from the NTS-KE server (the
hostname or address of the NTP server is provided to the client by the NTS-KE
server).
+
The NTS-KE server can be specified by IP address if it is included in the
server's certificate as a Subject Alternative Name (SAN).
*certset* _ID_::: *certset* _ID_:::
This option specifies which set of trusted certificates should be used to verify This option specifies which set of trusted certificates should be used to verify
the server's certificate when the *nts* option is enabled. Sets of certificates the server's certificate when the *nts* option is enabled. Sets of certificates
@ -824,6 +833,34 @@ changes in the frequency and offset of the clock. The offsets in the
<<chronyc.adoc#sourcestats,*sourcestats*>> reports (and the _tracking.log_ and <<chronyc.adoc#sourcestats,*sourcestats*>> reports (and the _tracking.log_ and
_statistics.log_ files) may be smaller than the actual offsets. _statistics.log_ files) may be smaller than the actual offsets.
[[ntsaeads1]]*ntsaeads* _ID_...::
This directive specifies a list of IDs of Authenticated Encryption with
Associated Data (AEAD) algorithms enabled for NTS authentication of NTP
messages. The algorithms are specified in decreasing order of priority.
Algorithms that are not supported by the installed version of the crypto
library (Nettle, GnuTLS) are ignored.
+
The following IDs are supported:
+
* 15: AES-SIV-CMAC-256
* 30: AES-128-GCM-SIV
{blank}::
+
The default list of IDs is _30 15_. AES-128-GCM-SIV is preferred over
AES-SIV-CMAC-256 for shorter keys, which makes NTS cookies shorter and improves
reliability of NTS in networks that block or limit rate of longer NTP messages.
+
The ID of the used algorithm is reported for each server by the
<<chronyc.adoc#authdata,*authdata*>> command.
+
An example of the directive is:
+
----
ntsaeads 15
----
+
This list is used also by the <<ntsaeads2,NTS server>>.
[[ntsdumpdir1]]*ntsdumpdir* _directory_:: [[ntsdumpdir1]]*ntsdumpdir* _directory_::
This directive specifies a directory for the client to save NTS cookies it This directive specifies a directory for the client to save NTS cookies it
received from the server in order to avoid making an NTS-KE request when received from the server in order to avoid making an NTS-KE request when
@ -1770,6 +1807,43 @@ per process that the NTS server will accept. The default value is 100. The
maximum practical value is half of the system *FD_SETSIZE* constant (usually maximum practical value is half of the system *FD_SETSIZE* constant (usually
1024). 1024).
[[ntsaeads2]]*ntsaeads* _ID_...::
This directive specifies a list of IDs of Authenticated Encryption with
Associated Data (AEAD) algorithms enabled for NTS authentication of NTP
messages. *chronyd* as a server uses the first enabled algorithm from the list
provided by the client. Algorithms that are not supported by the installed
version of the crypto library (Nettle, GnuTLS) are ignored.
+
The following IDs are supported:
+
* 15: AES-SIV-CMAC-256
* 30: AES-128-GCM-SIV
{blank}::
+
The default list of IDs is _30 15_. AES-128-GCM-SIV is preferred over
AES-SIV-CMAC-256 for shorter keys, which makes NTS cookies shorter and improves
reliability of NTS in networks that block or limit rate of longer NTP messages.
+
An example of the directive is:
+
----
ntsaeads 15
----
+
This list is used also by the <<ntsaeads1,NTS client>>.
+
Note the the NTS specification (RFC 8915) requires servers to support
AES-SIV-CMAC-256, i.e. 15 should be always included in the specified list.
+
The AES-128-GCM-SIV keys used by *chronyd* do not comply to RFC 8915 for
compatibility with older *chrony* clients unless the use of compliant keys is
negotiated with an
https://chrony-project.org/doc/spec/nts-compliant-128gcm.html[NTS-KE record].
Support for this record was added in version 4.6.1. As a client, *chronyd* can
interoperate with a server that uses compliant keys, but does not support the
negotiation, if it responds to incorrectly authenticated requests with an NTS
NAK.
[[ntsdumpdir2]]*ntsdumpdir* _directory_:: [[ntsdumpdir2]]*ntsdumpdir* _directory_::
This directive specifies a directory where *chronyd* operating as an NTS server This directive specifies a directory where *chronyd* operating as an NTS server
can save the keys which encrypt NTS cookies provided to clients. The keys are can save the keys which encrypt NTS cookies provided to clients. The keys are
@ -2747,7 +2821,11 @@ source is specified in the configuration file with a key shorter than 80 bits.
+ +
The recommended key types are AES ciphers and SHA3 hash functions. MD5 should The recommended key types are AES ciphers and SHA3 hash functions. MD5 should
be avoided unless no other type is supported on the server and client, or be avoided unless no other type is supported on the server and client, or
peers. peers. A major weakness of MD5 for the NTP MAC is a length extension attack,
where a man-in-the-middle attacker can add arbitrary extension fields to the
NTP message and update the MAC to pass the verification of the extended
message. The *extfield* option (enabling processing of the specified extension
field) should not be used for NTP sources authenticated with an MD5 key.
+ +
The <<chronyc.adoc#keygen,*keygen*>> command of *chronyc* can be used to The <<chronyc.adoc#keygen,*keygen*>> command of *chronyc* can be used to
generate random keys for the key file. By default, it generates 160-bit MD5 or generate random keys for the key file. By default, it generates 160-bit MD5 or
@ -2772,6 +2850,8 @@ e.g.:
---- ----
pidfile /run/chronyd.pid pidfile /run/chronyd.pid
---- ----
+
Setting this directive to _/_ disables writing and checking of the PID file.
[[ptpport]]*ptpport* _port_:: [[ptpport]]*ptpport* _port_::
The *ptpport* directive enables *chronyd* to send and receive NTP messages The *ptpport* directive enables *chronyd* to send and receive NTP messages

View file

@ -2,7 +2,7 @@
// //
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016 // Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2023 // Copyright (C) Miroslav Lichvar 2009-2017, 2019-2024
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as // it under the terms of version 2 of the GNU General Public License as
@ -364,9 +364,12 @@ a measurement is being made every 64 seconds. *chronyd* automatically varies
the polling rate in response to prevailing conditions. the polling rate in response to prevailing conditions.
*Reach*::: *Reach*:::
This shows the source's reachability register printed as an octal number. The This shows the source's reachability register printed as an octal number. The
register has 8 bits and is updated on every received or missed packet from register has 8 bits. It is shifted to left by one bit with each poll and it is
the source. A value of 377 indicates that a valid reply was received for all updated by 1 when a valid NTP response, or just a sample in case of a reference
from the last eight transmissions. clock, is received from the source. A value of 377 indicates that a valid
response or sample was received for all of the last 8 polls. Note that samples
can be dropped if they are not considered good enough for synchronisation, but
the reachability register will still have 1s for their polls.
*LastRx*::: *LastRx*:::
This column shows how long ago the last good sample (which is shown in the next This column shows how long ago the last good sample (which is shown in the next
column) was received from the source. Measurements that failed some tests are column) was received from the source. Measurements that failed some tests are

View file

@ -2,7 +2,7 @@
// //
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Luke Valenta 2023 // Copyright (C) Luke Valenta 2023
// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2023 // Copyright (C) Miroslav Lichvar 2014-2016, 2020-2024
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as // it under the terms of version 2 of the GNU General Public License as
@ -772,6 +772,17 @@ print all sources, even those that do not have a known address yet, with their
names as they were specified in the configuration. This can be useful to verify names as they were specified in the configuration. This can be useful to verify
that the names specified in the configuration are used as expected. that the names specified in the configuration are used as expected.
When DNSSEC is enabled, it will not work until the time is synchronized, as it
requires validating a signature timestamp and its expiration date, so if the
system time is too far in the future or the past DNSSEC validation will fail and
`chronyd` will be unable to resolve the address of the NTP server. In such cases,
if hostnames are the only options and bare IP addresses cannot be used, DNSSEC
can be disabled for `chronyd` using resolver-specific mechanisms, if available,
although of course that means losing the protection afforded by DNSSEC.
For example, when using systemd-resolved, the `SYSTEMD_NSS_RESOLVE_VALIDATE=0`
environment variable can be set, for example in the `chronyd` systemd unit via
`Environment=SYSTEMD_NSS_RESOLVE_VALIDATE=0`.
=== Is `chronyd` allowed to step the system clock? === Is `chronyd` allowed to step the system clock?
By default, `chronyd` adjusts the clock gradually by slowing it down or By default, `chronyd` adjusts the clock gradually by slowing it down or
@ -1155,6 +1166,53 @@ There are several different clocks used by `chronyd`:
synchronised by `chronyd`. Its offset is tracked relative to the NTP clock in synchronised by `chronyd`. Its offset is tracked relative to the NTP clock in
order to convert the hardware timestamps. order to convert the hardware timestamps.
=== How accurate is my system clock?
`chronyd` does not know how accurate really is the clock it is synchronizing.
Even if the measured offset of the clock is stable to nanoseconds, it could be
off by milliseconds due to asymmetric network delay, e.g. caused by asymmetric
routing or queuing delays in network switches. NTP provides root delay and root
dispersion to enable clients to estimate the maximum error of their clock.
Root delay measures the sum of round-trip times between all NTP servers on the
path from the client to the primary time source (e.g. a GPS receiver). Half of
the root delay is the maximum error due to asymmetric delays, assuming one
direction (e.g. from the client to the server) has a zero delay and the other
direction (from the server to the client) takes all of the measured delay. The
root delay also covers timestamping errors if the server implementation and
hardware meet the NTP requirement for transmit timestamps to never be late and
receive timestamps to never be early.
If you have additional information about the hardware and network between the
client and primary time source, you could modify the root delay to get a better
estimate of the maximum error. For example, from the physical distance of the
server and signal propagation speed in the cables a minimum symmetric
round-trip delay can be calculated and subtracted from the root delay measured
by NTP.
Root dispersion estimates errors due to instability of clocks and NTP
measurements. `chronyd` adjusts the rate at which root dispersion grows between
updates of the clock according to the stability of its NTP measurements. The
minimum rate is set by the the `maxclockerror` directive. By default it is 1
ppm (1 microsecond per second).
The estimated maximum error of the NTP clock is the sum of the root dispersion
and half of the root delay. This value is called root distance. The current
values of root dispersion and delay are included in the `tracking` report.
The estimated maximum error of the system clock, which is synchronized to the
NTP clock, is the sum of the root distance and remaining correction of the
system clock provided as `System time` in the `tracking` report. A maximum
value of this estimate between updates of the clock is included in the
`tracking` log.
Note that the resolution of the root delay and root dispersion fields in NTP
messages is about 15 microseconds and `chronyd` rounds the values up, i.e. the
minimum root distance an NTP client can normally observe is about 22.5
microseconds. An NTP extension field containing root delay and dispersion in a
better resolution of about 4 nanoseconds can be enabled by the `extfield F323`
option.
== Operating systems == Operating systems
=== Does `chrony` support Windows? === Does `chrony` support Windows?

View file

@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Patrick Oppenlander 2023 * Copyright (C) Patrick Oppenlander 2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as

View file

@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2023 * Copyright (C) Miroslav Lichvar 2009-2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as

View file

@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2023 * Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@ -219,8 +219,14 @@ NSR_Finalise(void)
ARR_DestroyInstance(pools); ARR_DestroyInstance(pools);
SCH_RemoveTimeout(resolving_id); SCH_RemoveTimeout(resolving_id);
/* Leave the unresolved sources allocated if the async resolver is running
to avoid reading the name from freed memory. The handler will not be
called as the scheduler should no longer be running at this point. */
if (!resolving_source) {
while (unresolved_sources) while (unresolved_sources)
remove_unresolved_source(unresolved_sources); remove_unresolved_source(unresolved_sources);
}
initialised = 0; initialised = 0;
} }

View file

@ -40,6 +40,7 @@
#define NKE_RECORD_COOKIE 5 #define NKE_RECORD_COOKIE 5
#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6 #define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7 #define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
#define NKE_RECORD_COMPLIANT_128GCM_EXPORT 1024
#define NKE_NEXT_PROTOCOL_NTPV4 0 #define NKE_NEXT_PROTOCOL_NTPV4 0
@ -49,8 +50,6 @@
#define NKE_ALPN_NAME "ntske/1" #define NKE_ALPN_NAME "ntske/1"
#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security" #define NKE_EXPORTER_LABEL "EXPORTER-network-time-security"
#define NKE_EXPORTER_CONTEXT_C2S "\x0\x0\x0\xf\x0"
#define NKE_EXPORTER_CONTEXT_S2C "\x0\x0\x0\xf\x1"
#define NKE_MAX_MESSAGE_LENGTH 16384 #define NKE_MAX_MESSAGE_LENGTH 16384
#define NKE_MAX_RECORD_BODY_LENGTH 256 #define NKE_MAX_RECORD_BODY_LENGTH 256

View file

@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2020-2021 * Copyright (C) Miroslav Lichvar 2020-2021, 2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@ -50,7 +50,9 @@ struct NKC_Instance_Record {
int got_response; int got_response;
int resolving_name; int resolving_name;
int compliant_128gcm;
NKE_Context context; NKE_Context context;
NKE_Context alt_context;
NKE_Cookie cookies[NKE_MAX_COOKIES]; NKE_Cookie cookies[NKE_MAX_COOKIES];
int num_cookies; int num_cookies;
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2]; char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
@ -98,12 +100,14 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg
/* ================================================== */ /* ================================================== */
#define MAX_AEAD_ALGORITHMS 4
static int static int
prepare_request(NKC_Instance inst) prepare_request(NKC_Instance inst)
{ {
NKSN_Instance session = inst->session; NKSN_Instance session = inst->session;
uint16_t data[2]; uint16_t data[MAX_AEAD_ALGORITHMS];
int length; int i, aead_algorithm, length;
NKSN_BeginMessage(session); NKSN_BeginMessage(session);
@ -111,15 +115,24 @@ prepare_request(NKC_Instance inst)
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0]))) if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
return 0; return 0;
length = 0; for (i = length = 0; i < ARR_GetSize(CNF_GetNtsAeads()) && length < MAX_AEAD_ALGORITHMS;
if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0) i++) {
data[length++] = htons(AEAD_AES_128_GCM_SIV); aead_algorithm = *(int *)ARR_GetElement(CNF_GetNtsAeads(), i);
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0) if (SIV_GetKeyLength(aead_algorithm) > 0)
data[length++] = htons(AEAD_AES_SIV_CMAC_256); data[length++] = htons(aead_algorithm);
}
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
length * sizeof (data[0]))) length * sizeof (data[0])))
return 0; return 0;
for (i = 0; i < length; i++) {
if (data[i] == htons(AEAD_AES_128_GCM_SIV)) {
if (!NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0))
return 0;
break;
}
}
if (!NKSN_EndMessage(session)) if (!NKSN_EndMessage(session))
return 0; return 0;
@ -139,6 +152,8 @@ process_response(NKC_Instance inst)
assert(sizeof (data) % sizeof (uint16_t) == 0); assert(sizeof (data) % sizeof (uint16_t) == 0);
assert(sizeof (uint16_t) == 2); assert(sizeof (uint16_t) == 2);
inst->compliant_128gcm = 0;
inst->alt_context.algorithm = AEAD_SIV_INVALID;
inst->num_cookies = 0; inst->num_cookies = 0;
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC; inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
inst->ntp_address.port = 0; inst->ntp_address.port = 0;
@ -165,15 +180,31 @@ process_response(NKC_Instance inst)
next_protocol = NKE_NEXT_PROTOCOL_NTPV4; next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
break; break;
case NKE_RECORD_AEAD_ALGORITHM: case NKE_RECORD_AEAD_ALGORITHM:
if (length != 2 || (ntohs(data[0]) != AEAD_AES_SIV_CMAC_256 && if (length != 2) {
ntohs(data[0]) != AEAD_AES_128_GCM_SIV) || DEBUG_LOG("Unexpected AEAD algorithm");
SIV_GetKeyLength(ntohs(data[0])) <= 0) {
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
error = 1; error = 1;
break; break;
} }
for (i = 0; i < ARR_GetSize(CNF_GetNtsAeads()); i++) {
if (ntohs(data[0]) == *(int *)ARR_GetElement(CNF_GetNtsAeads(), i) &&
SIV_GetKeyLength(ntohs(data[0])) > 0) {
aead_algorithm = ntohs(data[0]); aead_algorithm = ntohs(data[0]);
inst->context.algorithm = aead_algorithm; inst->context.algorithm = aead_algorithm;
}
}
if (aead_algorithm < 0) {
DEBUG_LOG("Unexpected AEAD algorithm");
error = 1;
}
break;
case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
if (length != 0) {
DEBUG_LOG("Non-empty compliant-128gcm record");
error = 1;
break;
}
DEBUG_LOG("Compliant AES-128-GCM-SIV export");
inst->compliant_128gcm = 1;
break; break;
case NKE_RECORD_ERROR: case NKE_RECORD_ERROR:
if (length == 2) if (length == 2)
@ -255,6 +286,7 @@ process_response(NKC_Instance inst)
static int static int
handle_message(void *arg) handle_message(void *arg)
{ {
SIV_Algorithm exporter_algorithm;
NKC_Instance inst = arg; NKC_Instance inst = arg;
if (!process_response(inst)) { if (!process_response(inst)) {
@ -262,8 +294,25 @@ handle_message(void *arg)
return 0; return 0;
} }
if (!NKSN_GetKeys(inst->session, inst->context.algorithm, exporter_algorithm = inst->context.algorithm;
&inst->context.c2s, &inst->context.s2c))
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
context incorrectly for compatibility with older chrony servers unless
the server confirmed support for the compliant context. Generate both
sets of keys in case the server uses the compliant context, but does not
support the negotiation record, assuming it will respond with an NTS NAK
to a request authenticated with the noncompliant key. */
if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !inst->compliant_128gcm) {
inst->alt_context.algorithm = inst->context.algorithm;
if (!NKSN_GetKeys(inst->session, inst->alt_context.algorithm, exporter_algorithm,
NKE_NEXT_PROTOCOL_NTPV4, &inst->alt_context.c2s, &inst->alt_context.s2c))
return 0;
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
}
if (!NKSN_GetKeys(inst->session, inst->context.algorithm, exporter_algorithm,
NKE_NEXT_PROTOCOL_NTPV4, &inst->context.c2s, &inst->context.s2c))
return 0; return 0;
if (inst->server_name[0] != '\0') { if (inst->server_name[0] != '\0') {
@ -428,7 +477,7 @@ NKC_IsActive(NKC_Instance inst)
/* ================================================== */ /* ================================================== */
int int
NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies, NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address) IPSockAddr *ntp_address)
{ {
@ -438,6 +487,7 @@ NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
return 0; return 0;
*context = inst->context; *context = inst->context;
*alt_context = inst->alt_context;
for (i = 0; i < inst->num_cookies && i < max_cookies; i++) for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
cookies[i] = inst->cookies[i]; cookies[i] = inst->cookies[i];

View file

@ -46,7 +46,7 @@ extern int NKC_Start(NKC_Instance inst);
extern int NKC_IsActive(NKC_Instance inst); extern int NKC_IsActive(NKC_Instance inst);
/* Get the NTS data if the session was successful */ /* Get the NTS data if the session was successful */
extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies, NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address); IPSockAddr *ntp_address);

View file

@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2020, 2022 * Copyright (C) Miroslav Lichvar 2020, 2022, 2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@ -337,8 +337,10 @@ helper_signal(int x)
/* ================================================== */ /* ================================================== */
static int static int
prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm) prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm,
int compliant_128gcm)
{ {
SIV_Algorithm exporter_algorithm;
NKE_Context context; NKE_Context context;
NKE_Cookie cookie; NKE_Cookie cookie;
char *ntp_server; char *ntp_server;
@ -371,6 +373,11 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum))) if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
return 0; return 0;
if (aead_algorithm == AEAD_AES_128_GCM_SIV && compliant_128gcm) {
if (!NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0))
return 0;
}
if (CNF_GetNTPPort() != NTP_PORT) { if (CNF_GetNTPPort() != NTP_PORT) {
datum = htons(CNF_GetNTPPort()); datum = htons(CNF_GetNTPPort());
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, &datum, sizeof (datum))) if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, &datum, sizeof (datum)))
@ -385,8 +392,16 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
} }
context.algorithm = aead_algorithm; context.algorithm = aead_algorithm;
exporter_algorithm = aead_algorithm;
if (!NKSN_GetKeys(session, aead_algorithm, &context.c2s, &context.s2c)) /* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
context incorrectly for compatibility with older chrony clients unless
the client requested the compliant context */
if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !compliant_128gcm)
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
if (!NKSN_GetKeys(session, aead_algorithm, exporter_algorithm,
NKE_NEXT_PROTOCOL_NTPV4, &context.c2s, &context.s2c))
return 0; return 0;
for (i = 0; i < NKE_MAX_COOKIES; i++) { for (i = 0; i < NKE_MAX_COOKIES; i++) {
@ -411,7 +426,8 @@ process_request(NKSN_Instance session)
int next_protocol_records = 0, aead_algorithm_records = 0; int next_protocol_records = 0, aead_algorithm_records = 0;
int next_protocol_values = 0, aead_algorithm_values = 0; int next_protocol_values = 0, aead_algorithm_values = 0;
int next_protocol = -1, aead_algorithm = -1, error = -1; int next_protocol = -1, aead_algorithm = -1, error = -1;
int i, critical, type, length; int i, j, critical, type, length;
int compliant_128gcm = 0;
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)]; uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0); assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0);
@ -446,10 +462,20 @@ process_request(NKSN_Instance session)
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) { for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
aead_algorithm_values++; aead_algorithm_values++;
/* Use the first supported algorithm */ /* Use the first enabled and supported algorithm */
if (aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0) for (j = 0; j < ARR_GetSize(CNF_GetNtsAeads()); j++) {
if (ntohs(data[i]) == *(int *)ARR_GetElement(CNF_GetNtsAeads(), j) &&
aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
aead_algorithm = ntohs(data[i]); aead_algorithm = ntohs(data[i]);
} }
}
break;
case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
if (length != 0) {
error = NKE_ERROR_BAD_REQUEST;
break;
}
compliant_128gcm = 1;
break; break;
case NKE_RECORD_ERROR: case NKE_RECORD_ERROR:
case NKE_RECORD_WARNING: case NKE_RECORD_WARNING:
@ -469,7 +495,7 @@ process_request(NKSN_Instance session)
error = NKE_ERROR_BAD_REQUEST; error = NKE_ERROR_BAD_REQUEST;
} }
if (!prepare_response(session, error, next_protocol, aead_algorithm)) if (!prepare_response(session, error, next_protocol, aead_algorithm, compliant_128gcm))
return 0; return 0;
return 1; return 1;

View file

@ -877,22 +877,42 @@ NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
/* ================================================== */ /* ================================================== */
int int
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c) NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm, SIV_Algorithm exporter_algorithm,
int next_protocol, NKE_Key *c2s, NKE_Key *s2c)
{ {
int length = SIV_GetKeyLength(siv); int length = SIV_GetKeyLength(algorithm);
struct {
uint16_t next_protocol;
uint16_t algorithm;
uint8_t is_s2c;
uint8_t _pad;
} context;
if (!inst->tls_session)
return 0;
if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) { if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) {
DEBUG_LOG("Invalid algorithm"); DEBUG_LOG("Invalid algorithm");
return 0; return 0;
} }
assert(sizeof (context) == 6);
context.next_protocol = htons(next_protocol);
context.algorithm = htons(exporter_algorithm);
context.is_s2c = 0;
if (gnutls_prf_rfc5705(inst->tls_session, if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S, sizeof (context) - 1, (char *)&context,
length, (char *)c2s->key) < 0 || length, (char *)c2s->key) < 0) {
gnutls_prf_rfc5705(inst->tls_session, DEBUG_LOG("Could not export key");
return 0;
}
context.is_s2c = 1;
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C, sizeof (context) - 1, (char *)&context,
length, (char *)s2c->key) < 0) { length, (char *)s2c->key) < 0) {
DEBUG_LOG("Could not export key"); DEBUG_LOG("Could not export key");
return 0; return 0;

View file

@ -77,8 +77,11 @@ extern int NKSN_EndMessage(NKSN_Instance inst);
extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length, extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
void *body, int buffer_length); void *body, int buffer_length);
/* Export NTS keys for a specified algorithm */ /* Export NTS keys for a specified algorithm (for compatibility reasons the
extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c); RFC5705 exporter context is allowed to have a different algorithm) */
extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm,
SIV_Algorithm exporter_algorithm,
int next_protocol, NKE_Key *c2s, NKE_Key *s2c);
/* Check if the session has stopped */ /* Check if the session has stopped */
extern int NKSN_IsStopped(NKSN_Instance inst); extern int NKSN_IsStopped(NKSN_Instance inst);

View file

@ -72,6 +72,7 @@ struct NNC_Instance_Record {
double last_nke_success; double last_nke_success;
NKE_Context context; NKE_Context context;
NKE_Context alt_context;
unsigned int context_id; unsigned int context_id;
NKE_Cookie cookies[NTS_MAX_COOKIES]; NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies; int num_cookies;
@ -105,6 +106,7 @@ reset_instance(NNC_Instance inst)
inst->last_nke_success = 0.0; inst->last_nke_success = 0.0;
memset(&inst->context, 0, sizeof (inst->context)); memset(&inst->context, 0, sizeof (inst->context));
memset(&inst->alt_context, 0, sizeof (inst->alt_context));
inst->context_id = 0; inst->context_id = 0;
memset(inst->cookies, 0, sizeof (inst->cookies)); memset(inst->cookies, 0, sizeof (inst->cookies));
inst->num_cookies = 0; inst->num_cookies = 0;
@ -165,6 +167,21 @@ check_cookies(NNC_Instance inst)
if (inst->num_cookies > 0 && if (inst->num_cookies > 0 &&
((inst->nak_response && !inst->ok_response) || ((inst->nak_response && !inst->ok_response) ||
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) { SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
/* Before dropping the cookies, check whether there is an alternate set of
keys available (exported with the compliant context for AES-128-GCM-SIV)
and the NAK was the only valid response after the last NTS-KE session,
indicating we use incorrect keys and switching to the other set of keys
for the following NTP requests might work */
if (inst->alt_context.algorithm != AEAD_SIV_INVALID &&
inst->alt_context.algorithm == inst->context.algorithm &&
inst->nke_attempts > 0 && inst->nak_response && !inst->ok_response) {
inst->context = inst->alt_context;
inst->alt_context.algorithm = AEAD_SIV_INVALID;
DEBUG_LOG("Switched to compliant keys");
return 1;
}
inst->num_cookies = 0; inst->num_cookies = 0;
DEBUG_LOG("Dropped cookies"); DEBUG_LOG("Dropped cookies");
} }
@ -261,7 +278,7 @@ get_cookies(NNC_Instance inst)
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES); assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
/* Get the new keys, cookies and NTP address if the session was successful */ /* Get the new keys, cookies and NTP address if the session was successful */
got_data = NKC_GetNtsData(inst->nke, &inst->context, got_data = NKC_GetNtsData(inst->nke, &inst->context, &inst->alt_context,
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES, inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
&ntp_address); &ntp_address);
@ -520,6 +537,7 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
new NTS-KE session to be started as soon as the cookies run out. */ new NTS-KE session to be started as soon as the cookies run out. */
inst->nke_attempts = 0; inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0; inst->next_nke_attempt = 0.0;
inst->alt_context.algorithm = AEAD_SIV_INVALID;
return 1; return 1;
} }
@ -643,6 +661,7 @@ load_cookies(NNC_Instance inst)
sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1) sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
goto error; goto error;
inst->alt_context.algorithm = AEAD_SIV_INVALID;
inst->context.algorithm = algorithm; inst->context.algorithm = algorithm;
inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key, inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
sizeof (inst->context.s2c.key)); sizeof (inst->context.s2c.key));
@ -687,6 +706,7 @@ error:
fclose(f); fclose(f);
memset(&inst->context, 0, sizeof (inst->context)); memset(&inst->context, 0, sizeof (inst->context));
memset(&inst->alt_context, 0, sizeof (inst->alt_context));
inst->num_cookies = 0; inst->num_cookies = 0;
} }

View file

@ -63,6 +63,7 @@ struct RCL_Instance_Record {
int driver_poll; int driver_poll;
int driver_polled; int driver_polled;
int poll; int poll;
int reached;
int leap_status; int leap_status;
int local; int local;
int pps_forced; int pps_forced;
@ -175,6 +176,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver_poll = params->driver_poll; inst->driver_poll = params->driver_poll;
inst->poll = params->poll; inst->poll = params->poll;
inst->driver_polled = 0; inst->driver_polled = 0;
inst->reached = 0;
inst->leap_status = LEAP_Normal; inst->leap_status = LEAP_Normal;
inst->local = params->local; inst->local = params->local;
inst->pps_forced = params->pps_forced; inst->pps_forced = params->pps_forced;
@ -665,6 +667,12 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
return 1; return 1;
} }
void
RCL_UpdateReachability(RCL_Instance instance)
{
instance->reached++;
}
double double
RCL_GetPrecision(RCL_Instance instance) RCL_GetPrecision(RCL_Instance instance)
{ {
@ -792,6 +800,9 @@ poll_timeout(void *arg)
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) { if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
inst->driver_polled = 0; inst->driver_polled = 0;
SRC_UpdateReachability(inst->source, inst->reached > 0);
inst->reached = 0;
if (SPF_GetFilteredSample(inst->filter, &sample)) { if (SPF_GetFilteredSample(inst->filter, &sample)) {
double local_freq, local_offset; double local_freq, local_offset;
struct timespec local_ref_time; struct timespec local_ref_time;
@ -807,7 +818,6 @@ poll_timeout(void *arg)
inst->leap_status = LEAP_Unsynchronised; inst->leap_status = LEAP_Unsynchronised;
} }
SRC_UpdateReachability(inst->source, 1);
SRC_UpdateStatus(inst->source, stratum, inst->leap_status); SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
SRC_AccumulateSample(inst->source, &sample); SRC_AccumulateSample(inst->source, &sample);
SRC_SelectSource(inst->source); SRC_SelectSource(inst->source);
@ -816,8 +826,6 @@ poll_timeout(void *arg)
follow_local(inst, &local_ref_time, local_freq, local_offset); follow_local(inst, &local_ref_time, local_freq, local_offset);
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion); log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
} else {
SRC_UpdateReachability(inst->source, 0);
} }
} }

View file

@ -81,6 +81,7 @@ extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second); extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time, extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction); double second, double dispersion, double raw_correction);
extern void RCL_UpdateReachability(RCL_Instance instance);
extern double RCL_GetPrecision(RCL_Instance instance); extern double RCL_GetPrecision(RCL_Instance instance);
extern int RCL_GetDriverPoll(RCL_Instance instance); extern int RCL_GetDriverPoll(RCL_Instance instance);

View file

@ -154,6 +154,8 @@ static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
} }
phc->last_extts = *phc_ts; phc->last_extts = *phc_ts;
RCL_UpdateReachability(instance);
if (!HCL_CookTime(phc->clock, phc_ts, &local_ts, &local_err)) if (!HCL_CookTime(phc->clock, phc_ts, &local_ts, &local_err))
return; return;
@ -204,6 +206,9 @@ static int phc_poll(RCL_Instance instance)
if (n_readings < 1) if (n_readings < 1)
return 0; return 0;
if (!phc->extpps)
RCL_UpdateReachability(instance);
if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err)) if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
return 0; return 0;

View file

@ -143,6 +143,8 @@ static int pps_poll(RCL_Instance instance)
pps->last_seq = seq; pps->last_seq = seq;
RCL_UpdateReachability(instance);
return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec); return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec);
} }

View file

@ -109,6 +109,8 @@ static int shm_poll(RCL_Instance instance)
shm->valid = 0; shm->valid = 0;
RCL_UpdateReachability(instance);
receive_ts.tv_sec = t.receiveTimeStampSec; receive_ts.tv_sec = t.receiveTimeStampSec;
clock_ts.tv_sec = t.clockTimeStampSec; clock_ts.tv_sec = t.clockTimeStampSec;

View file

@ -129,6 +129,8 @@ static void read_sample(int sockfd, int event, void *anything)
UTI_TimevalToTimespec(&sample.tv, &sys_ts); UTI_TimevalToTimespec(&sample.tv, &sys_ts);
UTI_NormaliseTimespec(&sys_ts); UTI_NormaliseTimespec(&sys_ts);
RCL_UpdateReachability(instance);
if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset)) if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset))
return; return;

View file

@ -4,6 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022 * Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
* Copyright (C) Andy Fiddaman 2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as

1
siv.h
View file

@ -36,6 +36,7 @@
/* Identifiers of SIV algorithms following the IANA AEAD registry */ /* Identifiers of SIV algorithms following the IANA AEAD registry */
typedef enum { typedef enum {
AEAD_SIV_INVALID = 0,
AEAD_AES_SIV_CMAC_256 = 15, AEAD_AES_SIV_CMAC_256 = 15,
AEAD_AES_SIV_CMAC_384 = 16, AEAD_AES_SIV_CMAC_384 = 16,
AEAD_AES_SIV_CMAC_512 = 17, AEAD_AES_SIV_CMAC_512 = 17,

View file

@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2023 * Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@ -526,11 +526,6 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
if (inst->reachability_size < SOURCE_REACH_BITS) if (inst->reachability_size < SOURCE_REACH_BITS)
inst->reachability_size++; inst->reachability_size++;
/* Source selection can change with unreachable sources */
if (inst->reachability == 0) {
SRC_SelectSource(NULL);
}
/* Check if special reference update mode failed */ /* Check if special reference update mode failed */
if (REF_GetMode() != REF_ModeNormal && special_mode_end()) { if (REF_GetMode() != REF_ModeNormal && special_mode_end()) {
REF_SetUnsynchronised(); REF_SetUnsynchronised();
@ -539,6 +534,10 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
/* Try to replace unreachable NTP sources */ /* Try to replace unreachable NTP sources */
if (inst->reachability == 0 && inst->reachability_size == SOURCE_REACH_BITS) if (inst->reachability == 0 && inst->reachability_size == SOURCE_REACH_BITS)
handle_bad_source(inst); handle_bad_source(inst);
/* Source selection can change with unreachable sources */
if (inst->reachability == 0)
SRC_SelectSource(NULL);
} }
/* ================================================== */ /* ================================================== */

View file

@ -36,7 +36,7 @@ Update interval : 16\.. seconds
.*$" || test_fail .*$" || test_fail
if echo "$refclock" | grep -q 'PHC.*nocrossts'; then if echo "$refclock" | grep -q 'PHC.*nocrossts'; then
check_file_messages "20.* GPS.*[0-9] N " 650 750 refclocks.log || test_fail check_file_messages "20.* GPS.*[0-9] N " 620 750 refclocks.log || test_fail
else else
check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
fi fi
@ -64,7 +64,7 @@ Stratum.*: 1
Root delay : 0\.000000001 seconds Root delay : 0\.000000001 seconds
.*$" || test_fail .*$" || test_fail
check_file_messages "20.* PPS1.*[0-9] N " 620 740 refclocks.log || test_fail check_file_messages "20.* PPS1.*[0-9] N " 610 740 refclocks.log || test_fail
check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail
rm -f tmp/refclocks.log rm -f tmp/refclocks.log
@ -89,14 +89,15 @@ Root delay : 0\.000000001 seconds
check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail
rm -f tmp/refclocks.log rm -f tmp/refclocks.log
min_sync_time=100 min_sync_time=80
max_sync_time=220 max_sync_time=180
chronyc_start=220 chronyc_start=220
client_conf=" client_conf="
refclock SHM 0 refid NMEA offset 0.35 delay 0.1 refclock SHM 0 refid NMEA offset 0.35 delay 0.1
refclock PPS /dev/pps0 refclock PPS /dev/pps0
logdir tmp logdir tmp
log refclocks" log refclocks
maxupdateskew 10000"
run_test || test_fail run_test || test_fail
check_chronyd_exit || test_fail check_chronyd_exit || test_fail
@ -108,11 +109,37 @@ Stratum.*: 1
Root delay : 0\.000000001 seconds Root delay : 0\.000000001 seconds
.*$" || test_fail .*$" || test_fail
check_file_messages "20.* PPS1.*[0-9] N " 800 940 refclocks.log || test_fail check_file_messages "20.* PPS1.*[0-9] N " 800 960 refclocks.log || test_fail
check_file_messages "20.* PPS1.*- N " 50 63 refclocks.log || test_fail check_file_messages "20.* PPS1.*- N " 50 63 refclocks.log || test_fail
rm -f tmp/refclocks.log rm -f tmp/refclocks.log
fi fi
export CLKNETSIM_PHC_JITTER_OFF=$[2 * 25 * 492]
export CLKNETSIM_PHC_JITTER_ON=$[2 * 25 * 8]
export CLKNETSIM_PHC_JITTER=1e-6
refclock_offset=0.0
refclock_jitter=1e-9
min_sync_time=5
max_sync_time=7
time_max_limit=1e-7
time_rms_limit=1e-8
client_conf="refclock PHC /dev/ptp0:nocrossts poll 0
logdir tmp
log refclocks"
chronyc_start=500
chronyc_conf="sources"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_chronyc_output "^MS.*
=*
#\* PHC0 0 0 377 8 .*$" || test_fail
unset CLKNETSIM_PHC_JITTER_OFF
unset CLKNETSIM_PHC_JITTER_ON
export CLKNETSIM_PHC_JITTER=1e-7
refclock_offset="(+ 0.399 (sum 1e-3))" refclock_offset="(+ 0.399 (sum 1e-3))"
refclock_jitter=1e-6 refclock_jitter=1e-6
servers=1 servers=1

View file

@ -21,6 +21,7 @@ EOF
clients=2 clients=2
peers=2 peers=2
freq_max_limit=1e-3
max_sync_time=1000 max_sync_time=1000
client_server_options="minpoll 6 maxpoll 6" client_server_options="minpoll 6 maxpoll 6"
client_peer_options="minpoll 6 maxpoll 6" client_peer_options="minpoll 6 maxpoll 6"
@ -30,6 +31,7 @@ check_chronyd_exit || test_fail
check_source_selection || test_fail check_source_selection || test_fail
check_sync || test_fail check_sync || test_fail
freq_max_limit=$default_freq_max_limit
base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))" base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))"
client_peer_options="" client_peer_options=""

View file

@ -58,7 +58,7 @@ MS Name/IP address Stratum Poll Reach LastRx Last sample
\^\? 192\.168\.123\.2 0 [0-9]+ 0 - \+0ns\[ \+0ns\] \+/- 0ns \^\? 192\.168\.123\.2 0 [0-9]+ 0 - \+0ns\[ \+0ns\] \+/- 0ns
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
============================================================================== ==============================================================================
SHM0 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s SHM0 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][012]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s 192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
192\.168\.123\.2 0 0 0 \+0\.000 2000\.000 \+0ns 4000ms 192\.168\.123\.2 0 0 0 \+0\.000 2000\.000 \+0ns 4000ms
210 n_samples = 0 210 n_samples = 0

View file

@ -313,4 +313,31 @@ check_sync && test_fail
check_file_messages " 3 1 .* 123 " 0 0 log.packets || test_fail check_file_messages " 3 1 .* 123 " 0 0 log.packets || test_fail
check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail
for server_aead in "" "15" "30"; do
for client_aead in "" "15" "30"; do
server_conf="
ntsaeads $server_aead
ntsserverkey tmp/server1.key
ntsservercert tmp/server1.crt
ntsprocesses 0"
client_conf="
nosystemcert
ntsaeads $client_aead
ntstrustedcerts tmp/server1.crt
ntstrustedcerts tmp/server2.crt"
client_server_conf=""
run_test || test_fail
check_chronyd_exit || test_fail
if [ -n "$server_aead" ] && [ "$server_aead" == "$client_aead" ] &&
( [ "$server_aead" != "30" ] || check_config_h '.*_SIV_GCM 1' ); then
check_source_selection || test_fail
check_sync || test_fail
else
check_source_selection && test_fail
check_sync && test_fail
fi
done
done
test_pass test_pass

View file

@ -53,4 +53,28 @@ check_log_messages "2010-01-01T0[5-9]:.*Source 192.168.123.. replaced with" 0 15
check_file_messages "20.*192.168.123.* 11.1 6 6 " 20 500 measurements.log || test_fail check_file_messages "20.*192.168.123.* 11.1 6 6 " 20 500 measurements.log || test_fail
rm -f tmp/measurements.log rm -f tmp/measurements.log
# 2 replaceable falsetickers and 1 replaceable unreachable server
servers=6
falsetickers=2
base_delay="(+ 1e-4 (* -1 (equal 0.1 to 3)))"
client_server_conf="
server nodes-4-1.net1.clk
server nodes-5-2.net1.clk
server nodes-6-3.net1.clk"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection && test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_log_messages "Can't synchronise: no majority" 1 1 || test_fail
check_log_messages "Detected falseticker" 0 2 || test_fail
check_log_messages "Source 192.168.123.. replaced with" 3 60 || test_fail
check_log_messages "Source 192.168.123.1 replaced with" 1 25 || test_fail
check_log_messages "Source 192.168.123.2 replaced with" 1 25 || test_fail
check_log_messages "Source 192.168.123.3 replaced with" 1 25 || test_fail
check_file_messages "20.*192.168.123.* 11.1 6 6 " 50 800 measurements.log || test_fail
rm -f tmp/measurements.log
test_pass test_pass

View file

@ -77,7 +77,32 @@ check_chronyc_output "^[^=]*
.. 127\.123\.5\.3 *[05] 7 [^^]* .. 127\.123\.5\.3 *[05] 7 [^^]*
.. 127\.123\.5\.6 [^^]*$" || test_fail .. 127\.123\.5\.6 [^^]*$" || test_fail
run_chronyc "reload sources" || test_fail
run_chronyc "reload sources" || test_fail
rm $TEST_DIR/conf5.d/{3,5,6}.sources
echo "server 127.123.5.7" > $TEST_DIR/conf5.d/7.sources
run_chronyc "reload sources" || test_fail
run_chronyc "sources" || test_fail
check_chronyc_output "^[^=]*
=*
.. 127\.123\.1\.1 [^^]*
.. 127\.123\.1\.3 [^^]*
.. 127\.123\.1\.4 [^^]*
.. 127\.123\.3\.1 [^^]*
.. 127\.123\.2\.2 [^^]*
.. 127\.123\.2\.3 [^^]*
.. 127\.123\.4\.4 [^^]*
.. 127\.123\.1\.2 *[05] 6 [^^]*
.. 127\.123\.5\.2 *[05] 5 [^^]*
.. 127\.123\.5\.7 [^^]*$" || test_fail
run_chronyc "reload sources" || test_fail
stop_chronyd || test_fail stop_chronyd || test_fail
check_chronyd_message_count "Could not add source.*\.5\.5.*in use" 1 1 || test_fail
check_chronyd_message_count "Could not add source" 1 1 || test_fail check_chronyd_message_count "Could not add source" 1 1 || test_fail
test_pass test_pass

View file

@ -1,6 +1,6 @@
/* /*
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2016, 2021 * Copyright (C) Miroslav Lichvar 2016, 2021, 2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as

View file

@ -1,6 +1,6 @@
/* /*
********************************************************************** **********************************************************************
* Copyright (C) Patrick Oppenlander 2023 * Copyright (C) Patrick Oppenlander 2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as

View file

@ -1,6 +1,6 @@
/* /*
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2020 * Copyright (C) Miroslav Lichvar 2020, 2024
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@ -23,8 +23,24 @@
#ifdef FEAT_NTS #ifdef FEAT_NTS
#include <nts_ke_client.c>
#include <local.h> #include <local.h>
#include <nts_ke_session.h>
#include <util.h>
#define NKSN_GetKeys get_keys
static int
get_keys(NKSN_Instance session, SIV_Algorithm algorithm, SIV_Algorithm exporter_algorithm,
int next_protocol, NKE_Key *c2s, NKE_Key *s2c)
{
c2s->length = SIV_GetKeyLength(algorithm);
UTI_GetRandomBytes(c2s->key, c2s->length);
s2c->length = SIV_GetKeyLength(algorithm);
UTI_GetRandomBytes(s2c->key, s2c->length);
return 1;
}
#include <nts_ke_client.c>
static void static void
prepare_response(NKSN_Instance session, int valid) prepare_response(NKSN_Instance session, int valid)
@ -88,12 +104,17 @@ prepare_response(NKSN_Instance session, int valid)
if (random() % 2) { if (random() % 2) {
length = random() % (sizeof (data) + 1); length = random() % (sizeof (data) + 1);
TEST_CHECK(NKSN_AddRecord(session, 0, 1000 + random() % 1000, data, length)); TEST_CHECK(NKSN_AddRecord(session, 0, 2000 + random() % 1000, data, length));
} }
if (random() % 2)
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0));
if (index != 8) { if (index != 8) {
for (i = 0; i < NKE_MAX_COOKIES; i++) { for (i = random() % NKE_MAX_COOKIES; i >= 0; i--) {
length = (random() % sizeof (data) + 1) / 4 * 4; length = (random() % sizeof (data) + 1) / 4 * 4;
if (i == 0 && length == 0)
length = 4;
if (index == 9) if (index == 9)
length += (length < sizeof (data) ? 1 : -1) * (random() % 3 + 1); length += (length < sizeof (data) ? 1 : -1) * (random() % 3 + 1);
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COOKIE, data, length)); TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COOKIE, data, length));
@ -103,12 +124,16 @@ prepare_response(NKSN_Instance session, int valid)
TEST_CHECK(NKSN_EndMessage(session)); TEST_CHECK(NKSN_EndMessage(session));
} }
#define MAX_COOKIES (2 * NKE_MAX_COOKIES)
void void
test_unit(void) test_unit(void)
{ {
NKE_Context context, alt_context;
NKE_Cookie cookies[MAX_COOKIES];
int i, j, r, valid, num_cookies;
NKC_Instance inst; NKC_Instance inst;
IPSockAddr addr; IPSockAddr addr;
int i, r, valid;
char conf[][100] = { char conf[][100] = {
"nosystemcert", "nosystemcert",
@ -127,10 +152,33 @@ test_unit(void)
TEST_CHECK(inst); TEST_CHECK(inst);
for (i = 0; i < 10000; i++) { for (i = 0; i < 10000; i++) {
inst->got_response = 0;
valid = random() % 2; valid = random() % 2;
prepare_response(inst->session, valid); prepare_response(inst->session, valid);
r = process_response(inst); r = handle_message(inst);
TEST_CHECK(r == valid); TEST_CHECK(r == valid);
memset(&context, 0, sizeof (context));
memset(&alt_context, 0, sizeof (alt_context));
num_cookies = 0;
r = NKC_GetNtsData(inst, &context, &alt_context,
cookies, &num_cookies, random() % MAX_COOKIES + 1, &addr);
TEST_CHECK(r == valid);
if (r) {
TEST_CHECK(context.algorithm != AEAD_SIV_INVALID);
TEST_CHECK(context.c2s.length > 0);
TEST_CHECK(context.c2s.length == SIV_GetKeyLength(context.algorithm));
TEST_CHECK(context.s2c.length == SIV_GetKeyLength(context.algorithm));
if (alt_context.algorithm != AEAD_SIV_INVALID) {
TEST_CHECK(context.c2s.length > 0);
TEST_CHECK(alt_context.c2s.length == SIV_GetKeyLength(alt_context.algorithm));
TEST_CHECK(alt_context.s2c.length == SIV_GetKeyLength(alt_context.algorithm));
}
TEST_CHECK(num_cookies > 0 && num_cookies <= NKE_MAX_COOKIES);
for (j = 0; j < num_cookies; j++)
TEST_CHECK(cookies[j].length > 0 && cookies[j].length % 4 == 0);
}
} }
NKC_DestroyInstance(inst); NKC_DestroyInstance(inst);

View file

@ -30,11 +30,12 @@
#define NKSN_GetKeys get_keys #define NKSN_GetKeys get_keys
static int static int
get_keys(NKSN_Instance session, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c) get_keys(NKSN_Instance session, SIV_Algorithm algorithm, SIV_Algorithm exporter_algorithm,
int next_protocol, NKE_Key *c2s, NKE_Key *s2c)
{ {
c2s->length = SIV_GetKeyLength(siv); c2s->length = SIV_GetKeyLength(algorithm);
UTI_GetRandomBytes(c2s->key, c2s->length); UTI_GetRandomBytes(c2s->key, c2s->length);
s2c->length = SIV_GetKeyLength(siv); s2c->length = SIV_GetKeyLength(algorithm);
UTI_GetRandomBytes(s2c->key, s2c->length); UTI_GetRandomBytes(s2c->key, s2c->length);
return 1; return 1;
} }
@ -91,7 +92,7 @@ prepare_request(NKSN_Instance session, int valid)
if (index == 8) { if (index == 8) {
length = random() % (sizeof (data) + 1); length = random() % (sizeof (data) + 1);
TEST_CHECK(NKSN_AddRecord(session, 1, 1000 + random() % 1000, data, length)); TEST_CHECK(NKSN_AddRecord(session, 1, 2000 + random() % 1000, data, length));
} }
if (random() % 2) { if (random() % 2) {
@ -105,9 +106,12 @@ prepare_request(NKSN_Instance session, int valid)
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_NTPV4_PORT_NEGOTIATION, data, length)); TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_NTPV4_PORT_NEGOTIATION, data, length));
} }
if (random() % 2)
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0));
if (random() % 2) { if (random() % 2) {
length = random() % (sizeof (data) + 1); length = random() % (sizeof (data) + 1);
TEST_CHECK(NKSN_AddRecord(session, 0, 1000 + random() % 1000, data, length)); TEST_CHECK(NKSN_AddRecord(session, 0, 2000 + random() % 1000, data, length));
} }
TEST_CHECK(NKSN_EndMessage(session)); TEST_CHECK(NKSN_EndMessage(session));
@ -174,7 +178,8 @@ test_unit(void)
for (i = 0; i < 10000; i++) { for (i = 0; i < 10000; i++) {
context.algorithm = AEAD_AES_SIV_CMAC_256; context.algorithm = AEAD_AES_SIV_CMAC_256;
get_keys(session, context.algorithm, &context.c2s, &context.s2c); get_keys(session, context.algorithm, random() % 100, NKE_NEXT_PROTOCOL_NTPV4,
&context.c2s, &context.s2c);
memset(&cookie, 0, sizeof (cookie)); memset(&cookie, 0, sizeof (cookie));
TEST_CHECK(NKS_GenerateCookie(&context, &cookie)); TEST_CHECK(NKS_GenerateCookie(&context, &cookie));
TEST_CHECK(NKS_DecodeCookie(&cookie, &context2)); TEST_CHECK(NKS_DecodeCookie(&cookie, &context2));

View file

@ -116,9 +116,19 @@ verify_message(NKSN_Instance inst)
TEST_CHECK(!NKSN_GetRecord(inst, &critical, &t, &length, buffer, sizeof (buffer))); TEST_CHECK(!NKSN_GetRecord(inst, &critical, &t, &length, buffer, sizeof (buffer)));
TEST_CHECK(NKSN_GetKeys(inst, AEAD_AES_SIV_CMAC_256, &c2s, &s2c)); for (i = 0; i < 10; i++) {
TEST_CHECK(NKSN_GetKeys(inst, AEAD_AES_SIV_CMAC_256, random(), random(), &c2s, &s2c));
TEST_CHECK(c2s.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256)); TEST_CHECK(c2s.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256));
TEST_CHECK(s2c.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256)); TEST_CHECK(s2c.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256));
if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0) {
TEST_CHECK(NKSN_GetKeys(inst, AEAD_AES_128_GCM_SIV, random(), random(), &c2s, &s2c));
TEST_CHECK(c2s.length == SIV_GetKeyLength(AEAD_AES_128_GCM_SIV));
TEST_CHECK(s2c.length == SIV_GetKeyLength(AEAD_AES_128_GCM_SIV));
} else {
TEST_CHECK(!NKSN_GetKeys(inst, AEAD_AES_128_GCM_SIV, random(), random(), &c2s, &s2c));
}
}
} }
static int static int
@ -166,6 +176,7 @@ test_unit(void)
const char *cert, *key; const char *cert, *key;
int sock_fds[2], i; int sock_fds[2], i;
uint32_t cert_id; uint32_t cert_id;
NKE_Key c2s, s2c;
LCL_Initialise(); LCL_Initialise();
TST_RegisterDummyDrivers(); TST_RegisterDummyDrivers();
@ -190,6 +201,9 @@ test_unit(void)
TEST_CHECK(NKSN_StartSession(server, sock_fds[0], "client", server_cred, 4.0)); TEST_CHECK(NKSN_StartSession(server, sock_fds[0], "client", server_cred, 4.0));
TEST_CHECK(NKSN_StartSession(client, sock_fds[1], "server", client_cred, 4.0)); TEST_CHECK(NKSN_StartSession(client, sock_fds[1], "server", client_cred, 4.0));
TEST_CHECK(!NKSN_GetKeys(server, AEAD_AES_SIV_CMAC_256, 0, 0, &c2s, &s2c));
TEST_CHECK(!NKSN_GetKeys(client, AEAD_AES_SIV_CMAC_256, 0, 0, &c2s, &s2c));
send_message(client); send_message(client);
request_received = response_received = 0; request_received = response_received = 0;
@ -201,6 +215,9 @@ test_unit(void)
TEST_CHECK(NKSN_IsStopped(server)); TEST_CHECK(NKSN_IsStopped(server));
TEST_CHECK(NKSN_IsStopped(client)); TEST_CHECK(NKSN_IsStopped(client));
TEST_CHECK(!NKSN_GetKeys(server, AEAD_AES_SIV_CMAC_256, 0, 0, &c2s, &s2c));
TEST_CHECK(!NKSN_GetKeys(client, AEAD_AES_SIV_CMAC_256, 0, 0, &c2s, &s2c));
TEST_CHECK(request_received); TEST_CHECK(request_received);
TEST_CHECK(response_received); TEST_CHECK(response_received);

View file

@ -33,7 +33,7 @@
#define NKC_IsActive(inst) (random() % 2) #define NKC_IsActive(inst) (random() % 2)
#define NKC_GetRetryFactor(inst) (1) #define NKC_GetRetryFactor(inst) (1)
static int get_nts_data(NKC_Instance inst, NKE_Context *context, static int get_nts_data(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies, NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address); IPSockAddr *ntp_address);
#define NKC_GetNtsData get_nts_data #define NKC_GetNtsData get_nts_data
@ -41,7 +41,7 @@ static int get_nts_data(NKC_Instance inst, NKE_Context *context,
#include <nts_ntp_client.c> #include <nts_ntp_client.c>
static int static int
get_nts_data(NKC_Instance inst, NKE_Context *context, get_nts_data(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies, NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address) IPSockAddr *ntp_address)
{ {
@ -60,6 +60,14 @@ get_nts_data(NKC_Instance inst, NKE_Context *context,
context->s2c.length = SIV_GetKeyLength(context->algorithm); context->s2c.length = SIV_GetKeyLength(context->algorithm);
UTI_GetRandomBytes(context->s2c.key, context->s2c.length); UTI_GetRandomBytes(context->s2c.key, context->s2c.length);
if (random() % 2) {
*alt_context = *context;
UTI_GetRandomBytes(alt_context->c2s.key, alt_context->c2s.length);
UTI_GetRandomBytes(alt_context->s2c.key, alt_context->s2c.length);
} else {
alt_context->algorithm = AEAD_SIV_INVALID;
}
*num_cookies = random() % max_cookies + 1; *num_cookies = random() % max_cookies + 1;
for (i = 0; i < *num_cookies; i++) { for (i = 0; i < *num_cookies; i++) {
cookies[i].length = random() % (sizeof (cookies[i].cookie) + 1); cookies[i].length = random() % (sizeof (cookies[i].cookie) + 1);