Compare commits

...

41 Commits

Author SHA1 Message Date
Miroslav Lichvar
ad8fb64276 doc: improve NEWS
Add a notable enhancement from 4.0-pre1 and make the description of
another enhancement less ambiguous.
2020-04-20 15:49:58 +02:00
Miroslav Lichvar
436c1d3ea2 doc: update README 2020-04-20 15:44:12 +02:00
Miroslav Lichvar
7fc5da5f80 check return value of SCK_OpenUnixSocketPair() 2020-04-20 15:44:12 +02:00
Miroslav Lichvar
105b3faa46 samplefilt: remove useless assignment 2020-04-20 15:44:12 +02:00
Miroslav Lichvar
709223826f doc: update documentation for recent NTS changes 2020-04-16 18:09:32 +02:00
Miroslav Lichvar
eace93f2af nts: allow disabling certificate time checks
Add "nocerttimecheck" directive to specify the number of clock updates
that need to be made before the time validation of certificates is
enabled. This makes NTS usable on machines that don't have a RTC.
2020-04-16 18:09:32 +02:00
Miroslav Lichvar
2775846db7 nts: provide time function to gnutls
Use the internal time instead of system time for validation checks in
gnutls.
2020-04-16 18:09:29 +02:00
Miroslav Lichvar
4aff08e95d nts: add server support for NTP server negotiation
Add ntsntpserver directive to specify the hostname of the NTP server
provided in NTS-KE response to clients.
2020-04-16 17:47:27 +02:00
Miroslav Lichvar
958d66f8a7 cmdmon: reload NTS server keys on rekey command
When ntsrotate is set to 0, allow the keys to be reloaded with the rekey
command of chronyc.
2020-04-16 15:25:51 +02:00
Miroslav Lichvar
85fa29c43d nts: enable external management of server keys
If ntsrotate is set to 0, don't generate new server keys and don't save
them to ntsdumpdir. This allows the keys to be managed externally and
shared with other servers.
2020-04-16 15:25:50 +02:00
Miroslav Lichvar
0344b9a9c9 nts: generate cookies from second newest key
Generate one server key in advance to give it time to be distributed to
other servers before it is actually used.
2020-04-16 15:23:25 +02:00
Miroslav Lichvar
04f6329773 nts: encode key ID in cookie in network order
This allows the server cookie to be decoded on different platforms.
2020-04-15 16:30:54 +02:00
Miroslav Lichvar
d690faeb19 cmdmon: save NTS cookies and server keys on dump command
Extend the dump command to save also the server NTS keys and client NTS
cookies. Remove the warning for unset dumpdir.
2020-04-15 16:30:54 +02:00
Miroslav Lichvar
0b2e77ae64 ntp: update auth-specific address sooner
When replacing an NTP source, update the NTS address before the NTP
address to save cookies with the old NTP address instead of the newly
resolved address (which may immediately change to an address provided by
NTS-KE).
2020-04-15 16:30:54 +02:00
Miroslav Lichvar
2a4fd0a5c6 nts: update TLS exporter label
Change the string to "EXPORTER-network-time-security" as specified in
the latest NTS draft.
2020-04-09 17:08:52 +02:00
Miroslav Lichvar
e569e1c9d9 test: extend 139-nts test 2020-04-09 17:08:52 +02:00
Miroslav Lichvar
7be360041c nts: extend server key file format
Include in the key dump file an identifier, the AEAD number, and the
age of the last key to improve robustness and avoid generating a new key
immediately on start.

Also, improve the code that saves and loads the file.
2020-04-09 17:08:46 +02:00
Miroslav Lichvar
2fa83b541c nts: save and load cookies on client
Save the NTS context and cookies to files in the NTS dumpdir when the
client NTS instances are destroyed or the address is changed, and reload
the data to avoid unnecessary NTS-KE requests when chronyd is restarted
or it is switching between different addresses resolved from the NTS-KE
or NTP name.
2020-04-09 16:57:32 +02:00
Miroslav Lichvar
8db9d59dac nts: rename ntscachedir directive to ntsdumpdir
This makes the naming consistent with the existing dumpdir directive and
the dump command.
2020-04-09 16:57:32 +02:00
Miroslav Lichvar
adcf073484 nts: refactor NTS context
Add a context structure for the algorithm and keys established by
NTS-KE. Modify the client to save the context and reset the SIV key to
the C2S/S2C key before each request/response instead of keeping two SIV
instances.

This will make it easier for the server to support different algorithms
and allow the client to save the context with cookies to disk.
2020-04-09 16:57:31 +02:00
Miroslav Lichvar
5296858411 nts: drop unused constant 2020-04-09 16:42:20 +02:00
Miroslav Lichvar
d603426389 util: add function to split string into words 2020-04-09 16:42:20 +02:00
Miroslav Lichvar
d3f4292968 util: constify input parameters 2020-04-09 16:42:20 +02:00
Miroslav Lichvar
4dde7198c8 sources: constify parameters of log_selection_message() 2020-04-09 16:42:20 +02:00
Miroslav Lichvar
b145d3ff51 doc: add sourcename to list of remote commands 2020-04-09 16:42:20 +02:00
Miroslav Lichvar
9b98247d9c nts: zero cookie placeholder
Zero the body of the cookie placeholder in client requests as
recommended by the latest NTS draft.
2020-03-26 15:30:34 +01:00
Miroslav Lichvar
eedabb3d27 nts: disable TLS version 1.2
Require TLS version 1.3 or later as specified in the latest NTS draft.
2020-03-26 15:30:27 +01:00
Miroslav Lichvar
66dc2b6d6b nts: rework NTS-KE retry interval
Make the NTS-KE retry interval exponentially increasing, using a factor
provided by the NKE session. Use shorter intervals when the server is
refusing TCP connections or the connection is closed or timing out
before the TLS handshake.
2020-03-26 15:30:27 +01:00
Miroslav Lichvar
bcdbbbd694 nts: include server address in client NTS-KE log messages 2020-03-26 15:30:27 +01:00
Miroslav Lichvar
7b07e47c08 nts: fix address in server NTS-KE log messages
The server session instances are reused for different clients. Separate
the server name from the label used in log messages and set it on each
start of the session.
2020-03-26 15:30:27 +01:00
Miroslav Lichvar
a608496faf ntp: fix log message for replaced source
When a source was replaced and the new source had the same slot as the
old source, a wrong message was logged. Fix the condition to distinguish
correctly between changed address and port.

Fixes: 9468fd4aa6 ("ntp: allow changing port of source")
2020-03-26 15:26:58 +01:00
Miroslav Lichvar
c687224a11 reference: improve check for close leap second
Improve the check to work with the actual timestamp of the leap second
instead of the closest midnight and don't turn it off on the leap
timeout. Also allow sample times to be checked in addition to the system
time and NTP time to avoid accumulation of samples mixing pre-leap and
post-leap timestamps (causing error of +/-0.5 or +/-1.0 seconds).
2020-03-26 11:10:08 +01:00
Miroslav Lichvar
a6f2a613f3 socket: remove obsolete comment 2020-03-26 11:06:54 +01:00
Miroslav Lichvar
cfa39af345 socket: fix severity check in debug logging
Don't waste time formatting the debug message in log_message() when
debug output is disabled.

Fixes: 86a3ef9ed1 ("socket: add new socket support")
2020-03-26 11:06:54 +01:00
Miroslav Lichvar
8bab35c122 socket: increase maximum number of received messages
The buffers are no longer on stack. Increase their number for better
performance on heavily loaded servers.
2020-03-26 11:06:54 +01:00
Miroslav Lichvar
b20ef4cd7f socket: simplify receiving messages
Don't require the caller to provide a SCK_Message (on stack). Modify the
SCK_ReceiveMessage*() functions to return a pointer to static buffers,
as the message buffer which SCK_Message points to already is.
2020-03-26 11:04:18 +01:00
Miroslav Lichvar
b8b751a932 socket: enable port sharing on Linux
On Linux, enable the SO_REUSEPORT option on sockets bound to a port in
order to support load balancing with multiple chronyd instances
(configured to not adjust the system clock).

The IP_FREEBIND option already allowed different instances to bind to
the same address and port, but only one was actually receiving packets.

As the instances don't share their state, sharing the NTP port doesn't
work well with the interleaved mode, symmetric mode, and rate limiting.

Sharing the NTS-KE port will not work until the server keys can be
derived from a shared key.
2020-03-24 16:29:33 +01:00
Miroslav Lichvar
4a390841eb doc: fix typo in smoothtime description 2020-03-19 17:03:25 +01:00
David Bohman
f506f44033 sys_macosx: fix build issue on Sierra and presumably earlier 2020-03-18 12:23:26 +01:00
Miroslav Lichvar
1f8355f154 test: make 139-nts more reliable 2020-03-18 12:23:26 +01:00
Miroslav Lichvar
ddc2761498 doc: fix typo in NEWS 2020-03-16 18:34:26 +01:00
44 changed files with 1062 additions and 370 deletions

5
NEWS
View File

@@ -7,13 +7,14 @@ Enhancements
* Add support for AES-CMAC keys (AES128, AES256) with Nettle
* Add support for maxsamples of 1 for faster update with -q/-Q option
* Add -L option to limit log messages by severity
* Avoid replacing NTP sources with unreachable addresses
* Avoid replacing NTP sources with sources that have unreachable address
* Improve pools to repeat name resolution to get "maxsources" sources
* Improve NTP loop test to prevent synchronisation to itself
* Update clock synchronisation status and leap status more frequently
* Update seccomp filter
* Add "add pool" command
* Add -N option and sourcename command to print original names of sources
* Add -a option to source/sourcestats command to print unresolved sources
* Add -a option to sources/sourcestats command to print unresolved sources
* Add reset command to drop all measurements
Bug fixes

1
README
View File

@@ -112,6 +112,7 @@ Benny Lyne Amorsen <benny@amorsen.dk>
Andrew Bishop <amb@gedanken.demon.co.uk>
Vincent Blut <vincent.debian@free.fr>
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
David Bohman <debohman@gmail.com>
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
Leigh Brown <leigh@solinno.co.uk>
Erik Bryer <ebryer@spots.ab.ca>

View File

@@ -1267,8 +1267,8 @@ give_help(void)
"\0\0"
"Other daemon commands:\0\0"
"cyclelogs\0Close and re-open log files\0"
"dump\0Dump all measurements to save files\0"
"rekey\0Re-read keys from key file\0"
"dump\0Dump measurements and NTS keys/cookies\0"
"rekey\0Re-read keys\0"
"reset\0Drop all measurements\0"
"shutdown\0Stop daemon\0"
"\0\0"

View File

@@ -44,6 +44,7 @@
#include "reference.h"
#include "manual.h"
#include "memory.h"
#include "nts_ke_server.h"
#include "local.h"
#include "addrfilt.h"
#include "conf.h"
@@ -309,6 +310,8 @@ static void
handle_dump(CMD_Request *rx_message, CMD_Reply *tx_message)
{
SRC_DumpSources();
NSR_DumpAuthData();
NKS_DumpKeys();
}
/* ================================================== */
@@ -617,6 +620,7 @@ static void
handle_rekey(CMD_Request *rx_message, CMD_Reply *tx_message)
{
KEY_Reload();
NKS_ReloadKeys();
}
/* ================================================== */
@@ -1237,7 +1241,7 @@ handle_reset(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
read_from_cmd_socket(int sock_fd, int event, void *anything)
{
SCK_Message sck_message;
SCK_Message *sck_message;
CMD_Request rx_message;
CMD_Reply tx_message;
IPAddr loopback_addr, remote_ip;
@@ -1246,26 +1250,27 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
unsigned short rx_command;
struct timespec now, cooked_now;
if (!SCK_ReceiveMessage(sock_fd, &sck_message, 0))
sck_message = SCK_ReceiveMessage(sock_fd, 0);
if (!sck_message)
return;
read_length = sck_message.length;
read_length = sck_message->length;
/* Get current time cheaply */
SCH_GetLastEventTime(&cooked_now, NULL, &now);
/* Check if it's from localhost (127.0.0.1, ::1, or Unix domain),
or an authorised address */
switch (sck_message.addr_type) {
switch (sck_message->addr_type) {
case SCK_ADDR_IP:
assert(sock_fd == sock_fd4 || sock_fd == sock_fd6);
remote_ip = sck_message.remote_addr.ip.ip_addr;
remote_ip = sck_message->remote_addr.ip.ip_addr;
SCK_GetLoopbackIPAddress(remote_ip.family, &loopback_addr);
localhost = UTI_CompareIPs(&remote_ip, &loopback_addr, NULL) == 0;
if (!localhost && !ADF_IsAllowed(access_auth_table, &remote_ip)) {
DEBUG_LOG("Unauthorised host %s",
UTI_IPSockAddrToString(&sck_message.remote_addr.ip));
UTI_IPSockAddrToString(&sck_message->remote_addr.ip));
return;
}
@@ -1291,7 +1296,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
return;
}
memcpy(&rx_message, sck_message.data, read_length);
memcpy(&rx_message, sck_message->data, read_length);
if (rx_message.pkt_type != PKT_TYPE_CMD_REQUEST ||
rx_message.res1 != 0 ||
@@ -1313,8 +1318,8 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
rx_command = ntohs(rx_message.command);
memset(&tx_message, 0, sizeof (tx_message));
sck_message.data = &tx_message;
sck_message.length = 0;
sck_message->data = &tx_message;
sck_message->length = 0;
tx_message.version = PROTO_VERSION_NUMBER;
tx_message.pkt_type = PKT_TYPE_CMD_REPLY;
@@ -1329,7 +1334,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
tx_message.status = htons(STT_BADPKTVERSION);
transmit_reply(sock_fd, &sck_message);
transmit_reply(sock_fd, sck_message);
}
return;
}
@@ -1339,7 +1344,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
DEBUG_LOG("Command packet has invalid command %d", rx_command);
tx_message.status = htons(STT_INVALID);
transmit_reply(sock_fd, &sck_message);
transmit_reply(sock_fd, sck_message);
return;
}
@@ -1348,7 +1353,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
expected_length);
tx_message.status = htons(STT_BADPKTLENGTH);
transmit_reply(sock_fd, &sck_message);
transmit_reply(sock_fd, sck_message);
return;
}
@@ -1629,7 +1634,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
static int do_it=1;
if (do_it) {
transmit_reply(sock_fd, &sck_message);
transmit_reply(sock_fd, sck_message);
}
#if 0

40
conf.c
View File

