Compare commits

..

183 Commits

Author SHA1 Message Date
Miroslav Lichvar
0639205617 doc: update NEWS 2020-09-16 12:09:52 +02:00
Miroslav Lichvar
3916c3366b update copyright years 2020-09-16 12:09:52 +02:00
Miroslav Lichvar
f0a33e7b28 client: drop support for GNU readline
GNU readline switched to GPLv3+ in version 6.0, which is incompatible
with the chrony's GPLv2 license.

Drop support for the readline library. Only editline is supported now.
2020-09-16 12:09:52 +02:00
Miroslav Lichvar
c9b8f8bc70 doc: update and improve FAQ 2020-09-16 12:09:50 +02:00
Miroslav Lichvar
983b0723f6 doc: improve chronyd man page 2020-09-16 12:07:19 +02:00
Miroslav Lichvar
02c38934ea main: add option to disable check for root
The -U option can be used to start chronyd under a non-root user if it
is provided with all capabilities and access to files, directories, and
devices, needed to operate correctly in the specified configuration. It
is not recommended in cases where the configuration is unknown.
2020-09-16 11:39:16 +02:00
Miroslav Lichvar
c28c2cde43 sys_linux: don't keep NET_BIND_SERVICE for unprivileged port
Don't keep the NET_BIND_SERVICE capability if the configured NTP port is
not privileged (i.e. not smaller than 1024).
2020-09-16 11:15:29 +02:00
Miroslav Lichvar
349323dec7 sys_linux: don't keep NET_RAW on new kernels
It seems the NET_RAW capability is no longer needed to bind a socket to
a device since Linux 5.7.
2020-09-16 11:15:29 +02:00
Miroslav Lichvar
ddfaf2e542 ntp: log error when SIOCSHWTSTAMP fails with EPERM
Increase the severity of the log message to "error" when
the SIOCSHWTSTAMP ioctl fails due missing the NET_ADMIN capability.
2020-09-16 11:15:29 +02:00
Miroslav Lichvar
3177474ae8 configure: require TLS1.3 support in gnutls
Before enabling NTS support, explicitly check for TLS1.3 support in
gnutls, which is required by NTS.
2020-09-16 11:15:29 +02:00
Miroslav Lichvar
cc535632d1 test: add ntp_auth unit test 2020-09-16 11:15:29 +02:00
Miroslav Lichvar
cb8ee57b9e test: fix ntp_core unit test
Fix setting of key_id in the response.

Fixes: f6625717cd ("test: improve ntp_core unit test")
2020-09-16 11:15:16 +02:00
Miroslav Lichvar
c0b19b3fea doc: improve chrony.conf man page 2020-09-10 15:04:27 +02:00
Miroslav Lichvar
8235da6885 doc: improve chronyc man page 2020-09-10 14:16:48 +02:00
Miroslav Lichvar
f6625717cd test: improve ntp_core unit test 2020-09-10 13:32:39 +02:00
Miroslav Lichvar
fdfcabd79b ntp: drop support for long NTPv4 MACs
Don't accept NTPv4 packets which have a MAC longer than 24 octets to
strictly follow RFC 7822, which specifies the maximum length of a MAC
and the minimum length of the last extension field to avoid an ambiguity
in parsing of the packet.

This removes an ugly hack that was needed to accept packets that
contained one or more extension fields without a MAC, before RFC 7822
was written and NTP implementations started using truncated MACs.

The long MACs were used by chrony in versions 2.x when configured to
authenticate a server or peer with a key using a 256-bit or longer hash
(e.g. SHA256). For compatibility with chrony >= 4.0, these clients/peers
will need to have "version 3" added to the server/peer line in
chrony.conf.
2020-09-10 13:31:57 +02:00
Miroslav Lichvar
2bb88b45c6 siv: return error if key is not set
Avoid encryption or decryption using uninitialized data, or causing a
crash, if a key was not set for the SIV instance.
2020-09-10 09:36:35 +02:00
Miroslav Lichvar
9820c22c1d nts: improve NTP client code
Reset the client instance more thoroughly and make sure the
nonce cannot be reused.
2020-09-10 09:36:35 +02:00
Miroslav Lichvar
bcd7bad467 client: improve help message for sources command 2020-09-10 09:36:35 +02:00
Miroslav Lichvar
83ea9fe284 cmdmon: rename status constants
Change the naming of reported selection status in the sources report to
better match the internal status.
2020-09-10 09:36:35 +02:00
Miroslav Lichvar
c74d6e458d sources: don't report untrusted sources as selectable
Show untrusted sources with the '?' symbol instead of '-' to make them
consistent with not selectable and selectable sources in the selectdata
description.
2020-09-10 09:36:35 +02:00
Miroslav Lichvar
ff466439fc configure: fix building with -NTP -CMDMON +SCFILTER
Don't enable privileged operations using the nameserv code unless
NTP is enabled.
2020-09-10 09:36:35 +02:00
Miroslav Lichvar
0fcdf4389b nts: log early client NTS-KE socket errors
Log an error message when SCK_OpenTcpSocket() fails in the NTS-KE
client, e.g. when connect() fails due to the port not being allowed in
the SELinux policy.
2020-09-10 09:36:35 +02:00
Miroslav Lichvar
9cb9021c87 cmdmon: remove unused test code 2020-09-09 14:14:54 +02:00
Miroslav Lichvar
9c36236742 cmdmon: check response length before sending
Before sending a cmdmon response, make sure it is not longer than the
request to avoid amplification in case the response/padding length is
incorrectly specified for a request.
2020-09-09 14:14:54 +02:00
Vincent Blut
adebb027be sys_linux: allow readlinkat in seccomp filter 2020-09-01 14:29:43 +02:00
Miroslav Lichvar
7d3798d7cd examples: improve chrony-wait service
Use the systemd TimeoutStartSec setting to report a timeout instead of
an error and reduce the timeout to 3 minutes.
2020-09-01 12:05:06 +02:00
Miroslav Lichvar
b7c7c293e5 conf: add clockprecision directive
Make the precision of the system clock configurable. This can be useful
on servers using hardware timestamping to reduce the amount of noise
added to the NTP timestamps and improve stability of NTP measurements.
2020-09-01 11:21:46 +02:00
Miroslav Lichvar
9ca250755f sys_linux: allow lstat and readlink in seccomp filter
These syscalls seem to be needed when gnutls is loading system trusted
certificates due to p11-kit >= 0.23.21 getting the program name from
/proc/self/exe.
2020-09-01 09:42:31 +02:00
Bryan Christianson
bd3b36865e test: extend frequency in ntp_adjtime() test
Extend the frequency range in the test to cover negative frequencies.
2020-08-31 10:17:21 +02:00
Bryan Christianson
538e1c5eb1 sys_timex: add workaround for broken ntp_adjtime() on macOS
On macOS 11.0 (Big Sur) beta, ntp_adjtime() incorrectly returns
timex.freq as an unsigned number. This patch is a workaround for the bug
and should be removed when Apple fix the problem (assuming they will).
2020-08-31 10:16:51 +02:00
Miroslav Lichvar
009f1a5ae8 doc: update NEWS 2020-08-25 11:49:44 +02:00
Miroslav Lichvar
4f1418abf9 doc: update README 2020-08-25 11:49:44 +02:00
Miroslav Lichvar
79b348f075 util: don't open symlink when appending to file
When opening a file for appending (i.e. a log file), use the O_NOFOLLOW
flag to get an error if the path is a symlink. Opening log files through
symlinks is no longer supported.

This is a protection against symlink attacks if chronyd is misconfigured
to write a log in a world-writable directory (e.g. /tmp). That is not
meant to become a recommended practice. Log messages will be lost, or
chronyd won't start, if a symlink exists at the location of the log
file.
2020-08-25 11:49:44 +02:00
Miroslav Lichvar
9d88c028e2 test: fix cookie length in nts_ke_client unit test 2020-08-25 11:49:44 +02:00
Miroslav Lichvar
51172b3510 nts: avoid key corruption on failed loading
Don't save a loaded key to the server key slot until it is fully
decoded.
2020-08-20 16:19:13 +02:00
Miroslav Lichvar
892636036a nts: explicitly disable session tickets
Session tickets should never be enabled with the currect code on both
clients and servers. Set the GNUTLS_NO_TICKETS flag when opening a TLS
session in case this understanding is wrong, or it changes in future, to
reduce the TLS attack surface.
2020-08-20 16:19:13 +02:00
Miroslav Lichvar
4cf6b29397 test: fix 102-hwtimestamp test for new ethtool
New ethtool using netlink messages has a different output.
2020-08-20 16:19:13 +02:00
Miroslav Lichvar
571359b366 test: extend 110-chronyc test 2020-08-20 13:27:53 +02:00
Miroslav Lichvar
0f009e7718 test: extend 007-cmdmon system test 2020-08-20 13:27:53 +02:00
Miroslav Lichvar
24effd7340 test: add 105-nts system test 2020-08-20 13:27:53 +02:00
Miroslav Lichvar
5289fc5f80 test: add 009-binddevice system test 2020-08-20 13:27:53 +02:00
Miroslav Lichvar
ca49304bd6 test: add 008-confload system test 2020-08-20 13:27:53 +02:00
Miroslav Lichvar
b7fbac617d conf: rename confdirs and sourcedirs directives
Rename the directives to confdir and sourcedir to better match an
expected use case with only one specified directory.
2020-08-20 13:27:46 +02:00
Miroslav Lichvar
839e9aa4af reference: fix assignment of frequency_sd
Fixes: 8afd62d954 ("reference: update synchronization status more frequently")
2020-08-19 09:39:26 +02:00
Miroslav Lichvar
c5ac15ad33 client: improve parsing of keygen arguments
Detect invalid syntax for the keygen command.
2020-08-19 09:39:26 +02:00
Miroslav Lichvar
598cd10c34 client: ignore case in add command
For consistency with chronyd configuration, make the source type in the
add command case insensitive.
2020-08-19 09:39:18 +02:00
Miroslav Lichvar
1885729024 client: drop unnecessary parsing of IPv4 address 2020-08-18 14:22:55 +02:00
Miroslav Lichvar
2127f63961 cmdmon: change name fields to unsigned type 2020-08-17 16:28:36 +02:00
Miroslav Lichvar
97a8b1e43b test: fix random failures in nts_ntp_client unit test
Fixes: 18d9243eb9 ("test: improve NTS unit tests")
2020-08-17 16:28:36 +02:00
Miroslav Lichvar
aeee1feda6 test: improve siv unit test 2020-08-13 16:37:38 +02:00
Miroslav Lichvar
18d9243eb9 test: improve NTS unit tests 2020-08-13 16:37:38 +02:00
Miroslav Lichvar
1aa4827b3b test: extend 139-nts test 2020-08-13 16:37:38 +02:00
Miroslav Lichvar
ed1077a788 nts: check all encrypted fields before saving cookies
Don't save any cookies if an encrypted extension field fails parsing.
2020-08-13 16:37:20 +02:00
Miroslav Lichvar
356c475a6a cmdmon: fix data field name in handle_ntp_source_name()
Fixes: 93f6358916 ("cmdmon: add request to get source name")
2020-08-13 10:40:18 +02:00
Miroslav Lichvar
9ac582fa35 socket: improve code
Add more assertions and other checks, and improve coding style a bit.
2020-08-13 10:40:18 +02:00
Miroslav Lichvar
8c75f44603 ntp: fix comments
Fix typos and remove an obsolete comment.
2020-08-13 10:40:18 +02:00
Miroslav Lichvar
0a63ad95ce ntp: reuse pool IDs for new pools
When adding a new pool, reuse unused pool IDs to avoid increasing the
pools array.
2020-08-13 10:39:37 +02:00
Miroslav Lichvar
d274fe44da ntp: rename pool fields to pool_id
Rename the pool fields holding the ID of the pool to avoid confusion
with the pool record and pool flag.
2020-08-10 12:27:33 +02:00
Miroslav Lichvar
6d1cb58d8f examples: add leapsecmode to chrony.conf examples 2020-08-06 11:34:32 +02:00
Miroslav Lichvar
784122d44f client: add missing option to help message 2020-08-04 13:04:04 +02:00
Miroslav Lichvar
32fb8d41ca test: fix compiler warning in ntp unit test 2020-08-04 12:24:51 +02:00
Miroslav Lichvar
4993c35e11 util: fix compiler warning
Replace the snprintf() call with memcpy() in UTI_PathToDir() to make it
clear a truncated string is expected.
2020-08-04 12:24:51 +02:00
Miroslav Lichvar
6a5665ca58 conf: add dscp directive
The directive sets the DSCP value in transmitted NTP packets, which can
be useful in local networks where switches/routers are configured to
prioritise packets with specific DSCP values.
2020-08-04 12:24:49 +02:00
Miroslav Lichvar
e5cf006378 sources: reset leap voting flag earlier in selection
Remove the leap vote from sources that get the noselect option, or
have too large distance or jitter.
2020-08-04 12:19:52 +02:00
Miroslav Lichvar
0e51552d2d ntp: improve auth code
Before generating a MAC, make sure there is enough space in the packet.
This is always true with the current code, but it may change when a
non-NTS extension field is supported.

Update the packet auth info after generating a MAC in case it's needed
before the transmission.

Add more assertions and make other changes for better readability.
2020-08-04 12:19:41 +02:00
Miroslav Lichvar
cc007ad93b test: improve nts_ntp_client unit test 2020-07-28 12:48:23 +02:00
Miroslav Lichvar
3096926547 nts: disable TLS 1.2 on server
It seems gnutls (at least in version 3.6.14) allows clients to connect
using TLS1.2 when it has a DTLS version enabled in the priority cache.

Disable all DTLS versions in order to disable TLS1.2.
2020-07-28 12:48:23 +02:00
Miroslav Lichvar
d48f012809 nts: improve NTS-NTP server/client code
Add more comments, assertions, debug messages, and other minor
changes to make the code more robust.
2020-07-28 12:48:23 +02:00
Miroslav Lichvar
def137bc80 nts: scale server listening backlog with number of helpers 2020-07-28 12:48:23 +02:00
Miroslav Lichvar
3e0272e55f nts: fix destroying of NTS-KE client
Destroy the NTS-KE session of the client immediately even when the
resolver of the NTP address is running. This removes the session
local change handler and avoids an assertion failure in the local
finalization.
2020-07-28 12:48:23 +02:00
Miroslav Lichvar
be503bbcf6 nts: move loading of syscall filter in NTS-KE server
Load the filter after NKS_Initialise() to avoid hitting
a fcntl syscall.

Fixes: 66e097e3e6 ("nts: improve NTS-KE server/client code")
2020-07-28 12:48:20 +02:00
Miroslav Lichvar
72bf3d26eb nts: fix error response to NTS-KE request
When the request has an unrecognized critical record before the
NEXT_PROTOCOL and AEAD_ALGORITHM records, respond with error 0
(unrecognized critical record) instead of 1 (bad request).

When the request has multiple NEXT_PROTOCOL or AEAD_ALGORITHM records,
respond with error 1 (bad request).
2020-07-23 15:53:24 +02:00
Miroslav Lichvar
cc20ead3dc nts: reset NAK indicator with new request
Don't restart NTS-KE if a spoofed NAK response was received and no valid
response is received for a subsequent request.
2020-07-20 16:52:46 +02:00
Miroslav Lichvar
fd8fbcd090 nts: don't allow malformed encrypted extension fields
Require data decrypted from the NTS authenticator field to contain
correctly formatted extension fields (known or unknown).
2020-07-20 16:52:42 +02:00
Miroslav Lichvar
77bd0f83fe main: remove unneeded code in signal handler
The handler is set up when the main code is already initialized.
2020-07-16 16:02:16 +02:00
Miroslav Lichvar
32a82a38fd siv: add more assertions
Make sure the returned tag and key lengths are sane.
2020-07-16 16:02:16 +02:00
Miroslav Lichvar
66e097e3e6 nts: improve NTS-KE server/client code
Add more assertions and comments, refactor initialization of the helper,
and make other changes to make the code more robust.
2020-07-16 16:02:08 +02:00
Miroslav Lichvar
51d77d6cfc logging: extend functionality
Add a function to get the current minimum severity and a function to set
a global prefix for debug messages in order to identify messages from
helpers.
2020-07-16 13:24:59 +02:00
Miroslav Lichvar
2bb0769516 conf: improve error message
Replace "command" with "directive" for consistency with the
documentation.
2020-07-16 12:07:43 +02:00
Miroslav Lichvar
58da0c0ad2 conf: adopt default bind*address values
Move the default values of the bind*address settings from the
ntp/nts/cmdmon code to conf.
2020-07-16 12:07:43 +02:00
Miroslav Lichvar
c10b66b579 nts: follow bind*device settings for NTS-KE sockets
Bind the server and client NTS-KE sockets to the specified device.
2020-07-16 12:07:35 +02:00
Miroslav Lichvar
55a90c3735 nts: deinit gnutls when setting of credentials fails
This is needed to cleanly exit when the server key/cert couldn't be
loaded.
2020-07-16 12:06:27 +02:00
Miroslav Lichvar
962afb9e7d nts: disable input when sending data in NTS-KE session
Ignore read events when sending data to avoid spinning with blocked
output.
2020-07-16 12:03:43 +02:00
Miroslav Lichvar
7abd982f87 doc: fix formatting with new asciidoctor
With newer asciidoctor versions a blank character seems to be required
in an empty description used to set the indentation level in a nested
list.

https://github.com/asciidoctor/asciidoctor/issues/2766
2020-07-16 12:02:29 +02:00
Miroslav Lichvar
c099aac79c socket: fix debug message for unsupported binding
Fixes: 4ef944b734 ("socket: add support for binding sockets to device")
Reported-by: Bryan Christianson <bryan@whatroute.net>
2020-07-10 09:04:20 +02:00
Miroslav Lichvar
828e6ce30f doc: mention automatic creation of directories 2020-07-09 14:47:33 +02:00
Miroslav Lichvar
dc08cbfe59 conf: create ntsdumpdir directory
Create the directory specified by the ntsdumpdir directive if it doesn't
exist, similarly to logdir and dumpdir.
2020-07-09 14:47:33 +02:00
Miroslav Lichvar
3bdcce6903 conf: restrict permissions of created directories
If logdir or dumpdir doesn't exist, create the directory with no
permissions for other users (mode 0750 instead of 0755).
2020-07-09 14:47:33 +02:00
Miroslav Lichvar
d93aa10bac cmac+hash: change parameter types
For consistency and safety, change the CMC and HSH functions to accept
signed lengths and handle negative values as errors. Also, change the
input data type to void * to not require casting in the caller.
2020-07-09 14:47:33 +02:00
Miroslav Lichvar
de4ecc72d1 nts: don't assume field position in NNA_DecryptAuthEF()
Modify NNA_DecryptAuthEF() to not assume that the authenticator is the
last extension field in the packet as some extension fields specified in
future may need to be placed after the authenticator. The caller of the
function is supposed to verify the position.
2020-07-09 14:47:33 +02:00
Miroslav Lichvar
db54bfc0c1 nts: check for negative length in NNA_DecryptAuthEF()
As other functions that accept a signed length, make sure it is sane in
NNA_DecryptAuthEF() too.
2020-07-09 14:47:33 +02:00
Miroslav Lichvar
72ee80debe nts: fix comment about message handler 2020-07-09 14:47:33 +02:00
Miroslav Lichvar
a3436c26f0 nts: improve session code
Add more comments and assertions, replace getsockopt() call with
SCK_GetIntOption(), replace strncmp() with memcmp(), move a return
statement for clarity, and remove an unused field from the instance
record.
2020-07-09 14:47:30 +02:00
Miroslav Lichvar
b0f5024d56 nts: log details about failed certificate verification 2020-07-09 14:46:57 +02:00
Miroslav Lichvar
eae4b2abe5 ntp: drop precompensation of TX timestamp
The daemon transmit timestamps are precompensated for the time it takes
to generate a MAC using a symmetric key (as measured on chronyd start)
and also an average round-trip time of the Samba signing of MS-SNTP
responses. This improves accuracy of the transmit timestamp, but it
has some issues.

The correction has a random error which is changing over time due to
variable CPU frequency, system load, migration to a different machine,
etc. If the measured delay is too large, the correction may cause the
transmit timestamp to be later than the actual transmission. Also, the
delay is measured for a packet of a minimal length with no extension
fields, and there is no support for NTS.

Drop the precompensation in favor of the interleaved mode, which now
avoids the authentication delay even when no kernel/hardware timestamps
are available.
2020-07-09 14:46:57 +02:00
Miroslav Lichvar
ff03b813b0 ntp: get TX timestamp after authentication
If the daemon transmit timestamp is saved for processing of a future
response or responding in the interleaved mode, get a more accurate
timestamp right before calling NIO_SendPacket(). Avoid unnecessary
reading of the clock for the transmit timestamp in the packet (i.e.
in interleaved modes and client basic mode).

This should improve accuracy and stability when authentication is
enabled in the client and symmetric basic modes and also interleaved
modes if kernel/hardware timestamps are not available.
2020-07-09 14:46:53 +02:00
Miroslav Lichvar
4e747da4b4 ntp+cmdmon: fix responding to link-local addresses
After commit e49aececce ("socket: don't set interface for sent
packets") the NTP and cmdmon server stopped responding to requests from
link-local addresses.

Set the interface specifically for packets sent to a link-local address.
2020-07-01 16:19:44 +02:00
Miroslav Lichvar
99e3c67a81 socket: add support for selecting interface again
Revert commit e49aececce ("socket: don't set interface for sent
packets") to allow the interface to be selected for outgoing packets,
but don't set it in the callers yet.
2020-07-01 16:19:44 +02:00
Miroslav Lichvar
c4a2550518 conf: add directives to specify interfaces for binding sockets
Add binddevice, bindacqdevice, and bindcmddevice directive to specify
the interface for binding the NTP server, NTP client, and command socket
respectively.
2020-07-01 16:19:44 +02:00
Miroslav Lichvar
4ef944b734 socket: add support for binding sockets to device
As a Linux-specific feature, allow sockets to be bound to a device using
the SO_BINDTODEVICE socket option. The CAP_NET_RAW capability is
required for setting the option.
2020-07-01 16:19:44 +02:00
Robert Fairley
0f04baeb97 examples: align onoffline with DHCP NM dispatcher
Similar to the DHCP dispatcher, add a variable for the chronyc
executable path, which can be overwritten more easily by
downstream packages if needed.

Also give an `.onoffline` suffix to more clearly differentiate
this script from `chrony.nm-dispatcher.dhcp`.
2020-06-29 17:43:49 +02:00
Robert Fairley
bf7f63eaed examples: add dispatcher for NTP servers from DHCP
Add new NM dispatcher script for NTP servers given by DHCP through
NetworkManager in a similar way to how distributions have done in
11-dhclient, e.g. [1]. New NTP servers are written as entries to a
file per-interface in /var/run/chrony-dhcp, which is re-read by
chronyd upon executing `chronyc reload sources`.

This provides a way for NTP server configuration to be carried over
from NetworkManager DHCP events to chrony, for DHCP clients other
than dhclient. Part of fixing integration where the NetworkManager
internal client is used, e.g [2].

Paths to the chronyc executable and sources directory are set in
variables, which may be overwritten by downstream packages, but
should work for distributions for the most part.

[1] https://src.fedoraproject.org/rpms/dhcp/blob/master/f/11-dhclient
[2] https://bugzilla.redhat.com/show_bug.cgi?id=1800901
2020-06-29 17:43:49 +02:00
Miroslav Lichvar
59cf4e0b96 nameserv: don't return scoped IPv6 addresses
Ignore IPv6 addresses returned by getaddrinfo() that have a non-zero
scope ID to avoid silently ignoring the ID if it was specified with the
% sign in the provided string.

This can be removed when the scope ID is returned from the function and
the callers handle it.
2020-06-29 17:43:35 +02:00
Miroslav Lichvar
3fc72c0cfa ntp: fix comment about find_slot() 2020-06-29 16:46:05 +02:00
Miroslav Lichvar
ad69f4f32b configure: link with libnssutil3 for NSS hash support
With recent NSS versions, the NSS low hash initialization seems to fail
unless the executable is linked with the libnssutil3 library.
2020-06-25 12:43:25 +02:00
Miroslav Lichvar
81c2b2e886 socket: handle negative sa_length
As the type of the sa_length parameter is signed, negative values
should be handled as invalid.
2020-06-25 12:43:25 +02:00
Miroslav Lichvar
c9f03fb222 logging: handle too many file logs
Don't rely on an assert to catch insufficient maximum number of file
logs (e.g. after introducing a new file log).
2020-06-25 12:43:25 +02:00
Miroslav Lichvar
b0fe443632 ntp: rework initial burst
Instead of making the initial burst only once and immediately after
chronyd start (even when iburst is specified together with the offline
option), trigger the burst whenever the connectivity changes from
offline to online.
2020-06-25 12:42:57 +02:00
Miroslav Lichvar
8882fb21e0 example: update chrony.conf examples
Add some new directives, remove dumponexit (it's a no-op), remove
broadcast (to not encourage its use), fix a typo, and remove a
OS-specific limitation.
2020-06-25 12:39:15 +02:00
Miroslav Lichvar
7d551d34a0 test: update cmdmon and chronyc tests with new commands 2020-06-17 15:59:29 +02:00
Miroslav Lichvar
feef0dd983 ntp: reduce poll adjustment with specific failed tests
Reduce the poll increment for measurements that are rejected due to a
failed maxdelay* test in order to better track the source.
2020-06-17 15:59:29 +02:00
Miroslav Lichvar
d29f7b7c70 nts: warn about missing NTS support
Log a warning message if an NTP source is specified with the nts option
and the request fails due to missing NTS support.
2020-06-17 15:59:29 +02:00
Miroslav Lichvar
e3cd248f0d nts: update NTS-KE port number
The port assigned by IANA for NTS-KE is 4460.
2020-06-17 15:59:18 +02:00
Miroslav Lichvar
27e20a568b socket: enable only specified IP families
Allow an IP family to be specified in the socket initialization in order
to globally disable the other family. This replaces the ntp_io and
cmdmon code handling the -4/-6 options and fixes a case where the NTP
client could still use a disabled family if the source was specified
with an IP address.
2020-06-17 15:24:55 +02:00
Miroslav Lichvar
80316de3b8 socket: don't log errors on removing socket
Call unlink() directly to avoid an error log message when a Unix domain
socket cannot be removed (e.g. SOCK refclock created for gpsd in
/var/run).
2020-06-17 15:24:55 +02:00
Miroslav Lichvar
f9e2a5852d cmdmon: avoid unsigned shorts
Change unsigned shorts to uint16_t or longer types to avoid any
assumptions about length of the short type.
2020-06-17 15:24:55 +02:00
Miroslav Lichvar
500c9cbf3b ntp: combine parameters of NCR_AddBroadcastDestination() 2020-06-17 15:24:25 +02:00
Miroslav Lichvar
46714fec2d conf: fix missing format string
Fixes: 519796de37 ("conf: add sourcedirs directive")
2020-06-10 15:55:32 +02:00
Miroslav Lichvar
e1d9a57bd0 conf: reset global pointers after parsing line
Don't leave dangling pointers in CNF_ParseLine().
2020-06-10 15:36:40 +02:00
Miroslav Lichvar
1b82604f61 main: add option to print configuration
Add -p option to chronyd to print lines from the configuration as they
are parsed and exit. It can be used to verify the syntax and get the
whole configuration when it is split into multiple files.
2020-06-10 14:10:59 +02:00
Miroslav Lichvar
d69ac07183 cmdmon: add reload sources command
Add the command which reloads the files from the directories specified
by the sourcedirs directive.
2020-06-10 13:56:43 +02:00
Miroslav Lichvar
519796de37 conf: add sourcedirs directive
Add a new directive to include configuration files that only specify NTP
sources and which will be possible to reload with a chronyc command.
2020-06-10 13:56:43 +02:00
Miroslav Lichvar
ea4811b3b3 conf: detect truncated lines
If the buffer filled by fgets() is full, indicating it might not contain
the whole line, abort with a fatal message.
2020-06-10 13:56:43 +02:00
Miroslav Lichvar
951f14ae06 ntp: add configuration ID to sources
Provide an ID for each configured NTP source to enable tracking and
removing of its corresponding sources, even after they change their
address.
2020-06-10 13:56:43 +02:00
Miroslav Lichvar
428f9e4228 test: disable object dependencies in main makefile
When the main makefile is used to get the list of chronyd objects in
order to build the unit tests, clang started (with the -MM option) to
generate the dependency files prints error messages about wrong
inclusions. Set a NODEPS variable to completely disable the generation
of the files.
2020-06-08 15:27:57 +02:00
Miroslav Lichvar
ea425bf01e client: add tab-completition for authdata command 2020-06-04 14:50:17 +02:00
Miroslav Lichvar
8567a0e466 client: add verbose text to authdata command 2020-06-04 14:50:17 +02:00
Miroslav Lichvar
f6bf12bdcd test: extend siv unit test 2020-06-04 14:50:17 +02:00
Miroslav Lichvar
e8968ea429 siv: add gnutls support
Add support for the AES-SIV-CMAC cipher in gnutls using the AEAD
interface. It should be available in gnutls-3.6.14.

This will enable NTS support on systems that have a pre-3.6 version of
Nettle, without falling back to the internal SIV implementation.
2020-06-04 14:50:17 +02:00
Miroslav Lichvar
cf10ce1b68 nts: allow missing SIV support
When compiled with NTS support, don't require a SIV cipher to be always
supported (e.g. due to a different version of a library used for
building). Handle this case with a fatal message instead of crash.
Also, check the support early in the client unit test to prevent a hang.
2020-06-04 14:50:17 +02:00
Miroslav Lichvar
15dc83420d test: fix sources unit test
Use different source addresses, fix a debug message and a memory leak.
2020-06-04 14:50:17 +02:00
Miroslav Lichvar
37dbc211cd sources: add more assertions 2020-06-04 14:50:17 +02:00
Miroslav Lichvar
ed78cda6ad sources: check for negative distance
This is not expected to happen, but make sure the endpoints of each
source are in the right order (i.e. the distance is not negative) to
prevent getting a negative depth in the selection.
2020-06-04 14:50:17 +02:00
Miroslav Lichvar
faff931a76 sources: require majority for trusted sources
Handle trusted sources as a separate set of sources which is required to
have a majority for the selection to proceed. This should improve the
selection with multiple trusted sources (e.g. due to the auth selection
mode).
2020-06-04 14:50:17 +02:00
Miroslav Lichvar
1e68671690 sources: relax selection of non-trusted sources
When the selection has some trusted sources, don't require non-trusted
sources to be contained in the best interval as that can usually pass
only one source if the best interval is the interval of the source, or
no source at all if the best interval is an intersection of multiple
sources.

Relax the requirement for non-trusted sources to be contained in the
best interval of trusted sources alone instead of all sources in the
trusted interval.
2020-06-04 14:50:17 +02:00
Miroslav Lichvar
8eb167fd21 sources: extend mark debug message 2020-06-04 14:50:17 +02:00
Miroslav Lichvar
bc46174e98 sources: include hostname in selection log message
When selecting an NTP source, include the hostname in the log message.
2020-06-04 14:50:17 +02:00
Miroslav Lichvar
b86c89460a cmdmon: update protocol changelog 2020-06-04 14:50:17 +02:00
Miroslav Lichvar
03541f3626 cmdmon: add selectdata command
Add a command to report selection-specific data.
2020-06-04 14:40:18 +02:00
Miroslav Lichvar
39a462496a cmdmon: don't report selection options in source report
The selection options returned as flags are not reported by the
client and will be better reported in a separate command with other
selection-specific data.
2020-06-02 08:53:56 +02:00
Miroslav Lichvar
7ba8994838 client: fix help message to indicate mask is optional 2020-05-25 17:58:53 +02:00
Miroslav Lichvar
8da025da99 test: add 140-noclientlog test 2020-05-21 16:19:59 +02:00
Miroslav Lichvar
5dc7242703 clientlog: fix check for ratelimit and noclientlog
Fixes: 3a2d33d5a3 ("clientlog: refactor client record and API")
2020-05-21 16:07:52 +02:00
Miroslav Lichvar
11bffa0d55 doc: improve answer for chronyc error in FAQ 2020-05-21 12:42:20 +02:00
Miroslav Lichvar
5f6f265f80 local: don't remove handlers in finalization
Require all handlers to be deregistered by their users before the local
finalization.
2020-05-21 12:42:18 +02:00
Miroslav Lichvar
bf92314dc4 test: check logs for assertion failures 2020-05-21 12:42:18 +02:00
Miroslav Lichvar
a3fda9f992 nts: free client cert credentials when not used
Destroy the client cert credentials when destroying the last NKC
instance instead of NKC_Finalise(). This allows the client to reload the
trusted cert file between NTS-KE sessions.
2020-05-21 12:42:18 +02:00
Miroslav Lichvar
cd34b377aa nts: add debug messages for gnutls init/deinit 2020-05-21 12:42:18 +02:00
Miroslav Lichvar
145423068b ntp: change NSR_RemoveSource() to accept IP address only
Change the function to accept IP address alone to make it clear that the
port is ignored.
2020-05-21 12:42:18 +02:00
Miroslav Lichvar
fb4c3f31c0 ntp: refactor slot finding
Change the find_slot() function to not match port and return the found
status directly. Add a separate function for matching both address and
port.
2020-05-21 12:42:18 +02:00
Miroslav Lichvar
60049f1551 conf: replace empty strings with NULL
Avoid mixing empty strings with NULLs in configuration strings to make
the handling of default or disabled values consistent.
2020-05-21 12:42:18 +02:00
Miroslav Lichvar
e555548dda reference: fix offset sign in log message
In the maxchange check, log the original offset instead of the absolute
value.
2020-05-21 12:20:11 +02:00
Miroslav Lichvar
eedf61b3a2 clientlog: add debug message for maximum number of records 2020-05-21 12:20:11 +02:00
Miroslav Lichvar
ab54f76a38 cmdmon: report new client and server statistics
Report the new clientlog data in the clients and serverstats reports.

Add -k option to the clients command to select between command and
NTS-KE data.
2020-05-21 12:20:08 +02:00
Miroslav Lichvar
f8df4789b1 clientlog: count authenticated NTP requests 2020-05-21 12:01:39 +02:00
Miroslav Lichvar
6366ebc17e clientlog: add NTS-KE service
Instead of sharing the NTP rate limiting with NTS-KE, specify a new
service for NTS-KE and use it in the NTS-KE server.

Add ntsratelimit directive for configuration.
2020-05-21 12:01:39 +02:00
Miroslav Lichvar
3a2d33d5a3 clientlog: refactor client record and API
Refactor the client record and clientlog API to reuse more code between
different services and enumerate the services instead of hardcoding NTP
and cmdmon.
2020-05-21 12:01:37 +02:00
Miroslav Lichvar
1afd5b23d7 clientlog: fix time_t variables
The last_hit and oldest_hit timestamps are uint32_t, not time_t.
2020-05-21 11:50:04 +02:00
Miroslav Lichvar
17fb9e3709 stubs: add NSR_GetAuthReport()
Fixes: 79c7384e5e ("cmdmon: add authdata command")
2020-05-21 11:50:04 +02:00
Vincent Blut
7a7295992f sys_linux: allow some *time64 syscalls in seccomp filter
These are needed for 32-bit architectures with new system calls using
64-bit time_t.
2020-05-18 17:39:22 +02:00
Vincent Blut
526974366f sys_linux: restructure syscalls in seccomp filter
Having one syscall per line improves the seccomp filter reading. It
should also make updates more straightforward.
2020-05-18 17:39:22 +02:00
Miroslav Lichvar
51fe589aeb cmdmon: add cookie length to authdata report 2020-05-18 17:39:22 +02:00
Miroslav Lichvar
28cf4acf13 cmdmon: limit reported clients by number of packets
Add a new field to the CLIENT_ACCESSES_BY_INDEX request to specify the
minimum number of NTP or cmdmon packets for a client to be reported.

Add -p option to the chronyc clients command to specify the threshold
(by default 0). This option can be used to minimize the number of cmdmon
requests when interested only in clients sending a large number
of requests.
2020-05-18 17:39:22 +02:00
Miroslav Lichvar
ee2220f2e7 cmdmon: allow client records to be reset
Add a flag to the CLIENT_ACCESSES_BY_INDEX request to reset the
NTP/cmdmon hits/dropped counters after reporting the current values.

Add -r option to the chronyc clients command to perform the reset. This
should make it easier to find clients that send large number of requests
over short periods of time.
2020-05-18 17:39:18 +02:00
Miroslav Lichvar
a6ec6ec3ac sources: ignore noselect sources when updating selection options
Ignore any sources specified with the noselect option with respect to
the auth selection mode.
2020-05-18 17:38:09 +02:00
Miroslav Lichvar
4f5343f086 doc: fix formatting of example in chrony.conf man page 2020-05-14 15:37:38 +02:00
Miroslav Lichvar
79c7384e5e cmdmon: add authdata command
Add a command to display information about authentication of NTP
sources.
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
75beeaf2b0 nts: assign ID to NTS context
For monitoring purposes, assign an incrementing ID to the client NTS
context.
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
f4ed2abdca keys: provide key type and length
Save the type and length of each key and add a function to get this
information.
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
11a5c7337a keys: remove forgotten declaration 2020-05-14 15:37:38 +02:00
Miroslav Lichvar
972c476c5a cmac: enumerate cipher algorithms
Identify the CMAC ciphers with an enum instead of string.
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
a8c8f2f309 hash: enumerate hash algorithms
Identify the algorithms with an enum instead of string.
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
a0d2513be6 cmdmon: fix ntp_source_name declaration in request
Fixes: 93f6358916 ("cmdmon: add request to get source name")
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
43dc0b3295 cmdmon: rename reset command to reset sources
Add a sources option for the reset command in case there are other
components that would need to be reset.
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
195ff5c51b reference: shorten logchange log message
Remove the "adjustment started" part from the "System clock wrong by *
seconds, adjustment started" log message as it might be confusing in
some cases. There may be a step instead of a slow adjustment, or there
may be no adjustment at all when running with the -x option.
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
e49aececce socket: don't set interface for sent packets
With asymmetric routing (e.g. with BGP) it may not be possible to
respond to a request using the same interface. In such case, setting the
interface index in IP*_PKTINFO* causes the packet to be silently dropped
by the kernel.

Until we can predict if sending with the specified interface will
succeed, or provide only a hint, don't set the interface and leave it
to the kernel to select an interface.

This reverts commit 5fc7674e36 ("ntp: set interface index in
IP*_PKTINFO when responding").

Reported-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
814b07c3a2 conf: detect infinite inclusion
Don't allow more than 10 nested inclusions using the include or
confdirs directive to cleanly handle a misconfiguration with a circular
inclusion.
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
3470ab66f0 conf: add better support for fragmented configuration
Add a confdirs directive to include *.conf files from multiple
directories. If a file with the same name exists in multiple
directories, only the first one in the order of the specified
directories will be included.
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
6901df5c18 sources: improve and add more debug messages 2020-05-14 15:37:38 +02:00
Miroslav Lichvar
bddb3b3228 sources: enable selection options with authentication
When authentication is enabled for an NTP source, unauthenticated NTP
sources need to be disabled or limited in selection. That might be
difficult to do when the configuration comes from different sources
(e.g. networking scripts adding servers from DHCP).

Define four modes for the source selection to consider authentication:
require, prefer, mix, ignore. In different modes different selection
options (require, trust, noselect) are added to authenticated and
unauthenticated sources.

The mode can be selected by the authselectmode directive. The mix mode
is the default. The ignore mode enables the old behavior, where all
sources are used exactly as specified in the configuration.
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
dfe877144a sources: allow modifications of selection options
Refactor the code to allow the selection options of the current sources
to be modified when other sources are added and removed. Also, make the
authentication status of each source available to the code which makes
the modifications.
2020-05-14 15:37:38 +02:00
Miroslav Lichvar
59a9b7a9f6 sources: reformat forward prototypes 2020-05-14 15:37:38 +02:00
113 changed files with 5342 additions and 1911 deletions

View File

@@ -134,4 +134,6 @@ Makefile : Makefile.in configure
.deps/%.d: %.c | .deps .deps/%.d: %.c | .deps
@$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@ @$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@
ifndef NODEPS
-include $(ALL_OBJS:%.o=.deps/%.d) -include $(ALL_OBJS:%.o=.deps/%.d)
endif

31
NEWS
View File

@@ -5,26 +5,51 @@ Enhancements
------------ ------------
* Add support for Network Time Security (NTS) authentication * Add support for Network Time Security (NTS) authentication
* Add support for AES-CMAC keys (AES128, AES256) with Nettle * Add support for AES-CMAC keys (AES128, AES256) with Nettle
* Add support for maxsamples of 1 for faster update with -q/-Q option * Add authselectmode directive to control selection of unauthenticated sources
* Add binddevice, bindacqdevice, bindcmddevice directives
* Add confdir directive to better support fragmented configuration
* Add sourcedir directive and "reload sources" command to support dynamic
NTP sources specified in files
* Add clockprecision directive
* Add dscp directive to set Differentiated Services Code Point (DSCP)
* Add -L option to limit log messages by severity * Add -L option to limit log messages by severity
* Add -p option to print whole configuration with included files
* Add -U option to allow start under non-root user
* Allow maxsamples to be set to 1 for faster update with -q/-Q option
* Avoid replacing NTP sources with sources that have unreachable address * Avoid replacing NTP sources with sources that have unreachable address
* Improve pools to repeat name resolution to get "maxsources" sources * Improve pools to repeat name resolution to get "maxsources" sources
* Improve source selection with trusted sources
* Improve NTP loop test to prevent synchronisation to itself * Improve NTP loop test to prevent synchronisation to itself
* Repeat iburst when NTP source is switched from offline state to online
* Update clock synchronisation status and leap status more frequently * Update clock synchronisation status and leap status more frequently
* Update seccomp filter * Update seccomp filter
* Add "add pool" command * Add "add pool" command
* Add "reset sources" command to drop all measurements
* Add authdata command to print details about NTP authentication
* Add selectdata command to print details about source selection
* Add -N option and sourcename command to print original names of sources * Add -N option and sourcename command to print original names of sources
* Add -a option to sources/sourcestats command to print unresolved sources * Add -a option to some commands to print also unresolved sources
* Add reset command to drop all measurements * Add -k, -p, -r options to clients command to select, limit, reset data
Bug fixes Bug fixes
--------- ---------
* Don't set interface for NTP responses to allow asymmetric routing
* Handle RTCs that don't support interrupts * Handle RTCs that don't support interrupts
* Respond to command requests with correct address on multihomed hosts * Respond to command requests with correct address on multihomed hosts
Removed features Removed features
---------------- ----------------
* Drop support for RIPEMD keys (RMD128, RMD160, RMD256, RMD320) * Drop support for RIPEMD keys (RMD128, RMD160, RMD256, RMD320)
* Drop support for long (non-standard) MACs in NTPv4 packets (chrony 2.x
clients using non-MD5/SHA1 keys need to use option "version 3")
* Drop support for line editing with GNU Readline
New in version 3.5.1
====================
Security fixes
--------------
* Create new file when writing pidfile (CVE-2020-14367)
New in version 3.5 New in version 3.5
================== ==================

40
README
View File

@@ -29,9 +29,7 @@ What will chrony run on?
The software is known to work on Linux, FreeBSD, NetBSD, macOS and The software is known to work on Linux, FreeBSD, NetBSD, macOS and
Solaris. Closely related systems may work too. Any other system will Solaris. Closely related systems may work too. Any other system will
likely require a porting exercise. You would need to start from one likely require a porting exercise.
of the existing system-specific drivers and look into the quirks of
certain system calls and the kernel on your target system.
How do I set it up? How do I set it up?
=================== ===================
@@ -55,24 +53,20 @@ Where are new versions announced?
================================= =================================
There is a low volume mailing list where new versions and other There is a low volume mailing list where new versions and other
important news relating to chrony is announced. You can join this list important news relating to chrony are announced. You can join this list
by sending mail with the subject "subscribe" to by sending mail with the subject "subscribe" to
chrony-announce-request@chrony.tuxfamily.org chrony-announce-request@chrony.tuxfamily.org
These messages will be copied to chrony-users (see below).
How can I get support for chrony? How can I get support for chrony?
and where can I discuss new features, possible bugs etc? =================================
========================================================
There are 3 mailing lists relating to chrony. chrony-announce was There are two other mailing lists relating to chrony. chrony-users is a
mentioned above. chrony-users is a users' discussion list, e.g. for discussion list for users, e.g. for questions about chrony configuration
general questions and answers about using chrony. chrony-dev is a more and bug reports. chrony-dev is a more technical list for developers,
technical list, e.g. for discussing how new features should be e.g. for submitting patches and discussing how new features should be
implemented, exchange of information between developers etc. To implemented. To subscribe to either of these lists, send a message with
subscribe to either of these lists, send a message with the subject the subject "subscribe" to
"subscribe" to
chrony-users-request@chrony.tuxfamily.org chrony-users-request@chrony.tuxfamily.org
or or
@@ -80,12 +74,6 @@ chrony-dev-request@chrony.tuxfamily.org
as applicable. as applicable.
When you are reporting a bug, please send us all the information you can.
Unfortunately, chrony has proven to be one of those programs where it is very
difficult to reproduce bugs in a different environment. So we may have to
interact with you quite a lot to obtain enough extra logging and tracing to
pin-point the problem in some cases. Please be patient and plan for this!
License License
======= =======
@@ -100,12 +88,13 @@ Miroslav Lichvar <mlichvar@redhat.com>
Acknowledgements Acknowledgements
================ ================
In writing the chronyd program, extensive use has been made of RFC 1305 In writing the chronyd program, extensive use has been made of the NTPv3 (RFC
and RFC 5905, written by David Mills. The source code of the NTP reference 1305) and NTPv4 (RFC 5905) specification. The source code of the xntpd/ntpd
implementation has been used to check the details of the protocol. implementation written by Dennis Fergusson, Lars Mathiesen, David Mills, and
others, has been used to check the details of the protocol.
The following people have provided patches and other major contributions The following people have provided patches and other major contributions
to the program : to chrony:
Lonnie Abelbeck <lonnie@abelbeck.com> Lonnie Abelbeck <lonnie@abelbeck.com>
Benny Lyne Amorsen <benny@amorsen.dk> Benny Lyne Amorsen <benny@amorsen.dk>
@@ -121,6 +110,7 @@ Bryan Christianson <bryan@whatroute.net>
Juliusz Chroboczek <jch@pps.jussieu.fr> Juliusz Chroboczek <jch@pps.jussieu.fr>
Christian Ehrhardt <christian.ehrhardt@canonical.com> Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com> Paul Elliott <pelliott@io.com>
Robert Fairley <rfairley@redhat.com>
Stefan R. Filipek <srfilipek@gmail.com> Stefan R. Filipek <srfilipek@gmail.com>
Mike Fleetwood <mike@rockover.demon.co.uk> Mike Fleetwood <mike@rockover.demon.co.uk>
Alexander Gretencord <arutha@gmx.de> Alexander Gretencord <arutha@gmx.de>

102
candm.h
View File

@@ -103,8 +103,12 @@
#define REQ_ONOFFLINE 63 #define REQ_ONOFFLINE 63
#define REQ_ADD_SOURCE 64 #define REQ_ADD_SOURCE 64
#define REQ_NTP_SOURCE_NAME 65 #define REQ_NTP_SOURCE_NAME 65
#define REQ_RESET 66 #define REQ_RESET_SOURCES 66
#define N_REQUEST_TYPES 67 #define REQ_AUTH_DATA 67
#define REQ_CLIENT_ACCESSES_BY_INDEX3 68
#define REQ_SELECT_DATA 69
#define REQ_RELOAD_SOURCES 70
#define N_REQUEST_TYPES 71
/* Structure used to exchange timespecs independent of time_t size */ /* Structure used to exchange timespecs independent of time_t size */
typedef struct { typedef struct {
@@ -267,7 +271,7 @@ typedef struct {
typedef struct { typedef struct {
uint32_t type; uint32_t type;
int8_t name[256]; uint8_t name[256];
uint32_t port; uint32_t port;
int32_t minpoll; int32_t minpoll;
int32_t maxpoll; int32_t maxpoll;
@@ -320,6 +324,8 @@ typedef struct {
typedef struct { typedef struct {
uint32_t first_index; uint32_t first_index;
uint32_t n_clients; uint32_t n_clients;
uint32_t min_hits;
uint32_t reset;
int32_t EOR; int32_t EOR;
} REQ_ClientAccessesByIndex; } REQ_ClientAccessesByIndex;
@@ -351,6 +357,16 @@ typedef struct {
int32_t EOR; int32_t EOR;
} REQ_NTPSourceName; } REQ_NTPSourceName;
typedef struct {
IPAddr ip_addr;
int32_t EOR;
} REQ_AuthData;
typedef struct {
uint32_t index;
int32_t EOR;
} REQ_SelectData;
/* ================================================== */ /* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1 #define PKT_TYPE_CMD_REQUEST 1
@@ -389,7 +405,8 @@ typedef struct {
Version 6 (no authentication) : changed format of client accesses by index Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types) and manual timestamp, added new fields and (using new request/reply types) and manual timestamp, added new fields and
flags to NTP source request and report, made length of manual list constant, flags to NTP source request and report, made length of manual list constant,
added new commands: ntpdata, refresh, serverstats, shutdown added new commands: authdata, ntpdata, onoffline, refresh, reset,
selectdata, serverstats, shutdown, sourcename
*/ */
#define PROTO_VERSION_NUMBER 6 #define PROTO_VERSION_NUMBER 6
@@ -403,8 +420,8 @@ typedef struct {
#define PROTO_VERSION_PADDING 6 #define PROTO_VERSION_PADDING 6
/* The maximum length of padding in request packet, currently /* The maximum length of padding in request packet, currently
defined by MANUAL_LIST */ defined by CLIENT_ACCESSES_BY_INDEX3 */
#define MAX_PADDING_LENGTH 396 #define MAX_PADDING_LENGTH 484
/* ================================================== */ /* ================================================== */
@@ -453,7 +470,9 @@ typedef struct {
REQ_ReselectDistance reselect_distance; REQ_ReselectDistance reselect_distance;
REQ_SmoothTime smoothtime; REQ_SmoothTime smoothtime;
REQ_NTPData ntp_data; REQ_NTPData ntp_data;
REQ_NTPData ntp_source_name; REQ_NTPSourceName ntp_source_name;
REQ_AuthData auth_data;
REQ_SelectData select_data;
} data; /* Command specific parameters */ } data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the /* Padding used to prevent traffic amplification. It only defines the
@@ -491,7 +510,11 @@ typedef struct {
#define RPY_MANUAL_TIMESTAMP2 17 #define RPY_MANUAL_TIMESTAMP2 17
#define RPY_MANUAL_LIST2 18 #define RPY_MANUAL_LIST2 18
#define RPY_NTP_SOURCE_NAME 19 #define RPY_NTP_SOURCE_NAME 19
#define N_REPLY_TYPES 20 #define RPY_AUTH_DATA 20
#define RPY_CLIENT_ACCESSES_BY_INDEX3 21
#define RPY_SERVER_STATS2 22
#define RPY_SELECT_DATA 23
#define N_REPLY_TYPES 24
/* Status codes */ /* Status codes */
#define STT_SUCCESS 0 #define STT_SUCCESS 0
@@ -530,17 +553,12 @@ typedef struct {
#define RPY_SD_MD_PEER 1 #define RPY_SD_MD_PEER 1
#define RPY_SD_MD_REF 2 #define RPY_SD_MD_REF 2
#define RPY_SD_ST_SYNC 0 #define RPY_SD_ST_SELECTED 0
#define RPY_SD_ST_UNREACH 1 #define RPY_SD_ST_NONSELECTABLE 1
#define RPY_SD_ST_FALSETICKER 2 #define RPY_SD_ST_FALSETICKER 2
#define RPY_SD_ST_JITTERY 3 #define RPY_SD_ST_JITTERY 3
#define RPY_SD_ST_CANDIDATE 4 #define RPY_SD_ST_UNSELECTED 4
#define RPY_SD_ST_OUTLIER 5 #define RPY_SD_ST_SELECTABLE 5
#define RPY_SD_FLAG_NOSELECT 0x1
#define RPY_SD_FLAG_PREFER 0x2
#define RPY_SD_FLAG_TRUST 0x4
#define RPY_SD_FLAG_REQUIRE 0x8
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
@@ -609,14 +627,17 @@ typedef struct {
typedef struct { typedef struct {
IPAddr ip; IPAddr ip;
uint32_t ntp_hits; uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits; uint32_t cmd_hits;
uint32_t ntp_drops; uint32_t ntp_drops;
uint32_t nke_drops;
uint32_t cmd_drops; uint32_t cmd_drops;
int8_t ntp_interval; int8_t ntp_interval;
int8_t nke_interval;
int8_t cmd_interval; int8_t cmd_interval;
int8_t ntp_timeout_interval; int8_t ntp_timeout_interval;
int8_t pad;
uint32_t last_ntp_hit_ago; uint32_t last_ntp_hit_ago;
uint32_t last_nke_hit_ago;
uint32_t last_cmd_hit_ago; uint32_t last_cmd_hit_ago;
} RPY_ClientAccesses_Client; } RPY_ClientAccesses_Client;
@@ -630,10 +651,13 @@ typedef struct {
typedef struct { typedef struct {
uint32_t ntp_hits; uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits; uint32_t cmd_hits;
uint32_t ntp_drops; uint32_t ntp_drops;
uint32_t nke_drops;
uint32_t cmd_drops; uint32_t cmd_drops;
uint32_t log_drops; uint32_t log_drops;
uint32_t ntp_auth_hits;
int32_t EOR; int32_t EOR;
} RPY_ServerStats; } RPY_ServerStats;
@@ -708,10 +732,48 @@ typedef struct {
} RPY_NTPData; } RPY_NTPData;
typedef struct { typedef struct {
int8_t name[256]; uint8_t name[256];
int32_t EOR; int32_t EOR;
} RPY_NTPSourceName; } RPY_NTPSourceName;
#define RPY_AD_MD_NONE 0
#define RPY_AD_MD_SYMMETRIC 1
#define RPY_AD_MD_NTS 2
typedef struct {
uint16_t mode;
uint16_t key_type;
uint32_t key_id;
uint16_t key_length;
uint16_t ke_attempts;
uint32_t last_ke_ago;
uint16_t cookies;
uint16_t cookie_length;
uint16_t nak;
uint16_t pad;
int32_t EOR;
} RPY_AuthData;
#define RPY_SD_OPTION_NOSELECT 0x1
#define RPY_SD_OPTION_PREFER 0x2
#define RPY_SD_OPTION_TRUST 0x4
#define RPY_SD_OPTION_REQUIRE 0x8
typedef struct {
uint32_t ref_id;
IPAddr ip_addr;
uint8_t state_char;
uint8_t authentication;
uint8_t pad[2];
uint16_t conf_options;
uint16_t eff_options;
uint32_t last_sample_ago;
Float score;
Float lo_limit;
Float hi_limit;
int32_t EOR;
} RPY_SelectData;
typedef struct { typedef struct {
uint8_t version; uint8_t version;
uint8_t pkt_type; uint8_t pkt_type;
@@ -742,6 +804,8 @@ typedef struct {
RPY_Smoothing smoothing; RPY_Smoothing smoothing;
RPY_NTPData ntp_data; RPY_NTPData ntp_data;
RPY_NTPSourceName ntp_source_name; RPY_NTPSourceName ntp_source_name;
RPY_AuthData auth_data;
RPY_SelectData select_data;
} data; /* Reply specific parameters */ } data; /* Reply specific parameters */
} CMD_Reply; } CMD_Reply;

388
client.c
View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016, 2018 * Copyright (C) Lonnie Abelbeck 2016, 2018
* Copyright (C) Miroslav Lichvar 2009-2018 * Copyright (C) Miroslav Lichvar 2009-2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -44,12 +44,7 @@
#include "util.h" #include "util.h"
#ifdef FEAT_READLINE #ifdef FEAT_READLINE
#ifdef USE_EDITLINE
#include <editline/readline.h> #include <editline/readline.h>
#else
#include <readline/readline.h>
#include <readline/history.h>
#endif
#endif #endif
/* ================================================== */ /* ================================================== */
@@ -222,7 +217,7 @@ open_socket(struct Address *addr)
switch (addr->type) { switch (addr->type) {
case SCK_ADDR_IP: case SCK_ADDR_IP:
sock_fd = SCK_OpenUdpSocket(&addr->addr.ip, NULL, 0); sock_fd = SCK_OpenUdpSocket(&addr->addr.ip, NULL, NULL, 0);
break; break;
case SCK_ADDR_UNIX: case SCK_ADDR_UNIX:
/* Construct path of our socket. Use the same directory as the server /* Construct path of our socket. Use the same directory as the server
@@ -960,39 +955,12 @@ process_cmd_cmddenyall(CMD_Request *msg, char *line)
/* ================================================== */ /* ================================================== */
static int
accheck_getaddr(char *line, IPAddr *addr)
{
unsigned long a, b, c, d;
IPAddr ip;
char *p;
p = line;
if (!*p) {
return 0;
} else {
if (sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d) == 4) {
addr->family = IPADDR_INET4;
addr->addr.in4 = (a<<24) | (b<<16) | (c<<8) | d;
return 1;
} else {
if (DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) {
return 0;
} else {
*addr = ip;
return 1;
}
}
}
}
/* ================================================== */
static int static int
process_cmd_accheck(CMD_Request *msg, char *line) process_cmd_accheck(CMD_Request *msg, char *line)
{ {
IPAddr ip; IPAddr ip;
msg->command = htons(REQ_ACCHECK); msg->command = htons(REQ_ACCHECK);
if (accheck_getaddr(line, &ip)) { if (DNS_Name2IPAddress(line, &ip, 1) == DNS_Success) {
UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
return 1; return 1;
} else { } else {
@@ -1008,7 +976,7 @@ process_cmd_cmdaccheck(CMD_Request *msg, char *line)
{ {
IPAddr ip; IPAddr ip;
msg->command = htons(REQ_CMDACCHECK); msg->command = htons(REQ_CMDACCHECK);
if (accheck_getaddr(line, &ip)) { if (DNS_Name2IPAddress(line, &ip, 1) == DNS_Success) {
UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
return 1; return 1;
} else { } else {
@@ -1084,11 +1052,11 @@ process_cmd_add_source(CMD_Request *msg, char *line)
word = line; word = line;
line = CPS_SplitWord(line); line = CPS_SplitWord(line);
if (!strcmp(word, "server")) { if (!strcasecmp(word, "server")) {
type = REQ_ADDSRC_SERVER; type = REQ_ADDSRC_SERVER;
} else if (!strcmp(word, "peer")) { } else if (!strcasecmp(word, "peer")) {
type = REQ_ADDSRC_PEER; type = REQ_ADDSRC_PEER;
} else if (!strcmp(word, "pool")) { } else if (!strcasecmp(word, "pool")) {
type = REQ_ADDSRC_POOL; type = REQ_ADDSRC_POOL;
} else { } else {
LOG(LOGS_ERR, "Invalid syntax for add command"); LOG(LOGS_ERR, "Invalid syntax for add command");
@@ -1120,7 +1088,7 @@ process_cmd_add_source(CMD_Request *msg, char *line)
assert(0); assert(0);
strncpy((char *)msg->data.ntp_source.name, data.name, strncpy((char *)msg->data.ntp_source.name, data.name,
sizeof (msg->data.ntp_source.name)); sizeof (msg->data.ntp_source.name));
msg->data.ntp_source.port = htonl((unsigned long) data.port); msg->data.ntp_source.port = htonl(data.port);
msg->data.ntp_source.minpoll = htonl(data.params.minpoll); msg->data.ntp_source.minpoll = htonl(data.params.minpoll);
msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll); msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll);
msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll); msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll);
@@ -1209,29 +1177,32 @@ give_help(void)
"Time sources:\0\0" "Time sources:\0\0"
"sources [-a] [-v]\0Display information about current sources\0" "sources [-a] [-v]\0Display information about current sources\0"
"sourcestats [-a] [-v]\0Display statistics about collected measurements\0" "sourcestats [-a] [-v]\0Display statistics about collected measurements\0"
"selectdata [-a] [-v]\0Display information about source selection\0"
"reselect\0Force reselecting synchronisation source\0" "reselect\0Force reselecting synchronisation source\0"
"reselectdist <dist>\0Modify reselection distance\0" "reselectdist <dist>\0Modify reselection distance\0"
"\0\0" "\0\0"
"NTP sources:\0\0" "NTP sources:\0\0"
"activity\0Check how many NTP sources are online/offline\0" "activity\0Check how many NTP sources are online/offline\0"
"authdata [-a] [-v]\0Display information about authentication\0"
"ntpdata [<address>]\0Display information about last valid measurement\0" "ntpdata [<address>]\0Display information about last valid measurement\0"
"add server <name> [options]\0Add new NTP server\0" "add server <name> [options]\0Add new NTP server\0"
"add pool <name> [options]\0Add new pool of NTP servers\0" "add pool <name> [options]\0Add new pool of NTP servers\0"
"add peer <name> [options]\0Add new NTP peer\0" "add peer <name> [options]\0Add new NTP peer\0"
"delete <address>\0Remove server or peer\0" "delete <address>\0Remove server or peer\0"
"burst <n-good>/<n-max> [<mask>/<address>]\0Start rapid set of measurements\0" "burst <n-good>/<n-max> [[<mask>/]<address>]\0Start rapid set of measurements\0"
"maxdelay <address> <delay>\0Modify maximum valid sample delay\0" "maxdelay <address> <delay>\0Modify maximum valid sample delay\0"
"maxdelayratio <address> <ratio>\0Modify maximum valid delay/minimum ratio\0" "maxdelayratio <address> <ratio>\0Modify maximum valid delay/minimum ratio\0"
"maxdelaydevratio <address> <ratio>\0Modify maximum valid delay/deviation ratio\0" "maxdelaydevratio <address> <ratio>\0Modify maximum valid delay/deviation ratio\0"
"minpoll <address> <poll>\0Modify minimum polling interval\0" "minpoll <address> <poll>\0Modify minimum polling interval\0"
"maxpoll <address> <poll>\0Modify maximum polling interval\0" "maxpoll <address> <poll>\0Modify maximum polling interval\0"
"minstratum <address> <stratum>\0Modify minimum stratum\0" "minstratum <address> <stratum>\0Modify minimum stratum\0"
"offline [<mask>/<address>]\0Set sources in subnet to offline status\0" "offline [[<mask>/]<address>]\0Set sources in subnet to offline status\0"
"online [<mask>/<address>]\0Set sources in subnet to online status\0" "online [[<mask>/]<address>]\0Set sources in subnet to online status\0"
"onoffline\0Set all sources to online or offline status\0" "onoffline\0Set all sources to online or offline status\0"
"\0according to network configuration\0" "\0according to network configuration\0"
"polltarget <address> <target>\0Modify poll target\0" "polltarget <address> <target>\0Modify poll target\0"
"refresh\0Refresh IP addresses\0" "refresh\0Refresh IP addresses\0"
"reload sources\0Re-read *.sources files\0"
"sourcename <address>\0Display original name\0" "sourcename <address>\0Display original name\0"
"\0\0" "\0\0"
"Manual time input:\0\0" "Manual time input:\0\0"
@@ -1242,7 +1213,7 @@ give_help(void)
"\0(e.g. Sep 25, 2015 16:30:05 or 16:30:05)\0" "\0(e.g. Sep 25, 2015 16:30:05 or 16:30:05)\0"
"\0\0NTP access:\0\0" "\0\0NTP access:\0\0"
"accheck <address>\0Check whether address is allowed\0" "accheck <address>\0Check whether address is allowed\0"
"clients\0Report on clients that have accessed the server\0" "clients [-p <packets>] [-k] [-r]\0Report on clients that accessed the server\0"
"serverstats\0Display statistics of the server\0" "serverstats\0Display statistics of the server\0"
"allow [<subnet>]\0Allow access to subnet as a default\0" "allow [<subnet>]\0Allow access to subnet as a default\0"
"allow all [<subnet>]\0Allow access to subnet and all children\0" "allow all [<subnet>]\0Allow access to subnet and all children\0"
@@ -1269,7 +1240,7 @@ give_help(void)
"cyclelogs\0Close and re-open log files\0" "cyclelogs\0Close and re-open log files\0"
"dump\0Dump measurements and NTS keys/cookies\0" "dump\0Dump measurements and NTS keys/cookies\0"
"rekey\0Re-read keys\0" "rekey\0Re-read keys\0"
"reset\0Drop all measurements\0" "reset sources\0Drop all measurements\0"
"shutdown\0Stop daemon\0" "shutdown\0Stop daemon\0"
"\0\0" "\0\0"
"Client commands:\0\0" "Client commands:\0\0"
@@ -1291,7 +1262,7 @@ give_help(void)
} }
/* ================================================== */ /* ================================================== */
/* Tab-completion when editline/readline is available */ /* Tab-completion when editline is available */
#ifdef FEAT_READLINE #ifdef FEAT_READLINE
@@ -1299,8 +1270,12 @@ enum {
TAB_COMPLETE_BASE_CMDS, TAB_COMPLETE_BASE_CMDS,
TAB_COMPLETE_ADD_OPTS, TAB_COMPLETE_ADD_OPTS,
TAB_COMPLETE_MANUAL_OPTS, TAB_COMPLETE_MANUAL_OPTS,
TAB_COMPLETE_RELOAD_OPTS,
TAB_COMPLETE_RESET_OPTS,
TAB_COMPLETE_SOURCES_OPTS, TAB_COMPLETE_SOURCES_OPTS,
TAB_COMPLETE_SOURCESTATS_OPTS, TAB_COMPLETE_SOURCESTATS_OPTS,
TAB_COMPLETE_AUTHDATA_OPTS,
TAB_COMPLETE_SELECTDATA_OPTS,
TAB_COMPLETE_MAX_INDEX TAB_COMPLETE_MAX_INDEX
}; };
@@ -1311,28 +1286,33 @@ command_name_generator(const char *text, int state)
{ {
const char *name, **names[TAB_COMPLETE_MAX_INDEX]; const char *name, **names[TAB_COMPLETE_MAX_INDEX];
const char *base_commands[] = { const char *base_commands[] = {
"accheck", "activity", "add", "allow", "burst", "accheck", "activity", "add", "allow", "authdata", "burst",
"clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete", "clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
"deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep", "deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll", "manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline", "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
"polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist", "reset", "polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset",
"retries", "rtcdata", "serverstats", "settime", "shutdown", "smoothing", "retries", "rtcdata", "selectdata", "serverstats", "settime", "shutdown", "smoothing",
"smoothtime", "sourcename", "sources", "sourcestats", "smoothtime", "sourcename", "sources", "sourcestats",
"timeout", "tracking", "trimrtc", "waitsync", "writertc", "timeout", "tracking", "trimrtc", "waitsync", "writertc",
NULL NULL
}; };
const char *add_options[] = { "peer", "pool", "server", NULL }; const char *add_options[] = { "peer", "pool", "server", NULL };
const char *manual_options[] = { "on", "off", "delete", "list", "reset", NULL }; const char *manual_options[] = { "on", "off", "delete", "list", "reset", NULL };
const char *sources_options[] = { "-a", "-v", NULL }; const char *reset_options[] = { "sources", NULL };
const char *sourcestats_options[] = { "-a", "-v", NULL }; const char *reload_options[] = { "sources", NULL };
const char *common_source_options[] = { "-a", "-v", NULL };
static int list_index, len; static int list_index, len;
names[TAB_COMPLETE_BASE_CMDS] = base_commands; names[TAB_COMPLETE_BASE_CMDS] = base_commands;
names[TAB_COMPLETE_ADD_OPTS] = add_options; names[TAB_COMPLETE_ADD_OPTS] = add_options;
names[TAB_COMPLETE_MANUAL_OPTS] = manual_options; names[TAB_COMPLETE_MANUAL_OPTS] = manual_options;
names[TAB_COMPLETE_SOURCES_OPTS] = sources_options; names[TAB_COMPLETE_RELOAD_OPTS] = reload_options;
names[TAB_COMPLETE_SOURCESTATS_OPTS] = sourcestats_options; names[TAB_COMPLETE_RESET_OPTS] = reset_options;
names[TAB_COMPLETE_AUTHDATA_OPTS] = common_source_options;
names[TAB_COMPLETE_SELECTDATA_OPTS] = common_source_options;
names[TAB_COMPLETE_SOURCES_OPTS] = common_source_options;
names[TAB_COMPLETE_SOURCESTATS_OPTS] = common_source_options;
if (!state) { if (!state) {
list_index = 0; list_index = 0;
@@ -1360,8 +1340,16 @@ command_name_completion(const char *text, int start, int end)
if (!strcmp(first, "add ")) { if (!strcmp(first, "add ")) {
tab_complete_index = TAB_COMPLETE_ADD_OPTS; tab_complete_index = TAB_COMPLETE_ADD_OPTS;
} else if (!strcmp(first, "authdata ")) {
tab_complete_index = TAB_COMPLETE_AUTHDATA_OPTS;
} else if (!strcmp(first, "manual ")) { } else if (!strcmp(first, "manual ")) {
tab_complete_index = TAB_COMPLETE_MANUAL_OPTS; tab_complete_index = TAB_COMPLETE_MANUAL_OPTS;
} else if (!strcmp(first, "reload ")) {
tab_complete_index = TAB_COMPLETE_RELOAD_OPTS;
} else if (!strcmp(first, "reset ")) {
tab_complete_index = TAB_COMPLETE_RESET_OPTS;
} else if (!strcmp(first, "selectdata ")) {
tab_complete_index = TAB_COMPLETE_SELECTDATA_OPTS;
} else if (!strcmp(first, "sources ")) { } else if (!strcmp(first, "sources ")) {
tab_complete_index = TAB_COMPLETE_SOURCES_OPTS; tab_complete_index = TAB_COMPLETE_SOURCES_OPTS;
} else if (!strcmp(first, "sourcestats ")) { } else if (!strcmp(first, "sourcestats ")) {
@@ -2056,7 +2044,7 @@ get_source_name(IPAddr *ip_addr, char *buf, int size)
UTI_IPHostToNetwork(ip_addr, &request.data.ntp_source_name.ip_addr); UTI_IPHostToNetwork(ip_addr, &request.data.ntp_source_name.ip_addr);
if (!request_reply(&request, &reply, RPY_NTP_SOURCE_NAME, 0) || if (!request_reply(&request, &reply, RPY_NTP_SOURCE_NAME, 0) ||
reply.data.ntp_source_name.name[sizeof (reply.data.ntp_source_name.name) - 1] != '\0' || reply.data.ntp_source_name.name[sizeof (reply.data.ntp_source_name.name) - 1] != '\0' ||
snprintf(buf, size, "%s", reply.data.ntp_source_name.name) >= size) snprintf(buf, size, "%s", (char *)reply.data.ntp_source_name.name) >= size)
return 0; return 0;
/* Make sure the name is printable */ /* Make sure the name is printable */
@@ -2153,8 +2141,8 @@ process_cmd_sources(char *line)
if (verbose) { if (verbose) {
printf("\n"); printf("\n");
printf(" .-- Source mode '^' = server, '=' = peer, '#' = local clock.\n"); printf(" .-- Source mode '^' = server, '=' = peer, '#' = local clock.\n");
printf(" / .- Source state '*' = current synced, '+' = combined , '-' = not combined,\n"); printf(" / .- Source state '*' = current best, '+' = combined, '-' = not combined,\n");
printf("| / '?' = unreachable, 'x' = time may be in error, '~' = time too variable.\n"); printf("| / 'x' = may be in error, '~' = too variable, '?' = unusable.\n");
printf("|| .- xxxx [ yyyy ] +/- zzzz\n"); printf("|| .- xxxx [ yyyy ] +/- zzzz\n");
printf("|| Reachability register (octal) -. | xxxx = adjusted offset,\n"); printf("|| Reachability register (octal) -. | xxxx = adjusted offset,\n");
printf("|| Log2(Polling interval) --. | | yyyy = measured offset,\n"); printf("|| Log2(Polling interval) --. | | yyyy = measured offset,\n");
@@ -2196,10 +2184,10 @@ process_cmd_sources(char *line)
} }
switch (ntohs(reply.data.source_data.state)) { switch (ntohs(reply.data.source_data.state)) {
case RPY_SD_ST_SYNC: case RPY_SD_ST_SELECTED:
state_ch = '*'; state_ch = '*';
break; break;
case RPY_SD_ST_UNREACH: case RPY_SD_ST_NONSELECTABLE:
state_ch = '?'; state_ch = '?';
break; break;
case RPY_SD_ST_FALSETICKER: case RPY_SD_ST_FALSETICKER:
@@ -2208,10 +2196,10 @@ process_cmd_sources(char *line)
case RPY_SD_ST_JITTERY: case RPY_SD_ST_JITTERY:
state_ch = '~'; state_ch = '~';
break; break;
case RPY_SD_ST_CANDIDATE: case RPY_SD_ST_UNSELECTED:
state_ch = '+'; state_ch = '+';
break; break;
case RPY_SD_ST_OUTLIER: case RPY_SD_ST_SELECTABLE:
state_ch = '-'; state_ch = '-';
break; break;
default: default:
@@ -2358,6 +2346,91 @@ process_cmd_tracking(char *line)
/* ================================================== */ /* ================================================== */
static int
process_cmd_authdata(char *line)
{
CMD_Request request;
CMD_Reply reply;
IPAddr ip_addr;
uint32_t i, source_mode, n_sources;
int all, verbose;
const char *mode_str;
char name[256];
parse_sources_options(line, &all, &verbose);
request.command = htons(REQ_N_SOURCES);
if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
return 0;
n_sources = ntohl(reply.data.n_sources.n_sources);
if (verbose) {
printf( " .- Auth. mechanism (NTS, SK - symmetric key)\n");
printf( " | Key length -. Cookie length (bytes) -.\n");
printf( " | (bits) | Num. of cookies --. |\n");
printf( " | | Key est. attempts | |\n");
printf( " | | | | |\n");
}
print_header("Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen");
/* "NNNNNNNNNNNNNNNNNNNNNNNNNNN MMMM KKKKK AAAA LLLL LLLL AAAA NNNN CCCC LLLL" */
for (i = 0; i < n_sources; i++) {
request.command = htons(REQ_SOURCE_DATA);
request.data.source_data.index = htonl(i);
if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0))
return 0;
source_mode = ntohs(reply.data.source_data.mode);
if (source_mode != RPY_SD_MD_CLIENT && source_mode != RPY_SD_MD_PEER)
continue;
UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr);
if (!all && ip_addr.family == IPADDR_ID)
continue;
request.command = htons(REQ_AUTH_DATA);
request.data.auth_data.ip_addr = reply.data.source_data.ip_addr;
if (!request_reply(&request, &reply, RPY_AUTH_DATA, 0))
return 0;
format_name(name, sizeof (name), 25, 0, 0, 1, &ip_addr);
switch (ntohs(reply.data.auth_data.mode)) {
case RPY_AD_MD_NONE:
mode_str = "-";
break;
case RPY_AD_MD_SYMMETRIC:
mode_str = "SK";
break;
case RPY_AD_MD_NTS:
mode_str = "NTS";
break;
default:
mode_str = "?";
break;
}
print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d %4d\n",
name, mode_str,
(unsigned long)ntohl(reply.data.auth_data.key_id),
ntohs(reply.data.auth_data.key_type),
ntohs(reply.data.auth_data.key_length),
(unsigned long)ntohl(reply.data.auth_data.last_ke_ago),
ntohs(reply.data.auth_data.ke_attempts),
ntohs(reply.data.auth_data.nak),
ntohs(reply.data.auth_data.cookies),
ntohs(reply.data.auth_data.cookie_length),
REPORT_END);
}
return 1;
}
/* ================================================== */
static int static int
process_cmd_ntpdata(char *line) process_cmd_ntpdata(char *line)
{ {
@@ -2474,6 +2547,81 @@ process_cmd_ntpdata(char *line)
/* ================================================== */ /* ================================================== */
static int
process_cmd_selectdata(char *line)
{
CMD_Request request;
CMD_Reply reply;
uint32_t i, n_sources;
int all, verbose, conf_options, eff_options;
char name[256];
IPAddr ip_addr;
parse_sources_options(line, &all, &verbose);
request.command = htons(REQ_N_SOURCES);
if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
return 0;
n_sources = ntohl(reply.data.n_sources.n_sources);
if (verbose) {
printf( " .-- State: N - noselect, M - missing samples, d/D - large distance,\n");
printf( " / ~ - jittery, w/W - waits for others, T - not trusted,\n");
printf( "| x - falseticker, P - not preferred, U - waits for update,\n");
printf( "| S - stale, O - orphan, + - combined, * - best.\n");
printf( "| Effective options ------. (N - noselect, P - prefer\n");
printf( "| Configured options -. \\ T - trust, R - require)\n");
printf( "| Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n");
printf( "| | | | |\n");
}
print_header("S Name/IP Address Auth COpts EOpts Last Score Interval ");
/* "S NNNNNNNNNNNNNNNNNNNNNNNNN A OOOO- OOOO- LLLL SSSSS LLLLLLL LLLLLLL" */
for (i = 0; i < n_sources; i++) {
request.command = htons(REQ_SELECT_DATA);
request.data.source_data.index = htonl(i);
if (!request_reply(&request, &reply, RPY_SELECT_DATA, 0))
return 0;
UTI_IPNetworkToHost(&reply.data.select_data.ip_addr, &ip_addr);
if (!all && ip_addr.family == IPADDR_ID)
continue;
format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC,
ntohl(reply.data.select_data.ref_id), 1, &ip_addr);
conf_options = ntohs(reply.data.select_data.conf_options);
eff_options = ntohs(reply.data.select_data.eff_options);
print_report("%c %-25s %c %c%c%c%c%c %c%c%c%c%c %I %5.1f %+S %+S\n",
reply.data.select_data.state_char,
name,
reply.data.select_data.authentication ? 'Y' : 'N',
conf_options & RPY_SD_OPTION_NOSELECT ? 'N' : '-',
conf_options & RPY_SD_OPTION_PREFER ? 'P' : '-',
conf_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
conf_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-',
'-',
eff_options & RPY_SD_OPTION_NOSELECT ? 'N' : '-',
eff_options & RPY_SD_OPTION_PREFER ? 'P' : '-',
eff_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
eff_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-',
'-',
(unsigned long)ntohl(reply.data.select_data.last_sample_ago),
UTI_FloatNetworkToHost(reply.data.select_data.score),
UTI_FloatNetworkToHost(reply.data.select_data.lo_limit),
UTI_FloatNetworkToHost(reply.data.select_data.hi_limit),
REPORT_END);
}
return 1;
}
/* ================================================== */
static int static int
process_cmd_serverstats(char *line) process_cmd_serverstats(char *line)
{ {
@@ -2481,19 +2629,25 @@ process_cmd_serverstats(char *line)
CMD_Reply reply; CMD_Reply reply;
request.command = htons(REQ_SERVER_STATS); request.command = htons(REQ_SERVER_STATS);
if (!request_reply(&request, &reply, RPY_SERVER_STATS, 0)) if (!request_reply(&request, &reply, RPY_SERVER_STATS2, 0))
return 0; return 0;
print_report("NTP packets received : %U\n" print_report("NTP packets received : %U\n"
"NTP packets dropped : %U\n" "NTP packets dropped : %U\n"
"Command packets received : %U\n" "Command packets received : %U\n"
"Command packets dropped : %U\n" "Command packets dropped : %U\n"
"Client log records dropped : %U\n", "Client log records dropped : %U\n"
"NTS-KE connections accepted: %U\n"
"NTS-KE connections dropped : %U\n"
"Authenticated NTP packets : %U\n",
(unsigned long)ntohl(reply.data.server_stats.ntp_hits), (unsigned long)ntohl(reply.data.server_stats.ntp_hits),
(unsigned long)ntohl(reply.data.server_stats.ntp_drops), (unsigned long)ntohl(reply.data.server_stats.ntp_drops),
(unsigned long)ntohl(reply.data.server_stats.cmd_hits), (unsigned long)ntohl(reply.data.server_stats.cmd_hits),
(unsigned long)ntohl(reply.data.server_stats.cmd_drops), (unsigned long)ntohl(reply.data.server_stats.cmd_drops),
(unsigned long)ntohl(reply.data.server_stats.log_drops), (unsigned long)ntohl(reply.data.server_stats.log_drops),
(unsigned long)ntohl(reply.data.server_stats.nke_hits),
(unsigned long)ntohl(reply.data.server_stats.nke_drops),
(unsigned long)ntohl(reply.data.server_stats.ntp_auth_hits),
REPORT_END); REPORT_END);
return 1; return 1;
@@ -2591,20 +2745,46 @@ process_cmd_clients(char *line)
CMD_Request request; CMD_Request request;
CMD_Reply reply; CMD_Reply reply;
IPAddr ip; IPAddr ip;
uint32_t i, n_clients, next_index, n_indices; uint32_t i, n_clients, next_index, n_indices, min_hits, reset;
RPY_ClientAccesses_Client *client; RPY_ClientAccesses_Client *client;
char name[50]; char header[80], name[50], *opt, *arg;
int nke;
next_index = 0; next_index = 0;
min_hits = 0;
reset = 0;
nke = 0;
print_header("Hostname NTP Drop Int IntL Last Cmd Drop Int Last"); while (*line) {
opt = line;
line = CPS_SplitWord(line);
if (strcmp(opt, "-k") == 0) {
nke = 1;
} else if (strcmp(opt, "-p") == 0) {
arg = line;
line = CPS_SplitWord(line);
if (sscanf(arg, "%"SCNu32, &min_hits) != 1) {
LOG(LOGS_ERR, "Invalid syntax for clients command");
return 0;
}
} else if (strcmp(opt, "-r") == 0) {
reset = 1;
}
}
snprintf(header, sizeof (header),
"Hostname NTP Drop Int IntL Last %6s Drop Int Last",
nke ? "NTS-KE" : "Cmd");
print_header(header);
while (1) { while (1) {
request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX2); request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX3);
request.data.client_accesses_by_index.first_index = htonl(next_index); request.data.client_accesses_by_index.first_index = htonl(next_index);
request.data.client_accesses_by_index.n_clients = htonl(MAX_CLIENT_ACCESSES); request.data.client_accesses_by_index.n_clients = htonl(MAX_CLIENT_ACCESSES);
request.data.client_accesses_by_index.min_hits = htonl(min_hits);
request.data.client_accesses_by_index.reset = htonl(reset);
if (!request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX2, 0)) if (!request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX3, 0))
return 0; return 0;
n_clients = ntohl(reply.data.client_accesses_by_index.n_clients); n_clients = ntohl(reply.data.client_accesses_by_index.n_clients);
@@ -2629,10 +2809,11 @@ process_cmd_clients(char *line)
client->ntp_interval, client->ntp_interval,
client->ntp_timeout_interval, client->ntp_timeout_interval,
(unsigned long)ntohl(client->last_ntp_hit_ago), (unsigned long)ntohl(client->last_ntp_hit_ago),
(unsigned long)ntohl(client->cmd_hits), (unsigned long)ntohl(nke ? client->nke_hits : client->cmd_hits),
(unsigned long)ntohl(client->cmd_drops), (unsigned long)ntohl(nke ? client->nke_drops : client->cmd_drops),
client->cmd_interval, nke ? client->nke_interval : client->cmd_interval,
(unsigned long)ntohl(client->last_cmd_hit_ago), (unsigned long)ntohl(nke ? client->last_nke_hit_ago :
client->last_cmd_hit_ago),
REPORT_END); REPORT_END);
} }
@@ -2836,10 +3017,32 @@ process_cmd_shutdown(CMD_Request *msg, char *line)
/* ================================================== */ /* ================================================== */
static void static int
process_cmd_reload(CMD_Request *msg, char *line)
{
if (!strcmp(line, "sources")) {
msg->command = htons(REQ_RELOAD_SOURCES);
} else {
LOG(LOGS_ERR, "Invalid syntax for reload command");
return 0;
}
return 1;
}
/* ================================================== */
static int
process_cmd_reset(CMD_Request *msg, char *line) process_cmd_reset(CMD_Request *msg, char *line)
{ {
msg->command = htons(REQ_RESET); if (!strcmp(line, "sources")) {
msg->command = htons(REQ_RESET_SOURCES);
} else {
LOG(LOGS_ERR, "Invalid syntax for reset command");
return 0;
}
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -2959,26 +3162,35 @@ process_cmd_retries(const char *line)
static int static int
process_cmd_keygen(char *line) process_cmd_keygen(char *line)
{ {
unsigned int i, args, cmac_length, length, id = 1, bits = 160;
unsigned char key[512]; unsigned char key[512];
char type[17]; const char *type;
unsigned int i, cmac_length, length, id = 1, bits = 160; char *words[3];
#ifdef FEAT_SECHASH #ifdef FEAT_SECHASH
snprintf(type, sizeof (type), "SHA1"); type = "SHA1";
#else #else
snprintf(type, sizeof (type), "MD5"); type = "MD5";
#endif #endif
if (sscanf(line, "%u %16s %u", &id, type, &bits)) args = UTI_SplitString(line, words, 3);
; if (args >= 2)
type = words[1];
if (args > 3 ||
(args >= 1 && sscanf(words[0], "%u", &id) != 1) ||
(args >= 3 && sscanf(words[2], "%u", &bits) != 1)) {
LOG(LOGS_ERR, "Invalid syntax for keygen command");
return 0;
}
#ifdef HAVE_CMAC #ifdef HAVE_CMAC
cmac_length = CMC_GetKeyLength(type); cmac_length = CMC_GetKeyLength(UTI_CmacNameToAlgorithm(type));
#else #else
cmac_length = 0; cmac_length = 0;
#endif #endif
if (HSH_GetHashId(type) >= 0) { if (HSH_GetHashId(UTI_HashNameToAlgorithm(type)) >= 0) {
length = (bits + 7) / 8; length = (bits + 7) / 8;
} else if (cmac_length > 0) { } else if (cmac_length > 0) {
length = cmac_length; length = cmac_length;
@@ -3038,6 +3250,9 @@ process_line(char *line)
} else { } else {
do_normal_submit = process_cmd_allow(&tx_message, line); do_normal_submit = process_cmd_allow(&tx_message, line);
} }
} else if (!strcmp(command, "authdata")) {
do_normal_submit = 0;
ret = process_cmd_authdata(line);
} else if (!strcmp(command, "burst")) { } else if (!strcmp(command, "burst")) {
do_normal_submit = process_cmd_burst(&tx_message, line); do_normal_submit = process_cmd_burst(&tx_message, line);
} else if (!strcmp(command, "clients")) { } else if (!strcmp(command, "clients")) {
@@ -3134,18 +3349,23 @@ process_line(char *line)
process_cmd_refresh(&tx_message, line); process_cmd_refresh(&tx_message, line);
} else if (!strcmp(command, "rekey")) { } else if (!strcmp(command, "rekey")) {
process_cmd_rekey(&tx_message, line); process_cmd_rekey(&tx_message, line);
} else if (!strcmp(command, "reload")) {
do_normal_submit = process_cmd_reload(&tx_message, line);
} else if (!strcmp(command, "reselect")) { } else if (!strcmp(command, "reselect")) {
process_cmd_reselect(&tx_message, line); process_cmd_reselect(&tx_message, line);
} else if (!strcmp(command, "reselectdist")) { } else if (!strcmp(command, "reselectdist")) {
do_normal_submit = process_cmd_reselectdist(&tx_message, line); do_normal_submit = process_cmd_reselectdist(&tx_message, line);
} else if (!strcmp(command, "reset")) { } else if (!strcmp(command, "reset")) {
process_cmd_reset(&tx_message, line); do_normal_submit = process_cmd_reset(&tx_message, line);
} else if (!strcmp(command, "retries")) { } else if (!strcmp(command, "retries")) {
ret = process_cmd_retries(line); ret = process_cmd_retries(line);
do_normal_submit = 0; do_normal_submit = 0;
} else if (!strcmp(command, "rtcdata")) { } else if (!strcmp(command, "rtcdata")) {
do_normal_submit = 0; do_normal_submit = 0;
ret = process_cmd_rtcreport(line); ret = process_cmd_rtcreport(line);
} else if (!strcmp(command, "selectdata")) {
do_normal_submit = 0;
ret = process_cmd_selectdata(line);
} else if (!strcmp(command, "serverstats")) { } else if (!strcmp(command, "serverstats")) {
do_normal_submit = 0; do_normal_submit = 0;
ret = process_cmd_serverstats(line); ret = process_cmd_serverstats(line);
@@ -3251,7 +3471,7 @@ static void
display_gpl(void) display_gpl(void)
{ {
printf("chrony version %s\n" printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2019 Richard P. Curnow and others\n" "Copyright (C) 1997-2003, 2007, 2009-2020 Richard P. Curnow and others\n"
"chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n" "chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
"you are welcome to redistribute it under certain conditions. See the\n" "you are welcome to redistribute it under certain conditions. See the\n"
"GNU General Public License version 2 for details.\n\n", "GNU General Public License version 2 for details.\n\n",
@@ -3358,7 +3578,7 @@ main(int argc, char **argv)
UTI_SetQuitSignalsHandler(signal_handler, 0); UTI_SetQuitSignalsHandler(signal_handler, 0);
SCK_Initialise(); SCK_Initialise(IPADDR_UNSPEC);
server_addresses = get_addresses(hostnames, port); server_addresses = get_addresses(hostnames, port);
if (!open_io()) if (!open_io())

View File

@@ -44,20 +44,17 @@
#include "util.h" #include "util.h"
#include "logging.h" #include "logging.h"
#define MAX_SERVICES 3
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
uint32_t last_ntp_hit; uint32_t last_hit[MAX_SERVICES];
uint32_t last_cmd_hit; uint32_t hits[MAX_SERVICES];
uint32_t ntp_hits; uint16_t drops[MAX_SERVICES];
uint32_t cmd_hits; uint16_t tokens[MAX_SERVICES];
uint16_t ntp_drops; int8_t rate[MAX_SERVICES];
uint16_t cmd_drops;
uint16_t ntp_tokens;
uint16_t cmd_tokens;
int8_t ntp_rate;
int8_t cmd_rate;
int8_t ntp_timeout_rate; int8_t ntp_timeout_rate;
uint8_t flags; uint8_t drop_flags;
NTP_int64 ntp_rx_ts; NTP_int64 ntp_rx_ts;
NTP_int64 ntp_tx_ts; NTP_int64 ntp_tx_ts;
} Record; } Record;
@@ -104,15 +101,12 @@ static uint32_t ts_offset;
#define MIN_LIMIT_BURST 1 #define MIN_LIMIT_BURST 1
#define MAX_LIMIT_BURST 255 #define MAX_LIMIT_BURST 255
static uint16_t max_ntp_tokens; static uint16_t max_tokens[MAX_SERVICES];
static uint16_t max_cmd_tokens; static uint16_t tokens_per_hit[MAX_SERVICES];
static uint16_t ntp_tokens_per_packet;
static uint16_t cmd_tokens_per_packet;
/* Reduction of token rates to avoid overflow of 16-bit counters. Negative /* Reduction of token rates to avoid overflow of 16-bit counters. Negative
shift is used for coarse limiting with intervals shorter than -TS_FRAC. */ shift is used for coarse limiting with intervals shorter than -TS_FRAC. */
static int ntp_token_shift; static int token_shift[MAX_SERVICES];
static int cmd_token_shift;
/* Rates at which responses are randomly allowed (in log2) when the /* Rates at which responses are randomly allowed (in log2) when the
buckets don't have enough tokens. This is necessary in order to buckets don't have enough tokens. This is necessary in order to
@@ -122,23 +116,18 @@ static int cmd_token_shift;
#define MIN_LEAK_RATE 1 #define MIN_LEAK_RATE 1
#define MAX_LEAK_RATE 4 #define MAX_LEAK_RATE 4
static int ntp_leak_rate; static int leak_rate[MAX_SERVICES];
static int cmd_leak_rate;
/* Flag indicating whether the last response was dropped */ /* Limit intervals in log2 */
#define FLAG_NTP_DROPPED 0x1 static int limit_interval[MAX_SERVICES];
/* NTP limit interval in log2 */
static int ntp_limit_interval;
/* Flag indicating whether facility is turned on or not */ /* Flag indicating whether facility is turned on or not */
static int active; static int active;
/* Global statistics */ /* Global statistics */
static uint32_t total_ntp_hits; static uint32_t total_hits[MAX_SERVICES];
static uint32_t total_cmd_hits; static uint32_t total_drops[MAX_SERVICES];
static uint32_t total_ntp_drops; static uint32_t total_ntp_auth_hits;
static uint32_t total_cmd_drops;
static uint32_t total_record_drops; static uint32_t total_record_drops;
#define NSEC_PER_SEC 1000000000U #define NSEC_PER_SEC 1000000000U
@@ -161,12 +150,28 @@ compare_ts(uint32_t x, uint32_t y)
/* ================================================== */ /* ================================================== */
static int
compare_total_hits(Record *x, Record *y)
{
uint32_t x_hits, y_hits;
int i;
for (i = 0, x_hits = y_hits = 0; i < MAX_SERVICES; i++) {
x_hits += x->hits[i];
y_hits += y->hits[i];
}
return x_hits > y_hits ? 1 : -1;
}
/* ================================================== */
static Record * static Record *
get_record(IPAddr *ip) get_record(IPAddr *ip)
{ {
unsigned int first, i; uint32_t last_hit, oldest_hit = 0;
time_t last_hit, oldest_hit = 0;
Record *record, *oldest_record; Record *record, *oldest_record;
unsigned int first, i, j;
if (!active || (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6)) if (!active || (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6))
return NULL; return NULL;
@@ -184,12 +189,13 @@ get_record(IPAddr *ip)
if (record->ip_addr.family == IPADDR_UNSPEC) if (record->ip_addr.family == IPADDR_UNSPEC)
break; break;
last_hit = compare_ts(record->last_ntp_hit, record->last_cmd_hit) > 0 ? for (j = 0; j < MAX_SERVICES; j++) {
record->last_ntp_hit : record->last_cmd_hit; if (j == 0 || compare_ts(last_hit, record->last_hit[j]) < 0)
last_hit = record->last_hit[j];
}
if (!oldest_record || compare_ts(oldest_hit, last_hit) > 0 || if (!oldest_record || compare_ts(oldest_hit, last_hit) > 0 ||
(oldest_hit == last_hit && record->ntp_hits + record->cmd_hits < (oldest_hit == last_hit && compare_total_hits(oldest_record, record) > 0)) {
oldest_record->ntp_hits + oldest_record->cmd_hits)) {
oldest_record = record; oldest_record = record;
oldest_hit = last_hit; oldest_hit = last_hit;
} }
@@ -211,14 +217,18 @@ get_record(IPAddr *ip)
} }
record->ip_addr = *ip; record->ip_addr = *ip;
record->last_ntp_hit = record->last_cmd_hit = INVALID_TS; for (i = 0; i < MAX_SERVICES; i++)
record->ntp_hits = record->cmd_hits = 0; record->last_hit[i] = INVALID_TS;
record->ntp_drops = record->cmd_drops = 0; for (i = 0; i < MAX_SERVICES; i++)
record->ntp_tokens = max_ntp_tokens; record->hits[i] = 0;
record->cmd_tokens = max_cmd_tokens; for (i = 0; i < MAX_SERVICES; i++)
record->ntp_rate = record->cmd_rate = INVALID_RATE; record->drops[i] = 0;
for (i = 0; i < MAX_SERVICES; i++)
record->tokens[i] = max_tokens[i];
for (i = 0; i < MAX_SERVICES; i++)
record->rate[i] = INVALID_RATE;
record->ntp_timeout_rate = INVALID_RATE; record->ntp_timeout_rate = INVALID_RATE;
record->flags = 0; record->drop_flags = 0;
UTI_ZeroNtp64(&record->ntp_rx_ts); UTI_ZeroNtp64(&record->ntp_rx_ts);
UTI_ZeroNtp64(&record->ntp_tx_ts); UTI_ZeroNtp64(&record->ntp_tx_ts);
@@ -306,31 +316,43 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
void void
CLG_Initialise(void) CLG_Initialise(void)
{ {
int interval, burst, leak_rate; int i, interval, burst, lrate;
max_ntp_tokens = max_cmd_tokens = 0; for (i = 0; i < MAX_SERVICES; i++) {
ntp_tokens_per_packet = cmd_tokens_per_packet = 0; max_tokens[i] = 0;
ntp_token_shift = cmd_token_shift = 0; tokens_per_hit[i] = 0;
ntp_leak_rate = cmd_leak_rate = 0; token_shift[i] = 0;
ntp_limit_interval = MIN_LIMIT_INTERVAL; leak_rate[i] = 0;
limit_interval[i] = MIN_LIMIT_INTERVAL;
if (CNF_GetNTPRateLimit(&interval, &burst, &leak_rate)) { switch (i) {
set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet, case CLG_NTP:
&ntp_token_shift); if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate))
ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE); continue;
ntp_limit_interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL); break;
} case CLG_NTSKE:
if (!CNF_GetNtsRateLimit(&interval, &burst, &lrate))
continue;
break;
case CLG_CMDMON:
if (!CNF_GetCommandRateLimit(&interval, &burst, &lrate))
continue;
break;
default:
assert(0);
}
if (CNF_GetCommandRateLimit(&interval, &burst, &leak_rate)) { set_bucket_params(interval, burst, &max_tokens[i], &tokens_per_hit[i], &token_shift[i]);
set_bucket_params(interval, burst, &max_cmd_tokens, &cmd_tokens_per_packet, leak_rate[i] = CLAMP(MIN_LEAK_RATE, lrate, MAX_LEAK_RATE);
&cmd_token_shift); limit_interval[i] = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
cmd_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
} }
active = !CNF_GetNoClientLog(); active = !CNF_GetNoClientLog();
if (!active) { if (!active) {
if (ntp_leak_rate || cmd_leak_rate) for (i = 0; i < MAX_SERVICES; i++) {
LOG_FATAL("ratelimit cannot be used with noclientlog"); if (leak_rate[i] != 0)
LOG_FATAL("Rate limiting cannot be enabled with noclientlog");
}
return; return;
} }
@@ -339,6 +361,7 @@ CLG_Initialise(void)
table where two copies exist at the same time. */ table where two copies exist at the same time. */
max_slots = CNF_GetClientLogLimit() / (sizeof (Record) * SLOT_SIZE * 3 / 2); max_slots = CNF_GetClientLogLimit() / (sizeof (Record) * SLOT_SIZE * 3 / 2);
max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS); max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS);
DEBUG_LOG("Max records %u", 1U << ((int)round(log(max_slots) / log(2)) + SLOT_BITS));
slots = 0; slots = 0;
records = NULL; records = NULL;
@@ -380,30 +403,33 @@ get_ts_from_timespec(struct timespec *ts)
/* ================================================== */ /* ================================================== */
static void static void
update_record(struct timespec *now, uint32_t *last_hit, uint32_t *hits, update_record(CLG_Service service, Record *record, struct timespec *now)
uint16_t *tokens, uint32_t max_tokens, int token_shift, int8_t *rate)
{ {
uint32_t interval, now_ts, prev_hit, new_tokens; uint32_t interval, now_ts, prev_hit, tokens;
int interval2; int interval2, tshift, mtokens;
int8_t *rate;
now_ts = get_ts_from_timespec(now); now_ts = get_ts_from_timespec(now);
prev_hit = *last_hit; prev_hit = record->last_hit[service];
*last_hit = now_ts; record->last_hit[service] = now_ts;
(*hits)++; record->hits[service]++;
interval = now_ts - prev_hit; interval = now_ts - prev_hit;
if (prev_hit == INVALID_TS || (int32_t)interval < 0) if (prev_hit == INVALID_TS || (int32_t)interval < 0)
return; return;
if (token_shift >= 0) tshift = token_shift[service];
new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift); mtokens = max_tokens[service];
else if (now_ts - prev_hit > max_tokens)
new_tokens = max_tokens; if (tshift >= 0)
tokens = (now_ts >> tshift) - (prev_hit >> tshift);
else if (now_ts - prev_hit > mtokens)
tokens = mtokens;
else else
new_tokens = (now_ts - prev_hit) << -token_shift; tokens = (now_ts - prev_hit) << -tshift;
*tokens = MIN(*tokens + new_tokens, max_tokens); record->tokens[service] = MIN(record->tokens[service] + tokens, mtokens);
/* Convert the interval to scaled and rounded log2 */ /* Convert the interval to scaled and rounded log2 */
if (interval) { if (interval) {
@@ -418,6 +444,11 @@ update_record(struct timespec *now, uint32_t *last_hit, uint32_t *hits,
interval2 = -RATE_SCALE * (TS_FRAC + 1); interval2 = -RATE_SCALE * (TS_FRAC + 1);
} }
/* For the NTP service, update one of the two rates depending on whether
the previous request of the client had a reply or it timed out */
rate = service == CLG_NTP && record->drop_flags & (1U << service) ?
&record->ntp_timeout_rate : &record->rate[service];
/* Update the rate in a rough approximation of exponential moving average */ /* Update the rate in a rough approximation of exponential moving average */
if (*rate == INVALID_RATE) { if (*rate == INVALID_RATE) {
*rate = -interval2; *rate = -interval2;
@@ -457,50 +488,33 @@ CLG_GetClientIndex(IPAddr *client)
/* ================================================== */ /* ================================================== */
int static void
CLG_LogNTPAccess(IPAddr *client, struct timespec *now) check_service_number(CLG_Service service)
{ {
Record *record; assert(service >= 0 && service <= MAX_SERVICES);
total_ntp_hits++;
record = get_record(client);
if (record == NULL)
return -1;
/* Update one of the two rates depending on whether the previous request
of the client had a reply or it timed out */
update_record(now, &record->last_ntp_hit, &record->ntp_hits,
&record->ntp_tokens, max_ntp_tokens, ntp_token_shift,
record->flags & FLAG_NTP_DROPPED ?
&record->ntp_timeout_rate : &record->ntp_rate);
DEBUG_LOG("NTP hits %"PRIu32" rate %d trate %d tokens %d",
record->ntp_hits, record->ntp_rate, record->ntp_timeout_rate,
record->ntp_tokens);
return get_index(record);
} }
/* ================================================== */ /* ================================================== */
int int
CLG_LogCommandAccess(IPAddr *client, struct timespec *now) CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now)
{ {
Record *record; Record *record;
total_cmd_hits++; check_service_number(service);
total_hits[service]++;
record = get_record(client); record = get_record(client);
if (record == NULL) if (record == NULL)
return -1; return -1;
update_record(now, &record->last_cmd_hit, &record->cmd_hits, update_record(service, record, now);
&record->cmd_tokens, max_cmd_tokens, cmd_token_shift,
&record->cmd_rate);
DEBUG_LOG("Cmd hits %"PRIu32" rate %d tokens %d", DEBUG_LOG("service %d hits %"PRIu32" rate %d trate %d tokens %d",
record->cmd_hits, record->cmd_rate, record->cmd_tokens); (int)service, record->hits[service], record->rate[service],
service == CLG_NTP ? record->ntp_timeout_rate : INVALID_RATE,
record->tokens[service]);
return get_index(record); return get_index(record);
} }
@@ -530,71 +544,53 @@ limit_response_random(int leak_rate)
/* ================================================== */ /* ================================================== */
int int
CLG_LimitNTPResponseRate(int index) CLG_LimitServiceRate(CLG_Service service, int index)
{ {
Record *record; Record *record;
int drop; int drop;
if (!ntp_tokens_per_packet) check_service_number(service);
if (tokens_per_hit[service] == 0)
return 0; return 0;
record = ARR_GetElement(records, index); record = ARR_GetElement(records, index);
record->flags &= ~FLAG_NTP_DROPPED; record->drop_flags &= ~(1U << service);
if (record->ntp_tokens >= ntp_tokens_per_packet) { if (record->tokens[service] >= tokens_per_hit[service]) {
record->ntp_tokens -= ntp_tokens_per_packet; record->tokens[service] -= tokens_per_hit[service];
return 0; return 0;
} }
drop = limit_response_random(ntp_leak_rate); drop = limit_response_random(leak_rate[service]);
/* Poorly implemented clients may send new requests at even a higher rate /* Poorly implemented NTP clients can send requests at a higher rate
when they are not getting replies. If the request rate seems to be more when they are not getting replies. If the request rate seems to be more
than twice as much as when replies are sent, give up on rate limiting to than twice as much as when replies are sent, give up on rate limiting to
reduce the amount of traffic. Invert the sense of the leak to respond to reduce the amount of traffic. Invert the sense of the leak to respond to
most of the requests, but still keep the estimated rate updated. */ most of the requests, but still keep the estimated rate updated. */
if (record->ntp_timeout_rate != INVALID_RATE && if (service == CLG_NTP && record->ntp_timeout_rate != INVALID_RATE &&
record->ntp_timeout_rate > record->ntp_rate + RATE_SCALE) record->ntp_timeout_rate > record->rate[service] + RATE_SCALE)
drop = !drop; drop = !drop;
if (!drop) { if (!drop) {
record->ntp_tokens = 0; record->tokens[service] = 0;
return 0; return 0;
} }
record->flags |= FLAG_NTP_DROPPED; record->drop_flags |= 1U << service;
record->ntp_drops++; record->drops[service]++;
total_ntp_drops++; total_drops[service]++;
return 1; return 1;
} }
/* ================================================== */ /* ================================================== */
int void
CLG_LimitCommandResponseRate(int index) CLG_LogAuthNtpRequest(void)
{ {
Record *record; total_ntp_auth_hits++;
if (!cmd_tokens_per_packet)
return 0;
record = ARR_GetElement(records, index);
if (record->cmd_tokens >= cmd_tokens_per_packet) {
record->cmd_tokens -= cmd_tokens_per_packet;
return 0;
}
if (!limit_response_random(cmd_leak_rate)) {
record->cmd_tokens = 0;
return 0;
}
record->cmd_drops++;
total_cmd_drops++;
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -614,7 +610,7 @@ void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
int int
CLG_GetNtpMinPoll(void) CLG_GetNtpMinPoll(void)
{ {
return ntp_limit_interval; return limit_interval[CLG_NTP];
} }
/* ================================================== */ /* ================================================== */
@@ -653,10 +649,12 @@ static uint32_t get_last_ago(uint32_t x, uint32_t y)
/* ================================================== */ /* ================================================== */
int int
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now) CLG_GetClientAccessReportByIndex(int index, int reset, uint32_t min_hits,
RPT_ClientAccessByIndex_Report *report, struct timespec *now)
{ {
Record *record; Record *record;
uint32_t now_ts; uint32_t now_ts;
int i, r;
if (!active || index < 0 || index >= ARR_GetSize(records)) if (!active || index < 0 || index >= ARR_GetSize(records))
return 0; return 0;
@@ -666,20 +664,44 @@ CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *repo
if (record->ip_addr.family == IPADDR_UNSPEC) if (record->ip_addr.family == IPADDR_UNSPEC)
return 0; return 0;
now_ts = get_ts_from_timespec(now); if (min_hits == 0) {
r = 1;
} else {
for (i = r = 0; i < MAX_SERVICES; i++) {
if (record->hits[i] >= min_hits) {
r = 1;
break;
}
}
}
report->ip_addr = record->ip_addr; if (r) {
report->ntp_hits = record->ntp_hits; now_ts = get_ts_from_timespec(now);
report->cmd_hits = record->cmd_hits;
report->ntp_drops = record->ntp_drops;
report->cmd_drops = record->cmd_drops;
report->ntp_interval = get_interval(record->ntp_rate);
report->cmd_interval = get_interval(record->cmd_rate);
report->ntp_timeout_interval = get_interval(record->ntp_timeout_rate);
report->last_ntp_hit_ago = get_last_ago(now_ts, record->last_ntp_hit);
report->last_cmd_hit_ago = get_last_ago(now_ts, record->last_cmd_hit);
return 1; report->ip_addr = record->ip_addr;
report->ntp_hits = record->hits[CLG_NTP];
report->nke_hits = record->hits[CLG_NTSKE];
report->cmd_hits = record->hits[CLG_CMDMON];
report->ntp_drops = record->drops[CLG_NTP];
report->nke_drops = record->drops[CLG_NTSKE];
report->cmd_drops = record->drops[CLG_CMDMON];
report->ntp_interval = get_interval(record->rate[CLG_NTP]);
report->nke_interval = get_interval(record->rate[CLG_NTSKE]);
report->cmd_interval = get_interval(record->rate[CLG_CMDMON]);
report->ntp_timeout_interval = get_interval(record->ntp_timeout_rate);
report->last_ntp_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_NTP]);
report->last_nke_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_NTSKE]);
report->last_cmd_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_CMDMON]);
}
if (reset) {
for (i = 0; i < MAX_SERVICES; i++) {
record->hits[i] = 0;
record->drops[i] = 0;
}
}
return r;
} }
/* ================================================== */ /* ================================================== */
@@ -687,9 +709,12 @@ CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *repo
void void
CLG_GetServerStatsReport(RPT_ServerStatsReport *report) CLG_GetServerStatsReport(RPT_ServerStatsReport *report)
{ {
report->ntp_hits = total_ntp_hits; report->ntp_hits = total_hits[CLG_NTP];
report->cmd_hits = total_cmd_hits; report->nke_hits = total_hits[CLG_NTSKE];
report->ntp_drops = total_ntp_drops; report->cmd_hits = total_hits[CLG_CMDMON];
report->cmd_drops = total_cmd_drops; report->ntp_drops = total_drops[CLG_NTP];
report->nke_drops = total_drops[CLG_NTSKE];
report->cmd_drops = total_drops[CLG_CMDMON];
report->log_drops = total_record_drops; report->log_drops = total_record_drops;
report->ntp_auth_hits = total_ntp_auth_hits;
} }

View File

@@ -31,20 +31,27 @@
#include "sysincl.h" #include "sysincl.h"
#include "reports.h" #include "reports.h"
typedef enum {
CLG_NTP = 0,
CLG_NTSKE,
CLG_CMDMON,
} CLG_Service;
extern void CLG_Initialise(void); extern void CLG_Initialise(void);
extern void CLG_Finalise(void); extern void CLG_Finalise(void);
extern int CLG_GetClientIndex(IPAddr *client); extern int CLG_GetClientIndex(IPAddr *client);
extern int CLG_LogNTPAccess(IPAddr *client, struct timespec *now); extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now); extern int CLG_LimitServiceRate(CLG_Service service, int index);
extern int CLG_LimitNTPResponseRate(int index); extern void CLG_LogAuthNtpRequest(void);
extern int CLG_LimitCommandResponseRate(int index);
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts); extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
extern int CLG_GetNtpMinPoll(void); extern int CLG_GetNtpMinPoll(void);
/* And some reporting functions, for use by chronyc. */ /* And some reporting functions, for use by chronyc. */
extern int CLG_GetNumberOfIndices(void); extern int CLG_GetNumberOfIndices(void);
extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now); extern int CLG_GetClientAccessReportByIndex(int index, int reset, uint32_t min_hits,
RPT_ClientAccessByIndex_Report *report,
struct timespec *now);
extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report); extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report);
#endif /* GOT_CLIENTLOG_H */ #endif /* GOT_CLIENTLOG_H */

17
cmac.h
View File

@@ -28,13 +28,20 @@
#ifndef GOT_CMAC_H #ifndef GOT_CMAC_H
#define GOT_CMAC_H #define GOT_CMAC_H
/* Avoid overlapping with the hash enumeration */
typedef enum {
CMC_INVALID = 0,
CMC_AES128 = 13,
CMC_AES256 = 14,
} CMC_Algorithm;
typedef struct CMC_Instance_Record *CMC_Instance; typedef struct CMC_Instance_Record *CMC_Instance;
extern unsigned int CMC_GetKeyLength(const char *cipher); extern int CMC_GetKeyLength(CMC_Algorithm algorithm);
extern CMC_Instance CMC_CreateInstance(const char *cipher, const unsigned char *key, extern CMC_Instance CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key,
unsigned int length); int length);
extern unsigned int CMC_Hash(CMC_Instance inst, const unsigned char *in, unsigned int in_len, extern int CMC_Hash(CMC_Instance inst, const void *in, int in_len,
unsigned char *out, unsigned int out_len); unsigned char *out, int out_len);
extern void CMC_DestroyInstance(CMC_Instance inst); extern void CMC_DestroyInstance(CMC_Instance inst);
#endif #endif

View File

@@ -44,12 +44,12 @@ struct CMC_Instance_Record {
/* ================================================== */ /* ================================================== */
unsigned int int
CMC_GetKeyLength(const char *cipher) CMC_GetKeyLength(CMC_Algorithm algorithm)
{ {
if (strcmp(cipher, "AES128") == 0) if (algorithm == CMC_AES128)
return AES128_KEY_SIZE; return AES128_KEY_SIZE;
else if (strcmp(cipher, "AES256") == 0) else if (algorithm == CMC_AES256)
return AES256_KEY_SIZE; return AES256_KEY_SIZE;
return 0; return 0;
} }
@@ -57,11 +57,11 @@ CMC_GetKeyLength(const char *cipher)
/* ================================================== */ /* ================================================== */
CMC_Instance CMC_Instance
CMC_CreateInstance(const char *cipher, const unsigned char *key, unsigned int length) CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, int length)
{ {
CMC_Instance inst; CMC_Instance inst;
if (length == 0 || length != CMC_GetKeyLength(cipher)) if (length <= 0 || length != CMC_GetKeyLength(algorithm))
return NULL; return NULL;
inst = MallocNew(struct CMC_Instance_Record); inst = MallocNew(struct CMC_Instance_Record);
@@ -83,10 +83,12 @@ CMC_CreateInstance(const char *cipher, const unsigned char *key, unsigned int le
/* ================================================== */ /* ================================================== */
unsigned int int
CMC_Hash(CMC_Instance inst, const unsigned char *in, unsigned int in_len, CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len)
unsigned char *out, unsigned int out_len)
{ {
if (in_len < 0 || out_len < 0)
return 0;
if (out_len > CMAC128_DIGEST_SIZE) if (out_len > CMAC128_DIGEST_SIZE)
out_len = CMAC128_DIGEST_SIZE; out_len = CMAC128_DIGEST_SIZE;

231
cmdmon.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016, 2018 * Copyright (C) Miroslav Lichvar 2009-2016, 2018-2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -135,7 +135,11 @@ static const char permissions[] = {
PERMIT_AUTH, /* ONOFFLINE */ PERMIT_AUTH, /* ONOFFLINE */
PERMIT_AUTH, /* ADD_SOURCE */ PERMIT_AUTH, /* ADD_SOURCE */
PERMIT_OPEN, /* NTP_SOURCE_NAME */ PERMIT_OPEN, /* NTP_SOURCE_NAME */
PERMIT_AUTH, /* RESET */ PERMIT_AUTH, /* RESET_SOURCES */
PERMIT_AUTH, /* AUTH_DATA */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX3 */
PERMIT_AUTH, /* SELECT_DATA */
PERMIT_AUTH, /* RELOAD_SOURCES */
}; };
/* ================================================== */ /* ================================================== */
@@ -153,23 +157,22 @@ static void read_from_cmd_socket(int sock_fd, int event, void *anything);
static int static int
open_socket(int family) open_socket(int family)
{ {
const char *local_path, *iface;
IPSockAddr local_addr; IPSockAddr local_addr;
const char *local_path;
int sock_fd, port; int sock_fd, port;
switch (family) { switch (family) {
case IPADDR_INET4: case IPADDR_INET4:
case IPADDR_INET6: case IPADDR_INET6:
port = CNF_GetCommandPort(); port = CNF_GetCommandPort();
if (port == 0 || !SCK_IsFamilySupported(family)) if (port == 0 || !SCK_IsIpFamilyEnabled(family))
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
CNF_GetBindCommandAddress(family, &local_addr.ip_addr); CNF_GetBindCommandAddress(family, &local_addr.ip_addr);
if (local_addr.ip_addr.family != family)
SCK_GetLoopbackIPAddress(family, &local_addr.ip_addr);
local_addr.port = port; local_addr.port = port;
iface = CNF_GetBindCommandInterface();
sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, SCK_FLAG_RX_DEST_ADDR); sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, iface, SCK_FLAG_RX_DEST_ADDR);
if (sock_fd < 0) { if (sock_fd < 0) {
LOG(LOGS_ERR, "Could not open command socket on %s", LOG(LOGS_ERR, "Could not open command socket on %s",
UTI_IPSockAddrToString(&local_addr)); UTI_IPSockAddrToString(&local_addr));
@@ -233,22 +236,17 @@ do_size_checks(void)
/* ================================================== */ /* ================================================== */
void void
CAM_Initialise(int family) CAM_Initialise(void)
{ {
assert(!initialised); assert(!initialised);
assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES); assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
do_size_checks(); do_size_checks();
initialised = 1; initialised = 1;
sock_fdu = INVALID_SOCK_FD; sock_fdu = INVALID_SOCK_FD;
sock_fd4 = INVALID_SOCK_FD; sock_fd4 = open_socket(IPADDR_INET4);
sock_fd6 = INVALID_SOCK_FD; sock_fd6 = open_socket(IPADDR_INET6);
if (family == IPADDR_UNSPEC || family == IPADDR_INET4)
sock_fd4 = open_socket(IPADDR_INET4);
if (family == IPADDR_UNSPEC || family == IPADDR_INET6)
sock_fd6 = open_socket(IPADDR_INET6);
access_auth_table = ADF_CreateTable(); access_auth_table = ADF_CreateTable();
} }
@@ -289,17 +287,28 @@ CAM_OpenUnixSocket(void)
{ {
/* This is separated from CAM_Initialise() as it needs to be called when /* This is separated from CAM_Initialise() as it needs to be called when
the process has already dropped the root privileges */ the process has already dropped the root privileges */
if (CNF_GetBindCommandPath()[0]) if (CNF_GetBindCommandPath())
sock_fdu = open_socket(IPADDR_UNSPEC); sock_fdu = open_socket(IPADDR_UNSPEC);
} }
/* ================================================== */ /* ================================================== */
static void static void
transmit_reply(int sock_fd, SCK_Message *message) transmit_reply(int sock_fd, int request_length, SCK_Message *message)
{ {
message->length = PKL_ReplyLength((CMD_Reply *)message->data); message->length = PKL_ReplyLength((CMD_Reply *)message->data);
if (request_length < message->length) {
DEBUG_LOG("Response longer than request req_len=%d res_len=%d",
request_length, message->length);
return;
}
/* Don't require responses to non-link-local addresses to use the same
interface */
if (!SCK_IsLinkLocalIPAddress(&message->remote_addr.ip.ip_addr))
message->if_index = INVALID_IF_INDEX;
if (!SCK_SendMessage(sock_fd, message, 0)) if (!SCK_SendMessage(sock_fd, message, 0))
return; return;
} }
@@ -569,11 +578,8 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.source_data.stratum = htons(report.stratum); tx_message->data.source_data.stratum = htons(report.stratum);
tx_message->data.source_data.poll = htons(report.poll); tx_message->data.source_data.poll = htons(report.poll);
switch (report.state) { switch (report.state) {
case RPT_SYNC: case RPT_NONSELECTABLE:
tx_message->data.source_data.state = htons(RPY_SD_ST_SYNC); tx_message->data.source_data.state = htons(RPY_SD_ST_NONSELECTABLE);
break;
case RPT_UNREACH:
tx_message->data.source_data.state = htons(RPY_SD_ST_UNREACH);
break; break;
case RPT_FALSETICKER: case RPT_FALSETICKER:
tx_message->data.source_data.state = htons(RPY_SD_ST_FALSETICKER); tx_message->data.source_data.state = htons(RPY_SD_ST_FALSETICKER);
@@ -581,11 +587,14 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
case RPT_JITTERY: case RPT_JITTERY:
tx_message->data.source_data.state = htons(RPY_SD_ST_JITTERY); tx_message->data.source_data.state = htons(RPY_SD_ST_JITTERY);
break; break;
case RPT_CANDIDATE: case RPT_SELECTABLE:
tx_message->data.source_data.state = htons(RPY_SD_ST_CANDIDATE); tx_message->data.source_data.state = htons(RPY_SD_ST_SELECTABLE);
break; break;
case RPT_OUTLIER: case RPT_UNSELECTED:
tx_message->data.source_data.state = htons(RPY_SD_ST_OUTLIER); tx_message->data.source_data.state = htons(RPY_SD_ST_UNSELECTED);
break;
case RPT_SELECTED:
tx_message->data.source_data.state = htons(RPY_SD_ST_SELECTED);
break; break;
} }
switch (report.mode) { switch (report.mode) {
@@ -599,11 +608,7 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.source_data.mode = htons(RPY_SD_MD_REF); tx_message->data.source_data.mode = htons(RPY_SD_MD_REF);
break; break;
} }
tx_message->data.source_data.flags = tx_message->data.source_data.flags = htons(0);
htons((report.sel_options & SRC_SELECT_PREFER ? RPY_SD_FLAG_PREFER : 0) |
(report.sel_options & SRC_SELECT_NOSELECT ? RPY_SD_FLAG_NOSELECT : 0) |
(report.sel_options & SRC_SELECT_TRUST ? RPY_SD_FLAG_TRUST : 0) |
(report.sel_options & SRC_SELECT_REQUIRE ? RPY_SD_FLAG_REQUIRE : 0));
tx_message->data.source_data.reachability = htons(report.reachability); tx_message->data.source_data.reachability = htons(report.reachability);
tx_message->data.source_data.since_sample = htonl(report.latest_meas_ago); tx_message->data.source_data.since_sample = htonl(report.latest_meas_ago);
tx_message->data.source_data.orig_latest_meas = UTI_FloatHostToNetwork(report.orig_latest_meas); tx_message->data.source_data.orig_latest_meas = UTI_FloatHostToNetwork(report.orig_latest_meas);
@@ -716,7 +721,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
return; return;
} }
port = (unsigned short)(ntohl(rx_message->data.ntp_source.port)); port = ntohl(rx_message->data.ntp_source.port);
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll); params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll); params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll); params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll);
@@ -751,7 +756,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) | (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0); (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
status = NSR_AddSourceByName(name, port, pool, type, &params); status = NSR_AddSourceByName(name, port, pool, type, &params, NULL);
switch (status) { switch (status) {
case NSR_Success: case NSR_Success:
break; break;
@@ -780,13 +785,12 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
static void static void
handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message) handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
NTP_Remote_Address rem_addr;
NSR_Status status; NSR_Status status;
IPAddr ip_addr;
UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &rem_addr.ip_addr); UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &ip_addr);
rem_addr.port = 0;
status = NSR_RemoveSource(&rem_addr); status = NSR_RemoveSource(&ip_addr);
switch (status) { switch (status) {
case NSR_Success: case NSR_Success:
break; break;
@@ -1000,7 +1004,7 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ClientAccessByIndex_Report report; RPT_ClientAccessByIndex_Report report;
RPY_ClientAccesses_Client *client; RPY_ClientAccesses_Client *client;
int n_indices; int n_indices;
uint32_t i, j, req_first_index, req_n_clients; uint32_t i, j, req_first_index, req_n_clients, req_min_hits, req_reset;
struct timespec now; struct timespec now;
SCH_GetLastEventTime(&now, NULL, NULL); SCH_GetLastEventTime(&now, NULL, NULL);
@@ -1009,6 +1013,8 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
req_n_clients = ntohl(rx_message->data.client_accesses_by_index.n_clients); req_n_clients = ntohl(rx_message->data.client_accesses_by_index.n_clients);
if (req_n_clients > MAX_CLIENT_ACCESSES) if (req_n_clients > MAX_CLIENT_ACCESSES)
req_n_clients = MAX_CLIENT_ACCESSES; req_n_clients = MAX_CLIENT_ACCESSES;
req_min_hits = ntohl(rx_message->data.client_accesses_by_index.min_hits);
req_reset = ntohl(rx_message->data.client_accesses_by_index.reset);
n_indices = CLG_GetNumberOfIndices(); n_indices = CLG_GetNumberOfIndices();
if (n_indices < 0) { if (n_indices < 0) {
@@ -1016,24 +1022,28 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
return; return;
} }
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX2); tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX3);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices); tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices);
for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) { for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) {
if (!CLG_GetClientAccessReportByIndex(i, &report, &now)) if (!CLG_GetClientAccessReportByIndex(i, req_reset, req_min_hits, &report, &now))
continue; continue;
client = &tx_message->data.client_accesses_by_index.clients[j++]; client = &tx_message->data.client_accesses_by_index.clients[j++];
UTI_IPHostToNetwork(&report.ip_addr, &client->ip); UTI_IPHostToNetwork(&report.ip_addr, &client->ip);
client->ntp_hits = htonl(report.ntp_hits); client->ntp_hits = htonl(report.ntp_hits);
client->nke_hits = htonl(report.nke_hits);
client->cmd_hits = htonl(report.cmd_hits); client->cmd_hits = htonl(report.cmd_hits);
client->ntp_drops = htonl(report.ntp_drops); client->ntp_drops = htonl(report.ntp_drops);
client->nke_drops = htonl(report.nke_drops);
client->cmd_drops = htonl(report.cmd_drops); client->cmd_drops = htonl(report.cmd_drops);
client->ntp_interval = report.ntp_interval; client->ntp_interval = report.ntp_interval;
client->nke_interval = report.nke_interval;
client->cmd_interval = report.cmd_interval; client->cmd_interval = report.cmd_interval;
client->ntp_timeout_interval = report.ntp_timeout_interval; client->ntp_timeout_interval = report.ntp_timeout_interval;
client->last_ntp_hit_ago = htonl(report.last_ntp_hit_ago); client->last_ntp_hit_ago = htonl(report.last_ntp_hit_ago);
client->last_nke_hit_ago = htonl(report.last_nke_hit_ago);
client->last_cmd_hit_ago = htonl(report.last_cmd_hit_ago); client->last_cmd_hit_ago = htonl(report.last_cmd_hit_ago);
} }
@@ -1135,12 +1145,15 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ServerStatsReport report; RPT_ServerStatsReport report;
CLG_GetServerStatsReport(&report); CLG_GetServerStatsReport(&report);
tx_message->reply = htons(RPY_SERVER_STATS); tx_message->reply = htons(RPY_SERVER_STATS2);
tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits); tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits);
tx_message->data.server_stats.nke_hits = htonl(report.nke_hits);
tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits); tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits);
tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops); tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops);
tx_message->data.server_stats.nke_drops = htonl(report.nke_drops);
tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops); tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops);
tx_message->data.server_stats.log_drops = htonl(report.log_drops); tx_message->data.server_stats.log_drops = htonl(report.log_drops);
tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits);
} }
/* ================================================== */ /* ================================================== */
@@ -1204,7 +1217,7 @@ handle_ntp_source_name(CMD_Request *rx_message, CMD_Reply *tx_message)
IPAddr addr; IPAddr addr;
char *name; char *name;
UTI_IPNetworkToHost(&rx_message->data.ntp_data.ip_addr, &addr); UTI_IPNetworkToHost(&rx_message->data.ntp_source_name.ip_addr, &addr);
name = NSR_GetName(&addr); name = NSR_GetName(&addr);
if (!name) { if (!name) {
@@ -1226,7 +1239,15 @@ handle_ntp_source_name(CMD_Request *rx_message, CMD_Reply *tx_message)
/* ================================================== */ /* ================================================== */
static void static void
handle_reset(CMD_Request *rx_message, CMD_Reply *tx_message) handle_reload_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
{
CNF_ReloadSources();
}
/* ================================================== */
static void
handle_reset_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
struct timespec cooked_now, now; struct timespec cooked_now, now;
@@ -1235,6 +1256,84 @@ handle_reset(CMD_Request *rx_message, CMD_Reply *tx_message)
LCL_NotifyExternalTimeStep(&now, &cooked_now, 0.0, 0.0); LCL_NotifyExternalTimeStep(&now, &cooked_now, 0.0, 0.0);
} }
/* ================================================== */
static void
handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_AuthReport report;
IPAddr ip_addr;
UTI_IPNetworkToHost(&rx_message->data.auth_data.ip_addr, &ip_addr);
if (!NSR_GetAuthReport(&ip_addr, &report)) {
tx_message->status = htons(STT_NOSUCHSOURCE);
return;
}
tx_message->reply = htons(RPY_AUTH_DATA);
switch (report.mode) {
case NTP_AUTH_NONE:
tx_message->data.auth_data.mode = htons(RPY_AD_MD_NONE);
break;
case NTP_AUTH_SYMMETRIC:
tx_message->data.auth_data.mode = htons(RPY_AD_MD_SYMMETRIC);
break;
case NTP_AUTH_NTS:
tx_message->data.auth_data.mode = htons(RPY_AD_MD_NTS);
break;
default:
break;
}
tx_message->data.auth_data.key_type = htons(report.key_type);
tx_message->data.auth_data.key_id = htonl(report.key_id);
tx_message->data.auth_data.key_length = htons(report.key_length);
tx_message->data.auth_data.ke_attempts = htons(report.ke_attempts);
tx_message->data.auth_data.last_ke_ago = htonl(report.last_ke_ago);
tx_message->data.auth_data.cookies = htons(report.cookies);
tx_message->data.auth_data.cookie_length = htons(report.cookie_length);
tx_message->data.auth_data.nak = htons(report.nak);
}
/* ================================================== */
static uint16_t
convert_select_options(int options)
{
return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
(options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
(options & SRC_SELECT_TRUST ? RPY_SD_OPTION_TRUST : 0) |
(options & SRC_SELECT_REQUIRE ? RPY_SD_OPTION_REQUIRE : 0);
}
/* ================================================== */
static void
handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_SelectReport report;
if (!SRC_GetSelectReport(ntohl(rx_message->data.select_data.index), &report)) {
tx_message->status = htons(STT_NOSUCHSOURCE);
return;
}
tx_message->reply = htons(RPY_SELECT_DATA);
tx_message->data.select_data.ref_id = htonl(report.ref_id);
UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.select_data.ip_addr);
tx_message->data.select_data.state_char = report.state_char;
tx_message->data.select_data.authentication = report.authentication;
tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options));
tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options));
tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);
tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score);
tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit);
}
/* ================================================== */ /* ================================================== */
/* Read a packet and process it */ /* Read a packet and process it */
@@ -1247,7 +1346,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
IPAddr loopback_addr, remote_ip; IPAddr loopback_addr, remote_ip;
int read_length, expected_length; int read_length, expected_length;
int localhost, allowed, log_index; int localhost, allowed, log_index;
unsigned short rx_command; uint16_t rx_command;
struct timespec now, cooked_now; struct timespec now, cooked_now;
sck_message = SCK_ReceiveMessage(sock_fd, 0); sck_message = SCK_ReceiveMessage(sock_fd, 0);
@@ -1305,11 +1404,11 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
return; return;
} }
log_index = CLG_LogCommandAccess(&remote_ip, &cooked_now); log_index = CLG_LogServiceAccess(CLG_CMDMON, &remote_ip, &cooked_now);
/* Don't reply to all requests from hosts other than localhost if the rate /* Don't reply to all requests from hosts other than localhost if the rate
is excessive */ is excessive */
if (!localhost && log_index >= 0 && CLG_LimitCommandResponseRate(log_index)) { if (!localhost && log_index >= 0 && CLG_LimitServiceRate(CLG_CMDMON, log_index)) {
DEBUG_LOG("Command packet discarded to limit response rate"); DEBUG_LOG("Command packet discarded to limit response rate");
return; return;
} }
@@ -1334,7 +1433,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) { if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
tx_message.status = htons(STT_BADPKTVERSION); tx_message.status = htons(STT_BADPKTVERSION);
transmit_reply(sock_fd, sck_message); transmit_reply(sock_fd, read_length, sck_message);
} }
return; return;
} }
@@ -1344,7 +1443,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
DEBUG_LOG("Command packet has invalid command %d", rx_command); DEBUG_LOG("Command packet has invalid command %d", rx_command);
tx_message.status = htons(STT_INVALID); tx_message.status = htons(STT_INVALID);
transmit_reply(sock_fd, sck_message); transmit_reply(sock_fd, read_length, sck_message);
return; return;
} }
@@ -1353,7 +1452,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
expected_length); expected_length);
tx_message.status = htons(STT_BADPKTLENGTH); tx_message.status = htons(STT_BADPKTLENGTH);
transmit_reply(sock_fd, sck_message); transmit_reply(sock_fd, read_length, sck_message);
return; return;
} }
@@ -1553,7 +1652,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_cyclelogs(&rx_message, &tx_message); handle_cyclelogs(&rx_message, &tx_message);
break; break;
case REQ_CLIENT_ACCESSES_BY_INDEX2: case REQ_CLIENT_ACCESSES_BY_INDEX3:
handle_client_accesses_by_index(&rx_message, &tx_message); handle_client_accesses_by_index(&rx_message, &tx_message);
break; break;
@@ -1613,8 +1712,20 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_ntp_source_name(&rx_message, &tx_message); handle_ntp_source_name(&rx_message, &tx_message);
break; break;
case REQ_RESET: case REQ_RESET_SOURCES:
handle_reset(&rx_message, &tx_message); handle_reset_sources(&rx_message, &tx_message);
break;
case REQ_AUTH_DATA:
handle_auth_data(&rx_message, &tx_message);
break;
case REQ_SELECT_DATA:
handle_select_data(&rx_message, &tx_message);
break;
case REQ_RELOAD_SOURCES:
handle_reload_sources(&rx_message, &tx_message);
break; break;
default: default:
@@ -1628,19 +1739,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
} }
/* Transmit the response */ /* Transmit the response */
{ transmit_reply(sock_fd, read_length, sck_message);
/* Include a simple way to lose one message in three to test resend */
static int do_it=1;
if (do_it) {
transmit_reply(sock_fd, sck_message);
}
#if 0
do_it = ((do_it + 1) % 3);
#endif
}
} }
/* ================================================== */ /* ================================================== */

View File

@@ -29,7 +29,7 @@
#include "addressing.h" #include "addressing.h"
extern void CAM_Initialise(int family); extern void CAM_Initialise(void);
extern void CAM_Finalise(void); extern void CAM_Finalise(void);

View File

@@ -151,7 +151,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1) if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
return 0; return 0;
} else if (!strcasecmp(cmd, "port")) { } else if (!strcasecmp(cmd, "port")) {
if (sscanf(line, "%hu%n", &src->port, &n) != 1) if (sscanf(line, "%d%n", &src->port, &n) != 1)
return 0; return 0;
} else if (!strcasecmp(cmd, "polltarget")) { } else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)

View File

@@ -32,7 +32,7 @@
typedef struct { typedef struct {
char *name; char *name;
unsigned short port; int port;
SourceParameters params; SourceParameters params;
} CPS_NTP_Source; } CPS_NTP_Source;

491
conf.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2017 * Copyright (C) Miroslav Lichvar 2009-2017, 2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -33,8 +33,10 @@
#include "conf.h" #include "conf.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "nts_ke.h"
#include "refclock.h" #include "refclock.h"
#include "cmdmon.h" #include "cmdmon.h"
#include "socket.h"
#include "srcparams.h" #include "srcparams.h"
#include "logging.h" #include "logging.h"
#include "nameserv.h" #include "nameserv.h"
@@ -42,6 +44,12 @@
#include "cmdparse.h" #include "cmdparse.h"
#include "util.h" #include "util.h"
/* ================================================== */
#define MAX_LINE_LENGTH 2048
#define MAX_CONF_DIRS 10
#define MAX_INCLUDE_LEVEL 10
/* ================================================== */ /* ================================================== */
/* Forward prototypes */ /* Forward prototypes */
@@ -51,11 +59,13 @@ static int parse_double(char *line, double *result);
static int parse_null(char *line); static int parse_null(char *line);
static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow); static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow);
static void parse_authselectmode(char *);
static void parse_bindacqaddress(char *); static void parse_bindacqaddress(char *);
static void parse_bindaddress(char *); static void parse_bindaddress(char *);
static void parse_bindcmdaddress(char *); static void parse_bindcmdaddress(char *);
static void parse_broadcast(char *); static void parse_broadcast(char *);
static void parse_clientloglimit(char *); static void parse_clientloglimit(char *);
static void parse_confdir(char *);
static void parse_fallbackdrift(char *); static void parse_fallbackdrift(char *);
static void parse_hwtimestamp(char *); static void parse_hwtimestamp(char *);
static void parse_include(char *); static void parse_include(char *);
@@ -70,12 +80,14 @@ static void parse_ratelimit(char *line, int *enabled, int *interval,
int *burst, int *leak); int *burst, int *leak);
static void parse_refclock(char *); static void parse_refclock(char *);
static void parse_smoothtime(char *); static void parse_smoothtime(char *);
static void parse_source(char *line, NTP_Source_Type type, int pool); static void parse_source(char *line, char *type, int fatal);
static void parse_sourcedir(char *);
static void parse_tempcomp(char *); static void parse_tempcomp(char *);
/* ================================================== */ /* ================================================== */
/* Configuration variables */ /* Configuration variables */
static int print_config = 0;
static int restarted = 0; static int restarted = 0;
static char *rtc_device; static char *rtc_device;
static int acquisition_port = -1; static int acquisition_port = -1;
@@ -88,7 +100,9 @@ static double correction_time_ratio = 3.0;
static double max_clock_error = 1.0; /* in ppm */ static double max_clock_error = 1.0; /* in ppm */
static double max_drift = 500000.0; /* in ppm */ static double max_drift = 500000.0; /* in ppm */
static double max_slew_rate = 1e6 / 12.0; /* in ppm */ static double max_slew_rate = 1e6 / 12.0; /* in ppm */
static double clock_precision = 0.0; /* in seconds */
static SRC_AuthSelectMode authselect_mode = SRC_AUTHSELECT_MIX;
static double max_distance = 3.0; static double max_distance = 3.0;
static double max_jitter = 1.0; static double max_jitter = 1.0;
static double reselect_distance = 1e-4; static double reselect_distance = 1e-4;
@@ -105,8 +119,8 @@ static int do_log_rtc = 0;
static int do_log_refclocks = 0; static int do_log_refclocks = 0;
static int do_log_tempcomp = 0; static int do_log_tempcomp = 0;
static int log_banner = 32; static int log_banner = 32;
static char *logdir; static char *logdir = NULL;
static char *dumpdir; static char *dumpdir = NULL;
static int enable_local=0; static int enable_local=0;
static int local_stratum; static int local_stratum;
@@ -180,21 +194,33 @@ static IPAddr bind_acq_address4, bind_acq_address6;
the loopback address will be used */ the loopback address will be used */
static IPAddr bind_cmd_address4, bind_cmd_address6; static IPAddr bind_cmd_address4, bind_cmd_address6;
/* Interface names to bind the NTP server, NTP client, and command socket */
static char *bind_ntp_iface = NULL;
static char *bind_acq_iface = NULL;
static char *bind_cmd_iface = NULL;
/* Path to the Unix domain command socket. */ /* Path to the Unix domain command socket. */
static char *bind_cmd_path; static char *bind_cmd_path = NULL;
/* Differentiated Services Code Point (DSCP) in transmitted NTP packets */
static int ntp_dscp = 0;
/* Path to Samba (ntp_signd) socket. */ /* Path to Samba (ntp_signd) socket. */
static char *ntp_signd_socket = NULL; static char *ntp_signd_socket = NULL;
/* Filename to use for storing pid of running chronyd, to prevent multiple /* Filename to use for storing pid of running chronyd, to prevent multiple
* chronyds being started. */ * chronyds being started. */
static char *pidfile; static char *pidfile = NULL;
/* Rate limiting parameters */ /* Rate limiting parameters */
static int ntp_ratelimit_enabled = 0; static int ntp_ratelimit_enabled = 0;
static int ntp_ratelimit_interval = 3; static int ntp_ratelimit_interval = 3;
static int ntp_ratelimit_burst = 8; static int ntp_ratelimit_burst = 8;
static int ntp_ratelimit_leak = 2; static int ntp_ratelimit_leak = 2;
static int nts_ratelimit_enabled = 0;
static int nts_ratelimit_interval = 6;
static int nts_ratelimit_burst = 8;
static int nts_ratelimit_leak = 2;
static int cmd_ratelimit_enabled = 0; static int cmd_ratelimit_enabled = 0;
static int cmd_ratelimit_interval = -4; static int cmd_ratelimit_interval = -4;
static int cmd_ratelimit_burst = 8; static int cmd_ratelimit_burst = 8;
@@ -228,7 +254,7 @@ static char *nts_dump_dir = NULL;
static char *nts_ntp_server = NULL; static char *nts_ntp_server = NULL;
static char *nts_server_cert_file = NULL; static char *nts_server_cert_file = NULL;
static char *nts_server_key_file = NULL; static char *nts_server_key_file = NULL;
static int nts_server_port = 11443; static int nts_server_port = NKE_PORT;
static int nts_server_processes = 1; static int nts_server_processes = 1;
static int nts_server_connections = 100; static int nts_server_connections = 100;
static int nts_refresh = 2419200; /* 4 weeks */ static int nts_refresh = 2419200; /* 4 weeks */
@@ -252,6 +278,10 @@ typedef struct {
/* Array of NTP_Source */ /* Array of NTP_Source */
static ARR_Instance ntp_sources; static ARR_Instance ntp_sources;
/* Array of (char *) */
static ARR_Instance ntp_source_dirs;
/* Array of uint32_t corresponding to ntp_sources (for sourcedirs reload) */
static ARR_Instance ntp_source_ids;
/* Array of RefclockParameters */ /* Array of RefclockParameters */
static ARR_Instance refclock_sources; static ARR_Instance refclock_sources;
@@ -268,8 +298,7 @@ static ARR_Instance ntp_restrictions;
static ARR_Instance cmd_restrictions; static ARR_Instance cmd_restrictions;
typedef struct { typedef struct {
IPAddr addr; NTP_Remote_Address addr;
unsigned short port;
int interval; int interval;
} NTP_Broadcast_Destination; } NTP_Broadcast_Destination;
@@ -283,6 +312,8 @@ static int line_number;
static const char *processed_file; static const char *processed_file;
static const char *processed_command; static const char *processed_command;
static int include_level = 0;
/* ================================================== */ /* ================================================== */
static void static void
@@ -349,26 +380,31 @@ CNF_Initialise(int r, int client_only)
init_sources = ARR_CreateInstance(sizeof (IPAddr)); init_sources = ARR_CreateInstance(sizeof (IPAddr));
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source)); ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
ntp_source_dirs = ARR_CreateInstance(sizeof (char *));
ntp_source_ids = ARR_CreateInstance(sizeof (uint32_t));
refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters)); refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters));
broadcasts = ARR_CreateInstance(sizeof (NTP_Broadcast_Destination)); broadcasts = ARR_CreateInstance(sizeof (NTP_Broadcast_Destination));
ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny)); ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny)); cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
dumpdir = Strdup("");
logdir = Strdup("");
rtc_device = Strdup(DEFAULT_RTC_DEVICE); rtc_device = Strdup(DEFAULT_RTC_DEVICE);
hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE); hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE);
user = Strdup(DEFAULT_USER); user = Strdup(DEFAULT_USER);
if (client_only) { if (client_only) {
cmd_port = ntp_port = 0; cmd_port = ntp_port = 0;
bind_cmd_path = Strdup("");
pidfile = Strdup("");
} else { } else {
bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET); bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
pidfile = Strdup(DEFAULT_PID_FILE); pidfile = Strdup(DEFAULT_PID_FILE);
} }
SCK_GetAnyLocalIPAddress(IPADDR_INET4, &bind_address4);
SCK_GetAnyLocalIPAddress(IPADDR_INET6, &bind_address6);
SCK_GetAnyLocalIPAddress(IPADDR_INET4, &bind_acq_address4);
SCK_GetAnyLocalIPAddress(IPADDR_INET6, &bind_acq_address6);
SCK_GetLoopbackIPAddress(IPADDR_INET4, &bind_cmd_address4);
SCK_GetLoopbackIPAddress(IPADDR_INET6, &bind_cmd_address6);
} }
/* ================================================== */ /* ================================================== */
@@ -384,9 +420,13 @@ CNF_Finalise(void)
for (i = 0; i < ARR_GetSize(ntp_sources); i++) for (i = 0; i < ARR_GetSize(ntp_sources); i++)
Free(((NTP_Source *)ARR_GetElement(ntp_sources, i))->params.name); Free(((NTP_Source *)ARR_GetElement(ntp_sources, i))->params.name);
for (i = 0; i < ARR_GetSize(ntp_source_dirs); i++)
Free(*(char **)ARR_GetElement(ntp_source_dirs, i));
ARR_DestroyInstance(init_sources); ARR_DestroyInstance(init_sources);
ARR_DestroyInstance(ntp_sources); ARR_DestroyInstance(ntp_sources);
ARR_DestroyInstance(ntp_source_dirs);
ARR_DestroyInstance(ntp_source_ids);
ARR_DestroyInstance(refclock_sources); ARR_DestroyInstance(refclock_sources);
ARR_DestroyInstance(broadcasts); ARR_DestroyInstance(broadcasts);
@@ -399,6 +439,9 @@ CNF_Finalise(void)
Free(keys_file); Free(keys_file);
Free(leapsec_tz); Free(leapsec_tz);
Free(logdir); Free(logdir);
Free(bind_ntp_iface);
Free(bind_acq_iface);
Free(bind_cmd_iface);
Free(bind_cmd_path); Free(bind_cmd_path);
Free(ntp_signd_socket); Free(ntp_signd_socket);
Free(pidfile); Free(pidfile);
@@ -417,14 +460,26 @@ CNF_Finalise(void)
/* ================================================== */ /* ================================================== */
void
CNF_EnablePrint(void)
{
print_config = 1;
}
/* ================================================== */
/* Read the configuration file */ /* Read the configuration file */
void void
CNF_ReadFile(const char *filename) CNF_ReadFile(const char *filename)
{ {
FILE *in; FILE *in;
char line[2048]; char line[MAX_LINE_LENGTH + 1];
int i; int i;
include_level++;
if (include_level > MAX_INCLUDE_LEVEL)
LOG_FATAL("Maximum include level reached");
in = UTI_OpenFile(NULL, filename, NULL, 'R', 0); in = UTI_OpenFile(NULL, filename, NULL, 'R', 0);
for (i = 1; fgets(line, sizeof(line), in); i++) { for (i = 1; fgets(line, sizeof(line), in); i++) {
@@ -432,6 +487,8 @@ CNF_ReadFile(const char *filename)
} }
fclose(in); fclose(in);
include_level--;
} }
/* ================================================== */ /* ================================================== */
@@ -446,31 +503,50 @@ CNF_ParseLine(const char *filename, int number, char *line)
processed_file = filename; processed_file = filename;
line_number = number; line_number = number;
/* Detect truncated line */
if (strlen(line) >= MAX_LINE_LENGTH)
other_parse_error("String too long");
/* Remove extra white-space and comments */ /* Remove extra white-space and comments */
CPS_NormalizeLine(line); CPS_NormalizeLine(line);
/* Skip blank lines */ /* Skip blank lines */
if (!*line) if (!*line) {
processed_file = NULL;
return; return;
}
/* We have a real line, now try to match commands */ /* We have a real line, now try to match commands */
processed_command = command = line; processed_command = command = line;
p = CPS_SplitWord(line); p = CPS_SplitWord(line);
if (print_config && strcasecmp(command, "include") && strcasecmp(command, "confdir"))
printf("%s%s%s\n", command, p[0] != '\0' ? " " : "", p);
if (!strcasecmp(command, "acquisitionport")) { if (!strcasecmp(command, "acquisitionport")) {
parse_int(p, &acquisition_port); parse_int(p, &acquisition_port);
} else if (!strcasecmp(command, "allow")) { } else if (!strcasecmp(command, "allow")) {
parse_allow_deny(p, ntp_restrictions, 1); parse_allow_deny(p, ntp_restrictions, 1);
} else if (!strcasecmp(command, "authselectmode")) {
parse_authselectmode(p);
} else if (!strcasecmp(command, "bindacqaddress")) { } else if (!strcasecmp(command, "bindacqaddress")) {
parse_bindacqaddress(p); parse_bindacqaddress(p);
} else if (!strcasecmp(command, "bindacqdevice")) {
parse_string(p, &bind_acq_iface);
} else if (!strcasecmp(command, "bindaddress")) { } else if (!strcasecmp(command, "bindaddress")) {
parse_bindaddress(p); parse_bindaddress(p);
} else if (!strcasecmp(command, "bindcmdaddress")) { } else if (!strcasecmp(command, "bindcmdaddress")) {
parse_bindcmdaddress(p); parse_bindcmdaddress(p);
} else if (!strcasecmp(command, "bindcmddevice")) {
parse_string(p, &bind_cmd_iface);
} else if (!strcasecmp(command, "binddevice")) {
parse_string(p, &bind_ntp_iface);
} else if (!strcasecmp(command, "broadcast")) { } else if (!strcasecmp(command, "broadcast")) {
parse_broadcast(p); parse_broadcast(p);
} else if (!strcasecmp(command, "clientloglimit")) { } else if (!strcasecmp(command, "clientloglimit")) {
parse_clientloglimit(p); parse_clientloglimit(p);
} else if (!strcasecmp(command, "clockprecision")) {
parse_double(p, &clock_precision);
} else if (!strcasecmp(command, "cmdallow")) { } else if (!strcasecmp(command, "cmdallow")) {
parse_allow_deny(p, cmd_restrictions, 1); parse_allow_deny(p, cmd_restrictions, 1);
} else if (!strcasecmp(command, "cmddeny")) { } else if (!strcasecmp(command, "cmddeny")) {
@@ -482,12 +558,16 @@ CNF_ParseLine(const char *filename, int number, char *line)
&cmd_ratelimit_burst, &cmd_ratelimit_leak); &cmd_ratelimit_burst, &cmd_ratelimit_leak);
} else if (!strcasecmp(command, "combinelimit")) { } else if (!strcasecmp(command, "combinelimit")) {
parse_double(p, &combine_limit); parse_double(p, &combine_limit);
} else if (!strcasecmp(command, "confdir")) {
parse_confdir(p);
} else if (!strcasecmp(command, "corrtimeratio")) { } else if (!strcasecmp(command, "corrtimeratio")) {
parse_double(p, &correction_time_ratio); parse_double(p, &correction_time_ratio);
} else if (!strcasecmp(command, "deny")) { } else if (!strcasecmp(command, "deny")) {
parse_allow_deny(p, ntp_restrictions, 0); parse_allow_deny(p, ntp_restrictions, 0);
} else if (!strcasecmp(command, "driftfile")) { } else if (!strcasecmp(command, "driftfile")) {
parse_string(p, &drift_file); parse_string(p, &drift_file);
} else if (!strcasecmp(command, "dscp")) {
parse_int(p, &ntp_dscp);
} else if (!strcasecmp(command, "dumpdir")) { } else if (!strcasecmp(command, "dumpdir")) {
parse_string(p, &dumpdir); parse_string(p, &dumpdir);
} else if (!strcasecmp(command, "dumponexit")) { } else if (!strcasecmp(command, "dumponexit")) {
@@ -556,6 +636,9 @@ CNF_ParseLine(const char *filename, int number, char *line)
no_system_cert = parse_null(p); no_system_cert = parse_null(p);
} else if (!strcasecmp(command, "ntpsigndsocket")) { } else if (!strcasecmp(command, "ntpsigndsocket")) {
parse_string(p, &ntp_signd_socket); parse_string(p, &ntp_signd_socket);
} else if (!strcasecmp(command, "ntsratelimit")) {
parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
&nts_ratelimit_burst, &nts_ratelimit_leak);
} else if (!strcasecmp(command, "ntstrustedcerts")) { } else if (!strcasecmp(command, "ntstrustedcerts")) {
parse_string(p, &nts_trusted_cert_file); parse_string(p, &nts_trusted_cert_file);
} else if (!strcasecmp(command, "ntscachedir") || } else if (!strcasecmp(command, "ntscachedir") ||
@@ -576,11 +659,11 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "ntsserverkey")) { } else if (!strcasecmp(command, "ntsserverkey")) {
parse_string(p, &nts_server_key_file); parse_string(p, &nts_server_key_file);
} else if (!strcasecmp(command, "peer")) { } else if (!strcasecmp(command, "peer")) {
parse_source(p, NTP_PEER, 0); parse_source(p, command, 1);
} else if (!strcasecmp(command, "pidfile")) { } else if (!strcasecmp(command, "pidfile")) {
parse_string(p, &pidfile); parse_string(p, &pidfile);
} else if (!strcasecmp(command, "pool")) { } else if (!strcasecmp(command, "pool")) {
parse_source(p, NTP_SERVER, 1); parse_source(p, command, 1);
} else if (!strcasecmp(command, "port")) { } else if (!strcasecmp(command, "port")) {
parse_int(p, &ntp_port); parse_int(p, &ntp_port);
} else if (!strcasecmp(command, "ratelimit")) { } else if (!strcasecmp(command, "ratelimit")) {
@@ -603,9 +686,11 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "sched_priority")) { } else if (!strcasecmp(command, "sched_priority")) {
parse_int(p, &sched_priority); parse_int(p, &sched_priority);
} else if (!strcasecmp(command, "server")) { } else if (!strcasecmp(command, "server")) {
parse_source(p, NTP_SERVER, 0); parse_source(p, command, 1);
} else if (!strcasecmp(command, "smoothtime")) { } else if (!strcasecmp(command, "smoothtime")) {
parse_smoothtime(p); parse_smoothtime(p);
} else if (!strcasecmp(command, "sourcedir")) {
parse_sourcedir(p);
} else if (!strcasecmp(command, "stratumweight")) { } else if (!strcasecmp(command, "stratumweight")) {
parse_double(p, &stratum_weight); parse_double(p, &stratum_weight);
} else if (!strcasecmp(command, "tempcomp")) { } else if (!strcasecmp(command, "tempcomp")) {
@@ -618,8 +703,10 @@ CNF_ParseLine(const char *filename, int number, char *line)
!strcasecmp(command, "linux_hz")) { !strcasecmp(command, "linux_hz")) {
LOG(LOGS_WARN, "%s directive is no longer supported", command); LOG(LOGS_WARN, "%s directive is no longer supported", command);
} else { } else {
other_parse_error("Invalid command"); other_parse_error("Invalid directive");
} }
processed_file = processed_command = NULL;
} }
/* ================================================== */ /* ================================================== */
@@ -671,15 +758,31 @@ parse_null(char *line)
/* ================================================== */ /* ================================================== */
static void static void
parse_source(char *line, NTP_Source_Type type, int pool) parse_source(char *line, char *type, int fatal)
{ {
NTP_Source source; NTP_Source source;
source.type = type; if (strcasecmp(type, "peer") == 0) {
source.pool = pool; source.type = NTP_PEER;
source.pool = 0;
} else if (strcasecmp(type, "pool") == 0) {
source.type = NTP_SERVER;
source.pool = 1;
} else if (strcasecmp(type, "server") == 0) {
source.type = NTP_SERVER;
source.pool = 0;
} else {
if (fatal)
command_parse_error();
return;
}
/* Avoid comparing uninitialized data in compare_sources() */
memset(&source.params, 0, sizeof (source.params));
if (!CPS_ParseNTPSourceAdd(line, &source.params)) { if (!CPS_ParseNTPSourceAdd(line, &source.params)) {
command_parse_error(); if (fatal)
command_parse_error();
return; return;
} }
@@ -689,6 +792,17 @@ parse_source(char *line, NTP_Source_Type type, int pool)
/* ================================================== */ /* ================================================== */
static void
parse_sourcedir(char *line)
{
char *s;
s = Strdup(line);
ARR_AppendElement(ntp_source_dirs, &s);
}
/* ================================================== */
static void static void
parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak) parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak)
{ {
@@ -1141,6 +1255,23 @@ parse_allow_deny(char *line, ARR_Instance restrictions, int allow)
/* ================================================== */ /* ================================================== */
static void
parse_authselectmode(char *line)
{
if (!strcasecmp(line, "require"))
authselect_mode = SRC_AUTHSELECT_REQUIRE;
else if (!strcasecmp(line, "prefer"))
authselect_mode = SRC_AUTHSELECT_PREFER;
else if (!strcasecmp(line, "mix"))
authselect_mode = SRC_AUTHSELECT_MIX;
else if (!strcasecmp(line, "ignore"))
authselect_mode = SRC_AUTHSELECT_IGNORE;
else
command_parse_error();
}
/* ================================================== */
static void static void
parse_bindacqaddress(char *line) parse_bindacqaddress(char *line)
{ {
@@ -1188,8 +1319,10 @@ parse_bindcmdaddress(char *line)
if (line[0] == '/') { if (line[0] == '/') {
parse_string(line, &bind_cmd_path); parse_string(line, &bind_cmd_path);
/* / disables the socket */ /* / disables the socket */
if (!strcmp(bind_cmd_path, "/")) if (strcmp(bind_cmd_path, "/") == 0) {
bind_cmd_path[0] = '\0'; Free(bind_cmd_path);
bind_cmd_path = NULL;
}
} else if (UTI_StringToIP(line, &ip)) { } else if (UTI_StringToIP(line, &ip)) {
if (ip.family == IPADDR_INET4) if (ip.family == IPADDR_INET4)
bind_cmd_address4 = ip; bind_cmd_address4 = ip;
@@ -1242,8 +1375,8 @@ parse_broadcast(char *line)
} }
destination = (NTP_Broadcast_Destination *)ARR_GetNewElement(broadcasts); destination = (NTP_Broadcast_Destination *)ARR_GetNewElement(broadcasts);
destination->addr = ip; destination->addr.ip_addr = ip;
destination->port = port; destination->addr.port = port;
destination->interval = interval; destination->interval = interval;
} }
@@ -1386,6 +1519,86 @@ parse_hwtimestamp(char *line)
/* ================================================== */ /* ================================================== */
static const char *
get_basename(const char *path)
{
const char *b = strrchr(path, '/');
return b ? b + 1 : path;
}
/* ================================================== */
static int
compare_basenames(const void *a, const void *b)
{
return strcmp(get_basename(*(const char * const *)a),
get_basename(*(const char * const *)b));
}
/* ================================================== */
static int
search_dirs(char *line, const char *suffix, void (*file_handler)(const char *path))
{
char *dirs[MAX_CONF_DIRS], buf[MAX_LINE_LENGTH], *path;
size_t i, j, k, locations, n_dirs;
glob_t gl;
n_dirs = UTI_SplitString(line, dirs, MAX_CONF_DIRS);
if (n_dirs < 1 || n_dirs > MAX_CONF_DIRS)
return 0;
/* Get the paths of all config files in the specified directories */
for (i = 0; i < n_dirs; i++) {
if (snprintf(buf, sizeof (buf), "%s/*%s", dirs[i], suffix) >= sizeof (buf))
assert(0);
if (glob(buf, GLOB_NOSORT | (i > 0 ? GLOB_APPEND : 0), NULL, &gl) != 0)
;
}
if (gl.gl_pathc > 0) {
/* Sort the paths by filenames */
qsort(gl.gl_pathv, gl.gl_pathc, sizeof (gl.gl_pathv[0]), compare_basenames);
for (i = 0; i < gl.gl_pathc; i += locations) {
/* Count directories containing files with this name */
for (j = i + 1, locations = 1; j < gl.gl_pathc; j++, locations++) {
if (compare_basenames(&gl.gl_pathv[i], &gl.gl_pathv[j]) != 0)
break;
}
/* Read the first file of this name in the order of the directive */
for (j = 0; j < n_dirs; j++) {
for (k = 0; k < locations; k++) {
path = gl.gl_pathv[i + k];
if (strncmp(path, dirs[j], strlen(dirs[j])) == 0 &&
strlen(dirs[j]) + 1 + strlen(get_basename(path)) == strlen(path)) {
file_handler(path);
break;
}
}
if (k < locations)
break;
}
}
}
globfree(&gl);
return 1;
}
/* ================================================== */
static void
parse_confdir(char *line)
{
if (!search_dirs(line, ".conf", CNF_ReadFile))
command_parse_error();
}
/* ================================================== */
static void static void
parse_include(char *line) parse_include(char *line)
{ {
@@ -1415,13 +1628,144 @@ parse_include(char *line)
/* ================================================== */ /* ================================================== */
static void
load_source_file(const char *filename)
{
char line[MAX_LINE_LENGTH + 1];
FILE *f;
f = UTI_OpenFile(NULL, filename, NULL, 'r', 0);
if (!f)
return;
while (fgets(line, sizeof (line), f)) {
if (strlen(line) >= MAX_LINE_LENGTH)
continue;
CPS_NormalizeLine(line);
if (line[0] == '\0')
continue;
parse_source(CPS_SplitWord(line), line, 0);
}
fclose(f);
}
/* ================================================== */
static int
compare_sources(const void *a, const void *b)
{
const NTP_Source *sa = a, *sb = b;
int d;
if (!sa->params.name)
return -1;
if (!sb->params.name)
return 1;
if ((d = strcmp(sa->params.name, sb->params.name)) != 0)
return d;
if ((d = (int)(sa->type) - (int)(sb->type)) != 0)
return d;
if ((d = sa->pool - sb->pool) != 0)
return d;
if ((d = sa->params.port - sb->params.port) != 0)
return d;
return memcmp(&sa->params.params, &sb->params.params, sizeof (sa->params.params));
}
/* ================================================== */
static void
reload_source_dirs(void)
{
NTP_Source *prev_sources, *new_sources, *source;
unsigned int i, j, prev_size, new_size, unresolved;
uint32_t *prev_ids, *new_ids;
char buf[MAX_LINE_LENGTH];
NSR_Status s;
int d;
prev_size = ARR_GetSize(ntp_source_ids);
if (prev_size > 0 && ARR_GetSize(ntp_sources) != prev_size)
assert(0);
/* Save the current sources and their configuration IDs */
prev_ids = MallocArray(uint32_t, prev_size);
memcpy(prev_ids, ARR_GetElements(ntp_source_ids), prev_size * sizeof (prev_ids[0]));
prev_sources = MallocArray(NTP_Source, prev_size);
memcpy(prev_sources, ARR_GetElements(ntp_sources), prev_size * sizeof (prev_sources[0]));
/* Load the sources again */
ARR_SetSize(ntp_sources, 0);
for (i = 0; i < ARR_GetSize(ntp_source_dirs); i++) {
if (snprintf(buf, sizeof (buf), "%s",
*(char **)ARR_GetElement(ntp_source_dirs, i)) >= sizeof (buf))
assert(0);
search_dirs(buf, ".sources", load_source_file);
}
/* Add new and remove existing sources according to the new configuration.
Avoid removing and adding the same source again to keep its state. */
new_size = ARR_GetSize(ntp_sources);
new_sources = ARR_GetElements(ntp_sources);
ARR_SetSize(ntp_source_ids, new_size);
new_ids = ARR_GetElements(ntp_source_ids);
unresolved = 0;
qsort(new_sources, new_size, sizeof (new_sources[0]), compare_sources);
for (i = j = 0; i < prev_size || j < new_size; ) {
if (i < prev_size && j < new_size)
d = compare_sources(&prev_sources[i], &new_sources[j]);
else
d = i < prev_size ? -1 : 1;
if (d < 0) {
/* Remove the missing source */
if (prev_sources[i].params.name[0] != '\0')
NSR_RemoveSourcesById(prev_ids[i]);
i++;
} else if (d > 0) {
/* Add a newly configured source */
source = &new_sources[j];
s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool,
source->type, &source->params.params, &new_ids[j]);
if (s == NSR_UnresolvedName) {
unresolved++;
} else if (s != NSR_Success) {
/* Mark the source as not present */
source->params.name[0] = '\0';
}
j++;
} else {
/* Keep the existing source */
new_ids[j] = prev_ids[i];
i++, j++;
}
}
for (i = 0; i < prev_size; i++)
Free(prev_sources[i].params.name);
Free(prev_sources);
Free(prev_ids);
if (unresolved > 0)
NSR_ResolveSources();
}
/* ================================================== */
void void
CNF_CreateDirs(uid_t uid, gid_t gid) CNF_CreateDirs(uid_t uid, gid_t gid)
{ {
char *dir; char *dir;
/* Create a directory for the Unix domain command socket */ /* Create a directory for the Unix domain command socket */
if (bind_cmd_path[0]) { if (bind_cmd_path) {
dir = UTI_PathToDir(bind_cmd_path); dir = UTI_PathToDir(bind_cmd_path);
UTI_CreateDirAndParents(dir, 0770, uid, gid); UTI_CreateDirAndParents(dir, 0770, uid, gid);
@@ -1430,16 +1774,19 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
domain sockets are ignored on some systems (e.g. Solaris). */ domain sockets are ignored on some systems (e.g. Solaris). */
if (!UTI_CheckDirPermissions(dir, 0770, uid, gid)) { if (!UTI_CheckDirPermissions(dir, 0770, uid, gid)) {
LOG(LOGS_WARN, "Disabled command socket %s", bind_cmd_path); LOG(LOGS_WARN, "Disabled command socket %s", bind_cmd_path);
bind_cmd_path[0] = '\0'; Free(bind_cmd_path);
bind_cmd_path = NULL;
} }
Free(dir); Free(dir);
} }
if (logdir[0]) if (logdir)
UTI_CreateDirAndParents(logdir, 0755, uid, gid); UTI_CreateDirAndParents(logdir, 0750, uid, gid);
if (dumpdir[0]) if (dumpdir)
UTI_CreateDirAndParents(dumpdir, 0755, uid, gid); UTI_CreateDirAndParents(dumpdir, 0750, uid, gid);
if (nts_dump_dir)
UTI_CreateDirAndParents(nts_dump_dir, 0750, uid, gid);
} }
/* ================================================== */ /* ================================================== */
@@ -1456,13 +1803,12 @@ CNF_AddInitSources(void)
/* Get the default NTP params */ /* Get the default NTP params */
CPS_ParseNTPSourceAdd(dummy_hostname, &cps_source); CPS_ParseNTPSourceAdd(dummy_hostname, &cps_source);
/* Add the address as an offline iburst server */ /* Add the address as a server specified with the iburst option */
ntp_addr.ip_addr = *(IPAddr *)ARR_GetElement(init_sources, i); ntp_addr.ip_addr = *(IPAddr *)ARR_GetElement(init_sources, i);
ntp_addr.port = cps_source.port; ntp_addr.port = cps_source.port;
cps_source.params.iburst = 1; cps_source.params.iburst = 1;
cps_source.params.connectivity = SRC_OFFLINE;
NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params); NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params, NULL);
} }
ARR_SetSize(init_sources, 0); ARR_SetSize(init_sources, 0);
@@ -1479,11 +1825,13 @@ CNF_AddSources(void)
for (i = 0; i < ARR_GetSize(ntp_sources); i++) { for (i = 0; i < ARR_GetSize(ntp_sources); i++) {
source = (NTP_Source *)ARR_GetElement(ntp_sources, i); source = (NTP_Source *)ARR_GetElement(ntp_sources, i);
NSR_AddSourceByName(source->params.name, source->params.port, NSR_AddSourceByName(source->params.name, source->params.port,
source->pool, source->type, &source->params.params); source->pool, source->type, &source->params.params, NULL);
Free(source->params.name); Free(source->params.name);
} }
ARR_SetSize(ntp_sources, 0); ARR_SetSize(ntp_sources, 0);
reload_source_dirs();
} }
/* ================================================== */ /* ================================================== */
@@ -1510,8 +1858,7 @@ CNF_AddBroadcasts(void)
for (i = 0; i < ARR_GetSize(broadcasts); i++) { for (i = 0; i < ARR_GetSize(broadcasts); i++) {
destination = (NTP_Broadcast_Destination *)ARR_GetElement(broadcasts, i); destination = (NTP_Broadcast_Destination *)ARR_GetElement(broadcasts, i);
NCR_AddBroadcastDestination(&destination->addr, destination->port, NCR_AddBroadcastDestination(&destination->addr, destination->interval);
destination->interval);
} }
ARR_SetSize(broadcasts, 0); ARR_SetSize(broadcasts, 0);
@@ -1519,6 +1866,14 @@ CNF_AddBroadcasts(void)
/* ================================================== */ /* ================================================== */
void
CNF_ReloadSources(void)
{
reload_source_dirs();
}
/* ================================================== */
int int
CNF_GetNTPPort(void) CNF_GetNTPPort(void)
{ {
@@ -1680,6 +2035,14 @@ CNF_GetCorrectionTimeRatio(void)
/* ================================================== */ /* ================================================== */
SRC_AuthSelectMode
CNF_GetAuthSelectMode(void)
{
return authselect_mode;
}
/* ================================================== */
double double
CNF_GetMaxSlewRate(void) CNF_GetMaxSlewRate(void)
{ {
@@ -1688,6 +2051,14 @@ CNF_GetMaxSlewRate(void)
/* ================================================== */ /* ================================================== */
double
CNF_GetClockPrecision(void)
{
return clock_precision;
}
/* ================================================== */
double double
CNF_GetMaxDistance(void) CNF_GetMaxDistance(void)
{ {
@@ -1897,6 +2268,30 @@ CNF_GetBindAcquisitionAddress(int family, IPAddr *addr)
/* ================================================== */ /* ================================================== */
char *
CNF_GetBindNtpInterface(void)
{
return bind_ntp_iface;
}
/* ================================================== */
char *
CNF_GetBindAcquisitionInterface(void)
{
return bind_acq_iface;
}
/* ================================================== */
char *
CNF_GetBindCommandInterface(void)
{
return bind_cmd_iface;
}
/* ================================================== */
char * char *
CNF_GetBindCommandPath(void) CNF_GetBindCommandPath(void)
{ {
@@ -1918,6 +2313,14 @@ CNF_GetBindCommandAddress(int family, IPAddr *addr)
/* ================================================== */ /* ================================================== */
int
CNF_GetNtpDscp(void)
{
return ntp_dscp;
}
/* ================================================== */
char * char *
CNF_GetNtpSigndSocket(void) CNF_GetNtpSigndSocket(void)
{ {
@@ -1976,6 +2379,16 @@ int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak)
/* ================================================== */ /* ================================================== */
int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak)
{
*interval = nts_ratelimit_interval;
*burst = nts_ratelimit_burst;
*leak = nts_ratelimit_leak;
return nts_ratelimit_enabled;
}
/* ================================================== */
int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak) int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak)
{ {
*interval = cmd_ratelimit_interval; *interval = cmd_ratelimit_interval;

12
conf.h
View File

@@ -30,10 +30,13 @@
#include "addressing.h" #include "addressing.h"
#include "reference.h" #include "reference.h"
#include "sources.h"
extern void CNF_Initialise(int restarted, int client_only); extern void CNF_Initialise(int restarted, int client_only);
extern void CNF_Finalise(void); extern void CNF_Finalise(void);
extern void CNF_EnablePrint(void);
extern char *CNF_GetRtcDevice(void); extern char *CNF_GetRtcDevice(void);
extern void CNF_ReadFile(const char *filename); extern void CNF_ReadFile(const char *filename);
@@ -46,6 +49,8 @@ extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void); extern void CNF_AddBroadcasts(void);
extern void CNF_AddRefclocks(void); extern void CNF_AddRefclocks(void);
extern void CNF_ReloadSources(void);
extern int CNF_GetAcquisitionPort(void); extern int CNF_GetAcquisitionPort(void);
extern int CNF_GetNTPPort(void); extern int CNF_GetNTPPort(void);
extern char *CNF_GetDriftFile(void); extern char *CNF_GetDriftFile(void);
@@ -74,7 +79,11 @@ extern void CNF_GetFallbackDrifts(int *min, int *max);
extern void CNF_GetBindAddress(int family, IPAddr *addr); extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr); extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr); extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
extern char *CNF_GetBindNtpInterface(void);
extern char *CNF_GetBindAcquisitionInterface(void);
extern char *CNF_GetBindCommandInterface(void);
extern char *CNF_GetBindCommandPath(void); extern char *CNF_GetBindCommandPath(void);
extern int CNF_GetNtpDscp(void);
extern char *CNF_GetNtpSigndSocket(void); extern char *CNF_GetNtpSigndSocket(void);
extern char *CNF_GetPidFile(void); extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void); extern REF_LeapMode CNF_GetLeapSecMode(void);
@@ -86,7 +95,9 @@ extern double CNF_GetMaxClockError(void);
extern double CNF_GetMaxDrift(void); extern double CNF_GetMaxDrift(void);
extern double CNF_GetCorrectionTimeRatio(void); extern double CNF_GetCorrectionTimeRatio(void);
extern double CNF_GetMaxSlewRate(void); extern double CNF_GetMaxSlewRate(void);
extern double CNF_GetClockPrecision(void);
extern SRC_AuthSelectMode CNF_GetAuthSelectMode(void);
extern double CNF_GetMaxDistance(void); extern double CNF_GetMaxDistance(void);
extern double CNF_GetMaxJitter(void); extern double CNF_GetMaxJitter(void);
extern double CNF_GetReselectDistance(void); extern double CNF_GetReselectDistance(void);
@@ -101,6 +112,7 @@ extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void); extern int CNF_GetLockMemory(void);
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak); extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
extern int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak);
extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak); extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only); extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2); extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2);

107
configure vendored
View File

@@ -5,7 +5,7 @@
# #
# Copyright (C) Richard P. Curnow 1997-2003 # Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Bryan Christianson 2016 # Copyright (C) Bryan Christianson 2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2018 # Copyright (C) Miroslav Lichvar 2009, 2012-2020
# Copyright (C) Stefan R. Filipek 2019 # Copyright (C) Stefan R. Filipek 2019
# #
# ======================================================================= # =======================================================================
@@ -108,11 +108,7 @@ for instance \`--prefix=$HOME'.
For better control, use the options below. For better control, use the options below.
--disable-readline Disable line editing support --disable-readline Disable line editing support
--without-readline Don't use GNU readline even if it is available
--without-editline Don't use editline even if it is available --without-editline Don't use editline even if it is available
--with-readline-includes=DIR Specify where readline include directory is
--with-readline-library=DIR Specify where readline lib directory is
--with-ncurses-library=DIR Specify where ncurses lib directory is
--disable-sechash Disable support for hashes other than MD5 --disable-sechash Disable support for hashes other than MD5
--without-nettle Don't use nettle even if it is available --without-nettle Don't use nettle even if it is available
--without-nss Don't use NSS even if it is available --without-nss Don't use NSS even if it is available
@@ -225,7 +221,6 @@ feat_cmdmon=1
feat_ntp=1 feat_ntp=1
feat_refclock=1 feat_refclock=1
feat_readline=1 feat_readline=1
try_readline=1
try_editline=1 try_editline=1
feat_sechash=1 feat_sechash=1
try_nettle=1 try_nettle=1
@@ -241,9 +236,6 @@ try_clockctl=0
feat_scfilter=0 feat_scfilter=0
try_seccomp=-1 try_seccomp=-1
priv_ops="" priv_ops=""
readline_lib=""
readline_inc=""
ncurses_lib=""
feat_ipv6=1 feat_ipv6=1
feat_phc=1 feat_phc=1
try_phc=0 try_phc=0
@@ -274,21 +266,9 @@ do
--disable-readline ) --disable-readline )
feat_readline=0 feat_readline=0
;; ;;
--without-readline )
try_readline=0
;;
--without-editline ) --without-editline )
try_editline=0 try_editline=0
;; ;;
--with-readline-library=* )
readline_lib=-L`echo $option | sed -e 's/^.*=//;'`
;;
--with-readline-includes=* )
readline_inc=-I`echo $option | sed -e 's/^.*=//;'`
;;
--with-ncurses-library=* )
ncurses_lib=-L`echo $option | sed -e 's/^.*=//;'`
;;
--prefix=* | --install_prefix=* ) --prefix=* | --install_prefix=* )
SETPREFIX=`echo $option | sed -e 's/[^=]*=//;'` SETPREFIX=`echo $option | sed -e 's/[^=]*=//;'`
;; ;;
@@ -657,6 +637,7 @@ if [ $feat_ipv6 = "1" ] && \
struct sockaddr_in6 n; struct sockaddr_in6 n;
char p[100]; char p[100];
n.sin6_addr = in6addr_any; n.sin6_addr = in6addr_any;
n.sin6_scope_id = 0;
return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));' return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));'
then then
add_def FEAT_IPV6 add_def FEAT_IPV6
@@ -803,10 +784,12 @@ if [ $feat_scfilter = "1" ] && [ $try_seccomp = "1" ] && \
'seccomp_init(SCMP_ACT_KILL);' 'seccomp_init(SCMP_ACT_KILL);'
then then
add_def FEAT_SCFILTER add_def FEAT_SCFILTER
# NAME2IPADDRESS shouldn't be enabled with other operations as the helper if [ $feat_ntp = "1" ]; then
# process works on one request at the time and the async resolver could # NAME2IPADDRESS shouldn't be enabled together with a privops operation
# block the main thread # used by the main thread as the helper process works on one request at
priv_ops="NAME2IPADDRESS RELOADDNS" # a time and the async resolver would block the main thread
priv_ops="NAME2IPADDRESS RELOADDNS"
fi
EXTRA_LIBS="$EXTRA_LIBS -lseccomp" EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
fi fi
@@ -875,37 +858,11 @@ fi
READLINE_LINK="" READLINE_LINK=""
if [ $feat_readline = "1" ]; then if [ $feat_readline = "1" ]; then
if [ $try_editline = "1" ]; then if [ $try_editline = "1" ]; then
if test_code editline 'stdio.h editline/readline.h' \ if test_code editline 'stdio.h editline/readline.h' '' '-ledit' \
"$readline_inc" "$readline_lib -ledit" \
'add_history(readline("prompt"));' 'add_history(readline("prompt"));'
then then
add_def FEAT_READLINE add_def FEAT_READLINE
add_def USE_EDITLINE READLINE_LINK="-ledit"
MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib -ledit"
fi
fi
if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then
if test_code readline 'stdio.h readline/readline.h readline/history.h' \
"$readline_inc" "$readline_lib -lreadline" \
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib -lreadline"
fi
fi
if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then
if test_code 'readline with -lncurses' \
'stdio.h readline/readline.h readline/history.h' \
"$readline_inc" "$readline_lib $ncurses_lib -lreadline -lncurses" \
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses"
fi fi
fi fi
@@ -939,7 +896,7 @@ fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then
test_cflags="`pkg_config --cflags nss`" test_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3" test_link="`pkg_config --libs-only-L nss` -lfreebl3 -lnssutil3"
if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \ if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \
"$test_cflags" "$test_link" \ "$test_cflags" "$test_link" \
'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));' 'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));'
@@ -966,31 +923,45 @@ EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ" EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
LIBS="$LIBS $HASH_LINK" LIBS="$LIBS $HASH_LINK"
if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ] && \ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
echo "$HASH_LINK" | grep 'nettle' > /dev/null; then
test_cflags="`pkg_config --cflags gnutls`" test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`" test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/gnutls.h' \ if test_code 'gnutls' 'gnutls/gnutls.h' \
"$test_cflags" "$test_link" ' "$test_cflags" "$test_link" '
return gnutls_init(NULL, 0) + return gnutls_init(NULL, 0) + GNUTLS_TLS1_3 +
gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) + gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);' && gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);'
test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
'aes128_set_encrypt_key(NULL, NULL);'
then then
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
LIBS="$LIBS $test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_NTS
add_def HAVE_SIV
if test_code 'SIV in nettle' \ if test_code 'SIV in nettle' \
'nettle/siv-cmac.h' "" "$LIBS" \ 'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key(NULL, NULL);' 'siv_cmac_aes128_set_key(NULL, NULL);'
then then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC add_def HAVE_NETTLE_SIV_CMAC
else
if test_code 'SIV in gnutls' 'gnutls/gnutls.h' \
"$test_cflags" "$test_link" '
return gnutls_aead_cipher_init(NULL, GNUTLS_CIPHER_AES_128_SIV, NULL);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
add_def HAVE_SIV
else
if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
'aes128_set_encrypt_key(NULL, NULL);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
fi
fi
fi
if grep '#define HAVE_SIV' config.h > /dev/null; then
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
LIBS="$LIBS $test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_NTS
fi fi
fi fi
fi fi

View File

@@ -3,7 +3,7 @@
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016 // Copyright (C) Stephen Wadeley 2016
// Copyright (C) Bryan Christianson 2017 // Copyright (C) Bryan Christianson 2017
// Copyright (C) Miroslav Lichvar 2009-2017 // Copyright (C) Miroslav Lichvar 2009-2020
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as // it under the terms of version 2 of the GNU General Public License as
@@ -32,12 +32,14 @@ chrony.conf - chronyd configuration file
== DESCRIPTION == DESCRIPTION
This file configures the *chronyd* daemon. The compiled-in location is This file configures the *chronyd* daemon. The compiled-in location is
_@SYSCONFDIR@/chrony.conf_, but other locations can be specified on the _@SYSCONFDIR@/chrony.conf_. Other locations can be specified on the
*chronyd* command line with the *-f* option. *chronyd* command line with the *-f* option.
Each directive in the configuration file is placed on a separate line. The Each directive in the configuration file is placed on a separate line. The
following sections describe each of the directives in turn. The directives can following sections describe each of the directives in turn. The directives are
occur in any order in the file and they are not case-sensitive. not case-sensitive. Generally, the directives can occur in any order in the file
and if a directive is specified multiple times, only the last one will be
effective. Exceptions are noted in the descriptions.
The configuration directives can also be specified directly on the *chronyd* The configuration directives can also be specified directly on the *chronyd*
command line. In this case each argument is parsed as a new line and the command line. In this case each argument is parsed as a new line and the
@@ -61,9 +63,10 @@ source. The client-server relationship is strictly hierarchical: a client might
synchronise its system time to that of the server, but the server's system time synchronise its system time to that of the server, but the server's system time
will never be influenced by that of a client. will never be influenced by that of a client.
+ +
The *server* directive is immediately followed by either the name of the This directive can be used multiple times to specify multiple servers.
server, or its IP address. The *server* directive supports the following +
options: The directive is immediately followed by either the name of the
server, or its IP address. It supports the following options:
+ +
*minpoll* _poll_::: *minpoll* _poll_:::
This option specifies the minimum interval between requests sent to the server This option specifies the minimum interval between requests sent to the server
@@ -82,13 +85,13 @@ interval should stay at or below 9 (512 seconds). The default is 10 (1024
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6 seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
months). months).
*iburst*::: *iburst*:::
With this option, the interval between the first four requests sent to the With this option, *chronyd* will start with a burst of 4-8 requests in order to
server will be 2 seconds or less instead of the interval specified by the make the first update of the clock sooner. It will also repeat the burst every
*minpoll* option, which allows *chronyd* to make the first update of the clock time the source is switched from the offline state to online with the
shortly after start. <<chronyc.adoc#online,*online*>> command in *chronyc*.
*burst*::: *burst*:::
With this option, *chronyd* will shorten the interval between up to four With this option, *chronyd* will send a burst of up to 4 requests when it
requests to 2 seconds or less when it cannot get a good measurement from the cannot get a good measurement from the
server. The number of requests in the burst is limited by the current polling server. The number of requests in the burst is limited by the current polling
interval to keep the average interval at or above the minimum interval, i.e. interval to keep the average interval at or above the minimum interval, i.e.
the current interval needs to be at least two times longer than the minimum the current interval needs to be at least two times longer than the minimum
@@ -200,10 +203,11 @@ authenticated source to be safely combined with unauthenticated sources in
order to improve the accuracy of the clock. They can be selected and used for order to improve the accuracy of the clock. They can be selected and used for
synchronisation only if they agree with the trusted and required source. synchronisation only if they agree with the trusted and required source.
*xleave*::: *xleave*:::
This option enables an interleaved mode which enables the server to This option enables the interleaved mode of NTP. It enables the server to
send transmit timestamps captured after the actual transmission (e.g. when the respond with more accurate transmit timestamps (e.g. kernel or hardware
server is running *chronyd* with software (kernel) or hardware timestamps), which cannot be contained in the transmitted packet itself and
timestamping). This can significantly improve the accuracy of the measurements. need to refer to a previous packet instead. This can significantly improve the
accuracy and stability of the measurements.
+ +
The interleaved mode is compatible with servers that support only the basic The interleaved mode is compatible with servers that support only the basic
mode. Note that even mode. Note that even
@@ -228,7 +232,7 @@ be specified. For normal servers this option should not be required (the
default is 123, the standard NTP port). default is 123, the standard NTP port).
*ntsport* _port_::: *ntsport* _port_:::
This option specifies the TCP port on which the server is listening for NTS-KE This option specifies the TCP port on which the server is listening for NTS-KE
connections when the *nts* option is enabled. The default is 11443. connections when the *nts* option is enabled. The default is 4460.
*presend* _poll_::: *presend* _poll_:::
If the timing measurements being made by *chronyd* are the only network data If the timing measurements being made by *chronyd* are the only network data
passing between two computers, you might find that some measurements are badly passing between two computers, you might find that some measurements are badly
@@ -275,6 +279,8 @@ directive, except that it is used to specify a pool of NTP servers rather than
a single NTP server. The pool name is expected to resolve to multiple addresses a single NTP server. The pool name is expected to resolve to multiple addresses
which might change over time. which might change over time.
+ +
This directive can be used multiple times to specify multiple pools.
+
All options valid in the <<server,*server*>> directive can be used in this All options valid in the <<server,*server*>> directive can be used in this
directive too. There is one option specific to the *pool* directive: directive too. There is one option specific to the *pool* directive:
+ +
@@ -284,7 +290,7 @@ This option sets the desired number of sources to be used from the pool.
sources responding to requests. The default value is 4 and the maximum value is sources responding to requests. The default value is 4 and the maximum value is
16. 16.
+ +
:: {blank}::
When an NTP source is unreachable, When an NTP source is unreachable,
marked as a falseticker, or has a distance larger than the limit set by the marked as a falseticker, or has a distance larger than the limit set by the
<<maxdistance,*maxdistance*>> directive, *chronyd* will try to replace the <<maxdistance,*maxdistance*>> directive, *chronyd* will try to replace the
@@ -305,6 +311,8 @@ is mainly useful when the NTP implementation of the peer (e.g. *ntpd*) supports
ephemeral symmetric associations and does not need to be configured with an ephemeral symmetric associations and does not need to be configured with an
address of this host. *chronyd* does not support ephemeral associations. address of this host. *chronyd* does not support ephemeral associations.
+ +
This directive can be used multiple times to specify multiple peers.
+
The following options of the *server* directive do not work in the *peer* The following options of the *server* directive do not work in the *peer*
directive: *iburst*, *burst*, *nts*, *presend*. directive: *iburst*, *burst*, *nts*, *presend*.
+ +
@@ -397,6 +405,8 @@ driver-specific parameter. The two parameters are followed by zero or more
refclock options. Some drivers have special options, which can be appended to refclock options. Some drivers have special options, which can be appended to
the driver-specific parameter using the *:* character. the driver-specific parameter using the *:* character.
+ +
This directive can be used multiple times to specify multiple reference clocks.
+
There are four drivers included in *chronyd*: There are four drivers included in *chronyd*:
+ +
*PPS*::: *PPS*:::
@@ -412,7 +422,7 @@ By default, the PPS refclock uses assert events (rising edge) for
synchronisation. With this option, it will use clear events (falling edge) synchronisation. With this option, it will use clear events (falling edge)
instead. instead.
+ +
::: {blank}:::
Examples: Examples:
+ +
---- ----
@@ -431,7 +441,7 @@ supports the following option:
This option specifies the permissions of the shared memory segment created by This option specifies the permissions of the shared memory segment created by
*chronyd*. They are specified as a numeric mode. The default value is 0600 *chronyd*. They are specified as a numeric mode. The default value is 0600
(read-write access for owner only). (read-write access for owner only).
::: {blank}:::
+ +
Examples: Examples:
+ +
@@ -485,7 +495,7 @@ value is 0.
This option enables timestamping of clear events (falling edge) instead of This option enables timestamping of clear events (falling edge) instead of
assert events (rising edge) in the PPS mode. This may not work with some assert events (rising edge) in the PPS mode. This may not work with some
clocks. clocks.
::: {blank}:::
+ +
Examples: Examples:
+ +
@@ -495,7 +505,7 @@ refclock PHC /dev/ptp1:nocrossts poll 3 pps
refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2 refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2
---- ----
+ +
:: {blank}::
The *refclock* directive supports the following options: The *refclock* directive supports the following options:
+ +
*poll* _poll_::: *poll* _poll_:::
@@ -609,12 +619,13 @@ behaviour, whereas the *settime* command allows samples of manually entered
time to be provided.) time to be provided.)
[[acquisitionport]]*acquisitionport* _port_:: [[acquisitionport]]*acquisitionport* _port_::
By default, *chronyd* uses a separate client socket for each configured server By default, *chronyd* as an NTP client opens a new socket for each request with
and their source port is chosen arbitrarily by the operating system. However, the source port chosen randomly by the operating system. The *acquisitionport*
you can use the *acquisitionport* directive to explicitly specify a port and directive can be used to specify the source port and use only one socket (per
use only one socket (per IPv4 or IPv6 address family) for all configured servers. IPv4 or IPv6 address family) for all configured servers. This can be useful for
This can be useful for getting through some firewalls. If set to 0, the source getting through some firewalls. It should not be used if not necessary as there
port of the socket will be chosen arbitrarily. is a small impact on security of the client. If set to 0, the source port of
the permanent socket will be chosen randomly by the operating system.
+ +
It can be set to the same port as is used by the NTP server (which can be It can be set to the same port as is used by the NTP server (which can be
configured with the <<port,*port*>> directive) to use only one socket for all configured with the <<port,*port*>> directive) to use only one socket for all
@@ -630,14 +641,40 @@ This would change the source port used for client requests to UDP port 1123.
You could then persuade the firewall administrator to open that port. You could then persuade the firewall administrator to open that port.
[[bindacqaddress]]*bindacqaddress* _address_:: [[bindacqaddress]]*bindacqaddress* _address_::
The *bindacqaddress* directive sets the network interface to which The *bindacqaddress* directive specifies a local IP address to which
*chronyd* will bind its NTP client sockets. The syntax is similar to the *chronyd* will bind its NTP and NTS-KE client sockets. The syntax is similar to
<<bindaddress,*bindaddress*>> and <<bindcmdaddress,*bindcmdaddress*>> the <<bindaddress,*bindaddress*>> and <<bindcmdaddress,*bindcmdaddress*>>
directives. directives.
+ +
For each of the IPv4 and IPv6 protocols, only one *bindacqaddress* directive For each of the IPv4 and IPv6 protocols, only one *bindacqaddress* directive
can be specified. can be specified.
[[bindacqdevice]]*bindacqdevice* _interface_::
The *bindacqdevice* directive binds the client sockets to a network device
specified by the interface name. This can be useful when the local address is
dynamic, or to enable an NTP source specified with a link-local IPv6 address.
This directive can specify only one interface and it is supported on Linux
only.
+
An example of the directive is:
+
----
bindacqdevice eth0
----
[[dscp]]*dscp* _point_::
The *dscp* directive sets the Differentiated Services Code Point (DSCP) in
transmitted NTP packets to the specified value. It can improve stability of NTP
measurements in local networks where switches or routers are configured to
prioritise forwarding of packets with specific DSCP values. The default value
is 0 and the maximum value is 63.
+
An example of the directive (setting the Expedited Forwarding class) is:
+
----
dscp 46
----
[[dumpdir]]*dumpdir* _directory_:: [[dumpdir]]*dumpdir* _directory_::
To compute the rate of gain or loss of time, *chronyd* has to store a To compute the rate of gain or loss of time, *chronyd* has to store a
measurement history for each of the time sources it uses. measurement history for each of the time sources it uses.
@@ -655,6 +692,8 @@ behaviour whilst it is not running). The *dumpdir* directive defines the
directory where the measurement histories are saved when *chronyd* exits, directory where the measurement histories are saved when *chronyd* exits,
or the <<chronyc.adoc#dump,*dump*>> command in *chronyc* is issued. or the <<chronyc.adoc#dump,*dump*>> command in *chronyc* is issued.
+ +
If the directory does not exist, it will be created automatically.
+
An example of the directive is: An example of the directive is:
+ +
---- ----
@@ -696,6 +735,8 @@ received from the server in order to avoid making an NTS-KE request when
source in files named by the IP address of the NTS-KE server (e.g. 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. _1.2.3.4.nts_). By default, the client does not save the cookies.
+ +
If the directory does not exist, it will be created automatically.
+
An example of the directive is: An example of the directive is:
+ +
---- ----
@@ -726,7 +767,7 @@ authentication mechanism to be used on computers which start with wrong time
has important security implications, e.g. if an NTP server was ever has important security implications, e.g. if an NTP server was ever
compromised, its certificate could be used in an attack after the expiration 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. time. The default value is 0, which means the time checks are always enabled.
+
An example of the directive is: An example of the directive is:
+ +
---- ----
@@ -739,6 +780,78 @@ with correct time.
=== Source selection === Source selection
[[authselectmode]]*authselectmode* _mode_::
NTP sources can be specified with the *key* or *nts* option to enable
authentication to limit the impact of man-in-the-middle attacks. The
attackers can drop or delay NTP packets (up to the *maxdelay* and
<<maxdistance,*maxdistance*>> limits), but they cannot modify the timestamps
contained in the packets. The attack can cause only a limited slew or step, and
also cause the clock to run faster or slower than real time (up to double of
the <<maxdrift,*maxdrift*>> limit).
+
When authentication is enabled for an NTP source, it is important to disable
unauthenticated NTP sources which could be exploited in the attack, e.g. if
they are not reachable only over a trusted network. Alternatively, the source
selection can be configured with the *require* and *trust* options to
synchronise to the unauthenticated sources only if they agree with the
authenticated sources and might have a positive impact on the accuracy of the
clock. Note that in this case the impact of the attack is higher. The attackers
cannot cause an arbitrarily large step or slew, but they have more control over
the frequency of the clock and can cause *chronyd* to report false information,
e.g. a significantly smaller root delay and dispersion.
+
This directive determines the default selection options for authenticated and
unauthenticated sources in order to simplify the configuration with the
configuration file and *chronyc* commands. It sets a policy for authentication.
+
Sources specified with the *noselect* option are ignored (not counted as either
authenticated or unauthenticated), and they always have only the selection
options specified in the configuration.
+
There are four modes:
+
*require*:::
Authentication is strictly required for NTP sources in this mode. If any
unauthenticated NTP sources are specified, they will automatically get the
*noselect* option to prevent them from being selected for synchronisation.
*prefer*:::
In this mode, authentication is optional and preferred. If it is enabled for at
least one NTP source, all unauthenticated NTP sources will get the *noselect*
option.
*mix*:::
In this mode, authentication is optional and synchronisation to a mix of
authenticated and unauthenticated NTP sources is allowed. If both authenticated
and unauthenticated NTP sources are specified, all authenticated NTP sources
and reference clocks will get the *require* and *trust* options to prevent
synchronisation to unauthenticated NTP sources if they do not agree with a
majority of the authenticated sources and reference clocks. This is the default
mode.
*ignore*:::
In this mode, authentication is ignored in the source selection. All sources
will have only the selection options that were specified in the configuration
file, or *chronyc* command. This was the behaviour of *chronyd* in versions
before 4.0.
{blank}::
+
As an example, the following configuration using the default *mix* mode:
+
----
server foo.example.net nts
server bar.example.net nts
server baz.example.net
refclock SHM 0
----
+
is equivalent to the following configuration using the *ignore* mode:
+
----
authselectmode ignore
server foo.example.net nts require trust
server bar.example.net nts require trust
server baz.example.net
refclock SHM 0 require trust
----
[[combinelimit]]*combinelimit* _limit_:: [[combinelimit]]*combinelimit* _limit_::
When *chronyd* has multiple sources available for synchronisation, it has to When *chronyd* has multiple sources available for synchronisation, it has to
select one source as the synchronisation source. The measured offsets and select one source as the synchronisation source. The measured offsets and
@@ -805,6 +918,27 @@ distances are in milliseconds.
=== System clock === System clock
[[clockprecision]]*clockprecision* _precision_::
The *clockprecision* directive specifies the precision of the system clock (in
seconds). It is used by *chronyd* to estimate the minimum noise in NTP
measurements and randomise low-order bits of timestamps in NTP responses. By
default, the precision is measured on start as the minimum time to read the
clock.
+
The measured value works well in most cases. However, it generally
overestimates the precision and it can be sensitive to the CPU speed, which can
change over time to save power. In some cases with a high-precision clocksource
(e.g. the Time Stamp Counter of the CPU) and hardware timestamping, setting the
precision on the server to a smaller value can improve stability of clients'
NTP measurements. The server's precision is reported on clients by the
<<chronyc.adoc#ntpdata,*ntpdata*>> command.
+
An example setting the precision to 8 nanoseconds is:
+
----
clockprecision 8e-9
----
[[corrtimeratio]]*corrtimeratio* _ratio_:: [[corrtimeratio]]*corrtimeratio* _ratio_::
When *chronyd* is slewing the system clock to correct an offset, the rate at When *chronyd* is slewing the system clock to correct an offset, the rate at
which it is slewing adds to the frequency error of the clock. On all supported which it is slewing adds to the frequency error of the clock. On all supported
@@ -913,7 +1047,7 @@ clock will be off for a longer time. On Linux with the default
No correction is applied to the clock for the leap second. The clock will be No correction is applied to the clock for the leap second. The clock will be
corrected later in normal operation when new measurements are made and the corrected later in normal operation when new measurements are made and the
estimated offset includes the one second error. estimated offset includes the one second error.
:: {blank}::
+ +
When serving time to NTP clients that cannot be configured to correct their When serving time to NTP clients that cannot be configured to correct their
clocks for a leap second by slewing, or to clients that would correct at clocks for a leap second by slewing, or to clients that would correct at
@@ -936,7 +1070,7 @@ A recommended configuration to enable a server leap smear is:
---- ----
leapsecmode slew leapsecmode slew
maxslewrate 1000 maxslewrate 1000
smoothtime 400 0.001 leaponly smoothtime 400 0.001024 leaponly
---- ----
+ +
The first directive is necessary to disable the clock step which would reset The first directive is necessary to disable the clock step which would reset
@@ -944,11 +1078,17 @@ the smoothing process. The second directive limits the slewing rate of the
local clock to 1000 ppm, which improves the stability of the smoothing process local clock to 1000 ppm, which improves the stability of the smoothing process
when the local correction starts and ends. The third directive enables the when the local correction starts and ends. The third directive enables the
server time smoothing process. It will start when the clock gets to 00:00:00 server time smoothing process. It will start when the clock gets to 00:00:00
UTC and it will take 17 hours 34 minutes to finish. The frequency offset will UTC and it will take 62500 seconds (about 17.36 hours) to finish. The frequency
be changing by 0.001 ppm per second and will reach a maximum of 31.623 ppm. The offset will be changing by 0.001024 ppm per second and will reach a maximum of
*leaponly* option makes the duration of the leap smear constant and allows the 32 ppm in 31250 seconds. The *leaponly* option makes the duration of the leap
clients to safely synchronise with multiple identically configured leap smear constant and allows the clients to safely synchronise with multiple
smearing servers. identically configured leap smearing servers.
+
The duration of the leap smear can be calculated from the specified wander as
+
----
duration = sqrt(4 / wander)
----
[[leapsectz]]*leapsectz* _timezone_:: [[leapsectz]]*leapsectz* _timezone_::
This directive specifies a timezone in the system tz database which *chronyd* This directive specifies a timezone in the system tz database which *chronyd*
@@ -1118,7 +1258,7 @@ coefficients _T0_, _k0_, _k1_, _k2_.
The frequency compensation is calculated (in ppm) as The frequency compensation is calculated (in ppm) as
+ +
---- ----
k0 + (T - T0) * k1 + (T - T0)^2 * k2 comp = k0 + (T - T0) * k1 + (T - T0)^2 * k2
---- ----
+ +
The result has to be between -10 ppm and 10 ppm, otherwise the measurement is The result has to be between -10 ppm and 10 ppm, otherwise the measurement is
@@ -1169,12 +1309,15 @@ _tempcomp.log_ file if enabled by the <<log,*log tempcomp*>> directive.
[[allow]]*allow* [*all*] [_subnet_]:: [[allow]]*allow* [*all*] [_subnet_]::
The *allow* directive is used to designate a particular subnet from which NTP The *allow* directive is used to designate a particular subnet from which NTP
clients are allowed to access the computer as an NTP server. clients are allowed to access the computer as an NTP server. It also controls
access of NTS-KE clients when NTS is enabled on the server.
+ +
The default is that no clients are allowed access, i.e. *chronyd* operates The default is that no clients are allowed access, i.e. *chronyd* operates
purely as an NTP client. If the *allow* directive is used, *chronyd* will be purely as an NTP client. If the *allow* directive is used, *chronyd* will be
both a client of its servers, and a server to other clients. both a client of its servers, and a server to other clients.
+ +
This directive can be used multiple times.
+
Examples of the use of the directive are as follows: Examples of the use of the directive are as follows:
+ +
---- ----
@@ -1241,18 +1384,19 @@ client access by this computer for it to work.
[[deny]]*deny* [*all*] [_subnet_]:: [[deny]]*deny* [*all*] [_subnet_]::
This is similar to the <<allow,*allow*>> directive, except that it denies NTP This is similar to the <<allow,*allow*>> directive, except that it denies NTP
client access to a particular subnet or host, rather than allowing it. and NTS-KE client access to a particular subnet or host, rather than allowing
it.
+ +
The syntax is identical. The syntax is identical and the directive can be used multiple times too.
+ +
There is also a *deny all* directive with similar behaviour to the *allow all* There is also a *deny all* directive with similar behaviour to the *allow all*
directive. directive.
[[bindaddress]]*bindaddress* _address_:: [[bindaddress]]*bindaddress* _address_::
The *bindaddress* directive binds the socket on which *chronyd* listens for NTP The *bindaddress* directive binds the sockets on which *chronyd* listens for
requests to a local address of the computer. On systems other than Linux, the NTP and NTS-KE requests to a local address of the computer. On systems other
address of the computer needs to be already configured when *chronyd* is than Linux, the address of the computer needs to be already configured when
started. *chronyd* is started.
+ +
An example of the use of the directive is: An example of the use of the directive is:
+ +
@@ -1264,12 +1408,25 @@ Currently, for each of the IPv4 and IPv6 protocols, only one *bindaddress*
directive can be specified. Therefore, it is not useful on computers which directive can be specified. Therefore, it is not useful on computers which
should serve NTP on multiple network interfaces. should serve NTP on multiple network interfaces.
[[binddevice]]*binddevice* _interface_::
The *binddevice* directive binds the NTP and NTS-KE server sockets to a network
device specified by the interface name. This directive can specify only one
interface and it is supported on Linux only.
+
An example of the directive is:
+
----
binddevice eth0
----
[[broadcast]]*broadcast* _interval_ _address_ [_port_]:: [[broadcast]]*broadcast* _interval_ _address_ [_port_]::
The *broadcast* directive is used to declare a broadcast address to which The *broadcast* directive is used to declare a broadcast address to which
chronyd should send packets in the NTP broadcast mode (i.e. make *chronyd* act chronyd should send packets in the NTP broadcast mode (i.e. make *chronyd* act
as a broadcast server). Broadcast clients on that subnet will be able to as a broadcast server). Broadcast clients on that subnet will be able to
synchronise. synchronise.
+ +
This directive can be used multiple times to specify multiple addresses.
+
The syntax is as follows: The syntax is as follows:
+ +
---- ----
@@ -1369,16 +1526,18 @@ configuration and to be synchronised to one another, without confusing clients
that poll more than one server. Each server needs to be configured to poll all that poll more than one server. Each server needs to be configured to poll all
other servers with the *local* directive. This ensures only the server with the other servers with the *local* directive. This ensures only the server with the
smallest reference ID has the local reference active and others are smallest reference ID has the local reference active and others are
synchronised to it. When that server fails, another will take over. synchronised to it. If that server stops responding, the server with the second
smallest reference ID will take over when its local reference mode activates
(root distance reaches the threshold configured by the *distance* option).
+ +
The *orphan* mode is compatible with the *ntpd*'s orphan mode (enabled by the The *orphan* mode is compatible with the *ntpd*'s orphan mode (enabled by the
*tos orphan* command). *tos orphan* command).
:: {blank}::
+ +
An example of the directive is: An example of the directive is:
+ +
---- ----
local stratum 10 orphan local stratum 10 orphan distance 0.1
---- ----
[[ntpsigndsocket]]*ntpsigndsocket* _directory_:: [[ntpsigndsocket]]*ntpsigndsocket* _directory_::
@@ -1400,7 +1559,7 @@ ntpsigndsocket /var/lib/samba/ntp_signd
[[ntsport]]*ntsport* _port_:: [[ntsport]]*ntsport* _port_::
This directive specifies the TCP port on which *chronyd* will provide the NTS This directive specifies the TCP port on which *chronyd* will provide the NTS
Key Establishment (NTS-KE) service. The default port is 11443. Key Establishment (NTS-KE) service. The default port is 4460.
+ +
The port will be open only when a certificate and key is specified by the The port will be open only when a certificate and key is specified by the
*ntsservercert* and *ntsserverkey* directives. *ntsservercert* and *ntsserverkey* directives.
@@ -1516,7 +1675,7 @@ source address from completely blocking responses to that address. The leak
rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at
least every fourth request has a response. The minimum value is 1 and the least every fourth request has a response. The minimum value is 1 and the
maximum value is 4. maximum value is 4.
:: {blank}::
+ +
An example use of the directive is: An example use of the directive is:
+ +
@@ -1528,6 +1687,17 @@ This would reduce the response rate for IP addresses sending packets on average
more than once per 2 seconds, or sending packets in bursts of more than 16 more than once per 2 seconds, or sending packets in bursts of more than 16
packets, by up to 75% (with default *leak* of 2). packets, by up to 75% (with default *leak* of 2).
[[ntsratelimit]]*ntsratelimit* [_option_]...::
This directive enables rate limiting of NTS-KE requests. It is similar to the
<<ratelimit,*ratelimit*>> directive, except the default interval is 6
(1 connection per 64 seconds).
+
An example of the use of the directive is:
+
----
ntsratelimit interval 3 burst 1
----
[[smoothtime]]*smoothtime* _max-freq_ _max-wander_ [*leaponly*]:: [[smoothtime]]*smoothtime* _max-freq_ _max-wander_ [*leaponly*]::
The *smoothtime* directive can be used to enable smoothing of the time that The *smoothtime* directive can be used to enable smoothing of the time that
*chronyd* serves to its clients to make it easier for them to track it and keep *chronyd* serves to its clients to make it easier for them to track it and keep
@@ -1581,8 +1751,8 @@ smoothtime 50000 0.01
=== Command and monitoring access === Command and monitoring access
[[bindcmdaddress]]*bindcmdaddress* _address_:: [[bindcmdaddress]]*bindcmdaddress* _address_::
The *bindcmdaddress* directive allows you to specify an IP address of an The *bindcmdaddress* directive specifies a local IP address to which *chronyd*
interface on which *chronyd* will listen for monitoring command packets (issued will bind the UDP socket listening for monitoring command packets (issued
by *chronyc*). On systems other than Linux, the address of the interface needs by *chronyc*). On systems other than Linux, the address of the interface needs
to be already configured when *chronyd* is started. to be already configured when *chronyd* is started.
+ +
@@ -1593,9 +1763,10 @@ directory will be created on start if it does not exist. The compiled-in default
path of the socket is _@CHRONYRUNDIR@/chronyd.sock_. The socket can be path of the socket is _@CHRONYRUNDIR@/chronyd.sock_. The socket can be
disabled by setting the path to _/_. disabled by setting the path to _/_.
+ +
By default, *chronyd* binds to the loopback interface (with addresses By default, *chronyd* binds the UDP sockets to the addresses _127.0.0.1_ and
_127.0.0.1_ and _::1_). This blocks all access except from localhost. To listen _::1_ (i.e. the loopback interface). This blocks all access except from
for command packets on all interfaces, you can add the lines: localhost. To listen for command packets on all interfaces, you can add the
lines:
+ +
---- ----
bindcmdaddress 0.0.0.0 bindcmdaddress 0.0.0.0
@@ -1613,6 +1784,17 @@ An example that sets the path of the Unix domain command socket is:
bindcmdaddress /var/run/chrony/chronyd.sock bindcmdaddress /var/run/chrony/chronyd.sock
---- ----
[[bindcmddevice]]*bindcmddevice* _interface_::
The *bindcmddevice* directive binds the UDP command sockets to a network device
specified by the interface name. This directive can specify only one interface
and it is supported on Linux only.
+
An example of the directive is:
+
----
bindcmddevice eth0
----
[[cmdallow]]*cmdallow* [*all*] [_subnet_]:: [[cmdallow]]*cmdallow* [*all*] [_subnet_]::
This is similar to the <<allow,*allow*>> directive, except that it allows This is similar to the <<allow,*allow*>> directive, except that it allows
monitoring access (rather than NTP client access) to a particular subnet or monitoring access (rather than NTP client access) to a particular subnet or
@@ -1685,7 +1867,8 @@ The *rtcautotrim* directive is used to keep the RTC close to the system clock
automatically. When the system clock is synchronised and the estimated error automatically. When the system clock is synchronised and the estimated error
between the two clocks is larger than the specified threshold, *chronyd* will between the two clocks is larger than the specified threshold, *chronyd* will
trim the RTC as if the <<chronyc.adoc#trimrtc,*trimrtc*>> command in *chronyc* trim the RTC as if the <<chronyc.adoc#trimrtc,*trimrtc*>> command in *chronyc*
was issued. was issued. The trimming operation is accurate to only about 1 second, which is
the minimum effective threshold.
+ +
This directive is effective only with the <<rtcfile,*rtcfile*>> directive. This directive is effective only with the <<rtcfile,*rtcfile*>> directive.
+ +
@@ -1991,7 +2174,7 @@ from the example line above):
. Applied compensation in ppm, positive means the system clock is running . Applied compensation in ppm, positive means the system clock is running
faster than it would be without the compensation. [3.6600e-01] faster than it would be without the compensation. [3.6600e-01]
+ +
:: {blank}::
An example of the directive is: An example of the directive is:
+ +
---- ----
@@ -2024,8 +2207,9 @@ which would cause a syslog message to be generated if a system clock error of ov
0.1 seconds starts to be compensated. 0.1 seconds starts to be compensated.
[[logdir]]*logdir* _directory_:: [[logdir]]*logdir* _directory_::
This directive allows the directory where log files are written to be This directive specifies the directory for writing log files enabled by the
specified. *log* directive. If the directory does not exist, it will be created
automatically.
+ +
An example of the use of this directive is: An example of the use of this directive is:
+ +
@@ -2053,6 +2237,56 @@ sendmail binary.
=== Miscellaneous === Miscellaneous
[[confdir]]*confdir* _directory_...::
The *confdir* directive includes configuration files with the _.conf_ suffix
from a directory. The files are included in the lexicographical order of the
file names.
+
Multiple directories (up to 10) can be specified with a single *confdir*
directive. In this case, if multiple directories contain a file with the same
name, only the first file in the order of the specified directories will be
included. This enables a fragmented configuration where existing fragments can
be replaced by adding files to a different directory.
+
This directive can be used multiple times.
+
An example of the directive is:
+
----
confdir @SYSCONFDIR@/chrony.d
----
[[sourcedir]]*sourcedir* _directory_...::
The *sourcedir* directive is identical to the *confdir* directive, except the
configuration files have the _.sources_ suffix, they can only specify NTP
sources (i.e. use the *server*, *pool*, and *peer* directive), and can be
reloaded by the <<chronyc.adoc#reload,*reload sources*>> command in
*chronyc*. It is particularly useful with dynamic sources like NTP servers
received from a DHCP server, which can be written to a file specific to the
network interface by a networking script.
+
This directive can be used multiple times.
+
An example of the directive is:
+
----
sourcedir /var/run/chrony-dhcp
----
[[include]]*include* _pattern_::
The *include* directive includes a configuration file, or multiple configuration
files if a wildcard pattern is specified. Unlike with the *confdir* directive,
the full name of the files needs to be specified and at least one file is
required to exist.
+
This directive can be used multiple times.
+
An example of the directive is:
+
----
include @SYSCONFDIR@/chrony.d/*.conf
----
[[hwtimestamp]]*hwtimestamp* _interface_ [_option_]...:: [[hwtimestamp]]*hwtimestamp* _interface_ [_option_]...::
This directive enables hardware timestamping of NTP packets sent to and This directive enables hardware timestamping of NTP packets sent to and
received from the specified network interface. The network interface controller received from the specified network interface. The network interface controller
@@ -2082,8 +2316,9 @@ indicated in the _measurements.log_ file if enabled by the <<log,*log
measurements*>> directive, and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in measurements*>> directive, and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in
*chronyc*. *chronyc*.
+ +
If the specified interface is _*_, *chronyd* will try to enable HW timestamping This directive can be used multiple times to enable HW timestamping on multiple
on all available interfaces. interfaces. If the specified interface is _*_, *chronyd* will try to enable HW
timestamping on all available interfaces.
+ +
The *hwtimestamp* directive has the following options: The *hwtimestamp* directive has the following options:
+ +
@@ -2123,14 +2358,14 @@ _ntp_::::
Enables timestamping of received NTP packets. Enables timestamping of received NTP packets.
_none_:::: _none_::::
Disables timestamping of received packets. Disables timestamping of received packets.
::: {blank}:::
The most specific filter for timestamping NTP packets which is supported by the The most specific filter for timestamping NTP packets which is supported by the
NIC is selected by default. Some NICs can timestamp only PTP packets, which NIC is selected by default. Some NICs can timestamp only PTP packets, which
limits the selection to the _none_ filter. Forcing timestamping of all packets limits the selection to the _none_ filter. Forcing timestamping of all packets
with the _all_ filter when the NIC supports both _all_ and _ntp_ filters can be with the _all_ filter when the NIC supports both _all_ and _ntp_ filters can be
useful when packets are received from or on a non-standard UDP port (e.g. useful when packets are received from or on a non-standard UDP port (e.g.
specified by the *port* directive). specified by the *port* directive).
:: {blank}::
+ +
Examples of the directive are: Examples of the directive are:
+ +
@@ -2140,17 +2375,6 @@ hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
hwtimestamp * hwtimestamp *
---- ----
[[include]]*include* _pattern_::
The *include* directive includes a configuration file or multiple configuration
files if a wildcard pattern is specified. This can be useful when maintaining
configuration on multiple hosts to keep the differences in separate files.
+
An example of the directive is:
+
----
include @SYSCONFDIR@/chrony.d/*.conf
----
[[keyfile]]*keyfile* _file_:: [[keyfile]]*keyfile* _file_::
This directive is used to specify the location of the file containing symmetric This directive is used to specify the location of the file containing symmetric
keys which are shared between NTP servers and clients, or peers, in order to keys which are shared between NTP servers and clients, or peers, in order to
@@ -2326,6 +2550,20 @@ makestep 1.0 3
rtcsync rtcsync
---- ----
If the servers (or pool) support the Network Time Security (NTS)
authentication mechanism and *chronyd* is compiled with NTS support, the *nts*
option will enable a secure synchronisation to the servers. The configuration
file could look like:
----
server foo.example.net iburst nts
server bar.example.net iburst nts
server baz.example.net iburst nts
driftfile @CHRONYVARDIR@/drift
makestep 1.0 3
rtcsync
----
=== NTP client with infrequent connection to NTP servers === NTP client with infrequent connection to NTP servers
This section shows how to configure *chronyd* for computers that have This section shows how to configure *chronyd* for computers that have

View File

@@ -2,7 +2,7 @@
// //
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016 // Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2017 // Copyright (C) Miroslav Lichvar 2009-2017, 2019-2020
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as // it under the terms of version 2 of the GNU General Public License as
@@ -39,7 +39,7 @@ running.
If no commands are specified on the command line, *chronyc* will expect input If no commands are specified on the command line, *chronyc* will expect input
from the user. The prompt _chronyc>_ will be displayed when it is being run from the user. The prompt _chronyc>_ will be displayed when it is being run
from a terminal. If *chronyc*'s input or output are redirected from or to a file, from a terminal. If *chronyc*'s input or output are redirected from or to a file,
the prompt is not shown. the prompt will not be shown.
There are two ways *chronyc* can access *chronyd*. One is the Internet There are two ways *chronyc* can access *chronyd*. One is the Internet
Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is
@@ -60,9 +60,7 @@ default, the commands are accepted only from localhost (127.0.0.1 or ::1).
All other commands are allowed only through the Unix domain socket. When sent All other commands are allowed only through the Unix domain socket. When sent
over the network, *chronyd* will respond with a '`Not authorised`' error, even over the network, *chronyd* will respond with a '`Not authorised`' error, even
if it is from localhost. In chrony versions before 2.2 they were allowed if it is from localhost.
from the network if they were authenticated with a password, but that is no
longer supported.
Having full access to *chronyd* via *chronyc* is more or less equivalent to Having full access to *chronyd* via *chronyc* is more or less equivalent to
being able to modify the *chronyd*'s configuration file and restart it. being able to modify the *chronyd*'s configuration file and restart it.
@@ -183,10 +181,12 @@ speeding up or slowing down the system clock until the error has been removed,
and then returning to the system clock's normal speed. A consequence of this is and then returning to the system clock's normal speed. A consequence of this is
that there will be a period when the system clock (as read by other programs) that there will be a period when the system clock (as read by other programs)
will be different from *chronyd*'s estimate of the current true time (which it will be different from *chronyd*'s estimate of the current true time (which it
reports to NTP clients when it is operating in server mode). The value reported reports to NTP clients when it is operating as a server). The value reported
on this line is the difference due to this effect. on this line is the difference due to this effect.
*Last offset*::: *Last offset*:::
This is the estimated local offset on the last clock update. This is the estimated local offset on the last clock update. A positive value
indicates the local time (as previously estimated true time) was ahead of the
time sources.
*RMS offset*::: *RMS offset*:::
This is a long-term average of the offset value. This is a long-term average of the offset value.
*Frequency*::: *Frequency*:::
@@ -317,18 +317,23 @@ The columns are as follows:
This indicates the mode of the source. _^_ means a server, _=_ means a peer This indicates the mode of the source. _^_ means a server, _=_ means a peer
and _#_ indicates a locally connected reference clock. and _#_ indicates a locally connected reference clock.
*S*::: *S*:::
This column indicates the state of the source. This column indicates the selection state of the source.
* _*_ indicates the source to which *chronyd* is currently synchronised. * _*_ indicates the best source which is currently selected for
* _+_ indicates acceptable sources which are combined with the selected synchronisation.
source. * _+_ indicates other sources selected for synchronisation, which are combined
* _-_ indicates acceptable sources which are excluded by the combining with the best source.
algorithm. * _-_ indicates a source which is considered to be selectable for
* _?_ indicates sources to which connectivity has been lost or whose packets synchronisation, but not currently selected.
do not pass all tests. It is also shown at start-up, until at least 3 samples * _x_ indicates a source which *chronyd* thinks is a falseticker (i.e. its
have been gathered from it. time is inconsistent with a majority of other sources, or sources specified
* _x_ indicates a clock which *chronyd* thinks is a falseticker (i.e. its with the *trust* option).
time is inconsistent with a majority of other sources).
* _~_ indicates a source whose time appears to have too much variability. * _~_ indicates a source whose time appears to have too much variability.
* _?_ indicates a source which is not considered to be selectable for
synchronisation for other reasons (e.g. unreachable, not synchronised, or
does not have enough measurements).
{blank}:::
The <<selectdata,*selectdata*>> command can be used to get more details about
the selection state.
*Name/IP address*::: *Name/IP address*:::
This shows the name or the IP address of the source, or reference ID for reference This shows the name or the IP address of the source, or reference ID for reference
clocks. clocks.
@@ -414,6 +419,92 @@ This is the estimated offset of the source.
*Std Dev*::: *Std Dev*:::
This is the estimated sample standard deviation. This is the estimated sample standard deviation.
[[selectdata]]*selectdata* [*-a*] [*-v*]::
The *selectdata* command displays information specific to the selection of time
sources. If the *-a* option is specified, all sources are displayed, including
those that do not have a known address yet. With the *-v* option, extra caption
lines are shown as a reminder of the meanings of the columns.
+
An example of the output is shown below.
+
----
S Name/IP Address Auth COpts EOpts Last Score Interval
====================================================================
D foo.example.net Y ----- --TR- 4 1.0 -61ms +62ms
* bar.example.net N ----- ----- 0 1.0 -6846us +7305us
+ baz.example.net N ----- ----- 10 1.0 -7381us +7355us
----
+
The columns are as follows:
+
*S*:::
This column indicates the state of the source after the last source selection.
It is similar to the state reported by the *sources* command, but more
states are reported.
{blank}:::
The following states indicate the source is not considered selectable for
synchronisation:
* _N_ - has the *noselect* option.
* _M_ - does not have enough measurements.
* _d_ - has a root distance larger than the maximum distance (configured by the
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
* _~_ - has a jitter larger than the maximum jitter (configured by the
<<chrony.conf.adoc#maxjitter,*maxjitter*>> directive).
* _w_ - waits for other sources to get out of the _M_ state.
* _S_ - has older measurements than other sources.
* _O_ - has a stratum equal or larger than the orphan stratum (configured by
the <<chrony.conf.adoc#local,*local*>> directive).
* _T_ - does not fully agree with sources that have the *trust* option.
* _x_ - does not agree with other sources (falseticker).
{blank}:::
The following states indicate the source is considered selectable, but it is
not currently used for synchronisation:
* _W_ - waits for other sources to be selectable (required by the
<<chrony.conf.adoc#minsources,*minsources*>> directive, or
the *require* option of another source).
* _P_ - another selectable source is preferred due to the *prefer* option.
* _U_ - waits for a new measurement (after selecting a different best source).
* _D_ - has, or recently had, a root distance which is too large to be combined
with other sources (configured by the
<<chrony.conf.adoc#combinelimit,*combinelimit*>> directive).
{blank}:::
The following states indicate the source is used for synchronisation of the
local clock:
* _+_ - combined with the best source.
* _*_ - selected as the best source to update the reference data (e.g. root
delay, root dispersion).
*Name/IP address*:::
This column shows the name or IP address of the source if it is an NTP server,
or the reference ID if it is a reference clock.
*Auth*:::
This column indicites whether an authentication mechanism is enabled for the
source. _Y_ means yes and _N_ means no.
*COpts*:::
This column displays the configured selection options of the source.
* _N_ indicates the *noselect* option.
* _P_ indicates the *prefer* option.
* _T_ indicates the *trust* option.
* _R_ indicates the *require* option.
*EOpts*:::
This column displays the current effective selection options of the source,
which can be different from the configured options due to the authentication
selection mode (configured by the
<<chrony.conf.adoc#authselmode,*authselmode*>> directive). The symbols are the
same as in the *COpts* column.
*Last*:::
This column displays how long ago was the last measurement of the source made
when the selection was performed.
*Score*:::
This column displays the current score against the source in the _*_ state. The
scoring system avoids frequent reselection when multiple sources have a similar
root distance. A value larger than 1 indicates this source was better than the
_*_ source in recent selections. If the score reaches 10, the best source will
be reselected and the scores will be reset to 1.
*Interval*:::
This column displays the lower and upper endpoint of the interval which was
expected to contain the true offset of the local clock considering the root
distance at the time of the selection.
[[reselect]]*reselect*:: [[reselect]]*reselect*::
To avoid excessive switching between sources, *chronyd* can stay synchronised To avoid excessive switching between sources, *chronyd* can stay synchronised
to a source even when it is not currently the best one among the available to a source even when it is not currently the best one among the available
@@ -454,6 +545,77 @@ the offline state.
the name of the server or peer was not resolved to an address yet; this source is the name of the server or peer was not resolved to an address yet; this source is
not visible in the *sources* and *sourcestats* reports. not visible in the *sources* and *sourcestats* reports.
[[authdata]]*authdata* [*-a*]::
The *authdata* command displays information specific to authentication of NTP
sources. If the *-a* option is specified, all sources are displayed, including
those that do not have a known address yet. An example of the output is
shown below.
+
----
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
foo.example.net NTS 1 15 256 135m 0 0 8 100
bar.example.net SK 30 13 128 - 0 0 0 0
baz.example.net - 0 0 0 - 0 0 0 0
----
+
The columns are as follows:
+
*Name/IP address*:::
This column shows the name or the IP address of the source.
*Mode*:::
This column shows which mechanism authenticates NTP packets received from the
source. _NTS_ means Network Time Security, _SK_ means a symmetric key, and _-_
means authentication is disabled.
*KeyID*:::
This column shows an identifier of the key used for authentication. With a
symmetric key, it is the ID from the <<chrony.conf.adoc#keyfile,key file>>.
With NTS, it is a number starting at zero and incremented by one with each
successful key establishment using the NTS-KE protocol, i.e. it shows how many
times the key establishment was performed with this source.
*Type*:::
This columns shows an identifier of the algorithm used for authentication.
With a symmetric key, it is the hash function or cipher specified in the key
file. With NTS, it is an authenticated encryption with associated data (AEAD)
algorithm, which is negotiated in the NTS-KE protocol. The following values can
be reported:
* 1: MD5
* 2: SHA1
* 3: SHA256
* 4: SHA384
* 5: SHA512
* 6: SHA3-224
* 7: SHA3-256
* 8: SHA3-384
* 9: SHA3-512
* 10: TIGER
* 11: WHIRLPOOL
* 13: AES128
* 14: AES256
* 15: AEAD-AES-SIV-CMAC-256
*KLen*:::
This column shows the length of the key in bits.
*Last*:::
This column shows how long ago the last successful key establishment was
performed. It is in seconds, or letters _m_, _h_, _d_ or _y_ indicate minutes,
hours, days, or years.
*Atmp*:::
This column shows the number of attempts to perform the key establishment since
the last successful key establishment. A number larger than 1 indicates a
problem with the network or server.
*NAK*:::
This column shows whether an NTS NAK was received since the last request.
A NAK indicates that authentication failed on the server side due to
*chronyd* using a cookie which is no longer valid and that it needs to perform
the key establishment again in order to get new cookies.
*Cook*:::
This column shows the number of NTS cookies that *chronyd* currently has. If
the key establishment was successful, a number smaller than 8 indicates a
problem with the network or server.
*CLen*:::
This column shows the length in bytes of the NTS cookie which will be used in
the next request.
[[ntpdata]]*ntpdata* [_address_]:: [[ntpdata]]*ntpdata* [_address_]::
The *ntpdata* command displays the last valid measurement and other The *ntpdata* command displays the last valid measurement and other
NTP-specific information about the specified NTP source, or all NTP sources NTP-specific information about the specified NTP source, or all NTP sources
@@ -626,7 +788,7 @@ alternative to the form with mask.
_address_::: _address_:::
This is an IP address or a hostname. The burst command is applied only to This is an IP address or a hostname. The burst command is applied only to
that source. that source.
:: {blank}::
+ +
If no _mask_ or _masked-address_ arguments are provided, every source will be If no _mask_ or _masked-address_ arguments are provided, every source will be
matched. matched.
@@ -771,6 +933,11 @@ Sources that stop responding will be replaced with newly resolved addresses
automatically after 8 polling intervals, but this command can still be useful automatically after 8 polling intervals, but this command can still be useful
to replace them immediately and not wait until they are marked as unreachable. to replace them immediately and not wait until they are marked as unreachable.
[[reload]]*reload* *sources*::
The *reload sources* command causes *chronyd* to re-read all _*.sources_ files
from the directories specified by the
<<chrony.conf.adoc#sourcedir,*sourcedir*>> directive.
[[sourcename]]*sourcename* _address_:: [[sourcename]]*sourcename* _address_::
The *sourcename* command prints the original hostname or address that was The *sourcename* command prints the original hostname or address that was
specified for an NTP source in the configuration file, or the *add* command. specified for an NTP source in the configuration file, or the *add* command.
@@ -816,7 +983,7 @@ The columns are as as follows:
. The regression residual at this point, in seconds. This allows '`outliers`' . The regression residual at this point, in seconds. This allows '`outliers`'
to be easily spotted, so that they can be deleted using the *manual delete* to be easily spotted, so that they can be deleted using the *manual delete*
command. command.
:: {blank}::
+ +
The *delete* form of the command deletes a single sample. The parameter is the The *delete* form of the command deletes a single sample. The parameter is the
index of the sample, as shown in the first column of the output from *manual index of the sample, as shown in the first column of the output from *manual
@@ -887,10 +1054,17 @@ This command can be used to examine the effect of a series of *allow*, *allow
all*, *deny*, and *deny all* commands specified either via *chronyc*, or in all*, *deny*, and *deny all* commands specified either via *chronyc*, or in
*chronyd*'s configuration file. *chronyd*'s configuration file.
[[clients]]*clients*:: [[clients]]*clients* [*-p* _packets_] [*-k*] [*-r*]::
This command shows a list of clients that have accessed the server, through This command shows a list of clients that have accessed the server, through
either the NTP or command ports. It does not include accesses over the NTP, command, or NTS-KE port. It does not include accesses over the Unix
the Unix domain command socket. There are no arguments. domain command socket.
+
The *-p* option specifies the minimum number of received NTP or command
packets, or accepted NTS-KE connections, needed to include a client in the
list. The default value is 0, i.e. all clients are reported. With the *-k*
option the last four columns will show the NTS-KE accesses instead of command
accesses. If the *-r* option is specified, *chronyd* will reset the counters of
received and dropped packets or connections after reporting the current values.
+ +
An example of the output is: An example of the output is:
+ +
@@ -915,20 +1089,22 @@ The columns are as follows:
. The average interval between NTP packets. . The average interval between NTP packets.
. The average interval between NTP packets after limiting the response rate. . The average interval between NTP packets after limiting the response rate.
. Time since the last NTP packet was received . Time since the last NTP packet was received
. The number of command packets received from the client. . The number of command packets or NTS-KE connections received/accepted from
. The number of command packets dropped to limit the response rate. the client.
. The average interval between command packets. . The number of command packets or NTS-KE connections dropped to limit the
. Time since the last command packet was received. response rate.
. The average interval between command packets or NTS-KE connections.
. Time since the last command packet or NTS-KE connection was
received/accepted.
[[serverstats]]*serverstats*:: [[serverstats]]*serverstats*::
The *serverstats* command displays how many valid NTP and command requests The *serverstats* command displays how many valid NTP and command requests, and
*chronyd* as a server received from clients, how many of them were dropped to NTS-KE connections, *chronyd* operating as a server received from clients, and
limit the response rate as configured by the how many of them were dropped due to rate limiting. It also displays how many
<<chrony.conf.adoc#ratelimit,*ratelimit*>> and
<<chrony.conf.adoc#cmdratelimit,*cmdratelimit*>> directives, and how many
client log records were dropped due to the memory limit configured by the client log records were dropped due to the memory limit configured by the
<<chrony.conf.adoc#clientloglimit,*clientloglimit*>> directive. An example of <<chrony.conf.adoc#clientloglimit,*clientloglimit*>> directive and how many of
the output is shown below. the NTP requests (from those which were not dropped) were authenticated. An
example of the output is shown below.
+ +
---- ----
NTP packets received : 1598 NTP packets received : 1598
@@ -936,6 +1112,9 @@ NTP packets dropped : 8
Command packets received : 19 Command packets received : 19
Command packets dropped : 0 Command packets dropped : 0
Client log records dropped : 0 Client log records dropped : 0
NTS-KE connections accepted: 3
NTS-KE connections dropped : 0
Authenticated NTP packets : 189
---- ----
[[allow]]*allow* [*all*] [_subnet_]:: [[allow]]*allow* [*all*] [_subnet_]::
@@ -1121,7 +1300,7 @@ more than 1 second away from the system clock):
error). error).
. Save the RTC parameters to the RTC file (specified with the . Save the RTC parameters to the RTC file (specified with the
<<chrony.conf.adoc#rtcfile,*rtcfile*>> directive in the configuration file). <<chrony.conf.adoc#rtcfile,*rtcfile*>> directive in the configuration file).
:: {blank}::
+ +
The last step is done as a precaution against the computer suffering a power The last step is done as a precaution against the computer suffering a power
failure before either the daemon exits or the <<writertc,*writertc*>> command failure before either the daemon exits or the <<writertc,*writertc*>> command
@@ -1154,10 +1333,7 @@ purged. An example of how to do this is shown below.
---- ----
# mv /var/log/chrony/measurements.log /var/log/chrony/measurements1.log # mv /var/log/chrony/measurements.log /var/log/chrony/measurements1.log
# chronyc cyclelogs # chronyc cyclelogs
# ls -l /var/log/chrony # rm /var/log/chrony/measurements1.log
-rw-r--r-- 1 root root 0 Jun 8 18:17 measurements.log
-rw-r--r-- 1 root root 12345 Jun 8 18:17 measurements1.log
# rm -f measurements1.log
---- ----
[[dump]]*dump*:: [[dump]]*dump*::
@@ -1177,12 +1353,14 @@ also re-reads the server NTS keys if
<<chrony.conf.adoc#ntsrotate,automatic rotation>> is disabled in the <<chrony.conf.adoc#ntsrotate,automatic rotation>> is disabled in the
configuration file. configuration file.
[[reset]]*reset*:: [[reset]]*reset* *sources*::
The *reset* command causes *chronyd* to drop all measurements and switch to the The *reset sources* command causes *chronyd* to drop all measurements and
unsynchronised state. This command can help *chronyd* with recovery when the switch to the unsynchronised state. This command can help *chronyd* with
measurements are known to be no longer valid or accurate, e.g. due to moving recovery when the measurements are known to be no longer valid or accurate,
the computer to a different network, or resuming the computer from a low-power e.g. due to moving the computer to a different network, or resuming the
state (which resets the system clock). computer from a low-power state (which resets the system clock). *chronyd* will
drop the measurements automatically when it detects the clock has made an
unexpected jump, but the detection is not completely reliable.
[[shutdown]]*shutdown*:: [[shutdown]]*shutdown*::
The *shutdown* command causes *chronyd* to exit. This is equivalent to sending The *shutdown* command causes *chronyd* to exit. This is equivalent to sending
@@ -1249,7 +1427,13 @@ keygen 73 SHA1 256
+ +
which generates a 256-bit SHA1 key with number 73. The printed line should which generates a 256-bit SHA1 key with number 73. The printed line should
then be securely transferred and added to the key files on both server and then be securely transferred and added to the key files on both server and
client, or peers. client, or peers. A different key should be generated for each client or peer.
+
An example using the AES128 cipher is:
+
----
keygen 151 AES128
----
[[exit]]*exit*:: [[exit]]*exit*::
[[quit]]*quit*:: [[quit]]*quit*::

View File

@@ -55,7 +55,7 @@ IPv6 sockets will be created.
*-f* _file_:: *-f* _file_::
This option can be used to specify an alternate location for the configuration This option can be used to specify an alternate location for the configuration
file. The default value is _@SYSCONFDIR@/chrony.conf_. file. The compiled-in default value is _@SYSCONFDIR@/chrony.conf_.
*-n*:: *-n*::
When run in this mode, the program will not detach itself from the terminal. When run in this mode, the program will not detach itself from the terminal.
@@ -76,6 +76,12 @@ the log file, syslog, or terminal. The following levels can be specified:
0 (informational), 1 (warning), 2 (non-fatal error), and 3 (fatal error). The 0 (informational), 1 (warning), 2 (non-fatal error), and 3 (fatal error). The
default value is 0. default value is 0.
*-p*::
When run in this mode, *chronyd* will print the configuration and exit. It will
not detach from the terminal. This option can be used to verify the syntax of
the configuration and get the whole configuration, even if it is split into
multiple files and read by the *include* or *confdir* directive.
*-q*:: *-q*::
When run in this mode, *chronyd* will set the system clock once and exit. It When run in this mode, *chronyd* will set the system clock once and exit. It
will not detach from the terminal. will not detach from the terminal.
@@ -131,7 +137,7 @@ running, but still allow it to adjust the frequency of the system clock.
*-u* _user_:: *-u* _user_::
This option sets the name of the system user to which *chronyd* will switch This option sets the name of the system user to which *chronyd* will switch
after start in order to drop root privileges. It overrides the after start in order to drop root privileges. It overrides the
<<chrony.conf.adoc#user,*user*>> directive. The default value is <<chrony.conf.adoc#user,*user*>> directive. The compiled-in default value is
_@DEFAULT_USER@_. _@DEFAULT_USER@_.
+ +
On Linux, *chronyd* needs to be compiled with support for the *libcap* library. On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
@@ -139,6 +145,16 @@ On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent. range of privileged system calls on behalf of the parent.
*-U*::
This option disables a check for root privileges to allow *chronyd* to be
started under a non-root user, assuming the process will have all capabilities
(e.g. provided by the service manager) and access to all files, directories,
and devices, needed to operate correctly in the specified configuration. Note
that different capabilities might be needed with different configurations and
different Linux kernel versions. Starting *chronyd* under a non-root user is
not recommended when the configuration is not known, or at least limited to
specific directives.
*-F* _level_:: *-F* _level_::
This option configures a system call filter when *chronyd* is compiled with This option configures a system call filter when *chronyd* is compiled with
support for the Linux secure computing (seccomp) facility. In level 1 the support for the Linux secure computing (seccomp) facility. In level 1 the
@@ -146,10 +162,10 @@ process is killed when a forbidden system call is made, in level -1 the SIGSYS
signal is thrown instead and in level 0 the filter is disabled. The default signal is thrown instead and in level 0 the filter is disabled. The default
value is 0. value is 0.
+ +
It's recommended to enable the filter only when it's known to work on the It is recommended to enable the filter only when it is known to work on the
version of the system where *chrony* is installed as the filter needs to allow version of the system where *chrony* is installed as the filter needs to allow
also system calls made from libraries that *chronyd* is using (e.g. libc) and also system calls made from libraries that *chronyd* is using (e.g. libc) and
different versions or implementations of the libraries may make different different versions or implementations of the libraries might make different
system calls. If the filter is missing some system call, *chronyd* could be system calls. If the filter is missing some system call, *chronyd* could be
killed even in normal operation. killed even in normal operation.
@@ -168,10 +184,8 @@ This mode is only supported on Linux.
This option disables the control of the system clock. *chronyd* will not try to This option disables the control of the system clock. *chronyd* will not try to
make any adjustments of the clock. It will assume the clock is free running and make any adjustments of the clock. It will assume the clock is free running and
still track its offset and frequency relative to the estimated true time. This still track its offset and frequency relative to the estimated true time. This
option allows *chronyd* to run without the capability to adjust or set the option allows *chronyd* to be started without the capability to adjust or set
system clock (e.g. in some containers) in order to operate as an NTP server. It the system clock (e.g. in some containers) to operate as an NTP server.
is not recommended to run *chronyd* (with or without *-x*) when another process
is controlling the system clock.
*-v*:: *-v*::
With this option *chronyd* will print version number to the terminal and exit. With this option *chronyd* will print version number to the terminal and exit.

View File

@@ -1,7 +1,7 @@
// This file is part of chrony // This file is part of chrony
// //
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2014-2016 // Copyright (C) Miroslav Lichvar 2014-2016, 2020
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as // it under the terms of version 2 of the GNU General Public License as
@@ -24,17 +24,20 @@
=== How does `chrony` compare to `ntpd`? === How does `chrony` compare to `ntpd`?
`chronyd` was designed to work well in a wide range of conditions and it can `chrony` and `ntpd` are two different implementations of the Network Time
usually synchronise the system clock faster and with better time accuracy. It Protocol (NTP).
doesn't implement some of the less useful NTP modes like broadcast client or
multicast server/client. `chrony` is a newer implementation, which was designed to work well in a wider
range of conditions. It can usually synchronise the system clock faster and
with better time accuracy. It has many features, but it does not implement some
of the less useful NTP modes like broadcast client or multicast server/client.
If your computer is connected to the Internet only for few minutes at a time, If your computer is connected to the Internet only for few minutes at a time,
the network connection is often congested, you turn your computer off or the network connection is often congested, you turn your computer off or
suspend it frequently, the clock is not very stable (e.g. there are rapid suspend it frequently, the clock is not very stable (e.g. there are rapid
changes in the temperature or it's a virtual machine), or you want to use NTP changes in the temperature or it is a virtual machine), or you want to use NTP
on an isolated network with no hardware reference clocks in sight, `chrony` on an isolated network with no hardware reference clocks in sight, `chrony`
will probably work much better for you. will probably work better for you.
For a more detailed comparison of features and performance, see the For a more detailed comparison of features and performance, see the
https://chrony.tuxfamily.org/comparison.html[comparison page] on the `chrony` https://chrony.tuxfamily.org/comparison.html[comparison page] on the `chrony`
@@ -59,7 +62,7 @@ slewing, which would take a very long time. The `makestep` directive does
that. that.
In order to keep the real-time clock (RTC) close to the true time, so the In order to keep the real-time clock (RTC) close to the true time, so the
system time is reasonably close to the true time when it's initialised on the system time is reasonably close to the true time when it is initialised on the
next boot from the RTC, the `rtcsync` directive enables a mode in which the next boot from the RTC, the `rtcsync` directive enables a mode in which the
system time is periodically copied to the RTC. It is supported on Linux and system time is periodically copied to the RTC. It is supported on Linux and
macOS. macOS.
@@ -75,52 +78,36 @@ makestep 1 3
rtcsync rtcsync
---- ----
=== How do I make an NTP server from an NTP client? === How do I make an NTP server?
You need to add an `allow` directive to the _chrony.conf_ file in order to open You need to add an `allow` directive to the _chrony.conf_ file in order to open
the NTP port and allow `chronyd` to reply to client requests. `allow` with no the NTP port and allow `chronyd` to reply to client requests. `allow` with no
specified subnet allows access from all IPv4 and IPv6 addresses. specified subnet allows access from all IPv4 and IPv6 addresses.
=== I have several computers on a LAN. Should be all clients of an external server? === Should all computers on a LAN be clients of an external server?
The best configuration is usually to make one computer the server, with It depends on the requirements. Usually, the best configuration is to make one
the others as clients of it. Add a `local` directive to the server's computer the server, with the others as clients of it. Add a `local` directive
_chrony.conf_ file. This configuration will be better because to the server's _chrony.conf_ file. This configuration will be better because
* the load on the external connection is less * the load on the external connection is less
* the load on the external NTP server(s) is less * the load on the external NTP server(s) is less
* if your external connection goes down, the computers on the LAN * if your external connection goes down, the computers on the LAN
will maintain a common time with each other. will maintain a common time with each other.
=== Must I specify servers by IP address if DNS is not available on chronyd start? === Must I specify servers by IP address if DNS is not available on `chronyd` start?
No. Starting from version 1.25, `chronyd` will keep trying to resolve No, `chronyd` will keep trying to resolve
the names specified by the `server`, `pool`, and `peer` directives in an the names specified by the `server`, `pool`, and `peer` directives in an
increasing interval until it succeeds. The `online` command can be issued from increasing interval until it succeeds. The `online` command can be issued from
`chronyc` to force `chronyd` to try to resolve the names immediately. `chronyc` to force `chronyd` to try to resolve the names immediately.
=== How can I make `chronyd` more secure? === How can I make `chronyd` more secure?
If you don't need to serve time to NTP clients or peers, you can add `port 0` If you do not need to use `chronyc`, or you want to run `chronyc` only
to the _chrony.conf_ file to completely disable the NTP server functionality
and prevent NTP requests from reaching `chronyd`. Starting from version 2.0,
the NTP server port is open only when client access is allowed by the `allow`
directive or command, an NTP peer is configured, or the `broadcast` directive
is used.
If you don't need to use `chronyc` remotely, you can add the following
directives to the configuration file to bind the command sockets to the
loopback interface. This is done by default since version 2.0.
----
bindcmdaddress 127.0.0.1
bindcmdaddress ::1
----
If you don't need to use `chronyc` at all or you need to run `chronyc` only
under the root or _chrony_ user (which can access `chronyd` through a Unix under the root or _chrony_ user (which can access `chronyd` through a Unix
domain socket since version 2.2), you can disable the internet command sockets domain socket), you can disable the IPv4 and IPv6 command sockets (by default
completely by adding `cmdport 0` to the configuration file. listening on localhost) by adding `cmdport 0` to the configuration file.
You can specify an unprivileged user with the `-u` option, or the `user` You can specify an unprivileged user with the `-u` option, or the `user`
directive in the _chrony.conf_ file, to which `chronyd` will switch after start directive in the _chrony.conf_ file, to which `chronyd` will switch after start
@@ -133,23 +120,23 @@ a very limited range of privileged system calls on behalf of the parent.
Also, if `chronyd` is compiled with support for the Linux secure computing Also, if `chronyd` is compiled with support for the Linux secure computing
(seccomp) facility, you can enable a system call filter with the `-F` option. (seccomp) facility, you can enable a system call filter with the `-F` option.
It will significantly reduce the kernel attack surface and possibly prevent It will significantly reduce the kernel attack surface and possibly prevent
kernel exploits from the `chronyd` process if it's compromised. It's kernel exploits from the `chronyd` process if it is compromised. It is
recommended to enable the filter only when it's known to work on the version of recommended to enable the filter only when it is known to work on the version of
the system where `chrony` is installed as the filter needs to allow also system the system where `chrony` is installed as the filter needs to allow also system
calls made from libraries that `chronyd` is using (e.g. libc) and different calls made from libraries that `chronyd` is using (e.g. libc) and different
versions or implementations of the libraries may make different system calls. versions or implementations of the libraries might make different system calls.
If the filter is missing some system call, `chronyd` could be killed even in If the filter is missing some system call, `chronyd` could be killed even in
normal operation. normal operation.
=== How can I improve the accuracy of the system clock with NTP sources? === How can I improve the accuracy of the system clock with NTP sources?
Select NTP servers that are well synchronised, stable and close to your Select NTP servers that are well synchronised, stable and close to your
network. It's better to use more than one server, three or four is usually network. It is better to use more than one server, three or four is usually
recommended as the minimum, so `chronyd` can detect servers that serve false recommended as the minimum, so `chronyd` can detect servers that serve false
time and combine measurements from multiple sources. time and combine measurements from multiple sources.
If you have a network card with hardware timestamping supported on Linux, it If you have a network card with hardware timestamping supported on Linux, it
can be enabled by the *hwtimestamp* directive in the _chrony.conf_ file. It can be enabled by the `hwtimestamp` directive in the _chrony.conf_ file. It
should make local receive and transmit timestamps of NTP packets much more should make local receive and transmit timestamps of NTP packets much more
accurate. accurate.
@@ -163,8 +150,8 @@ default values are 6 (64 seconds) for `minpoll`, 10 (1024 seconds) for
`maxpoll` and 8 (samples) for `polltarget`. The default values should be used `maxpoll` and 8 (samples) for `polltarget`. The default values should be used
for general servers on the Internet. With your own NTP servers, or if you have for general servers on the Internet. With your own NTP servers, or if you have
permission to poll some servers more frequently, setting these options for permission to poll some servers more frequently, setting these options for
shorter polling intervals may significantly improve the accuracy of the system shorter polling intervals might significantly improve the accuracy of the
clock. system clock.
The optimal polling interval depends mainly on two factors, stability of the The optimal polling interval depends mainly on two factors, stability of the
network latency and stability of the system clock (which mainly depends on the network latency and stability of the system clock (which mainly depends on the
@@ -174,7 +161,7 @@ temperature change).
Generally, if the `sourcestats` command usually reports a small number of Generally, if the `sourcestats` command usually reports a small number of
samples retained for a source (e.g. fewer than 16), a shorter polling interval samples retained for a source (e.g. fewer than 16), a shorter polling interval
should be considered. If the number of samples is usually at the maximum of 64, should be considered. If the number of samples is usually at the maximum of 64,
a longer polling interval may work better. a longer polling interval might work better.
An example of the directive for an NTP server on the Internet that you are An example of the directive for an NTP server on the Internet that you are
allowed to poll frequently could be allowed to poll frequently could be
@@ -210,7 +197,7 @@ server ntp.local minpoll 2 maxpoll 4 xleave
When combined with local hardware timestamping, good network switches, and even When combined with local hardware timestamping, good network switches, and even
shorter polling intervals, a sub-microsecond accuracy and stability of a few shorter polling intervals, a sub-microsecond accuracy and stability of a few
tens of nanoseconds may be possible. For example: tens of nanoseconds might be possible. For example:
---- ----
server ntp.local minpoll 0 maxpoll 0 xleave server ntp.local minpoll 0 maxpoll 0 xleave
@@ -221,10 +208,11 @@ For best stability, the CPU should be running at a constant frequency (i.e.
disabled power saving and performance boosting). Energy-Efficient Ethernet disabled power saving and performance boosting). Energy-Efficient Ethernet
(EEE) should be disabled in the network. The switches should be configured to (EEE) should be disabled in the network. The switches should be configured to
prioritize NTP packets, especially if the network is expected to be heavily prioritize NTP packets, especially if the network is expected to be heavily
loaded. loaded. The `dscp` directive can be used to set the Differentiated Services
Code Point in transmitted NTP packets if needed.
If it is acceptable for NTP clients in the network to send requests at an If it is acceptable for NTP clients in the network to send requests at a high
excessive rate, a sub-second polling interval may be specified. A median filter rate, a sub-second polling interval can be specified. A median filter
can be enabled in order to update the clock at a reduced rate with more stable can be enabled in order to update the clock at a reduced rate with more stable
measurements. For example: measurements. For example:
@@ -237,7 +225,7 @@ hwtimestamp eth0 minpoll -6
Yes. With the `-q` option `chronyd` will set the system clock once and exit. Yes. With the `-q` option `chronyd` will set the system clock once and exit.
With the `-Q` option it will print the measured offset without setting the With the `-Q` option it will print the measured offset without setting the
clock. If you don't want to use a configuration file, NTP servers can be clock. If you do not want to use a configuration file, NTP servers can be
specified on the command line. For example: specified on the command line. For example:
---- ----
@@ -249,17 +237,28 @@ well synchronised and responding to all requests. If not synchronised or
responding, it would take about 10 seconds for `chronyd` to give up and exit responding, it would take about 10 seconds for `chronyd` to give up and exit
with a non-zero status. A faster configuration is possible. A single server can with a non-zero status. A faster configuration is possible. A single server can
be used instead of four servers, the number of measurements can be reduced with be used instead of four servers, the number of measurements can be reduced with
the `maxsamples` option, and a timeout can be specified with the `-t` option. the `maxsamples` option to one (supported in `chrony` version 4.0), and a
The following command would take only up to about 1 second. timeout can be specified with the `-t` option. The following command would take
only up to about one second.
---- ----
# chronyd -q -t 1 'server pool.ntp.org iburst maxsamples 1' # chronyd -q -t 1 'server pool.ntp.org iburst maxsamples 1'
---- ----
It is not recommended to run `chronyd` with the `-q` option periodically (e.g.
from a cron job) as a replacement for the daemon mode, because it performs
significantly worse (e.g. the clock is stepped and its frequency is not
corrected). If you must run it this way and you are using a public NTP server,
make sure `chronyd` does not always start around the first second of a minute,
e.g. by adding a random sleep before the `chronyd` command. Public servers
typically receive large bursts of requests around the first second as there is
a large number of NTP clients started from cron with no delay.
=== Can `chronyd` be configured to control the clock like `ntpd`? === Can `chronyd` be configured to control the clock like `ntpd`?
It is not possible to perfectly emulate `ntpd`, but there are some options that It is not possible to perfectly emulate `ntpd`, but there are some options that
can configure `chronyd` to behave more like `ntpd`. can configure `chronyd` to behave more like `ntpd` if there is a reason to
prefer that.
In the following example the `minsamples` directive slows down the response to In the following example the `minsamples` directive slows down the response to
changes in the frequency and offset of the clock. The `maxslewrate` and changes in the frequency and offset of the clock. The `maxslewrate` and
@@ -279,10 +278,56 @@ maxchange 1000 1 1
maxclockerror 15 maxclockerror 15
---- ----
Note that increasing `minsamples` may cause the offsets in the `tracking` and Note that increasing `minsamples` might cause the offsets in the `tracking` and
`sourcestats` reports/logs to be significantly smaller than the actual offsets `sourcestats` reports/logs to be significantly smaller than the actual offsets
and be unsuitable for monitoring. and be unsuitable for monitoring.
=== Can NTP server be separated from NTP client?
Yes, it is possible to run multiple instances of `chronyd` on the same
computer. One can be configured as an NTP client, and another as a server. They
need to use different pidfiles, NTP ports, command ports, and Unix domain
command sockets. The server instance should be started with the `-x` option to
avoid touching the clock. It can be configured to serve the system time with
the `local` directive, or synchronise its NTP clock to the client instance
running on localhost using a non-standard NTP port.
On Linux, starting with `chrony` version 4.0, it is also possible to run
multiple server instances sharing a port to utilise multiple cores of the CPU.
Note that the client/server interleaved mode requires that all packets from an
address are handled by the same server instance.
=== Should be a leap smear enabled on NTP server?
With the `smoothtime` and `leapsecmode` directives it is possible to enable a
server leap smear in order to hide leap seconds from clients and force them to
follow a slow server's adjustment instead.
This feature should be used only in local networks and only when necessary,
e.g. when the clients cannot be configured to handle the leap seconds as
needed, or their number is so large that configuring them all would be
impractical. The clients should use only one leap-smearing server, or multiple
identically configured leap-smearing servers. Note that some clients can get
leap seconds from external sources (e.g. with the `leapsectz` directive in
`chrony`) and they will not work correctly with a leap smearing server.
=== Does `chrony` support PTP?
No, the Precision Time Protocol (PTP) is not supported and there are no plans
to support it. It is a complex protocol, which shares some issues with the
NTP broadcast mode. One of the main differences between NTP and PTP is that PTP
was designed to be easily supported in hardware (e.g. network switches and
routers) in order to make more stable and accurate measurements. PTP relies on
the hardware support. NTP does not rely on any support in the hardware, but if
it had the same support as PTP, it could perform equally well.
On Linux, `chrony` supports hardware clocks that some NICs have for PTP. They
are called PTP hardware clocks (PHC). They can be used as reference clocks
(specified by the `refclock` directive) and for hardware timestamping of NTP
packets (enabled by the `hwtimestamp` directive) if the NIC can timestamp other
packets than PTP, which is usually the case at least for transmitted packets.
The `ethtool -T` command can be used to verify the timestamping support.
=== What happened to the `commandkey` and `generatecommandkey` directives? === What happened to the `commandkey` and `generatecommandkey` directives?
They were removed in version 2.2. Authentication is no longer supported in the They were removed in version 2.2. Authentication is no longer supported in the
@@ -299,11 +344,11 @@ following questions.
=== Behind a firewall? === Behind a firewall?
Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it's Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it
zero, it means `chronyd` did not get any valid responses from the NTP server is zero, it means `chronyd` did not get any valid responses from the NTP server
you are trying to use. If there is a firewall between you and the server, the you are trying to use. If there is a firewall between you and the server, the
packets may be blocked. Try using a tool like `wireshark` or `tcpdump` to see packets might be blocked. Try using a tool like `wireshark` or `tcpdump` to see
if you're getting any responses from the server. if you are getting any responses from the server.
When `chronyd` is receiving responses from the servers, the output of the When `chronyd` is receiving responses from the servers, the output of the
`sources` command issued few minutes after `chronyd` start might look like `sources` command issued few minutes after `chronyd` start might look like
@@ -320,9 +365,10 @@ MS Name/IP address Stratum Poll Reach LastRx Last sample
=== Are NTP servers specified with the `offline` option? === Are NTP servers specified with the `offline` option?
Check that you're using ``chronyc``'s `online` and `offline` commands Check that the ``chronyc``'s `online` and `offline` commands are used
appropriately. The `activity` command prints the number of sources that are appropriately (e.g. in the system networking scripts). The `activity` command
currently online and offline. For example: prints the number of sources that are currently online and offline. For
example:
---- ----
200 OK 200 OK
@@ -349,9 +395,9 @@ makestep 1 3
---- ----
the clock would be stepped in the first three updates if its offset was larger the clock would be stepped in the first three updates if its offset was larger
than one second. Normally, it's recommended to allow the step only in the first than one second. Normally, it is recommended to allow the step only in the first
few updates, but in some cases (e.g. a computer without an RTC or virtual few updates, but in some cases (e.g. a computer without an RTC or virtual
machine which can be suspended and resumed with an incorrect time) it may be machine which can be suspended and resumed with an incorrect time) it might be
necessary to allow the step on any clock update. The example above would change necessary to allow the step on any clock update. The example above would change
to to
@@ -363,7 +409,7 @@ makestep 1 -1
A common issue with Windows NTP servers is that they report a very large root A common issue with Windows NTP servers is that they report a very large root
dispersion (e.g. three seconds or more), which causes `chronyd` to ignore the dispersion (e.g. three seconds or more), which causes `chronyd` to ignore the
server for being too inaccurate. The `sources` command may show a valid server for being too inaccurate. The `sources` command might show a valid
measurement, but the server is not selected for synchronisation. You can check measurement, but the server is not selected for synchronisation. You can check
the root dispersion of the server with the ``chronyc``'s `ntpdata` command. the root dispersion of the server with the ``chronyc``'s `ntpdata` command.
@@ -374,6 +420,29 @@ synchronisation to such a server. For example:
maxdistance 16.0 maxdistance 16.0
---- ----
=== An unreachable source is selected?
When `chronyd` is configured with multiple time sources, it tries to select the
most accurate and stable sources for synchronisation of the system clock. They
are marked with the _*_ or _+_ symbol in the report printed by the `sources`
command.
When the best source (marked with the _*_ symbol) becomes unreachable (e.g. NTP
server stops responding), or the measurements are no longer accepted (e.g. a
change in network routing adds a delay), `chronyd` will not immediately switch
to the second best source in an attempt to minimise the error of the clock. It
will let the clock run free for as long as its estimated error (in terms of
root distance) based on previous measurements is smaller than the estimated
error of the second source, and there is still an interval which contains some
measurements from both sources.
If the first source was significantly better than the second source, it can
take many hours (up to 64 times the maximum polling interval) before the second
source is selected. If you do not like this behaviour, you can force a faster
reselection by increasing the clock error rate (`maxclockerror` directive),
shortening the polling interval (`maxpoll` option), or reducing the number of
samples (`maxsamples` option).
=== Using a PPS reference clock? === Using a PPS reference clock?
A pulse-per-second (PPS) reference clock requires a non-PPS time source to A pulse-per-second (PPS) reference clock requires a non-PPS time source to
@@ -409,31 +478,33 @@ to be used for synchronisation.
When accessing `chronyd` remotely, make sure that the _chrony.conf_ file (on When accessing `chronyd` remotely, make sure that the _chrony.conf_ file (on
the computer where `chronyd` is running) has a `cmdallow` entry for the the computer where `chronyd` is running) has a `cmdallow` entry for the
computer you are running `chronyc` on and an appropriate `bindcmdaddress` computer you are running `chronyc` on and an appropriate `bindcmdaddress`
directive. This isn't necessary for localhost. directive. This is not necessary for localhost.
Perhaps `chronyd` is not running. Try using the `ps` command (e.g. on Linux, Perhaps `chronyd` is not running. Try using the `ps` command (e.g. on Linux,
`ps -auxw`) to see if it's running. Or try `netstat -a` and see if the ports `ps -auxw`) to see if it is running. Or try `netstat -a` and see if the UDP
123/udp and 323/udp are listening. If `chronyd` is not running, you may have a port 323 is listening. If `chronyd` is not running, you might have a problem
problem with the way you are trying to start it (e.g. at boot time). with the way you are trying to start it (e.g. at boot time).
Perhaps you have a firewall set up in a way that blocks packets on port Perhaps you have a firewall set up in a way that blocks packets on the UDP
323/udp. You need to amend the firewall configuration in this case. port 323. You need to amend the firewall configuration in this case.
=== I keep getting the error `501 Not authorised` === I keep getting the error `501 Not authorised`
Since version 2.2, the `password` command doesn't do anything and `chronyc` This error indicates that `chronyc` sent the command to `chronyd` using a UDP
needs to run locally under the root or _chrony_ user, which are allowed to socket instead of the Unix domain socket (e.g. _/var/run/chrony/chronyd.sock_),
access the ``chronyd``'s Unix domain command socket. which is required for some commands. For security reasons, only the root and
_chrony_ users are allowed to access the socket.
With older versions, you need to authenticate with the `password` command first It is also possible that the socket does not exist. `chronyd` will not create
or use the `-a` option to authenticate automatically on start. The the socket if the directory has a wrong owner or permissions. In this case
configuration file needs to specify a file which contains keys (`keyfile` there should be an error message from `chronyd` in the system log.
directive) and which key in the key file should be used for `chronyc`
authentication (`commandkey` directive).
=== Why does `chronyc tracking` always print an IPv4 address as reference ID? === What is the reference ID reported by the `tracking` command?
The reference ID is a 32-bit value and in versions before 3.0 it was printed in The reference ID is a 32-bit value used in NTP to prevent synchronisation
loops.
In `chrony` versions before 3.0 it was printed in the
quad-dotted notation, even if the reference source did not actually have an quad-dotted notation, even if the reference source did not actually have an
IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but
for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For
@@ -457,7 +528,7 @@ Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
=== What is the real-time clock (RTC)? === What is the real-time clock (RTC)?
This is the clock which keeps the time even when your computer is turned off. This is the clock which keeps the time even when your computer is turned off.
It is used to initialise the system clock on boot. It normally doesn't drift It is used to initialise the system clock on boot. It normally does not drift
more than few seconds per day. more than few seconds per day.
There are two approaches how `chronyd` can work with it. One is to use the There are two approaches how `chronyd` can work with it. One is to use the
@@ -472,17 +543,17 @@ monitor the rate at which the RTC gains or loses time. When `chronyd` is
started with the `-s` option on the next boot, it will set the system time from started with the `-s` option on the next boot, it will set the system time from
the RTC and also compensate for the drift it has measured previously. The the RTC and also compensate for the drift it has measured previously. The
`rtcautotrim` directive can be used to keep the RTC close to the true time, but `rtcautotrim` directive can be used to keep the RTC close to the true time, but
it's not strictly necessary if its only purpose is to set the system clock when it is not strictly necessary if its only purpose is to set the system clock when
`chronyd` is started on boot. See the documentation for details. `chronyd` is started on boot. See the documentation for details.
=== I want to use ``chronyd``'s RTC support. Must I disable `hwclock`? === Does `hwclock` have to be disabled?
The `hwclock` program is often set-up by default in the boot and shutdown The `hwclock` program is often set-up by default in the boot and shutdown
scripts with many Linux installations. With the kernel RTC synchronisation scripts with many Linux installations. With the kernel RTC synchronisation
(`rtcsync` directive), the RTC will be set also every 11 minutes as long as the (`rtcsync` directive), the RTC will be set also every 11 minutes as long as the
system clock is synchronised. If you want to use ``chronyd``'s RTC monitoring system clock is synchronised. If you want to use ``chronyd``'s RTC monitoring
(`rtcfile` directive), it's important to disable `hwclock` in the shutdown (`rtcfile` directive), it is important to disable `hwclock` in the shutdown
procedure. If you don't, it will over-write the RTC with a new value, unknown procedure. If you do not that, it will over-write the RTC with a new value, unknown
to `chronyd`. At the next reboot, `chronyd` started with the `-s` option will to `chronyd`. At the next reboot, `chronyd` started with the `-s` option will
compensate this (wrong) time with its estimate of how far the RTC has drifted compensate this (wrong) time with its estimate of how far the RTC has drifted
whilst the power was off, giving a meaningless initial system time. whilst the power was off, giving a meaningless initial system time.
@@ -501,7 +572,7 @@ things
=== I get `Could not open /dev/rtc, Device or resource busy` in my syslog file === I get `Could not open /dev/rtc, Device or resource busy` in my syslog file
Some other program running on the system may be using the device. Some other program running on the system might be using the device.
=== What if my computer does not have an RTC or backup battery? === What if my computer does not have an RTC or backup battery?
@@ -516,7 +587,7 @@ observe backward steps.
=== Can `chronyd` be driven from broadcast/multicast NTP servers? === Can `chronyd` be driven from broadcast/multicast NTP servers?
No, the broadcast/multicast client mode is not supported and there is currently No, the broadcast/multicast client mode is not supported and there is currently
no plan to implement it. While the mode may be useful to simplify configuration no plan to implement it. While this mode can simplify configuration
of clients in large networks, it is inherently less accurate and less secure of clients in large networks, it is inherently less accurate and less secure
(even with authentication) than the ordinary client/server mode. (even with authentication) than the ordinary client/server mode.
@@ -533,7 +604,8 @@ thousands of clients using the ordinary client/server mode.
Yes, the `broadcast` directive can be used to enable the broadcast server mode Yes, the `broadcast` directive can be used to enable the broadcast server mode
to serve time to clients in the network which support the broadcast client mode to serve time to clients in the network which support the broadcast client mode
(it's not supported in `chronyd`, see the previous question). (it is not supported in `chronyd`). Note that this mode should generally be
avoided. See the previous question.
=== Can `chronyd` keep the system clock a fixed offset away from real time? === Can `chronyd` keep the system clock a fixed offset away from real time?
@@ -549,9 +621,21 @@ offline, `chronyd` would make new measurements immediately after issuing the
`online` command. `online` command.
Unless the network connection lasts only few minutes (less than the maximum Unless the network connection lasts only few minutes (less than the maximum
polling interval), the delay is usually not a problem, and it may be acceptable polling interval), the delay is usually not a problem, and it might be acceptable
to keep all sources online all the time. to keep all sources online all the time.
=== Why is an offset measured between two computers synchronised to each another?
When two computers are synchronised to each other using the client/server or
symmetric NTP mode, there is an expectation that NTP measurements between the
two computers made on both ends show an average offset close to zero.
With `chronyd` that can be expected only when the interleaved mode (`xleave`
option) is enabled. Otherwise, `chronyd` will use different transmit timestamps
(e.g. daemon timestamp vs kernel timestamp) for serving time and
synchronisation of its own clock, which creates an asymmetry in the
timestamping and causes the other end to measure a significant offset.
== Operating systems == Operating systems
=== Does `chrony` support Windows? === Does `chrony` support Windows?

View File

@@ -100,7 +100,7 @@ than MD5, which can be used for NTP authentication with a symmetric key. If you
don't want to enable the support, specify the `--disable-sechash` flag to don't want to enable the support, specify the `--disable-sechash` flag to
`configure`. `configure`.
If development files for the editline or readline library are available, If development files for the editline library are available,
`chronyc` will be built with line editing support. If you don't want this, `chronyc` will be built with line editing support. If you don't want this,
specify the `--disable-readline` flag to `configure`. specify the `--disable-readline` flag to `configure`.
@@ -170,43 +170,6 @@ https://github.com/seccomp/libseccomp[libseccomp] library and the
the kernel attack surface and possibly prevent kernel exploits from `chronyd` the kernel attack surface and possibly prevent kernel exploits from `chronyd`
if it is compromised. if it is compromised.
== Support for line editing libraries
`chronyc` can be built with support for line editing, this allows you to use
the cursor keys to replay and edit old commands. Two libraries are supported
which provide such functionality, editline and GNU readline.
Please note that readline since version 6.0 is licensed under GPLv3+ which is
incompatible with chrony's license GPLv2. You should use editline instead if
you don't want to use older readline versions.
The `configure` script will automatically enable the line editing support if
one of the supported libraries is available. If they are both available, the
editline library will be used.
If you don't want to use it (in which case `chronyc` will use a minimal command
line interface), invoke `configure` like this:
----
./configure --disable-readline other-options...
----
If you have editline, readline or ncurses installed in locations that aren't
normally searched by the compiler and linker, you need to use extra options:
`--with-readline-includes=directory_name`::
This defines the name of the directory above the one where `readline.h` is.
`readline.h` is assumed to be in `editline` or `readline` subdirectory of the
named directory.
`--with-readline-library=directory_name`::
This defines the directory containing the `libedit.a` or `libedit.so` file,
or `libreadline.a` or `libreadline.so` file.
`--with-ncurses-library=directory_name`::
This defines the directory containing the `libncurses.a` or `libncurses.so`
file.
== Extra options for package builders == Extra options for package builders
The `configure` and `make` procedures have some extra options that may be The `configure` and `make` procedures have some extra options that may be

View File

@@ -8,9 +8,11 @@ Wants=time-sync.target
[Service] [Service]
Type=oneshot Type=oneshot
# Wait up to ~10 minutes for chronyd to synchronize and the remaining # Wait for chronyd to update the clock and the remaining
# clock correction to be less than 0.1 seconds # correction to be less than 0.1 seconds
ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 600 0.1 0.0 1 ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 0 0.1 0.0 1
# Wait for at most 3 minutes
TimeoutStartSec=180
RemainAfterExit=yes RemainAfterExit=yes
StandardOutput=null StandardOutput=null

View File

@@ -25,9 +25,18 @@ rtcsync
# Serve time even if not synchronized to a time source. # Serve time even if not synchronized to a time source.
#local stratum 10 #local stratum 10
# Require authentication (nts or key option) for all NTP sources.
#authselectmode require
# Specify file containing keys for NTP authentication. # Specify file containing keys for NTP authentication.
#keyfile /etc/chrony.keys #keyfile /etc/chrony.keys
# Save NTS keys and cookies.
ntsdumpdir /var/lib/chrony
# Insert/delete leap seconds by slewing instead of stepping.
#leapsecmode slew
# Get TAI-UTC offset and leap seconds from the system tz database. # Get TAI-UTC offset and leap seconds from the system tz database.
#leapsectz right/UTC #leapsectz right/UTC

View File

@@ -57,6 +57,20 @@
! maxdrift 100 ! maxdrift 100
# By default, chronyd allows synchronisation to an unauthenticated NTP
# source (i.e. specified without the nts and key options) if it agrees with
# a majority of authenticated NTP sources, or if no authenticated source is
# specified. If you don't want chronyd to ever synchronise to an
# unauthenticated NTP source, uncomment the first from the following lines.
# If you don't want to synchronise to an unauthenticated NTP source only
# when an authenticated source is specified, uncomment the second line.
# If you want chronyd to ignore authentication in the source selection,
# uncomment the third line.
! authselectmode require
! authselectmode prefer
! authselectmode ignore
####################################################################### #######################################################################
### FILENAMES ETC ### FILENAMES ETC
# Chrony likes to keep information about your computer's clock in files. # Chrony likes to keep information about your computer's clock in files.
@@ -72,22 +86,37 @@ driftfile /var/lib/chrony/drift
! keyfile /etc/chrony.keys ! keyfile /etc/chrony.keys
# If you specify an NTP server with the nts option to enable authentication
# with the Network Time Security (NTS) mechanism, or enable server NTS with
# the ntsservercert and ntsserverkey directives below, the following line will
# allow the client/server to save the NTS keys and cookies in order to reduce
# the number of key establishments (NTS-KE sessions).
ntsdumpdir /var/lib/chrony
# If chronyd is configured to act as an NTP server and you want to enable NTS
# for its clients, you will need a TLS certificate and private key. Uncomment
# and edit the following lines to specify the locations of the certificate and
# key.
! ntsservercert /etc/.../foo.example.net.crt
! ntsserverkey /etc/.../foo.example.net.key
# chronyd can save the measurement history for the servers to files when # chronyd can save the measurement history for the servers to files when
# it it exits. This is useful in 2 situations: # it exits. This is useful in 2 situations:
# #
# 1. On Linux, if you stop chronyd and restart it with '-r' (e.g. after # 1. If you stop chronyd and restart it with the '-r' option (e.g. after
# an upgrade), the old measurements will still be relevant when chronyd # an upgrade), the old measurements will still be relevant when chronyd
# is restarted. This will reduce the time needed to get accurate # is restarted. This will reduce the time needed to get accurate
# gain/loss measurements, especially with a dial-up link. # gain/loss measurements.
# #
# 2. Again on Linux, if you use the RTC support and start chronyd with # 2. On Linux, if you use the RTC support and start chronyd with
# '-r -s' on bootup, measurements from the last boot will still be # '-r -s' on bootup, measurements from the last boot will still be
# useful (the real time clock is used to 'flywheel' chronyd between # useful (the real time clock is used to 'flywheel' chronyd between
# boots). # boots).
# #
# Enable these two options to use this. # Uncomment the following line to use this.
! dumponexit
! dumpdir /var/lib/chrony ! dumpdir /var/lib/chrony
# chronyd writes its process ID to a file. If you try to start a second # chronyd writes its process ID to a file. If you try to start a second
@@ -116,6 +145,18 @@ driftfile /var/lib/chrony/drift
! makestep 1.0 3 ! makestep 1.0 3
#######################################################################
### LEAP SECONDS
# A leap second is an occasional one-second correction of the UTC
# time scale. By default, chronyd tells the kernel to insert/delete
# the leap second, which makes a backward/forward step to correct the
# clock for it. As with the makestep directive, this jump can upset
# some applications. If you prefer chronyd to make a gradual
# correction, causing the clock to be off for a longer time, uncomment
# the following line.
! leapsecmode slew
####################################################################### #######################################################################
### LOGGING ### LOGGING
# If you want to log information about the time measurements chronyd has # If you want to log information about the time measurements chronyd has
@@ -135,8 +176,6 @@ driftfile /var/lib/chrony/drift
####################################################################### #######################################################################
### ACTING AS AN NTP SERVER ### ACTING AS AN NTP SERVER
# You might want the computer to be an NTP server for other computers. # You might want the computer to be an NTP server for other computers.
# e.g. you might be running chronyd on a dial-up machine that has a LAN
# sitting behind it with several 'satellite' computers on it.
# #
# By default, chronyd does not allow any clients to access it. You need # By default, chronyd does not allow any clients to access it. You need
# to explicitly enable access using 'allow' and 'deny' directives. # to explicitly enable access using 'allow' and 'deny' directives.
@@ -152,15 +191,6 @@ driftfile /var/lib/chrony/drift
# You can have as many allow and deny directives as you need. The order # You can have as many allow and deny directives as you need. The order
# is unimportant. # is unimportant.
# If you want chronyd to act as an NTP broadcast server, enable and edit
# (and maybe copy) the following line. This means that a broadcast
# packet is sent to the address 192.168.1.255 every 60 seconds. The
# address MUST correspond to the broadcast address of one of the network
# interfaces on your machine. If you have multiple network interfaces,
# add a broadcast line for each.
! broadcast 60 192.168.1.255
# If you want to present your computer's time for others to synchronise # If you want to present your computer's time for others to synchronise
# with, even if you don't seem to be synchronised to any NTP servers # with, even if you don't seem to be synchronised to any NTP servers
# yourself, enable the following line. The value 10 may be varied # yourself, enable the following line. The value 10 may be varied

View File

@@ -0,0 +1,43 @@
#!/bin/sh
# This is a NetworkManager dispatcher script for chronyd to update
# its NTP sources passed from DHCP options. Note that this script is
# specific to NetworkManager-dispatcher due to use of the
# DHCP4_NTP_SERVERS environment variable.
export LC_ALL=C
interface=$1
action=$2
chronyc=/usr/bin/chronyc
default_server_options=iburst
server_dir=/var/run/chrony-dhcp
dhcp_server_file=$server_dir/$interface.sources
# DHCP4_NTP_SERVERS is passed from DHCP options by NetworkManager.
nm_dhcp_servers=$DHCP4_NTP_SERVERS
add_servers_from_dhcp() {
rm -f "$dhcp_server_file"
for server in $nm_dhcp_servers; do
echo "server $server $default_server_options" >> "$dhcp_server_file"
done
$chronyc reload sources > /dev/null 2>&1 || :
}
clear_servers_from_dhcp() {
if [ -f "$dhcp_server_file" ]; then
rm -f "$dhcp_server_file"
$chronyc reload sources > /dev/null 2>&1 || :
fi
}
mkdir -p $server_dir
if [ "$action" = "up" ] || [ "$action" = "dhcp4-change" ]; then
add_servers_from_dhcp
elif [ "$action" = "down" ]; then
clear_servers_from_dhcp
fi
exit 0

View File

@@ -5,11 +5,13 @@
export LC_ALL=C export LC_ALL=C
chronyc=/usr/bin/chronyc
# For NetworkManager consider only up/down events # For NetworkManager consider only up/down events
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0 [ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off # Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
chronyc onoffline > /dev/null 2>&1 $chronyc onoffline > /dev/null 2>&1
exit 0 exit 0

23
hash.h
View File

@@ -31,12 +31,25 @@
/* length of hash values produced by SHA512 */ /* length of hash values produced by SHA512 */
#define MAX_HASH_LENGTH 64 #define MAX_HASH_LENGTH 64
extern int HSH_GetHashId(const char *name); typedef enum {
HSH_INVALID = 0,
HSH_MD5 = 1,
HSH_SHA1 = 2,
HSH_SHA256 = 3,
HSH_SHA384 = 4,
HSH_SHA512 = 5,
HSH_SHA3_224 = 6,
HSH_SHA3_256 = 7,
HSH_SHA3_384 = 8,
HSH_SHA3_512 = 9,
HSH_TIGER = 10,
HSH_WHIRLPOOL = 11,
} HSH_Algorithm;
extern unsigned int HSH_Hash(int id, extern int HSH_GetHashId(HSH_Algorithm algorithm);
const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len, extern int HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
unsigned char *out, unsigned int out_len); unsigned char *out, int out_len);
extern void HSH_Finalise(void); extern void HSH_Finalise(void);

View File

@@ -36,20 +36,22 @@
static MD5_CTX ctx; static MD5_CTX ctx;
int int
HSH_GetHashId(const char *name) HSH_GetHashId(HSH_Algorithm algorithm)
{ {
/* only MD5 is supported */ /* only MD5 is supported */
if (strcmp(name, "MD5")) if (algorithm != HSH_MD5)
return -1; return -1;
return 0; return 0;
} }
unsigned int int
HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len, HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
const unsigned char *in2, unsigned int in2_len, unsigned char *out, int out_len)
unsigned char *out, unsigned int out_len)
{ {
if (in1_len < 0 || in2_len < 0 || out_len < 0)
return 0;
MD5Init(&ctx); MD5Init(&ctx);
MD5Update(&ctx, in1, in1_len); MD5Update(&ctx, in1, in1_len);
if (in2) if (in2)

View File

@@ -35,36 +35,36 @@
#include "memory.h" #include "memory.h"
struct hash { struct hash {
const char *name; const HSH_Algorithm algorithm;
const char *int_name; const char *int_name;
const struct nettle_hash *nettle_hash; const struct nettle_hash *nettle_hash;
void *context; void *context;
}; };
static struct hash hashes[] = { static struct hash hashes[] = {
{ "MD5", "md5", NULL, NULL }, { HSH_MD5, "md5", NULL, NULL },
{ "SHA1", "sha1", NULL, NULL }, { HSH_SHA1, "sha1", NULL, NULL },
{ "SHA256", "sha256", NULL, NULL }, { HSH_SHA256, "sha256", NULL, NULL },
{ "SHA384", "sha384", NULL, NULL }, { HSH_SHA384, "sha384", NULL, NULL },
{ "SHA512", "sha512", NULL, NULL }, { HSH_SHA512, "sha512", NULL, NULL },
{ "SHA3-224", "sha3_224", NULL, NULL }, { HSH_SHA3_224, "sha3_224", NULL, NULL },
{ "SHA3-256", "sha3_256", NULL, NULL }, { HSH_SHA3_256, "sha3_256", NULL, NULL },
{ "SHA3-384", "sha3_384", NULL, NULL }, { HSH_SHA3_384, "sha3_384", NULL, NULL },
{ "SHA3-512", "sha3_512", NULL, NULL }, { HSH_SHA3_512, "sha3_512", NULL, NULL },
{ NULL, NULL, NULL, NULL } { 0, NULL, NULL, NULL }
}; };
int int
HSH_GetHashId(const char *name) HSH_GetHashId(HSH_Algorithm algorithm)
{ {
int id, nid; int id, nid;
for (id = 0; hashes[id].name; id++) { for (id = 0; hashes[id].algorithm != 0; id++) {
if (!strcmp(name, hashes[id].name)) if (hashes[id].algorithm == algorithm)
break; break;
} }
if (!hashes[id].name) if (hashes[id].algorithm == 0)
return -1; return -1;
if (hashes[id].context) if (hashes[id].context)
@@ -84,14 +84,16 @@ HSH_GetHashId(const char *name)
return id; return id;
} }
unsigned int int
HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len, HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
const unsigned char *in2, unsigned int in2_len, unsigned char *out, int out_len)
unsigned char *out, unsigned int out_len)
{ {
const struct nettle_hash *hash; const struct nettle_hash *hash;
void *context; void *context;
if (in1_len < 0 || in2_len < 0 || out_len < 0)
return 0;
hash = hashes[id].nettle_hash; hash = hashes[id].nettle_hash;
context = hashes[id].context; context = hashes[id].context;
@@ -112,7 +114,7 @@ HSH_Finalise(void)
{ {
int i; int i;
for (i = 0; hashes[i].name; i++) { for (i = 0; hashes[i].algorithm != 0; i++) {
if (hashes[i].context) if (hashes[i].context)
Free(hashes[i].context); Free(hashes[i].context);
} }

View File

@@ -38,30 +38,30 @@ static NSSLOWInitContext *ictx;
struct hash { struct hash {
HASH_HashType type; HASH_HashType type;
const char *name; HSH_Algorithm algorithm;
NSSLOWHASHContext *context; NSSLOWHASHContext *context;
}; };
static struct hash hashes[] = { static struct hash hashes[] = {
{ HASH_AlgMD5, "MD5", NULL }, { HASH_AlgMD5, HSH_MD5, NULL },
{ HASH_AlgSHA1, "SHA1", NULL }, { HASH_AlgSHA1, HSH_SHA1, NULL },
{ HASH_AlgSHA256, "SHA256", NULL }, { HASH_AlgSHA256, HSH_SHA256, NULL },
{ HASH_AlgSHA384, "SHA384", NULL }, { HASH_AlgSHA384, HSH_SHA384, NULL },
{ HASH_AlgSHA512, "SHA512", NULL }, { HASH_AlgSHA512, HSH_SHA512, NULL },
{ 0, NULL, NULL } { 0, 0, NULL }
}; };
int int
HSH_GetHashId(const char *name) HSH_GetHashId(HSH_Algorithm algorithm)
{ {
int i; int i;
for (i = 0; hashes[i].name; i++) { for (i = 0; hashes[i].algorithm != 0; i++) {
if (!strcmp(name, hashes[i].name)) if (hashes[i].algorithm == algorithm)
break; break;
} }
if (!hashes[i].name) if (hashes[i].algorithm == 0)
return -1; /* not found */ return -1; /* not found */
if (!ictx && !(ictx = NSSLOW_Init())) if (!ictx && !(ictx = NSSLOW_Init()))
@@ -74,14 +74,16 @@ HSH_GetHashId(const char *name)
return i; return i;
} }
unsigned int int
HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len, HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
const unsigned char *in2, unsigned int in2_len, unsigned char *out, int out_len)
unsigned char *out, unsigned int out_len)
{ {
unsigned char buf[MAX_HASH_LENGTH]; unsigned char buf[MAX_HASH_LENGTH];
unsigned int ret = 0; unsigned int ret = 0;
if (in1_len < 0 || in2_len < 0 || out_len < 0)
return 0;
NSSLOWHASH_Begin(hashes[id].context); NSSLOWHASH_Begin(hashes[id].context);
NSSLOWHASH_Update(hashes[id].context, in1, in1_len); NSSLOWHASH_Update(hashes[id].context, in1, in1_len);
if (in2) if (in2)
@@ -99,7 +101,7 @@ HSH_Finalise(void)
{ {
int i; int i;
for (i = 0; hashes[i].name; i++) { for (i = 0; hashes[i].algorithm != 0; i++) {
if (hashes[i].context) if (hashes[i].context)
NSSLOWHASH_Destroy(hashes[i].context); NSSLOWHASH_Destroy(hashes[i].context);
} }

View File

@@ -32,51 +32,51 @@
#include "util.h" #include "util.h"
struct hash { struct hash {
const char *name; HSH_Algorithm algorithm;
const char *int_name; const char *int_name;
const struct ltc_hash_descriptor *desc; const struct ltc_hash_descriptor *desc;
}; };
static const struct hash hashes[] = { static const struct hash hashes[] = {
{ "MD5", "md5", &md5_desc }, { HSH_MD5, "md5", &md5_desc },
#ifdef LTC_SHA1 #ifdef LTC_SHA1
{ "SHA1", "sha1", &sha1_desc }, { HSH_SHA1, "sha1", &sha1_desc },
#endif #endif
#ifdef LTC_SHA256 #ifdef LTC_SHA256
{ "SHA256", "sha256", &sha256_desc }, { HSH_SHA256, "sha256", &sha256_desc },
#endif #endif
#ifdef LTC_SHA384 #ifdef LTC_SHA384
{ "SHA384", "sha384", &sha384_desc }, { HSH_SHA384, "sha384", &sha384_desc },
#endif #endif
#ifdef LTC_SHA512 #ifdef LTC_SHA512
{ "SHA512", "sha512", &sha512_desc }, { HSH_SHA512, "sha512", &sha512_desc },
#endif #endif
#ifdef LTC_SHA3 #ifdef LTC_SHA3
{ "SHA3-224", "sha3-224", &sha3_224_desc }, { HSH_SHA3_224, "sha3-224", &sha3_224_desc },
{ "SHA3-256", "sha3-256", &sha3_256_desc }, { HSH_SHA3_256, "sha3-256", &sha3_256_desc },
{ "SHA3-384", "sha3-384", &sha3_384_desc }, { HSH_SHA3_384, "sha3-384", &sha3_384_desc },
{ "SHA3-512", "sha3-512", &sha3_512_desc }, { HSH_SHA3_512, "sha3-512", &sha3_512_desc },
#endif #endif
#ifdef LTC_TIGER #ifdef LTC_TIGER
{ "TIGER", "tiger", &tiger_desc }, { HSH_TIGER, "tiger", &tiger_desc },
#endif #endif
#ifdef LTC_WHIRLPOOL #ifdef LTC_WHIRLPOOL
{ "WHIRLPOOL", "whirlpool", &whirlpool_desc }, { HSH_WHIRLPOOL, "whirlpool", &whirlpool_desc },
#endif #endif
{ NULL, NULL, NULL } { 0, NULL, NULL }
}; };
int int
HSH_GetHashId(const char *name) HSH_GetHashId(HSH_Algorithm algorithm)
{ {
int i, h; int i, h;
for (i = 0; hashes[i].name; i++) { for (i = 0; hashes[i].algorithm != 0; i++) {
if (!strcmp(name, hashes[i].name)) if (hashes[i].algorithm == algorithm)
break; break;
} }
if (!hashes[i].name) if (hashes[i].algorithm == 0)
return -1; /* not found */ return -1; /* not found */
h = find_hash(hashes[i].int_name); h = find_hash(hashes[i].int_name);
@@ -89,15 +89,17 @@ HSH_GetHashId(const char *name)
return find_hash(hashes[i].int_name); return find_hash(hashes[i].int_name);
} }
unsigned int int
HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len, HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
const unsigned char *in2, unsigned int in2_len, unsigned char *out, int out_len)
unsigned char *out, unsigned int out_len)
{ {
unsigned char buf[MAX_HASH_LENGTH]; unsigned char buf[MAX_HASH_LENGTH];
unsigned long len; unsigned long len;
int r; int r;
if (in1_len < 0 || in2_len < 0 || out_len < 0)
return 0;
len = sizeof (buf); len = sizeof (buf);
if (in2) if (in2)
r = hash_memory_multi(id, buf, &len, r = hash_memory_multi(id, buf, &len,

123
keys.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2012-2016 * Copyright (C) Miroslav Lichvar 2012-2016, 2019-2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -50,16 +50,16 @@ typedef enum {
typedef struct { typedef struct {
uint32_t id; uint32_t id;
int type;
int length;
KeyClass class; KeyClass class;
union { union {
struct { struct {
unsigned char *value; unsigned char *value;
int length;
int hash_id; int hash_id;
} ntp_mac; } ntp_mac;
CMC_Instance cmac; CMC_Instance cmac;
} data; } data;
int auth_delay;
} Key; } Key;
static ARR_Instance keys; static ARR_Instance keys;
@@ -121,38 +121,6 @@ get_key(unsigned int index)
return ((Key *)ARR_GetElements(keys)) + index; return ((Key *)ARR_GetElements(keys)) + index;
} }
/* ================================================== */
static int
determine_hash_delay(uint32_t key_id)
{
NTP_Packet pkt;
struct timespec before, after;
double diff, min_diff;
int i, nsecs;
memset(&pkt, 0, sizeof (pkt));
for (i = 0; i < 10; i++) {
LCL_ReadRawTime(&before);
KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_HEADER_LENGTH,
(unsigned char *)&pkt + NTP_HEADER_LENGTH,
sizeof (pkt) - NTP_HEADER_LENGTH);
LCL_ReadRawTime(&after);
diff = UTI_DiffTimespecsToDouble(&after, &before);
if (i == 0 || min_diff > diff)
min_diff = diff;
}
nsecs = 1.0e9 * min_diff;
DEBUG_LOG("authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs);
return nsecs;
}
/* ================================================== */ /* ================================================== */
/* Decode key encoded in ASCII or HEX */ /* Decode key encoded in ASCII or HEX */
@@ -201,6 +169,8 @@ KEY_Reload(void)
FILE *in; FILE *in;
char line[2048], *key_file, *key_value; char line[2048], *key_file, *key_value;
const char *key_type; const char *key_type;
HSH_Algorithm hash_algorithm;
CMC_Algorithm cmac_algorithm;
int hash_id; int hash_id;
Key key; Key key;
@@ -238,27 +208,40 @@ KEY_Reload(void)
continue; continue;
} }
hash_id = HSH_GetHashId(key_type); hash_algorithm = UTI_HashNameToAlgorithm(key_type);
cmac_key_length = CMC_GetKeyLength(key_type); cmac_algorithm = UTI_CmacNameToAlgorithm(key_type);
if (hash_id >= 0) { if (hash_algorithm != 0) {
hash_id = HSH_GetHashId(hash_algorithm);
if (hash_id < 0) {
LOG(LOGS_WARN, "Unsupported %s in key %"PRIu32, "hash function", key.id);
continue;
}
key.class = NTP_MAC; key.class = NTP_MAC;
key.type = hash_algorithm;
key.length = key_length;
key.data.ntp_mac.value = MallocArray(unsigned char, key_length); key.data.ntp_mac.value = MallocArray(unsigned char, key_length);
memcpy(key.data.ntp_mac.value, key_value, key_length); memcpy(key.data.ntp_mac.value, key_value, key_length);
key.data.ntp_mac.length = key_length;
key.data.ntp_mac.hash_id = hash_id; key.data.ntp_mac.hash_id = hash_id;
} else if (cmac_key_length > 0) { } else if (cmac_algorithm != 0) {
if (cmac_key_length != key_length) { cmac_key_length = CMC_GetKeyLength(cmac_algorithm);
if (cmac_key_length == 0) {
LOG(LOGS_WARN, "Unsupported %s in key %"PRIu32, "cipher", key.id);
continue;
} else if (cmac_key_length != key_length) {
LOG(LOGS_WARN, "Invalid length of %s key %"PRIu32" (expected %u bits)", LOG(LOGS_WARN, "Invalid length of %s key %"PRIu32" (expected %u bits)",
key_type, key.id, 8 * cmac_key_length); key_type, key.id, 8 * cmac_key_length);
continue; continue;
} }
key.class = CMAC; key.class = CMAC;
key.data.cmac = CMC_CreateInstance(key_type, (unsigned char *)key_value, key_length); key.type = cmac_algorithm;
key.length = key_length;
key.data.cmac = CMC_CreateInstance(cmac_algorithm, (unsigned char *)key_value,
key_length);
assert(key.data.cmac); assert(key.data.cmac);
} else { } else {
LOG(LOGS_WARN, "Unknown hash function or cipher in key %"PRIu32, key.id); LOG(LOGS_WARN, "Invalid type in key %"PRIu32, key.id);
continue; continue;
} }
@@ -280,9 +263,6 @@ KEY_Reload(void)
/* Erase any passwords from stack */ /* Erase any passwords from stack */
memset(line, 0, sizeof (line)); memset(line, 0, sizeof (line));
for (i = 0; i < ARR_GetSize(keys); i++)
get_key(i)->auth_delay = determine_hash_delay(get_key(i)->id);
} }
/* ================================================== */ /* ================================================== */
@@ -338,21 +318,6 @@ KEY_KeyKnown(uint32_t key_id)
/* ================================================== */ /* ================================================== */
int
KEY_GetAuthDelay(uint32_t key_id)
{
Key *key;
key = get_key_by_id(key_id);
if (!key)
return 0;
return key->auth_delay;
}
/* ================================================== */
int int
KEY_GetAuthLength(uint32_t key_id) KEY_GetAuthLength(uint32_t key_id)
{ {
@@ -387,24 +352,36 @@ KEY_CheckKeyLength(uint32_t key_id)
if (!key) if (!key)
return 0; return 0;
switch (key->class) { return key->length >= MIN_SECURE_KEY_LENGTH;
case NTP_MAC: }
return key->data.ntp_mac.length >= MIN_SECURE_KEY_LENGTH;
default: /* ================================================== */
return 1;
} int
KEY_GetKeyInfo(uint32_t key_id, int *type, int *bits)
{
Key *key;
key = get_key_by_id(key_id);
if (!key)
return 0;
*type = key->type;
*bits = 8 * key->length;
return 1;
} }
/* ================================================== */ /* ================================================== */
static int static int
generate_auth(Key *key, const unsigned char *data, int data_len, generate_auth(Key *key, const void *data, int data_len, unsigned char *auth, int auth_len)
unsigned char *auth, int auth_len)
{ {
switch (key->class) { switch (key->class) {
case NTP_MAC: case NTP_MAC:
return HSH_Hash(key->data.ntp_mac.hash_id, key->data.ntp_mac.value, return HSH_Hash(key->data.ntp_mac.hash_id, key->data.ntp_mac.value,
key->data.ntp_mac.length, data, data_len, auth, auth_len); key->length, data, data_len, auth, auth_len);
case CMAC: case CMAC:
return CMC_Hash(key->data.cmac, data, data_len, auth, auth_len); return CMC_Hash(key->data.cmac, data, data_len, auth, auth_len);
default: default:
@@ -415,7 +392,7 @@ generate_auth(Key *key, const unsigned char *data, int data_len,
/* ================================================== */ /* ================================================== */
static int static int
check_auth(Key *key, const unsigned char *data, int data_len, check_auth(Key *key, const void *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len) const unsigned char *auth, int auth_len, int trunc_len)
{ {
unsigned char buf[MAX_HASH_LENGTH]; unsigned char buf[MAX_HASH_LENGTH];
@@ -429,7 +406,7 @@ check_auth(Key *key, const unsigned char *data, int data_len,
/* ================================================== */ /* ================================================== */
int int
KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len, KEY_GenerateAuth(uint32_t key_id, const void *data, int data_len,
unsigned char *auth, int auth_len) unsigned char *auth, int auth_len)
{ {
Key *key; Key *key;
@@ -445,7 +422,7 @@ KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
/* ================================================== */ /* ================================================== */
int int
KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len, KEY_CheckAuth(uint32_t key_id, const void *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len) const unsigned char *auth, int auth_len, int trunc_len)
{ {
Key *key; Key *key;

9
keys.h
View File

@@ -34,15 +34,14 @@ extern void KEY_Finalise(void);
extern void KEY_Reload(void); extern void KEY_Reload(void);
extern int KEY_GetKey(uint32_t key_id, char **key, int *len);
extern int KEY_KeyKnown(uint32_t key_id); extern int KEY_KeyKnown(uint32_t key_id);
extern int KEY_GetAuthDelay(uint32_t key_id);
extern int KEY_GetAuthLength(uint32_t key_id); extern int KEY_GetAuthLength(uint32_t key_id);
extern int KEY_CheckKeyLength(uint32_t key_id); extern int KEY_CheckKeyLength(uint32_t key_id);
extern int KEY_GetKeyInfo(uint32_t key_id, int *type, int *bits);
extern int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, extern int KEY_GenerateAuth(uint32_t key_id, const void *data, int data_len,
int data_len, unsigned char *auth, int auth_len); unsigned char *auth, int auth_len);
extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len, extern int KEY_CheckAuth(uint32_t key_id, const void *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len); const unsigned char *auth, int auth_len, int trunc_len);
#endif /* GOT_KEYS_H */ #endif /* GOT_KEYS_H */

40
local.c
View File

@@ -108,8 +108,8 @@ static double max_clock_error;
#define NSEC_PER_SEC 1000000000 #define NSEC_PER_SEC 1000000000
static void static double
calculate_sys_precision(void) measure_clock_precision(void)
{ {
struct timespec ts, old_ts; struct timespec ts, old_ts;
int iters, diff, best; int iters, diff, best;
@@ -135,18 +135,7 @@ calculate_sys_precision(void)
assert(best > 0); assert(best > 0);
precision_quantum = 1.0e-9 * best; return 1.0e-9 * best;
/* Get rounded log2 value of the measured precision */
precision_log = 0;
while (best < 707106781) {
precision_log--;
best *= 2;
}
assert(precision_log >= -30);
DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
} }
/* ================================================== */ /* ================================================== */
@@ -170,7 +159,16 @@ LCL_Initialise(void)
current_freq_ppm = 0.0; current_freq_ppm = 0.0;
temp_comp_ppm = 0.0; temp_comp_ppm = 0.0;
calculate_sys_precision(); precision_quantum = CNF_GetClockPrecision();
if (precision_quantum <= 0.0)
precision_quantum = measure_clock_precision();
precision_quantum = CLAMP(1.0e-9, precision_quantum, 1.0);
precision_log = round(log(precision_quantum) / log(2.0));
/* NTP code doesn't support smaller log than -30 */
assert(precision_log >= -30);
DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
/* This is the maximum allowed frequency offset in ppm, the time must /* This is the maximum allowed frequency offset in ppm, the time must
never stop or run backwards */ never stop or run backwards */
@@ -185,13 +183,11 @@ LCL_Initialise(void)
void void
LCL_Finalise(void) LCL_Finalise(void)
{ {
while (change_list.next != &change_list) /* Make sure all handlers have been removed */
LCL_RemoveParameterChangeHandler(change_list.next->handler, if (change_list.next != &change_list)
change_list.next->anything); assert(0);
if (dispersion_notify_list.next != &dispersion_notify_list)
while (dispersion_notify_list.next != &dispersion_notify_list) assert(0);
LCL_RemoveDispersionNotifyHandler(dispersion_notify_list.next->handler,
dispersion_notify_list.next->anything);
} }
/* ================================================== */ /* ================================================== */

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2014, 2018 * Copyright (C) Miroslav Lichvar 2011-2014, 2018-2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -33,6 +33,7 @@
#include "conf.h" #include "conf.h"
#include "logging.h" #include "logging.h"
#include "memory.h"
#include "util.h" #include "util.h"
/* This is used by DEBUG_LOG macro */ /* This is used by DEBUG_LOG macro */
@@ -61,12 +62,16 @@ static int n_filelogs = 0;
static struct LogFile logfiles[MAX_FILELOGS]; static struct LogFile logfiles[MAX_FILELOGS];
/* Global prefix for debug messages */
static char *debug_prefix;
/* ================================================== */ /* ================================================== */
/* Init function */ /* Init function */
void void
LOG_Initialise(void) LOG_Initialise(void)
{ {
debug_prefix = Strdup("");
initialised = 1; initialised = 1;
LOG_OpenFileLog(NULL); LOG_OpenFileLog(NULL);
} }
@@ -85,6 +90,8 @@ LOG_Finalise(void)
LOG_CycleLogFiles(); LOG_CycleLogFiles();
Free(debug_prefix);
initialised = 0; initialised = 0;
} }
@@ -132,6 +139,8 @@ void LOG_Message(LOG_Severity severity,
time_t t; time_t t;
struct tm *tm; struct tm *tm;
assert(initialised);
if (!system_log && file_log && severity >= log_min_severity) { if (!system_log && file_log && severity >= log_min_severity) {
/* Don't clutter up syslog with timestamps and internal debugging info */ /* Don't clutter up syslog with timestamps and internal debugging info */
time(&t); time(&t);
@@ -142,7 +151,7 @@ void LOG_Message(LOG_Severity severity,
} }
#if DEBUG > 0 #if DEBUG > 0
if (log_min_severity <= LOGS_DEBUG) if (log_min_severity <= LOGS_DEBUG)
fprintf(file_log, "%s:%d:(%s) ", filename, line_number, function_name); fprintf(file_log, "%s%s:%d:(%s) ", debug_prefix, filename, line_number, function_name);
#endif #endif
} }
@@ -220,6 +229,23 @@ void LOG_SetMinSeverity(LOG_Severity severity)
/* ================================================== */ /* ================================================== */
LOG_Severity
LOG_GetMinSeverity(void)
{
return log_min_severity;
}
/* ================================================== */
void
LOG_SetDebugPrefix(const char *prefix)
{
Free(debug_prefix);
debug_prefix = Strdup(prefix);
}
/* ================================================== */
void void
LOG_SetParentFd(int fd) LOG_SetParentFd(int fd)
{ {
@@ -243,7 +269,10 @@ LOG_CloseParentFd()
LOG_FileID LOG_FileID
LOG_FileOpen(const char *name, const char *banner) LOG_FileOpen(const char *name, const char *banner)
{ {
assert(n_filelogs < MAX_FILELOGS); if (n_filelogs >= MAX_FILELOGS) {
assert(0);
return -1;
}
logfiles[n_filelogs].name = name; logfiles[n_filelogs].name = name;
logfiles[n_filelogs].banner = banner; logfiles[n_filelogs].banner = banner;
@@ -267,7 +296,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
if (!logfiles[id].file) { if (!logfiles[id].file) {
char *logdir = CNF_GetLogDir(); char *logdir = CNF_GetLogDir();
if (logdir[0] == '\0') { if (!logdir) {
LOG(LOGS_WARN, "logdir not specified"); LOG(LOGS_WARN, "logdir not specified");
logfiles[id].name = NULL; logfiles[id].name = NULL;
return; return;

View File

@@ -97,6 +97,12 @@ extern void LOG_Message(LOG_Severity severity, const char *format, ...);
prefixed with the filename, line number, and function name. */ prefixed with the filename, line number, and function name. */
extern void LOG_SetMinSeverity(LOG_Severity severity); extern void LOG_SetMinSeverity(LOG_Severity severity);
/* Get the minimum severity */
extern LOG_Severity LOG_GetMinSeverity(void);
/* Set a prefix for debug messages */
extern void LOG_SetDebugPrefix(const char *prefix);
/* Log messages to a file instead of stderr, or stderr again if NULL */ /* Log messages to a file instead of stderr, or stderr again if NULL */
extern void LOG_OpenFileLog(const char *log_file); extern void LOG_OpenFileLog(const char *log_file);

55
main.c
View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009 * Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2012-2018 * Copyright (C) Miroslav Lichvar 2012-2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -38,7 +38,6 @@
#include "ntp_signd.h" #include "ntp_signd.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "nts_ke_client.h"
#include "nts_ke_server.h" #include "nts_ke_server.h"
#include "nts_ntp_server.h" #include "nts_ntp_server.h"
#include "socket.h" #include "socket.h"
@@ -91,7 +90,7 @@ delete_pidfile(void)
{ {
const char *pidfile = CNF_GetPidFile(); const char *pidfile = CNF_GetPidFile();
if (!pidfile[0]) if (!pidfile)
return; return;
if (!UTI_RemoveFile(NULL, pidfile, NULL)) if (!UTI_RemoveFile(NULL, pidfile, NULL))
@@ -105,9 +104,7 @@ MAI_CleanupAndExit(void)
{ {
if (!initialised) exit(exit_status); if (!initialised) exit(exit_status);
if (CNF_GetDumpDir()[0] != '\0') { SRC_DumpSources();
SRC_DumpSources();
}
/* Don't update clock when removing sources */ /* Don't update clock when removing sources */
REF_SetMode(REF_ModeIgnore); REF_SetMode(REF_ModeIgnore);
@@ -116,7 +113,6 @@ MAI_CleanupAndExit(void)
TMC_Finalise(); TMC_Finalise();
MNL_Finalise(); MNL_Finalise();
CLG_Finalise(); CLG_Finalise();
NKC_Finalise();
NKS_Finalise(); NKS_Finalise();
NNS_Finalise(); NNS_Finalise();
NSD_Finalise(); NSD_Finalise();
@@ -125,13 +121,15 @@ MAI_CleanupAndExit(void)
NCR_Finalise(); NCR_Finalise();
NIO_Finalise(); NIO_Finalise();
CAM_Finalise(); CAM_Finalise();
SCK_Finalise();
KEY_Finalise(); KEY_Finalise();
RCL_Finalise(); RCL_Finalise();
SRC_Finalise(); SRC_Finalise();
REF_Finalise(); REF_Finalise();
RTC_Finalise(); RTC_Finalise();
SYS_Finalise(); SYS_Finalise();
SCK_Finalise();
SCH_Finalise(); SCH_Finalise();
LCL_Finalise(); LCL_Finalise();
PRV_Finalise(); PRV_Finalise();
@@ -150,7 +148,6 @@ MAI_CleanupAndExit(void)
static void static void
signal_cleanup(int x) signal_cleanup(int x)
{ {
if (!initialised) exit(0);
SCH_QuitProgram(); SCH_QuitProgram();
} }
@@ -261,7 +258,7 @@ check_pidfile(void)
FILE *in; FILE *in;
int pid, count; int pid, count;
if (!pidfile[0]) if (!pidfile)
return; return;
in = UTI_OpenFile(NULL, pidfile, NULL, 'r', 0); in = UTI_OpenFile(NULL, pidfile, NULL, 'r', 0);
@@ -289,7 +286,7 @@ write_pidfile(void)
const char *pidfile = CNF_GetPidFile(); const char *pidfile = CNF_GetPidFile();
FILE *out; FILE *out;
if (!pidfile[0]) if (!pidfile)
return; return;
out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644); out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644);
@@ -377,7 +374,7 @@ go_daemon(void)
static void static void
print_help(const char *progname) print_help(const char *progname)
{ {
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n", printf("Usage: %s [-4|-6] [-n|-d] [-p|-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
progname); progname);
} }
@@ -414,7 +411,7 @@ int main
int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = -1; int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = -1;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0; int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
int clock_control = 1, system_log = 1, log_severity = LOGS_INFO; int clock_control = 1, system_log = 1, log_severity = LOGS_INFO;
int config_args = 0; int user_check = 1, config_args = 0, print_config = 0;
do_platform_checks(); do_platform_checks();
@@ -434,7 +431,7 @@ int main
optind = 1; optind = 1;
/* Parse short command-line options */ /* Parse short command-line options */
while ((opt = getopt(argc, argv, "46df:F:hl:L:mnP:qQrRst:u:vx")) != -1) { while ((opt = getopt(argc, argv, "46df:F:hl:L:mnpP:qQrRst:u:Uvx")) != -1) {
switch (opt) { switch (opt) {
case '4': case '4':
case '6': case '6':
@@ -463,6 +460,12 @@ int main
case 'n': case 'n':
nofork = 1; nofork = 1;
break; break;
case 'p':
print_config = 1;
user_check = 0;
nofork = 1;
system_log = 0;
break;
case 'P': case 'P':
sched_priority = parse_int_arg(optarg); sched_priority = parse_int_arg(optarg);
break; break;
@@ -476,6 +479,7 @@ int main
ref_mode = REF_ModePrintOnce; ref_mode = REF_ModePrintOnce;
nofork = 1; nofork = 1;
client_only = 1; client_only = 1;
user_check = 0;
clock_control = 0; clock_control = 0;
system_log = 0; system_log = 0;
break; break;
@@ -494,6 +498,9 @@ int main
case 'u': case 'u':
user = optarg; user = optarg;
break; break;
case 'U':
user_check = 0;
break;
case 'v': case 'v':
print_version(); print_version();
return 0; return 0;
@@ -506,7 +513,7 @@ int main
} }
} }
if (getuid() && !client_only) if (user_check && getuid() != 0)
LOG_FATAL("Not superuser"); LOG_FATAL("Not superuser");
/* Turn into a daemon */ /* Turn into a daemon */
@@ -527,6 +534,8 @@ int main
DNS_SetAddressFamily(address_family); DNS_SetAddressFamily(address_family);
CNF_Initialise(restarted, client_only); CNF_Initialise(restarted, client_only);
if (print_config)
CNF_EnablePrint();
/* Parse the config file or the remaining command line arguments */ /* Parse the config file or the remaining command line arguments */
config_args = argc - optind; config_args = argc - optind;
@@ -537,6 +546,9 @@ int main
CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]); CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
} }
if (print_config)
return 0;
/* Check whether another chronyd may already be running */ /* Check whether another chronyd may already be running */
check_pidfile(); check_pidfile();
@@ -556,16 +568,20 @@ int main
PRV_Initialise(); PRV_Initialise();
LCL_Initialise(); LCL_Initialise();
SCH_Initialise(); SCH_Initialise();
SCK_Initialise(address_family);
/* Start helper processes if needed */
NKS_PreInitialise(pw->pw_uid, pw->pw_gid, scfilter_level);
SYS_Initialise(clock_control); SYS_Initialise(clock_control);
RTC_Initialise(do_init_rtc); RTC_Initialise(do_init_rtc);
SRC_Initialise(); SRC_Initialise();
RCL_Initialise(); RCL_Initialise();
KEY_Initialise(); KEY_Initialise();
SCK_Initialise();
/* Open privileged ports before dropping root */ /* Open privileged ports before dropping root */
CAM_Initialise(address_family); CAM_Initialise();
NIO_Initialise(address_family); NIO_Initialise();
NCR_Initialise(); NCR_Initialise();
CNF_SetupAccessRestrictions(); CNF_SetupAccessRestrictions();
@@ -590,8 +606,7 @@ int main
NSR_Initialise(); NSR_Initialise();
NSD_Initialise(); NSD_Initialise();
NNS_Initialise(); NNS_Initialise();
NKS_Initialise(scfilter_level); NKS_Initialise();
NKC_Initialise();
CLG_Initialise(); CLG_Initialise();
MNL_Initialise(); MNL_Initialise();
TMC_Initialise(); TMC_Initialise();

View File

@@ -92,6 +92,7 @@ MNL_Initialise(void)
void void
MNL_Finalise(void) MNL_Finalise(void)
{ {
LCL_RemoveParameterChangeHandler(slew_samples, NULL);
} }
/* ================================================== */ /* ================================================== */

View File

@@ -95,6 +95,9 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
case AF_INET6: case AF_INET6:
if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET6) if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET6)
continue; continue;
/* Don't return an address that would lose a scope ID */
if (((struct sockaddr_in6 *)ai->ai_addr)->sin6_scope_id != 0)
continue;
ip_addrs[i].family = IPADDR_INET6; ip_addrs[i].family = IPADDR_INET6;
memcpy(&ip_addrs[i].addr.in6, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr, memcpy(&ip_addrs[i].addr.in6, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr,
sizeof (ip_addrs->addr.in6)); sizeof (ip_addrs->addr.in6));

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2019 * Copyright (C) Miroslav Lichvar 2019-2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -55,20 +55,29 @@ generate_symmetric_auth(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *inf
{ {
int auth_len, max_auth_len; int auth_len, max_auth_len;
if (info->length + NTP_MIN_MAC_LENGTH > sizeof (*packet)) {
DEBUG_LOG("Packet too long");
return 0;
}
/* Truncate long MACs in NTPv4 packets to allow deterministic parsing /* Truncate long MACs in NTPv4 packets to allow deterministic parsing
of extension fields (RFC 7822) */ of extension fields (RFC 7822) */
max_auth_len = (info->version == 4 ? NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH) - 4; max_auth_len = (info->version == 4 ? NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH) - 4;
max_auth_len = MIN(max_auth_len, sizeof (NTP_Packet) - info->length - 4); max_auth_len = MIN(max_auth_len, sizeof (*packet) - info->length - 4);
auth_len = KEY_GenerateAuth(key_id, (unsigned char *)packet, info->length, auth_len = KEY_GenerateAuth(key_id, packet, info->length,
(unsigned char *)packet + info->length + 4, max_auth_len); (unsigned char *)packet + info->length + 4, max_auth_len);
if (!auth_len) { if (auth_len < NTP_MIN_MAC_LENGTH - 4) {
DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id); DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id);
return 0; return 0;
} }
*(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id); *(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id);
info->length += 4 + auth_len;
info->auth.mac.start = info->length;
info->auth.mac.length = 4 + auth_len;
info->auth.mac.key_id = key_id;
info->length += info->auth.mac.length;
return 1; return 1;
} }
@@ -86,7 +95,7 @@ check_symmetric_auth(NTP_Packet *packet, NTP_PacketInfo *info)
trunc_len = info->version == 4 && info->auth.mac.length <= NTP_MAX_V4_MAC_LENGTH ? trunc_len = info->version == 4 && info->auth.mac.length <= NTP_MAX_V4_MAC_LENGTH ?
NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH; NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH;
if (!KEY_CheckAuth(info->auth.mac.key_id, (void *)packet, info->auth.mac.start, if (!KEY_CheckAuth(info->auth.mac.key_id, packet, info->auth.mac.start,
(unsigned char *)packet + info->auth.mac.start + 4, (unsigned char *)packet + info->auth.mac.start + 4,
info->auth.mac.length - 4, trunc_len - 4)) info->auth.mac.length - 4, trunc_len - 4))
return 0; return 0;
@@ -96,31 +105,13 @@ check_symmetric_auth(NTP_Packet *packet, NTP_PacketInfo *info)
/* ================================================== */ /* ================================================== */
static void
adjust_timestamp(NTP_AuthMode mode, uint32_t key_id, struct timespec *ts)
{
switch (mode) {
case NTP_AUTH_SYMMETRIC:
ts->tv_nsec += KEY_GetAuthDelay(key_id);
UTI_NormaliseTimespec(ts);
break;
case NTP_AUTH_MSSNTP:
ts->tv_nsec += NSD_GetAuthDelay(key_id);
UTI_NormaliseTimespec(ts);
default:
break;
}
}
/* ================================================== */
static int static int
is_zero_data(unsigned char *data, int length) is_zero_data(unsigned char *data, int length)
{ {
int i; int i;
for (i = 0; i < length; i++) for (i = 0; i < length; i++)
if (data[i]) if (data[i] != 0)
return 0; return 0;
return 1; return 1;
} }
@@ -230,14 +221,6 @@ NAU_PrepareRequestAuth(NAU_Instance instance)
/* ================================================== */ /* ================================================== */
void
NAU_AdjustRequestTimestamp(NAU_Instance instance, struct timespec *ts)
{
adjust_timestamp(instance->mode, instance->key_id, ts);
}
/* ================================================== */
int int
NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketInfo *info) NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketInfo *info)
{ {
@@ -256,6 +239,8 @@ NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketIn
assert(0); assert(0);
} }
info->auth.mode = instance->mode;
return 1; return 1;
} }
@@ -277,6 +262,8 @@ NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info)
if (remainder <= 0) if (remainder <= 0)
return 1; return 1;
assert(remainder % 4 == 0);
/* In NTPv3 and older packets don't have extension fields. Anything after /* In NTPv3 and older packets don't have extension fields. Anything after
the header is assumed to be a MAC. */ the header is assumed to be a MAC. */
if (info->version <= 3) { if (info->version <= 3) {
@@ -287,7 +274,7 @@ NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info)
/* Check if it is an MS-SNTP authenticator field or extended authenticator /* Check if it is an MS-SNTP authenticator field or extended authenticator
field with zeroes as digest */ field with zeroes as digest */
if (info->version == 3 && info->auth.mac.key_id) { if (info->version == 3 && info->auth.mac.key_id != 0) {
if (remainder == 20 && is_zero_data(data + parsed + 4, remainder - 4)) if (remainder == 20 && is_zero_data(data + parsed + 4, remainder - 4))
info->auth.mode = NTP_AUTH_MSSNTP; info->auth.mode = NTP_AUTH_MSSNTP;
else if (remainder == 72 && is_zero_data(data + parsed + 8, remainder - 8)) else if (remainder == 72 && is_zero_data(data + parsed + 8, remainder - 8))
@@ -313,25 +300,13 @@ NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info)
if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_V4_MAC_LENGTH) if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_V4_MAC_LENGTH)
break; break;
/* The NTPv4-specific limit for MAC length enables deterministic parsing of
packets with extension fields (RFC 7822), but we support longer MACs in
packets with no extension fields for compatibility with older chrony
clients. Check if the longer MAC would authenticate the packet before
trying to parse the data as an extension field. */
if (parsed == NTP_HEADER_LENGTH &&
remainder > NTP_MAX_V4_MAC_LENGTH && remainder <= NTP_MAX_MAC_LENGTH &&
KEY_CheckAuth(ntohl(*(uint32_t *)(data + parsed)), data, parsed,
(void *)(data + parsed + 4), remainder - 4, NTP_MAX_MAC_LENGTH - 4))
break;
/* Check if this is a valid NTPv4 extension field and skip it */ /* Check if this is a valid NTPv4 extension field and skip it */
if (!NEF_ParseField(packet, info->length, parsed, &ef_length, &ef_type, NULL, NULL)) { if (!NEF_ParseField(packet, info->length, parsed, &ef_length, &ef_type, NULL, NULL)) {
/* Invalid MAC or format error */ DEBUG_LOG("Invalid format");
DEBUG_LOG("Invalid format or MAC");
return 0; return 0;
} }
assert(ef_length > 0); assert(ef_length > 0 && ef_length % 4 == 0);
switch (ef_type) { switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER: case NTP_EF_NTS_UNIQUE_IDENTIFIER:
@@ -353,9 +328,6 @@ NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info)
/* No MAC */ /* No MAC */
return 1; return 1;
} else if (remainder >= NTP_MIN_MAC_LENGTH) { } else if (remainder >= NTP_MIN_MAC_LENGTH) {
/* This is not 100% reliable as a MAC could fail to authenticate and could
pass as an extension field, leaving reminder smaller than the minimum MAC
length */
info->auth.mode = NTP_AUTH_SYMMETRIC; info->auth.mode = NTP_AUTH_SYMMETRIC;
info->auth.mac.start = parsed; info->auth.mac.start = parsed;
info->auth.mac.length = remainder; info->auth.mac.length = remainder;
@@ -384,6 +356,9 @@ NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
case NTP_AUTH_MSSNTP: case NTP_AUTH_MSSNTP:
/* MS-SNTP requests are not authenticated */ /* MS-SNTP requests are not authenticated */
break; break;
case NTP_AUTH_MSSNTP_EXT:
/* Not supported yet */
return 0;
case NTP_AUTH_NTS: case NTP_AUTH_NTS:
if (!NNS_CheckRequestAuth(request, info, kod)) if (!NNS_CheckRequestAuth(request, info, kod))
return 0; return 0;
@@ -397,14 +372,6 @@ NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
/* ================================================== */ /* ================================================== */
void
NAU_AdjustResponseTimestamp(NTP_Packet *request, NTP_PacketInfo *info, struct timespec *ts)
{
adjust_timestamp(info->auth.mode, info->auth.mac.key_id, ts);
}
/* ================================================== */
int int
NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info, NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
NTP_Packet *response, NTP_PacketInfo *response_info, NTP_Packet *response, NTP_PacketInfo *response_info,
@@ -434,6 +401,8 @@ NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
return 0; return 0;
} }
response_info->auth.mode = request_info->auth.mode;
return 1; return 1;
} }
@@ -498,3 +467,28 @@ NAU_DumpData(NAU_Instance instance)
break; break;
} }
} }
/* ================================================== */
void
NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report)
{
memset(report, 0, sizeof (*report));
report->mode = instance->mode;
report->last_ke_ago = -1;
switch (instance->mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
report->key_id = instance->key_id;
KEY_GetKeyInfo(instance->key_id, &report->key_type, &report->key_length);
break;
case NTP_AUTH_NTS:
NNC_GetReport(instance->nts, report);
break;
default:
assert(0);
}
}

View File

@@ -29,6 +29,7 @@
#include "addressing.h" #include "addressing.h"
#include "ntp.h" #include "ntp.h"
#include "reports.h"
typedef struct NAU_Instance_Record *NAU_Instance; typedef struct NAU_Instance_Record *NAU_Instance;
@@ -50,10 +51,6 @@ extern int NAU_GetSuggestedNtpVersion(NAU_Instance instance);
/* Perform operations necessary for NAU_GenerateRequestAuth() */ /* Perform operations necessary for NAU_GenerateRequestAuth() */
extern int NAU_PrepareRequestAuth(NAU_Instance instance); extern int NAU_PrepareRequestAuth(NAU_Instance instance);
/* Adjust a transmit timestamp for an estimated minimum time it takes to call
NAU_GenerateRequestAuth() */
extern void NAU_AdjustRequestTimestamp(NAU_Instance instance, struct timespec *ts);
/* Extend a request with data required by the authentication mode */ /* Extend a request with data required by the authentication mode */
extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request,
NTP_PacketInfo *info); NTP_PacketInfo *info);
@@ -65,11 +62,6 @@ extern int NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info);
kod code is returned, a KoD response should be sent back. */ kod code is returned, a KoD response should be sent back. */
extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod); extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod);
/* Adjust a transmit timestamp for an estimated minimum time it takes to call
NAU_GenerateResponseAuth() */
extern void NAU_AdjustResponseTimestamp(NTP_Packet *request, NTP_PacketInfo *info,
struct timespec *ts);
/* Extend a response with data required by the authentication mode. This /* Extend a response with data required by the authentication mode. This
function can be called only if the previous call of NAU_CheckRequestAuth() function can be called only if the previous call of NAU_CheckRequestAuth()
was on the same request. */ was on the same request. */
@@ -89,4 +81,7 @@ extern void NAU_ChangeAddress(NAU_Instance instance, IPAddr *address);
/* Save authentication-specific data to speed up the next start */ /* Save authentication-specific data to speed up the next start */
extern void NAU_DumpData(NAU_Instance instance); extern void NAU_DumpData(NAU_Instance instance);
/* Provide a report about the current authentication state */
extern void NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report);
#endif #endif

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2018 * Copyright (C) Miroslav Lichvar 2009-2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -78,6 +78,7 @@ struct NCR_Instance_Record {
SCH_TimeoutID tx_timeout_id; /* Timeout ID for next transmission */ SCH_TimeoutID tx_timeout_id; /* Timeout ID for next transmission */
int tx_suspended; /* Boolean indicating we can't transmit yet */ int tx_suspended; /* Boolean indicating we can't transmit yet */
int auto_iburst; /* If 1, initiate a burst when going online */
int auto_burst; /* If 1, initiate a burst on each poll */ int auto_burst; /* If 1, initiate a burst on each poll */
int auto_offline; /* If 1, automatically go offline when requests int auto_offline; /* If 1, automatically go offline when requests
cannot be sent */ cannot be sent */
@@ -297,6 +298,7 @@ static void transmit_timeout(void *arg);
static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx); static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
static double get_separation(int poll); static double get_separation(int poll);
static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info); static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info);
static void set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity);
/* ================================================== */ /* ================================================== */
@@ -555,6 +557,7 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
result->max_delay_ratio = CLAMP(0.0, params->max_delay_ratio, MAX_MAXDELAYRATIO); result->max_delay_ratio = CLAMP(0.0, params->max_delay_ratio, MAX_MAXDELAYRATIO);
result->max_delay_dev_ratio = CLAMP(0.0, params->max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO); result->max_delay_dev_ratio = CLAMP(0.0, params->max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
result->offset_correction = params->offset; result->offset_correction = params->offset;
result->auto_iburst = params->iburst;
result->auto_burst = params->burst; result->auto_burst = params->burst;
result->auto_offline = params->auto_offline; result->auto_offline = params->auto_offline;
result->poll_target = params->poll_target; result->poll_target = params->poll_target;
@@ -582,8 +585,8 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
/* Create a source instance for this NTP source */ /* Create a source instance for this NTP source */
result->source = SRC_CreateNewInstance(UTI_IPToRefid(&remote_addr->ip_addr), result->source = SRC_CreateNewInstance(UTI_IPToRefid(&remote_addr->ip_addr),
SRC_NTP, params->sel_options, SRC_NTP, NAU_IsAuthEnabled(result->auth),
&result->remote_addr.ip_addr, params->sel_options, &result->remote_addr.ip_addr,
params->min_samples, params->max_samples, params->min_samples, params->max_samples,
params->min_delay, params->asymmetry); params->min_delay, params->asymmetry);
@@ -596,9 +599,7 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
result->rx_timeout_id = 0; result->rx_timeout_id = 0;
result->tx_timeout_id = 0; result->tx_timeout_id = 0;
result->tx_suspended = 1; result->tx_suspended = 1;
result->opmode = params->connectivity == SRC_ONLINE || result->opmode = MD_OFFLINE;
(params->connectivity == SRC_MAYBE_ONLINE &&
NIO_IsServerConnectable(remote_addr)) ? MD_ONLINE : MD_OFFLINE;
result->local_poll = result->minpoll; result->local_poll = result->minpoll;
result->poll_score = 0.0; result->poll_score = 0.0;
zero_local_timestamp(&result->local_tx); zero_local_timestamp(&result->local_tx);
@@ -608,9 +609,7 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
NCR_ResetInstance(result); NCR_ResetInstance(result);
if (params->iburst) { set_connectivity(result, params->connectivity);
NCR_InitiateSampleBurst(result, IBURST_GOOD_SAMPLES, IBURST_TOTAL_SAMPLES);
}
return result; return result;
} }
@@ -962,6 +961,10 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
smooth_time = 0; smooth_time = 0;
smooth_offset = 0.0; smooth_offset = 0.0;
/* Get an initial transmit timestamp. A more accurate timestamp will be
taken later in this function. */
SCH_GetLastEventTime(&local_transmit, NULL, NULL);
if (my_mode == MODE_CLIENT) { if (my_mode == MODE_CLIENT) {
/* Don't reveal local time or state of the clock in client packets */ /* Don't reveal local time or state of the clock in client packets */
precision = 32; precision = 32;
@@ -969,10 +972,6 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
our_root_delay = our_root_dispersion = 0.0; our_root_delay = our_root_dispersion = 0.0;
UTI_ZeroTimespec(&our_ref_time); UTI_ZeroTimespec(&our_ref_time);
} else { } else {
/* This is accurate enough and cheaper than calling LCL_ReadCookedTime.
A more accurate timestamp will be taken later in this function. */
SCH_GetLastEventTime(&local_transmit, NULL, NULL);
REF_GetReferenceParams(&local_transmit, REF_GetReferenceParams(&local_transmit,
&are_we_synchronised, &leap_status, &are_we_synchronised, &leap_status,
&our_stratum, &our_stratum,
@@ -1018,12 +1017,8 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
message.poll = my_poll; message.poll = my_poll;
message.precision = precision; message.precision = precision;
/* If we're sending a client mode packet and we aren't synchronized yet,
we might have to set up artificial values for some of these parameters */
message.root_delay = UTI_DoubleToNtp32(our_root_delay); message.root_delay = UTI_DoubleToNtp32(our_root_delay);
message.root_dispersion = UTI_DoubleToNtp32(our_root_dispersion); message.root_dispersion = UTI_DoubleToNtp32(our_root_dispersion);
message.reference_id = htonl(our_ref_id); message.reference_id = htonl(our_ref_id);
/* Now fill in timestamps */ /* Now fill in timestamps */
@@ -1063,20 +1058,13 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
/* Prepare random bits which will be added to the transmit timestamp */ /* Prepare random bits which will be added to the transmit timestamp */
UTI_GetNtp64Fuzz(&ts_fuzz, precision); UTI_GetNtp64Fuzz(&ts_fuzz, precision);
/* Transmit - this our local time right now! Also, we might need to /* Get a more accurate transmit timestamp if it needs to be saved in the
store this for our own use later, next time we receive a message packet (i.e. in the server, symmetric, and broadcast basic modes) */
from the source we're sending to now. */ if (!interleaved && precision < 32) {
LCL_ReadCookedTime(&local_transmit, &local_transmit_err); LCL_ReadCookedTime(&local_transmit, &local_transmit_err);
if (smooth_time)
if (smooth_time) UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit);
UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit); }
/* Pre-compensate the transmit time by approximately how long it will take
to generate the authentication data */
if (auth)
NAU_AdjustRequestTimestamp(auth, &local_transmit);
else
NAU_AdjustResponseTimestamp(request, request_info, &local_transmit);
UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit, UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
&message.transmit_ts, &ts_fuzz); &message.transmit_ts, &ts_fuzz);
@@ -1112,9 +1100,16 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
return 0; return 0;
} }
/* If the transmit timestamp will be saved, get an even more
accurate daemon timestamp closer to the transmission */
if (local_tx)
LCL_ReadCookedTime(&local_transmit, &local_transmit_err);
ret = NIO_SendPacket(&message, where_to, from, info.length, local_tx != NULL); ret = NIO_SendPacket(&message, where_to, from, info.length, local_tx != NULL);
if (local_tx) { if (local_tx) {
if (smooth_time)
UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit);
local_tx->ts = local_transmit; local_tx->ts = local_transmit;
local_tx->err = local_transmit_err; local_tx->err = local_transmit_err;
local_tx->source = NTP_TS_DAEMON; local_tx->source = NTP_TS_DAEMON;
@@ -1796,8 +1791,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
break; break;
} }
} else { } else {
/* Slowly increase the polling interval if we can't get good packet */ /* Slowly increase the polling interval if we can't get a good response */
adjust_poll(inst, 0.1); adjust_poll(inst, testD ? 0.02 : 0.1);
} }
/* If in client mode, no more packets are expected to be coming from the /* If in client mode, no more packets are expected to be coming from the
@@ -2081,10 +2076,10 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
} }
kod = 0; kod = 0;
log_index = CLG_LogNTPAccess(&remote_addr->ip_addr, &rx_ts->ts); log_index = CLG_LogServiceAccess(CLG_NTP, &remote_addr->ip_addr, &rx_ts->ts);
/* Don't reply to all requests if the rate is excessive */ /* Don't reply to all requests if the rate is excessive */
if (log_index >= 0 && CLG_LimitNTPResponseRate(log_index)) { if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTP, log_index)) {
DEBUG_LOG("NTP packet discarded to limit response rate"); DEBUG_LOG("NTP packet discarded to limit response rate");
return; return;
} }
@@ -2096,17 +2091,10 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
/* Don't respond unless a non-zero KoD was returned */ /* Don't respond unless a non-zero KoD was returned */
if (kod == 0) if (kod == 0)
return; return;
} else if (info.auth.mode != NTP_AUTH_NONE && info.auth.mode != NTP_AUTH_MSSNTP) {
CLG_LogAuthNtpRequest();
} }
/* If it is an NTPv4 packet with a long MAC and no extension fields,
respond with a NTPv3 packet to avoid breaking RFC 7822 and keep
the length symmetric. Otherwise, respond with the same version. */
if (info.version == 4 && info.ext_fields == 0 && info.auth.mode == NTP_AUTH_SYMMETRIC &&
info.auth.mac.length > NTP_MAX_V4_MAC_LENGTH)
version = 3;
else
version = info.version;
local_ntp_rx = local_ntp_tx = NULL; local_ntp_rx = local_ntp_tx = NULL;
tx_ts = NULL; tx_ts = NULL;
interleaved = 0; interleaved = 0;
@@ -2136,6 +2124,9 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
poll = CLG_GetNtpMinPoll(); poll = CLG_GetNtpMinPoll();
poll = MAX(poll, message->poll); poll = MAX(poll, message->poll);
/* Respond with the same version */
version = info.version;
/* Send a reply */ /* Send a reply */
transmit_packet(my_mode, interleaved, poll, version, kod, NULL, transmit_packet(my_mode, interleaved, poll, version, kod, NULL,
&message->receive_ts, &message->transmit_ts, &message->receive_ts, &message->transmit_ts,
@@ -2255,13 +2246,9 @@ NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double dof
/* ================================================== */ /* ================================================== */
void static void
NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity) set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity)
{ {
char *s;
s = UTI_IPToString(&inst->remote_addr.ip_addr);
if (connectivity == SRC_MAYBE_ONLINE) if (connectivity == SRC_MAYBE_ONLINE)
connectivity = NIO_IsServerConnectable(&inst->remote_addr) ? SRC_ONLINE : SRC_OFFLINE; connectivity = NIO_IsServerConnectable(&inst->remote_addr) ? SRC_ONLINE : SRC_OFFLINE;
@@ -2272,17 +2259,17 @@ NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity)
/* Nothing to do */ /* Nothing to do */
break; break;
case MD_OFFLINE: case MD_OFFLINE:
LOG(LOGS_INFO, "Source %s online", s);
inst->opmode = MD_ONLINE; inst->opmode = MD_ONLINE;
NCR_ResetInstance(inst); NCR_ResetInstance(inst);
start_initial_timeout(inst); start_initial_timeout(inst);
if (inst->auto_iburst)
NCR_InitiateSampleBurst(inst, IBURST_GOOD_SAMPLES, IBURST_TOTAL_SAMPLES);
break; break;
case MD_BURST_WAS_ONLINE: case MD_BURST_WAS_ONLINE:
/* Will revert */ /* Will revert */
break; break;
case MD_BURST_WAS_OFFLINE: case MD_BURST_WAS_OFFLINE:
inst->opmode = MD_BURST_WAS_ONLINE; inst->opmode = MD_BURST_WAS_ONLINE;
LOG(LOGS_INFO, "Source %s online", s);
break; break;
default: default:
assert(0); assert(0);
@@ -2291,14 +2278,12 @@ NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity)
case SRC_OFFLINE: case SRC_OFFLINE:
switch (inst->opmode) { switch (inst->opmode) {
case MD_ONLINE: case MD_ONLINE:
LOG(LOGS_INFO, "Source %s offline", s);
take_offline(inst); take_offline(inst);
break; break;
case MD_OFFLINE: case MD_OFFLINE:
break; break;
case MD_BURST_WAS_ONLINE: case MD_BURST_WAS_ONLINE:
inst->opmode = MD_BURST_WAS_OFFLINE; inst->opmode = MD_BURST_WAS_OFFLINE;
LOG(LOGS_INFO, "Source %s offline", s);
break; break;
case MD_BURST_WAS_OFFLINE: case MD_BURST_WAS_OFFLINE:
break; break;
@@ -2313,6 +2298,26 @@ NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity)
/* ================================================== */ /* ================================================== */
void
NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity)
{
OperatingMode prev_opmode;
int was_online, is_online;
prev_opmode = inst->opmode;
set_connectivity(inst, connectivity);
/* Report an important change */
was_online = prev_opmode == MD_ONLINE || prev_opmode == MD_BURST_WAS_ONLINE;
is_online = inst->opmode == MD_ONLINE || inst->opmode == MD_BURST_WAS_ONLINE;
if (was_online != is_online)
LOG(LOGS_INFO, "Source %s %s",
UTI_IPToString(&inst->remote_addr.ip_addr), is_online ? "online" : "offline");
}
/* ================================================== */
void void
NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll) NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
{ {
@@ -2444,6 +2449,14 @@ NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *n
/* ================================================== */ /* ================================================== */
void
NCR_GetAuthReport(NCR_Instance inst, RPT_AuthReport *report)
{
NAU_GetReport(inst->auth, report);
}
/* ================================================== */
void void
NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report) NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report)
{ {
@@ -2596,14 +2609,13 @@ broadcast_timeout(void *arg)
/* ================================================== */ /* ================================================== */
void void
NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval) NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval)
{ {
BroadcastDestination *destination; BroadcastDestination *destination;
destination = (BroadcastDestination *)ARR_GetNewElement(broadcasts); destination = (BroadcastDestination *)ARR_GetNewElement(broadcasts);
destination->addr.ip_addr = *addr; destination->addr = *addr;
destination->addr.port = port;
destination->local_addr.ip_addr.family = IPADDR_UNSPEC; destination->local_addr.ip_addr.family = IPADDR_UNSPEC;
destination->local_addr.if_index = INVALID_IF_INDEX; destination->local_addr.if_index = INVALID_IF_INDEX;
destination->local_addr.sock_fd = NIO_OpenServerSocket(&destination->addr); destination->local_addr.sock_fd = NIO_OpenServerSocket(&destination->addr);

View File

@@ -122,6 +122,7 @@ extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target);
extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples); extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);
extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now); extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now);
extern void NCR_GetAuthReport(NCR_Instance inst, RPT_AuthReport *report);
extern void NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report); extern void NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report);
extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all); extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all);
@@ -138,6 +139,6 @@ extern int NCR_IsSyncPeer(NCR_Instance instance);
extern void NCR_DumpAuthData(NCR_Instance inst); extern void NCR_DumpAuthData(NCR_Instance inst);
extern void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval); extern void NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval);
#endif /* GOT_NTP_CORE_H */ #endif /* GOT_NTP_CORE_H */

View File

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

View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009 * Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018 * Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -83,19 +83,20 @@ static void read_from_socket(int sock_fd, int event, void *anything);
static int static int
open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr) open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr)
{ {
int sock_fd, sock_flags, events = SCH_FILE_INPUT; int sock_fd, sock_flags, dscp, events = SCH_FILE_INPUT;
IPSockAddr local_addr; IPSockAddr local_addr;
char *iface;
if (!SCK_IsFamilySupported(family)) if (!SCK_IsIpFamilyEnabled(family))
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
if (!client_only) if (!client_only) {
CNF_GetBindAddress(family, &local_addr.ip_addr); CNF_GetBindAddress(family, &local_addr.ip_addr);
else iface = CNF_GetBindNtpInterface();
} else {
CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr); CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr);
iface = CNF_GetBindAcquisitionInterface();
if (local_addr.ip_addr.family != family) }
SCK_GetAnyLocalIPAddress(family, &local_addr.ip_addr);
local_addr.port = local_port; local_addr.port = local_port;
@@ -103,13 +104,21 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
if (!client_only) if (!client_only)
sock_flags |= SCK_FLAG_BROADCAST; sock_flags |= SCK_FLAG_BROADCAST;
sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, sock_flags); sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, iface, sock_flags);
if (sock_fd < 0) { if (sock_fd < 0) {
if (!client_only) if (!client_only)
LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr)); LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr));
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
dscp = CNF_GetNtpDscp();
if (dscp > 0 && dscp < 64) {
#ifdef IP_TOS
if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
;
#endif
}
if (!client_only && family == IPADDR_INET4 && local_addr.port > 0) if (!client_only && family == IPADDR_INET4 && local_addr.port > 0)
bound_server_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY; bound_server_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
@@ -152,7 +161,7 @@ close_socket(int sock_fd)
/* ================================================== */ /* ================================================== */
void void
NIO_Initialise(int family) NIO_Initialise(void)
{ {
int server_port, client_port; int server_port, client_port;
@@ -191,25 +200,18 @@ NIO_Initialise(int family)
server_sock_ref4 = 0; server_sock_ref4 = 0;
server_sock_ref6 = 0; server_sock_ref6 = 0;
if (family == IPADDR_UNSPEC || family == IPADDR_INET4) { if (permanent_server_sockets && server_port) {
if (permanent_server_sockets && server_port) server_sock_fd4 = open_socket(IPADDR_INET4, server_port, 0, NULL);
server_sock_fd4 = open_socket(IPADDR_INET4, server_port, 0, NULL); server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL);
if (!separate_client_sockets) {
if (client_port != server_port || !server_port)
client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL);
else
client_sock_fd4 = server_sock_fd4;
}
} }
if (family == IPADDR_UNSPEC || family == IPADDR_INET6) { if (!separate_client_sockets) {
if (permanent_server_sockets && server_port) if (client_port != server_port || !server_port) {
server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL); client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL);
if (!separate_client_sockets) { client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL);
if (client_port != server_port || !server_port) } else {
client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL); client_sock_fd4 = server_sock_fd4;
else client_sock_fd6 = server_sock_fd6;
client_sock_fd6 = server_sock_fd6;
} }
} }
@@ -455,9 +457,13 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
message.remote_addr.ip.port = remote_addr->port; message.remote_addr.ip.port = remote_addr->port;
} }
message.if_index = local_addr->if_index;
message.local_addr.ip = local_addr->ip_addr; message.local_addr.ip = local_addr->ip_addr;
/* Don't require responses to non-link-local addresses to use the same
interface */
message.if_index = SCK_IsLinkLocalIPAddress(&message.remote_addr.ip.ip_addr) ?
local_addr->if_index : INVALID_IF_INDEX;
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR) #if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */ /* On FreeBSD a local IPv4 address cannot be specified on bound socket */
if (message.local_addr.ip.family == IPADDR_INET4 && if (message.local_addr.ip.family == IPADDR_INET4 &&

View File

@@ -33,7 +33,7 @@
#include "addressing.h" #include "addressing.h"
/* Function to initialise the module. */ /* Function to initialise the module. */
extern void NIO_Initialise(int family); extern void NIO_Initialise(void);
/* Function to finalise the module */ /* Function to finalise the module */
extern void NIO_Finalise(void); extern void NIO_Finalise(void);

View File

@@ -125,7 +125,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
return 1; return 1;
} }
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0); sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0) if (sock_fd < 0)
return 0; return 0;
@@ -200,7 +200,8 @@ add_interface(CNF_HwTsInterface *conf_iface)
req.ifr_data = (char *)&ts_config; req.ifr_data = (char *)&ts_config;
if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) { if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno)); LOG(errno == EPERM ? LOGS_ERR : LOGS_DEBUG,
"ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
/* Check the current timestamping configuration in case this interface /* Check the current timestamping configuration in case this interface
allows only reading of the configuration and it was already configured allows only reading of the configuration and it was already configured
@@ -285,7 +286,7 @@ update_interface_speed(struct Interface *iface)
struct ifreq req; struct ifreq req;
int sock_fd, link_speed; int sock_fd, link_speed;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0); sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0) if (sock_fd < 0)
return; return;
@@ -320,7 +321,7 @@ check_timestamping_option(int option)
{ {
int sock_fd; int sock_fd;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0); sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0) if (sock_fd < 0)
return 0; return 0;
@@ -341,7 +342,7 @@ open_dummy_socket(void)
{ {
int sock_fd, events = 0; int sock_fd, events = 0;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0); sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0) if (sock_fd < 0)
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
@@ -757,7 +758,7 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
l2_length = message->length; l2_length = message->length;
message->length = extract_udp_data(message->data, &message->remote_addr.ip, message->length); message->length = extract_udp_data(message->data, &message->remote_addr.ip, message->length);
DEBUG_LOG("Extracted message for %s fd=%d len=%u", DEBUG_LOG("Extracted message for %s fd=%d len=%d",
UTI_IPSockAddrToString(&message->remote_addr.ip), UTI_IPSockAddrToString(&message->remote_addr.ip),
local_addr->sock_fd, message->length); local_addr->sock_fd, message->length);

View File

@@ -96,14 +96,6 @@ static unsigned int queue_tail;
/* Unix domain socket connected to ntp_signd */ /* Unix domain socket connected to ntp_signd */
static int sock_fd; static int sock_fd;
#define MIN_AUTH_DELAY 1.0e-5
#define MAX_AUTH_DELAY 1.0e-2
/* Average time needed for signing one packet. This is used to adjust the
transmit timestamp in NTP packets. The timestamp won't be very accurate as
the delay is variable, but it should be good enough for MS-SNTP clients. */
static double auth_delay;
/* Flag indicating if the MS-SNTP authentication is enabled */ /* Flag indicating if the MS-SNTP authentication is enabled */
static int enabled; static int enabled;
@@ -183,10 +175,6 @@ process_response(SignInstance *inst)
NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr, NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
ntohl(inst->response.length) + sizeof (inst->response.length) - ntohl(inst->response.length) + sizeof (inst->response.length) -
offsetof(SigndResponse, signed_packet), 0); offsetof(SigndResponse, signed_packet), 0);
/* Update exponential moving average of the authentication delay */
delay = CLAMP(MIN_AUTH_DELAY, delay, MAX_AUTH_DELAY);
auth_delay += 0.1 * (delay - auth_delay);
} }
/* ================================================== */ /* ================================================== */
@@ -274,7 +262,6 @@ void
NSD_Initialise() NSD_Initialise()
{ {
sock_fd = INVALID_SOCK_FD; sock_fd = INVALID_SOCK_FD;
auth_delay = MIN_AUTH_DELAY;
enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0]; enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0];
if (!enabled) if (!enabled)
@@ -301,13 +288,6 @@ NSD_Finalise()
/* ================================================== */ /* ================================================== */
extern int NSD_GetAuthDelay(uint32_t key_id)
{
return 1.0e9 * auth_delay;
}
/* ================================================== */
int int
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info, NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr) NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)

View File

@@ -35,9 +35,6 @@ extern void NSD_Initialise(void);
/* Finalisation function */ /* Finalisation function */
extern void NSD_Finalise(void); extern void NSD_Finalise(void);
/* Function to get an estimate of delay due to signing */
extern int NSD_GetAuthDelay(uint32_t key_id);
/* Function to sign an NTP packet and send it */ /* Function to sign an NTP packet and send it */
extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info, extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr); NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr);

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016 * Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -54,10 +54,12 @@ typedef struct {
is not resolved yet) */ is not resolved yet) */
NCR_Instance data; /* Data for the protocol engine for this source */ NCR_Instance data; /* Data for the protocol engine for this source */
char *name; /* Name of the source, may be NULL */ char *name; /* Name of the source, may be NULL */
int pool; /* Number of the pool from which was this source int pool_id; /* ID of the pool from which was this source
added or INVALID_POOL */ added or INVALID_POOL */
int tentative; /* Flag indicating there was no valid response int tentative; /* Flag indicating there was no valid response
received from the source yet */ received from the source yet */
uint32_t conf_id; /* Configuration ID, which can be shared with
different sources in case of a pool */
} SourceRecord; } SourceRecord;
/* Hash table of SourceRecord, its size is a power of two and it's never /* Hash table of SourceRecord, its size is a power of two and it's never
@@ -73,13 +75,16 @@ static int auto_start_sources = 0;
/* Last assigned address ID */ /* Last assigned address ID */
static uint32_t last_address_id = 0; static uint32_t last_address_id = 0;
/* Last assigned configuration ID */
static uint32_t last_conf_id = 0;
/* Source scheduled for name resolving (first resolving or replacement) */ /* Source scheduled for name resolving (first resolving or replacement) */
struct UnresolvedSource { struct UnresolvedSource {
/* Current address of the source (IDADDR_ID is used for a single source /* Current address of the source (IPADDR_ID is used for a single source
with unknown address and IPADDR_UNSPEC for a pool of sources */ with unknown address and IPADDR_UNSPEC for a pool of sources) */
NTP_Remote_Address address; NTP_Remote_Address address;
/* ID of the pool if not a single source */ /* ID of the pool if not a single source */
int pool; int pool_id;
/* Name to be resolved */ /* Name to be resolved */
char *name; char *name;
/* Flag indicating addresses should be used in a random order */ /* Flag indicating addresses should be used in a random order */
@@ -114,7 +119,7 @@ struct SourcePool {
int max_sources; int max_sources;
}; };
/* Array of SourcePool */ /* Array of SourcePool (indexed by their ID) */
static ARR_Instance pools; static ARR_Instance pools;
/* ================================================== */ /* ================================================== */
@@ -123,7 +128,7 @@ static ARR_Instance pools;
static void resolve_sources(void); static void resolve_sources(void);
static void rehash_records(void); static void rehash_records(void);
static void clean_source_record(SourceRecord *record); static void clean_source_record(SourceRecord *record);
static void remove_pool_sources(int pool, int tentative, int unresolved); static void remove_pool_sources(int pool_id, int tentative, int unresolved);
static void remove_unresolved_source(struct UnresolvedSource *us); static void remove_unresolved_source(struct UnresolvedSource *us);
static void static void
@@ -185,6 +190,8 @@ NSR_Finalise(void)
clean_source_record(record); clean_source_record(record);
} }
LCL_RemoveParameterChangeHandler(slew_sources, NULL);
ARR_DestroyInstance(records); ARR_DestroyInstance(records);
ARR_DestroyInstance(pools); ARR_DestroyInstance(pools);
@@ -195,39 +202,30 @@ NSR_Finalise(void)
} }
/* ================================================== */ /* ================================================== */
/* Return slot number and whether the IP address was matched or not. /* Find a slot matching an IP address. It is assumed that there can
found = 0 => Neither IP nor port matched, empty slot returned only ever be one record for a particular IP address. */
found = 1 => Only IP matched, port doesn't match
found = 2 => Both IP and port matched.
It is assumed that there can only ever be one record for a static int
particular IP address. (If a different port comes up, it probably find_slot(IPAddr *ip_addr, int *slot)
means someone is running ntpdate -d or something). Thus, if we
match the IP address we stop the search regardless of whether the
port number matches.
*/
static void
find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
{ {
SourceRecord *record; SourceRecord *record;
uint32_t hash; uint32_t hash;
unsigned int i, size; unsigned int i, size;
unsigned short port;
size = ARR_GetSize(records); size = ARR_GetSize(records);
*slot = 0; *slot = 0;
*found = 0;
if (remote_addr->ip_addr.family != IPADDR_INET4 && switch (ip_addr->family) {
remote_addr->ip_addr.family != IPADDR_INET6 && case IPADDR_INET4:
remote_addr->ip_addr.family != IPADDR_ID) case IPADDR_INET6:
return; case IPADDR_ID:
break;
default:
return 0;
}
hash = UTI_IPToHash(&remote_addr->ip_addr); hash = UTI_IPToHash(ip_addr);
port = remote_addr->port;
for (i = 0; i < size / 2; i++) { for (i = 0; i < size / 2; i++) {
/* Use quadratic probing */ /* Use quadratic probing */
@@ -237,12 +235,26 @@ find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
if (!record->remote_addr) if (!record->remote_addr)
break; break;
if (!UTI_CompareIPs(&record->remote_addr->ip_addr, if (UTI_CompareIPs(&record->remote_addr->ip_addr, ip_addr, NULL) == 0)
&remote_addr->ip_addr, NULL)) { return 1;
*found = record->remote_addr->port == port ? 2 : 1;
return;
}
} }
return 0;
}
/* ================================================== */
/* Find a slot matching an IP address and port. The function returns:
0 => IP not matched, empty slot returned if a valid address was provided
1 => Only IP matched, port doesn't match
2 => Both IP and port matched. */
static int
find_slot2(NTP_Remote_Address *remote_addr, int *slot)
{
if (!find_slot(&remote_addr->ip_addr, slot))
return 0;
return get_record(*slot)->remote_addr->port == remote_addr->port ? 2 : 1;
} }
/* ================================================== */ /* ================================================== */
@@ -261,7 +273,7 @@ rehash_records(void)
{ {
SourceRecord *temp_records; SourceRecord *temp_records;
unsigned int i, old_size, new_size; unsigned int i, old_size, new_size;
int slot, found; int slot;
old_size = ARR_GetSize(records); old_size = ARR_GetSize(records);
@@ -281,8 +293,8 @@ rehash_records(void)
if (!temp_records[i].remote_addr) if (!temp_records[i].remote_addr)
continue; continue;
find_slot(temp_records[i].remote_addr, &slot, &found); if (find_slot2(temp_records[i].remote_addr, &slot) != 0)
assert(!found); assert(0);
*get_record(slot) = temp_records[i]; *get_record(slot) = temp_records[i];
} }
@@ -294,16 +306,16 @@ rehash_records(void)
/* Procedure to add a new source */ /* Procedure to add a new source */
static NSR_Status static NSR_Status
add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, SourceParameters *params, int pool) add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
SourceParameters *params, int pool_id, uint32_t conf_id)
{ {
SourceRecord *record; SourceRecord *record;
int slot, found; int slot;
assert(initialised); assert(initialised);
/* Find empty bin & check that we don't have the address already */ /* Find empty bin & check that we don't have the address already */
find_slot(remote_addr, &slot, &found); if (find_slot2(remote_addr, &slot) != 0) {
if (found) {
return NSR_AlreadyInUse; return NSR_AlreadyInUse;
} else { } else {
if (remote_addr->ip_addr.family != IPADDR_INET4 && if (remote_addr->ip_addr.family != IPADDR_INET4 &&
@@ -315,21 +327,22 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, So
if (!check_hashtable_size(n_sources, ARR_GetSize(records))) { if (!check_hashtable_size(n_sources, ARR_GetSize(records))) {
rehash_records(); rehash_records();
find_slot(remote_addr, &slot, &found); if (find_slot2(remote_addr, &slot) != 0)
assert(0);
} }
assert(!found);
record = get_record(slot); record = get_record(slot);
record->data = NCR_CreateInstance(remote_addr, type, params, name); record->data = NCR_CreateInstance(remote_addr, type, params, name);
record->remote_addr = NCR_GetRemoteAddress(record->data); record->remote_addr = NCR_GetRemoteAddress(record->data);
record->name = name ? Strdup(name) : NULL; record->name = name ? Strdup(name) : NULL;
record->pool = pool; record->pool_id = pool_id;
record->tentative = 1; record->tentative = 1;
record->conf_id = conf_id;
if (record->pool != INVALID_POOL) { if (record->pool_id != INVALID_POOL) {
get_pool(record->pool)->sources++; get_pool(record->pool_id)->sources++;
if (!UTI_IsIPReal(&remote_addr->ip_addr)) if (!UTI_IsIPReal(&remote_addr->ip_addr))
get_pool(record->pool)->unresolved_sources++; get_pool(record->pool_id)->unresolved_sources++;
} }
if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr)) if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr))
@@ -351,13 +364,13 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
LOG_Severity severity; LOG_Severity severity;
char *name; char *name;
find_slot(old_addr, &slot1, &found); found = find_slot2(old_addr, &slot1);
if (!found) if (found == 0)
return NSR_NoSuchSource; return NSR_NoSuchSource;
/* Make sure there is no other source using the new address (with the same /* Make sure there is no other source using the new address (with the same
or different port), but allow a source to have its port changed */ or different port), but allow a source to have its port changed */
find_slot(new_addr, &slot2, &found); found = find_slot2(new_addr, &slot2);
if (found == 2 || (found != 0 && slot1 != slot2)) if (found == 2 || (found != 0 && slot1 != slot2))
return NSR_AlreadyInUse; return NSR_AlreadyInUse;
@@ -367,15 +380,15 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
if (!UTI_IsIPReal(&old_addr->ip_addr) && UTI_IsIPReal(&new_addr->ip_addr)) { if (!UTI_IsIPReal(&old_addr->ip_addr) && UTI_IsIPReal(&new_addr->ip_addr)) {
if (auto_start_sources) if (auto_start_sources)
NCR_StartInstance(record->data); NCR_StartInstance(record->data);
if (record->pool != INVALID_POOL) if (record->pool_id != INVALID_POOL)
get_pool(record->pool)->unresolved_sources--; get_pool(record->pool_id)->unresolved_sources--;
} }
if (!record->tentative) { if (!record->tentative) {
record->tentative = 1; record->tentative = 1;
if (record->pool != INVALID_POOL) if (record->pool_id != INVALID_POOL)
get_pool(record->pool)->confirmed_sources--; get_pool(record->pool_id)->confirmed_sources--;
} }
name = record->name; name = record->name;
@@ -430,12 +443,12 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr)); DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr));
if (us->pool != INVALID_POOL) { if (us->pool_id != INVALID_POOL) {
/* In the pool resolving mode, try to replace all sources from /* In the pool resolving mode, try to replace all sources from
the pool which don't have a real address yet */ the pool which don't have a real address yet */
for (j = 0; j < ARR_GetSize(records); j++) { for (j = 0; j < ARR_GetSize(records); j++) {
record = get_record(j); record = get_record(j);
if (!record->remote_addr || record->pool != us->pool || if (!record->remote_addr || record->pool_id != us->pool_id ||
UTI_IsIPReal(&record->remote_addr->ip_addr)) UTI_IsIPReal(&record->remote_addr->ip_addr))
continue; continue;
old_addr = *record->remote_addr; old_addr = *record->remote_addr;
@@ -456,15 +469,14 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
static int static int
is_resolved(struct UnresolvedSource *us) is_resolved(struct UnresolvedSource *us)
{ {
int slot, found; int slot;
if (us->pool != INVALID_POOL) { if (us->pool_id != INVALID_POOL) {
return get_pool(us->pool)->unresolved_sources <= 0; return get_pool(us->pool_id)->unresolved_sources <= 0;
} else { } else {
/* If the address is no longer present, it was removed or replaced /* If the address is no longer present, it was removed or replaced
(i.e. resolved) */ (i.e. resolved) */
find_slot(&us->address, &slot, &found); return find_slot2(&us->address, &slot) == 0;
return !found;
} }
} }
@@ -597,27 +609,64 @@ remove_unresolved_source(struct UnresolvedSource *us)
/* ================================================== */ /* ================================================== */
NSR_Status static int get_unused_pool_id(void)
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params)
{ {
return add_source(remote_addr, NULL, type, params, INVALID_POOL); struct UnresolvedSource *us;
int i;
for (i = 0; i < ARR_GetSize(pools); i++) {
if (get_pool(i)->sources > 0)
continue;
/* Make sure there is no name waiting to be resolved using this pool */
for (us = unresolved_sources; us; us = us->next) {
if (us->pool_id == i)
break;
}
if (us)
continue;
return i;
}
return INVALID_POOL;
} }
/* ================================================== */ /* ================================================== */
NSR_Status NSR_Status
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params) NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
NSR_Status s;
s = add_source(remote_addr, NULL, type, params, INVALID_POOL, last_conf_id + 1);
if (s != NSR_Success)
return s;
last_conf_id++;
if (conf_id)
*conf_id = last_conf_id;
return s;
}
/* ================================================== */
NSR_Status
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{ {
struct UnresolvedSource *us; struct UnresolvedSource *us;
struct SourcePool *sp; struct SourcePool *sp;
NTP_Remote_Address remote_addr; NTP_Remote_Address remote_addr;
int i, new_sources; int i, new_sources, pool_id;
/* If the name is an IP address, don't bother with full resolving now /* If the name is an IP address, don't bother with full resolving now
or later when trying to replace the source */ or later when trying to replace the source */
if (UTI_StringToIP(name, &remote_addr.ip_addr)) { if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
remote_addr.port = port; remote_addr.port = port;
return NSR_AddSource(&remote_addr, type, params); return NSR_AddSource(&remote_addr, type, params, conf_id);
} }
/* Make sure the name is at least printable and has no spaces */ /* Make sure the name is at least printable and has no spaces */
@@ -635,26 +684,37 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, Source
remote_addr.port = port; remote_addr.port = port;
if (!pool) { if (!pool) {
us->pool = INVALID_POOL; us->pool_id = INVALID_POOL;
us->address = remote_addr; us->address = remote_addr;
new_sources = 1; new_sources = 1;
} else { } else {
sp = (struct SourcePool *)ARR_GetNewElement(pools); pool_id = get_unused_pool_id();
if (pool_id != INVALID_POOL) {
sp = get_pool(pool_id);
} else {
sp = ARR_GetNewElement(pools);
pool_id = ARR_GetSize(pools) - 1;
}
sp->sources = 0; sp->sources = 0;
sp->unresolved_sources = 0; sp->unresolved_sources = 0;
sp->confirmed_sources = 0; sp->confirmed_sources = 0;
sp->max_sources = CLAMP(1, params->max_sources, MAX_POOL_SOURCES); sp->max_sources = CLAMP(1, params->max_sources, MAX_POOL_SOURCES);
us->pool = ARR_GetSize(pools) - 1; us->pool_id = pool_id;
us->address.ip_addr.family = IPADDR_UNSPEC; us->address.ip_addr.family = IPADDR_UNSPEC;
new_sources = MIN(2 * sp->max_sources, MAX_POOL_SOURCES); new_sources = MIN(2 * sp->max_sources, MAX_POOL_SOURCES);
} }
append_unresolved_source(us); append_unresolved_source(us);
last_conf_id++;
if (conf_id)
*conf_id = last_conf_id;
for (i = 0; i < new_sources; i++) { for (i = 0; i < new_sources; i++) {
if (i > 0) if (i > 0)
remote_addr.ip_addr.addr.id = ++last_address_id; remote_addr.ip_addr.addr.id = ++last_address_id;
if (add_source(&remote_addr, name, type, params, us->pool) != NSR_Success) if (add_source(&remote_addr, name, type, params, us->pool_id, last_conf_id) != NSR_Success)
return NSR_TooManySources; return NSR_TooManySources;
} }
@@ -721,8 +781,8 @@ clean_source_record(SourceRecord *record)
{ {
assert(record->remote_addr); assert(record->remote_addr);
if (record->pool != INVALID_POOL) { if (record->pool_id != INVALID_POOL) {
struct SourcePool *pool = get_pool(record->pool); struct SourcePool *pool = get_pool(record->pool_id);
pool->sources--; pool->sources--;
if (!UTI_IsIPReal(&record->remote_addr->ip_addr)) if (!UTI_IsIPReal(&record->remote_addr->ip_addr))
@@ -745,19 +805,16 @@ clean_source_record(SourceRecord *record)
/* Procedure to remove a source. We don't bother whether the port /* Procedure to remove a source. We don't bother whether the port
address is matched - we're only interested in removing a record for address is matched - we're only interested in removing a record for
the right IP address. Thus the caller can specify the port number the right IP address. */
as zero if it wishes. */
NSR_Status NSR_Status
NSR_RemoveSource(NTP_Remote_Address *remote_addr) NSR_RemoveSource(IPAddr *address)
{ {
int slot, found; int slot;
assert(initialised); assert(initialised);
find_slot(remote_addr, &slot, &found); if (find_slot(address, &slot) == 0)
if (!found) {
return NSR_NoSuchSource; return NSR_NoSuchSource;
}
clean_source_record(get_record(slot)); clean_source_record(get_record(slot));
@@ -771,6 +828,24 @@ NSR_RemoveSource(NTP_Remote_Address *remote_addr)
/* ================================================== */ /* ================================================== */
void
NSR_RemoveSourcesById(uint32_t conf_id)
{
SourceRecord *record;
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr || record->conf_id != conf_id)
continue;
clean_source_record(record);
}
rehash_records();
}
/* ================================================== */
void void
NSR_RemoveAllSources(void) NSR_RemoveAllSources(void)
{ {
@@ -803,7 +878,7 @@ resolve_source_replacement(SourceRecord *record)
stuck to a pair of addresses if the order doesn't change, or a group of stuck to a pair of addresses if the order doesn't change, or a group of
IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */ IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */
us->random_order = record->tentative; us->random_order = record->tentative;
us->pool = INVALID_POOL; us->pool_id = INVALID_POOL;
us->address = *record->remote_addr; us->address = *record->remote_addr;
append_unresolved_source(us); append_unresolved_source(us);
@@ -817,16 +892,11 @@ NSR_HandleBadSource(IPAddr *address)
{ {
static struct timespec last_replacement; static struct timespec last_replacement;
struct timespec now; struct timespec now;
NTP_Remote_Address remote_addr;
SourceRecord *record; SourceRecord *record;
int slot, found;
double diff; double diff;
int slot;
remote_addr.ip_addr = *address; if (!find_slot(address, &slot))
remote_addr.port = 0;
find_slot(&remote_addr, &slot, &found);
if (!found)
return; return;
record = get_record(slot); record = get_record(slot);
@@ -877,7 +947,7 @@ NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr, NTP_Remote_Address *new
/* ================================================== */ /* ================================================== */
static void remove_pool_sources(int pool, int tentative, int unresolved) static void remove_pool_sources(int pool_id, int tentative, int unresolved)
{ {
SourceRecord *record; SourceRecord *record;
unsigned int i, removed; unsigned int i, removed;
@@ -885,7 +955,7 @@ static void remove_pool_sources(int pool, int tentative, int unresolved)
for (i = removed = 0; i < ARR_GetSize(records); i++) { for (i = removed = 0; i < ARR_GetSize(records); i++) {
record = get_record(i); record = get_record(i);
if (!record->remote_addr || record->pool != pool) if (!record->remote_addr || record->pool_id != pool_id)
continue; continue;
if ((tentative && !record->tentative) || if ((tentative && !record->tentative) ||
@@ -908,14 +978,9 @@ static void remove_pool_sources(int pool, int tentative, int unresolved)
uint32_t uint32_t
NSR_GetLocalRefid(IPAddr *address) NSR_GetLocalRefid(IPAddr *address)
{ {
NTP_Remote_Address remote_addr; int slot;
int slot, found;
remote_addr.ip_addr = *address; if (!find_slot(address, &slot))
remote_addr.port = 0;
find_slot(&remote_addr, &slot, &found);
if (!found)
return 0; return 0;
return NCR_GetLocalRefid(get_record(slot)->data); return NCR_GetLocalRefid(get_record(slot)->data);
@@ -926,16 +991,11 @@ NSR_GetLocalRefid(IPAddr *address)
char * char *
NSR_GetName(IPAddr *address) NSR_GetName(IPAddr *address)
{ {
NTP_Remote_Address remote_addr;
int slot, found;
SourceRecord *record; SourceRecord *record;
int slot;
remote_addr.ip_addr = *address; if (!find_slot(address, &slot))
remote_addr.port = 0; return 0;
find_slot(&remote_addr, &slot, &found);
if (!found)
return NULL;
record = get_record(slot); record = get_record(slot);
if (record->name) if (record->name)
@@ -954,12 +1014,12 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
{ {
SourceRecord *record; SourceRecord *record;
struct SourcePool *pool; struct SourcePool *pool;
int slot, found; int slot;
assert(initialised); assert(initialised);
find_slot(remote_addr, &slot, &found); /* Must match IP address AND port number */
if (found == 2) { /* Must match IP address AND port number */ if (find_slot2(remote_addr, &slot) == 2) {
record = get_record(slot); record = get_record(slot);
if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length)) if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
@@ -969,8 +1029,8 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
/* This was the first good reply from the source */ /* This was the first good reply from the source */
record->tentative = 0; record->tentative = 0;
if (record->pool != INVALID_POOL) { if (record->pool_id != INVALID_POOL) {
pool = get_pool(record->pool); pool = get_pool(record->pool_id);
pool->confirmed_sources++; pool->confirmed_sources++;
DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->confirmed_sources); DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->confirmed_sources);
@@ -978,7 +1038,7 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
/* If the number of sources from the pool reached the configured /* If the number of sources from the pool reached the configured
maximum, remove the remaining tentative sources */ maximum, remove the remaining tentative sources */
if (pool->confirmed_sources >= pool->max_sources) if (pool->confirmed_sources >= pool->max_sources)
remove_pool_sources(record->pool, 1, 0); remove_pool_sources(record->pool_id, 1, 0);
} }
} }
} else { } else {
@@ -993,11 +1053,10 @@ NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length) NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
{ {
SourceRecord *record; SourceRecord *record;
int slot, found; int slot;
find_slot(remote_addr, &slot, &found); /* Must match IP address AND port number */
if (find_slot2(remote_addr, &slot) == 2) {
if (found == 2) { /* Must match IP address AND port number */
record = get_record(slot); record = get_record(slot);
NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length); NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
} else { } else {
@@ -1074,18 +1133,13 @@ NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity
int int
NSR_ModifyMinpoll(IPAddr *address, int new_minpoll) NSR_ModifyMinpoll(IPAddr *address, int new_minpoll)
{ {
int slot, found; int slot;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found); if (!find_slot(address, &slot))
if (found == 0) {
return 0; return 0;
} else {
NCR_ModifyMinpoll(get_record(slot)->data, new_minpoll); NCR_ModifyMinpoll(get_record(slot)->data, new_minpoll);
return 1; return 1;
}
} }
/* ================================================== */ /* ================================================== */
@@ -1093,18 +1147,13 @@ NSR_ModifyMinpoll(IPAddr *address, int new_minpoll)
int int
NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll) NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll)
{ {
int slot, found; int slot;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found); if (!find_slot(address, &slot))
if (found == 0) {
return 0; return 0;
} else {
NCR_ModifyMaxpoll(get_record(slot)->data, new_maxpoll); NCR_ModifyMaxpoll(get_record(slot)->data, new_maxpoll);
return 1; return 1;
}
} }
/* ================================================== */ /* ================================================== */
@@ -1112,18 +1161,13 @@ NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll)
int int
NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay) NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay)
{ {
int slot, found; int slot;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found); if (!find_slot(address, &slot))
if (found == 0) {
return 0; return 0;
} else {
NCR_ModifyMaxdelay(get_record(slot)->data, new_max_delay); NCR_ModifyMaxdelay(get_record(slot)->data, new_max_delay);
return 1; return 1;
}
} }
/* ================================================== */ /* ================================================== */
@@ -1131,18 +1175,13 @@ NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay)
int int
NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio) NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio)
{ {
int slot, found; int slot;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found); if (!find_slot(address, &slot))
if (found == 0) {
return 0; return 0;
} else {
NCR_ModifyMaxdelayratio(get_record(slot)->data, new_max_delay_ratio); NCR_ModifyMaxdelayratio(get_record(slot)->data, new_max_delay_ratio);
return 1; return 1;
}
} }
/* ================================================== */ /* ================================================== */
@@ -1150,18 +1189,13 @@ NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio)
int int
NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_dev_ratio) NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_dev_ratio)
{ {
int slot, found; int slot;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found); if (!find_slot(address, &slot))
if (found == 0) {
return 0; return 0;
} else {
NCR_ModifyMaxdelaydevratio(get_record(slot)->data, new_max_delay_dev_ratio); NCR_ModifyMaxdelaydevratio(get_record(slot)->data, new_max_delay_dev_ratio);
return 1; return 1;
}
} }
/* ================================================== */ /* ================================================== */
@@ -1169,18 +1203,13 @@ NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_dev_ratio)
int int
NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum) NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
{ {
int slot, found; int slot;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found); if (!find_slot(address, &slot))
if (found == 0) {
return 0; return 0;
} else {
NCR_ModifyMinstratum(get_record(slot)->data, new_min_stratum); NCR_ModifyMinstratum(get_record(slot)->data, new_min_stratum);
return 1; return 1;
}
} }
/* ================================================== */ /* ================================================== */
@@ -1188,18 +1217,13 @@ NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
int int
NSR_ModifyPolltarget(IPAddr *address, int new_poll_target) NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
{ {
int slot, found; int slot;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found); if (!find_slot(address, &slot))
if (found == 0) {
return 0; return 0;
} else {
NCR_ModifyPolltarget(get_record(slot)->data, new_poll_target); NCR_ModifyPolltarget(get_record(slot)->data, new_poll_target);
return 1; return 1;
}
} }
/* ================================================== */ /* ================================================== */
@@ -1235,13 +1259,9 @@ NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples,
void void
NSR_ReportSource(RPT_SourceReport *report, struct timespec *now) NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
{ {
NTP_Remote_Address rem_addr; int slot;
int slot, found;
rem_addr.ip_addr = report->ip_addr; if (find_slot(&report->ip_addr, &slot)) {
rem_addr.port = 0;
find_slot(&rem_addr, &slot, &found);
if (found) {
NCR_ReportSource(get_record(slot)->data, report, now); NCR_ReportSource(get_record(slot)->data, report, now);
} else { } else {
report->poll = 0; report->poll = 0;
@@ -1249,6 +1269,20 @@ NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
} }
} }
/* ================================================== */
int
NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report)
{
int slot;
if (!find_slot(address, &slot))
return 0;
NCR_GetAuthReport(get_record(slot)->data, report);
return 1;
}
/* ================================================== */ /* ================================================== */
/* The ip address is assumed to be completed on input, that is how we /* The ip address is assumed to be completed on input, that is how we
identify the source record. */ identify the source record. */
@@ -1256,13 +1290,9 @@ NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
int int
NSR_GetNTPReport(RPT_NTPReport *report) NSR_GetNTPReport(RPT_NTPReport *report)
{ {
NTP_Remote_Address rem_addr; int slot;
int slot, found;
rem_addr.ip_addr = report->remote_addr; if (!find_slot(&report->remote_addr, &slot))
rem_addr.port = 0;
find_slot(&rem_addr, &slot, &found);
if (!found)
return 0; return 0;
NCR_GetNTPReport(get_record(slot)->data, report); NCR_GetNTPReport(get_record(slot)->data, report);

View File

@@ -50,14 +50,15 @@ typedef enum {
} NSR_Status; } NSR_Status;
/* Procedure to add a new server or peer source. */ /* Procedure to add a new server or peer source. */
extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params); extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id);
/* Procedure to add a new server, peer source, or pool of servers specified by /* Procedure to add a new server, peer source, or pool of servers specified by
name instead of address. The name is resolved in exponentially increasing name instead of address. The name is resolved in exponentially increasing
intervals until it succeeds or fails with a non-temporary error. If the intervals until it succeeds or fails with a non-temporary error. If the
name is an address, it is equivalent to NSR_AddSource(). */ name is an address, it is equivalent to NSR_AddSource(). */
extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
SourceParameters *params); SourceParameters *params, uint32_t *conf_id);
/* Function type for handlers to be called back when an attempt /* Function type for handlers to be called back when an attempt
* (possibly unsuccessful) to resolve unresolved sources ends */ * (possibly unsuccessful) to resolve unresolved sources ends */
@@ -76,7 +77,10 @@ extern void NSR_StartSources(void);
extern void NSR_AutoStartSources(void); extern void NSR_AutoStartSources(void);
/* Procedure to remove a source */ /* Procedure to remove a source */
extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr); extern NSR_Status NSR_RemoveSource(IPAddr *address);
/* Procedure to remove all sources matching a configuration ID */
extern void NSR_RemoveSourcesById(uint32_t conf_id);
/* Procedure to remove all sources */ /* Procedure to remove all sources */
extern void NSR_RemoveAllSources(void); extern void NSR_RemoveAllSources(void);
@@ -136,6 +140,8 @@ extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAd
extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now); extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now);
extern int NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report);
extern int NSR_GetNTPReport(RPT_NTPReport *report); extern int NSR_GetNTPReport(RPT_NTPReport *report);
extern void NSR_GetActivityReport(RPT_ActivityReport *report); extern void NSR_GetActivityReport(RPT_ActivityReport *report);

View File

@@ -29,6 +29,8 @@
#include "siv.h" #include "siv.h"
#define NKE_PORT 4460
#define NKE_RECORD_CRITICAL_BIT (1U << 15) #define NKE_RECORD_CRITICAL_BIT (1U << 15)
#define NKE_RECORD_END_OF_MESSAGE 0 #define NKE_RECORD_END_OF_MESSAGE 0
#define NKE_RECORD_NEXT_PROTOCOL 1 #define NKE_RECORD_NEXT_PROTOCOL 1

View File

@@ -58,7 +58,8 @@ struct NKC_Instance_Record {
/* ================================================== */ /* ================================================== */
static void *client_credentials; static void *client_credentials = NULL;
static int client_credentials_refs = 0;
/* ================================================== */ /* ================================================== */
@@ -71,7 +72,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg
inst->resolving_name = 0; inst->resolving_name = 0;
if (inst->destroying) { if (inst->destroying) {
NKC_DestroyInstance(inst); Free(inst);
return; return;
} }
@@ -84,7 +85,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg
inst->ntp_address.ip_addr = ip_addrs[0]; inst->ntp_address.ip_addr = ip_addrs[0];
/* Prefer an address of the same family as NTS-KE */ /* Prefer an address in the same family as the NTS-KE server */
for (i = 0; i < n_addrs; i++) { for (i = 0; i < n_addrs; i++) {
DEBUG_LOG("%s resolved to %s", inst->server_name, UTI_IPToString(&ip_addrs[i])); DEBUG_LOG("%s resolved to %s", inst->server_name, UTI_IPToString(&ip_addrs[i]));
if (ip_addrs[i].family == inst->address.ip_addr.family) { if (ip_addrs[i].family == inst->address.ip_addr.family) {
@@ -168,13 +169,21 @@ process_response(NKC_Instance inst)
error = 1; error = 1;
break; break;
case NKE_RECORD_COOKIE: case NKE_RECORD_COOKIE:
DEBUG_LOG("Got cookie #%d length=%d", inst->num_cookies + 1, length); DEBUG_LOG("Got cookie length=%d", length);
assert(NKE_MAX_COOKIE_LENGTH == sizeof (inst->cookies[inst->num_cookies].cookie));
if (length <= NKE_MAX_COOKIE_LENGTH && inst->num_cookies < NKE_MAX_COOKIES) { if (length < 1 || length > NKE_MAX_COOKIE_LENGTH || length % 4 != 0 ||
inst->cookies[inst->num_cookies].length = length; inst->num_cookies >= NKE_MAX_COOKIES) {
memcpy(inst->cookies[inst->num_cookies].cookie, data, length); DEBUG_LOG("Unexpected length/cookie");
inst->num_cookies++; break;
} }
assert(NKE_MAX_COOKIE_LENGTH == sizeof (inst->cookies[inst->num_cookies].cookie));
assert(NKE_MAX_COOKIES == sizeof (inst->cookies) /
sizeof (inst->cookies[inst->num_cookies]));
inst->cookies[inst->num_cookies].length = length;
memcpy(inst->cookies[inst->num_cookies].cookie, data, length);
inst->num_cookies++;
break; break;
case NKE_RECORD_NTPV4_SERVER_NEGOTIATION: case NKE_RECORD_NTPV4_SERVER_NEGOTIATION:
if (length < 1 || length >= sizeof (inst->server_name)) { if (length < 1 || length >= sizeof (inst->server_name)) {
@@ -256,23 +265,6 @@ handle_message(void *arg)
/* ================================================== */ /* ================================================== */
void
NKC_Initialise(void)
{
client_credentials = NULL;
}
/* ================================================== */
void
NKC_Finalise(void)
{
if (client_credentials)
NKSN_DestroyCertCredentials(client_credentials);
}
/* ================================================== */
NKC_Instance NKC_Instance
NKC_CreateInstance(IPSockAddr *address, const char *name) NKC_CreateInstance(IPSockAddr *address, const char *name)
{ {
@@ -287,10 +279,10 @@ NKC_CreateInstance(IPSockAddr *address, const char *name)
inst->destroying = 0; inst->destroying = 0;
inst->got_response = 0; inst->got_response = 0;
/* Create the credentials with the first client instance and share them /* Share the credentials with other client instances */
with other instances */
if (!client_credentials) if (!client_credentials)
client_credentials = NKSN_CreateCertCredentials(NULL, NULL, CNF_GetNtsTrustedCertFile()); client_credentials = NKSN_CreateCertCredentials(NULL, NULL, CNF_GetNtsTrustedCertFile());
client_credentials_refs++;
return inst; return inst;
} }
@@ -300,15 +292,23 @@ NKC_CreateInstance(IPSockAddr *address, const char *name)
void void
NKC_DestroyInstance(NKC_Instance inst) NKC_DestroyInstance(NKC_Instance inst)
{ {
/* If the resolver is running, destroy the instance later when finished */ NKSN_DestroyInstance(inst->session);
Free(inst->name);
client_credentials_refs--;
if (client_credentials_refs <= 0 && client_credentials) {
NKSN_DestroyCertCredentials(client_credentials);
client_credentials = NULL;
}
/* If the asynchronous resolver is running, let the handler free
the instance later */
if (inst->resolving_name) { if (inst->resolving_name) {
inst->destroying = 1; inst->destroying = 1;
return; return;
} }
NKSN_DestroyInstance(inst->session);
Free(inst->name);
Free(inst); Free(inst);
} }
@@ -318,33 +318,35 @@ int
NKC_Start(NKC_Instance inst) NKC_Start(NKC_Instance inst)
{ {
IPSockAddr local_addr; IPSockAddr local_addr;
char label[512]; char label[512], *iface;
int sock_fd; int sock_fd;
assert(!NKC_IsActive(inst)); assert(!NKC_IsActive(inst));
inst->got_response = 0;
if (!client_credentials) { if (!client_credentials) {
DEBUG_LOG("Missing client credentials"); DEBUG_LOG("Missing client credentials");
return 0; return 0;
} }
/* Follow the bindacqaddress setting */ /* Follow the bindacqaddress and bindacqdevice settings */
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr); CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
if (local_addr.ip_addr.family != inst->address.ip_addr.family)
SCK_GetAnyLocalIPAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
local_addr.port = 0; local_addr.port = 0;
iface = CNF_GetBindAcquisitionInterface();
sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, 0);
if (sock_fd < 0)
return 0;
/* Make a label containing both the address and name of the server */ /* Make a label containing both the address and name of the server */
if (snprintf(label, sizeof (label), "%s (%s)", if (snprintf(label, sizeof (label), "%s (%s)",
UTI_IPSockAddrToString(&inst->address), inst->name) >= sizeof (label)) UTI_IPSockAddrToString(&inst->address), inst->name) >= sizeof (label))
; ;
/* Start a NTS-KE session */ sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, iface, 0);
if (sock_fd < 0) {
LOG(LOGS_ERR, "Could not connect to %s", label);
return 0;
}
/* Start an NTS-KE session */
if (!NKSN_StartSession(inst->session, sock_fd, label, client_credentials, CLIENT_TIMEOUT)) { if (!NKSN_StartSession(inst->session, sock_fd, label, client_credentials, CLIENT_TIMEOUT)) {
SCK_CloseSocket(sock_fd); SCK_CloseSocket(sock_fd);
return 0; return 0;
@@ -388,7 +390,7 @@ NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
*ntp_address = inst->ntp_address; *ntp_address = inst->ntp_address;
return i; return 1;
} }
/* ================================================== */ /* ================================================== */

View File

@@ -32,10 +32,6 @@
typedef struct NKC_Instance_Record *NKC_Instance; typedef struct NKC_Instance_Record *NKC_Instance;
/* Init and fini functions */
extern void NKC_Initialise(void);
extern void NKC_Finalise(void);
/* Create a client NTS-KE instance */ /* Create a client NTS-KE instance */
extern NKC_Instance NKC_CreateInstance(IPSockAddr *address, const char *name); extern NKC_Instance NKC_CreateInstance(IPSockAddr *address, const char *name);

View File

@@ -33,10 +33,12 @@
#include "array.h" #include "array.h"
#include "conf.h" #include "conf.h"
#include "clientlog.h" #include "clientlog.h"
#include "local.h"
#include "logging.h" #include "logging.h"
#include "memory.h" #include "memory.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "nts_ke_session.h" #include "nts_ke_session.h"
#include "privops.h"
#include "siv.h" #include "siv.h"
#include "socket.h" #include "socket.h"
#include "sched.h" #include "sched.h"
@@ -59,7 +61,7 @@
typedef struct { typedef struct {
uint32_t key_id; uint32_t key_id;
uint8_t nonce[SERVER_COOKIE_NONCE_LENGTH]; unsigned char nonce[SERVER_COOKIE_NONCE_LENGTH];
} ServerCookieHeader; } ServerCookieHeader;
typedef struct { typedef struct {
@@ -87,6 +89,7 @@ static int server_sock_fd4;
static int server_sock_fd6; static int server_sock_fd6;
static int helper_sock_fd; static int helper_sock_fd;
static int is_helper;
static int initialised = 0; static int initialised = 0;
@@ -106,13 +109,15 @@ handle_client(int sock_fd, IPSockAddr *addr)
NKSN_Instance inst, *instp; NKSN_Instance inst, *instp;
int i; int i;
/* Leave at least half of the descriptors which can handled by select()
to other use */
if (sock_fd > FD_SETSIZE / 2) { if (sock_fd > FD_SETSIZE / 2) {
DEBUG_LOG("Rejected connection from %s (%s)", DEBUG_LOG("Rejected connection from %s (%s)",
UTI_IPSockAddrToString(addr), "too many descriptors"); UTI_IPSockAddrToString(addr), "too many descriptors");
return 0; return 0;
} }
/* Find a slot which is free or has a stopped session */ /* Find an unused server slot or one with an already stopped session */
for (i = 0, inst = NULL; i < ARR_GetSize(sessions); i++) { for (i = 0, inst = NULL; i < ARR_GetSize(sessions); i++) {
instp = ARR_GetElement(sessions, i); instp = ARR_GetElement(sessions, i);
if (!*instp) { if (!*instp) {
@@ -132,6 +137,8 @@ handle_client(int sock_fd, IPSockAddr *addr)
return 0; return 0;
} }
assert(server_credentials);
if (!NKSN_StartSession(inst, sock_fd, UTI_IPSockAddrToString(addr), if (!NKSN_StartSession(inst, sock_fd, UTI_IPSockAddrToString(addr),
server_credentials, SERVER_TIMEOUT)) server_credentials, SERVER_TIMEOUT))
return 0; return 0;
@@ -149,6 +156,8 @@ handle_helper_request(int fd, int event, void *arg)
IPSockAddr client_addr; IPSockAddr client_addr;
int sock_fd; int sock_fd;
/* Receive the helper request with the NTS-KE session socket.
With multiple helpers EAGAIN errors are expected here. */
message = SCK_ReceiveMessage(fd, SCK_FLAG_MSG_DESCRIPTOR); message = SCK_ReceiveMessage(fd, SCK_FLAG_MSG_DESCRIPTOR);
if (!message) if (!message)
return; return;
@@ -160,16 +169,20 @@ handle_helper_request(int fd, int event, void *arg)
return; return;
} }
if (message->length != sizeof (HelperRequest)) { if (!initialised) {
DEBUG_LOG("Unexpected message length"); DEBUG_LOG("Uninitialised helper");
SCK_CloseSocket(sock_fd); SCK_CloseSocket(sock_fd);
return; return;
} }
if (message->length != sizeof (HelperRequest))
LOG_FATAL("Invalid helper request");
req = message->data; req = message->data;
/* Extract the server key and client address from the request */ /* Extract the current server key and client address from the request */
server_keys[current_server_key].id = ntohl(req->key_id); server_keys[current_server_key].id = ntohl(req->key_id);
assert(sizeof (server_keys[current_server_key].key) == sizeof (req->key));
memcpy(server_keys[current_server_key].key, req->key, memcpy(server_keys[current_server_key].key, req->key,
sizeof (server_keys[current_server_key].key)); sizeof (server_keys[current_server_key].key));
UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr); UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr);
@@ -177,7 +190,7 @@ handle_helper_request(int fd, int event, void *arg)
if (!SIV_SetKey(server_keys[current_server_key].siv, server_keys[current_server_key].key, if (!SIV_SetKey(server_keys[current_server_key].siv, server_keys[current_server_key].key,
SIV_GetKeyLength(SERVER_COOKIE_SIV))) SIV_GetKeyLength(SERVER_COOKIE_SIV)))
assert(0); LOG_FATAL("Could not set SIV key");
if (!handle_client(sock_fd, &client_addr)) { if (!handle_client(sock_fd, &client_addr)) {
SCK_CloseSocket(sock_fd); SCK_CloseSocket(sock_fd);
@@ -190,14 +203,14 @@ handle_helper_request(int fd, int event, void *arg)
/* ================================================== */ /* ================================================== */
static void static void
accept_connection(int server_fd, int event, void *arg) accept_connection(int listening_fd, int event, void *arg)
{ {
SCK_Message message; SCK_Message message;
IPSockAddr addr; IPSockAddr addr;
int log_index, sock_fd; int log_index, sock_fd;
struct timespec now; struct timespec now;
sock_fd = SCK_AcceptConnection(server_fd, &addr); sock_fd = SCK_AcceptConnection(listening_fd, &addr);
if (sock_fd < 0) if (sock_fd < 0)
return; return;
@@ -209,8 +222,9 @@ accept_connection(int server_fd, int event, void *arg)
} }
SCH_GetLastEventTime(&now, NULL, NULL); SCH_GetLastEventTime(&now, NULL, NULL);
log_index = CLG_LogNTPAccess(&addr.ip_addr, &now);
if (log_index >= 0 && CLG_LimitNTPResponseRate(log_index)) { log_index = CLG_LogServiceAccess(CLG_NTSKE, &addr.ip_addr, &now);
if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTSKE, log_index)) {
DEBUG_LOG("Rejected connection from %s (%s)", DEBUG_LOG("Rejected connection from %s (%s)",
UTI_IPSockAddrToString(&addr), "rate limit"); UTI_IPSockAddrToString(&addr), "rate limit");
SCK_CloseSocket(sock_fd); SCK_CloseSocket(sock_fd);
@@ -222,9 +236,11 @@ accept_connection(int server_fd, int event, void *arg)
if (helper_sock_fd != INVALID_SOCK_FD) { if (helper_sock_fd != INVALID_SOCK_FD) {
HelperRequest req; HelperRequest req;
/* Include the current server key and client address in the request */
memset(&req, 0, sizeof (req)); memset(&req, 0, sizeof (req));
/* Include the current server key and client address in the request */
req.key_id = htonl(server_keys[current_server_key].id); req.key_id = htonl(server_keys[current_server_key].id);
assert(sizeof (req.key) == sizeof (server_keys[current_server_key].key));
memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key)); memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key));
UTI_IPHostToNetwork(&addr.ip_addr, &req.client_addr); UTI_IPHostToNetwork(&addr.ip_addr, &req.client_addr);
req.client_port = htons(addr.port); req.client_port = htons(addr.port);
@@ -234,7 +250,12 @@ accept_connection(int server_fd, int event, void *arg)
message.length = sizeof (req); message.length = sizeof (req);
message.descriptor = sock_fd; message.descriptor = sock_fd;
errno = 0;
if (!SCK_SendMessage(helper_sock_fd, &message, SCK_FLAG_MSG_DESCRIPTOR)) { if (!SCK_SendMessage(helper_sock_fd, &message, SCK_FLAG_MSG_DESCRIPTOR)) {
/* If sending failed with EPIPE, it means all helpers closed their end of
the socket (e.g. due to a fatal error) */
if (errno == EPIPE)
LOG_FATAL("NTS-KE helpers failed");
SCK_CloseSocket(sock_fd); SCK_CloseSocket(sock_fd);
return; return;
} }
@@ -253,28 +274,30 @@ accept_connection(int server_fd, int event, void *arg)
/* ================================================== */ /* ================================================== */
static int static int
open_socket(int family, int port) open_socket(int family)
{ {
IPSockAddr local_addr; IPSockAddr local_addr;
int sock_fd; int backlog, sock_fd;
char *iface;
if (!SCK_IsFamilySupported(family)) if (!SCK_IsIpFamilyEnabled(family))
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
CNF_GetBindAddress(family, &local_addr.ip_addr); CNF_GetBindAddress(family, &local_addr.ip_addr);
local_addr.port = CNF_GetNtsServerPort();
iface = CNF_GetBindNtpInterface();
if (local_addr.ip_addr.family != family) sock_fd = SCK_OpenTcpSocket(NULL, &local_addr, iface, 0);
SCK_GetAnyLocalIPAddress(family, &local_addr.ip_addr);
local_addr.port = port;
sock_fd = SCK_OpenTcpSocket(NULL, &local_addr, 0);
if (sock_fd < 0) { if (sock_fd < 0) {
LOG(LOGS_ERR, "Could not open NTS-KE socket on %s", UTI_IPSockAddrToString(&local_addr)); LOG(LOGS_ERR, "Could not open NTS-KE socket on %s", UTI_IPSockAddrToString(&local_addr));
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
if (!SCK_ListenOnSocket(sock_fd, CNF_GetNtsServerConnections())) { /* Set the maximum number of waiting connections on the socket to the maximum
number of concurrent sessions */
backlog = MAX(CNF_GetNtsServerProcesses(), 1) * CNF_GetNtsServerConnections();
if (!SCK_ListenOnSocket(sock_fd, backlog)) {
SCK_CloseSocket(sock_fd); SCK_CloseSocket(sock_fd);
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
@@ -311,6 +334,15 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
datum = htons(error); datum = htons(error);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_ERROR, &datum, sizeof (datum))) if (!NKSN_AddRecord(session, 1, NKE_RECORD_ERROR, &datum, sizeof (datum)))
return 0; return 0;
} else if (next_protocol < 0) {
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, NULL, 0))
return 0;
} else if (aead_algorithm < 0) {
datum = htons(next_protocol);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
return 0;
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, NULL, 0))
return 0;
} else { } else {
datum = htons(next_protocol); datum = htons(next_protocol);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum))) if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
@@ -357,6 +389,8 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
static int static int
process_request(NKSN_Instance session) process_request(NKSN_Instance session)
{ {
int next_protocol_records = 0, aead_algorithm_records = 0;
int next_protocol_values = 0, aead_algorithm_values = 0;
int next_protocol = -1, aead_algorithm = -1, error = -1; int next_protocol = -1, aead_algorithm = -1, error = -1;
int i, critical, type, length; int i, critical, type, length;
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)]; uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
@@ -364,7 +398,7 @@ process_request(NKSN_Instance session)
assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0); assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0);
assert(sizeof (uint16_t) == 2); assert(sizeof (uint16_t) == 2);
while (error == -1) { while (error < 0) {
if (!NKSN_GetRecord(session, &critical, &type, &length, &data, sizeof (data))) if (!NKSN_GetRecord(session, &critical, &type, &length, &data, sizeof (data)))
break; break;
@@ -374,7 +408,11 @@ process_request(NKSN_Instance session)
error = NKE_ERROR_BAD_REQUEST; error = NKE_ERROR_BAD_REQUEST;
break; break;
} }
next_protocol_records++;
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) { for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
next_protocol_values++;
if (ntohs(data[i]) == NKE_NEXT_PROTOCOL_NTPV4) if (ntohs(data[i]) == NKE_NEXT_PROTOCOL_NTPV4)
next_protocol = NKE_NEXT_PROTOCOL_NTPV4; next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
} }
@@ -384,7 +422,11 @@ process_request(NKSN_Instance session)
error = NKE_ERROR_BAD_REQUEST; error = NKE_ERROR_BAD_REQUEST;
break; break;
} }
aead_algorithm_records++;
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) { for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
aead_algorithm_values++;
if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256) if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256)
aead_algorithm = AEAD_AES_SIV_CMAC_256; aead_algorithm = AEAD_AES_SIV_CMAC_256;
} }
@@ -400,8 +442,12 @@ process_request(NKSN_Instance session)
} }
} }
if (aead_algorithm < 0 || next_protocol < 0) if (error < 0) {
error = NKE_ERROR_BAD_REQUEST; if (next_protocol_records != 1 || next_protocol_values < 1 ||
(next_protocol == NKE_NEXT_PROTOCOL_NTPV4 &&
(aead_algorithm_records != 1 || aead_algorithm_values < 1)))
error = NKE_ERROR_BAD_REQUEST;
}
if (!prepare_response(session, error, next_protocol, aead_algorithm)) if (!prepare_response(session, error, next_protocol, aead_algorithm))
return 0; return 0;
@@ -426,18 +472,22 @@ generate_key(int index)
{ {
int key_length; int key_length;
assert(index < MAX_SERVER_KEYS); if (index < 0 || index >= MAX_SERVER_KEYS)
assert(0);
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV); key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
if (key_length > sizeof (server_keys[index].key)) if (key_length > sizeof (server_keys[index].key))
assert(0); assert(0);
UTI_GetRandomBytesUrandom(server_keys[index].key, key_length); UTI_GetRandomBytesUrandom(server_keys[index].key, key_length);
if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
assert(0); if (!server_keys[index].siv ||
!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
LOG_FATAL("Could not set SIV key");
UTI_GetRandomBytes(&server_keys[index].id, sizeof (server_keys[index].id)); UTI_GetRandomBytes(&server_keys[index].id, sizeof (server_keys[index].id));
/* Encode the index in the lowest bits of the ID */
server_keys[index].id &= -1U << KEY_ID_INDEX_BITS; server_keys[index].id &= -1U << KEY_ID_INDEX_BITS;
server_keys[index].id |= index; server_keys[index].id |= index;
@@ -486,6 +536,7 @@ save_keys(void)
fclose(f); fclose(f);
/* Rename the temporary file, or remove it if that fails */
if (!UTI_RenameTempFile(dump_dir, DUMP_FILENAME, ".tmp", NULL)) { if (!UTI_RenameTempFile(dump_dir, DUMP_FILENAME, ".tmp", NULL)) {
if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, ".tmp")) if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, ".tmp"))
; ;
@@ -509,6 +560,7 @@ static void
load_keys(void) load_keys(void)
{ {
char *dump_dir, line[1024], *words[MAX_WORDS]; char *dump_dir, line[1024], *words[MAX_WORDS];
unsigned char key[SIV_MAX_KEY_LENGTH];
int i, index, key_length, algorithm; int i, index, key_length, algorithm;
double key_age; double key_age;
FILE *f; FILE *f;
@@ -536,15 +588,17 @@ load_keys(void)
sscanf(words[0], "%"PRIX32, &id) != 1) sscanf(words[0], "%"PRIX32, &id) != 1)
goto error; goto error;
index = id % MAX_SERVER_KEYS; if (UTI_HexToBytes(words[1], key, sizeof (key)) != key_length)
if (UTI_HexToBytes(words[1], server_keys[index].key,
sizeof (server_keys[index].key)) != key_length)
goto error; goto error;
index = id % MAX_SERVER_KEYS;
server_keys[index].id = id; server_keys[index].id = id;
assert(sizeof (server_keys[index].key) == sizeof (key));
memcpy(server_keys[index].key, key, key_length);
if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length)) if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
assert(0); LOG_FATAL("Could not set SIV key");
DEBUG_LOG("Loaded key %"PRIX32, id); DEBUG_LOG("Loaded key %"PRIX32, id);
@@ -575,36 +629,43 @@ key_timeout(void *arg)
/* ================================================== */ /* ================================================== */
static void static void
start_helper(int id, int scfilter_level, int main_fd, int helper_fd) run_helper(uid_t uid, gid_t gid, int scfilter_level)
{ {
pid_t pid; LOG_Severity log_severity;
pid = fork(); /* Finish minimal initialisation and run using the scheduler loop
similarly to the main process */
if (pid < 0) DEBUG_LOG("Helper started");
LOG_FATAL("fork() failed : %s", strerror(errno));
if (pid > 0) /* Suppress a log message about disabled clock control */
return; log_severity = LOG_GetMinSeverity();
LOG_SetMinSeverity(LOGS_ERR);
SCK_CloseSocket(main_fd); SYS_Initialise(0);
LOG_SetMinSeverity(log_severity);
if (!geteuid() && (uid || gid))
SYS_DropRoot(uid, gid);
NKS_Initialise();
LOG_CloseParentFd();
SCH_Reset();
SCH_AddFileHandler(helper_fd, SCH_FILE_INPUT, handle_helper_request, NULL);
UTI_SetQuitSignalsHandler(helper_signal, 1); UTI_SetQuitSignalsHandler(helper_signal, 1);
if (scfilter_level != 0) if (scfilter_level != 0)
SYS_EnableSystemCallFilter(scfilter_level, SYS_NTSKE_HELPER); SYS_EnableSystemCallFilter(scfilter_level, SYS_NTSKE_HELPER);
initialised = 1;
DEBUG_LOG("NTS-KE helper #%d started", id);
SCH_MainLoop(); SCH_MainLoop();
NKS_Finalise(); DEBUG_LOG("Helper exiting");
DEBUG_LOG("NTS-KE helper #%d exiting", id); NKS_Finalise();
SCK_Finalise();
SYS_Finalise();
SCH_Finalise();
LCL_Finalise();
PRV_Finalise();
CNF_Finalise();
LOG_Finalise();
exit(0); exit(0);
} }
@@ -612,15 +673,65 @@ start_helper(int id, int scfilter_level, int main_fd, int helper_fd)
/* ================================================== */ /* ================================================== */
void void
NKS_Initialise(int scfilter_level) NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level)
{
int i, processes, sock_fd1, sock_fd2;
char prefix[16];
pid_t pid;
helper_sock_fd = INVALID_SOCK_FD;
is_helper = 0;
if (!CNF_GetNtsServerCertFile() || !CNF_GetNtsServerKeyFile())
return;
processes = CNF_GetNtsServerProcesses();
if (processes <= 0)
return;
/* Start helper processes to perform (computationally expensive) NTS-KE
sessions with clients on sockets forwarded from the main process */
sock_fd1 = SCK_OpenUnixSocketPair(0, &sock_fd2);
if (sock_fd1 < 0)
LOG_FATAL("Could not open socket pair");
for (i = 0; i < processes; i++) {
pid = fork();
if (pid < 0)
LOG_FATAL("fork() failed : %s", strerror(errno));
if (pid > 0)
continue;
is_helper = 1;
snprintf(prefix, sizeof (prefix), "nks#%d:", i + 1);
LOG_SetDebugPrefix(prefix);
LOG_CloseParentFd();
SCK_CloseSocket(sock_fd1);
SCH_AddFileHandler(sock_fd2, SCH_FILE_INPUT, handle_helper_request, NULL);
run_helper(uid, gid, scfilter_level);
}
SCK_CloseSocket(sock_fd2);
helper_sock_fd = sock_fd1;
}
/* ================================================== */
void
NKS_Initialise(void)
{ {
char *cert, *key; char *cert, *key;
int i, processes;
double key_delay; double key_delay;
int i;
server_sock_fd4 = INVALID_SOCK_FD; server_sock_fd4 = INVALID_SOCK_FD;
server_sock_fd6 = INVALID_SOCK_FD; server_sock_fd6 = INVALID_SOCK_FD;
helper_sock_fd = INVALID_SOCK_FD;
cert = CNF_GetNtsServerCertFile(); cert = CNF_GetNtsServerCertFile();
key = CNF_GetNtsServerKeyFile(); key = CNF_GetNtsServerKeyFile();
@@ -628,19 +739,20 @@ NKS_Initialise(int scfilter_level)
if (!cert || !key) if (!cert || !key)
return; return;
server_credentials = NKSN_CreateCertCredentials(cert, key, NULL); if (helper_sock_fd == INVALID_SOCK_FD) {
if (!server_credentials) server_credentials = NKSN_CreateCertCredentials(cert, key, NULL);
return; if (!server_credentials)
return;
} else {
server_credentials = NULL;
}
sessions = ARR_CreateInstance(sizeof (NKSN_Instance)); sessions = ARR_CreateInstance(sizeof (NKSN_Instance));
for (i = 0; i < CNF_GetNtsServerConnections(); i++) for (i = 0; i < CNF_GetNtsServerConnections(); i++)
*(NKSN_Instance *)ARR_GetNewElement(sessions) = NULL; *(NKSN_Instance *)ARR_GetNewElement(sessions) = NULL;
for (i = 0; i < MAX_SERVER_KEYS; i++)
server_keys[i].siv = NULL;
server_sock_fd4 = open_socket(IPADDR_INET4, CNF_GetNtsServerPort());
server_sock_fd6 = open_socket(IPADDR_INET6, CNF_GetNtsServerPort());
/* Generate random keys, even if they will be replaced by reloaded keys,
or unused (in the helper) */
for (i = 0; i < MAX_SERVER_KEYS; i++) { for (i = 0; i < MAX_SERVER_KEYS; i++) {
server_keys[i].siv = SIV_CreateInstance(SERVER_COOKIE_SIV); server_keys[i].siv = SIV_CreateInstance(SERVER_COOKIE_SIV);
generate_key(i); generate_key(i);
@@ -648,29 +760,18 @@ NKS_Initialise(int scfilter_level)
current_server_key = MAX_SERVER_KEYS - 1; current_server_key = MAX_SERVER_KEYS - 1;
load_keys(); if (!is_helper) {
server_sock_fd4 = open_socket(IPADDR_INET4);
server_sock_fd6 = open_socket(IPADDR_INET6);
key_rotation_interval = MAX(CNF_GetNtsRotate(), 0); load_keys();
if (key_rotation_interval > 0) { key_rotation_interval = MAX(CNF_GetNtsRotate(), 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(); if (key_rotation_interval > 0) {
key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts);
if (processes > 0) { SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL);
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);
SCK_CloseSocket(sock_fd2);
helper_sock_fd = sock_fd1;
} }
initialised = 1; initialised = 1;
@@ -687,6 +788,7 @@ NKS_Finalise(void)
return; return;
if (helper_sock_fd != INVALID_SOCK_FD) { if (helper_sock_fd != INVALID_SOCK_FD) {
/* Send the helpers a request to exit */
for (i = 0; i < CNF_GetNtsServerProcesses(); i++) { for (i = 0; i < CNF_GetNtsServerProcesses(); i++) {
if (!SCK_Send(helper_sock_fd, "", 1, 0)) if (!SCK_Send(helper_sock_fd, "", 1, 0))
; ;
@@ -698,11 +800,11 @@ NKS_Finalise(void)
if (server_sock_fd6 != INVALID_SOCK_FD) if (server_sock_fd6 != INVALID_SOCK_FD)
SCK_CloseSocket(server_sock_fd6); SCK_CloseSocket(server_sock_fd6);
save_keys(); if (!is_helper)
for (i = 0; i < MAX_SERVER_KEYS; i++) { save_keys();
if (server_keys[i].siv != NULL)
SIV_DestroyInstance(server_keys[i].siv); for (i = 0; i < MAX_SERVER_KEYS; i++)
} SIV_DestroyInstance(server_keys[i].siv);
for (i = 0; i < ARR_GetSize(sessions); i++) { for (i = 0; i < ARR_GetSize(sessions); i++) {
NKSN_Instance session = *(NKSN_Instance *)ARR_GetElement(sessions, i); NKSN_Instance session = *(NKSN_Instance *)ARR_GetElement(sessions, i);
@@ -711,7 +813,8 @@ NKS_Finalise(void)
} }
ARR_DestroyInstance(sessions); ARR_DestroyInstance(sessions);
NKSN_DestroyCertCredentials(server_credentials); if (server_credentials)
NKSN_DestroyCertCredentials(server_credentials);
} }
/* ================================================== */ /* ================================================== */
@@ -808,7 +911,7 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
return 0; return 0;
} }
if (cookie->length <= sizeof (*header)) { if (cookie->length <= (int)sizeof (*header)) {
DEBUG_LOG("Invalid cookie length"); DEBUG_LOG("Invalid cookie length");
return 0; return 0;
} }

View File

@@ -30,7 +30,8 @@
#include "nts_ke.h" #include "nts_ke.h"
/* Init and fini functions */ /* Init and fini functions */
extern void NKS_Initialise(int scfilter_level); extern void NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level);
extern void NKS_Initialise(void);
extern void NKS_Finalise(void); extern void NKS_Finalise(void);
/* Save the current server keys */ /* Save the current server keys */

View File

@@ -81,7 +81,6 @@ struct NKSN_Instance_Record {
struct Message message; struct Message message;
int new_message; int new_message;
int ended_message;
}; };
/* ================================================== */ /* ================================================== */
@@ -110,6 +109,8 @@ add_record(struct Message *message, int critical, int type, const void *body, in
{ {
struct RecordHeader header; struct RecordHeader header;
assert(message->length <= sizeof (message->data));
if (body_length < 0 || body_length > 0xffff || type < 0 || type > 0x7fff || if (body_length < 0 || body_length > 0xffff || type < 0 || type > 0x7fff ||
message->length + sizeof (header) + body_length > sizeof (message->data)) message->length + sizeof (header) + body_length > sizeof (message->data))
return 0; return 0;
@@ -153,6 +154,7 @@ get_record(struct Message *message, int *critical, int *type, int *body_length,
blen = ntohs(header.body_length); blen = ntohs(header.body_length);
rlen = sizeof (header) + blen; rlen = sizeof (header) + blen;
assert(blen >= 0 && rlen > 0);
if (message->length < message->parsed + rlen) if (message->length < message->parsed + rlen)
return 0; return 0;
@@ -215,7 +217,8 @@ create_tls_session(int server_mode, int sock_fd, const char *server_name,
unsigned int flags; unsigned int flags;
int r; int r;
r = gnutls_init(&session, GNUTLS_NONBLOCK | (server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT)); r = gnutls_init(&session, GNUTLS_NONBLOCK | GNUTLS_NO_TICKETS |
(server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
if (r < 0) { if (r < 0) {
LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r)); LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r));
return NULL; return NULL;
@@ -302,33 +305,25 @@ session_timeout(void *arg)
/* ================================================== */ /* ================================================== */
static int static int
get_socket_error(int sock_fd) check_alpn(NKSN_Instance inst)
{ {
int optval; gnutls_datum_t alpn;
socklen_t optlen = sizeof (optval);
if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) { if (gnutls_alpn_get_selected_protocol(inst->tls_session, &alpn) < 0 ||
DEBUG_LOG("getsockopt() failed : %s", strerror(errno)); alpn.size != sizeof (NKE_ALPN_NAME) - 1 ||
return EINVAL; memcmp(alpn.data, NKE_ALPN_NAME, sizeof (NKE_ALPN_NAME) - 1) != 0)
} return 0;
return optval; return 1;
} }
/* ================================================== */ /* ================================================== */
static int static void
check_alpn(NKSN_Instance inst) set_input_output(NKSN_Instance inst, int output)
{ {
gnutls_datum_t alpn; SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_INPUT, !output);
int r; SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, output);
r = gnutls_alpn_get_selected_protocol(inst->tls_session, &alpn);
if (r < 0 || alpn.size != sizeof (NKE_ALPN_NAME) - 1 ||
strncmp((const char *)alpn.data, NKE_ALPN_NAME, sizeof (NKE_ALPN_NAME) - 1))
return 0;
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -354,7 +349,7 @@ change_state(NKSN_Instance inst, KeState state)
assert(0); assert(0);
} }
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, output); set_input_output(inst, output);
inst->state = state; inst->state = state;
} }
@@ -375,9 +370,11 @@ handle_event(NKSN_Instance inst, int event)
if (event != SCH_FILE_OUTPUT) if (event != SCH_FILE_OUTPUT)
return 0; return 0;
r = get_socket_error(inst->sock_fd); /* Get the socket error */
if (!SCK_GetIntOption(inst->sock_fd, SOL_SOCKET, SO_ERROR, &r))
r = EINVAL;
if (r) { if (r != 0) {
LOG(LOGS_ERR, "Could not connect to %s : %s", inst->label, strerror(r)); LOG(LOGS_ERR, "Could not connect to %s : %s", inst->label, strerror(r));
stop_session(inst); stop_session(inst);
return 0; return 0;
@@ -393,8 +390,22 @@ handle_event(NKSN_Instance inst, int event)
if (r < 0) { if (r < 0) {
if (gnutls_error_is_fatal(r)) { if (gnutls_error_is_fatal(r)) {
gnutls_datum_t cert_error;
/* Get a description of verification errors */
if (r != GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR ||
gnutls_certificate_verification_status_print(
gnutls_session_get_verify_cert_status(inst->tls_session),
gnutls_certificate_type_get(inst->tls_session), &cert_error, 0) < 0)
cert_error.data = NULL;
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"TLS handshake with %s failed : %s", inst->label, gnutls_strerror(r)); "TLS handshake with %s failed : %s%s%s", inst->label, gnutls_strerror(r),
cert_error.data ? " " : "", cert_error.data ? (const char *)cert_error.data : "");
if (cert_error.data)
gnutls_free(cert_error.data);
stop_session(inst); stop_session(inst);
/* Increase the retry interval if the handshake did not fail due /* Increase the retry interval if the handshake did not fail due
@@ -406,8 +417,7 @@ handle_event(NKSN_Instance inst, int event)
} }
/* Disable output when the handshake is trying to receive data */ /* Disable output when the handshake is trying to receive data */
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
gnutls_record_get_direction(inst->tls_session));
return 0; return 0;
} }
@@ -432,6 +442,7 @@ handle_event(NKSN_Instance inst, int event)
case KE_SEND: case KE_SEND:
assert(inst->new_message && message->complete); assert(inst->new_message && message->complete);
assert(message->length <= sizeof (message->data) && message->length > message->sent);
r = gnutls_record_send(inst->tls_session, &message->data[message->sent], r = gnutls_record_send(inst->tls_session, &message->data[message->sent],
message->length - message->sent); message->length - message->sent);
@@ -499,7 +510,9 @@ handle_event(NKSN_Instance inst, int event)
/* Server will send a response to the client */ /* Server will send a response to the client */
change_state(inst, inst->server ? KE_SEND : KE_SHUTDOWN); change_state(inst, inst->server ? KE_SEND : KE_SHUTDOWN);
break;
/* Return success to process the received message */
return 1;
case KE_SHUTDOWN: case KE_SHUTDOWN:
r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR); r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR);
@@ -512,8 +525,7 @@ handle_event(NKSN_Instance inst, int event)
} }
/* Disable output when the TLS shutdown is trying to receive data */ /* Disable output when the TLS shutdown is trying to receive data */
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
gnutls_record_get_direction(inst->tls_session));
return 0; return 0;
} }
@@ -525,9 +537,8 @@ handle_event(NKSN_Instance inst, int event)
default: default:
assert(0); assert(0);
return 0;
} }
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -540,6 +551,9 @@ read_write_socket(int fd, int event, void *arg)
if (!handle_event(inst, event)) if (!handle_event(inst, event))
return; return;
/* A valid message was received. Call the handler to process the message,
and prepare a response if it is a server. */
reset_message_parsing(&inst->message); reset_message_parsing(&inst->message);
if (!(inst->handler)(inst->handler_arg)) { if (!(inst->handler)(inst->handler_arg)) {
@@ -588,16 +602,19 @@ init_gnutls(void)
if (r < 0) if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r)); LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
/* NTS specification requires TLS1.3 or later */ /* Prepare a priority cache for server and client NTS-KE sessions
(the NTS specification requires TLS1.3 or later) */
r = gnutls_priority_init2(&priority_cache, r = gnutls_priority_init2(&priority_cache,
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2", "-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND); NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
if (r < 0) if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r)); LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r));
/* Use our clock instead of the system clock in certificate verification */
gnutls_global_set_time_function(get_time); gnutls_global_set_time_function(get_time);
gnutls_initialised = 1; gnutls_initialised = 1;
DEBUG_LOG("Initialised");
LCL_AddParameterChangeHandler(handle_step, NULL); LCL_AddParameterChangeHandler(handle_step, NULL);
} }
@@ -607,13 +624,15 @@ init_gnutls(void)
static void static void
deinit_gnutls(void) deinit_gnutls(void)
{ {
assert(gnutls_initialised); if (!gnutls_initialised || credentials_counter > 0)
return;
LCL_RemoveParameterChangeHandler(handle_step, NULL); LCL_RemoveParameterChangeHandler(handle_step, NULL);
gnutls_priority_deinit(priority_cache); gnutls_priority_deinit(priority_cache);
gnutls_global_deinit(); gnutls_global_deinit();
gnutls_initialised = 0; gnutls_initialised = 0;
DEBUG_LOG("Deinitialised");
} }
/* ================================================== */ /* ================================================== */
@@ -658,6 +677,7 @@ error:
LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r)); LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
if (credentials) if (credentials)
gnutls_certificate_free_credentials(credentials); gnutls_certificate_free_credentials(credentials);
deinit_gnutls();
return NULL; return NULL;
} }
@@ -668,9 +688,6 @@ NKSN_DestroyCertCredentials(void *credentials)
{ {
gnutls_certificate_free_credentials(credentials); gnutls_certificate_free_credentials(credentials);
credentials_counter--; credentials_counter--;
if (credentials_counter != 0)
return;
deinit_gnutls(); deinit_gnutls();
} }
@@ -688,7 +705,7 @@ NKSN_CreateInstance(int server_mode, const char *server_name,
inst->server_name = server_name ? Strdup(server_name) : NULL; inst->server_name = server_name ? Strdup(server_name) : NULL;
inst->handler = handler; inst->handler = handler;
inst->handler_arg = handler_arg; inst->handler_arg = handler_arg;
/* Replace NULL arg with the session itself */ /* Replace a NULL argument with the session itself */
if (!inst->handler_arg) if (!inst->handler_arg)
inst->handler_arg = inst; inst->handler_arg = inst;
@@ -735,7 +752,6 @@ NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
reset_message(&inst->message); reset_message(&inst->message);
inst->new_message = 0; inst->new_message = 0;
inst->ended_message = 0;
change_state(inst, inst->server ? KE_HANDSHAKE : KE_WAIT_CONNECT); change_state(inst, inst->server ? KE_HANDSHAKE : KE_WAIT_CONNECT);
@@ -769,6 +785,7 @@ NKSN_EndMessage(NKSN_Instance inst)
{ {
assert(!inst->message.complete); assert(!inst->message.complete);
/* Terminate the message */
if (!add_record(&inst->message, 1, NKE_RECORD_END_OF_MESSAGE, NULL, 0)) if (!add_record(&inst->message, 1, NKE_RECORD_END_OF_MESSAGE, NULL, 0))
return 0; return 0;
@@ -787,9 +804,13 @@ NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
assert(inst->message.complete); assert(inst->message.complete);
if (body_length)
*body_length = 0;
if (!get_record(&inst->message, critical, &type2, body_length, body, buffer_length)) if (!get_record(&inst->message, critical, &type2, body_length, body, buffer_length))
return 0; return 0;
/* Hide the end-of-message record */
if (type2 == NKE_RECORD_END_OF_MESSAGE) if (type2 == NKE_RECORD_END_OF_MESSAGE)
return 0; return 0;

View File

@@ -32,7 +32,7 @@
typedef struct NKSN_Instance_Record *NKSN_Instance; typedef struct NKSN_Instance_Record *NKSN_Instance;
/* Handler for received NTS-KE messages. A non-zero return code stops /* Handler for received NTS-KE messages. A zero return code stops
the session. */ the session. */
typedef int (*NKSN_MessageHandler)(void *arg); typedef int (*NKSN_MessageHandler)(void *arg);

View File

@@ -102,6 +102,10 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
body = (unsigned char *)(header + 1); body = (unsigned char *)(header + 1);
ciphertext = body + nonce_length + nonce_padding; ciphertext = body + nonce_length + nonce_padding;
if ((unsigned char *)header + auth_length !=
ciphertext + ciphertext_length + ciphertext_padding + additional_padding)
assert(0);
memcpy(body, nonce, nonce_length); memcpy(body, nonce, nonce_length);
memset(body + nonce_length, 0, nonce_padding); memset(body + nonce_length, 0, nonce_padding);
@@ -128,11 +132,14 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
void *ef_body; void *ef_body;
struct AuthHeader *header; struct AuthHeader *header;
if (buffer_length < 0)
return 0;
if (!NEF_ParseField(packet, info->length, ef_start, if (!NEF_ParseField(packet, info->length, ef_start,
NULL, &ef_type, &ef_body, &ef_body_length)) NULL, &ef_type, &ef_body, &ef_body_length))
return 0; return 0;
if (ef_type != NTP_EF_NTS_AUTH_AND_EEF) if (ef_type != NTP_EF_NTS_AUTH_AND_EEF || ef_body_length < sizeof (*header))
return 0; return 0;
header = ef_body; header = ef_body;
@@ -145,7 +152,7 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
return 0; return 0;
nonce = (unsigned char *)(header + 1); nonce = (unsigned char *)(header + 1);
ciphertext = (unsigned char *)(header + 1) + get_padded_length(nonce_length); ciphertext = nonce + get_padded_length(nonce_length);
siv_tag_length = SIV_GetTagLength(siv); siv_tag_length = SIV_GetTagLength(siv);
@@ -163,8 +170,9 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
} }
*plaintext_length = ciphertext_length - siv_tag_length; *plaintext_length = ciphertext_length - siv_tag_length;
assert(*plaintext_length >= 0);
if (!SIV_Decrypt(siv, nonce, nonce_length, packet, info->length - ef_body_length - 4, if (!SIV_Decrypt(siv, nonce, nonce_length, packet, ef_start,
ciphertext, ciphertext_length, plaintext, *plaintext_length)) { ciphertext, ciphertext_length, plaintext, *plaintext_length)) {
DEBUG_LOG("SIV decrypt failed"); DEBUG_LOG("SIV decrypt failed");
return 0; return 0;

View File

@@ -43,14 +43,17 @@
#include "siv.h" #include "siv.h"
#include "util.h" #include "util.h"
/* Maximum length of all cookies to avoid IP fragmentation */
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108) #define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
/* Magic string of files containing keys and cookies */
#define DUMP_IDENTIFIER "NNC0\n" #define DUMP_IDENTIFIER "NNC0\n"
struct NNC_Instance_Record { struct NNC_Instance_Record {
const IPSockAddr *ntp_address; const IPSockAddr *ntp_address;
IPSockAddr nts_address; IPSockAddr nts_address;
char *name; char *name;
NKC_Instance nke; NKC_Instance nke;
SIV_Instance siv; SIV_Instance siv;
@@ -60,9 +63,11 @@ struct NNC_Instance_Record {
double last_nke_success; double last_nke_success;
NKE_Context context; NKE_Context context;
unsigned int context_id;
NKE_Cookie cookies[NTS_MAX_COOKIES]; NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies; int num_cookies;
int cookie_index; int cookie_index;
int auth_ready;
int nak_response; int nak_response;
int ok_response; int ok_response;
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH]; unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
@@ -79,14 +84,24 @@ static void load_cookies(NNC_Instance inst);
static void static void
reset_instance(NNC_Instance inst) reset_instance(NNC_Instance inst)
{ {
if (inst->nke)
NKC_DestroyInstance(inst->nke);
inst->nke = NULL;
if (inst->siv)
SIV_DestroyInstance(inst->siv);
inst->siv = NULL;
inst->load_attempt = 0; inst->load_attempt = 0;
inst->nke_attempts = 0; inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0; inst->next_nke_attempt = 0.0;
inst->last_nke_success = 0.0; inst->last_nke_success = 0.0;
memset(&inst->context, 0, sizeof (inst->context)); memset(&inst->context, 0, sizeof (inst->context));
inst->context_id = 0;
memset(inst->cookies, 0, sizeof (inst->cookies));
inst->num_cookies = 0; inst->num_cookies = 0;
inst->cookie_index = 0; inst->cookie_index = 0;
inst->auth_ready = 0;
inst->nak_response = 0; inst->nak_response = 0;
inst->ok_response = 1; inst->ok_response = 1;
memset(inst->nonce, 0, sizeof (inst->nonce)); memset(inst->nonce, 0, sizeof (inst->nonce));
@@ -120,10 +135,7 @@ NNC_DestroyInstance(NNC_Instance inst)
{ {
save_cookies(inst); save_cookies(inst);
if (inst->nke) reset_instance(inst);
NKC_DestroyInstance(inst->nke);
if (inst->siv)
SIV_DestroyInstance(inst->siv);
Free(inst->name); Free(inst->name);
Free(inst); Free(inst);
@@ -134,17 +146,15 @@ NNC_DestroyInstance(NNC_Instance inst)
static int static int
check_cookies(NNC_Instance inst) check_cookies(NNC_Instance inst)
{ {
/* Force NKE if a NAK was received since last valid auth */ /* Force a new NTS-KE session if a NAK was received without a valid response,
if (inst->nak_response && !inst->ok_response && inst->num_cookies > 0) { or the keys encrypting the cookies need to be refreshed */
if (inst->num_cookies > 0 &&
((inst->nak_response && !inst->ok_response) ||
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
inst->num_cookies = 0; inst->num_cookies = 0;
DEBUG_LOG("Dropped cookies"); DEBUG_LOG("Dropped cookies");
} }
/* Force NKE if the keys encrypting the cookies are too old */
if (inst->num_cookies > 0 &&
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())
inst->num_cookies = 0;
return inst->num_cookies > 0; return inst->num_cookies > 0;
} }
@@ -201,10 +211,11 @@ get_cookies(NNC_Instance inst)
double now; double now;
int got_data; int got_data;
assert(!check_cookies(inst)); assert(inst->num_cookies == 0);
now = SCH_GetLastEventMonoTime(); now = SCH_GetLastEventMonoTime();
/* Create and start a new NTS-KE session if not already present */
if (!inst->nke) { if (!inst->nke) {
if (now < inst->next_nke_attempt) { if (now < inst->next_nke_attempt) {
DEBUG_LOG("Limiting NTS-KE request rate (%f seconds)", DEBUG_LOG("Limiting NTS-KE request rate (%f seconds)",
@@ -229,9 +240,13 @@ get_cookies(NNC_Instance inst)
update_next_nke_attempt(inst, now); update_next_nke_attempt(inst, now);
/* Wait until the session stops */
if (NKC_IsActive(inst->nke)) if (NKC_IsActive(inst->nke))
return 0; return 0;
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
/* Get the new keys, cookies and NTP address if the session was successful */
got_data = NKC_GetNtsData(inst->nke, &inst->context, got_data = NKC_GetNtsData(inst->nke, &inst->context,
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES, inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
&ntp_address); &ntp_address);
@@ -246,17 +261,20 @@ get_cookies(NNC_Instance inst)
SIV_DestroyInstance(inst->siv); SIV_DestroyInstance(inst->siv);
inst->siv = NULL; inst->siv = NULL;
inst->context_id++;
/* Force a new session if the NTP address is used by another source, with
an expectation that it will eventually get a non-conflicting address */
if (!set_ntp_address(inst, &ntp_address)) { if (!set_ntp_address(inst, &ntp_address)) {
inst->num_cookies = 0; inst->num_cookies = 0;
return 0; return 0;
} }
inst->last_nke_success = now;
inst->cookie_index = 0; inst->cookie_index = 0;
inst->nak_response = 0; inst->nak_response = 0;
inst->last_nke_success = now;
return 1; return 1;
} }
@@ -265,11 +283,15 @@ get_cookies(NNC_Instance inst)
int int
NNC_PrepareForAuth(NNC_Instance inst) NNC_PrepareForAuth(NNC_Instance inst)
{ {
inst->auth_ready = 0;
/* Try to reload saved keys and cookies (once for the NTS-KE address) */
if (!inst->load_attempt) { if (!inst->load_attempt) {
load_cookies(inst); load_cookies(inst);
inst->load_attempt = 1; inst->load_attempt = 1;
} }
/* Get new cookies if there are not any, or they are no longer usable */
if (!check_cookies(inst)) { if (!check_cookies(inst)) {
if (!get_cookies(inst)) if (!get_cookies(inst))
return 0; return 0;
@@ -284,8 +306,11 @@ NNC_PrepareForAuth(NNC_Instance inst)
return 0; return 0;
} }
UTI_GetRandomBytes(&inst->uniq_id, sizeof (inst->uniq_id)); /* Prepare data for NNC_GenerateRequestAuth() */
UTI_GetRandomBytes(&inst->nonce, sizeof (inst->nonce)); UTI_GetRandomBytes(inst->uniq_id, sizeof (inst->uniq_id));
UTI_GetRandomBytes(inst->nonce, sizeof (inst->nonce));
inst->auth_ready = 1;
return 1; return 1;
} }
@@ -300,7 +325,7 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
int i, req_cookies; int i, req_cookies;
void *ef_body; void *ef_body;
if (inst->num_cookies == 0 || !inst->siv) if (!inst->auth_ready || inst->num_cookies == 0 || !inst->siv)
return 0; return 0;
if (info->mode != MODE_CLIENT) if (info->mode != MODE_CLIENT)
@@ -311,7 +336,7 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
MAX_TOTAL_COOKIE_LENGTH / (cookie->length + 4)); MAX_TOTAL_COOKIE_LENGTH / (cookie->length + 4));
if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER, if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER,
&inst->uniq_id, sizeof (inst->uniq_id))) inst->uniq_id, sizeof (inst->uniq_id)))
return 0; return 0;
if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE, if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE,
@@ -331,6 +356,8 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
inst->num_cookies--; inst->num_cookies--;
inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES; inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES;
inst->auth_ready = 0;
inst->nak_response = 0;
inst->ok_response = 0; inst->ok_response = 0;
return 1; return 1;
@@ -338,6 +365,23 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
/* ================================================== */ /* ================================================== */
static int
parse_encrypted_efs(NNC_Instance inst, unsigned char *plaintext, int length)
{
int ef_length, parsed;
for (parsed = 0; parsed < length; parsed += ef_length) {
if (!NEF_ParseSingleField(plaintext, length, parsed, &ef_length, NULL, NULL, NULL)) {
DEBUG_LOG("Could not parse encrypted EF");
return 0;
}
}
return 1;
}
/* ================================================== */
static int static int
extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length) extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
{ {
@@ -349,7 +393,7 @@ extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
for (parsed = 0; parsed < length; parsed += ef_length) { for (parsed = 0; parsed < length; parsed += ef_length) {
if (!NEF_ParseSingleField(plaintext, length, parsed, if (!NEF_ParseSingleField(plaintext, length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length)) &ef_length, &ef_type, &ef_body, &ef_body_length))
break; return 0;
if (ef_type != NTP_EF_NTS_COOKIE) if (ef_type != NTP_EF_NTS_COOKIE)
continue; continue;
@@ -365,6 +409,9 @@ extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
continue; continue;
index = (inst->cookie_index + inst->num_cookies) % NTS_MAX_COOKIES; index = (inst->cookie_index + inst->num_cookies) % NTS_MAX_COOKIES;
assert(index >= 0 && index < NTS_MAX_COOKIES);
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
memcpy(inst->cookies[index].cookie, ef_body, ef_body_length); memcpy(inst->cookies[index].cookie, ef_body, ef_body_length);
inst->cookies[index].length = ef_body_length; inst->cookies[index].length = ef_body_length;
inst->num_cookies++; inst->num_cookies++;
@@ -391,8 +438,8 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
if (info->ext_fields == 0 || info->mode != MODE_SERVER) if (info->ext_fields == 0 || info->mode != MODE_SERVER)
return 0; return 0;
/* Accept only one response per request */ /* Accept at most one response per request */
if (inst->ok_response) if (inst->ok_response || inst->auth_ready)
return 0; return 0;
if (!inst->siv || if (!inst->siv ||
@@ -404,7 +451,8 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) { for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed, if (!NEF_ParseField(packet, info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length)) &ef_length, &ef_type, &ef_body, &ef_body_length))
break; /* This is not expected as the packet already passed NAU_ParsePacket() */
return 0;
switch (ef_type) { switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER: case NTP_EF_NTS_UNIQUE_IDENTIFIER:
@@ -428,6 +476,9 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
plaintext, sizeof (plaintext), &plaintext_length)) plaintext, sizeof (plaintext), &plaintext_length))
return 0; return 0;
if (!parse_encrypted_efs(inst, plaintext, plaintext_length))
return 0;
has_valid_auth = 1; has_valid_auth = 1;
break; break;
default: default:
@@ -467,11 +518,6 @@ NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
{ {
save_cookies(inst); save_cookies(inst);
if (inst->nke)
NKC_DestroyInstance(inst->nke);
inst->nke = NULL;
inst->num_cookies = 0;
inst->nts_address.ip_addr = *address; inst->nts_address.ip_addr = *address;
reset_instance(inst); reset_instance(inst);
@@ -507,9 +553,9 @@ save_cookies(NNC_Instance inst)
context_time = inst->last_nke_success - SCH_GetLastEventMonoTime(); context_time = inst->last_nke_success - SCH_GetLastEventMonoTime();
context_time += UTI_TimespecToDouble(&now); context_time += UTI_TimespecToDouble(&now);
if (fprintf(f, "%s%.1f\n%s %d\n%d ", if (fprintf(f, "%s%.1f\n%s %d\n%u %d ",
DUMP_IDENTIFIER, context_time, UTI_IPToString(&inst->ntp_address->ip_addr), DUMP_IDENTIFIER, context_time, UTI_IPToString(&inst->ntp_address->ip_addr),
inst->ntp_address->port, (int)inst->context.algorithm) < 0 || inst->ntp_address->port, inst->context_id, (int)inst->context.algorithm) < 0 ||
!UTI_BytesToHex(inst->context.s2c.key, inst->context.s2c.length, buf, sizeof (buf)) || !UTI_BytesToHex(inst->context.s2c.key, inst->context.s2c.length, buf, sizeof (buf)) ||
fprintf(f, "%s ", buf) < 0 || fprintf(f, "%s ", buf) < 0 ||
!UTI_BytesToHex(inst->context.c2s.key, inst->context.c2s.length, buf, sizeof (buf)) || !UTI_BytesToHex(inst->context.c2s.key, inst->context.c2s.length, buf, sizeof (buf)) ||
@@ -538,12 +584,13 @@ error:
/* ================================================== */ /* ================================================== */
#define MAX_WORDS 3 #define MAX_WORDS 4
static void static void
load_cookies(NNC_Instance inst) load_cookies(NNC_Instance inst)
{ {
char line[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename, *words[MAX_WORDS]; char line[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename, *words[MAX_WORDS];
unsigned int context_id;
int i, algorithm, port; int i, algorithm, port;
double context_time; double context_time;
struct timespec now; struct timespec now;
@@ -573,14 +620,14 @@ load_cookies(NNC_Instance inst)
sscanf(words[0], "%lf", &context_time) != 1 || sscanf(words[0], "%lf", &context_time) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 || !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 || !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 || !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 4 ||
sscanf(words[0], "%d", &algorithm) != 1) sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
goto error; goto error;
inst->context.algorithm = algorithm; inst->context.algorithm = algorithm;
inst->context.s2c.length = UTI_HexToBytes(words[1], inst->context.s2c.key, inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
sizeof (inst->context.s2c.key)); sizeof (inst->context.s2c.key));
inst->context.c2s.length = UTI_HexToBytes(words[2], inst->context.c2s.key, inst->context.c2s.length = UTI_HexToBytes(words[3], inst->context.c2s.key,
sizeof (inst->context.c2s.key)); sizeof (inst->context.c2s.key));
if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) || if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
@@ -608,6 +655,7 @@ load_cookies(NNC_Instance inst)
if (context_time > 0) if (context_time > 0)
context_time = 0; context_time = 0;
inst->last_nke_success = context_time + SCH_GetLastEventMonoTime(); inst->last_nke_success = context_time + SCH_GetLastEventMonoTime();
inst->context_id = context_id;
DEBUG_LOG("Loaded %d cookies for %s", i, filename); DEBUG_LOG("Loaded %d cookies for %s", i, filename);
return; return;
@@ -627,3 +675,21 @@ NNC_DumpData(NNC_Instance inst)
{ {
save_cookies(inst); save_cookies(inst);
} }
/* ================================================== */
void
NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report)
{
report->key_id = inst->context_id;
report->key_type = inst->context.algorithm;
report->key_length = 8 * inst->context.s2c.length;
report->ke_attempts = inst->nke_attempts;
if (report->key_length > 0)
report->last_ke_ago = SCH_GetLastEventMonoTime() - inst->last_nke_success;
else
report->last_ke_ago = -1;
report->cookies = inst->num_cookies;
report->cookie_length = inst->num_cookies > 0 ? inst->cookies[inst->cookie_index].length : 0;
report->nak = inst->nak_response;
}

View File

@@ -29,6 +29,7 @@
#include "addressing.h" #include "addressing.h"
#include "ntp.h" #include "ntp.h"
#include "reports.h"
typedef struct NNC_Instance_Record *NNC_Instance; typedef struct NNC_Instance_Record *NNC_Instance;
@@ -45,4 +46,6 @@ extern void NNC_ChangeAddress(NNC_Instance inst, IPAddr *address);
extern void NNC_DumpData(NNC_Instance inst); extern void NNC_DumpData(NNC_Instance inst);
extern void NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report);
#endif #endif

View File

@@ -67,6 +67,8 @@ NNS_Initialise(void)
server = Malloc(sizeof (struct NtsServer)); server = Malloc(sizeof (struct NtsServer));
server->siv = SIV_CreateInstance(SERVER_SIV); server->siv = SIV_CreateInstance(SERVER_SIV);
if (!server->siv)
LOG_FATAL("Could not initialise SIV cipher");
} }
/* ================================================== */ /* ================================================== */
@@ -110,15 +112,18 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) { for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed, if (!NEF_ParseField(packet, info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length)) &ef_length, &ef_type, &ef_body, &ef_body_length))
break; /* This is not expected as the packet already passed NAU_ParsePacket() */
return 0;
switch (ef_type) { switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER: case NTP_EF_NTS_UNIQUE_IDENTIFIER:
has_uniq_id = 1; has_uniq_id = 1;
break; break;
case NTP_EF_NTS_COOKIE: case NTP_EF_NTS_COOKIE:
if (has_cookie || ef_body_length > sizeof (cookie.cookie)) if (has_cookie || ef_body_length > sizeof (cookie.cookie)) {
DEBUG_LOG("Unexpected cookie/length");
return 0; return 0;
}
cookie.length = ef_body_length; cookie.length = ef_body_length;
memcpy(cookie.cookie, ef_body, ef_body_length); memcpy(cookie.cookie, ef_body, ef_body_length);
has_cookie = 1; has_cookie = 1;
@@ -133,6 +138,11 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
cookie_length = ef_body_length; cookie_length = ef_body_length;
break; break;
case NTP_EF_NTS_AUTH_AND_EEF: case NTP_EF_NTS_AUTH_AND_EEF:
if (parsed + ef_length != info->length) {
DEBUG_LOG("Auth not last EF");
return 0;
}
auth_start = parsed; auth_start = parsed;
has_auth = 1; has_auth = 1;
break; break;
@@ -169,8 +179,10 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
for (parsed = 0; parsed < plaintext_length; parsed += ef_length) { for (parsed = 0; parsed < plaintext_length; parsed += ef_length) {
if (!NEF_ParseSingleField(plaintext, plaintext_length, parsed, if (!NEF_ParseSingleField(plaintext, plaintext_length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length)) &ef_length, &ef_type, &ef_body, &ef_body_length)) {
break; DEBUG_LOG("Could not parse encrypted EF");
return 0;
}
switch (ef_type) { switch (ef_type) {
case NTP_EF_NTS_COOKIE_PLACEHOLDER: case NTP_EF_NTS_COOKIE_PLACEHOLDER:
@@ -190,13 +202,18 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
return 0; return 0;
} }
/* Prepare data for NNS_GenerateResponseAuth() to minimise the time spent
there (when the TX timestamp is already set) */
UTI_GetRandomBytes(server->nonce, sizeof (server->nonce)); UTI_GetRandomBytes(server->nonce, sizeof (server->nonce));
server->num_cookies = MIN(NTS_MAX_COOKIES, requested_cookies); assert(sizeof (server->cookies) / sizeof (server->cookies[0]) == NTS_MAX_COOKIES);
for (i = 0; i < server->num_cookies; i++) for (i = 0; i < NTS_MAX_COOKIES && i < requested_cookies; i++)
if (!NKS_GenerateCookie(&context, &server->cookies[i])) if (!NKS_GenerateCookie(&context, &server->cookies[i]))
return 0; return 0;
server->num_cookies = i;
return 1; return 1;
} }
@@ -215,14 +232,16 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
if (!server || req_info->mode != MODE_CLIENT || res_info->mode != MODE_SERVER) if (!server || req_info->mode != MODE_CLIENT || res_info->mode != MODE_SERVER)
return 0; return 0;
/* Make sure this is a response to the expected request */ /* Make sure this is a response to the request from the last call
of NNS_CheckRequestAuth() */
if (UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) != 0) if (UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) != 0)
assert(0); assert(0);
for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) { for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) {
if (!NEF_ParseField(request, req_info->length, parsed, if (!NEF_ParseField(request, req_info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length)) &ef_length, &ef_type, &ef_body, &ef_body_length))
break; /* This is not expected as the packet already passed NAU_ParsePacket() */
return 0;
switch (ef_type) { switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER: case NTP_EF_NTS_UNIQUE_IDENTIFIER:
@@ -240,7 +259,7 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) { for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) {
if (!NEF_SetField(plaintext, sizeof (plaintext), plaintext_length, if (!NEF_SetField(plaintext, sizeof (plaintext), plaintext_length,
NTP_EF_NTS_COOKIE, &server->cookies[i].cookie, NTP_EF_NTS_COOKIE, server->cookies[i].cookie,
server->cookies[i].length, &ef_length)) server->cookies[i].length, &ef_length))
return 0; return 0;
@@ -250,6 +269,8 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
server->num_cookies = 0; server->num_cookies = 0;
/* Generate an authenticator field which will make the length
of the response equal to the length of the request */
if (!NNA_GenerateAuthEF(response, res_info, server->siv, if (!NNA_GenerateAuthEF(response, res_info, server->siv,
server->nonce, sizeof (server->nonce), server->nonce, sizeof (server->nonce),
plaintext, plaintext_length, plaintext, plaintext_length,

View File

@@ -110,8 +110,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(smoothtime, null), /* SMOOTHTIME */ REQ_LENGTH_ENTRY(smoothtime, null), /* SMOOTHTIME */
REQ_LENGTH_ENTRY(null, null), /* REFRESH */ REQ_LENGTH_ENTRY(null, null), /* REFRESH */
REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */ REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */
REQ_LENGTH_ENTRY(client_accesses_by_index, { 0, 0 }, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */ REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */ REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
{ 0, 0 }, /* ADD_SERVER2 */ { 0, 0 }, /* ADD_SERVER2 */
@@ -123,7 +122,12 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SOURCE */ REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SOURCE */
REQ_LENGTH_ENTRY(ntp_source_name, REQ_LENGTH_ENTRY(ntp_source_name,
ntp_source_name), /* NTP_SOURCE_NAME */ ntp_source_name), /* NTP_SOURCE_NAME */
REQ_LENGTH_ENTRY(null, null), /* RESET */ REQ_LENGTH_ENTRY(null, null), /* RESET_SOURCES */
REQ_LENGTH_ENTRY(auth_data, auth_data), /* AUTH_DATA */
REQ_LENGTH_ENTRY(client_accesses_by_index,
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
}; };
static const uint16_t reply_lengths[] = { static const uint16_t reply_lengths[] = {
@@ -141,12 +145,16 @@ static const uint16_t reply_lengths[] = {
0, /* MANUAL_LIST - not supported */ 0, /* MANUAL_LIST - not supported */
RPY_LENGTH_ENTRY(activity), /* ACTIVITY */ RPY_LENGTH_ENTRY(activity), /* ACTIVITY */
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */ RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */ 0, /* SERVER_STATS - not supported */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */ 0, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */ RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */ RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */ RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */ RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */
RPY_LENGTH_ENTRY(auth_data), /* AUTH_DATA */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS2 */
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
}; };
/* ================================================== */ /* ================================================== */

View File

@@ -253,8 +253,9 @@ RCL_AddRefclock(RefclockParameters *params)
inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length, inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length,
params->max_dispersion, 0.6); params->max_dispersion, 0.6);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL, inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, 0, params->sel_options,
params->min_samples, params->max_samples, 0.0, 0.0); NULL, params->min_samples, params->max_samples,
0.0, 0.0);
DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d", DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
params->driver_name, UTI_RefidToString(inst->ref_id), params->driver_name, UTI_RefidToString(inst->ref_id),

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2018 * Copyright (C) Miroslav Lichvar 2009-2018, 2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -304,6 +304,8 @@ REF_Finalise(void)
update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew); update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew);
} }
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
Free(fb_drifts); Free(fb_drifts);
initialised = 0; initialised = 0;
@@ -489,8 +491,7 @@ maybe_log_offset(double offset, time_t now)
abs_offset = fabs(offset); abs_offset = fabs(offset);
if (abs_offset > log_change_threshold) { if (abs_offset > log_change_threshold) {
LOG(LOGS_WARN, "System clock wrong by %.6f seconds, adjustment started", LOG(LOGS_WARN, "System clock wrong by %.6f seconds", -offset);
-offset);
} }
if (do_mail_change && if (do_mail_change &&
@@ -556,8 +557,7 @@ is_offset_ok(double offset)
return 1; return 1;
} }
offset = fabs(offset); if (fabs(offset) > max_offset) {
if (offset > max_offset) {
LOG(LOGS_WARN, LOG(LOGS_WARN,
"Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ", "Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ",
-offset, max_offset, !max_offset_ignore ? "exiting" : "ignored"); -offset, max_offset, !max_offset_ignore ? "exiting" : "ignored");
@@ -1018,7 +1018,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
our_residual_freq = residual_frequency; our_residual_freq = residual_frequency;
our_root_delay = root_delay; our_root_delay = root_delay;
our_root_dispersion = root_dispersion; our_root_dispersion = root_dispersion;
our_frequency_sd = offset_sd; our_frequency_sd = frequency_sd;
our_offset_sd = offset_sd; our_offset_sd = offset_sd;
last_ref_update = mono_now; last_ref_update = mono_now;
last_ref_update_interval = update_interval; last_ref_update_interval = update_interval;

View File

@@ -36,8 +36,14 @@ typedef struct {
int stratum; int stratum;
int poll; int poll;
enum {RPT_NTP_CLIENT, RPT_NTP_PEER, RPT_LOCAL_REFERENCE} mode; enum {RPT_NTP_CLIENT, RPT_NTP_PEER, RPT_LOCAL_REFERENCE} mode;
enum {RPT_SYNC, RPT_UNREACH, RPT_FALSETICKER, RPT_JITTERY, RPT_CANDIDATE, RPT_OUTLIER} state; enum {
int sel_options; RPT_NONSELECTABLE,
RPT_FALSETICKER,
RPT_JITTERY,
RPT_SELECTABLE,
RPT_UNSELECTED,
RPT_SELECTED,
} state;
int reachability; int reachability;
unsigned long latest_meas_ago; /* seconds */ unsigned long latest_meas_ago; /* seconds */
@@ -78,8 +84,8 @@ typedef struct {
typedef struct { typedef struct {
struct timespec ref_time; struct timespec ref_time;
unsigned short n_samples; unsigned long n_samples;
unsigned short n_runs; unsigned long n_runs;
unsigned long span_seconds; unsigned long span_seconds;
double rtc_seconds_fast; double rtc_seconds_fast;
double rtc_gain_rate_ppm; double rtc_gain_rate_ppm;
@@ -88,22 +94,29 @@ typedef struct {
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
uint32_t ntp_hits; uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits; uint32_t cmd_hits;
uint16_t ntp_drops; uint16_t ntp_drops;
uint16_t nke_drops;
uint16_t cmd_drops; uint16_t cmd_drops;
int8_t ntp_interval; int8_t ntp_interval;
int8_t nke_interval;
int8_t cmd_interval; int8_t cmd_interval;
int8_t ntp_timeout_interval; int8_t ntp_timeout_interval;
uint32_t last_ntp_hit_ago; uint32_t last_ntp_hit_ago;
uint32_t last_nke_hit_ago;
uint32_t last_cmd_hit_ago; uint32_t last_cmd_hit_ago;
} RPT_ClientAccessByIndex_Report; } RPT_ClientAccessByIndex_Report;
typedef struct { typedef struct {
uint32_t ntp_hits; uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits; uint32_t cmd_hits;
uint32_t ntp_drops; uint32_t ntp_drops;
uint32_t nke_drops;
uint32_t cmd_drops; uint32_t cmd_drops;
uint32_t log_drops; uint32_t log_drops;
uint32_t ntp_auth_hits;
} RPT_ServerStatsReport; } RPT_ServerStatsReport;
typedef struct { typedef struct {
@@ -160,4 +173,29 @@ typedef struct {
uint32_t total_valid_count; uint32_t total_valid_count;
} RPT_NTPReport; } RPT_NTPReport;
typedef struct {
NTP_AuthMode mode;
uint32_t key_id;
int key_type;
int key_length;
int ke_attempts;
uint32_t last_ke_ago;
int cookies;
int cookie_length;
int nak;
} RPT_AuthReport;
typedef struct {
uint32_t ref_id;
IPAddr ip_addr;
char state_char;
int authentication;
int conf_options;
int eff_options;
uint32_t last_sample_ago;
double score;
double lo_limit;
double hi_limit;
} RPT_SelectReport;
#endif /* GOT_REPORTS_H */ #endif /* GOT_REPORTS_H */

View File

@@ -573,6 +573,10 @@ RTC_Linux_Finalise(void)
(void) RTC_Linux_WriteParameters(); (void) RTC_Linux_WriteParameters();
} }
if (rtc_sec)
LCL_RemoveParameterChangeHandler(slew_samples, NULL);
Free(rtc_sec); Free(rtc_sec);
Free(rtc_trim); Free(rtc_trim);
Free(system_times); Free(system_times);

14
sched.c
View File

@@ -490,20 +490,6 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
assert(0); assert(0);
} }
/* ================================================== */
void
SCH_Reset(void)
{
while (n_timer_queue_entries > 0)
SCH_RemoveTimeout(timer_queue.next->id);
while (one_highest_fd > 0) {
close(one_highest_fd - 1);
SCH_RemoveFileHandler(one_highest_fd - 1);
}
}
/* ================================================== */ /* ================================================== */
/* Try to dispatch any timeouts that have already gone by, and /* Try to dispatch any timeouts that have already gone by, and
keep going until all are done. (The earlier ones may take so keep going until all are done. (The earlier ones may take so

View File

@@ -85,9 +85,6 @@ extern SCH_TimeoutID SCH_AddTimeoutInClass(double min_delay, double separation,
/* The next one probably ought to return a status code */ /* The next one probably ought to return a status code */
extern void SCH_RemoveTimeout(SCH_TimeoutID); extern void SCH_RemoveTimeout(SCH_TimeoutID);
/* Remove all timeouts and close all file descriptors */
extern void SCH_Reset(void);
extern void SCH_MainLoop(void); extern void SCH_MainLoop(void);
extern void SCH_QuitProgram(void); extern void SCH_QuitProgram(void);

256
siv_gnutls.c Normal file
View File

@@ -0,0 +1,256 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
SIV ciphers using the GnuTLS library
*/
#include "config.h"
#include "sysincl.h"
#include <gnutls/crypto.h>
#include "logging.h"
#include "memory.h"
#include "siv.h"
struct SIV_Instance_Record {
gnutls_cipher_algorithm_t algorithm;
gnutls_aead_cipher_hd_t cipher;
};
/* ================================================== */
static int instance_counter = 0;
static int gnutls_initialised = 0;
/* ================================================== */
static void
init_gnutls(void)
{
int r;
if (gnutls_initialised)
return;
r = gnutls_global_init();
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
DEBUG_LOG("Initialised");
gnutls_initialised = 1;
}
/* ================================================== */
static void
deinit_gnutls(void)
{
assert(gnutls_initialised);
gnutls_global_deinit();
gnutls_initialised = 0;
DEBUG_LOG("Deinitialised");
}
/* ================================================== */
static gnutls_cipher_algorithm_t
get_cipher_algorithm(SIV_Algorithm algorithm)
{
switch (algorithm) {
case AEAD_AES_SIV_CMAC_256:
return GNUTLS_CIPHER_AES_128_SIV;
default:
return 0;
}
}
/* ================================================== */
SIV_Instance
SIV_CreateInstance(SIV_Algorithm algorithm)
{
gnutls_cipher_algorithm_t calgo;
SIV_Instance instance;
calgo = get_cipher_algorithm(algorithm);
if (calgo == 0)
return NULL;
if (instance_counter == 0)
init_gnutls();
/* Check if the cipher is actually supported */
if (gnutls_cipher_get_tag_size(calgo) == 0)
return NULL;
instance = MallocNew(struct SIV_Instance_Record);
instance->algorithm = calgo;
instance->cipher = NULL;
instance_counter++;
return instance;
}
/* ================================================== */
void
SIV_DestroyInstance(SIV_Instance instance)
{
if (instance->cipher)
gnutls_aead_cipher_deinit(instance->cipher);
Free(instance);
instance_counter--;
if (instance_counter == 0)
deinit_gnutls();
}
/* ================================================== */
int
SIV_GetKeyLength(SIV_Algorithm algorithm)
{
gnutls_cipher_algorithm_t calgo = get_cipher_algorithm(algorithm);
int len;
if (calgo == 0)
return 0;
len = gnutls_cipher_get_key_size(calgo);
if (len < 1 || len > SIV_MAX_KEY_LENGTH)
LOG_FATAL("Invalid key length");
return len;
}
/* ================================================== */
int
SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
{
gnutls_aead_cipher_hd_t cipher;
gnutls_datum_t datum;
int r;
if (length <= 0 || length != gnutls_cipher_get_key_size(instance->algorithm))
return 0;
datum.data = (unsigned char *)key;
datum.size = length;
/* Initialise a new cipher with the provided key (gnutls does not seem to
have a function to change the key directly) */
r = gnutls_aead_cipher_init(&cipher, instance->algorithm, &datum);
if (r < 0) {
DEBUG_LOG("Could not initialise %s : %s", "cipher", gnutls_strerror(r));
return 0;
}
/* Replace the previous cipher */
if (instance->cipher)
gnutls_aead_cipher_deinit(instance->cipher);
instance->cipher = cipher;
return 1;
}
/* ================================================== */
int
SIV_GetTagLength(SIV_Instance instance)
{
int len;
len = gnutls_cipher_get_tag_size(instance->algorithm);
if (len < 1 || len > SIV_MAX_TAG_LENGTH)
LOG_FATAL("Invalid tag length");
return len;
}
/* ================================================== */
int
SIV_Encrypt(SIV_Instance instance,
const unsigned char *nonce, int nonce_length,
const void *assoc, int assoc_length,
const void *plaintext, int plaintext_length,
unsigned char *ciphertext, int ciphertext_length)
{
size_t clen = ciphertext_length;
if (!instance->cipher)
return 0;
if (nonce_length < 1 || assoc_length < 0 ||
plaintext_length < 0 || ciphertext_length < 0)
return 0;
assert(assoc && plaintext);
if (gnutls_aead_cipher_encrypt(instance->cipher,
nonce, nonce_length, assoc, assoc_length, 0,
plaintext, plaintext_length, ciphertext, &clen) < 0)
return 0;
if (clen != ciphertext_length)
return 0;
return 1;
}
/* ================================================== */
int
SIV_Decrypt(SIV_Instance instance,
const unsigned char *nonce, int nonce_length,
const void *assoc, int assoc_length,
const unsigned char *ciphertext, int ciphertext_length,
void *plaintext, int plaintext_length)
{
size_t plen = plaintext_length;
if (!instance->cipher)
return 0;
if (nonce_length < 1 || assoc_length < 0 ||
plaintext_length < 0 || ciphertext_length < 0)
return 0;
assert(assoc && plaintext);
if (gnutls_aead_cipher_decrypt(instance->cipher,
nonce, nonce_length, assoc, assoc_length, 0,
ciphertext, ciphertext_length, plaintext, &plen) < 0)
return 0;
if (plen != plaintext_length)
return 0;
return 1;
}

View File

@@ -39,6 +39,7 @@
struct SIV_Instance_Record { struct SIV_Instance_Record {
struct siv_cmac_aes128_ctx siv; struct siv_cmac_aes128_ctx siv;
int key_set;
}; };
/* ================================================== */ /* ================================================== */
@@ -52,6 +53,7 @@ SIV_CreateInstance(SIV_Algorithm algorithm)
return NULL; return NULL;
instance = MallocNew(struct SIV_Instance_Record); instance = MallocNew(struct SIV_Instance_Record);
instance->key_set = 0;
return instance; return instance;
} }
@@ -69,6 +71,8 @@ SIV_DestroyInstance(SIV_Instance instance)
int int
SIV_GetKeyLength(SIV_Algorithm algorithm) SIV_GetKeyLength(SIV_Algorithm algorithm)
{ {
assert(32 <= SIV_MAX_KEY_LENGTH);
if (algorithm == AEAD_AES_SIV_CMAC_256) if (algorithm == AEAD_AES_SIV_CMAC_256)
return 32; return 32;
return 0; return 0;
@@ -84,6 +88,8 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
siv_cmac_aes128_set_key(&instance->siv, key); siv_cmac_aes128_set_key(&instance->siv, key);
instance->key_set = 1;
return 1; return 1;
} }
@@ -92,6 +98,8 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
int int
SIV_GetTagLength(SIV_Instance instance) SIV_GetTagLength(SIV_Instance instance)
{ {
assert(SIV_DIGEST_SIZE <= SIV_MAX_TAG_LENGTH);
return SIV_DIGEST_SIZE; return SIV_DIGEST_SIZE;
} }
@@ -104,6 +112,9 @@ SIV_Encrypt(SIV_Instance instance,
const void *plaintext, int plaintext_length, const void *plaintext, int plaintext_length,
unsigned char *ciphertext, int ciphertext_length) unsigned char *ciphertext, int ciphertext_length)
{ {
if (!instance->key_set)
return 0;
if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 || if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 ||
plaintext_length < 0 || plaintext_length > ciphertext_length || plaintext_length < 0 || plaintext_length > ciphertext_length ||
plaintext_length + SIV_DIGEST_SIZE != ciphertext_length) plaintext_length + SIV_DIGEST_SIZE != ciphertext_length)
@@ -126,6 +137,9 @@ SIV_Decrypt(SIV_Instance instance,
const unsigned char *ciphertext, int ciphertext_length, const unsigned char *ciphertext, int ciphertext_length,
void *plaintext, int plaintext_length) void *plaintext, int plaintext_length)
{ {
if (!instance->key_set)
return 0;
if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 || if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 ||
plaintext_length < 0 || plaintext_length > ciphertext_length || plaintext_length < 0 || plaintext_length > ciphertext_length ||
plaintext_length + SIV_DIGEST_SIZE != ciphertext_length) plaintext_length + SIV_DIGEST_SIZE != ciphertext_length)

View File

@@ -272,6 +272,10 @@ void SMT_Initialise(void)
void SMT_Finalise(void) void SMT_Finalise(void)
{ {
if (!enabled)
return;
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
} }
int SMT_IsEnabled(void) int SMT_IsEnabled(void)

130
socket.c
View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009 * Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2019 * Copyright (C) Miroslav Lichvar 2009, 2013-2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -82,6 +82,10 @@ struct MessageHeader {
static int initialised; static int initialised;
/* Flags indicating in which IP families sockets can be requested */
static int ip4_enabled;
static int ip6_enabled;
/* Flags supported by socket() */ /* Flags supported by socket() */
static int supported_socket_flags; static int supported_socket_flags;
@@ -114,7 +118,7 @@ prepare_buffers(unsigned int n)
hdr->msg_hdr.msg_namelen = sizeof (msg->name); hdr->msg_hdr.msg_namelen = sizeof (msg->name);
hdr->msg_hdr.msg_iov = &msg->iov; hdr->msg_hdr.msg_iov = &msg->iov;
hdr->msg_hdr.msg_iovlen = 1; hdr->msg_hdr.msg_iovlen = 1;
hdr->msg_hdr.msg_control = &msg->cmsg_buf; hdr->msg_hdr.msg_control = msg->cmsg_buf;
hdr->msg_hdr.msg_controllen = sizeof (msg->cmsg_buf); hdr->msg_hdr.msg_controllen = sizeof (msg->cmsg_buf);
hdr->msg_hdr.msg_flags = 0; hdr->msg_hdr.msg_flags = 0;
hdr->msg_len = 0; hdr->msg_len = 0;
@@ -172,7 +176,7 @@ check_socket_flag(int sock_flag, int fd_flag, int fs_flag)
static int static int
set_socket_nonblock(int sock_fd) set_socket_nonblock(int sock_fd)
{ {
if (fcntl(sock_fd, F_SETFL, O_NONBLOCK)) { if (fcntl(sock_fd, F_SETFL, O_NONBLOCK) < 0) {
DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno)); DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno));
return 0; return 0;
} }
@@ -332,6 +336,23 @@ is_any_address(IPAddr *addr)
/* ================================================== */ /* ================================================== */
static int
bind_device(int sock_fd, const char *iface)
{
#ifdef SO_BINDTODEVICE
if (setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, iface, strlen(iface) + 1) < 0) {
DEBUG_LOG("Could not bind socket to %s : %s", iface, strerror(errno));
return 0;
}
return 1;
#else
DEBUG_LOG("Could not bind socket to %s : %s", iface, "Not supported");
return 0;
#endif
}
/* ================================================== */
static int static int
bind_ip_address(int sock_fd, IPSockAddr *addr, int flags) bind_ip_address(int sock_fd, IPSockAddr *addr, int flags)
{ {
@@ -399,7 +420,8 @@ connect_ip_address(int sock_fd, IPSockAddr *addr)
/* ================================================== */ /* ================================================== */
static int static int
open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int type, int flags) open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface,
int type, int flags)
{ {
int domain, family, sock_fd; int domain, family, sock_fd;
@@ -412,10 +434,14 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int type, int fl
switch (family) { switch (family) {
case IPADDR_INET4: case IPADDR_INET4:
if (!ip4_enabled)
return INVALID_SOCK_FD;
domain = AF_INET; domain = AF_INET;
break; break;
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
case IPADDR_INET6: case IPADDR_INET6:
if (!ip6_enabled)
return INVALID_SOCK_FD;
domain = AF_INET6; domain = AF_INET6;
break; break;
#endif #endif
@@ -434,6 +460,9 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int type, int fl
if (!set_ip_options(sock_fd, family, flags)) if (!set_ip_options(sock_fd, family, flags))
goto error; goto error;
if (iface && !bind_device(sock_fd, iface))
goto error;
/* Bind the socket if a non-any local address/port was specified */ /* Bind the socket if a non-any local address/port was specified */
if (local_addr && local_addr->ip_addr.family != IPADDR_UNSPEC && if (local_addr && local_addr->ip_addr.family != IPADDR_UNSPEC &&
(local_addr->port != 0 || !is_any_address(&local_addr->ip_addr)) && (local_addr->port != 0 || !is_any_address(&local_addr->ip_addr)) &&
@@ -476,8 +505,8 @@ bind_unix_address(int sock_fd, const char *addr, int flags)
} }
saddr.un.sun_family = AF_UNIX; saddr.un.sun_family = AF_UNIX;
if (!UTI_RemoveFile(NULL, addr, NULL)) if (unlink(addr) < 0)
; DEBUG_LOG("Could not remove %s : %s", addr, strerror(errno));
/* PRV_BindSocket() doesn't support Unix sockets yet */ /* PRV_BindSocket() doesn't support Unix sockets yet */
if (bind(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) { if (bind(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) {
@@ -627,9 +656,8 @@ log_message(int sock_fd, int direction, SCK_Message *message, const char *prefix
case SCK_ADDR_IP: case SCK_ADDR_IP:
if (message->remote_addr.ip.ip_addr.family != IPADDR_UNSPEC) if (message->remote_addr.ip.ip_addr.family != IPADDR_UNSPEC)
remote_addr = UTI_IPSockAddrToString(&message->remote_addr.ip); remote_addr = UTI_IPSockAddrToString(&message->remote_addr.ip);
if (message->local_addr.ip.family != IPADDR_UNSPEC) { if (message->local_addr.ip.family != IPADDR_UNSPEC)
local_addr = UTI_IPToString(&message->local_addr.ip); local_addr = UTI_IPToString(&message->local_addr.ip);
}
break; break;
case SCK_ADDR_UNIX: case SCK_ADDR_UNIX:
remote_addr = message->remote_addr.path; remote_addr = message->remote_addr.path;
@@ -655,7 +683,7 @@ log_message(int sock_fd, int direction, SCK_Message *message, const char *prefix
snprintf(tslen, sizeof (tslen), " tslen=%d", message->timestamp.l2_length); snprintf(tslen, sizeof (tslen), " tslen=%d", message->timestamp.l2_length);
} }
DEBUG_LOG("%s message%s%s%s%s fd=%d len=%u%s%s%s%s%s%s", DEBUG_LOG("%s message%s%s%s%s fd=%d len=%d%s%s%s%s%s%s",
prefix, prefix,
remote_addr ? (direction > 0 ? " from " : " to ") : "", remote_addr ? (direction > 0 ? " from " : " to ") : "",
remote_addr ? remote_addr : "", remote_addr ? remote_addr : "",
@@ -710,7 +738,7 @@ init_message_nonaddress(SCK_Message *message)
/* ================================================== */ /* ================================================== */
static int static int
process_header(struct msghdr *msg, unsigned int msg_length, int sock_fd, int flags, process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
SCK_Message *message) SCK_Message *message)
{ {
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
@@ -892,7 +920,10 @@ receive_messages(int sock_fd, int flags, int max_messages, int *num_messages)
hdr = ARR_GetElements(recv_headers); hdr = ARR_GetElements(recv_headers);
n = ARR_GetSize(recv_headers); n = ARR_GetSize(recv_headers);
n = MIN(n, max_messages); n = MIN(n, max_messages);
assert(n >= 1);
if (n < 1 || n > MAX_RECV_MESSAGES ||
n > ARR_GetSize(recv_messages) || n > ARR_GetSize(recv_sck_messages))
assert(0);
recv_flags = get_recv_flags(flags); recv_flags = get_recv_flags(flags);
@@ -1001,6 +1032,11 @@ send_message(int sock_fd, SCK_Message *message, int flags)
msg.msg_namelen = 0; msg.msg_namelen = 0;
} }
if (message->length < 0) {
DEBUG_LOG("Invalid length %d", message->length);
return 0;
}
iov.iov_base = message->data; iov.iov_base = message->data;
iov.iov_len = message->length; iov.iov_len = message->length;
msg.msg_iov = &iov; msg.msg_iov = &iov;
@@ -1094,8 +1130,15 @@ send_message(int sock_fd, SCK_Message *message, int flags)
/* ================================================== */ /* ================================================== */
void void
SCK_Initialise(void) SCK_Initialise(int family)
{ {
ip4_enabled = family == IPADDR_INET4 || family == IPADDR_UNSPEC;
#ifdef FEAT_IPV6
ip6_enabled = family == IPADDR_INET6 || family == IPADDR_UNSPEC;
#else
ip6_enabled = 0;
#endif
recv_messages = ARR_CreateInstance(sizeof (struct Message)); recv_messages = ARR_CreateInstance(sizeof (struct Message));
ARR_SetSize(recv_messages, MAX_RECV_MESSAGES); ARR_SetSize(recv_messages, MAX_RECV_MESSAGES);
recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader)); recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader));
@@ -1135,15 +1178,13 @@ SCK_Finalise(void)
/* ================================================== */ /* ================================================== */
int int
SCK_IsFamilySupported(int family) SCK_IsIpFamilyEnabled(int family)
{ {
switch (family) { switch (family) {
case IPADDR_INET4: case IPADDR_INET4:
return 1; return ip4_enabled;
case IPADDR_INET6: case IPADDR_INET6:
#ifdef FEAT_IPV6 return ip6_enabled;
return 1;
#endif
default: default:
return 0; return 0;
} }
@@ -1194,6 +1235,23 @@ SCK_GetLoopbackIPAddress(int family, IPAddr *local_addr)
/* ================================================== */ /* ================================================== */
int
SCK_IsLinkLocalIPAddress(IPAddr *addr)
{
switch (addr->family) {
case IPADDR_INET4:
/* 169.254.0.0/16 */
return (addr->addr.in4 & 0xffff0000) == 0xa9fe0000;
case IPADDR_INET6:
/* fe80::/10 */
return addr->addr.in6[0] == 0xfe && (addr->addr.in6[1] & 0xc0) == 0x80;
default:
return 0;
}
}
/* ================================================== */
void void
SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address, SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
socklen_t address_len)) socklen_t address_len))
@@ -1204,17 +1262,17 @@ SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
/* ================================================== */ /* ================================================== */
int int
SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags) SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface, int flags)
{ {
return open_ip_socket(remote_addr, local_addr, SOCK_DGRAM, flags); return open_ip_socket(remote_addr, local_addr, iface, SOCK_DGRAM, flags);
} }
/* ================================================== */ /* ================================================== */
int int
SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags) SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface, int flags)
{ {
return open_ip_socket(remote_addr, local_addr, SOCK_STREAM, flags); return open_ip_socket(remote_addr, local_addr, iface, SOCK_STREAM, flags);
} }
/* ================================================== */ /* ================================================== */
@@ -1353,10 +1411,15 @@ SCK_ShutdownConnection(int sock_fd)
/* ================================================== */ /* ================================================== */
int int
SCK_Receive(int sock_fd, void *buffer, unsigned int length, int flags) SCK_Receive(int sock_fd, void *buffer, int length, int flags)
{ {
int r; int r;
if (length < 0) {
DEBUG_LOG("Invalid length %d", length);
return -1;
}
r = recv(sock_fd, buffer, length, get_recv_flags(flags)); r = recv(sock_fd, buffer, length, get_recv_flags(flags));
if (r < 0) { if (r < 0) {
@@ -1372,16 +1435,21 @@ SCK_Receive(int sock_fd, void *buffer, unsigned int length, int flags)
/* ================================================== */ /* ================================================== */
int int
SCK_Send(int sock_fd, const void *buffer, unsigned int length, int flags) SCK_Send(int sock_fd, const void *buffer, int length, int flags)
{ {
int r; int r;
assert(flags == 0); assert(flags == 0);
if (length < 0) {
DEBUG_LOG("Invalid length %d", length);
return -1;
}
r = send(sock_fd, buffer, length, 0); r = send(sock_fd, buffer, length, 0);
if (r < 0) { if (r < 0) {
DEBUG_LOG("Could not send data fd=%d len=%u : %s", sock_fd, length, strerror(errno)); DEBUG_LOG("Could not send data fd=%d len=%d : %s", sock_fd, length, strerror(errno));
return r; return r;
} }
@@ -1444,8 +1512,10 @@ SCK_RemoveSocket(int sock_fd)
saddr.sa.sa_family != AF_UNIX) saddr.sa.sa_family != AF_UNIX)
return 0; return 0;
if (!UTI_RemoveFile(NULL, saddr.un.sun_path, NULL)) if (unlink(saddr.un.sun_path) < 0) {
DEBUG_LOG("Could not remove %s : %s", saddr.un.sun_path, strerror(errno));
return 0; return 0;
}
return 1; return 1;
} }
@@ -1468,7 +1538,7 @@ SCK_SockaddrToIPSockAddr(struct sockaddr *sa, int sa_length, IPSockAddr *ip_sa)
switch (sa->sa_family) { switch (sa->sa_family) {
case AF_INET: case AF_INET:
if (sa_length < sizeof (struct sockaddr_in)) if (sa_length < (int)sizeof (struct sockaddr_in))
return; return;
ip_sa->ip_addr.family = IPADDR_INET4; ip_sa->ip_addr.family = IPADDR_INET4;
ip_sa->ip_addr.addr.in4 = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); ip_sa->ip_addr.addr.in4 = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
@@ -1476,7 +1546,7 @@ SCK_SockaddrToIPSockAddr(struct sockaddr *sa, int sa_length, IPSockAddr *ip_sa)
break; break;
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
case AF_INET6: case AF_INET6:
if (sa_length < sizeof (struct sockaddr_in6)) if (sa_length < (int)sizeof (struct sockaddr_in6))
return; return;
ip_sa->ip_addr.family = IPADDR_INET6; ip_sa->ip_addr.family = IPADDR_INET6;
memcpy(&ip_sa->ip_addr.addr.in6, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, memcpy(&ip_sa->ip_addr.addr.in6, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr,
@@ -1496,7 +1566,7 @@ SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length)
{ {
switch (ip_sa->ip_addr.family) { switch (ip_sa->ip_addr.family) {
case IPADDR_INET4: case IPADDR_INET4:
if (sa_length < sizeof (struct sockaddr_in)) if (sa_length < (int)sizeof (struct sockaddr_in))
return 0; return 0;
memset(sa, 0, sizeof (struct sockaddr_in)); memset(sa, 0, sizeof (struct sockaddr_in));
sa->sa_family = AF_INET; sa->sa_family = AF_INET;
@@ -1508,7 +1578,7 @@ SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length)
return sizeof (struct sockaddr_in); return sizeof (struct sockaddr_in);
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
case IPADDR_INET6: case IPADDR_INET6:
if (sa_length < sizeof (struct sockaddr_in6)) if (sa_length < (int)sizeof (struct sockaddr_in6))
return 0; return 0;
memset(sa, 0, sizeof (struct sockaddr_in6)); memset(sa, 0, sizeof (struct sockaddr_in6));
sa->sa_family = AF_INET6; sa->sa_family = AF_INET6;
@@ -1521,7 +1591,7 @@ SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length)
return sizeof (struct sockaddr_in6); return sizeof (struct sockaddr_in6);
#endif #endif
default: default:
if (sa_length < sizeof (struct sockaddr)) if (sa_length < (int)sizeof (struct sockaddr))
return 0; return 0;
memset(sa, 0, sizeof (struct sockaddr)); memset(sa, 0, sizeof (struct sockaddr));
sa->sa_family = AF_UNSPEC; sa->sa_family = AF_UNSPEC;

View File

@@ -49,7 +49,7 @@ typedef enum {
typedef struct { typedef struct {
void *data; void *data;
unsigned int length; int length;
SCK_AddressType addr_type; SCK_AddressType addr_type;
int if_index; int if_index;
@@ -73,27 +73,33 @@ typedef struct {
int descriptor; int descriptor;
} SCK_Message; } SCK_Message;
/* Initialisation function */ /* Initialisation function (the specified IP family is enabled,
extern void SCK_Initialise(void); or all if IPADDR_UNSPEC) */
extern void SCK_Initialise(int family);
/* Finalisation function */ /* Finalisation function */
extern void SCK_Finalise(void); extern void SCK_Finalise(void);
/* Check if support for the IP family was enabled in the build */ /* Check if support for the IP family is enabled */
extern int SCK_IsFamilySupported(int family); extern int SCK_IsIpFamilyEnabled(int family);
/* Get the 0.0.0.0/::0 or 127.0.0.1/::1 address */ /* Get the 0.0.0.0/::0 or 127.0.0.1/::1 address */
extern void SCK_GetAnyLocalIPAddress(int family, IPAddr *local_addr); extern void SCK_GetAnyLocalIPAddress(int family, IPAddr *local_addr);
extern void SCK_GetLoopbackIPAddress(int family, IPAddr *local_addr); extern void SCK_GetLoopbackIPAddress(int family, IPAddr *local_addr);
/* Check if an IP address is a link-local address */
extern int SCK_IsLinkLocalIPAddress(IPAddr *addr);
/* Specify a bind()-like function for binding sockets to privileged ports when /* Specify a bind()-like function for binding sockets to privileged ports when
running in a restricted process (e.g. after dropping root privileges) */ running in a restricted process (e.g. after dropping root privileges) */
extern void SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address, extern void SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
socklen_t address_len)); socklen_t address_len));
/* Open socket */ /* Open a socket (addresses and iface may be NULL) */
extern int SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags); extern int SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr,
extern int SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags); const char *iface, int flags);
extern int SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr,
const char *iface, int flags);
extern int SCK_OpenUnixDatagramSocket(const char *remote_addr, const char *local_addr, extern int SCK_OpenUnixDatagramSocket(const char *remote_addr, const char *local_addr,
int flags); int flags);
extern int SCK_OpenUnixStreamSocket(const char *remote_addr, const char *local_addr, extern int SCK_OpenUnixStreamSocket(const char *remote_addr, const char *local_addr,
@@ -113,8 +119,8 @@ extern int SCK_AcceptConnection(int sock_fd, IPSockAddr *remote_addr);
extern int SCK_ShutdownConnection(int sock_fd); extern int SCK_ShutdownConnection(int sock_fd);
/* Receive and send data on connected sockets - recv()/send() wrappers */ /* Receive and send data on connected sockets - recv()/send() wrappers */
extern int SCK_Receive(int sock_fd, void *buffer, unsigned int length, int flags); extern int SCK_Receive(int sock_fd, void *buffer, int length, int flags);
extern int SCK_Send(int sock_fd, const void *buffer, unsigned int length, int flags); extern int SCK_Send(int sock_fd, const void *buffer, int length, int flags);
/* Receive a single message or multiple messages. The functions return /* Receive a single message or multiple messages. The functions return
a pointer to static buffers, or NULL on error. The buffers are valid until a pointer to static buffers, or NULL on error. The buffers are valid until

329
sources.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2016, 2018 * Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -118,7 +118,13 @@ struct SRC_Instance_Record {
/* Type of the source */ /* Type of the source */
SRC_Type type; SRC_Type type;
/* Options used when selecting sources */ /* Flag indicating that the source is authenticated */
int authenticated;
/* Configured selection options */
int conf_sel_options;
/* Effective selection options */
int sel_options; int sel_options;
/* Score against currently selected source */ /* Score against currently selected source */
@@ -172,13 +178,11 @@ static double combine_limit;
/* ================================================== */ /* ================================================== */
/* Forward prototype */ /* Forward prototype */
static void static void update_sel_options(void);
slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq, static void slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything); double doffset, LCL_ChangeType change_type, void *anything);
static void static void add_dispersion(double dispersion, void *anything);
add_dispersion(double dispersion, void *anything); static char *source_to_string(SRC_Instance inst);
static char *
source_to_string(SRC_Instance inst);
/* ================================================== */ /* ================================================== */
/* Initialisation function */ /* Initialisation function */
@@ -218,9 +222,9 @@ void SRC_Finalise(void)
/* Function to create a new instance. This would be called by one of /* Function to create a new instance. This would be called by one of
the individual source-type instance creation routines. */ the individual source-type instance creation routines. */
SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options, SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated,
IPAddr *addr, int min_samples, int max_samples, int sel_options, IPAddr *addr, int min_samples,
double min_delay, double asymmetry) int max_samples, double min_delay, double asymmetry)
{ {
SRC_Instance result; SRC_Instance result;
@@ -253,6 +257,8 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_optio
result->index = n_sources; result->index = n_sources;
result->type = type; result->type = type;
result->authenticated = authenticated;
result->conf_sel_options = sel_options;
result->sel_options = sel_options; result->sel_options = sel_options;
result->active = 0; result->active = 0;
@@ -261,6 +267,8 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_optio
n_sources++; n_sources++;
update_sel_options();
return result; return result;
} }
@@ -285,6 +293,8 @@ void SRC_DestroyInstance(SRC_Instance instance)
--n_sources; --n_sources;
Free(instance); Free(instance);
update_sel_options();
/* If this was the previous reference source, we have to reselect! */ /* If this was the previous reference source, we have to reselect! */
if (selected_source_index == dead_index) if (selected_source_index == dead_index)
SRC_ReselectSource(); SRC_ReselectSource();
@@ -306,6 +316,8 @@ SRC_ResetInstance(SRC_Instance instance)
instance->leap = LEAP_Unsynchronised; instance->leap = LEAP_Unsynchronised;
instance->leap_vote = 0; instance->leap_vote = 0;
memset(&instance->sel_info, 0, sizeof (instance->sel_info));
SST_ResetInstance(instance->stats); SST_ResetInstance(instance->stats);
} }
@@ -386,7 +398,7 @@ SRC_AccumulateSample(SRC_Instance inst, NTP_Sample *sample)
assert(initialised); assert(initialised);
DEBUG_LOG("ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d", DEBUG_LOG("src=%s ts=%s offset=%e delay=%e disp=%e stratum=%d",
source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset, source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset,
sample->root_delay, sample->root_dispersion, sample->stratum); sample->root_delay, sample->root_dispersion, sample->stratum);
@@ -427,7 +439,7 @@ special_mode_end(void)
if (!sources[i]->active) if (!sources[i]->active)
continue; continue;
/* Don't expect more updates than from an offline iburst NTP source */ /* Don't expect more updates than the initial burst of an NTP source */
if (sources[i]->reachability_size >= SOURCE_REACH_BITS - 1) if (sources[i]->reachability_size >= SOURCE_REACH_BITS - 1)
continue; continue;
@@ -482,6 +494,75 @@ SRC_ResetReachability(SRC_Instance inst)
/* ================================================== */ /* ================================================== */
static void
update_sel_options(void)
{
int options, auth_ntp_options, unauth_ntp_options, refclk_options;
int i, auth_ntp_sources, unauth_ntp_sources;
auth_ntp_sources = unauth_ntp_sources = 0;
for (i = 0; i < n_sources; i++) {
if (sources[i]->conf_sel_options & SRC_SELECT_NOSELECT)
continue;
if (sources[i]->type != SRC_NTP)
continue;
if (sources[i]->authenticated)
auth_ntp_sources++;
else
unauth_ntp_sources++;
}
auth_ntp_options = unauth_ntp_options = refclk_options = 0;
/* Determine which selection options need to be added to authenticated NTP
sources, unauthenticated NTP sources, and refclocks, to follow the
configured selection mode */
switch (CNF_GetAuthSelectMode()) {
case SRC_AUTHSELECT_IGNORE:
break;
case SRC_AUTHSELECT_MIX:
if (auth_ntp_sources > 0 && unauth_ntp_sources > 0)
auth_ntp_options = refclk_options = SRC_SELECT_REQUIRE | SRC_SELECT_TRUST;
break;
case SRC_AUTHSELECT_PREFER:
if (auth_ntp_sources > 0)
unauth_ntp_options = SRC_SELECT_NOSELECT;
break;
case SRC_AUTHSELECT_REQUIRE:
unauth_ntp_options = SRC_SELECT_NOSELECT;
break;
default:
assert(0);
}
for (i = 0; i < n_sources; i++) {
options = sources[i]->conf_sel_options;
if (options & SRC_SELECT_NOSELECT)
continue;
switch (sources[i]->type) {
case SRC_NTP:
options |= sources[i]->authenticated ? auth_ntp_options : unauth_ntp_options;
break;
case SRC_REFCLOCK:
options |= refclk_options;
break;
default:
assert(0);
}
if (sources[i]->sel_options != options) {
DEBUG_LOG("changing %s from %x to %x", source_to_string(sources[i]),
(unsigned int)sources[i]->sel_options, (unsigned int)options);
sources[i]->sel_options = options;
}
}
}
/* ================================================== */
static void static void
log_selection_message(const char *format, const char *arg) log_selection_message(const char *format, const char *arg)
{ {
@@ -492,6 +573,24 @@ log_selection_message(const char *format, const char *arg)
/* ================================================== */ /* ================================================== */
static void
log_selection_source(const char *format, SRC_Instance inst)
{
char buf[320], *name, *ntp_name;
name = source_to_string(inst);
ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : NULL;
if (ntp_name && strcmp(name, ntp_name) != 0)
snprintf(buf, sizeof (buf), "%s (%s)", name, ntp_name);
else
snprintf(buf, sizeof (buf), "%s", name);
log_selection_message(format, buf);
}
/* ================================================== */
static int static int
compare_sort_elements(const void *a, const void *b) compare_sort_elements(const void *a, const void *b)
{ {
@@ -529,6 +628,20 @@ source_to_string(SRC_Instance inst)
/* ================================================== */ /* ================================================== */
static void
mark_source(SRC_Instance inst, SRC_Status status)
{
inst->status = status;
DEBUG_LOG("%s status=%d options=%x reach=%o/%d updates=%d distant=%d leap=%d vote=%d lo=%f hi=%f",
source_to_string(inst), (int)inst->status, (unsigned int)inst->sel_options,
(unsigned int)inst->reachability, inst->reachability_size, inst->updates,
inst->distant, (int)inst->leap, inst->leap_vote,
inst->sel_info.lo_limit, inst->sel_info.hi_limit);
}
/* ================================================== */
static void static void
mark_ok_sources(SRC_Status status) mark_ok_sources(SRC_Status status)
{ {
@@ -537,7 +650,7 @@ mark_ok_sources(SRC_Status status)
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
if (sources[i]->status != SRC_OK) if (sources[i]->status != SRC_OK)
continue; continue;
sources[i]->status = status; mark_source(sources[i], status);
} }
} }
@@ -588,12 +701,12 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
} }
if (sources[index]->distant) { if (sources[index]->distant) {
sources[index]->status = SRC_DISTANT; mark_source(sources[index], SRC_DISTANT);
continue; continue;
} }
if (sources[index]->status == SRC_OK) if (sources[index]->status == SRC_OK)
sources[index]->status = SRC_UNSELECTED; mark_source(sources[index], SRC_UNSELECTED);
elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time); elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time);
src_offset += elapsed * src_frequency; src_offset += elapsed * src_frequency;
@@ -642,12 +755,13 @@ SRC_SelectSource(SRC_Instance updated_inst)
struct timespec now, ref_time; struct timespec now, ref_time;
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source; int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source;
int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach; int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach;
int depth, best_depth, trust_depth, best_trust_depth; int depth, best_depth, trust_depth, best_trust_depth, n_sel_trust_sources;
int combined, stratum, min_stratum, max_score_index; int combined, stratum, min_stratum, max_score_index;
int orphan_stratum, orphan_source; int orphan_stratum, orphan_source;
double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew; double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
double src_root_delay, src_root_dispersion; double src_root_delay, src_root_dispersion;
double best_lo, best_hi, distance, sel_src_distance, max_score; double best_lo, best_hi, distance, sel_src_distance, max_score;
double best_trust_lo, best_trust_hi;
double first_sample_ago, max_reach_sample_ago; double first_sample_ago, max_reach_sample_ago;
NTP_Leap leap_status; NTP_Leap leap_status;
@@ -669,7 +783,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Step 1 - build intervals about each source */ /* Step 1 - build intervals about each source */
n_endpoints = 0; n_endpoints = 0;
n_sel_sources = 0; n_sel_sources = n_sel_trust_sources = 0;
n_badstats_sources = 0; n_badstats_sources = 0;
sel_req_source = 0; sel_req_source = 0;
max_sel_reach = max_badstat_reach = 0; max_sel_reach = max_badstat_reach = 0;
@@ -679,6 +793,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
assert(sources[i]->status != SRC_OK); assert(sources[i]->status != SRC_OK);
/* Don't allow the source to vote on leap seconds unless it's selectable */
sources[i]->leap_vote = 0;
/* If some sources are specified with the require option, at least one /* If some sources are specified with the require option, at least one
of them will have to be selectable in order to update the clock */ of them will have to be selectable in order to update the clock */
if (sources[i]->sel_options & SRC_SELECT_REQUIRE) if (sources[i]->sel_options & SRC_SELECT_REQUIRE)
@@ -686,7 +803,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Ignore sources which were added with the noselect option */ /* Ignore sources which were added with the noselect option */
if (sources[i]->sel_options & SRC_SELECT_NOSELECT) { if (sources[i]->sel_options & SRC_SELECT_NOSELECT) {
sources[i]->status = SRC_UNSELECTABLE; mark_source(sources[i], SRC_UNSELECTABLE);
continue; continue;
} }
@@ -698,7 +815,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (!si->select_ok) { if (!si->select_ok) {
++n_badstats_sources; ++n_badstats_sources;
sources[i]->status = SRC_BAD_STATS; mark_source(sources[i], SRC_BAD_STATS);
if (max_badstat_reach < sources[i]->reachability) if (max_badstat_reach < sources[i]->reachability)
max_badstat_reach = sources[i]->reachability; max_badstat_reach = sources[i]->reachability;
continue; continue;
@@ -714,20 +831,20 @@ SRC_SelectSource(SRC_Instance updated_inst)
si->hi_limit += extra_disp; si->hi_limit += extra_disp;
} }
/* Require the root distance to be below the allowed maximum */ /* Require the root distance to be below the allowed maximum and the
if (si->root_distance > max_distance) { endpoints to be in the right order (i.e. a non-negative distance) */
sources[i]->status = SRC_BAD_DISTANCE; if (!(si->root_distance <= max_distance && si->lo_limit <= si->hi_limit)) {
mark_source(sources[i], SRC_BAD_DISTANCE);
continue; continue;
} }
/* And the same applies for the estimated standard deviation */ /* And the same applies for the estimated standard deviation */
if (si->std_dev > max_jitter) { if (si->std_dev > max_jitter) {
sources[i]->status = SRC_JITTERY; mark_source(sources[i], SRC_JITTERY);
continue; continue;
} }
sources[i]->status = SRC_OK; /* For now */ sources[i]->status = SRC_OK; /* For now */
sources[i]->leap_vote = 0;
if (sources[i]->reachability && max_reach_sample_ago < first_sample_ago) if (sources[i]->reachability && max_reach_sample_ago < first_sample_ago)
max_reach_sample_ago = first_sample_ago; max_reach_sample_ago = first_sample_ago;
@@ -752,7 +869,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
can still be selected if its newest sample is not older than the oldest can still be selected if its newest sample is not older than the oldest
sample from reachable sources. */ sample from reachable sources. */
if (!sources[i]->reachability && max_reach_sample_ago < si->last_sample_ago) { if (!sources[i]->reachability && max_reach_sample_ago < si->last_sample_ago) {
sources[i]->status = SRC_STALE; mark_source(sources[i], SRC_STALE);
continue; continue;
} }
@@ -774,7 +891,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
local unsychronised time and others are synchronised to it. */ local unsychronised time and others are synchronised to it. */
if (si->stratum >= orphan_stratum && sources[i]->type == SRC_NTP) { if (si->stratum >= orphan_stratum && sources[i]->type == SRC_NTP) {
sources[i]->status = SRC_ORPHAN; mark_source(sources[i], SRC_ORPHAN);
if (si->stratum == orphan_stratum && sources[i]->reachability && if (si->stratum == orphan_stratum && sources[i]->reachability &&
(orphan_source == INVALID_SOURCE || (orphan_source == INVALID_SOURCE ||
@@ -804,6 +921,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (sources[i]->status != SRC_OK) if (sources[i]->status != SRC_OK)
continue; continue;
if (sources[i]->sel_options & SRC_SELECT_TRUST)
n_sel_trust_sources++;
si = &sources[i]->sel_info; si = &sources[i]->sel_info;
j1 = n_endpoints; j1 = n_endpoints;
@@ -820,7 +940,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_endpoints += 2; n_endpoints += 2;
} }
DEBUG_LOG("badstat=%d sel=%d badstat_reach=%x sel_reach=%x size=%d max_reach_ago=%f", DEBUG_LOG("badstat=%d sel=%d badstat_reach=%o sel_reach=%o size=%d max_reach_ago=%f",
n_badstats_sources, n_sel_sources, (unsigned int)max_badstat_reach, n_badstats_sources, n_sel_sources, (unsigned int)max_badstat_reach,
(unsigned int)max_sel_reach, max_sel_reach_size, max_reach_sample_ago); (unsigned int)max_sel_reach, max_sel_reach_size, max_reach_sample_ago);
@@ -876,7 +996,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
trust_depth = best_trust_depth = 0; trust_depth = best_trust_depth = 0;
depth = best_depth = 0; depth = best_depth = 0;
best_lo = best_hi = 0.0; best_lo = best_hi = best_trust_lo = best_trust_hi = 0.0;
for (i = 0; i < n_endpoints; i++) { for (i = 0; i < n_endpoints; i++) {
switch (sort_list[i].tag) { switch (sort_list[i].tag) {
@@ -886,14 +1006,20 @@ SRC_SelectSource(SRC_Instance updated_inst)
trust_depth++; trust_depth++;
if (trust_depth > best_trust_depth || if (trust_depth > best_trust_depth ||
(trust_depth == best_trust_depth && depth > best_depth)) { (trust_depth == best_trust_depth && depth > best_depth)) {
best_trust_depth = trust_depth; if (trust_depth > best_trust_depth) {
best_trust_depth = trust_depth;
best_trust_lo = sort_list[i].offset;
}
best_depth = depth; best_depth = depth;
best_lo = sort_list[i].offset; best_lo = sort_list[i].offset;
} }
break; break;
case HIGH: case HIGH:
if (trust_depth == best_trust_depth && depth == best_depth) if (trust_depth == best_trust_depth) {
best_hi = sort_list[i].offset; if (depth == best_depth)
best_hi = sort_list[i].offset;
best_trust_hi = sort_list[i].offset;
}
if (sources[sort_list[i].index]->sel_options & SRC_SELECT_TRUST) if (sources[sort_list[i].index]->sel_options & SRC_SELECT_TRUST)
trust_depth--; trust_depth--;
depth--; depth--;
@@ -901,11 +1027,16 @@ SRC_SelectSource(SRC_Instance updated_inst)
default: default:
assert(0); assert(0);
} }
assert(trust_depth <= depth);
assert(trust_depth >= 0);
} }
if (best_depth <= n_sel_sources / 2 && !best_trust_depth) { assert(depth == 0 && trust_depth == 0);
/* Could not even get half the reachable sources to agree and there assert(2 * n_sel_sources == n_endpoints);
are no trusted sources - clearly we can't synchronise */
if ((best_trust_depth == 0 && best_depth <= n_sel_sources / 2) ||
(best_trust_depth > 0 && best_trust_depth <= n_sel_trust_sources / 2)) {
/* Could not even get half the reachable (trusted) sources to agree */
if (selected_source_index != INVALID_SOURCE) { if (selected_source_index != INVALID_SOURCE) {
log_selection_message("Can't synchronise: no majority", NULL); log_selection_message("Can't synchronise: no majority", NULL);
@@ -932,24 +1063,28 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue; continue;
/* Check if source's interval contains the best interval, or is wholly /* Check if source's interval contains the best interval, or is wholly
contained within it. If there are any trusted sources the first contained within it. If there are any trusted sources, other sources
condition is applied only to them to not allow non-trusted sources to are required to be wholly contained within the best interval of the
move the final offset outside the interval. */ trusted sources to not allow non-trusted sources to move the final
if (((!best_trust_depth || sources[i]->sel_options & SRC_SELECT_TRUST) && offset outside the trusted interval. */
sources[i]->sel_info.lo_limit <= best_lo && if ((sources[i]->sel_info.lo_limit <= best_lo &&
sources[i]->sel_info.hi_limit >= best_hi) || sources[i]->sel_info.hi_limit >= best_hi) ||
(sources[i]->sel_info.lo_limit >= best_lo && (sources[i]->sel_info.lo_limit >= best_lo &&
sources[i]->sel_info.hi_limit <= best_hi)) { sources[i]->sel_info.hi_limit <= best_hi)) {
if (!(best_trust_depth == 0 || (sources[i]->sel_options & SRC_SELECT_TRUST) ||
(sources[i]->sel_info.lo_limit >= best_trust_lo &&
sources[i]->sel_info.hi_limit <= best_trust_hi))) {
mark_source(sources[i], SRC_UNTRUSTED);
continue;
}
sel_sources[n_sel_sources++] = i; sel_sources[n_sel_sources++] = i;
if (sources[i]->sel_options & SRC_SELECT_REQUIRE) if (sources[i]->sel_options & SRC_SELECT_REQUIRE)
sel_req_source = 0; sel_req_source = 0;
} else if (sources[i]->sel_info.lo_limit <= best_lo &&
sources[i]->sel_info.hi_limit >= best_hi) {
sources[i]->status = SRC_UNTRUSTED;
} else { } else {
sources[i]->status = SRC_FALSETICKER; mark_source(sources[i], SRC_FALSETICKER);
} }
} }
@@ -982,7 +1117,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (i < n_sel_sources) { if (i < n_sel_sources) {
for (i = j = 0; i < n_sel_sources; i++) { for (i = j = 0; i < n_sel_sources; i++) {
if (!(sources[sel_sources[i]]->sel_options & SRC_SELECT_PREFER)) if (!(sources[sel_sources[i]]->sel_options & SRC_SELECT_PREFER))
sources[sel_sources[i]]->status = SRC_NONPREFERRED; mark_source(sources[sel_sources[i]], SRC_NONPREFERRED);
else else
sel_sources[j++] = sel_sources[i]; sel_sources[j++] = sel_sources[i];
} }
@@ -1046,10 +1181,8 @@ SRC_SelectSource(SRC_Instance updated_inst)
sources[i]->sel_score = 1.0 / distance; sources[i]->sel_score = 1.0 / distance;
} }
DEBUG_LOG("select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%u dist=%f", DEBUG_LOG("%s score=%f dist=%f",
sources[i]->sel_score, sources[i]->ref_id, source_to_string(sources[i]), sources[i]->sel_score, distance);
updated_inst ? updated_inst->ref_id : 0,
sources[i]->status, distance);
if (max_score < sources[i]->sel_score) { if (max_score < sources[i]->sel_score) {
max_score = sources[i]->sel_score; max_score = sources[i]->sel_score;
@@ -1070,13 +1203,11 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (sources[max_score_index]->updates == 0) { if (sources[max_score_index]->updates == 0) {
selected_source_index = INVALID_SOURCE; selected_source_index = INVALID_SOURCE;
mark_ok_sources(SRC_WAITS_UPDATE); mark_ok_sources(SRC_WAITS_UPDATE);
DEBUG_LOG("best source has no updates");
return; return;
} }
selected_source_index = max_score_index; selected_source_index = max_score_index;
log_selection_message("Selected source %s", log_selection_source("Selected source %s", sources[selected_source_index]);
source_to_string(sources[selected_source_index]));
/* New source has been selected, reset all scores */ /* New source has been selected, reset all scores */
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
@@ -1085,7 +1216,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
} }
} }
sources[selected_source_index]->status = SRC_SELECTED; mark_source(sources[selected_source_index], SRC_SELECTED);
/* Don't update reference when the selected source has no new samples */ /* Don't update reference when the selected source has no new samples */
@@ -1095,8 +1226,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
for (i = 0; i < n_sel_sources; i++) { for (i = 0; i < n_sel_sources; i++) {
index = sel_sources[i]; index = sel_sources[i];
if (sources[index]->status == SRC_OK) if (sources[index]->status == SRC_OK)
sources[index]->status = sources[index]->distant ? mark_source(sources[index], sources[index]->distant ? SRC_DISTANT : SRC_UNSELECTED);
SRC_DISTANT : SRC_UNSELECTED;
} }
return; return;
} }
@@ -1196,9 +1326,8 @@ FILE *open_dumpfile(SRC_Instance inst, char mode)
char filename[64], *dumpdir; char filename[64], *dumpdir;
dumpdir = CNF_GetDumpDir(); dumpdir = CNF_GetDumpDir();
if (dumpdir[0] == '\0') { if (!dumpdir)
return NULL; return NULL;
}
/* Include IP address in the name for NTP sources, or reference ID in hex */ /* Include IP address in the name for NTP sources, or reference ID in hex */
if (inst->type == SRC_NTP && UTI_IsIPReal(inst->ip_addr)) if (inst->type == SRC_NTP && UTI_IsIPReal(inst->ip_addr))
@@ -1262,7 +1391,7 @@ SRC_RemoveDumpFiles(void)
size_t i; size_t i;
dumpdir = CNF_GetDumpDir(); dumpdir = CNF_GetDumpDir();
if (dumpdir[0] == '\0' || if (!dumpdir ||
snprintf(pattern, sizeof (pattern), "%s/*.dat", dumpdir) >= sizeof (pattern)) snprintf(pattern, sizeof (pattern), "%s/*.dat", dumpdir) >= sizeof (pattern))
return; return;
@@ -1370,26 +1499,24 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now)
case SRC_JITTERY: case SRC_JITTERY:
report->state = RPT_JITTERY; report->state = RPT_JITTERY;
break; break;
case SRC_UNTRUSTED:
case SRC_WAITS_SOURCES: case SRC_WAITS_SOURCES:
case SRC_NONPREFERRED: case SRC_NONPREFERRED:
case SRC_WAITS_UPDATE: case SRC_WAITS_UPDATE:
case SRC_DISTANT: case SRC_DISTANT:
case SRC_OUTLIER: case SRC_OUTLIER:
report->state = RPT_OUTLIER; report->state = RPT_SELECTABLE;
break; break;
case SRC_UNSELECTED: case SRC_UNSELECTED:
report->state = RPT_CANDIDATE; report->state = RPT_UNSELECTED;
break; break;
case SRC_SELECTED: case SRC_SELECTED:
report->state = RPT_SYNC; report->state = RPT_SELECTED;
break; break;
default: default:
report->state = RPT_UNREACH; report->state = RPT_NONSELECTABLE;
break; break;
} }
report->sel_options = src->sel_options;
report->reachability = src->reachability; report->reachability = src->reachability;
/* Call stats module to fill out estimates */ /* Call stats module to fill out estimates */
@@ -1423,6 +1550,78 @@ SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec
/* ================================================== */ /* ================================================== */
static char
get_status_char(SRC_Status status)
{
switch (status) {
case SRC_UNSELECTABLE:
return 'N';
case SRC_BAD_STATS:
return 'M';
case SRC_BAD_DISTANCE:
return 'd';
case SRC_JITTERY:
return '~';
case SRC_WAITS_STATS:
return 'w';
case SRC_STALE:
return 'S';
case SRC_ORPHAN:
return 'O';
case SRC_UNTRUSTED:
return 'T';
case SRC_FALSETICKER:
return 'x';
case SRC_WAITS_SOURCES:
return 'W';
case SRC_NONPREFERRED:
return 'P';
case SRC_WAITS_UPDATE:
return 'U';
case SRC_DISTANT:
return 'D';
case SRC_OUTLIER:
return 'L';
case SRC_UNSELECTED:
return '+';
case SRC_SELECTED:
return '*';
default:
return '?';
}
}
/* ================================================== */
int
SRC_GetSelectReport(int index, RPT_SelectReport *report)
{
SRC_Instance inst;
if (index >= n_sources || index < 0)
return 0;
inst = sources[index];
report->ref_id = inst->ref_id;
if (inst->ip_addr)
report->ip_addr = *inst->ip_addr;
else
report->ip_addr.family = IPADDR_UNSPEC;
report->state_char = get_status_char(inst->status);
report->authentication = inst->authenticated;
report->conf_options = inst->conf_sel_options;
report->eff_options = inst->sel_options;
report->last_sample_ago = inst->sel_info.last_sample_ago;
report->score = inst->sel_score;
report->lo_limit = inst->sel_info.lo_limit;
report->hi_limit = inst->sel_info.hi_limit;
return 1;
}
/* ================================================== */
SRC_Type SRC_Type
SRC_GetType(int index) SRC_GetType(int index)
{ {

View File

@@ -51,6 +51,14 @@ extern void SRC_Initialise(void);
/* Finalisation function */ /* Finalisation function */
extern void SRC_Finalise(void); extern void SRC_Finalise(void);
/* Modes for selecting NTP sources based on their authentication status */
typedef enum {
SRC_AUTHSELECT_IGNORE,
SRC_AUTHSELECT_MIX,
SRC_AUTHSELECT_PREFER,
SRC_AUTHSELECT_REQUIRE,
} SRC_AuthSelectMode;
typedef enum { typedef enum {
SRC_NTP, /* NTP client/peer */ SRC_NTP, /* NTP client/peer */
SRC_REFCLOCK /* Rerefence clock */ SRC_REFCLOCK /* Rerefence clock */
@@ -59,9 +67,9 @@ typedef enum {
/* Function to create a new instance. This would be called by one of /* Function to create a new instance. This would be called by one of
the individual source-type instance creation routines. */ the individual source-type instance creation routines. */
extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options, extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated,
IPAddr *addr, int min_samples, int max_samples, int sel_options, IPAddr *addr, int min_samples,
double min_delay, double asymmetry); int max_samples, double min_delay, double asymmetry);
/* Function to get rid of a source when it is being unconfigured. /* Function to get rid of a source when it is being unconfigured.
This may cause the current reference source to be reselected, if this This may cause the current reference source to be reselected, if this
@@ -122,9 +130,10 @@ extern int SRC_IsSyncPeer(SRC_Instance inst);
extern int SRC_IsReachable(SRC_Instance inst); extern int SRC_IsReachable(SRC_Instance inst);
extern int SRC_ReadNumberOfSources(void); extern int SRC_ReadNumberOfSources(void);
extern int SRC_ActiveSources(void); extern int SRC_ActiveSources(void);
extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now);
extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now);
extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now); extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now);
extern int SRC_GetSelectReport(int index, RPT_SelectReport *report);
extern SRC_Type SRC_GetType(int index); extern SRC_Type SRC_GetType(int index);

View File

@@ -76,7 +76,7 @@ typedef struct {
#define SRC_DEFAULT_MINSAMPLES (-1) #define SRC_DEFAULT_MINSAMPLES (-1)
#define SRC_DEFAULT_MAXSAMPLES (-1) #define SRC_DEFAULT_MAXSAMPLES (-1)
#define SRC_DEFAULT_ASYMMETRY 1.0 #define SRC_DEFAULT_ASYMMETRY 1.0
#define SRC_DEFAULT_NTSPORT 11443 #define SRC_DEFAULT_NTSPORT 4460
#define INACTIVE_AUTHKEY 0 #define INACTIVE_AUTHKEY 0
/* Flags for source selection */ /* Flags for source selection */

56
stubs.c
View File

@@ -49,7 +49,7 @@
#include "sched.h" #include "sched.h"
#include "util.h" #include "util.h"
#ifndef FEAT_ASYNCDNS #if defined(FEAT_NTP) && !defined(FEAT_ASYNCDNS)
/* This is a blocking implementation used when asynchronous resolving is not available */ /* This is a blocking implementation used when asynchronous resolving is not available */
@@ -112,7 +112,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
#ifndef FEAT_CMDMON #ifndef FEAT_CMDMON
void void
CAM_Initialise(int family) CAM_Initialise(void)
{ {
} }
@@ -147,7 +147,7 @@ MNL_Finalise(void)
#ifndef FEAT_NTP #ifndef FEAT_NTP
void void
NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval) NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval)
{ {
} }
@@ -174,7 +174,7 @@ NCR_CheckAccessRestriction(IPAddr *ip_addr)
} }
void void
NIO_Initialise(int family) NIO_Initialise(void)
{ {
} }
@@ -194,23 +194,30 @@ NSR_Finalise(void)
} }
NSR_Status NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params) NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{ {
return NSR_TooManySources; return NSR_TooManySources;
} }
NSR_Status NSR_Status
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params) NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{ {
return NSR_TooManySources; return NSR_TooManySources;
} }
NSR_Status NSR_Status
NSR_RemoveSource(NTP_Remote_Address *remote_addr) NSR_RemoveSource(IPAddr *address)
{ {
return NSR_NoSuchSource; return NSR_NoSuchSource;
} }
void
NSR_RemoveSourcesById(uint32_t conf_id)
{
}
void void
NSR_RemoveAllSources(void) NSR_RemoveAllSources(void)
{ {
@@ -319,6 +326,12 @@ NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
memset(report, 0, sizeof (*report)); memset(report, 0, sizeof (*report));
} }
int
NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report)
{
return 0;
}
int int
NSR_GetNTPReport(RPT_NTPReport *report) NSR_GetNTPReport(RPT_NTPReport *report)
{ {
@@ -414,12 +427,6 @@ NSD_Finalise(void)
{ {
} }
int
NSD_GetAuthDelay(uint32_t key_id)
{
return 0;
}
int int
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info, NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr) NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
@@ -431,21 +438,20 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
#ifndef HAVE_CMAC #ifndef HAVE_CMAC
unsigned int int
CMC_GetKeyLength(const char *cipher) CMC_GetKeyLength(CMC_Algorithm algorithm)
{ {
return 0; return 0;
} }
CMC_Instance CMC_Instance
CMC_CreateInstance(const char *cipher, const unsigned char *key, unsigned int length) CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, int length)
{ {
return NULL; return NULL;
} }
unsigned int int
CMC_Hash(CMC_Instance inst, const unsigned char *in, unsigned int in_len, CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len)
unsigned char *out, unsigned int out_len)
{ {
return 0; return 0;
} }
@@ -504,14 +510,16 @@ NNC_PrepareForAuth(NNC_Instance inst)
int int
NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info) NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info)
{ {
DEBUG_LOG("NTS support disabled"); static int logged = 0;
LOG(logged ? LOGS_DEBUG : LOGS_WARN, "Missing NTS support");
logged = 1;
return 0; return 0;
} }
int int
NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info) NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info)
{ {
DEBUG_LOG("NTS support disabled");
return 0; return 0;
} }
@@ -526,17 +534,17 @@ NNC_DumpData(NNC_Instance inst)
} }
void void
NKC_Initialise(void) NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report)
{ {
} }
void void
NKC_Finalise(void) NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level)
{ {
} }
void void
NKS_Initialise(int scfilter_level) NKS_Initialise(void)
{ {
} }

View File

@@ -417,6 +417,8 @@ SYS_Generic_Finalise(void)
LCL_ReadRawTime(&now); LCL_ReadRawTime(&now);
stop_fastslew(&now); stop_fastslew(&now);
LCL_RemoveParameterChangeHandler(handle_step, NULL);
} }
/* ================================================== */ /* ================================================== */

View File

@@ -437,10 +437,16 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control)
UTI_DropRoot(uid, gid); UTI_DropRoot(uid, gid);
/* Keep CAP_NET_BIND_SERVICE only if a server NTP port can be opened /* Keep CAP_NET_BIND_SERVICE if the NTP server sockets may need to be bound
and keep CAP_SYS_TIME only if the clock control is enabled */ to a privileged port.
if (snprintf(cap_text, sizeof (cap_text), "%s %s", Keep CAP_NET_RAW if an NTP socket may need to be bound to a device on
CNF_GetNTPPort() ? "cap_net_bind_service=ep" : "", kernels before 5.7.
Keep CAP_SYS_TIME if the clock control is enabled. */
if (snprintf(cap_text, sizeof (cap_text), "%s %s %s",
(CNF_GetNTPPort() > 0 && CNF_GetNTPPort() < 1024) ?
"cap_net_bind_service=ep" : "",
(CNF_GetBindNtpInterface() || CNF_GetBindAcquisitionInterface()) &&
!SYS_Linux_CheckKernelVersion(5, 7) ? "cap_net_raw=ep" : "",
clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text)) clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text))
assert(0); assert(0);
@@ -478,36 +484,123 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context)
{ {
const int syscalls[] = { const int syscalls[] = {
/* Clock */ /* Clock */
SCMP_SYS(adjtimex), SCMP_SYS(clock_adjtime), SCMP_SYS(clock_gettime), SCMP_SYS(adjtimex),
SCMP_SYS(gettimeofday), SCMP_SYS(settimeofday), SCMP_SYS(time), SCMP_SYS(clock_adjtime),
#ifdef __NR_clock_adjtime64
SCMP_SYS(clock_adjtime64),
#endif
SCMP_SYS(clock_gettime),
#ifdef __NR_clock_gettime64
SCMP_SYS(clock_gettime64),
#endif
SCMP_SYS(gettimeofday),
SCMP_SYS(settimeofday),
SCMP_SYS(time),
/* Process */ /* Process */
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getpid), SCMP_SYS(clone),
SCMP_SYS(getrlimit), SCMP_SYS(getuid), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn), SCMP_SYS(exit),
SCMP_SYS(rt_sigprocmask), SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn), SCMP_SYS(exit_group),
SCMP_SYS(wait4), SCMP_SYS(waitpid), SCMP_SYS(getpid),
SCMP_SYS(getrlimit),
SCMP_SYS(getuid),
SCMP_SYS(rt_sigaction),
SCMP_SYS(rt_sigreturn),
SCMP_SYS(rt_sigprocmask),
SCMP_SYS(set_tid_address),
SCMP_SYS(sigreturn),
SCMP_SYS(wait4),
SCMP_SYS(waitpid),
/* Memory */ /* Memory */
SCMP_SYS(brk), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(mmap2), SCMP_SYS(brk),
SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt), SCMP_SYS(madvise),
SCMP_SYS(mmap),
SCMP_SYS(mmap2),
SCMP_SYS(mprotect),
SCMP_SYS(mremap),
SCMP_SYS(munmap),
SCMP_SYS(shmdt),
/* Filesystem */ /* Filesystem */
SCMP_SYS(_llseek), SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown), SCMP_SYS(_llseek),
SCMP_SYS(chown32), SCMP_SYS(faccessat), SCMP_SYS(fchmodat), SCMP_SYS(fchownat), SCMP_SYS(access),
SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(getdents), SCMP_SYS(getdents64), SCMP_SYS(chmod),
SCMP_SYS(lseek), SCMP_SYS(newfstatat), SCMP_SYS(rename), SCMP_SYS(renameat), SCMP_SYS(chown),
SCMP_SYS(renameat2), SCMP_SYS(stat), SCMP_SYS(stat64), SCMP_SYS(statfs), SCMP_SYS(chown32),
SCMP_SYS(statfs64), SCMP_SYS(unlink), SCMP_SYS(unlinkat), SCMP_SYS(faccessat),
SCMP_SYS(fchmodat),
SCMP_SYS(fchownat),
SCMP_SYS(fstat),
SCMP_SYS(fstat64),
SCMP_SYS(getdents),
SCMP_SYS(getdents64),
SCMP_SYS(lseek),
SCMP_SYS(lstat),
SCMP_SYS(lstat64),
SCMP_SYS(newfstatat),
SCMP_SYS(readlink),
SCMP_SYS(readlinkat),
SCMP_SYS(rename),
SCMP_SYS(renameat),
SCMP_SYS(renameat2),
SCMP_SYS(stat),
SCMP_SYS(stat64),
SCMP_SYS(statfs),
SCMP_SYS(statfs64),
SCMP_SYS(unlink),
SCMP_SYS(unlinkat),
/* Socket */ /* Socket */
SCMP_SYS(accept), SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname), SCMP_SYS(accept),
SCMP_SYS(getsockopt), SCMP_SYS(recv), SCMP_SYS(recvfrom), SCMP_SYS(bind),
SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg), SCMP_SYS(send), SCMP_SYS(sendmmsg), SCMP_SYS(connect),
SCMP_SYS(sendmsg), SCMP_SYS(sendto), SCMP_SYS(shutdown), SCMP_SYS(getsockname),
SCMP_SYS(getsockopt),
SCMP_SYS(recv),
SCMP_SYS(recvfrom),
SCMP_SYS(recvmmsg),
#ifdef __NR_recvmmsg_time64
SCMP_SYS(recvmmsg_time64),
#endif
SCMP_SYS(recvmsg),
SCMP_SYS(send),
SCMP_SYS(sendmmsg),
SCMP_SYS(sendmsg),
SCMP_SYS(sendto),
SCMP_SYS(shutdown),
/* TODO: check socketcall arguments */ /* TODO: check socketcall arguments */
SCMP_SYS(socketcall), SCMP_SYS(socketcall),
/* General I/O */ /* General I/O */
SCMP_SYS(_newselect), SCMP_SYS(close), SCMP_SYS(open), SCMP_SYS(openat), SCMP_SYS(pipe), SCMP_SYS(_newselect),
SCMP_SYS(pipe2), SCMP_SYS(poll), SCMP_SYS(ppoll), SCMP_SYS(pselect6), SCMP_SYS(read), SCMP_SYS(close),
SCMP_SYS(futex), SCMP_SYS(select), SCMP_SYS(set_robust_list), SCMP_SYS(write), SCMP_SYS(open),
SCMP_SYS(openat),
SCMP_SYS(pipe),
SCMP_SYS(pipe2),
SCMP_SYS(poll),
SCMP_SYS(ppoll),
#ifdef __NR_ppoll_time64
SCMP_SYS(ppoll_time64),
#endif
SCMP_SYS(pselect6),
#ifdef __NR_pselect6_time64
SCMP_SYS(pselect6_time64),
#endif
SCMP_SYS(read),
SCMP_SYS(futex),
#ifdef __NR_futex_time64
SCMP_SYS(futex_time64),
#endif
SCMP_SYS(select),
SCMP_SYS(set_robust_list),
SCMP_SYS(write),
/* Miscellaneous */ /* Miscellaneous */
SCMP_SYS(getrandom), SCMP_SYS(sysinfo), SCMP_SYS(uname), SCMP_SYS(getrandom),
SCMP_SYS(sysinfo),
SCMP_SYS(uname),
}; };
const int socket_domains[] = { const int socket_domains[] = {

View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2001 * Copyright (C) Richard P. Curnow 1997-2001
* Copyright (C) J. Hannken-Illjes 2001 * Copyright (C) J. Hannken-Illjes 2001
* Copyright (C) Bryan Christianson 2015, 2017 * Copyright (C) Bryan Christianson 2015, 2017, 2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as

View File

@@ -68,6 +68,25 @@ static int sys_tai_offset;
/* ================================================== */ /* ================================================== */
static double
convert_timex_frequency(const struct timex *txc)
{
double freq_ppm;
freq_ppm = txc->freq / FREQ_SCALE;
#ifdef MACOSX
/* Temporary workaround for Apple bug treating freq as unsigned number */
if (freq_ppm > 32767) {
freq_ppm -= 65536;
}
#endif
return -freq_ppm;
}
/* ================================================== */
static double static double
read_frequency(void) read_frequency(void)
{ {
@@ -77,7 +96,7 @@ read_frequency(void)
SYS_Timex_Adjust(&txc, 0); SYS_Timex_Adjust(&txc, 0);
return txc.freq / -FREQ_SCALE; return convert_timex_frequency(&txc);
} }
/* ================================================== */ /* ================================================== */
@@ -92,7 +111,7 @@ set_frequency(double freq_ppm)
SYS_Timex_Adjust(&txc, 0); SYS_Timex_Adjust(&txc, 0);
return txc.freq / -FREQ_SCALE; return convert_timex_frequency(&txc);
} }
/* ================================================== */ /* ================================================== */

View File

@@ -17,12 +17,15 @@ for opts in \
"--disable-rtc" \ "--disable-rtc" \
"--disable-sechash" \ "--disable-sechash" \
"--disable-cmdmon" \ "--disable-cmdmon" \
"--disable-cmdmon --enable-scfilter" \
"--disable-ntp" \ "--disable-ntp" \
"--disable-ntp --enable-scfilter" \
"--disable-nts" \ "--disable-nts" \
"--disable-refclock" \ "--disable-refclock" \
"--disable-timestamping" \ "--disable-timestamping" \
"--disable-timestamping --disable-ntp" \ "--disable-timestamping --disable-ntp" \
"--disable-cmdmon --disable-ntp" \ "--disable-cmdmon --disable-ntp" \
"--disable-cmdmon --disable-ntp --enable-scfilter" \
"--disable-cmdmon --disable-refclock" \ "--disable-cmdmon --disable-refclock" \
"--disable-cmdmon --disable-ntp --disable-refclock" "--disable-cmdmon --disable-ntp --disable-refclock"
do do

View File

@@ -52,15 +52,15 @@ test_freqrange(void)
printf("freq range:\n"); printf("freq range:\n");
for (i = 0; i <= 1000; i += 50) { for (i = -1000; i <= 1000; i += 50) {
t.modes = MOD_FREQUENCY; t.modes = MOD_FREQUENCY;
t.freq = i << 16; t.freq = i * (1 << 16);
printf("%4d ppm => ", i); printf("%4d ppm => ", i);
if (try_ntpadjtime(&t) < 0) if (try_ntpadjtime(&t) < 0)
continue; continue;
printf("%4ld ppm : ", t.freq / (1 << 16)); printf("%4ld ppm : ", t.freq / (1 << 16));
printf("%s\n", t.freq == i << 16 ? "ok" : "fail"); printf("%s\n", t.freq == i * (1 << 16) ? "ok" : "fail");
} }
} }

View File

@@ -8,12 +8,17 @@ check_config_h 'FEAT_REFCLOCK 1' || test_skip
check_config_h 'FEAT_CMDMON 1' || test_skip check_config_h 'FEAT_CMDMON 1' || test_skip
refclock_jitter=$jitter refclock_jitter=$jitter
client_server_conf="
server node1.net1.clk
server 192.168.123.2"
client_conf=" client_conf="
refclock SHM 0 noselect refclock SHM 0 noselect
smoothtime 400 0.001 leaponly" smoothtime 400 0.001 leaponly"
chronyc_conf="activity chronyc_conf="activity
tracking tracking
sourcename 192.168.123.1
sourcename 192.168.123.2
sources sources
sourcestats sourcestats
manual list manual list
@@ -25,7 +30,7 @@ run_test || test_fail
check_chronyd_exit || test_fail check_chronyd_exit || test_fail
check_chronyc_output "^200 OK check_chronyc_output "^200 OK
1 sources online 2 sources online
0 sources offline 0 sources offline
0 sources doing burst \(return to online\) 0 sources doing burst \(return to online\)
0 sources doing burst \(return to offline\) 0 sources doing burst \(return to offline\)
@@ -43,14 +48,18 @@ Root delay : 0\.000...... seconds
Root dispersion : 0\.000...... seconds Root dispersion : 0\.000...... seconds
Update interval : [0-9]+\.. seconds Update interval : [0-9]+\.. seconds
Leap status : Normal Leap status : Normal
node1\.net1\.clk
192\.168\.123\.2
MS Name/IP address Stratum Poll Reach LastRx Last sample MS Name/IP address Stratum Poll Reach LastRx Last sample
=============================================================================== ===============================================================================
#\? SHM0 0 4 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s #\? SHM0 0 4 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
\^\* 192\.168\.123\.1 1 [67] 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s \^\* 192\.168\.123\.1 1 [67] 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
\^\? 192\.168\.123\.2 0 [0-9]+ 0 - \+0ns\[ \+0ns\] \+/- 0ns
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
============================================================================== ==============================================================================
SHM0 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s SHM0 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s 192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
192\.168\.123\.2 0 0 0 \+0\.000 2000\.000 \+0ns 4000ms
210 n_samples = 0 210 n_samples = 0
# Date Time\(UTC\) Slewed Original Residual # Date Time\(UTC\) Slewed Original Residual
======================================================= =======================================================
@@ -71,11 +80,22 @@ run_test || test_fail
check_chronyd_exit || test_fail check_chronyd_exit || test_fail
check_chronyc_output "^Reference ID : C0A87B01 \(node1\.net1\.clk\)" \ check_chronyc_output "^Reference ID : C0A87B01 \(node1\.net1\.clk\)" \
|| test_fail || test_fail
chronyc_options="-c"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^C0A87B01,192\.168\.123\.1,2,12623049..\..........,-?0\.0000.....,-?0\.000......,0\.000......,(99|100)\....,-?[0-9]\....,[0-9]\....,0\.000......,0\.000......,[0-9]+\..,Normal$" \
|| test_fail
chronyc_options=""
server_strata=0 server_strata=0
chronyc_start=0 chronyc_start=0
client_server_conf=""
client_conf="" client_conf=""
server_conf="server 192.168.123.1"
limit=1 limit=1
for chronyc_conf in \ for chronyc_conf in \
@@ -93,10 +113,14 @@ for chronyc_conf in \
"allow ::/0" \ "allow ::/0" \
"allow" \ "allow" \
"allow all 10/24" \ "allow all 10/24" \
"authdata" \
"burst 5/10" \ "burst 5/10" \
"burst 3/5 255.255.255.0/1.2.3.0" \ "burst 3/5 255.255.255.0/1.2.3.0" \
"burst 1/2 1.2.3.0/24" \ "burst 1/2 1.2.3.0/24" \
"clients" \ "clients" \
"clients -k" \
"clients -p 100" \
"clients -r" \
"cmdaccheck 1.2.3.4" \ "cmdaccheck 1.2.3.4" \
"cmdallow 1.2.3.4" \ "cmdallow 1.2.3.4" \
"cmdallow all 1.2.3.0/24" \ "cmdallow all 1.2.3.0/24" \
@@ -136,9 +160,11 @@ for chronyc_conf in \
"polltarget 1.2.3.4 10" \ "polltarget 1.2.3.4 10" \
"refresh" \ "refresh" \
"rekey" \ "rekey" \
"reload sources" \
"reselect" \ "reselect" \
"reselectdist 1e-3" \ "reselectdist 1e-3" \
"reset" \ "reset sources" \
"selectdata" \
"settime 16:30" \ "settime 16:30" \
"settime 16:30:05" \ "settime 16:30:05" \
"settime Nov 21, 2015 16:30:05" \ "settime Nov 21, 2015 16:30:05" \
@@ -151,7 +177,7 @@ for chronyc_conf in \
do do
run_test || test_fail run_test || test_fail
check_chronyd_exit || test_fail check_chronyd_exit || test_fail
check_chronyc_output "501 Not authorised" || test_fail check_chronyc_output "501 Not authorised$" || test_fail
done done
chronyc_conf="dns -n chronyc_conf="dns -n

View File

@@ -65,4 +65,28 @@ check_packet_interval || test_fail
check_source_selection && test_fail check_source_selection && test_fail
check_sync && test_fail check_sync && test_fail
cat > tmp/keys <<-EOF
1 MD5 HEX:1B81CBF88D4A73F2E8CE59647F6E5C1719B6CAF5
EOF
server_conf="keyfile tmp/keys"
client_server_conf="
server 192.168.123.1 key 1
server 192.168.123.2
server 192.168.123.3"
for authselectmode in require prefer mix ignore; do
client_conf="keyfile tmp/keys
authselectmode $authselectmode"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
if [ $authselectmode = ignore ]; then
check_sync || test_fail
else
check_sync && test_fail
fi
done
test_pass test_pass

View File

@@ -46,7 +46,7 @@ check_sync || test_fail
check_file_messages "20.*123\.1.* 111 111 1111" 75 80 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 "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 " 2 1 .* 4460 " 260 300 log.packets || test_fail
check_file_messages "." 6 6 ntskeys || test_fail check_file_messages "." 6 6 ntskeys || test_fail
rm -f tmp/measurements.log rm -f tmp/measurements.log
@@ -61,7 +61,7 @@ check_sync || test_fail
check_file_messages "20.*123\.1.* 111 111 1111" 99 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 "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 " 2 1 .* 4460 " 350 390 log.packets || test_fail
check_file_messages "." 6 6 ntskeys || test_fail check_file_messages "." 6 6 ntskeys || test_fail
check_file_messages "." 11 12 192.168.123.1.nts || test_fail check_file_messages "." 11 12 192.168.123.1.nts || test_fail
rm -f tmp/measurements.log rm -f tmp/measurements.log
@@ -80,8 +80,8 @@ 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 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 "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 " 2 1 .* 4460 " 6 10 log.packets || test_fail
check_file_messages "^9\.......e+03 2 1 .* 11443 " 6 10 log.packets || test_fail check_file_messages "^9\.......e+03 2 1 .* 4460 " 6 10 log.packets || test_fail
check_file_messages "." 6 6 ntskeys || test_fail check_file_messages "." 6 6 ntskeys || test_fail
check_file_messages "." 11 12 192.168.123.1.nts || test_fail check_file_messages "." 11 12 192.168.123.1.nts || test_fail
rm -f tmp/measurements.log rm -f tmp/measurements.log
@@ -95,6 +95,79 @@ check_source_selection && test_fail
check_sync && test_fail check_sync && test_fail
check_file_messages " 2 1 .* 123 " 0 0 log.packets || 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 check_file_messages " 2 1 .* 4460 " 10 20 log.packets || test_fail
export CLKNETSIM_START_DATE=$(date -d 'Jan 2 00:00:01 UTC 2010' +'%s')
client_conf="
nosystemcert
ntstrustedcerts tmp/server.crt"
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 .* 4460 " 10 20 log.packets || test_fail
check_log_messages "expired certificate" 4 4 || test_fail
client_conf+="
nocerttimecheck 1"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010' +'%s')
server_conf="
ntsserverkey tmp/server.key
ntsservercert tmp/server.crt
ntsprocesses 0
ntsrotate 0
ntsdumpdir tmp
ntsntpserver 192.168.123.2"
client_conf="
nosystemcert
ntstrustedcerts tmp/server.crt
ntsrefresh 500"
client_server_conf="server node1.net1.clk $client_server_options"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection && test_fail
check_sync && test_fail
check_file_messages " 2 1 .* 4460 " 50 100 log.packets || test_fail
check_file_messages " 2 2 .* 4460 " 0 0 log.packets || test_fail
check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 6 8 || test_fail
check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 6 8 || test_fail
servers=2
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_file_messages " 3 1 .* 4460 " 100 150 log.packets || test_fail
check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail
check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 1 1 || test_fail
check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 0 0 || test_fail
server_conf+="
ntsratelimit interval 12 burst 1 leak 4"
client_chronyd_options="-d -d"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection && test_fail
check_file_messages " 3 1 .* 4460 1 0 2" 25 50 log.packets || test_fail
check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail
check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 2 6 || test_fail
check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 1 6 || test_fail
test_pass test_pass

21
test/simulation/140-noclientlog Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
. ./test.common
test_start "noclientlog option"
server_conf="noclientlog"
client_server_options="xleave"
client_conf="
logdir tmp
log rawmeasurements"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_file_messages "111 111 1111.* 4B " 30 200 measurements.log || test_fail
check_file_messages "111 111 1111.* 4I " 0 0 measurements.log || test_fail
test_pass

View File

@@ -64,6 +64,7 @@ default_client_conf=""
default_chronyc_conf="" default_chronyc_conf=""
default_server_chronyd_options="" default_server_chronyd_options=""
default_client_chronyd_options="" default_client_chronyd_options=""
default_chronyc_options=""
default_time_max_limit=1e-3 default_time_max_limit=1e-3
default_freq_max_limit=5e-4 default_freq_max_limit=5e-4
@@ -276,6 +277,7 @@ check_chronyd_exit() {
grep -q 'chronyd exiting' tmp/log.$i && \ grep -q 'chronyd exiting' tmp/log.$i && \
! grep -q 'Adjustment.*exceeds.*exiting' tmp/log.$i && \ ! grep -q 'Adjustment.*exceeds.*exiting' tmp/log.$i && \
! grep -q 'Assertion.*failed' tmp/log.$i && \
test_ok || test_bad test_ok || test_bad
[ $? -eq 0 ] || ret=1 [ $? -eq 0 ] || ret=1
done done
@@ -503,7 +505,7 @@ run_test() {
echo "node${node}_start = $chronyc_start" >> tmp/conf echo "node${node}_start = $chronyc_start" >> tmp/conf
start_client $node chronyc "$chronyc_conf" "" \ start_client $node chronyc "$chronyc_conf" "" \
"$([ $dns -eq 0 ] && printf "%s" "-n") -h $(get_node_name $[$node - $clients])" && \ "$([ $dns -eq 0 ] && printf "%s" "-n") -h $(get_node_name $[$node - $clients]) $chronyc_options" && \
test_ok || test_error test_ok || test_error
[ $? -ne 0 ] && return 1 [ $? -ne 0 ] && return 1

View File

@@ -5,62 +5,150 @@
test_start "chronyc commands" test_start "chronyc commands"
start_chronyd || test_fail start_chronyd || test_fail
wait_for_sync || test_fail
for command in \ for command in \
"accheck 1.2.3.4" \ "allow 1.2.3.4" \
"delete $server" \ "deny 1.2.3.4" \
"add server $server" \ "cmddeny" \
"deny" \ "cmdallow" \
"allow" \
"burst 1/1" \
"clients" \
"cmdallow 1.2.3.4" \
"cmdaccheck 1.2.3.4" \
"cmddeny 1.2.3.4" \ "cmddeny 1.2.3.4" \
"cmdallow 1.2.3.4" \
"add server 127.123.1.1" \
"delete 127.123.1.1" \
"burst 1/1" \
"cyclelogs" \ "cyclelogs" \
"dfreq 1.0e-3" \ "dfreq 1.0e-3" \
"doffset -0.1" \ "doffset -0.1" \
"dump" \ "dump" \
"local off" \ "local off" \
"local" \ "local" \
"manual on" \ "maxdelay $server 1e-1" \
"settime now" \
"manual delete 0" \
"settime now" \
"manual reset" \
"manual off" \
"maxdelay $server 1e-2" \
"maxdelaydevratio $server 5.0" \ "maxdelaydevratio $server 5.0" \
"maxdelayratio $server 3.0" \ "maxdelayratio $server 3.0" \
"maxpoll $server 5" \ "maxpoll $server 5" \
"maxupdateskew $server 10.0" \ "maxupdateskew $server 10.0" \
"minpoll $server 3" \ "minpoll $server 3" \
"minstratum $server 1" \ "minstratum $server 1" \
"ntpdata $server" \
"offline" \ "offline" \
"online" \ "online" \
"onoffline" \ "onoffline" \
"polltarget $server 10" \ "polltarget $server 10" \
"refresh" \ "refresh" \
"rekey" \ "rekey" \
"reload sources" \
"reselect" \ "reselect" \
"reselectdist 1e-3" \ "reselectdist 1e-3" \
"serverstats" \ "reset sources" \
"smoothtime reset" \ "smoothtime reset" \
"smoothtime activate" \ "smoothtime activate" \
"shutdown" \
; do ; do
run_chronyc "$command" || test_fail run_chronyc "$command" || test_fail
check_chronyc_output "^200 OK$" || test_fail
done done
run_chronyc "accheck $server" || test_fail
check_chronyc_output "^208 Access allowed$" || test_fail
run_chronyc "accheck 1.2.3.4" || test_fail
check_chronyc_output "^209 Access denied$" || test_fail
run_chronyc "cmdaccheck 1.2.3.4" || test_fail
check_chronyc_output "^208 Access allowed$" || test_fail
run_chronyc "authdata" || test_fail
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
127\.0\.0\.1 - 0 0 0 - 0 0 0 0$" \
|| test_chronyc
run_chronyc "clients" || test_fail
check_chronyc_output "^Hostname NTP Drop Int IntL Last Cmd Drop Int Last
===============================================================================
127\.0\.0\.1 [1-9] 0 - - [0-9] 0 0 - -$" \
|| test_fail
run_chronyc "ntpdata $server" || test_fail
check_chronyc_output "^Remote address : 127\.0\.0\.1 \(7F000001\)
Remote port : [0-9]+
Local address : 127\.0\.0\.1 \(7F000001\)
Leap status : Normal
Version : 4
Mode : Server
Stratum : 10
Poll interval : -6 \(0 seconds\)
Precision : [0-9 +-]+ \(0\.[0-9]+ seconds\)
Root delay : 0\.000000 seconds
Root dispersion : 0\.000000 seconds
Reference ID : 7F7F0101 \(\)
Reference time : [A-Za-z0-9: ]+
Offset : [+-]0\.......... seconds
Peer delay : 0\.......... seconds
Peer dispersion : 0\.......... seconds
Response time : 0\.......... seconds
Jitter asymmetry: \+0\.00
NTP tests : 111 111 1110
Interleaved : No
Authenticated : No
TX timestamping : (Daemon|Kernel)
RX timestamping : (Daemon|Kernel)
Total TX : [0-9]+
Total RX : [0-9]+
Total valid RX : [0-9]+$" || test_fail
run_chronyc "selectdata" || test_fail
check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval +
====================================================================
M 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns$" || test_fail
run_chronyc "serverstats" || test_fail
check_chronyc_output "^NTP packets received : [0-9]+
NTP packets dropped : 0
Command packets received : [0-9]+
Command packets dropped : 0
Client log records dropped : 0
NTS-KE connections accepted: 0
NTS-KE connections dropped : 0
Authenticated NTP packets : 0$" || test_fail
run_chronyc "manual on" || test_fail
check_chronyc_output "^200 OK$" || test_fail
run_chronyc "settime now" || test_fail
check_chronyc_output "^200 OK
Clock was.*$" || test_fail
run_chronyc "manual delete 0" || test_fail
check_chronyc_output "^200 OK$" || test_fail
run_chronyc "settime now" || test_fail
check_chronyc_output "^200 OK
Clock was.*$" || test_fail
run_chronyc "manual list" || test_fail
check_chronyc_output "^210 n_samples = 1
# Date Time\(UTC\) Slewed Original Residual
=======================================================
0.*$" || test_fail
run_chronyc "manual reset" || test_fail
check_chronyc_output "^200 OK$" || test_fail
run_chronyc "manual off" || test_fail
check_chronyc_output "^200 OK$" || test_fail
run_chronyc "shutdown" || test_fail
check_chronyc_output "^200 OK$" || test_fail
stop_chronyd || test_fail stop_chronyd || test_fail
check_chronyd_messages || test_fail check_chronyd_messages || test_fail
start_chronyd || test_fail start_chronyd || test_fail
run_chronyc "makestep" && test_fail run_chronyc "makestep" && test_fail
check_chronyc_output "500 Failure" || test_fail check_chronyc_output "500 Failure" || test_fail
run_chronyc "trimrtc" && test_fail run_chronyc "trimrtc" && test_fail
check_chronyc_output "513 RTC driver not running" || test_fail check_chronyc_output "513 RTC driver not running" || test_fail
run_chronyc "writertc" && test_fail run_chronyc "writertc" && test_fail
check_chronyc_output "513 RTC driver not running" || test_fail check_chronyc_output "513 RTC driver not running" || test_fail

75
test/system/008-confload Executable file
View File

@@ -0,0 +1,75 @@
#!/usr/bin/env bash
. ./test.common
test_start "loading of configuration"
minimal_config=1
extra_chronyd_directives="
include $TEST_DIR/conf1.d/conf.1
confdir $TEST_DIR/conf1.d
confdir $TEST_DIR/conf2.d $TEST_DIR/conf3.d $TEST_DIR/conf4.d
sourcedir $TEST_DIR/conf5.d
include $TEST_DIR/conf1.d/conf.2"
mkdir $TEST_DIR/conf{1,2,3,4,5}.d
echo "server 127.123.1.1" > $TEST_DIR/conf1.d/conf.1
echo "server 127.123.1.2" > $TEST_DIR/conf1.d/conf.2
echo "server 127.123.1.3" > $TEST_DIR/conf1.d/3.conf
echo "server 127.123.1.4" > $TEST_DIR/conf1.d/4.conf
echo "server 127.123.2.2" > $TEST_DIR/conf2.d/2.conf
echo "server 127.123.2.3" > $TEST_DIR/conf2.d/3.conf
echo "server 127.123.3.1" > $TEST_DIR/conf3.d/1.conf
echo "server 127.123.3.2" > $TEST_DIR/conf3.d/2.conf
echo "server 127.123.3.3" > $TEST_DIR/conf3.d/3.conf
echo "server 127.123.4.1" > $TEST_DIR/conf4.d/1.conf
echo "server 127.123.4.2" > $TEST_DIR/conf4.d/2.conf
echo "server 127.123.4.3" > $TEST_DIR/conf4.d/3.conf
echo "server 127.123.4.4" > $TEST_DIR/conf4.d/4.conf
echo "server 127.123.5.1" > $TEST_DIR/conf5.d/1.sources
echo "server 127.123.5.2" > $TEST_DIR/conf5.d/2.sources
echo "server 127.123.5.3" > $TEST_DIR/conf5.d/3.sources
start_chronyd || test_fail
wait_for_sync || test_fail
run_chronyc "sources" || test_fail
check_chronyc_output "^[^=]*
=*
.. 127\.123\.1\.1 [^^]*
.. 127\.123\.1\.3 [^^]*
.. 127\.123\.1\.4 [^^]*
.. 127\.123\.3\.1 [^^]*
.. 127\.123\.2\.2 [^^]*
.. 127\.123\.2\.3 [^^]*
.. 127\.123\.4\.4 [^^]*
.. 127\.123\.1\.2 [^^]*
.. 127\.123\.5\.1 [^^]*
.. 127\.123\.5\.2 [^^]*
.. 127\.123\.5\.3 [^^]*$" || test_fail
rm $TEST_DIR/conf5.d/1.sources
echo "server 127.123.5.2 minpoll 7" > $TEST_DIR/conf5.d/2.sources
echo > $TEST_DIR/conf5.d/3.sources
echo "server 127.123.5.4" > $TEST_DIR/conf5.d/4.sources
run_chronyc "reload sources" || test_fail
run_chronyc "sources" || test_fail
check_chronyc_output "^[^=]*
=*
.. 127\.123\.1\.1 [^^]*
.. 127\.123\.1\.3 [^^]*
.. 127\.123\.1\.4 [^^]*
.. 127\.123\.3\.1 [^^]*
.. 127\.123\.2\.2 [^^]*
.. 127\.123\.2\.3 [^^]*
.. 127\.123\.4\.4 [^^]*
.. 127\.123\.1\.2 *0 6 [^^]*
.. 127\.123\.5\.2 *0 7 [^^]*
.. 127\.123\.5\.4 [^^]*$" || test_fail
stop_chronyd || test_fail
test_pass

24
test/system/009-binddevice Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
. ./test.common
[ "$(uname -s)" = "Linux" ] || test_skip "non-Linux system"
test_start "binddevice directives"
extra_chronyd_directives="
binddevice lo
bindacqdevice lo
bindcmddevice lo"
start_chronyd || test_fail
wait_for_sync || test_fail
run_chronyc "ntpdata $server" || test_fail
check_chronyc_output "^Remote address" || test_fail
stop_chronyd || test_fail
check_chronyd_messages || test_fail
check_chronyd_files || test_fail
test_pass

View File

@@ -7,7 +7,7 @@
hwts_iface="" hwts_iface=""
for iface_path in /sys/class/net/*; do for iface_path in /sys/class/net/*; do
iface=$(basename "$iface_path") iface=$(basename "$iface_path")
if ethtool -T "$iface" 2> /dev/null | grep -q HWTSTAMP_FILTER_ALL; then if ethtool -T "$iface" 2> /dev/null | grep -q ' all\($\| \)'; then
hwts_iface="$iface" hwts_iface="$iface"
break break
fi fi

72
test/system/105-nts Executable file
View File

@@ -0,0 +1,72 @@
#!/usr/bin/env bash
. ./test.common
server_name="chrony-nts-test"
check_chronyd_features NTS || test_skip "NTS support disabled"
certtool --help &> /dev/null || test_skip "certtool missing"
sed -i "/ $server_name\$/d" /etc/hosts && echo "$server $server_name" >> /etc/hosts || \
test_skip "Cannot modify /etc/hosts"
check_chronyd_features PRIVDROP && user="nobody"
test_start "NTS authentication"
cat > $TEST_DIR/cert.cfg <<EOF
cn = "$server_name"
serial = 001
activation_date = "$(date -d '1 year ago' +'%Y-%m-%d') 00:00:00 UTC"
expiration_date = "$(date -d '1 year' +'%Y-%m-%d') 00:00:00 UTC"
signing_key
encryption_key
EOF
certtool --generate-privkey --key-type=ed25519 --outfile $TEST_DIR/server.key \
&> $TEST_DIR/certtool.log
certtool --generate-self-signed --load-privkey $TEST_DIR/server.key \
--template $TEST_DIR/cert.cfg --outfile $TEST_DIR/server.crt &>> $TEST_DIR/certtool.log
chown $user $TEST_DIR/server.*
ntpport=$(get_free_port)
ntsport=$(get_free_port)
server_options="port $ntpport nts ntsport $ntsport"
extra_chronyd_directives="
port $ntpport
ntsport $ntsport
ntsserverkey $TEST_DIR/server.key
ntsservercert $TEST_DIR/server.crt
ntstrustedcerts $TEST_DIR/server.crt
ntsdumpdir $TEST_LIBDIR
ntsprocesses 3"
start_chronyd || test_fail
sleep 3
run_chronyc "authdata" || test_fail
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
127\.0\.0\.1 NTS 1 15 256 [0-9] 0 0 [78] 100$" || test_fail
stop_chronyd || test_fail
check_chronyd_messages || test_fail
check_chronyd_files || test_fail
rm -f $TEST_LOGDIR/measurements.log
server_options="port $ntpport nts ntsport $((ntsport + 1))"
start_chronyd || test_fail
sleep 2
run_chronyc "authdata" || test_fail
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
127\.0\.0\.1 NTS 1 15 256 [0-9] 0 0 [78] 100$" || test_fail
stop_chronyd || test_fail
check_chronyd_messages || test_fail
check_chronyd_files || test_fail
test_pass

View File

@@ -97,6 +97,8 @@ default_extra_chronyd_directives=""
default_extra_chronyd_options="" default_extra_chronyd_options=""
default_clock_control=0 default_clock_control=0
default_server=127.0.0.1 default_server=127.0.0.1
default_server_name=127.0.0.1
default_server_options=""
default_user=root default_user=root
# Initialize test settings from their defaults # Initialize test settings from their defaults
@@ -178,7 +180,7 @@ get_free_port() {
while true; do while true; do
port=$((RANDOM % 10000 + 10000)) port=$((RANDOM % 10000 + 10000))
netstat -aln | grep '^udp.*:'$port && continue netstat -aln | grep '^\(tcp\|udp\).*[:.]'"$port " && continue
break break
done done
@@ -210,7 +212,7 @@ generate_chrony_conf() {
echo "cmdallow" echo "cmdallow"
echo "local" echo "local"
echo "server $server port $ntpport minpoll -6 maxpoll -6" echo "server $server_name port $ntpport minpoll -6 maxpoll -6 $server_options"
[ "$server" = "127.0.0.1" ] && echo "bindacqaddress $server" [ "$server" = "127.0.0.1" ] && echo "bindacqaddress $server"
echo "bindaddress 127.0.0.1" echo "bindaddress 127.0.0.1"

View File

@@ -12,7 +12,8 @@ TEST_OBJS := $(sort $(patsubst %.c,%.o,$(wildcard *.c)))
TESTS := $(patsubst %.o,%.test,$(filter-out $(SHARED_OBJS),$(TEST_OBJS))) TESTS := $(patsubst %.o,%.test,$(filter-out $(SHARED_OBJS),$(TEST_OBJS)))
CHRONYD_OBJS := $(patsubst %.o,$(CHRONY_SRCDIR)/%.o,$(filter-out main.o,\ CHRONYD_OBJS := $(patsubst %.o,$(CHRONY_SRCDIR)/%.o,$(filter-out main.o,\
$(filter %.o,$(shell $(MAKE) -f $(CHRONY_SRCDIR)/Makefile print-chronyd-objects)))) $(filter %.o,$(shell $(MAKE) -f $(CHRONY_SRCDIR)/Makefile \
print-chronyd-objects NODEPS=1))))
all: $(TESTS) all: $(TESTS)

View File

@@ -29,12 +29,14 @@ void
test_unit(void) test_unit(void)
{ {
int i, j, index; int i, j, index;
CLG_Service s;
struct timespec ts; struct timespec ts;
IPAddr ip; IPAddr ip;
char conf[][100] = { char conf[][100] = {
"clientloglimit 10000", "clientloglimit 10000",
"ratelimit interval 3 burst 4 leak 3", "ratelimit interval 3 burst 4 leak 3",
"cmdratelimit interval 3 burst 4 leak 3", "cmdratelimit interval 3 burst 4 leak 3",
"ntsratelimit interval 6 burst 8 leak 3",
}; };
CNF_Initialise(0, 0); CNF_Initialise(0, 0);
@@ -55,15 +57,10 @@ test_unit(void)
TST_GetRandomAddress(&ip, IPADDR_UNSPEC, i % 8 ? -1 : i / 8 % 9); TST_GetRandomAddress(&ip, IPADDR_UNSPEC, i % 8 ? -1 : i / 8 % 9);
DEBUG_LOG("address %s", UTI_IPToString(&ip)); DEBUG_LOG("address %s", UTI_IPToString(&ip));
if (random() % 2) { s = random() % MAX_SERVICES;
index = CLG_LogNTPAccess(&ip, &ts); index = CLG_LogServiceAccess(s, &ip, &ts);
TEST_CHECK(index >= 0); TEST_CHECK(index >= 0);
CLG_LimitNTPResponseRate(index); CLG_LimitServiceRate(s, index);
} else {
index = CLG_LogCommandAccess(&ip, &ts);
TEST_CHECK(index >= 0);
CLG_LimitCommandResponseRate(index);
}
UTI_AddDoubleToTimespec(&ts, (1 << random() % 14) / 100.0, &ts); UTI_AddDoubleToTimespec(&ts, (1 << random() % 14) / 100.0, &ts);
} }
@@ -72,11 +69,13 @@ test_unit(void)
DEBUG_LOG("records %u", ARR_GetSize(records)); DEBUG_LOG("records %u", ARR_GetSize(records));
TEST_CHECK(ARR_GetSize(records) == 64); TEST_CHECK(ARR_GetSize(records) == 64);
s = CLG_NTP;
for (i = j = 0; i < 10000; i++) { for (i = j = 0; i < 10000; i++) {
ts.tv_sec += 1; ts.tv_sec += 1;
index = CLG_LogNTPAccess(&ip, &ts); index = CLG_LogServiceAccess(s, &ip, &ts);
TEST_CHECK(index >= 0); TEST_CHECK(index >= 0);
if (!CLG_LimitNTPResponseRate(index)) if (!CLG_LimitServiceRate(s, index))
j++; j++;
} }

View File

@@ -22,6 +22,7 @@
#include <sysincl.h> #include <sysincl.h>
#include <cmac.h> #include <cmac.h>
#include <logging.h> #include <logging.h>
#include <util.h>
#include "test.h" #include "test.h"
#define MAX_KEY_LENGTH 64 #define MAX_KEY_LENGTH 64
@@ -30,9 +31,9 @@
struct cmac_test { struct cmac_test {
const char *name; const char *name;
const unsigned char key[MAX_KEY_LENGTH]; const unsigned char key[MAX_KEY_LENGTH];
unsigned int key_length; int key_length;
const unsigned char hash[MAX_HASH_LENGTH]; const unsigned char hash[MAX_HASH_LENGTH];
unsigned int hash_length; int hash_length;
}; };
void void
@@ -49,29 +50,36 @@ test_unit(void)
{ "", "", 0, "", 0 } { "", "", 0, "", 0 }
}; };
CMC_Algorithm algorithm;
CMC_Instance inst; CMC_Instance inst;
unsigned int length; int i, j, length;
int i, j;
#ifndef HAVE_CMAC #ifndef HAVE_CMAC
TEST_REQUIRE(0); TEST_REQUIRE(0);
#endif #endif
TEST_CHECK(CMC_INVALID == 0);
for (i = 0; tests[i].name[0] != '\0'; i++) { for (i = 0; tests[i].name[0] != '\0'; i++) {
TEST_CHECK(CMC_GetKeyLength(tests[i].name) == tests[i].key_length); algorithm = UTI_CmacNameToAlgorithm(tests[i].name);
TEST_CHECK(algorithm != 0);
TEST_CHECK(CMC_GetKeyLength(algorithm) == tests[i].key_length);
DEBUG_LOG("testing %s", tests[i].name); DEBUG_LOG("testing %s", tests[i].name);
for (j = 0; j <= 128; j++) { for (j = -1; j <= 128; j++) {
if (j == tests[i].key_length) if (j == tests[i].key_length)
continue; continue;
TEST_CHECK(!CMC_CreateInstance(tests[i].name, tests[i].key, j)); TEST_CHECK(!CMC_CreateInstance(algorithm, tests[i].key, j));
} }
inst = CMC_CreateInstance(tests[i].name, tests[i].key, tests[i].key_length); inst = CMC_CreateInstance(algorithm, tests[i].key, tests[i].key_length);
TEST_CHECK(inst); TEST_CHECK(inst);
TEST_CHECK(!CMC_CreateInstance("nosuchcipher", tests[i].key, tests[i].key_length)); TEST_CHECK(!CMC_CreateInstance(0, tests[i].key, tests[i].key_length));
TEST_CHECK(CMC_Hash(inst, data, -1, hash, sizeof (hash)) == 0);
TEST_CHECK(CMC_Hash(inst, data, sizeof (data) - 1, hash, -1) == 0);
for (j = 0; j <= sizeof (hash); j++) { for (j = 0; j <= sizeof (hash); j++) {
memset(hash, 0, sizeof (hash)); memset(hash, 0, sizeof (hash));

View File

@@ -22,12 +22,13 @@
#include <sysincl.h> #include <sysincl.h>
#include <hash.h> #include <hash.h>
#include <logging.h> #include <logging.h>
#include <util.h>
#include "test.h" #include "test.h"
struct hash_test { struct hash_test {
const char *name; const char *name;
const unsigned char out[MAX_HASH_LENGTH]; const unsigned char out[MAX_HASH_LENGTH];
unsigned int length; int length;
}; };
void void
@@ -69,27 +70,35 @@ test_unit(void)
{ "", "", 0 } { "", "", 0 }
}; };
unsigned int length; HSH_Algorithm algorithm;
int i, j, hash_id; int i, j, hash_id, length;
TEST_CHECK(HSH_INVALID == 0);
for (i = 0; tests[i].name[0] != '\0'; i++) { for (i = 0; tests[i].name[0] != '\0'; i++) {
hash_id = HSH_GetHashId(tests[i].name); algorithm = UTI_HashNameToAlgorithm(tests[i].name);
TEST_CHECK(algorithm != 0);
hash_id = HSH_GetHashId(algorithm);
if (hash_id < 0) { if (hash_id < 0) {
TEST_CHECK(strcmp(tests[i].name, "MD5")); TEST_CHECK(algorithm != HSH_MD5);
#ifdef FEAT_SECHASH #ifdef FEAT_SECHASH
TEST_CHECK(strcmp(tests[i].name, "SHA1")); TEST_CHECK(algorithm != HSH_SHA1);
TEST_CHECK(strcmp(tests[i].name, "SHA256")); TEST_CHECK(algorithm != HSH_SHA256);
TEST_CHECK(strcmp(tests[i].name, "SHA384")); TEST_CHECK(algorithm != HSH_SHA384);
TEST_CHECK(strcmp(tests[i].name, "SHA512")); TEST_CHECK(algorithm != HSH_SHA512);
#endif #endif
continue; continue;
} }
DEBUG_LOG("testing %s", tests[i].name); DEBUG_LOG("testing %s", tests[i].name);
TEST_CHECK(HSH_Hash(hash_id, data1, -1, NULL, 0, out, sizeof (out)) == 0);
TEST_CHECK(HSH_Hash(hash_id, data1, sizeof (data1) - 1, data2, -1, out, sizeof (out)) == 0);
TEST_CHECK(HSH_Hash(hash_id, data1, sizeof (data1) - 1, NULL, 0, out, -1) == 0);
for (j = 0; j <= sizeof (out); j++) { for (j = 0; j <= sizeof (out); j++) {
TEST_CHECK(HSH_GetHashId(tests[i].name) == hash_id); TEST_CHECK(HSH_GetHashId(algorithm) == hash_id);
TEST_CHECK(HSH_GetHashId("nosuchhash") < 0); TEST_CHECK(HSH_GetHashId(0) < 0);
memset(out, 0, sizeof (out)); memset(out, 0, sizeof (out));
length = HSH_Hash(hash_id, data1, sizeof (data1) - 1, data2, sizeof (data2) - 1, length = HSH_Hash(hash_id, data1, sizeof (data1) - 1, data2, sizeof (data2) - 1,

View File

@@ -99,7 +99,7 @@ generate_key_file(const char *name, uint32_t *keys)
void void
test_unit(void) test_unit(void)
{ {
int i, j, data_len, auth_len; int i, j, data_len, auth_len, type, bits;
uint32_t keys[KEYS], key; uint32_t keys[KEYS], key;
unsigned char data[100], auth[MAX_HASH_LENGTH]; unsigned char data[100], auth[MAX_HASH_LENGTH];
char conf[][100] = { char conf[][100] = {
@@ -125,7 +125,6 @@ test_unit(void)
for (j = 0; j < KEYS; j++) { for (j = 0; j < KEYS; j++) {
TEST_CHECK(KEY_KeyKnown(keys[j])); TEST_CHECK(KEY_KeyKnown(keys[j]));
TEST_CHECK(KEY_GetAuthDelay(keys[j]) >= 0);
TEST_CHECK(KEY_GetAuthLength(keys[j]) >= 16); TEST_CHECK(KEY_GetAuthLength(keys[j]) >= 16);
data_len = random() % (sizeof (data) + 1); data_len = random() % (sizeof (data) + 1);
@@ -144,12 +143,16 @@ test_unit(void)
auth[auth_len - 1]++; auth[auth_len - 1]++;
TEST_CHECK(!KEY_CheckAuth(keys[j], data, data_len, auth, auth_len, auth_len)); TEST_CHECK(!KEY_CheckAuth(keys[j], data, data_len, auth, auth_len, auth_len));
TEST_CHECK(KEY_GetKeyInfo(keys[j], &type, &bits));
TEST_CHECK(type > 0 && bits > 0);
} }
for (j = 0; j < 1000; j++) { for (j = 0; j < 1000; j++) {
UTI_GetRandomBytes(&key, sizeof (key)); UTI_GetRandomBytes(&key, sizeof (key));
if (KEY_KeyKnown(key)) if (KEY_KeyKnown(key))
continue; continue;
TEST_CHECK(!KEY_GetKeyInfo(key, &type, &bits));
TEST_CHECK(!KEY_GenerateAuth(key, data, data_len, auth, sizeof (auth))); TEST_CHECK(!KEY_GenerateAuth(key, data, data_len, auth, sizeof (auth)));
TEST_CHECK(!KEY_CheckAuth(key, data, data_len, auth, auth_len, auth_len)); TEST_CHECK(!KEY_CheckAuth(key, data, data_len, auth, auth_len, auth_len));
} }

Some files were not shown because too many files have changed in this diff Show More