@@ -223,8 +223,9 @@ static char *leapsec_tz = NULL;
/* Name of the user to which will be dropped root privileges. */
static char *user;
/* NTS cache dir, certificates, private key, and port */
static char *nts_cachedir = NULL;
/* NTS server and client configuration */
static char *nts_dump_dir = NULL;
static char *nts_ntp_server = NULL;
static char *nts_server_cert_file = NULL;
static char *nts_server_key_file = NULL;
static int nts_server_port = 11443;
@@ -234,6 +235,9 @@ static int nts_refresh = 2419200; /* 4 weeks */
static int nts_rotate = 604800; /* 1 week */
static char *nts_trusted_cert_file = NULL;
/* Number of clock updates needed to enable certificate time checks */
static int no_cert_time_check = 0;
/* Flag disabling use of system trusted certificates */
static int no_system_cert = 0;
@@ -404,7 +408,8 @@ CNF_Finalise(void)
Free(mail_user_on_change);
Free(tempcomp_sensor_file);
Free(tempcomp_point_file);
Free(nts_cachedir);
Free(nts_dump_dir);
Free(nts_ntp_server);
Free(nts_server_cert_file);
Free(nts_server_key_file);
Free(nts_trusted_cert_file);
@@ -543,6 +548,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_int(p, &min_samples);
} else if (!strcasecmp(command, "minsources")) {
parse_int(p, &min_sources);
} else if (!strcasecmp(command, "nocerttimecheck")) {
parse_int(p, &no_cert_time_check);
} else if (!strcasecmp(command, "noclientlog")) {
no_client_log = parse_null(p);
} else if (!strcasecmp(command, "nosystemcert")) {
@@ -551,8 +558,11 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_string(p, &ntp_signd_socket);
} else if (!strcasecmp(command, "ntstrustedcerts")) {
parse_string(p, &nts_trusted_cert_file);
} else if (!strcasecmp(command, "ntscachedir")) {
parse_string(p, &nts_cachedir);
} else if (!strcasecmp(command, "ntscachedir") ||
!strcasecmp(command, "ntsdumpdir")) {
parse_string(p, &nts_dump_dir);
} else if (!strcasecmp(command, "ntsntpserver")) {
parse_string(p, &nts_ntp_server);
} else if (!strcasecmp(command, "ntsport")) {
parse_int(p, &nts_server_port);
} else if (!strcasecmp(command, "ntsprocesses")) {
@@ -2069,9 +2079,17 @@ CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
/* ================================================== */
char *
CNF_GetNtsCacheDir(void)
CNF_GetNtsDumpDir(void)
{
return nts_cachedir;
return nts_dump_dir;
}
/* ================================================== */
char *
CNF_GetNtsNtpServer(void)
{
return nts_ntp_server;
}
/* ================================================== */
@@ -2145,3 +2163,11 @@ CNF_GetNoSystemCert(void)
{
return no_system_cert;
}
/* ================================================== */
int
CNF_GetNoCertTimeCheck(void)
{
return no_cert_time_check;
}

4
conf.h
View File

@@ -139,7 +139,8 @@ typedef struct {
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern char *CNF_GetNtsCacheDir(void);
extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void);
extern char *CNF_GetNtsServerCertFile(void);
extern char *CNF_GetNtsServerKeyFile(void);
extern int CNF_GetNtsServerPort(void);
@@ -149,5 +150,6 @@ extern int CNF_GetNtsRefresh(void);
extern int CNF_GetNtsRotate(void);
extern char *CNF_GetNtsTrustedCertFile(void);
extern int CNF_GetNoSystemCert(void);
extern int CNF_GetNoCertTimeCheck(void);
#endif /* GOT_CONF_H */

View File

@@ -689,6 +689,21 @@ changes in the frequency and offset of the clock. The offsets in the
<<chronyc.adoc#sourcestats,*sourcestats*>> reports (and the _tracking.log_ and
_statistics.log_ files) may be smaller than the actual offsets.
[[ntsdumpdir1]]*ntsdumpdir* _directory_::
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
*chronyd* is started again. The cookies are saved separately for each NTP
source in files named by the IP address of the NTS-KE server (e.g.
_1.2.3.4.nts_). By default, the client does not save the cookies.
+
An example of the directive is:
+
----
ntsdumpdir @CHRONYVARDIR@
----
+
This directory is used also by the <<ntsdumpdir2,NTS server>> to save keys.
[[ntsrefresh]]*ntsrefresh* _interval_::
This directive specifies the maximum interval between NTS-KE handshakes (in
seconds) in order to refresh the keys authenticating NTP packets. The default
@@ -703,6 +718,25 @@ of NTS servers in addition to the system's default trusted CAs (if the
[[nosystemcert]]*nosystemcert*::
This directive disables the system's default trusted CAs.
[[nocerttimecheck]]*nocerttimecheck* _limit_::
This directive disables the checks of the activation and expiration times of
certificates for the specified number of clock updates. It allows the NTS
authentication mechanism to be used on computers which start with wrong time
(e.g. due to not having an RTC or backup battery). Disabling the time checks
has important security implications, e.g. if an NTP server was ever
compromised, its certificate could be used in an attack after the expiration
time. The default value is 0, which means the time checks are always enabled.
An example of the directive is:
+
----
nocerttimecheck 1
----
+
This would disable the time checks until the clock is updated for the first
time, assuming the first update corrects the clock and later checks can work
with correct time.
=== Source selection
[[combinelimit]]*combinelimit* _limit_::
@@ -888,7 +922,7 @@ slightly different rates when it is necessary to keep them close together, the
enable a server leap smear.
+
When smearing a leap second, the leap status is suppressed on the server and
the served time is corrected slowly be slewing instead of stepping. The clients
the served time is corrected slowly by slewing instead of stepping. The clients
do not need any special configuration as they do not know there is any leap
second and they follow the server time which eventually brings them back to
UTC. Care must be taken to ensure they use only NTP servers which smear the
@@ -1390,16 +1424,50 @@ process will be started and all NTS-KE requests will be handled by the main
This directive specifies the maximum number of concurrent NTS-KE connections
per process that the NTS server will accept. The default value is 100.
[[ntscachedir]]*ntscachedir* _directory_::
This directive specifies a directory to save the keys which the NTS server uses
to encrypt NTS cookies in order to prevent a storm of NTS-KE handshakes when
the server is restarted. By default, the server does not save the keys.
[[ntsdumpdir2]]*ntsdumpdir* _directory_::
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
saved to a single file named _ntskeys_. When *chronyd* is restarted, reloading
the keys allows the clients to continue using old cookies and avoids a storm of
NTS-KE requests. By default, the server does not save the keys.
+
An example of the directive is:
+
----
ntsdumpdir @CHRONYVARDIR@
----
+
This directory is used also by the <<ntsdumpdir1,NTS client>> to save NTS cookies.
[[ntsntpserver]]*ntsntpserver* _hostname_::
This directive specifies the hostname or address of the NTP server(s) which is
provided in the NTS-KE response to the clients. It allows the NTS-KE server to
be separated from the NTP server. However, the servers need to share the keys,
i.e. external key management needs to be enabled by setting
<<ntsrotate,*ntsrotate*>> to 0. By default, no hostname or address is provided
to the clients, which means they should use the same server for NTS-KE and NTP.
[[ntsrotate]]*ntsrotate* _interval_::
This directive specifies the rotation interval (in seconds) of the server key
which encrypts cookies. The server keeps up to 3 previous keys to give the
clients enough time to get cookies encrypted by the latest key. The default
interval is 604800 (1 week).
which encrypts the NTS cookies. New keys are generated automatically. The
server keeps two previous keys to give the clients time to get new cookies
encrypted by the latest key. The default interval is 604800 seconds (1 week).
+
The automatic rotation of the keys can be disabled by setting *ntsrotate* to 0.
In this case the keys are assumed to be managed externally. *chronyd* will not
save the keys to the _ntskeys_ file and will reload the keys from the file when
the <<chronyc.adoc#rekey,*rekey*>> command is issued in *chronyc*. The file can
be periodically copied from another server running *chronyd* (which does
not have *ntsrotate* set to 0) in order to have one or more servers dedicated
to NTS-KE. The NTS-KE servers need to be configured with the
<<ntsntpname,*ntsntpname*>> directive to point the clients to the right NTP
server.
+
An example of the directive is:
+
----
ntsrotate 2592000
----
[[port]]*port* _port_::
This option allows you to configure the port on which *chronyd* will listen for

View File

@@ -51,7 +51,8 @@ running under a non-root user), it will try to connect to 127.0.0.1 and then
Only the following monitoring commands, which do not affect the behaviour of
*chronyd*, are allowed from the network: *activity*, *manual list*,
*rtcdata*, *smoothing*, *sources*, *sourcestats*, *tracking*, *waitsync*. The
*rtcdata*, *smoothing*, *sourcename*, *sources*, *sourcestats*, *tracking*,
*waitsync*. The
set of hosts from which *chronyd* will accept these commands can be configured
with the <<chrony.conf.adoc#cmdallow,*cmdallow*>> directive in the *chronyd*'s
configuration file or the <<cmdallow,*cmdallow*>> command in *chronyc*. By
@@ -1163,13 +1164,18 @@ purged. An example of how to do this is shown below.
The *dump* command causes *chronyd* to write its current history of
measurements for each of its sources to dump files in the directory specified
in the configuration file by the <<chrony.conf.adoc#dumpdir,*dumpdir*>>
directive and also write server NTS keys and client NTS cookies to the
directory specified by the <<chrony.conf.adoc#ntsdumpdir1,*ntsdumpdir*>>
directive. Note that *chronyd* does this automatically when it exits. This
command is mainly useful for inspection of the history whilst *chronyd* is
running.
command is mainly useful for inspection whilst *chronyd* is running.
[[rekey]]*rekey*::
The *rekey* command causes *chronyd* to re-read the key file specified in the
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive.
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive. It
also re-reads the server NTS keys if
<<chrony.conf.adoc#ntsdumpdir2,*ntsdumpdir*>> is specified and
<<chrony.conf.adoc#ntsrotate,automatic rotation>> is disabled in the
configuration file.
[[reset]]*reset*::
The *reset* command causes *chronyd* to drop all measurements and switch to the

View File

@@ -484,3 +484,17 @@ NAU_ChangeAddress(NAU_Instance instance, IPAddr *address)
assert(0);
}
}
/* ================================================== */
void
NAU_DumpData(NAU_Instance instance)
{
switch (instance->mode) {
case NTP_AUTH_NTS:
NNC_DumpData(instance->nts);
break;
default:
break;
}
}

View File

@@ -86,4 +86,7 @@ extern int NAU_CheckResponseAuth(NAU_Instance instance, NTP_Packet *response,
/* Change an authentication-specific address (e.g. after replacing a source) */
extern void NAU_ChangeAddress(NAU_Instance instance, IPAddr *address);
/* Save authentication-specific data to speed up the next start */
extern void NAU_DumpData(NAU_Instance instance);
#endif

View File

@@ -703,6 +703,11 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int
{
memset(&inst->report, 0, sizeof (inst->report));
NCR_ResetInstance(inst);
/* Update the authentication-specific address before NTP address */
if (!ntp_only)
NAU_ChangeAddress(inst->auth, &remote_addr->ip_addr);
inst->remote_addr = *remote_addr;
if (inst->mode == MODE_CLIENT)
@@ -718,9 +723,6 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int
SRC_SetRefid(inst->source, UTI_IPToRefid(&remote_addr->ip_addr),
&inst->remote_addr.ip_addr);
SRC_ResetInstance(inst->source);
if (!ntp_only)
NAU_ChangeAddress(inst->auth, &remote_addr->ip_addr);
}
/* ================================================== */
@@ -2560,6 +2562,14 @@ int NCR_IsSyncPeer(NCR_Instance inst)
/* ================================================== */
void
NCR_DumpAuthData(NCR_Instance inst)
{
NAU_DumpData(inst->auth);
}
/* ================================================== */
static void
broadcast_timeout(void *arg)
{

View File

@@ -136,6 +136,8 @@ extern uint32_t NCR_GetLocalRefid(NCR_Instance inst);
extern int NCR_IsSyncPeer(NCR_Instance instance);
extern void NCR_DumpAuthData(NCR_Instance inst);
extern void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval);
#endif /* GOT_NTP_CORE_H */

View File

@@ -404,10 +404,7 @@ process_message(SCK_Message *message, int sock_fd, int event)
static void
read_from_socket(int sock_fd, int event, void *anything)
{
/* This should only be called when there is something
to read, otherwise it may block */
SCK_Message messages[SCK_MAX_RECV_MESSAGES];
SCK_Message *messages;
int i, received, flags = 0;
#ifdef HAVE_LINUX_TIMESTAMPING
@@ -423,8 +420,8 @@ read_from_socket(int sock_fd, int event, void *anything)
#endif
}
received = SCK_ReceiveMessages(sock_fd, messages, SCK_MAX_RECV_MESSAGES, flags);
if (received <= 0)
messages = SCK_ReceiveMessages(sock_fd, flags, &received);
if (!messages)
return;
for (i = 0; i < received; i++)

View File

@@ -381,7 +381,7 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
name = record->name;
severity = UTI_IsIPReal(&old_addr->ip_addr) ? LOGS_INFO : LOGS_DEBUG;
if (slot1 != slot2) {
if (found == 0) {
/* The hash table must be rebuilt for the changed address */
rehash_records();
@@ -1297,6 +1297,18 @@ NSR_GetActivityReport(RPT_ActivityReport *report)
}
}
/* ================================================== */
void
NSR_DumpAuthData(void)
{
SourceRecord *record;
int i;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr)
continue;
NCR_DumpAuthData(record->data);
}
}

View File

@@ -140,4 +140,6 @@ extern int NSR_GetNTPReport(RPT_NTPReport *report);
extern void NSR_GetActivityReport(RPT_ActivityReport *report);
extern void NSR_DumpAuthData(void);
#endif /* GOT_NTP_SOURCES_H */

View File

@@ -46,7 +46,7 @@
#define NKE_ERROR_INTERNAL_SERVER_ERROR 2
#define NKE_ALPN_NAME "ntske/1"
#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security/1"
#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"
@@ -56,11 +56,21 @@
#define NKE_MAX_COOKIES 8
#define NKE_MAX_KEY_LENGTH SIV_MAX_KEY_LENGTH
#define NKE_RETRY_FACTOR2_CONNECT 4
#define NKE_RETRY_FACTOR2_TLS 10
#define NKE_MAX_RETRY_INTERVAL2 19
typedef struct {
int length;
unsigned char key[NKE_MAX_KEY_LENGTH];
} NKE_Key;
typedef struct {
SIV_Algorithm algorithm;
NKE_Key c2s;
NKE_Key s2c;
} NKE_Context;
typedef struct {
int length;
unsigned char cookie[NKE_MAX_COOKIE_LENGTH];

View File

@@ -49,8 +49,7 @@ struct NKC_Instance_Record {
int got_response;
int resolving_name;
SIV_Algorithm siv_algorithm;
NKE_Key c2s, s2c;
NKE_Context context;
NKE_Cookie cookies[NKE_MAX_COOKIES];
int num_cookies;
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 1];
@@ -156,7 +155,7 @@ process_response(NKC_Instance inst)
break;
}
aead_algorithm = AEAD_AES_SIV_CMAC_256;
inst->siv_algorithm = aead_algorithm;
inst->context.algorithm = aead_algorithm;
break;
case NKE_RECORD_ERROR:
if (length == 2)
@@ -237,7 +236,8 @@ handle_message(void *arg)
return 0;
}
if (!NKSN_GetKeys(inst->session, inst->siv_algorithm, &inst->c2s, &inst->s2c))
if (!NKSN_GetKeys(inst->session, inst->context.algorithm,
&inst->context.c2s, &inst->context.s2c))
return 0;
if (inst->server_name[0] != '\0') {
@@ -318,6 +318,7 @@ int
NKC_Start(NKC_Instance inst)
{
IPSockAddr local_addr;
char label[512];
int sock_fd;
assert(!NKC_IsActive(inst));
@@ -338,8 +339,13 @@ NKC_Start(NKC_Instance inst)
if (sock_fd < 0)
return 0;
/* Make a label containing both the address and name of the server */
if (snprintf(label, sizeof (label), "%s (%s)",
UTI_IPSockAddrToString(&inst->address), inst->name) >= sizeof (label))
;
/* Start a NTS-KE session */
if (!NKSN_StartSession(inst->session, sock_fd, client_credentials, CLIENT_TIMEOUT)) {
if (!NKSN_StartSession(inst->session, sock_fd, label, client_credentials, CLIENT_TIMEOUT)) {
SCK_CloseSocket(sock_fd);
return 0;
}
@@ -365,8 +371,7 @@ NKC_IsActive(NKC_Instance inst)
/* ================================================== */
int
NKC_GetNtsData(NKC_Instance inst,
SIV_Algorithm *siv_algorithm, NKE_Key *c2s, NKE_Key *s2c,
NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address)
{
@@ -375,9 +380,7 @@ NKC_GetNtsData(NKC_Instance inst,
if (!inst->got_response || inst->resolving_name)
return 0;
*siv_algorithm = inst->siv_algorithm;
*c2s = inst->c2s;
*s2c = inst->s2c;
*context = inst->context;
for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
cookies[i] = inst->cookies[i];
@@ -387,3 +390,11 @@ NKC_GetNtsData(NKC_Instance inst,
return i;
}
/* ================================================== */
int
NKC_GetRetryFactor(NKC_Instance inst)
{
return NKSN_GetRetryFactor(inst->session);
}

View File

@@ -50,9 +50,11 @@ extern int NKC_Start(NKC_Instance inst);
extern int NKC_IsActive(NKC_Instance inst);
/* Get the NTS data if the session was successful */
extern int NKC_GetNtsData(NKC_Instance inst,
SIV_Algorithm *siv_algorithm, NKE_Key *c2s, NKE_Key *s2c,
extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address);
/* Get a factor to calculate retry interval (in log2 seconds) */
extern int NKC_GetRetryFactor(NKC_Instance inst);
#endif

View File

@@ -50,8 +50,10 @@
#define KEY_ID_INDEX_BITS 2
#define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS)
#define FUTURE_KEYS 1
#define MIN_KEY_ROTATE_INTERVAL 1.0
#define DUMP_FILENAME "ntskeys"
#define DUMP_IDENTIFIER "NKS0\n"
#define INVALID_SOCK_FD (-7)
@@ -78,6 +80,8 @@ typedef struct {
static ServerKey server_keys[MAX_SERVER_KEYS];
static int current_server_key;
static double last_server_key_ts;
static int key_rotation_interval;
static int server_sock_fd4;
static int server_sock_fd6;
@@ -113,7 +117,7 @@ handle_client(int sock_fd, IPSockAddr *addr)
instp = ARR_GetElement(sessions, i);
if (!*instp) {
/* NULL handler arg will be replaced with the session instance */
inst = NKSN_CreateInstance(1, UTI_IPSockAddrToString(addr), handle_message, NULL);
inst = NKSN_CreateInstance(1, NULL, handle_message, NULL);
*instp = inst;
break;
} else if (NKSN_IsStopped(*instp)) {
@@ -128,7 +132,8 @@ handle_client(int sock_fd, IPSockAddr *addr)
return 0;
}
if (!NKSN_StartSession(inst, sock_fd, server_credentials, SERVER_TIMEOUT))
if (!NKSN_StartSession(inst, sock_fd, UTI_IPSockAddrToString(addr),
server_credentials, SERVER_TIMEOUT))
return 0;
return 1;
@@ -139,28 +144,29 @@ handle_client(int sock_fd, IPSockAddr *addr)
static void
handle_helper_request(int fd, int event, void *arg)
{
SCK_Message message;
SCK_Message *message;
HelperRequest *req;
IPSockAddr client_addr;
int sock_fd;
if (!SCK_ReceiveMessage(fd, &message, SCK_FLAG_MSG_DESCRIPTOR))
message = SCK_ReceiveMessage(fd, SCK_FLAG_MSG_DESCRIPTOR);
if (!message)
return;
sock_fd = message.descriptor;
sock_fd = message->descriptor;
if (sock_fd < 0) {
/* Message with no descriptor is a shutdown command */
SCH_QuitProgram();
return;
}
if (message.length != sizeof (HelperRequest)) {
if (message->length != sizeof (HelperRequest)) {
DEBUG_LOG("Unexpected message length");
SCK_CloseSocket(sock_fd);
return;
}
req = message.data;
req = message->data;
/* Extract the server key and client address from the request */
server_keys[current_server_key].id = ntohl(req->key_id);
@@ -291,8 +297,9 @@ helper_signal(int x)
static int
prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm)
{
NKE_Context context;
NKE_Cookie cookie;
NKE_Key c2s, s2c;
char *ntp_server;
uint16_t datum;
int i;
@@ -319,19 +326,20 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
return 0;
}
/* This should be configurable */
if (0) {
const char server[] = "::1";
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_SERVER_NEGOTIATION, server,
sizeof (server) - 1))
ntp_server = CNF_GetNtsNtpServer();
if (ntp_server) {
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_SERVER_NEGOTIATION,
ntp_server, strlen(ntp_server)))
return 0;
}
if (!NKSN_GetKeys(session, aead_algorithm, &c2s, &s2c))
context.algorithm = aead_algorithm;
if (!NKSN_GetKeys(session, aead_algorithm, &context.c2s, &context.s2c))
return 0;
for (i = 0; i < NKE_MAX_COOKIES; i++) {
if (!NKS_GenerateCookie(&c2s, &s2c, &cookie))
if (!NKS_GenerateCookie(&context, &cookie))
return 0;
if (!NKSN_AddRecord(session, 0, NKE_RECORD_COOKIE, cookie.cookie, cookie.length))
return 0;
@@ -434,6 +442,8 @@ generate_key(int index)
server_keys[index].id |= index;
DEBUG_LOG("Generated server key %"PRIX32, server_keys[index].id);
last_server_key_ts = SCH_GetLastEventMonoTime();
}
/* ================================================== */
@@ -441,77 +451,96 @@ generate_key(int index)
static void
save_keys(void)
{
char hex_key[SIV_MAX_KEY_LENGTH * 2 + 1];
char buf[SIV_MAX_KEY_LENGTH * 2 + 1], *dump_dir;
int i, index, key_length;
char *cachedir;
double last_key_age;
FILE *f;
cachedir = CNF_GetNtsCacheDir();
if (!cachedir)
/* Don't save the keys if rotation is disabled to enable an external
management of the keys (e.g. share them with another server) */
if (key_rotation_interval == 0)
return;
f = UTI_OpenFile(cachedir, "ntskeys", ".tmp", 'w', 0600);
dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir)
return;
f = UTI_OpenFile(dump_dir, DUMP_FILENAME, ".tmp", 'w', 0600);
if (!f)
return;
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
last_key_age = SCH_GetLastEventMonoTime() - last_server_key_ts;
if (fprintf(f, "%s%d %.1f\n", DUMP_IDENTIFIER, SERVER_COOKIE_SIV, last_key_age) < 0)
goto error;
for (i = 0; i < MAX_SERVER_KEYS; i++) {
index = (current_server_key + i + 1) % MAX_SERVER_KEYS;
index = (current_server_key + i + 1 + FUTURE_KEYS) % MAX_SERVER_KEYS;
if (key_length > sizeof (server_keys[index].key) ||
!UTI_BytesToHex(server_keys[index].key, key_length, hex_key, sizeof (hex_key))) {
assert(0);
break;
}
fprintf(f, "%08"PRIX32" %s\n", server_keys[index].id, hex_key);
!UTI_BytesToHex(server_keys[index].key, key_length, buf, sizeof (buf)) ||
fprintf(f, "%08"PRIX32" %s\n", server_keys[index].id, buf) < 0)
goto error;
}
fclose(f);
if (!UTI_RenameTempFile(cachedir, "ntskeys", ".tmp", NULL))
if (!UTI_RenameTempFile(dump_dir, DUMP_FILENAME, ".tmp", NULL)) {
if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, ".tmp"))
;
}
return;
error:
DEBUG_LOG("Could not %s server keys", "save");
fclose(f);
if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, NULL))
;
}
/* ================================================== */
#define MAX_WORDS 2
static void
load_keys(void)
{
int i, index, line_length, key_length, n;
char *cachedir, line[1024];
char *dump_dir, line[1024], *words[MAX_WORDS];
int i, index, key_length, algorithm;
double key_age;
FILE *f;
uint32_t id;
cachedir = CNF_GetNtsCacheDir();
if (!cachedir)
dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir)
return;
f = UTI_OpenFile(cachedir, "ntskeys", NULL, 'r', 0);
f = UTI_OpenFile(dump_dir, DUMP_FILENAME, NULL, 'r', 0);
if (!f)
return;
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
sscanf(words[0], "%d", &algorithm) != 1 || algorithm != SERVER_COOKIE_SIV ||
sscanf(words[1], "%lf", &key_age) != 1)
goto error;
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0);
for (i = 0; i < MAX_SERVER_KEYS; i++) {
if (!fgets(line, sizeof (line), f))
break;
line_length = strlen(line);
if (line_length < 10)
break;
/* Drop '\n' */
line[line_length - 1] = '\0';
if (sscanf(line, "%"PRIX32"%n", &id, &n) != 1 || line[n] != ' ')
break;
for (i = 0; i < MAX_SERVER_KEYS && fgets(line, sizeof (line), f); i++) {
if (UTI_SplitString(line, words, MAX_WORDS) != 2 ||
sscanf(words[0], "%"PRIX32, &id) != 1)
goto error;
index = id % MAX_SERVER_KEYS;
if (UTI_HexToBytes(line + n + 1, server_keys[index].key,
if (UTI_HexToBytes(words[1], server_keys[index].key,
sizeof (server_keys[index].key)) != key_length)
break;
goto error;
server_keys[index].id = id;
if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
@@ -519,10 +548,16 @@ load_keys(void)
DEBUG_LOG("Loaded key %"PRIX32, id);
current_server_key = index;
current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS;
}
fclose(f);
return;
error:
DEBUG_LOG("Could not %s server keys", "load");
fclose(f);
}
/* ================================================== */
@@ -531,11 +566,10 @@ static void
key_timeout(void *arg)
{
current_server_key = (current_server_key + 1) % MAX_SERVER_KEYS;
generate_key(current_server_key);
generate_key((current_server_key + FUTURE_KEYS) % MAX_SERVER_KEYS);
save_keys();
SCH_AddTimeoutByDelay(MAX(CNF_GetNtsRotate(), MIN_KEY_ROTATE_INTERVAL),
key_timeout, NULL);
SCH_AddTimeoutByDelay(key_rotation_interval, key_timeout, NULL);
}
/* ================================================== */
@@ -582,6 +616,7 @@ NKS_Initialise(int scfilter_level)
{
char *cert, *key;
int i, processes;
double key_delay;
server_sock_fd4 = INVALID_SOCK_FD;
server_sock_fd6 = INVALID_SOCK_FD;
@@ -615,7 +650,12 @@ NKS_Initialise(int scfilter_level)
load_keys();
key_timeout(NULL);
key_rotation_interval = MAX(CNF_GetNtsRotate(), 0);
if (key_rotation_interval > 0) {
key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts);
SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL);
}
processes = CNF_GetNtsServerProcesses();
@@ -623,6 +663,8 @@ NKS_Initialise(int scfilter_level)
int sock_fd1, sock_fd2;
sock_fd1 = SCK_OpenUnixSocketPair(0, &sock_fd2);
if (sock_fd1 < 0)
LOG_FATAL("Could not open socket pair");
for (i = 0; i < processes; i++)
start_helper(i + 1, scfilter_level, sock_fd1, sock_fd2);
@@ -674,10 +716,31 @@ NKS_Finalise(void)
/* ================================================== */
void
NKS_DumpKeys(void)
{
save_keys();
}
/* ================================================== */
void
NKS_ReloadKeys(void)
{
/* Don't load the keys if they are expected to be generated by this server
instance (i.e. they are already loaded) to not delay the next rotation */
if (key_rotation_interval > 0)
return;
load_keys();
}
/* ================================================== */
/* A server cookie consists of key ID, nonce, and encrypted C2S+S2C keys */
int
NKS_GenerateCookie(NKE_Key *c2s, NKE_Key *s2c, NKE_Cookie *cookie)
NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
{
unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
int plaintext_length, tag_length;
@@ -689,8 +752,14 @@ NKS_GenerateCookie(NKE_Key *c2s, NKE_Key *s2c, NKE_Cookie *cookie)
return 0;
}
if (c2s->length < 0 || c2s->length > NKE_MAX_KEY_LENGTH ||
s2c->length < 0 || s2c->length > NKE_MAX_KEY_LENGTH) {
/* The algorithm is hardcoded for now */
if (context->algorithm != AEAD_AES_SIV_CMAC_256) {
DEBUG_LOG("Unexpected SIV algorithm");
return 0;
}
if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH ||
context->s2c.length < 0 || context->s2c.length > NKE_MAX_KEY_LENGTH) {
DEBUG_LOG("Invalid key length");
return 0;
}
@@ -699,14 +768,13 @@ NKS_GenerateCookie(NKE_Key *c2s, NKE_Key *s2c, NKE_Cookie *cookie)
header = (ServerCookieHeader *)cookie->cookie;
/* Keep the fields in the host byte order */
header->key_id = key->id;
header->key_id = htonl(key->id);
UTI_GetRandomBytes(header->nonce, sizeof (header->nonce));
plaintext_length = c2s->length + s2c->length;
plaintext_length = context->c2s.length + context->s2c.length;
assert(plaintext_length <= sizeof (plaintext));
memcpy(plaintext, c2s->key, c2s->length);
memcpy(plaintext + c2s->length, s2c->key, s2c->length);
memcpy(plaintext, context->c2s.key, context->c2s.length);
memcpy(plaintext + context->c2s.length, context->s2c.key, context->s2c.length);
tag_length = SIV_GetTagLength(key->siv);
cookie->length = sizeof (*header) + plaintext_length + tag_length;
@@ -727,12 +795,13 @@ NKS_GenerateCookie(NKE_Key *c2s, NKE_Key *s2c, NKE_Cookie *cookie)
/* ================================================== */
int
NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Key *c2s, NKE_Key *s2c)
NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
{
unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
int ciphertext_length, plaintext_length, tag_length;
ServerCookieHeader *header;
ServerKey *key;
uint32_t key_id;
if (!initialised) {
DEBUG_LOG("NTS server disabled");
@@ -748,9 +817,10 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Key *c2s, NKE_Key *s2c)
ciphertext = cookie->cookie + sizeof (*header);
ciphertext_length = cookie->length - sizeof (*header);
key = &server_keys[header->key_id % MAX_SERVER_KEYS];
if (header->key_id != key->id) {
DEBUG_LOG("Unknown key %"PRIX32, header->key_id);
key_id = ntohl(header->key_id);
key = &server_keys[key_id % MAX_SERVER_KEYS];
if (key_id != key->id) {
DEBUG_LOG("Unknown key %"PRIX32, key_id);
return 0;
}
@@ -774,12 +844,14 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Key *c2s, NKE_Key *s2c)
return 0;
}
c2s->length = plaintext_length / 2;
s2c->length = plaintext_length / 2;
assert(c2s->length <= sizeof (c2s->key));
context->algorithm = AEAD_AES_SIV_CMAC_256;
memcpy(c2s->key, plaintext, c2s->length);
memcpy(s2c->key, plaintext + c2s->length, s2c->length);
context->c2s.length = plaintext_length / 2;
context->s2c.length = plaintext_length / 2;
assert(context->c2s.length <= sizeof (context->c2s.key));
memcpy(context->c2s.key, plaintext, context->c2s.length);
memcpy(context->s2c.key, plaintext + context->c2s.length, context->s2c.length);
return 1;
}

View File

@@ -33,10 +33,16 @@
extern void NKS_Initialise(int scfilter_level);
extern void NKS_Finalise(void);
/* Generate a new NTS cookie containing the C2S and S2C keys */
extern int NKS_GenerateCookie(NKE_Key *c2s, NKE_Key *s2c, NKE_Cookie *cookie);
/* Save the current server keys */
extern void NKS_DumpKeys(void);
/* Validate a cookie and extract the C2S and S2C keys */
extern int NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Key *c2s, NKE_Key *s2c);
/* Reload the keys */
extern void NKS_ReloadKeys(void);
/* Generate an NTS cookie with a given context */
extern int NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie);
/* Validate a cookie and decode the context */
extern int NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context);
#endif

View File

@@ -31,6 +31,7 @@
#include "nts_ke_session.h"
#include "conf.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "siv.h"
@@ -39,6 +40,7 @@
#include "util.h"
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#define INVALID_SOCK_FD (-8)
@@ -66,14 +68,16 @@ typedef enum {
struct NKSN_Instance_Record {
int server;
char *name;
char *server_name;
NKSN_MessageHandler handler;
void *handler_arg;
KeState state;
int sock_fd;
char *label;
gnutls_session_t tls_session;
SCH_TimeoutID timeout_id;
int retry_factor;
struct Message message;
int new_message;
@@ -86,6 +90,8 @@ static gnutls_priority_t priority_cache;
static int credentials_counter = 0;
static int clock_updates = 0;
/* ================================================== */
static void
@@ -206,6 +212,7 @@ create_tls_session(int server_mode, int sock_fd, const char *server_name,
unsigned char alpn_name[sizeof (NKE_ALPN_NAME)];
gnutls_session_t session;
gnutls_datum_t alpn;
unsigned int flags;
int r;
r = gnutls_init(&session, GNUTLS_NONBLOCK | (server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
@@ -218,7 +225,15 @@ create_tls_session(int server_mode, int sock_fd, const char *server_name,
r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name));
if (r < 0)
goto error;
gnutls_session_set_verify_cert(session, server_name, 0);
flags = 0;
if (clock_updates < CNF_GetNoCertTimeCheck()) {
flags |= GNUTLS_VERIFY_DISABLE_TIME_CHECKS | GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS;
DEBUG_LOG("Disabled time checks");
}
gnutls_session_set_verify_cert(session, server_name, flags);
}
r = gnutls_priority_set(session, priority);
@@ -261,6 +276,9 @@ stop_session(NKSN_Instance inst)
SCK_CloseSocket(inst->sock_fd);
inst->sock_fd = INVALID_SOCK_FD;
Free(inst->label);
inst->label = NULL;
gnutls_deinit(inst->tls_session);
inst->tls_session = NULL;
@@ -275,7 +293,7 @@ session_timeout(void *arg)
{
NKSN_Instance inst = arg;
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE session with %s timed out", inst->name);
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE session with %s timed out", inst->label);
inst->timeout_id = 0;
stop_session(inst);
@@ -360,12 +378,12 @@ handle_event(NKSN_Instance inst, int event)
r = get_socket_error(inst->sock_fd);
if (r) {
LOG(LOGS_ERR, "Could not connect to %s : %s", inst->name, strerror(r));
LOG(LOGS_ERR, "Could not connect to %s : %s", inst->label, strerror(r));
stop_session(inst);
return 0;
}
DEBUG_LOG("Connected to %s", inst->name);
DEBUG_LOG("Connected to %s", inst->label);
change_state(inst, KE_HANDSHAKE);
return 0;
@@ -376,8 +394,14 @@ handle_event(NKSN_Instance inst, int event)
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"TLS handshake with %s failed : %s", inst->name, gnutls_strerror(r));
"TLS handshake with %s failed : %s", inst->label, gnutls_strerror(r));
stop_session(inst);
/* Increase the retry interval if the handshake did not fail due
to the other end closing the connection */
if (r != GNUTLS_E_PULL_ERROR && r != GNUTLS_E_PREMATURE_TERMINATION)
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
return 0;
}
@@ -387,15 +411,17 @@ handle_event(NKSN_Instance inst, int event)
return 0;
}
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
if (DEBUG) {
char *description = gnutls_session_get_desc(inst->tls_session);
DEBUG_LOG("Handshake with %s completed %s",
inst->name, description ? description : "");
inst->label, description ? description : "");
gnutls_free(description);
}
if (!check_alpn(inst)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE not supported by %s", inst->name);
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE not supported by %s", inst->label);
stop_session(inst);
return 0;
}
@@ -413,13 +439,13 @@ handle_event(NKSN_Instance inst, int event)
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"Could not send NTS-KE message to %s : %s", inst->name, gnutls_strerror(r));
"Could not send NTS-KE message to %s : %s", inst->label, gnutls_strerror(r));
stop_session(inst);
}
return 0;
}
DEBUG_LOG("Sent %d bytes to %s", r, inst->name);
DEBUG_LOG("Sent %d bytes to %s", r, inst->label);
message->sent += r;
if (message->sent < message->length)
@@ -448,13 +474,13 @@ handle_event(NKSN_Instance inst, int event)
if (gnutls_error_is_fatal(r) || r == GNUTLS_E_REHANDSHAKE) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"Could not receive NTS-KE message from %s : %s",
inst->name, gnutls_strerror(r));
inst->label, gnutls_strerror(r));
stop_session(inst);
}
return 0;
}
DEBUG_LOG("Received %d bytes from %s", r, inst->name);
DEBUG_LOG("Received %d bytes from %s", r, inst->label);
message->length += r;
@@ -462,7 +488,7 @@ handle_event(NKSN_Instance inst, int event)
if (!check_message_format(message, r == 0)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"Received invalid NTS-KE message from %s", inst->name);
"Received invalid NTS-KE message from %s", inst->label);
stop_session(inst);
return 0;
}
@@ -480,7 +506,7 @@ handle_event(NKSN_Instance inst, int event)
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
DEBUG_LOG("Shutdown with %s failed : %s", inst->name, gnutls_strerror(r));
DEBUG_LOG("Shutdown with %s failed : %s", inst->label, gnutls_strerror(r));
stop_session(inst);
return 0;
}
@@ -524,6 +550,30 @@ read_write_socket(int fd, int event, void *arg)
/* ================================================== */
static time_t
get_time(time_t *t)
{
struct timespec now;
LCL_ReadCookedTime(&now, NULL);
if (t)
*t = now.tv_sec;
return now.tv_sec;
}
/* ================================================== */
static void
handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
if (change_type != LCL_ChangeUnknownStep && clock_updates < INT_MAX)
clock_updates++;
}
/* ================================================== */
static int gnutls_initialised = 0;
static void
@@ -538,13 +588,18 @@ init_gnutls(void)
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
/* NTS specification requires TLS1.2 or later */
r = gnutls_priority_init2(&priority_cache, "-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1",
/* NTS specification requires TLS1.3 or later */
r = gnutls_priority_init2(&priority_cache,
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2",
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r));
gnutls_global_set_time_function(get_time);
gnutls_initialised = 1;
LCL_AddParameterChangeHandler(handle_step, NULL);
}
/* ================================================== */
@@ -554,6 +609,8 @@ deinit_gnutls(void)
{
assert(gnutls_initialised);
LCL_RemoveParameterChangeHandler(handle_step, NULL);
gnutls_priority_deinit(priority_cache);
gnutls_global_deinit();
gnutls_initialised = 0;
@@ -620,7 +677,7 @@ NKSN_DestroyCertCredentials(void *credentials)
/* ================================================== */
NKSN_Instance
NKSN_CreateInstance(int server_mode, const char *name,
NKSN_CreateInstance(int server_mode, const char *server_name,
NKSN_MessageHandler handler, void *handler_arg)
{
NKSN_Instance inst;
@@ -628,7 +685,7 @@ NKSN_CreateInstance(int server_mode, const char *name,
inst = MallocNew(struct NKSN_Instance_Record);
inst->server = server_mode;
inst->name = Strdup(name);
inst->server_name = server_name ? Strdup(server_name) : NULL;
inst->handler = handler;
inst->handler_arg = handler_arg;
/* Replace NULL arg with the session itself */
@@ -637,8 +694,10 @@ NKSN_CreateInstance(int server_mode, const char *name,
inst->state = KE_STOPPED;
inst->sock_fd = INVALID_SOCK_FD;
inst->label = NULL;
inst->tls_session = NULL;
inst->timeout_id = 0;
inst->retry_factor = NKE_RETRY_FACTOR2_CONNECT;
return inst;
}
@@ -650,19 +709,19 @@ NKSN_DestroyInstance(NKSN_Instance inst)
{
stop_session(inst);
Free(inst->name);
Free(inst->server_name);
Free(inst);
}
/* ================================================== */
int
NKSN_StartSession(NKSN_Instance inst, int sock_fd, void *credentials, double timeout)
NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
void *credentials, double timeout)
{
assert(inst->state == KE_STOPPED);
inst->tls_session = create_tls_session(inst->server, sock_fd,
inst->server ? NULL : inst->name,
inst->tls_session = create_tls_session(inst->server, sock_fd, inst->server_name,
credentials, priority_cache);
if (!inst->tls_session)
return 0;
@@ -670,7 +729,9 @@ NKSN_StartSession(NKSN_Instance inst, int sock_fd, void *credentials, double tim
inst->sock_fd = sock_fd;
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, inst);
inst->label = Strdup(label);
inst->timeout_id = SCH_AddTimeoutByDelay(timeout, session_timeout, inst);
inst->retry_factor = NKE_RETRY_FACTOR2_CONNECT;
reset_message(&inst->message);
inst->new_message = 0;
@@ -777,3 +838,11 @@ NKSN_StopSession(NKSN_Instance inst)
{
stop_session(inst);
}
/* ================================================== */
int
NKSN_GetRetryFactor(NKSN_Instance inst)
{
return inst->retry_factor;
}

View File

@@ -45,15 +45,15 @@ extern void *NKSN_CreateCertCredentials(char *cert, char *key, char *trusted_cer
extern void NKSN_DestroyCertCredentials(void *credentials);
/* Create an instance */
extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *name,
extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *server_name,
NKSN_MessageHandler handler, void *handler_arg);
/* Destroy an instance */
extern void NKSN_DestroyInstance(NKSN_Instance inst);
/* Start a new NTS-KE session */
extern int NKSN_StartSession(NKSN_Instance inst, int sock_fd, void *credentials,
double timeout);
extern int NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
void *credentials, double timeout);
/* Begin an NTS-KE message. A request should be made right after starting
the session and response should be made in the message handler. */
@@ -80,4 +80,8 @@ extern int NKSN_IsStopped(NKSN_Instance inst);
/* Stop the session */
extern void NKSN_StopSession(NKSN_Instance inst);
/* Get a factor to calculate retry interval (in log2 seconds)
based on the session state or how it was terminated */
extern int NKSN_GetRetryFactor(NKSN_Instance inst);
#endif

View File

@@ -44,18 +44,22 @@
#include "util.h"
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
#define MIN_NKE_RETRY_INTERVAL 1000
#define DUMP_IDENTIFIER "NNC0\n"
struct NNC_Instance_Record {
const IPSockAddr *ntp_address;
IPSockAddr nts_address;
char *name;
SIV_Instance siv_c2s;
SIV_Instance siv_s2c;
NKC_Instance nke;
SIV_Instance siv;
double last_nke_attempt;
int load_attempt;
int nke_attempts;
double next_nke_attempt;
double last_nke_success;
NKE_Context context;
NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies;
int cookie_index;
@@ -67,11 +71,20 @@ struct NNC_Instance_Record {
/* ================================================== */
static void save_cookies(NNC_Instance inst);
static void load_cookies(NNC_Instance inst);
/* ================================================== */
static void
reset_instance(NNC_Instance inst)
{
inst->last_nke_attempt = -MIN_NKE_RETRY_INTERVAL;
inst->load_attempt = 0;
inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0;
inst->last_nke_success = 0.0;
memset(&inst->context, 0, sizeof (inst->context));
inst->num_cookies = 0;
inst->cookie_index = 0;
inst->nak_response = 0;
@@ -92,8 +105,7 @@ NNC_CreateInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *
inst->ntp_address = ntp_address;
inst->nts_address = *nts_address;
inst->name = name ? Strdup(name) : NULL;
inst->siv_c2s = NULL;
inst->siv_s2c = NULL;
inst->siv = NULL;
inst->nke = NULL;
reset_instance(inst);
@@ -106,12 +118,12 @@ NNC_CreateInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *
void
NNC_DestroyInstance(NNC_Instance inst)
{
save_cookies(inst);
if (inst->nke)
NKC_DestroyInstance(inst->nke);
if (inst->siv_c2s)
SIV_DestroyInstance(inst->siv_c2s);
if (inst->siv_s2c)
SIV_DestroyInstance(inst->siv_s2c);
if (inst->siv)
SIV_DestroyInstance(inst->siv);
Free(inst->name);
Free(inst);
@@ -120,7 +132,7 @@ NNC_DestroyInstance(NNC_Instance inst)
/* ================================================== */
static int
is_nke_needed(NNC_Instance inst)
check_cookies(NNC_Instance inst)
{
/* Force NKE if a NAK was received since last valid auth */
if (inst->nak_response && !inst->ok_response && inst->num_cookies > 0) {
@@ -133,7 +145,7 @@ is_nke_needed(NNC_Instance inst)
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())
inst->num_cookies = 0;
return inst->num_cookies == 0;
return inst->num_cookies > 0;
}
/* ================================================== */
@@ -167,22 +179,36 @@ set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
/* ================================================== */
static void
update_next_nke_attempt(NNC_Instance inst, double now)
{
int factor, interval;
if (!inst->nke)
return;
factor = NKC_GetRetryFactor(inst->nke);
interval = MIN(factor + inst->nke_attempts - 1, NKE_MAX_RETRY_INTERVAL2);
inst->next_nke_attempt = now + UTI_Log2ToDouble(interval);
}
/* ================================================== */
static int
get_nke_data(NNC_Instance inst)
get_cookies(NNC_Instance inst)
{
NTP_Remote_Address ntp_address;
SIV_Algorithm siv;
NKE_Key c2s, s2c;
double now;
int got_data;
assert(is_nke_needed(inst));
assert(!check_cookies(inst));
now = SCH_GetLastEventMonoTime();
if (!inst->nke) {
if (now - inst->last_nke_attempt < MIN_NKE_RETRY_INTERVAL) {
DEBUG_LOG("Limiting NTS-KE request rate");
if (now < inst->next_nke_attempt) {
DEBUG_LOG("Limiting NTS-KE request rate (%f seconds)",
inst->next_nke_attempt - now);
return 0;
}
@@ -194,16 +220,19 @@ get_nke_data(NNC_Instance inst)
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name);
inst->nke_attempts++;
update_next_nke_attempt(inst, now);
if (!NKC_Start(inst->nke))
return 0;
inst->last_nke_attempt = now;
}
update_next_nke_attempt(inst, now);
if (NKC_IsActive(inst->nke))
return 0;
got_data = NKC_GetNtsData(inst->nke, &siv, &c2s, &s2c,
got_data = NKC_GetNtsData(inst->nke, &inst->context,
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
&ntp_address);
@@ -213,6 +242,10 @@ get_nke_data(NNC_Instance inst)
if (!got_data)
return 0;
if (inst->siv)
SIV_DestroyInstance(inst->siv);
inst->siv = NULL;
if (!set_ntp_address(inst, &ntp_address)) {
inst->num_cookies = 0;
return 0;
@@ -220,22 +253,6 @@ get_nke_data(NNC_Instance inst)
inst->cookie_index = 0;
if (inst->siv_c2s)
SIV_DestroyInstance(inst->siv_c2s);
if (inst->siv_s2c)
SIV_DestroyInstance(inst->siv_s2c);
inst->siv_c2s = SIV_CreateInstance(siv);
inst->siv_s2c = SIV_CreateInstance(siv);
if (!inst->siv_c2s || !inst->siv_s2c ||
!SIV_SetKey(inst->siv_c2s, c2s.key, c2s.length) ||
!SIV_SetKey(inst->siv_s2c, s2c.key, s2c.length)) {
DEBUG_LOG("Could not initialise SIV");
inst->num_cookies = 0;
return 0;
}
inst->nak_response = 0;
inst->last_nke_success = now;
@@ -248,8 +265,22 @@ get_nke_data(NNC_Instance inst)
int
NNC_PrepareForAuth(NNC_Instance inst)
{
if (is_nke_needed(inst)) {
if (!get_nke_data(inst))
if (!inst->load_attempt) {
load_cookies(inst);
inst->load_attempt = 1;
}
if (!check_cookies(inst)) {
if (!get_cookies(inst))
return 0;
}
if (!inst->siv)
inst->siv = SIV_CreateInstance(inst->context.algorithm);
if (!inst->siv ||
!SIV_SetKey(inst->siv, inst->context.c2s.key, inst->context.c2s.length)) {
DEBUG_LOG("Could not set SIV key");
return 0;
}
@@ -267,8 +298,9 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
{
NKE_Cookie *cookie;
int i, req_cookies;
void *ef_body;
if (inst->num_cookies == 0 || !inst->siv_c2s)
if (inst->num_cookies == 0 || !inst->siv)
return 0;
if (info->mode != MODE_CLIENT)
@@ -287,12 +319,13 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
return 0;
for (i = 0; i < req_cookies - 1; i++) {
if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE_PLACEHOLDER,
cookie->cookie, cookie->length))
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_COOKIE_PLACEHOLDER,
cookie->length, &ef_body))
return 0;
memset(ef_body, 0, cookie->length);
}
if (!NNA_GenerateAuthEF(packet, info, inst->siv_c2s, inst->nonce, sizeof (inst->nonce),
if (!NNA_GenerateAuthEF(packet, info, inst->siv, inst->nonce, sizeof (inst->nonce),
(const unsigned char *)"", 0, NTP_MAX_V4_MAC_LENGTH + 4))
return 0;
@@ -362,8 +395,11 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
if (inst->ok_response)
return 0;
if (!inst->siv_s2c)
if (!inst->siv ||
!SIV_SetKey(inst->siv, inst->context.s2c.key, inst->context.s2c.length)) {
DEBUG_LOG("Could not set SIV key");
return 0;
}
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed,
@@ -388,7 +424,7 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
return 0;
}
if (!NNA_DecryptAuthEF(packet, info, inst->siv_s2c, parsed,
if (!NNA_DecryptAuthEF(packet, info, inst->siv, parsed,
plaintext, sizeof (plaintext), &plaintext_length))
return 0;
@@ -418,7 +454,8 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
/* At this point we know the client interoperates with the server. Allow a
new NTS-KE session to be started as soon as the cookies run out. */
inst->last_nke_attempt = -MIN_NKE_RETRY_INTERVAL;
inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0;
return 1;
}
@@ -428,6 +465,8 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
void
NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
{
save_cookies(inst);
if (inst->nke)
NKC_DestroyInstance(inst->nke);
@@ -439,3 +478,152 @@ NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
DEBUG_LOG("NTS reset");
}
/* ================================================== */
static void
save_cookies(NNC_Instance inst)
{
char buf[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename;
struct timespec now;
double context_time;
FILE *f;
int i;
if (inst->num_cookies < 1 || !UTI_IsIPReal(&inst->nts_address.ip_addr))
return;
dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir)
return;
filename = UTI_IPToString(&inst->nts_address.ip_addr);
f = UTI_OpenFile(dump_dir, filename, ".tmp", 'w', 0600);
if (!f)
return;
SCH_GetLastEventTime(&now, NULL, NULL);
context_time = inst->last_nke_success - SCH_GetLastEventMonoTime();
context_time += UTI_TimespecToDouble(&now);
if (fprintf(f, "%s%.1f\n%s %d\n%d ",
DUMP_IDENTIFIER, context_time, UTI_IPToString(&inst->ntp_address->ip_addr),
inst->ntp_address->port, (int)inst->context.algorithm) < 0 ||
!UTI_BytesToHex(inst->context.s2c.key, inst->context.s2c.length, buf, sizeof (buf)) ||
fprintf(f, "%s ", buf) < 0 ||
!UTI_BytesToHex(inst->context.c2s.key, inst->context.c2s.length, buf, sizeof (buf)) ||
fprintf(f, "%s\n", buf) < 0)
goto error;
for (i = 0; i < inst->num_cookies; i++) {
if (!UTI_BytesToHex(inst->cookies[i].cookie, inst->cookies[i].length, buf, sizeof (buf)) ||
fprintf(f, "%s\n", buf) < 0)
goto error;
}
fclose(f);
if (!UTI_RenameTempFile(dump_dir, filename, ".tmp", ".nts"))
;
return;
error:
DEBUG_LOG("Could not %s cookies for %s", "save", filename);
fclose(f);
if (!UTI_RemoveFile(dump_dir, filename, ".nts"))
;
}
/* ================================================== */
#define MAX_WORDS 3
static void
load_cookies(NNC_Instance inst)
{
char line[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename, *words[MAX_WORDS];
int i, algorithm, port;
double context_time;
struct timespec now;
IPSockAddr ntp_addr;
FILE *f;
dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir)
return;
filename = UTI_IPToString(&inst->nts_address.ip_addr);
f = UTI_OpenFile(dump_dir, filename, ".nts", 'r', 0);
if (!f)
return;
/* Don't load this file again */
if (!UTI_RemoveFile(dump_dir, filename, ".nts"))
;
if (inst->siv)
SIV_DestroyInstance(inst->siv);
inst->siv = NULL;
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
sscanf(words[0], "%lf", &context_time) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
!UTI_StringToIP(words[0], &ntp_addr.ip_addr) || sscanf(words[1], "%d", &port) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 3 ||
sscanf(words[0], "%d", &algorithm) != 1)
goto error;
inst->context.algorithm = algorithm;
inst->context.s2c.length = UTI_HexToBytes(words[1], inst->context.s2c.key,
sizeof (inst->context.s2c.key));
inst->context.c2s.length = UTI_HexToBytes(words[2], inst->context.c2s.key,
sizeof (inst->context.c2s.key));
if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
inst->context.c2s.length != inst->context.s2c.length)
goto error;
for (i = 0; i < NTS_MAX_COOKIES && fgets(line, sizeof (line), f); i++) {
if (UTI_SplitString(line, words, MAX_WORDS) != 1)
goto error;
inst->cookies[i].length = UTI_HexToBytes(words[0], inst->cookies[i].cookie,
sizeof (inst->cookies[i].cookie));
if (inst->cookies[i].length == 0)
goto error;
}
inst->num_cookies = i;
ntp_addr.port = port;
if (!set_ntp_address(inst, &ntp_addr))
goto error;
SCH_GetLastEventTime(&now, NULL, NULL);
context_time -= UTI_TimespecToDouble(&now);
if (context_time > 0)
context_time = 0;
inst->last_nke_success = context_time + SCH_GetLastEventMonoTime();
DEBUG_LOG("Loaded %d cookies for %s", i, filename);
return;
error:
DEBUG_LOG("Could not %s cookies for %s", "load", filename);
fclose(f);
memset(&inst->context, 0, sizeof (inst->context));
inst->num_cookies = 0;
}
/* ================================================== */
void
NNC_DumpData(NNC_Instance inst)
{
save_cookies(inst);
}

View File

@@ -43,4 +43,6 @@ extern int NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
extern void NNC_ChangeAddress(NNC_Instance inst, IPAddr *address);
extern void NNC_DumpData(NNC_Instance inst);
#endif

View File

@@ -41,6 +41,8 @@
#include "siv.h"
#include "util.h"
#define SERVER_SIV AEAD_AES_SIV_CMAC_256
struct NtsServer {
SIV_Instance siv;
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
@@ -64,7 +66,7 @@ NNS_Initialise(void)
}
server = Malloc(sizeof (struct NtsServer));
server->siv = SIV_CreateInstance(AEAD_AES_SIV_CMAC_256);
server->siv = SIV_CreateInstance(SERVER_SIV);
}
/* ================================================== */
@@ -88,8 +90,8 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
int ef_type, ef_body_length, ef_length, has_uniq_id = 0, has_auth = 0, has_cookie = 0;
int i, plaintext_length, parsed, requested_cookies, cookie_length = -1, auth_start = 0;
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
NKE_Context context;
NKE_Cookie cookie;
NKE_Key c2s, s2c;
void *ef_body;
if (!server)
@@ -144,12 +146,17 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
return 0;
}
if (!NKS_DecodeCookie(&cookie, &c2s, &s2c)) {
if (!NKS_DecodeCookie(&cookie, &context)) {
*kod = NTP_KOD_NTS_NAK;
return 0;
}
if (!SIV_SetKey(server->siv, c2s.key, c2s.length)) {
if (context.algorithm != SERVER_SIV) {
DEBUG_LOG("Unexpected SIV");
return 0;
}
if (!SIV_SetKey(server->siv, context.c2s.key, context.c2s.length)) {
DEBUG_LOG("Could not set C2S key");
return 0;
}
@@ -178,7 +185,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
}
}
if (!SIV_SetKey(server->siv, s2c.key, s2c.length)) {
if (!SIV_SetKey(server->siv, context.s2c.key, context.s2c.length)) {
DEBUG_LOG("Could not set S2C key");
return 0;
}
@@ -187,7 +194,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
server->num_cookies = MIN(NTS_MAX_COOKIES, requested_cookies);
for (i = 0; i < server->num_cookies; i++)
if (!NKS_GenerateCookie(&c2s, &s2c, &server->cookies[i]))
if (!NKS_GenerateCookie(&context, &server->cookies[i]))
return 0;
return 1;

View File

@@ -171,22 +171,22 @@ send_response(int fd, const PrvResponse *res)
static int
receive_from_daemon(int fd, PrvRequest *req)
{
SCK_Message message;
SCK_Message *message;
if (!SCK_ReceiveMessage(fd, &message, SCK_FLAG_MSG_DESCRIPTOR) ||
message.length != sizeof (*req))
message = SCK_ReceiveMessage(fd, SCK_FLAG_MSG_DESCRIPTOR);
if (!message || message->length != sizeof (*req))
return 0;
memcpy(req, message.data, sizeof (*req));
memcpy(req, message->data, sizeof (*req));
if (req->op == OP_BINDSOCKET) {
req->data.bind_socket.sock = message.descriptor;
req->data.bind_socket.sock = message->descriptor;
/* return error if valid descriptor not found */
if (req->data.bind_socket.sock < 0)
return 0;
} else if (message.descriptor >= 0) {
SCK_CloseSocket(message.descriptor);
} else if (message->descriptor >= 0) {
SCK_CloseSocket(message->descriptor);
return 0;
}
@@ -644,6 +644,8 @@ PRV_StartHelper(void)
LOG_FATAL("Helper already running");
sock_fd1 = SCK_OpenUnixSocketPair(SCK_FLAG_BLOCK, &sock_fd2);
if (sock_fd1 < 0)
LOG_FATAL("Could not open socket pair");
pid = fork();
if (pid < 0)

View File

@@ -112,6 +112,9 @@ static void update_drift_file(double, double);
/* Leap second handling mode */
static REF_LeapMode leap_mode;
/* Time of UTC midnight of the upcoming or previous leap second */
static time_t leap_when;
/* Flag indicating the clock was recently corrected for leap second and it may
not have correct time yet (missing 23:59:60 in the UTC time scale) */
static int leap_in_progress;
@@ -246,6 +249,7 @@ REF_Initialise(void)
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan, &local_distance);
UTI_ZeroTimespec(&local_ref_time);
leap_when = 0;
leap_timeout_id = 0;
leap_in_progress = 0;
leap_mode = CNF_GetLeapSecMode();
@@ -720,10 +724,12 @@ set_leap_timeout(time_t now)
if (!our_leap_sec)
return;
leap_when = (now / (24 * 3600) + 1) * (24 * 3600);
/* Insert leap second at 0:00:00 UTC, delete at 23:59:59 UTC. If the clock
will be corrected by the system, timeout slightly sooner to be sure it
will happen before the system correction. */
when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600);
when.tv_sec = leap_when;
when.tv_nsec = 0;
if (our_leap_sec < 0)
when.tv_sec--;
@@ -767,7 +773,7 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
}
if ((leap_sec != our_leap_sec || tai_offset != our_tai_offset)
&& !REF_IsLeapSecondClose()) {
&& !REF_IsLeapSecondClose(NULL, 0.0)) {
our_leap_sec = leap_sec;
our_tai_offset = tai_offset;
@@ -1324,22 +1330,24 @@ REF_DisableLocal(void)
#define LEAP_SECOND_CLOSE 5
int REF_IsLeapSecondClose(void)
static int
is_leap_close(time_t t)
{
return t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE;
}
/* ================================================== */
int REF_IsLeapSecondClose(struct timespec *ts, double offset)
{
struct timespec now, now_raw;
time_t t;
if (!our_leap_sec)
return 0;
SCH_GetLastEventTime(&now, NULL, &now_raw);
t = now.tv_sec > 0 ? now.tv_sec : -now.tv_sec;
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
if (is_leap_close(now.tv_sec) || is_leap_close(now_raw.tv_sec))
return 1;
t = now_raw.tv_sec > 0 ? now_raw.tv_sec : -now_raw.tv_sec;
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
if (ts && (is_leap_close(ts->tv_sec) || is_leap_close(ts->tv_sec + offset)))
return 1;
return 0;

View File

@@ -184,9 +184,9 @@ extern void REF_ModifyMakestep(int limit, double threshold);
extern void REF_EnableLocal(int stratum, double distance, int orphan);
extern void REF_DisableLocal(void);
/* Check if current raw or cooked time is close to a leap second
and is better to discard any measurements */
extern int REF_IsLeapSecondClose(void);
/* Check if either of the current raw and cooked time, and optionally a
provided timestamp with an offset, is close to a leap second */
extern int REF_IsLeapSecondClose(struct timespec *ts, double offset);
/* Return TAI-UTC offset corresponding to a time in UTC if available */
extern int REF_GetTaiOffset(struct timespec *ts);

View File

@@ -267,7 +267,7 @@ select_samples(SPF_Instance filter)
}
}
for (i = j = 0, k = -1; i < filter->used; i++) {
for (i = j = 0; i < filter->used; i++) {
if (selected[i] != -1)
selected[j++] = (selected[i] + filter->used - o) % filter->used;
}

View File

@@ -68,7 +68,7 @@ struct Message {
};
#ifdef HAVE_RECVMMSG
#define MAX_RECV_MESSAGES SCK_MAX_RECV_MESSAGES
#define MAX_RECV_MESSAGES 16
#define MessageHeader mmsghdr
#else
/* Compatible with mmsghdr */
@@ -85,9 +85,10 @@ static int initialised;
/* Flags supported by socket() */
static int supported_socket_flags;
/* Arrays of Message and MessageHeader */
/* Arrays of Message, MessageHeader, and SCK_Message */
static ARR_Instance recv_messages;
static ARR_Instance recv_headers;
static ARR_Instance recv_sck_messages;
static unsigned int received_messages;
@@ -342,6 +343,14 @@ bind_ip_address(int sock_fd, IPSockAddr *addr, int flags)
if (addr->port > 0 && !SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_REUSEADDR, 1))
;
#if defined(LINUX) && defined(SO_REUSEPORT)
/* Allow multiple instances to bind to the same port in order to enable load
balancing. Don't enable this option on non-Linux systems as it has
a slightly different meaning there (with some important implications). */
if (addr->port > 0 && !SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_REUSEPORT, 1))
;
#endif
#ifdef IP_FREEBIND
/* Allow binding to an address that doesn't exist yet */
if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_FREEBIND, 1))
@@ -604,7 +613,7 @@ log_message(int sock_fd, int direction, SCK_Message *message, const char *prefix
const char *local_addr, *remote_addr;
char if_index[20], tss[10], tsif[20], tslen[20];
if (DEBUG <= 0 || log_min_severity < LOGS_DEBUG)
if (DEBUG <= 0 || log_min_severity > LOGS_DEBUG)
return;
remote_addr = NULL;
@@ -859,22 +868,27 @@ process_header(struct msghdr *msg, unsigned int msg_length, int sock_fd, int fla
/* ================================================== */
static int
receive_messages(int sock_fd, SCK_Message *messages, int max_messages, int flags)
static SCK_Message *
receive_messages(int sock_fd, int flags, int max_messages, int *num_messages)
{
struct MessageHeader *hdr;
SCK_Message *messages;
unsigned int i, n;
int ret, recv_flags = 0;
assert(initialised);
*num_messages = 0;
if (max_messages < 1)
return 0;
return NULL;
/* Prepare used buffers for new messages */
prepare_buffers(received_messages);
received_messages = 0;
messages = ARR_GetElements(recv_sck_messages);
hdr = ARR_GetElements(recv_headers);
n = ARR_GetSize(recv_headers);
n = MIN(n, max_messages);
@@ -895,7 +909,7 @@ receive_messages(int sock_fd, SCK_Message *messages, int max_messages, int flags
if (ret < 0) {
handle_recv_error(sock_fd, flags);
return 0;
return NULL;
}
received_messages = n;
@@ -903,13 +917,15 @@ receive_messages(int sock_fd, SCK_Message *messages, int max_messages, int flags
for (i = 0; i < n; i++) {
hdr = ARR_GetElement(recv_headers, i);
if (!process_header(&hdr->msg_hdr, hdr->msg_len, sock_fd, flags, &messages[i]))
return 0;
return NULL;
log_message(sock_fd, 1, &messages[i],
flags & SCK_FLAG_MSG_ERRQUEUE ? "Received error" : "Received", NULL);
}
return n;
*num_messages = n;
return messages;
}
/* ================================================== */
@@ -1084,6 +1100,8 @@ SCK_Initialise(void)
ARR_SetSize(recv_messages, MAX_RECV_MESSAGES);
recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader));
ARR_SetSize(recv_headers, MAX_RECV_MESSAGES);
recv_sck_messages = ARR_CreateInstance(sizeof (SCK_Message));
ARR_SetSize(recv_sck_messages, MAX_RECV_MESSAGES);
received_messages = MAX_RECV_MESSAGES;
@@ -1107,6 +1125,7 @@ SCK_Initialise(void)
void
SCK_Finalise(void)
{
ARR_DestroyInstance(recv_sck_messages);
ARR_DestroyInstance(recv_headers);
ARR_DestroyInstance(recv_messages);
@@ -1373,18 +1392,20 @@ SCK_Send(int sock_fd, const void *buffer, unsigned int length, int flags)
/* ================================================== */
int
SCK_ReceiveMessage(int sock_fd, SCK_Message *message, int flags)
SCK_Message *
SCK_ReceiveMessage(int sock_fd, int flags)
{
return SCK_ReceiveMessages(sock_fd, message, 1, flags);
int num_messages;
return receive_messages(sock_fd, flags, 1, &num_messages);
}
/* ================================================== */
int
SCK_ReceiveMessages(int sock_fd, SCK_Message *messages, int max_messages, int flags)
SCK_Message *
SCK_ReceiveMessages(int sock_fd, int flags, int *num_messages)
{
return receive_messages(sock_fd, messages, max_messages, flags);
return receive_messages(sock_fd, flags, MAX_RECV_MESSAGES, num_messages);
}
/* ================================================== */

View File

@@ -41,9 +41,6 @@
#define SCK_FLAG_MSG_ERRQUEUE 1
#define SCK_FLAG_MSG_DESCRIPTOR 2
/* Maximum number of received messages */
#define SCK_MAX_RECV_MESSAGES 4
typedef enum {
SCK_ADDR_UNSPEC = 0,
SCK_ADDR_IP,
@@ -119,12 +116,11 @@ extern int SCK_ShutdownConnection(int sock_fd);
extern int SCK_Receive(int sock_fd, void *buffer, unsigned int length, int flags);
extern int SCK_Send(int sock_fd, const void *buffer, unsigned int length, int flags);
/* Receive a single message or multiple messages. The functions return the
number of received messages, or 0 on error. The returned data point to
static buffers, which are valid until another call of these functions. */
extern int SCK_ReceiveMessage(int sock_fd, SCK_Message *message, int flags);
extern int SCK_ReceiveMessages(int sock_fd, SCK_Message *messages, int max_messages,
int flags);
/* Receive a single message or multiple messages. The functions return
a pointer to static buffers, or NULL on error. The buffers are valid until
another call of the functions and can be reused for sending messages. */
extern SCK_Message *SCK_ReceiveMessage(int sock_fd, int flags);
extern SCK_Message *SCK_ReceiveMessages(int sock_fd, int flags, int *num_messages);
/* Initialise a new message (e.g. before sending) */
extern void SCK_InitMessage(SCK_Message *message, SCK_AddressType addr_type);

View File

@@ -361,7 +361,7 @@ get_leap_status(void)
void
SRC_SetLeapStatus(SRC_Instance inst, NTP_Leap leap)
{
if (REF_IsLeapSecondClose())
if (REF_IsLeapSecondClose(NULL, 0.0))
return;
inst->leap = leap;
@@ -390,7 +390,7 @@ SRC_AccumulateSample(SRC_Instance inst, NTP_Sample *sample)
source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset,
sample->root_delay, sample->root_dispersion, sample->stratum);
if (REF_IsLeapSecondClose()) {
if (REF_IsLeapSecondClose(&sample->time, sample->offset)) {
LOG(LOGS_INFO, "Dropping sample around leap second");
return;
}
@@ -483,7 +483,7 @@ SRC_ResetReachability(SRC_Instance inst)
/* ================================================== */
static void
log_selection_message(char *format, char *arg)
log_selection_message(const char *format, const char *arg)
{
if (REF_GetMode() != REF_ModeNormal)
return;
@@ -1197,7 +1197,6 @@ FILE *open_dumpfile(SRC_Instance inst, char mode)
dumpdir = CNF_GetDumpDir();
if (dumpdir[0] == '\0') {
LOG(LOGS_WARN, "dumpdir not specified");
return NULL;
}

20
stubs.c
View File

@@ -331,6 +331,11 @@ NSR_GetActivityReport(RPT_ActivityReport *report)
memset(report, 0, sizeof (*report));
}
void
NSR_DumpAuthData(void)
{
}
#ifndef FEAT_CMDMON
void
@@ -515,6 +520,11 @@ NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
{
}
void
NNC_DumpData(NNC_Instance inst)
{
}
void
NKC_Initialise(void)
{
@@ -535,4 +545,14 @@ NKS_Finalise(void)
{
}
void
NKS_DumpKeys(void)
{
}
void
NKS_ReloadKeys(void)
{
}
#endif /* !FEAT_NTS */

View File

@@ -523,6 +523,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context)
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
#endif
{ SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR },
#ifdef SO_REUSEPORT
{ SOL_SOCKET, SO_REUSEPORT },
#endif
{ SOL_SOCKET, SO_TIMESTAMP }, { SOL_SOCKET, SO_TIMESTAMPNS },
#ifdef HAVE_LINUX_TIMESTAMPING
{ SOL_SOCKET, SO_SELECT_ERR_QUEUE }, { SOL_SOCKET, SO_TIMESTAMPING },

View File

@@ -46,8 +46,9 @@
#include "privops.h"
#include "util.h"
#ifdef HAVE_MACOS_SYS_TIMEX
#include <dlfcn.h>
#ifdef HAVE_MACOS_SYS_TIMEX
#include "sys_netbsd.h"
static int have_ntp_adjtime = 0;

View File

@@ -29,7 +29,8 @@ server_conf="
ntsserverkey tmp/server.key
ntsservercert tmp/server.crt
ntsprocesses 0
ntsrotate 64
ntsrotate 66
ntsdumpdir tmp
"
client_server_options="minpoll 6 maxpoll 6 nts"
client_conf="
@@ -43,27 +44,57 @@ check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_file_messages "20.*123\.1.* 111 111 1111" 91 93 measurements.log || test_fail
check_file_messages "20.*123\.1.* 111 001 0000" 30 32 measurements.log || test_fail
check_file_messages "20.*123\.1.* 111 111 1111" 75 80 measurements.log || test_fail
check_file_messages "20.*123\.1.* 111 001 0000" 37 39 measurements.log || test_fail
check_file_messages " 2 1 .* 11443 " 260 300 log.packets || test_fail
check_file_messages "." 6 6 ntskeys || test_fail
rm -f tmp/measurements.log
client_conf+="
ntsrefresh 120"
ntsrefresh 120
ntsdumpdir tmp"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_file_messages "20.*123\.1.* 111 111 1111" 101 103 measurements.log || test_fail
check_file_messages "20.*123\.1.* 111 111 1111" 99 103 measurements.log || test_fail
check_file_messages "20.*123\.1.* 111 001 0000" 0 0 measurements.log || test_fail
check_file_messages " 2 1 .* 11443 " 350 390 log.packets || test_fail
check_file_messages "." 6 6 ntskeys || test_fail
check_file_messages "." 11 12 192.168.123.1.nts || test_fail
rm -f tmp/measurements.log
client_conf=""
export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010 + 40000 sec' +'%s')
server_conf+="
ntsrotate 100000"
client_conf+="
ntsrefresh 39500"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_file_messages "20.*123\.1.* 111 111 1111" 150 160 measurements.log || test_fail
check_file_messages "20.*123\.1.* 111 001 0000" 0 0 measurements.log || test_fail
check_file_messages " 2 1 .* 11443 " 6 10 log.packets || test_fail
check_file_messages "^9\.......e+03 2 1 .* 11443 " 6 10 log.packets || test_fail
check_file_messages "." 6 6 ntskeys || test_fail
check_file_messages "." 11 12 192.168.123.1.nts || test_fail
rm -f tmp/measurements.log
client_conf="
nosystemcert"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection && test_fail
check_sync && test_fail
check_file_messages " 2 1 .* 123 " 0 0 log.packets || test_fail
check_file_messages " 2 1 .* 11443 " 10 20 log.packets || test_fail
test_pass

View File

@@ -24,6 +24,7 @@
#ifdef FEAT_NTS
#include <nts_ke_client.c>
#include <local.h>
static void
prepare_response(NKSN_Instance session, int valid)
@@ -110,6 +111,7 @@ test_unit(void)
for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
CNF_ParseLine(NULL, i + 1, conf[i]);
LCL_Initialise();
NKC_Initialise();
SCK_GetLoopbackIPAddress(AF_INET, &addr.ip_addr);
@@ -128,6 +130,7 @@ test_unit(void)
NKC_DestroyInstance(inst);
NKC_Finalise();
LCL_Finalise();
CNF_Finalise();
}
#else

View File

@@ -132,13 +132,13 @@ void
test_unit(void)
{
NKSN_Instance session;
NKE_Context context, context2;
NKE_Cookie cookie;
NKE_Key c2s, s2c, c2s2, s2c2;
int i, valid, l;
uint32_t sum, sum2;
char conf[][100] = {
"ntscachedir .",
"ntsdumpdir .",
"ntsport 0",
"ntsprocesses 0",
"ntsserverkey nts_ke.key",
@@ -156,7 +156,7 @@ test_unit(void)
unlink("ntskeys");
NKS_Initialise(0);
session = NKSN_CreateInstance(1, "", handle_message, NULL);
session = NKSN_CreateInstance(1, NULL, handle_message, NULL);
for (i = 0; i < 10000; i++) {
valid = random() % 2;
@@ -167,14 +167,16 @@ test_unit(void)
for (i = 0; i < 10000; i++) {
get_keys(session, AEAD_AES_SIV_CMAC_256, &c2s, &s2c);
context.algorithm = AEAD_AES_SIV_CMAC_256;
get_keys(session, context.algorithm, &context.c2s, &context.s2c);
memset(&cookie, 0, sizeof (cookie));
TEST_CHECK(NKS_GenerateCookie(&c2s, &s2c, &cookie));
TEST_CHECK(NKS_DecodeCookie(&cookie, &c2s2, &s2c2));
TEST_CHECK(c2s.length == c2s2.length);
TEST_CHECK(s2c.length == s2c2.length);
TEST_CHECK(memcmp(c2s.key, c2s2.key, c2s.length) == 0);
TEST_CHECK(memcmp(s2c.key, s2c2.key, s2c.length) == 0);
TEST_CHECK(NKS_GenerateCookie(&context, &cookie));
TEST_CHECK(NKS_DecodeCookie(&cookie, &context2));
TEST_CHECK(context.algorithm == context2.algorithm);
TEST_CHECK(context.c2s.length == context2.c2s.length);
TEST_CHECK(context.s2c.length == context2.s2c.length);
TEST_CHECK(memcmp(context.c2s.key, context2.c2s.key, context.c2s.length) == 0);
TEST_CHECK(memcmp(context.s2c.key, context2.s2c.key, context.s2c.length) == 0);
if (random() % 4) {
cookie.cookie[random() % (cookie.length)]++;
@@ -185,7 +187,7 @@ test_unit(void)
while (l == cookie.length)
cookie.length = random() % (sizeof (cookie.cookie) + 1);
}
TEST_CHECK(!NKS_DecodeCookie(&cookie, &c2s2, &s2c2));
TEST_CHECK(!NKS_DecodeCookie(&cookie, &context2));
}
unlink("ntskeys");

View File

@@ -142,7 +142,7 @@ test_unit(void)
for (i = 0; i < 50; i++) {
SCH_Initialise();
server = NKSN_CreateInstance(1, "client", handle_request, NULL);
server = NKSN_CreateInstance(1, NULL, handle_request, NULL);
client = NKSN_CreateInstance(0, "test", handle_response, NULL);
server_cred = NKSN_CreateCertCredentials("nts_ke.crt", "nts_ke.key", NULL);
@@ -152,8 +152,8 @@ test_unit(void)
TEST_CHECK(fcntl(sock_fds[0], F_SETFL, O_NONBLOCK) == 0);
TEST_CHECK(fcntl(sock_fds[1], F_SETFL, O_NONBLOCK) == 0);
TEST_CHECK(NKSN_StartSession(server, sock_fds[0], server_cred, 4.0));
TEST_CHECK(NKSN_StartSession(client, sock_fds[1], client_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));
send_message(client);

View File

@@ -32,27 +32,29 @@
#define NKC_Start(inst) (random() % 2)
#define NKC_IsActive(inst) (random() % 2)
static int get_nts_data(NKC_Instance inst, SIV_Algorithm *siv_algorithm, NKE_Key *c2s, NKE_Key *s2c,
NKE_Cookie *cookies, int *num_cookies, int max_cookies, IPSockAddr *ntp_address);
static int get_nts_data(NKC_Instance inst, NKE_Context *context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address);
#define NKC_GetNtsData get_nts_data
#include <nts_ntp_client.c>
static int
get_nts_data(NKC_Instance inst, SIV_Algorithm *siv_algorithm, NKE_Key *c2s, NKE_Key *s2c,
NKE_Cookie *cookies, int *num_cookies, int max_cookies, IPSockAddr *ntp_address)
get_nts_data(NKC_Instance inst, NKE_Context *context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address)
{
int i;
if (random() % 2)
return 0;
*siv_algorithm = AEAD_AES_SIV_CMAC_256;
context->algorithm = AEAD_AES_SIV_CMAC_256;
c2s->length = SIV_GetKeyLength(*siv_algorithm);
UTI_GetRandomBytes(c2s->key, c2s->length);
s2c->length = SIV_GetKeyLength(*siv_algorithm);
UTI_GetRandomBytes(s2c->key, s2c->length);
context->c2s.length = SIV_GetKeyLength(context->algorithm);
UTI_GetRandomBytes(context->c2s.key, context->c2s.length);
context->s2c.length = SIV_GetKeyLength(context->algorithm);
UTI_GetRandomBytes(context->s2c.key, context->s2c.length);
*num_cookies = random() % max_cookies + 1;
for (i = 0; i < *num_cookies; i++) {
@@ -85,12 +87,11 @@ get_request(NNC_Instance inst)
TEST_CHECK(!NNC_GenerateRequestAuth(inst, &packet, &info));
while (!NNC_PrepareForAuth(inst)) {
inst->last_nke_attempt = random() % 100000 - 50000;
inst->next_nke_attempt = SCH_GetLastEventMonoTime() + random() % 10 - 7;
}
TEST_CHECK(inst->num_cookies > 0);
TEST_CHECK(inst->siv_c2s);
TEST_CHECK(inst->siv_s2c);
TEST_CHECK(inst->siv);
memcpy(nonce, inst->nonce, sizeof (nonce));
memcpy(uniq_id, inst->uniq_id, sizeof (uniq_id));
@@ -103,7 +104,7 @@ get_request(NNC_Instance inst)
(inst->cookies[inst->cookie_index].length + 4));
expected_length = info.length + 4 + sizeof (inst->uniq_id) +
req_cookies * (4 + inst->cookies[inst->cookie_index].length) +
4 + 4 + sizeof (inst->nonce) + SIV_GetTagLength(inst->siv_c2s);
4 + 4 + sizeof (inst->nonce) + SIV_GetTagLength(inst->siv);
DEBUG_LOG("length=%d cookie_length=%d expected_length=%d",
info.length, inst->cookies[inst->cookie_index].length, expected_length);
@@ -126,6 +127,7 @@ prepare_response(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info, in
unsigned char cookie[508], plaintext[512], nonce[512];
int nonce_length, cookie_length, plaintext_length, min_auth_length;
int index, auth_start;
SIV_Instance siv;
memset(packet, 0, sizeof (*packet));
packet->lvm = NTP_LVM(0, 4, MODE_SERVER);
@@ -175,10 +177,15 @@ prepare_response(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info, in
TEST_CHECK(NEF_SetField(plaintext, sizeof (plaintext), 0, NTP_EF_NTS_COOKIE,
cookie, cookie_length, &plaintext_length));
auth_start = info->length;
if (index != 4)
TEST_CHECK(NNA_GenerateAuthEF(packet, info, inst->siv_s2c,
if (index != 4) {
siv = SIV_CreateInstance(inst->context.algorithm);
TEST_CHECK(siv);
TEST_CHECK(SIV_SetKey(siv, inst->context.s2c.key, inst->context.s2c.length));
TEST_CHECK(NNA_GenerateAuthEF(packet, info, siv,
nonce, nonce_length, plaintext, plaintext_length,
min_auth_length));
SIV_DestroyInstance(siv);
}
if (index == 5)
((unsigned char *)packet)[auth_start + 8]++;
}

View File

@@ -32,17 +32,18 @@ static void
prepare_request(NTP_Packet *packet, NTP_PacketInfo *info, int valid, int nak)
{
unsigned char uniq_id[NTS_MIN_UNIQ_ID_LENGTH], nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
NKE_Key c2s, s2c;
SIV_Instance siv;
NKE_Context context;
NKE_Cookie cookie;
int i, index, cookie_start, auth_start;
c2s.length = SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256);
UTI_GetRandomBytes(&c2s.key, c2s.length);
s2c.length = SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256);
UTI_GetRandomBytes(&s2c.key, s2c.length);
context.algorithm = SERVER_SIV;
context.c2s.length = SIV_GetKeyLength(context.algorithm);
UTI_GetRandomBytes(&context.c2s.key, context.c2s.length);
context.s2c.length = SIV_GetKeyLength(context.algorithm);
UTI_GetRandomBytes(&context.s2c.key, context.s2c.length);
TEST_CHECK(NKS_GenerateCookie(&c2s, &s2c, &cookie));
TEST_CHECK(NKS_GenerateCookie(&context, &cookie));
UTI_GetRandomBytes(uniq_id, sizeof (uniq_id));
UTI_GetRandomBytes(nonce, sizeof (nonce));
@@ -78,8 +79,8 @@ prepare_request(NTP_Packet *packet, NTP_PacketInfo *info, int valid, int nak)
auth_start = info->length;
if (index != 2) {
siv = SIV_CreateInstance(AEAD_AES_SIV_CMAC_256);
TEST_CHECK(SIV_SetKey(siv, c2s.key, c2s.length));
siv = SIV_CreateInstance(context.algorithm);
TEST_CHECK(SIV_SetKey(siv, context.c2s.key, context.c2s.length));
TEST_CHECK(NNA_GenerateAuthEF(packet, info, siv, nonce, sizeof (nonce),
(const unsigned char *)"", 0, 0));
SIV_DestroyInstance(siv);

View File

@@ -30,7 +30,7 @@ void test_unit(void) {
Timespec tspec;
Float f;
int i, j, c;
char buf[16], *s;
char buf[16], *s, *words[3];
uid_t uid;
gid_t gid;
struct stat st;
@@ -333,4 +333,37 @@ void test_unit(void) {
TEST_CHECK(UTI_BytesToHex("\xAB\x12\x34\x56\x78\x00\x01", 7, buf, 14) == 0);
TEST_CHECK(UTI_BytesToHex("\xAB\x12\x34\x56\x78\x00\x01", 7, buf, 15) == 1);
TEST_CHECK(strcmp(buf, "AB123456780001") == 0);
TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "") < sizeof (buf));
TEST_CHECK(UTI_SplitString(buf, words, 3) == 0);
TEST_CHECK(!words[0]);
TEST_CHECK(snprintf(buf, sizeof (buf), "%s", " ") < sizeof (buf));
TEST_CHECK(UTI_SplitString(buf, words, 3) == 0);
TEST_CHECK(!words[0]);
TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "a \n ") < sizeof (buf));
TEST_CHECK(UTI_SplitString(buf, words, 3) == 1);
TEST_CHECK(words[0] == buf + 0);
TEST_CHECK(strcmp(words[0], "a") == 0);
TEST_CHECK(snprintf(buf, sizeof (buf), "%s", " a ") < sizeof (buf));
TEST_CHECK(UTI_SplitString(buf, words, 3) == 1);
TEST_CHECK(words[0] == buf + 2);
TEST_CHECK(strcmp(words[0], "a") == 0);
TEST_CHECK(snprintf(buf, sizeof (buf), "%s", " \n a") < sizeof (buf));
TEST_CHECK(UTI_SplitString(buf, words, 3) == 1);
TEST_CHECK(words[0] == buf + 4);
TEST_CHECK(strcmp(words[0], "a") == 0);
TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "a b") < sizeof (buf));
TEST_CHECK(UTI_SplitString(buf, words, 1) == 2);
TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "a b") < sizeof (buf));
TEST_CHECK(UTI_SplitString(buf, words, 2) == 2);
TEST_CHECK(words[0] == buf + 0);
TEST_CHECK(words[1] == buf + 4);
TEST_CHECK(strcmp(words[0], "a") == 0);
TEST_CHECK(strcmp(words[1], "b") == 0);
TEST_CHECK(snprintf(buf, sizeof (buf), "%s", " a b ") < sizeof (buf));
TEST_CHECK(UTI_SplitString(buf, words, 3) == 2);
TEST_CHECK(words[0] == buf + 1);
TEST_CHECK(words[1] == buf + 3);
TEST_CHECK(strcmp(words[0], "a") == 0);
TEST_CHECK(strcmp(words[1], "b") == 0);
}

96
util.c
View File

@@ -56,7 +56,7 @@ UTI_IsZeroTimespec(struct timespec *ts)
/* ================================================== */
void
UTI_TimevalToTimespec(struct timeval *tv, struct timespec *ts)
UTI_TimevalToTimespec(const struct timeval *tv, struct timespec *ts)
{
ts->tv_sec = tv->tv_sec;
ts->tv_nsec = 1000 * tv->tv_usec;
@@ -65,7 +65,7 @@ UTI_TimevalToTimespec(struct timeval *tv, struct timespec *ts)
/* ================================================== */
void
UTI_TimespecToTimeval(struct timespec *ts, struct timeval *tv)
UTI_TimespecToTimeval(const struct timespec *ts, struct timeval *tv)
{
tv->tv_sec = ts->tv_sec;
tv->tv_usec = ts->tv_nsec / 1000;
@@ -74,7 +74,7 @@ UTI_TimespecToTimeval(struct timespec *ts, struct timeval *tv)
/* ================================================== */
double
UTI_TimespecToDouble(struct timespec *ts)
UTI_TimespecToDouble(const struct timespec *ts)
{
return ts->tv_sec + 1.0e-9 * ts->tv_nsec;
}
@@ -109,7 +109,7 @@ UTI_NormaliseTimespec(struct timespec *ts)
/* ================================================== */
double
UTI_TimevalToDouble(struct timeval *tv)
UTI_TimevalToDouble(const struct timeval *tv)
{
return tv->tv_sec + 1.0e-6 * tv->tv_usec;
}
@@ -149,7 +149,7 @@ UTI_NormaliseTimeval(struct timeval *x)
/* ================================================== */
int
UTI_CompareTimespecs(struct timespec *a, struct timespec *b)
UTI_CompareTimespecs(const struct timespec *a, const struct timespec *b)
{
if (a->tv_sec < b->tv_sec)
return -1;
@@ -165,7 +165,7 @@ UTI_CompareTimespecs(struct timespec *a, struct timespec *b)
/* ================================================== */
void
UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *b)
UTI_DiffTimespecs(struct timespec *result, const struct timespec *a, const struct timespec *b)
{
result->tv_sec = a->tv_sec - b->tv_sec;
result->tv_nsec = a->tv_nsec - b->tv_nsec;
@@ -176,7 +176,7 @@ UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *
/* Calculate result = a - b and return as a double */
double
UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b)
UTI_DiffTimespecsToDouble(const struct timespec *a, const struct timespec *b)
{
return ((double)a->tv_sec - (double)b->tv_sec) + 1.0e-9 * (a->tv_nsec - b->tv_nsec);
}
@@ -184,7 +184,7 @@ UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b)
/* ================================================== */
void
UTI_AddDoubleToTimespec(struct timespec *start, double increment, struct timespec *end)
UTI_AddDoubleToTimespec(const struct timespec *start, double increment, struct timespec *end)
{
time_t int_part;
@@ -198,7 +198,7 @@ UTI_AddDoubleToTimespec(struct timespec *start, double increment, struct timespe
/* Calculate the average and difference (as a double) of two timespecs */
void
UTI_AverageDiffTimespecs(struct timespec *earlier, struct timespec *later,
UTI_AverageDiffTimespecs(const struct timespec *earlier, const struct timespec *later,
struct timespec *average, double *diff)
{
*diff = UTI_DiffTimespecsToDouble(later, earlier);
@@ -208,8 +208,8 @@ UTI_AverageDiffTimespecs(struct timespec *earlier, struct timespec *later,
/* ================================================== */
void
UTI_AddDiffToTimespec(struct timespec *a, struct timespec *b,
struct timespec *c, struct timespec *result)
UTI_AddDiffToTimespec(const struct timespec *a, const struct timespec *b,
const struct timespec *c, struct timespec *result)
{
double diff;
@@ -230,7 +230,7 @@ static int pool_ptr = 0;
/* Convert a timespec into a temporary string, largely for diagnostic display */
char *
UTI_TimespecToString(struct timespec *ts)
UTI_TimespecToString(const struct timespec *ts)
{
char *result;
@@ -250,7 +250,7 @@ UTI_TimespecToString(struct timespec *ts)
for diagnostic display */
char *
UTI_Ntp64ToString(NTP_int64 *ntp_ts)
UTI_Ntp64ToString(const NTP_int64 *ntp_ts)
{
struct timespec ts;
UTI_Ntp64ToTimespec(ntp_ts, &ts);
@@ -281,10 +281,10 @@ UTI_RefidToString(uint32_t ref_id)
/* ================================================== */
char *
UTI_IPToString(IPAddr *addr)
UTI_IPToString(const IPAddr *addr)
{
unsigned long a, b, c, d, ip;
uint8_t *ip6;
const uint8_t *ip6;
char *result;
result = NEXT_BUFFER;
@@ -375,7 +375,7 @@ UTI_StringToIdIP(const char *addr, IPAddr *ip)
/* ================================================== */
int
UTI_IsIPReal(IPAddr *ip)
UTI_IsIPReal(const IPAddr *ip)
{
switch (ip->family) {
case IPADDR_INET4:
@@ -389,7 +389,7 @@ UTI_IsIPReal(IPAddr *ip)
/* ================================================== */
uint32_t
UTI_IPToRefid(IPAddr *ip)
UTI_IPToRefid(const IPAddr *ip)
{
static int MD5_hash = -1;
unsigned char buf[16];
@@ -414,10 +414,10 @@ UTI_IPToRefid(IPAddr *ip)
/* ================================================== */
uint32_t
UTI_IPToHash(IPAddr *ip)
UTI_IPToHash(const IPAddr *ip)
{
static uint32_t seed = 0;
unsigned char *addr;
const unsigned char *addr;
unsigned int i, len;
uint32_t hash;
@@ -452,7 +452,7 @@ UTI_IPToHash(IPAddr *ip)
/* ================================================== */
void
UTI_IPHostToNetwork(IPAddr *src, IPAddr *dest)
UTI_IPHostToNetwork(const IPAddr *src, IPAddr *dest)
{
/* Don't send uninitialized bytes over network */
memset(dest, 0, sizeof (IPAddr));
@@ -477,7 +477,7 @@ UTI_IPHostToNetwork(IPAddr *src, IPAddr *dest)
/* ================================================== */
void
UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest)
UTI_IPNetworkToHost(const IPAddr *src, IPAddr *dest)
{
dest->family = ntohs(src->family);
dest->_pad = 0;
@@ -500,7 +500,7 @@ UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest)
/* ================================================== */
int
UTI_CompareIPs(IPAddr *a, IPAddr *b, IPAddr *mask)
UTI_CompareIPs(const IPAddr *a, const IPAddr *b, const IPAddr *mask)
{
int i, d;
@@ -536,7 +536,7 @@ UTI_CompareIPs(IPAddr *a, IPAddr *b, IPAddr *mask)
/* ================================================== */
char *
UTI_IPSockAddrToString(IPSockAddr *sa)
UTI_IPSockAddrToString(const IPSockAddr *sa)
{
char *result;
@@ -571,7 +571,8 @@ UTI_TimeToLogForm(time_t t)
/* ================================================== */
void
UTI_AdjustTimespec(struct timespec *old_ts, struct timespec *when, struct timespec *new_ts, double *delta_time, double dfreq, double doffset)
UTI_AdjustTimespec(const struct timespec *old_ts, const struct timespec *when,
struct timespec *new_ts, double *delta_time, double dfreq, double doffset)
{
double elapsed;
@@ -644,7 +645,7 @@ UTI_ZeroNtp64(NTP_int64 *ts)
/* ================================================== */
int
UTI_IsZeroNtp64(NTP_int64 *ts)
UTI_IsZeroNtp64(const NTP_int64 *ts)
{
return !ts->hi && !ts->lo;
}
@@ -652,7 +653,7 @@ UTI_IsZeroNtp64(NTP_int64 *ts)
/* ================================================== */
int
UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b)
UTI_CompareNtp64(const NTP_int64 *a, const NTP_int64 *b)
{
int32_t diff;
@@ -672,7 +673,8 @@ UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b)
/* ================================================== */
int
UTI_IsEqualAnyNtp64(NTP_int64 *a, NTP_int64 *b1, NTP_int64 *b2, NTP_int64 *b3)
UTI_IsEqualAnyNtp64(const NTP_int64 *a, const NTP_int64 *b1, const NTP_int64 *b2,
const NTP_int64 *b3)
{
if (b1 && a->lo == b1->lo && a->hi == b1->hi)
return 1;
@@ -694,7 +696,7 @@ UTI_IsEqualAnyNtp64(NTP_int64 *a, NTP_int64 *b1, NTP_int64 *b2, NTP_int64 *b3)
#define NSEC_PER_NTP64 4.294967296
void
UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz)
UTI_TimespecToNtp64(const struct timespec *src, NTP_int64 *dest, const NTP_int64 *fuzz)
{
uint32_t hi, lo, sec, nsec;
@@ -723,7 +725,7 @@ UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz)
/* ================================================== */
void
UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest)
UTI_Ntp64ToTimespec(const NTP_int64 *src, struct timespec *dest)
{
uint32_t ntp_sec, ntp_frac;
@@ -755,7 +757,7 @@ UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest)
#define MIN_ENDOFTIME_DISTANCE (365 * 24 * 3600)
int
UTI_IsTimeOffsetSane(struct timespec *ts, double offset)
UTI_IsTimeOffsetSane(const struct timespec *ts, double offset)
{
double t;
@@ -801,7 +803,7 @@ UTI_Log2ToDouble(int l)
/* ================================================== */
void
UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest)
UTI_TimespecNetworkToHost(const Timespec *src, struct timespec *dest)
{
uint32_t sec_low, nsec;
#ifdef HAVE_LONG_TIME_T
@@ -826,7 +828,7 @@ UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest)
/* ================================================== */
void
UTI_TimespecHostToNetwork(struct timespec *src, Timespec *dest)
UTI_TimespecHostToNetwork(const struct timespec *src, Timespec *dest)
{
dest->tv_nsec = htonl(src->tv_nsec);
#ifdef HAVE_LONG_TIME_T
@@ -1400,3 +1402,33 @@ UTI_HexToBytes(const char *hex, void *buf, unsigned int len)
return *hex == '\0' ? i : 0;
}
/* ================================================== */
int
UTI_SplitString(char *string, char **words, int max_saved_words)
{
char *s = string;
int i;
for (i = 0; i < max_saved_words; i++)
words[i] = NULL;
for (i = 0; ; i++) {
/* Zero white-space characters before the word */
while (*s != '\0' && isspace((unsigned char)*s))
*s++ = '\0';
if (*s == '\0')
break;
if (i < max_saved_words)
words[i] = s;
/* Find the next word */
while (*s != '\0' && !isspace((unsigned char)*s))
s++;
}
return i;
}

71
util.h
View File

@@ -41,13 +41,13 @@ extern void UTI_ZeroTimespec(struct timespec *ts);
extern int UTI_IsZeroTimespec(struct timespec *ts);
/* Convert a timeval into a timespec */
extern void UTI_TimevalToTimespec(struct timeval *tv, struct timespec *ts);
extern void UTI_TimevalToTimespec(const struct timeval *tv, struct timespec *ts);
/* Convert a timespec into a timeval */
extern void UTI_TimespecToTimeval(struct timespec *ts, struct timeval *tv);
extern void UTI_TimespecToTimeval(const struct timespec *ts, struct timeval *tv);
/* Convert a timespec into a floating point number of seconds */
extern double UTI_TimespecToDouble(struct timespec *ts);
extern double UTI_TimespecToDouble(const struct timespec *ts);
/* Convert a number of seconds expressed in floating point into a
timespec */
@@ -58,7 +58,7 @@ extern void UTI_DoubleToTimespec(double d, struct timespec *ts);
extern void UTI_NormaliseTimespec(struct timespec *ts);
/* Convert a timeval into a floating point number of seconds */
extern double UTI_TimevalToDouble(struct timeval *tv);
extern double UTI_TimevalToDouble(const struct timeval *tv);
/* Convert a number of seconds expressed in floating point into a
timeval */
@@ -70,54 +70,60 @@ extern void UTI_NormaliseTimeval(struct timeval *x);
/* Returns -1 if a comes earlier than b, 0 if a is the same time as b,
and +1 if a comes after b */
extern int UTI_CompareTimespecs(struct timespec *a, struct timespec *b);
extern int UTI_CompareTimespecs(const struct timespec *a, const struct timespec *b);
/* Calculate result = a - b */
extern void UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *b);
extern void UTI_DiffTimespecs(struct timespec *result,
const struct timespec *a, const struct timespec *b);
/* Calculate result = a - b and return as a double */
extern double UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b);
extern double UTI_DiffTimespecsToDouble(const struct timespec *a, const struct timespec *b);
/* Add a double increment to a timespec to get a new one. 'start' is
the starting time, 'end' is the result that we return. This is
safe to use if start and end are the same */
extern void UTI_AddDoubleToTimespec(struct timespec *start, double increment, struct timespec *end);
extern void UTI_AddDoubleToTimespec(const struct timespec *start, double increment,
struct timespec *end);
/* Calculate the average and difference (as a double) of two timespecs */
extern void UTI_AverageDiffTimespecs(struct timespec *earlier, struct timespec *later, struct timespec *average, double *diff);
extern void UTI_AverageDiffTimespecs(const struct timespec *earlier, const struct timespec *later,
struct timespec *average, double *diff);
/* Calculate result = a - b + c */
extern void UTI_AddDiffToTimespec(struct timespec *a, struct timespec *b, struct timespec *c, struct timespec *result);
extern void UTI_AddDiffToTimespec(const struct timespec *a, const struct timespec *b,
const struct timespec *c, struct timespec *result);
/* Convert a timespec into a temporary string, largely for diagnostic
display */
extern char *UTI_TimespecToString(struct timespec *ts);
extern char *UTI_TimespecToString(const struct timespec *ts);
/* Convert an NTP timestamp into a temporary string, largely for
diagnostic display */
extern char *UTI_Ntp64ToString(NTP_int64 *ts);
extern char *UTI_Ntp64ToString(const NTP_int64 *ts);
/* Convert ref_id into a temporary string, for diagnostics */
extern char *UTI_RefidToString(uint32_t ref_id);
/* Convert an IP address to string, for diagnostics */
extern char *UTI_IPToString(IPAddr *ip);
extern char *UTI_IPToString(const IPAddr *ip);
extern int UTI_StringToIP(const char *addr, IPAddr *ip);
extern int UTI_StringToIdIP(const char *addr, IPAddr *ip);
extern int UTI_IsIPReal(IPAddr *ip);
extern uint32_t UTI_IPToRefid(IPAddr *ip);
extern uint32_t UTI_IPToHash(IPAddr *ip);
extern void UTI_IPHostToNetwork(IPAddr *src, IPAddr *dest);
extern void UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest);
extern int UTI_CompareIPs(IPAddr *a, IPAddr *b, IPAddr *mask);
extern int UTI_IsIPReal(const IPAddr *ip);
extern uint32_t UTI_IPToRefid(const IPAddr *ip);
extern uint32_t UTI_IPToHash(const IPAddr *ip);
extern void UTI_IPHostToNetwork(const IPAddr *src, IPAddr *dest);
extern void UTI_IPNetworkToHost(const IPAddr *src, IPAddr *dest);
extern int UTI_CompareIPs(const IPAddr *a, const IPAddr *b, const IPAddr *mask);
extern char *UTI_IPSockAddrToString(IPSockAddr *sa);
extern char *UTI_IPSockAddrToString(const IPSockAddr *sa);
extern char *UTI_TimeToLogForm(time_t t);
/* Adjust time following a frequency/offset change */
extern void UTI_AdjustTimespec(struct timespec *old_ts, struct timespec *when, struct timespec *new_ts, double *delta_time, double dfreq, double doffset);
extern void UTI_AdjustTimespec(const struct timespec *old_ts, const struct timespec *when,
struct timespec *new_ts, double *delta_time,
double dfreq, double doffset);
/* Get zero NTP timestamp with random bits below precision */
extern void UTI_GetNtp64Fuzz(NTP_int64 *ts, int precision);
@@ -129,30 +135,32 @@ extern NTP_int32 UTI_DoubleToNtp32(double x);
extern void UTI_ZeroNtp64(NTP_int64 *ts);
/* Check if an NTP timestamp is zero */
extern int UTI_IsZeroNtp64(NTP_int64 *ts);
extern int UTI_IsZeroNtp64(const NTP_int64 *ts);
/* Compare two NTP timestamps. Returns -1 if a is before b, 0 if a is equal to
b, and 1 if a is after b. */
extern int UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b);
extern int UTI_CompareNtp64(const NTP_int64 *a, const NTP_int64 *b);
/* Compare an NTP timestamp with up to three other timestamps. Returns 0
if a is not equal to any of b1, b2, and b3, 1 otherwise. */
extern int UTI_IsEqualAnyNtp64(NTP_int64 *a, NTP_int64 *b1, NTP_int64 *b2, NTP_int64 *b3);
extern int UTI_IsEqualAnyNtp64(const NTP_int64 *a, const NTP_int64 *b1,
const NTP_int64 *b2, const NTP_int64 *b3);
/* Convert a timespec into an NTP timestamp */
extern void UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz);
extern void UTI_TimespecToNtp64(const struct timespec *src, NTP_int64 *dest,
const NTP_int64 *fuzz);
/* Convert an NTP timestamp into a timespec */
extern void UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest);
extern void UTI_Ntp64ToTimespec(const NTP_int64 *src, struct timespec *dest);
/* Check if time + offset is sane */
extern int UTI_IsTimeOffsetSane(struct timespec *ts, double offset);
extern int UTI_IsTimeOffsetSane(const struct timespec *ts, double offset);
/* Get 2 raised to power of a signed integer */
extern double UTI_Log2ToDouble(int l);
extern void UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest);
extern void UTI_TimespecHostToNetwork(struct timespec *src, Timespec *dest);
extern void UTI_TimespecNetworkToHost(const Timespec *src, struct timespec *dest);
extern void UTI_TimespecHostToNetwork(const struct timespec *src, Timespec *dest);
extern double UTI_FloatNetworkToHost(Float x);
extern Float UTI_FloatHostToNetwork(double x);
@@ -212,6 +220,11 @@ extern int UTI_BytesToHex(const void *buf, unsigned int buf_len, char *hex, unsi
is supported. */
extern unsigned int UTI_HexToBytes(const char *hex, void *buf, unsigned int len);
/* Split a string into words separated by whitespace characters. It returns
the number of words found in the string, but saves only up to the specified
number of pointers to the words. */
extern int UTI_SplitString(char *string, char **words, int max_saved_words);
/* Macros to get maximum and minimum of two values */
#ifdef MAX
#undef MAX