Compare commits

...

757 Commits

Author SHA1 Message Date
Miroslav Lichvar
43189651b0 doc: update NEWS 2024-07-30 14:05:42 +02:00
Miroslav Lichvar
f518b8d00f doc: update README 2024-07-30 12:11:09 +02:00
Miroslav Lichvar
42b3c40c32 doc: fix typo in kod option description 2024-07-30 12:11:09 +02:00
Miroslav Lichvar
66512ebcb3 ntp: make sure new configuration IDs are unused
The configuration IDs assigned to individual sources (used when they
don't have a resolved IP address) and pools of sources are 32-bit. The
ID could overflow if some sources were very frequently removed and added
again. Two unrelated sources could end up with the same ID, causing some
operations to unexpectedly impact only one or both sources.

Make sure the ID is currently unused before assigning it to a new source.
2024-07-30 12:11:09 +02:00
Miroslav Lichvar
3940d2aae3 leapdb: add explicit cast to int64_t
Add an explicit cast to int64_t to not rely on LEAP_SEC_LIST_OFFSET
not fitting in 32-bit time_t.
2024-07-30 12:09:53 +02:00
Miroslav Lichvar
34be117c9c main: check for killed foreground process
On start, if the foreground process waiting for the daemon process to
close the pipe (after finishing the RTC initialization, initstepslew,
etc) is killed, terminate the daemon too assuming that whatever killed
the foreground process it wanted all chronyd processes to stop.

In the daemon, before closing the pipe file descriptor, send an empty
message to check if the pipe isn't already closed on the other end.
2024-07-04 16:50:22 +02:00
Miroslav Lichvar
7915f52495 logging: add function to send message to foreground process 2024-07-04 16:50:22 +02:00
Miroslav Lichvar
05bd4898a9 test: fix 142-ntpoverptp 2024-06-20 15:10:42 +02:00
Miroslav Lichvar
4da088ec2f ntp: make NTP-over-PTP domain configurable
Add ptpdomain directive to set the domain number of transmitted and
accepted NTP-over-PTP messages. It might need to be changed in networks
using a PTP profile with the same domain number. The default domain
number of 123 follows the current NTP-over-PTP specification.
2024-06-20 15:00:17 +02:00
Miroslav Lichvar
c46e0549ab ntp: update NTP-over-PTP support
Following the latest version of the draft, accept NTP messages in both
PTPv2 and PTPv2.1 messages, accept sync messages in addition to delay
request messages, and check the minorSdoId field in PTPv2.1 messages.

Transmitted messages are still PTPv2 delay requests.

Don't switch to the organization-specific TLV yet. Wait for the NTP TLV
subtype and Network Correction extension field to be assigned by IANA to
avoid an additional break in compatibility.
2024-06-20 14:35:28 +02:00
Miroslav Lichvar
8f5b308414 test: make 124-tai more reliable
Reported-by: Reinhard Max <max@suse.de>
2024-06-04 16:25:55 +02:00
Miroslav Lichvar
084fe6b0cc doc: clarify prefer source option 2024-06-04 16:25:55 +02:00
Miroslav Lichvar
ebfc676d74 ntp: limit offset correction to supported NTP interval
When an NTP source is specified with the offset option, the corrected
offset may get outside of the supported NTP interval (by default -50..86
years around the build date). If the source passed the source selection,
the offset would be rejected only later in the adjustment of the local
clock.

Check the offset validity as part of the NTP test A to make the source
unselectable and make it visible in the measurements log and ntpdata
report.
2024-05-02 14:43:51 +02:00
Miroslav Lichvar
adaca0ff19 reference: switch is_leap_close() from time_t to double
Avoid undefined behavior in the timestamp conversion from double to
time_t in REF_IsLeapSecondClose() with NTP sources configured with a
large offset correction.
2024-05-02 14:43:46 +02:00
Miroslav Lichvar
84d6c7a527 sources: allow logging one selection failure on start
Allow one message about failed selection (e.g. no selectable sources)
to be logged before first successful selection when a source has
full-size reachability register (8 polls with a received or missed
response).

This should make it more obvious that chronyd has a wrong configuration
or there is a firewall/networking issue.
2024-05-02 12:51:38 +02:00
Miroslav Lichvar
c43efccf02 sources: update source selection with unreachable sources
When updating the reachability register of a source with zero, call the
source selection even if the source is not the currently selected as the
best source. But do that only if all reachability bits are zero, i.e.
there was no synchronized response for last 8 polls.

This will enable the source selection to log a message when only
unreachable sources are updating reachability and it decreases the
number of unnecessary source selections.
2024-04-30 15:52:18 +02:00
Miroslav Lichvar
1affd03cca sources: reorder unsynchronised source status
In the source selection, check for the unsynchronized leap status after
getting sourcestats data. The unsynchronized source status is supposed
to indicate an unsynchronized source that is providing samples, not a
source which doesn't have any samples.

Also, fix the comment describing the status.

Fixes: 4c29f8888c ("sources: handle unsynchronized sources in selection")
2024-04-30 15:52:18 +02:00
Miroslav Lichvar
276591172e ntp: improve copying of server status
When a server specified with the copy option responds with an
unsynchronized status (e.g. due to selection failure), reset the
source instance to immediately switch the local reference status
instead of waiting for the source to become unreachable after 8 polls.
2024-04-29 11:21:45 +02:00
Rob Gill
989ef702aa doc: fix typo in README
Typo correction only - no change to content

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2024-04-16 07:50:11 +02:00
Rob Gill
1920b1efde doc: fix typo in chronyc docs
Typo fix only - no change to content

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2024-04-16 07:49:59 +02:00
Miroslav Lichvar
bb5db828c6 ntp: log failed connection to Samba signd socket
Log an error message (in addition to the socket-specific debug message)
when the connection to signd socket fails, but only once before a
successful signd exchange to avoid flooding the system log.
2024-04-15 16:35:33 +02:00
Miroslav Lichvar
dcc94a4c10 doc: add contributing.adoc 2024-04-11 16:52:06 +02:00
Miroslav Lichvar
2ed72c49c9 test: add --enable-debug option to 002-scanbuild 2024-04-11 12:53:01 +02:00
Miroslav Lichvar
342b588e3b avoid some static analysis errors
Modify the code to avoid some false positives reported by the clang and
gcc static analyzers.
2024-04-11 10:31:02 +02:00
Miroslav Lichvar
a914140bd4 sys_linux: disable other external timestamping channels
Use new ioctls added in Linux 6.7 to disable receiving events from other
channels when enabling external timestamping on a PHC. This should save
some CPU time when other applications or chronyd instances are using
other channels of the same PHC.
2024-04-10 15:33:04 +02:00
Miroslav Lichvar
28e4eec1c4 refclock: update comment in PHC driver
Since Linux 6.7 external timestamping events are no longer shared among
all descriptors of a PHC. Each descriptor gets its own copy of each
timestamp.
2024-04-10 12:13:29 +02:00
Miroslav Lichvar
5235c51801 cmdmon: add reserved fields to local command
Add two reserved fields initialized to zero to the new REQ_LOCAL3
command to allow adding more options (e.g. delay in activation) without
changing the command number again.
2024-04-04 16:24:43 +02:00
Miroslav Lichvar
26ea4e35e7 test: add tests of local directive options 2024-04-04 16:24:02 +02:00
Andy Fiddaman
9397ae2b0a reference: add "local activate" option
This option sets an activating root distance for the local reference. The
local reference will not be used until the root distance drops below the
configured value for the first time. This can be used to prevent the local
reference from being activated on a server which has never been synchronised
with an upstream server. The default value of 0.0 causes no activating
distance to be used, such that the local reference is always eligible for
activation.
2024-04-04 15:17:05 +02:00
Miroslav Lichvar
b8ead3485b leapdb: fix leapsec list processing with 32-bit time_t
A 32-bit time_t value overflows when converted to the Y1900 epoch used
in the leapsec list. Use a 64-bit variable in get_list_leap() to fix the
comparisons on systems using 32-bit time_t.

Fixes: 53823b9f1c ("leapdb: support leap-seconds.list as second source")
2024-04-03 11:01:44 +02:00
Miroslav Lichvar
24d28cd679 ntp: add server support for KoD RATE
Add "kod" option to the ratelimit directive to respond with the KoD
RATE code to randomly selected requests exceeding the configured limit.
This complements the client support of KoD RATE. It's disabled by
default.

There can be only one KoD code in one response. If both NTS NAK and RATE
codes are triggered, drop the response. The KoD RATE code can be set in
an NTS-authenticated response.
2024-04-02 15:39:12 +02:00
Miroslav Lichvar
aac898343e clientlog: add support for KoD rate limiting
Add a third return value to CLG_LimitServiceRate() to indicate the
server should send a response requesting the client to reduce its
polling rate. It randomly selects from a fraction (configurable to 1/2,
1/4, 1/8, 1/16, or disabled) of responses which would be dropped
(after selecting responses for the leak option).
2024-04-02 15:23:26 +02:00
Miroslav Lichvar
c8c7f518b1 clientlog: return enum from CLG_LimitServiceRate()
Change CLG_LimitServiceRate() to return an enum in preparation for
adding KoD RATE support.
2024-04-02 11:55:02 +02:00
Miroslav Lichvar
ce956c99a8 nts: check for NTS NAK specifically when responding
Ignore other KoD codes than NTS NAK when deciding if the server response
should not be authenticated.
2024-04-02 11:33:04 +02:00
Miroslav Lichvar
863866354d ntp: avoid unnecessary restart of resolving round on refresh
Don't call NSR_ResolveSources() when a resolving round is already
started. This cuts the number of calls of the system resolver made due
to the refresh command to half.
2024-03-14 16:39:41 +01:00
Miroslav Lichvar
6e5513c80b ntp: don't keep refresh requests in list of unresolved sources
The refresh command adds requests to reresolve addresses of all sources.
If some sources didn't have an IP address resolved yet, the
corresponding requests were not removed after failed resolving. Repeated
refresh commands increased the number of requests and number of calls of
the system resolver, which might not be caching DNS responses.

Remove all refresh requests from the list after resolving attempt to fix
that.

Reported-by: t.barnewski@avm.de
Fixes: d7e3ad17ff ("ntp: create sources for unresolved addresses")
2024-03-14 16:39:41 +01:00
Miroslav Lichvar
6d0143e963 ntp: add more debug messages for resolving 2024-03-14 16:39:32 +01:00
Miroslav Lichvar
f49be7f063 conf: don't load sourcedir during initstepslew and RTC init
If the reload sources command was received in the chronyd start-up
sequence with initstepslew and/or RTC init (-s option), the sources
loaded from sourcedirs caused a crash due to failed assertion after
adding sources specified in the config.

Ignore the reload sources command until chronyd enters the normal
operation mode.

Fixes: 519796de37 ("conf: add sourcedirs directive")
2024-03-12 14:57:30 +01:00
Miroslav Lichvar
7fe98a83b8 test: replace another C99-style declaration in for loop 2024-03-11 12:00:12 +01:00
Miroslav Lichvar
ad37c409c9 cmdmon: add offset command
Add a new command to modify the offset option of NTP sources and
reference clocks.
2024-03-07 16:20:27 +01:00
Miroslav Lichvar
719c6f6a8a ntp+refclock: add functions to modify offset option 2024-03-07 16:19:04 +01:00
Miroslav Lichvar
b0750136b5 rtc+getdate: initialize tm_wday for mktime()
Even though mktime() is documented as ignoring the tm_wday field, the
coverity static analyzer complains about passing an uninitialized value.
Set the field to zero to make it happy.
2024-03-04 11:38:16 +01:00
Miroslav Lichvar
ad79aec946 test: avoid C99-style declaration in for loop
This fixes compilation without the -std=c99 option with an older gcc.
2024-03-04 11:38:11 +01:00
Miroslav Lichvar
008dc16727 examples: switch chrony.conf examples to leapseclist 2024-02-08 16:21:42 +01:00
Miroslav Lichvar
6cf9fe2f16 test: improve 113-leapsecond and 124-tai tests
Use leapseclist instead of leapsectz and test also negative leap
seconds. Add a test for leapsectz when the date command indicates
right/UTC is available on the system and mktime() works as expected.
Check TAI offset in the server's log.
2024-02-08 15:54:24 +01:00
Patrick Oppenlander
637b77d1bd test: add leapdb unit test 2024-02-08 15:54:24 +01:00
Patrick Oppenlander
53823b9f1c leapdb: support leap-seconds.list as second source
The existing implementation of getting leap second information from a
timezone in get_tz_leap() relies on non-portable C library behaviour.

Specifically, mktime is not required to return '60' in the tm_sec field
when a leap second is inserted leading to "Timezone right/UTC failed
leap second check, ignoring" errors on musl based systems.

This patch adds support for getting leap second information from the
leap-seconds.list file included with tzdata and adds a new configuration
directive leapseclist to switch on the feature.
2024-02-08 15:54:24 +01:00
Patrick Oppenlander
83f90279b0 leapdb: move source check into separate function
The sanity checks are valid for all possible sources of leap second
information, so move them into a separate function check_leap_source().
2024-02-08 15:54:21 +01:00
Patrick Oppenlander
02ae9a8607 leapdb: make twice per day check logic common
We want to do the twice per day check regardless of the data source.
Move the check up one level from get_tz_leap() into LDB_GetLeap().
2024-02-08 12:54:37 +01:00
Patrick Oppenlander
017d6f8f56 reference: move leap second source into leapdb
Separate out source of leap second data into a new module in preparation
for supporting more sources such as leap-seconds.list.
2024-02-08 12:54:37 +01:00
Miroslav Lichvar
eb26d13140 cmdmon: add timestamp counters to ntpdata report 2024-02-07 10:23:40 +01:00
Miroslav Lichvar
8d19f49341 ntp: add per-source counters of kernel and hardware timestamps 2024-02-07 10:23:40 +01:00
Miroslav Lichvar
637fa29e1e cmdmon: add ipv4/ipv6 options to add source command 2024-02-07 10:23:40 +01:00
Miroslav Lichvar
2d349595ee cmdmon: simplify flag checking in handle_add_source() 2024-02-07 10:23:40 +01:00
Miroslav Lichvar
5cb584d6c1 conf: add ipv4 and ipv6 options to server/pool/peer directive
Accept "ipv4" and "ipv6" options in the server/pool/peer directive to
use only IPv4 or IPv6 addresses respectively.

The configuration is different from the "server [-4|-6] hostname" syntax
supported by ntpd to avoid breaking existing scripts which expect the
hostname to always be the first argument of the directives.
2024-02-07 10:23:40 +01:00
Miroslav Lichvar
d7c2b1d2f3 ntp: support per-source IP family restriction
Add a new parameter to the NSR_AddSourceByName() function to allow
individual sources to be limited to IPv4 or IPv6 addresses. This doesn't
change the options passed to the resolver. It's just an additional
filter in the processing of resolved addresses following the -4/-6
command-line option of chronyd.
2024-02-07 10:23:36 +01:00
Miroslav Lichvar
e11b518a1f ntp: fix authenticated requests in serverstats
Fix the CLG_UpdateNtpStats() call to count requests passing the
authentication check instead of requests triggering a KoD response
(i.e. NTS NAK).
2024-01-08 11:46:32 +01:00
Miroslav Lichvar
120dfb8b36 update copyright years 2023-12-05 14:22:10 +01:00
Miroslav Lichvar
598b893e1d doc: update FAQ on improving accuracy 2023-12-05 14:22:10 +01:00
Miroslav Lichvar
89aa8fa342 doc: mention dependency of net corrections on HW timestamping 2023-12-05 14:22:08 +01:00
Miroslav Lichvar
42fdad5dcc doc: improve description of reload sources command 2023-12-04 16:50:51 +01:00
Miroslav Lichvar
3ee7b3e786 sources: rework logging of selection loss
The commit 5dd288dc0c ("sources: reselect earlier when removing
selected source") didn't cover all paths that can lead to a missing log
message when all sources are removed.

Add a flag to track the loss of selection and postpone the log message
in transient states where no message is logged to avoid spamming in
normal operation. Call SRC_SelectSource() after removing the source
to get a log message if there are no (selectable) sources left.

Reported-by: Thomas Lange <thomas@corelatus.se>
2023-11-28 12:21:23 +01:00
Miroslav Lichvar
426fe2ee58 doc: update NEWS 2023-11-22 13:53:23 +01:00
Miroslav Lichvar
3f66202d79 doc: update README 2023-11-22 11:56:16 +01:00
Miroslav Lichvar
ed6b0b55c7 doc: replace foobar naming in examples 2023-11-22 11:56:15 +01:00
Miroslav Lichvar
5e5adbea0c doc: update description of NTP over PTP in FAQ 2023-11-22 11:56:15 +01:00
Miroslav Lichvar
82959431df doc: mention version supporting socket activation in FAQ 2023-11-22 11:56:15 +01:00
Miroslav Lichvar
b92b2da24a doc: improve ntstrustedcerts description 2023-11-22 11:55:27 +01:00
Miroslav Lichvar
68a3d52086 doc: improve cmdport description 2023-11-22 09:48:36 +01:00
Miroslav Lichvar
1a15be1e9e sources: drop unreachable log message
With forced reselection during source removal selected_source_index
can only be INVALID_SOURCE if there are no sources. The "Can't
synchronise: no sources" message couldn't be logged even before that as
SRC_ReselectSource() resets the index before calling SRC_SelectSource().

Replace the message with an assertion.
2023-11-21 12:38:41 +01:00
Miroslav Lichvar
5dd288dc0c sources: reselect earlier when removing selected source
When a selected source is being removed, reset the instance and rerun
the selection while the source is still marked as selected. This forces
a "Can't synchronise" message to be logged when all sources are removed.

Reported-by: Thomas Lange <thomas@corelatus.se>
2023-11-21 12:38:41 +01:00
Miroslav Lichvar
cbee464c75 sources: reselect after resetting selected source
Avoid showing in the sources report a selected source which has no
samples (e.g. after replacement).
2023-11-21 12:38:41 +01:00
Miroslav Lichvar
4e36295889 ntp: allow reusing timestamps of unused samples
When switching from basic mode to interleaved mode following a response
which wasn't accumulated due to failed test A, B, C, or D, allow
timestamps of the failed sample to be reused in interleaved mode, i.e.
replacing the server's less accurate transmit timestamp with a more
accurate timestamp server can turn a failed sample into acceptable one.

Move the presend check into test A to simplify the code.
2023-11-21 12:38:41 +01:00
Miroslav Lichvar
2d2642bb82 ntp: fix presend in interleaved mode
The presend option in interleaved mode uses two presend requests instead
of one to get an interleaved response from servers like chrony which
delay the first interleaved response due to an optimization saving
timestamps only for clients actually using the interleaved mode.

After commit 0ae6f2485b ("ntp: don't use first response in interleaved
mode") the first interleaved response following the two presend
responses in basic mode is dropped as the preferred set of timestamps
minimizing error in delay was already used by the second sample in
basic mode. There are only three responses in the burst and no sample is
accumulated.

Increasing the number of presend requests to three to get a fourth
sample would be wasteful. Instead, allow reusing timestamps of the
second presend sample in basic mode, which is never accumulated.

Reported-by: Aaron Thompson
Fixes: 0ae6f2485b ("ntp: don't use first response in interleaved mode")
2023-11-21 12:38:41 +01:00
Miroslav Lichvar
9c6eaccc32 nts: close reusable sockets in helper process
Close all reusable sockets when the NTS-KE server helper is forked. It
is not supposed to have access to any of the server sockets, just the
socket for getting requests from the main process and the syslog socket.
2023-11-21 12:38:41 +01:00
Miroslav Lichvar
0aa4d5ac14 socket: provide function for closing reusable sockets 2023-11-21 12:38:39 +01:00
Miroslav Lichvar
ee9d721b7b socket: set close-on-exec on all reusable sockets
Set the CLOEXEC flag on all reusable sockets in the initialization to
avoid leaking them to sendmail (mailonchange directive) in case the
chrony configuration doesn't use all sockets provided by systemd.
2023-11-20 13:33:45 +01:00
Luke Valenta
b6eec0068a doc: add FAQ section on minimizing service downtime 2023-11-13 17:05:46 +01:00
Luke Valenta
e6a0476eb7 socket: add support for systemd sockets
Before opening new IPv4/IPv6 server sockets, chronyd will check for
matching reusable sockets passed from the service manager (for example,
passed via systemd socket activation:
https://www.freedesktop.org/software/systemd/man/latest/sd_listen_fds.html)
and use those instead.

Aside from IPV6_V6ONLY (which cannot be set on already-bound sockets),
the daemon sets the same socket options on reusable sockets as it would
on sockets it opens itself.

Unit tests test the correct parsing of the LISTEN_FDS environment
variable.

Add 011-systemd system test to test socket activation for DGRAM and
STREAM sockets (both IPv4 and IPv6).  The tests use the
systemd-socket-activate test tool, which has some limitations requiring
workarounds discussed in inline comments.
2023-11-13 17:05:26 +01:00
Luke Valenta
c063b9e78a logging: move severity_chars to fix compiler warning 2023-11-08 15:09:03 +01:00
Luke Valenta
f6f1863fe2 logging: add log severity to file log prefix when debug is enabled 2023-11-07 15:43:57 +01:00
Miroslav Lichvar
51a621bc2b ntp: initialize network correction of transmitted packets
Initialize the unused value of network correction parsed from
own transmitted packets to avoid a use-of-uninitialized-value error
in NIO_UnwrapMessage() reported by clang.

Fixes: 6372a9f93f ("ntp: save PTP correction from NTP-over-PTP messages")
2023-11-02 12:53:00 +01:00
Luke Valenta
1488b31a38 doc: document '-L -1' option for debug logging output 2023-10-24 11:37:56 +02:00
Miroslav Lichvar
70cdd8b1ef ntp: add client support for network correction
If the network correction is known for both the request and response,
and their sum is not larger that the measured peer delay, allowing the
transparent clocks to be running up to 100 ppm faster than the client's
clock, apply the corrections to the NTP offset and peer delay. Don't
correct the root delay to not change the estimated maximum error.
2023-09-26 15:14:13 +02:00
Miroslav Lichvar
8eef631009 ntp: add server support for network correction
Provide the network correction (PTP correction + RX duration) of the
request in the new extension field if included in the request and
NTP-over-PTP is enabled.
2023-09-26 15:14:13 +02:00
Miroslav Lichvar
d9ae724c60 ntp: add extension field to provide network correction
To be able to verify PTP corrections, the client will need to know both
the correction of the request received by the server and the correction
of the response. Add a new experimental NTP extension field that the
clients will use to request the correction and servers return the
value.
2023-09-26 15:14:13 +02:00
Miroslav Lichvar
6372a9f93f ntp: save PTP correction from NTP-over-PTP messages
When the RX duration is known (HW timestamping), save the PTP correction
from received PTP messages in the local RX timestamp.
2023-09-26 15:14:13 +02:00
Miroslav Lichvar
b0267475e3 ntp: extend local timestamp for PTP correction
Add two new fields to the NTP_Local_Timestamp structure:
- receive duration as the time it takes to receive the ethernet frame,
  currently known only with HW timestamping
- network correction as a generalized PTP correction

The PTP correction is provided by transparent clocks in the correction
field of PTP messages to remove the receive, processing and queueing
delays of network switches and routers. Only one-step end-to-end unicast
transparent clocks are useful for NTP-over-PTP. Two-step transparent
clocks use follow-up messages and peer-to-peer transparent clocks don't
handle delay requests.

The RX duration will be included in the network correction to compensate
for asymmetric link speeds of the server and client as the NTP RX
timestamp corresponds to the end of the reception (in order to
compensate for the asymmetry in the normal case when no corrections
are applied).
2023-09-26 15:10:19 +02:00
Miroslav Lichvar
07134f2625 ntp: add function for detection of experimental fields 2023-09-26 15:03:33 +02:00
Miroslav Lichvar
85db8e3a9c ntp: assert size of exp_mono_root field 2023-09-26 15:02:06 +02:00
Miroslav Lichvar
05f4f79cbf ntp: rename exp1 extension field
Rename the exp1 extension field to exp_mono_root (monotonic timestamp +
root delay/dispersion) to better distinguish it from future experimental
extension fields.
2023-09-26 15:01:24 +02:00
Miroslav Lichvar
bf616eafa1 util: add conversion between intervals and NTP 64-bit format
This will be needed to save PTP correction in NTP timestamp format.
2023-09-26 15:00:06 +02:00
Miroslav Lichvar
e08a0ee668 doc: don't require same version for experimental features 2023-09-26 14:58:42 +02:00
Miroslav Lichvar
f2d7baa94f configure: prefer gnutls over nss and tomcrypt for hashing
Reorder the tests in the configure script to prefer gnutls over nss and
tomcrypt as its support includes AES-CMAC.
2023-09-12 10:36:23 +02:00
Miroslav Lichvar
558931524d configure: don't try AES-SIV-CMAC in nettle when disabled
Avoid confusing message when --without-nettle is specified.
2023-09-12 10:31:36 +02:00
Miroslav Lichvar
a74b63277a siv: add support for AES-GCM-SIV in gnutls
Add support for AES-128-GCM-SIV in the current development code of
gnutls. There doesn't seem to be an API to get the cipher's minimum and
maximum nonce length and it doesn't check for invalid lengths. Hardcode
and check the limits in chrony for now.
2023-09-12 10:31:36 +02:00
Miroslav Lichvar
aa8196328c conf: improve log message for failed additions in sources reload
Describe the error status in the log message when adding a source from
sourcedir failed.
2023-09-12 08:11:25 +02:00
Miroslav Lichvar
37deee7140 conf: cast subtraction operands in source comparison
Cast the values to int to not break the sorting in case they are changed
to unsigned types.
2023-09-12 08:03:23 +02:00
Miroslav Lichvar
7ff74d9efe conf: fix reloading modified sources specified by IP address
When reloading a modified source from sourcedir which is ordered before
the original source (e.g. maxpoll was decreased), the new source is
added before the original one is removed. If the source is specified by
IP address, the addition fails due to the conflict with the original
source. Sources specified by hostname don't conflict. They are resolved
later (repeatedly if the resolver provides only conflicting addresses).

Split the processing of sorted source lists into two phases, so all
modified sources are removed before they are added again to avoid the
conflict.

Reported-by: Thomas Lange <thomas@corelatus.se>
2023-09-12 08:02:36 +02:00
Miroslav Lichvar
43320a1d6b doc: update NEWS and README 2023-08-09 15:20:55 +02:00
Josef 'Jeff' Sipek
8caaa0b056 socket: enable nanosecond resolution RX timestamp on FreeBSD
FreeBSD allows switching the receive timestamp format to struct timespec by
setting the SO_TS_CLOCK socket option to SO_TS_REALTIME after enabling
SO_TIMESTAMP.  If successful, the kernel then starts adding SCM_REALTIME
control messages instead of SCM_TIMESTAMP.
2023-08-09 15:19:46 +02:00
Miroslav Lichvar
e48a34392c test: make 139-nts more reliable 2023-08-08 17:11:46 +02:00
Miroslav Lichvar
8bc8bf9cc4 test: make 114-presend more reliable
Avoid frequently ending in the middle of a client/server exchange with
long delays. This changed after commit 4a11399c2e ("ntp: rework
calculation of transmit timeout").
2023-08-08 16:24:10 +02:00
Miroslav Lichvar
3dc9f1ff92 ntp: don't require previous HW TX timestamp to wait for another
Clients sockets are closed immediately after receiving valid response.
Don't wait for the first early HW TX timestamp to enable waiting for
late timestamps. It may take a long time or never come if the HW/driver
is consistently slow. It's a chicken and egg problem.

Instead, simply check if HW timestamping is enabled on at least one
interface. Responses from NTP sources on other interfaces will always be
saved (for 1 millisecond by default).
2023-08-08 16:06:58 +02:00
Miroslav Lichvar
7bc7d00297 ntp: fix adding noselect to selection options
If noselect is present in the configured options, don't assume it
cannot change and the effective options are equal. This fixes chronyc
selectopts +noselect command.

Fixes: 3877734814 ("sources: add function to modify selection options")
2023-08-07 14:58:48 +02:00
Bryan Christianson
b5cf861cd7 contrib: replace tuxfamily with chrony-project
The chrony project has moved from tuxfamily.org to chrony-project.org.
Reflect these changes in the macOS startup files and documentation.
2023-08-01 12:11:27 +02:00
Miroslav Lichvar
25cc84d5e2 doc: update links to chrony website 2023-07-27 13:05:23 +02:00
Miroslav Lichvar
f74e4cf1fe doc: don't mention mailing lists in README
Current information about mailing lists is available on the project's
website.
2023-07-26 16:33:49 +02:00
Miroslav Lichvar
5f66722b66 update copyright years 2023-07-20 12:57:33 +02:00
Miroslav Lichvar
b31461af7a doc: add more questions to FAQ 2023-07-20 12:55:31 +02:00
Miroslav Lichvar
ae177f2742 doc: fix typo in FAQ 2023-07-20 12:47:11 +02:00
Miroslav Lichvar
1a736078df doc: refer to root distance in chronyc sources report 2023-07-20 12:46:49 +02:00
Miroslav Lichvar
9b46ea7255 test: make 132-logchange more reliable 2023-07-18 15:16:03 +02:00
Miroslav Lichvar
ff4e932f17 test: make 148-replacement more reliable 2023-07-18 15:15:45 +02:00
Miroslav Lichvar
68c35a0072 test: improve ntp_sources unit test 2023-07-18 14:53:54 +02:00
Miroslav Lichvar
b6c634298d ntp: handle negotiated NTS-KE server in refreshment
When refreshing a source, compare the newly resolved addresses with the
originally resolved address instead of the current address to avoid
unnecessary replacements when the address is changed due to the NTS-KE
server negotiation.
2023-07-18 14:53:54 +02:00
Miroslav Lichvar
010df12459 nts: fix log severity for loaded server keys 2023-07-18 14:53:54 +02:00
Miroslav Lichvar
22ef2fbb0e makefile: compile getdate.o with -fwrapv option
The getdate code (copied from gnulib before it was switched to GPLv3)
has multiple issues with signed integer overflows. Use the -fwrapv
compiler option for this object to at least make the operations defined.
2023-07-18 14:52:55 +02:00
Miroslav Lichvar
7a03206222 doc: update NEWS 2023-06-21 11:28:54 +02:00
Miroslav Lichvar
b86c50bb9f ntp: refresh IP addresses periodically
Refresh NTP sources specified by hostname periodically (every 2 weeks
by default) to avoid long-running instances using a server which is no
longer intended for service, even if it is still responding correctly
and would not be replaced as unreachable, and help redistributing load
in large pools like pool.ntp.org. Only one source is refreshed at a time
to not interrupt clock updates if there are multiple selectable servers.

The refresh directive configures the interval. A value of 0 disables
the periodic refreshment.

Suggested-by: Ask Bjørn Hansen <ask@develooper.com>
2023-06-21 11:28:42 +02:00
Miroslav Lichvar
36f9b24dfe doc: remove out-of-date statement in server description
chronyc refresh no longer forces replacement of sources.

Fixes: b2dac47c82 ("ntp: avoid unneccessary replacements on refresh command")
2023-06-20 15:28:07 +02:00
Miroslav Lichvar
e0b75b87bf ntp: remove resolving timeout in finalization
Don't assume NSR_Finalise() can be called only on exit when the
scheduler is finalized.
2023-06-20 13:03:53 +02:00
Miroslav Lichvar
6661a61486 sched: reset timer queue in finalization
Don't leave dangling pointers to timer queue entries when they are
freed in the scheduler finalization in case some code tried to remove
a timer later.

Fixes: 6ea1082a72 ("sched: free timer blocks on exit")
2023-06-19 16:15:07 +02:00
Miroslav Lichvar
bc76291750 examples: don't set ProcSubset=pid in systemd unit files
This option seems to break detection of the FIPS mode, which is needed
by gnutls.
2023-06-15 15:23:40 +02:00
Miroslav Lichvar
2aefadd129 sources: delay source replacement
Wait for four consecutive source selections giving a bad status
(falseticker, bad distance or jittery) before triggering the source
replacement. This should reduce the rate of unnecessary replacements
and shorten the time needed to find a solution when unreplaceable
falsetickers are preventing other sources from forming a majority due
to switching back and forth to unreachable servers.
2023-06-15 14:23:47 +02:00
Miroslav Lichvar
123cb497b9 sources: replace reachable sources in selection
Instead of waiting for the next update of reachability, trigger
replacement of falsetickers, jittery and distant sources as soon as
the selection status is updated in their SRC_SelectSource() call.
2023-06-15 14:23:47 +02:00
Miroslav Lichvar
0c38e4a6ca ntp: reset poll score
When the polling interval is reset (e.g. after replacement), don't
forget to reset also the score impacting the next poll adjustment.
2023-06-15 14:23:47 +02:00
Miroslav Lichvar
0db30fd0b1 main: wait for parent process to terminate
When starting the daemon, wait in the grandparent process for the parent
process to terminate before exiting to avoid systemd logging a warning
"Supervising process $PID which is not our child". Waiting for the pipe
to be closed by the kernel when the parent process exits is not
sufficient.

Reported-by: Jan Pazdziora <jpazdziora@redhat.com>
2023-06-12 16:40:53 +02:00
Miroslav Lichvar
b90d2c084f ntp: randomize replacement interval
Replacement attempts are globally rate limited to one per 7*2^8 seconds
to limit the rate of DNS requests for public servers like pool.ntp.org.
If multiple sources are repeatedly attempting replacement (at their
polling intervals), one source can be getting all attempts for periods
of time.

Use a randomly generated interval to randomize the order of source
replacements without changing the average rate.
2023-06-08 16:14:48 +02:00
Miroslav Lichvar
ab8da7ecb9 ntp: use monotonic time for replacement interval
Avoid errors in the measured interval due to clock steps.
2023-06-08 16:10:26 +02:00
Miroslav Lichvar
05809e937c ntp: add debug message for bad sources 2023-06-08 16:10:26 +02:00
Miroslav Lichvar
8265fe2e30 client: check for allocation errors in tab completition 2023-06-08 16:10:26 +02:00
Miroslav Lichvar
c11a052955 client: avoid passing uninitialized address to format_name()
The clang memory sanitizer seems to trigger on an uninitialized value
passed to format_name() when the source is a refclock, even though the
value is not used for anything. Pass 0 in this case to avoid the error.
2023-06-08 15:56:19 +02:00
Miroslav Lichvar
109970f687 memory: use free() instead of realloc() for size 0
valgrind 3.21.0 reports realloc() of 0 bytes as an error due to having
different behavior on different systems. The only place where this can
happen in chrony is the array, which doesn't care what value realloc()
returns.

Modify the realloc wrapper to call free() in this case to make valgrind
happy.
2023-06-08 14:31:52 +02:00
Miroslav Lichvar
ca10b9e072 sys_linux: allow writev and TIOCGWINSZ in seccomp filter
Allow more syscalls for musl.

Reported-by: jvoisin <julien.voisin@dustri.org>
2023-06-01 14:52:56 +02:00
Miroslav Lichvar
19da1d95a8 test: set root ownership of tmp directory in system tests
Allow the tests to be started under a non-zero GID.
2023-06-01 14:52:56 +02:00
Miroslav Lichvar
61da7d0913 test: modify order of scfilter levels in system tests
Start with positive levels to get the offending system calls in the
system or audit log.
2023-06-01 14:52:56 +02:00
Miroslav Lichvar
105f1f90c1 test: fix 010-nts test for AES-GCM-SIV support 2023-06-01 14:52:51 +02:00
Miroslav Lichvar
c9d791e02d nts: don't load zero-length keys with unsupported algorithm
Don't load keys and cookies from the client's dump file if it has an
unsupported algorithm and unparseable keys (matching the algorithm's
expected length of zero). They would fail all SIV operations and trigger
new NTS-KE session.
2023-05-29 16:08:13 +02:00
Miroslav Lichvar
de678ff780 doc: clarify limitation of refresh command 2023-05-25 10:28:56 +02:00
Miroslav Lichvar
e16bcca617 sys_linux: allow membarrier in seccomp filter
This system call is used by musl.

Reported-by: jvoisin <julien.voisin@dustri.org>
2023-05-25 10:28:56 +02:00
Miroslav Lichvar
b57d7040b3 configure: add option to disable AES-GCM-SIV support 2023-05-25 10:28:56 +02:00
Miroslav Lichvar
c80858f738 nts: remove superfluous semicolon 2023-05-25 10:28:56 +02:00
Miroslav Lichvar
81bf7cdcdc nts: initialize unused part of server key
Initialize the unused part of shorter server NTS keys (AES-128-GCM-SIV)
loaded from ntsdumpdir to avoid sending uninitialized data in requests
to the NTS-KE helper process.

Do that also for newly generated keys in case the memory will be
allocated dynamically.

Fixes: b1230efac3 ("nts: add support for encrypting cookies with AES-128-GCM-SIV")
2023-05-25 10:28:50 +02:00
Miroslav Lichvar
b8b3830dc4 ntp: randomize address selection on all source replacements
If the resolver orders addresses by IP family, there is more than one
address in the preferred IP family, and they are all reachable, but
not selectable (e.g. falsetickers in a small pool which cannot remove
them from DNS), chronyd is unable to switch to addresses in the other IP
family as it follows the resolver's order.

Enable randomization of the address selection for all source
replacements and not just replacement of (unreachable) tentative
sources. If the system doesn't have connectivity in the other family,
the addresses will be skipped and no change in behavior should be
observed.
2023-05-23 09:33:48 +02:00
Miroslav Lichvar
d4738e1259 ntp: set minimum polltarget
The polltarget value is used in a floating-point division in the
calculation of the poll adjustment. Set 1 as the minimum accepted
polltarget value to avoid working with infinite values.
2023-05-18 10:46:46 +02:00
Miroslav Lichvar
5ba42cee45 ntp: reset polling interval when replacing sources
Set the polling interval to minpoll when changing address of a source,
but only if it is reachable to avoid increasing load on server or
network in case that is the reason for the source being unreachable.

This shortens the time needed to replace a falseticker or
unsynchronized source with a selectable source.
2023-05-18 10:46:42 +02:00
Miroslav Lichvar
b2dac47c82 ntp: avoid unneccessary replacements on refresh command
When the refresh command is issued, instead of trying to replace all
NTP sources as if they were unreachable or falsetickers, keep using the
current address if it is still returned by the resolver for the name.
This avoids unnecessary loss of measurements and switching to
potentially unreachable addresses.
2023-05-15 17:23:48 +02:00
Miroslav Lichvar
6a6161dc0f doc: update NEWS 2023-05-10 14:28:42 +02:00
Miroslav Lichvar
a4eb5be8ea doc: update list of contributors in README 2023-05-10 10:46:57 +02:00
Rupesh Patel
3050e29b1d examples: improve chrony.nm-dispatcher.onoffline script 2023-05-10 10:46:57 +02:00
Miroslav Lichvar
fb1af6e55b test: add 146-offline test 2023-04-18 11:39:27 +02:00
Miroslav Lichvar
47a13ae88c md5: fix old-style function definitions
This fixes -Wdeprecated-non-prototype clang warnings.

Reported-by: Bryan Christianson <bryan@whatroute.net>
2023-04-17 11:40:18 +02:00
Miroslav Lichvar
a8496658a0 test: free memory in unit tests on exit 2023-04-13 16:22:50 +02:00
Miroslav Lichvar
6ea1082a72 sched: free timer blocks on exit
Save pointers to allocated timer blocks and free them on exit. This
fixes the long-standing annoying "possibly lost" leak reported by
valgrind.
2023-04-13 16:22:50 +02:00
Miroslav Lichvar
4f674539fd test: add 145-rtc test 2023-04-13 16:22:50 +02:00
Miroslav Lichvar
68d3fb4af8 doc: improve description of chronyd -Q option 2023-04-13 16:22:47 +02:00
Miroslav Lichvar
3c24f2c2ed test: add option to dump traffic to pcaps
This will be useful for debugging.
2023-04-03 14:36:30 +02:00
Miroslav Lichvar
0189dac7d8 ntp: save response when waiting for HW TX timestamp
Rework handling of late HW TX timestamps. Instead of suspending reading
from client-only sockets that have HW TX timestamping enabled, save the
whole response if it is valid and a HW TX timestamp was received for the
source before. When the timestamp is received, or the configurable
timeout is reached, process the saved response again, but skip the
authentication test as the NTS code allows only one response per
request. Only one valid response per source can be saved. If a second
valid response is received while waiting for the timestamp, process both
responses immediately in the order they were received.

The main advantage of this approach is that it works on all sockets, i.e.
even in the symmetric mode and with NTP-over-PTP, and the kernel does
not need to buffer invalid responses.
2023-03-30 15:44:19 +02:00
Miroslav Lichvar
4a11399c2e ntp: rework calculation of transmit timeout
Previously, in the calculation of the next transmission time
corresponding to the current polling interval, the reference point was
the current time in the client mode (i.e. the time when the response is
processed) and the last transmission time in the symmetric mode.

Rework the code to use the last transmission in both modes and make it
independent from the time when the response is processed to avoid extra
delays due to waiting for HW TX timestamps.
2023-03-30 15:28:08 +02:00
Miroslav Lichvar
cf98551ea1 client: avoid casting to long
Use the PRI*32 specifiers in printf formats to avoid casting received
values to unsigned long.
2023-03-30 15:17:52 +02:00
Miroslav Lichvar
5508b01bd8 cmdmon: switch serverstats to 64-bit integers
Update the serverstats response to use the new 64-bit integers.

Don't define a new value for the response as it already had an
incompatible change since the latest release (new fields added for
timestamp counters).
2023-03-30 15:17:52 +02:00
Miroslav Lichvar
907accec87 clientlog: extend serverstats counters to 64 bits
On a busy server the 32-bit counters included in the serverstats report
may overflow every few hours or days. Extend them to 64 bits.
2023-03-30 15:17:52 +02:00
Miroslav Lichvar
a511029cc2 cmdmon: define 64-bit integer
Add a structure for 64-bit integers without requiring 64-bit alignment
to be usable in CMD_Reply without struct packing.

Add utility functions for conversion to/from network order. Avoid using
be64toh() and htobe64() as they don't seem to be available on all
supported systems.
2023-03-30 15:17:50 +02:00
Miroslav Lichvar
0845df7684 cmdmon: add timestamp counters to serverstats report
Add the new RX/TX daemon/kernel/hardware timestamp counters to the
serverstats report.
2023-03-22 10:47:22 +01:00
Miroslav Lichvar
2f961ab36a clientlog: count RX and TX timestamps for each source
Count served timestamps in all combinations of RX/TX and
daemon/kernel/hardware. Repurpose CLG_LogAuthNtpRequest() to update all
NTP-specific stats in one call per accepted request and response.
2023-03-22 09:42:35 +01:00
Miroslav Lichvar
a0cf7f7f12 clientlog: save source of transmit timestamps
Add the timestamp source to the data kept for clients using interleaved
mode to allow extending server statistics.
2023-03-22 09:42:06 +01:00
Miroslav Lichvar
a5f1a113f0 ntp: remove unnecessary check for NULL local timestamp
After 5f4cbaab7e ("ntp: optimize detection of clients using
interleaved mode") the local TX timestamp is saved for all requests
indicating interleaved mode even when no previous RX timestamp is found.
2023-03-22 09:13:53 +01:00
Miroslav Lichvar
5160f14fdc ntp: add maximum PHC poll interval
Specify maxpoll for HW timestamping (default minpoll + 1) to track the
PHC well even when there is little NTP traffic on the interface. After
each PHC reading schedule a timeout according to the maxpoll. Polling
between minpoll and maxpoll is still triggered by HW timestamps.

Wait for the first HW timestamp before adding the timeout to avoid
polling PHCs on interfaces that are enabled in the configuration but
not used for NTP. Add a new scheduling class to separate polling of
different PHCs to avoid too long intervals between processing I/O
events.
2023-03-22 09:13:53 +01:00
Miroslav Lichvar
b0a2ad2535 doc: add missing word in serverstats description 2023-03-22 09:13:53 +01:00
Miroslav Lichvar
ecdde75f8f doc: replace offensive words 2023-03-22 09:13:47 +01:00
Miroslav Lichvar
2d80be9541 doc: mention NTP port number in FAQ 2023-03-22 08:58:20 +01:00
Miroslav Lichvar
ab776ed9d8 ntp: make socket resume timeout configurable
In some cases even the new timeout of 1 millisecond is not sufficient to
get all HW TX timestamps. Add a new directive to allow users to
specify longer timeouts.
2023-03-09 16:13:27 +01:00
Miroslav Lichvar
ccebec3eb6 ntp: increase socket resume timeout to 1 millisecond
This seems to work significantly better on some hardware and is still
shorter than burst interval at the minimum configurable poll.
2023-03-09 16:05:38 +01:00
Miroslav Lichvar
3ea3e0efd7 ntp: add support for multiple suspended sockets
With some hardware it takes milliseconds to get the HW TX timestamp.

Rework the code to handle multiple suspended client-only sockets at the
same time in order to allow longer timeouts, which may overlap for
different sources. Instead of waiting for the first read event simply
suspend the socket and create timeout when the HW TX timestamp is
requested.
2023-03-09 15:49:43 +01:00
Dan Drown
c3e4e3e47a ntp: increment sequence id in PTP messages 2023-03-07 17:00:08 +01:00
Miroslav Lichvar
e949e1d991 test: update description of 106-refclock 2023-03-02 15:34:03 +01:00
Miroslav Lichvar
c8649ccb7e refclock_phc: support multiple extpps refclocks on one PHC
The Linux kernel (as of 6.2) has a shared queue of external timestamps
for all descriptors of the same PHC. If multiple refclocks using the
same PHC and the same or different channels were specified, some
refclocks didn't receive any or most of their timestamps, depending on
the rate and timing of the events (with the previous commit avoiding
blocking reads).

Track extpps-enabled refclocks in an array. Add PHC index to the PHC
instance. When a timestamp is read from the descriptor, provide it to
all refclocks that have the same PHC index and a channel matching the
event.

Make sure the timestamp is different from the previous one in case the
kernel will be improved to duplicate the timestamps for different
descriptors.

Reported-by: Matt Corallo <ntp-lists@mattcorallo.com>
2023-03-02 15:33:59 +01:00
Miroslav Lichvar
39ff7ceeca sys_linux: avoid blocking in reading of external PHC timestamp
The kernel has a common queue for all readers of a PHC device. With
multiple PHC refclocks using the same device some reads blocked. PHC
devices don't seem to support non-blocking reads. Use poll() to check if
a timestamp is available before reading from the descriptor.
2023-03-02 15:13:18 +01:00
Miroslav Lichvar
06945d927b test: add array unit test 2023-03-02 10:41:28 +01:00
Miroslav Lichvar
caf82b1a45 array: add function for removing elements 2023-03-02 10:41:28 +01:00
Miroslav Lichvar
f99b2f633b ntp: count missing samples when waiting for NTS-KE
Count missing samples for the median filter when
NAU_PrepareRequestAuth() is failing.

Fixes: 4234732b08 ("ntp: rework filter option to count missing samples")
2023-02-28 10:02:40 +01:00
Miroslav Lichvar
6270a3eb7c ntp: don't adjust poll interval when waiting for NTS-KE
Don't adjust the NTP polling interval and decrement the burst count when
NAU_PrepareRequestAuth() fails (e.g. no NTS-KE response received yet,
network being down, or the server refusing connections), same as if an
NTP request could not be sent. Rely on the rate limiting implemented in
the NTS code.
2023-02-27 15:36:30 +01:00
Miroslav Lichvar
1daa40a2f7 nts: use shorter NTS-KE retry interval when network is down
When chronyd configured with an NTS source not specified as offline and
resolvable without network was started before the network was up, it was
using an unnecessarily long NTS-KE retry interval, same as if the server
was refusing the connections.

When the network is down, the connect() call made from NKC_Start() on
the non-blocking TCP socket should fail with a different error than
EINPROGRESS and cause NKC_Start() to return with failure. Add a constant
2-second retry interval (matching default iburst) for this case.
2023-02-27 11:43:22 +01:00
Miroslav Lichvar
a1406eded3 nts: destroy NTS-KE client right after failed start
When NKC_Start() fails (e.g. due to unreachable network), don't wait for
the next poll to destroy the client and another poll to create and start
it again.
2023-02-23 15:01:45 +01:00
Miroslav Lichvar
1eb8994c00 client: add -e option to indicate end of response
In a non-tty session with chronyc it is not possible to detect the
end of the response without relying on timeouts, or separate responses
to a repeated command if using the -c option.

Add -e option to end each response with a line containing a single dot.
2023-02-02 17:06:46 +01:00
Miroslav Lichvar
221e5fb501 doc: improve description of refclock filter option 2023-02-02 15:05:05 +01:00
Miroslav Lichvar
ecfbde9872 doc: describe minimum useful ntsrefresh 2023-02-02 14:47:31 +01:00
Miroslav Lichvar
dec07aa844 sourcestats: don't fudge refclock LastRx in sources report
The sample time used in calculation of the last_meas_ago (LastRx) value
in the sources report is aligned to the second to minimize the leak
of the NTP receive timestamp, which could be useful in some attacks.

There is no need to do that with reference clocks, which are often used
with very short polling intervals and an extra second in the LastRx
value can be misinterpreted as a missed sample.
2023-02-02 11:26:04 +01:00
Miroslav Lichvar
5b3d4dfe76 sources: warn about detected falsetickers
Log a warning message for each detected falseticker, but only once
between changes in the selection of the best source. Don't print all
sources when no majority is reached as that case has its own warning
message.
2023-01-26 17:03:35 +01:00
Miroslav Lichvar
dc0f0cd134 sources: enable no majority message before first selection
Add a separate flag to allow the "no majority" message to be logged even
before the first successful selection.
2023-01-26 16:15:47 +01:00
Miroslav Lichvar
bd37efa52e sources: increase log level of no majority message
When the selection fails due to no majority, log the message as a
warning to get the admin's attention.
2023-01-26 16:05:57 +01:00
Miroslav Lichvar
c71185a0e5 doc: add missing description of selection log field 2023-01-26 12:04:28 +01:00
Miroslav Lichvar
f149b7b758 examples: add AES keys to chrony.keys.example 2023-01-25 15:59:45 +01:00
Miroslav Lichvar
883b0dde94 conf: warn if not having read-only access to keys
After dropping root privileges, log a warning message if chronyd
doesn't have read access or has (unnecessary) write access to the
files containing symmetric and server NTS keys.
2023-01-25 14:44:59 +01:00
Miroslav Lichvar
9cba9c8585 keys+nts: warn if loading world-readable/writable key
Log a warning message if the file specified by the keyfile or
ntsserverkey directive is world-readable or writable, which is likely
an insecure misconfiguration. There is no check of directories
containing the file.
2023-01-19 16:39:40 +01:00
Miroslav Lichvar
88e711ad9a refclock: fix preprocessor conditional
Split the new SOCK conditional using __GLIBC_PREREQ macro (which has
arguments) to fix compilation when it is not defined.

Fix also debug message using sizeof(time_t) in case it's enabled on
64-bit systems.

Reported-by: Bryan Christianson <bryan@whatroute.net>
Fixes: badaa83c31 ("refclock: convert mismatched timeval in SOCK messages")
2023-01-19 11:51:41 +01:00
Miroslav Lichvar
badaa83c31 refclock: convert mismatched timeval in SOCK messages
On 32-bit glibc-based (>=2.34) systems, allow the SOCK client to send
messages with timevals using the other time_t size than chrony. If the
length of the received message corresponds to the other size, convert
the timeval and move the rest of the message before its processing.

This is needed for compatibility with the current development version of
gpsd, which forces 64-bit time_t on these systems, while chrony needs to
be compiled with the same time_t as gnutls.
2023-01-18 16:39:25 +01:00
Miroslav Lichvar
bbeec7361c doc: deprecate SHM refclocks in favor of SOCK
The NTP SHM refclock protocol has the following properties:

- the memory segments have a predictable key (first segment 0x4e545030)
- it's expected to work in any order of starting chronyd and the program
  providing samples to chronyd, i.e. both the consumer and producer need
  to be able to create the segment
- the producer and consumer generally don't know under which user is
  the other side running (e.g. gpsd can create the segment as root and
  also as nobody after it drops root privileges)
- there is no authentication of data provided via SHM
- there is no way to restart the protocol

This makes it difficult for chronyd to ensure it is receiving
measurements from the process that the admin expects it to and not some
other process that managed to create the segment before it was started.
It's up to the admin to configure the system so that chronyd or the
producer is started before untrusted applications or users can create
the segment, or at least verify at some point later that the segment was
created with the expected owner and permissions.

There doesn't seem to be a backward-compatible fix of the protocol. Even
if one side could detect the segment had a wrong owner or permissions,
it wouldn't be able to tell the other side to reattach after recreating
the segment with the expected owner and permissions, if it still had the
permissions to do that.

The protocol would need to specify which side is responsible for
creating the segment and the start order would need to strictly follow
that.

As gpsd (likely the most common refclock source for chronyd) now
supports in the latest version SOCK even for message-based timing,
update the man page and FAQ to deprecate SHM in favor of SOCK.
2023-01-12 16:23:15 +01:00
Miroslav Lichvar
6fba5a4a7f examples: add chronyd-restricted.service
This is a more restricted version of the chronyd service intended for
minimal NTP/NTS client configurations. The daemon is started without
root privileges and is allowed to write only to its own runtime, state,
and log directories. It cannot bind to privileged ports in order to
operate as an NTP server, or provide monitoring access over IPv4/IPv6.
It cannot use reference clocks, HW timestamping, RTC tracking, and other
features.
2023-01-11 15:53:24 +01:00
Miroslav Lichvar
26889a8cb7 cmdmon+client: add selectopts command
This command uses the new source function to modify configured selection
options of an NTP source or reference clock.
2022-12-14 17:04:49 +01:00
Miroslav Lichvar
cd278d1826 cmdmon+client: split out conversion of selection options
This will be shared with new command modifying the selection options.
2022-12-14 17:04:49 +01:00
Miroslav Lichvar
3877734814 sources: add function to modify selection options
Add a function to add new selection options or remove existing options
specified in the configuration for both NTP sources and reference
clocks.

Provide a pair of IP address and reference ID to identify the source
depending on the type. Find the source directly in the array of sources
instead of going through the NSR hashtable for NTP sources to not
complicate it unnecessarily.
2022-12-14 17:04:49 +01:00
Miroslav Lichvar
19f2ab9e09 sources: add assertion for instance index 2022-12-14 17:04:46 +01:00
Miroslav Lichvar
3260dc82fe cmdparse: add functions for parsing refclock refid and select options
This will be used in new chronyc command working on refclocks.
2022-12-14 17:04:23 +01:00
Miroslav Lichvar
1a98c5ffa9 ntp: update comment about minimum request spacing 2022-12-05 16:44:38 +01:00
Miroslav Lichvar
8247b8525f log more changes made by chronyc commands
Log important changes from chronyc for auditing purposes.

Add log messages for:
- loaded symmetric keys and server NTS keys (logged also on start)
- modified maxupdateskew and makestep
- enabled/disabled local reference mode (logged also on start)
- reset time smoothing (logged also on clock steps)
- reset sources
2022-12-01 16:58:23 +01:00
Mike Ryan
8901293be8 ntp: set DSCP for IPv6
Chrony's dscp setting currently applies to IPv4 only. This patch sets
the necessary option for IPv6 as well.
2022-11-16 17:15:11 +01:00
Miroslav Lichvar
e789b0817f ntp+cmdmon: log allow/deny commands
Log added NTP and command access restrictions, using INFO severity if
from a chronyc command, DEBUG otherwise (i.e. from the config).
2022-11-16 17:15:07 +01:00
Miroslav Lichvar
d0fd04c0a2 util: add function for printing access subnets 2022-11-16 17:00:45 +01:00
Miroslav Lichvar
7122321249 ntp: log added and removed sources
Log a message when a single NTP source or pool of sources is added or
removed. Use the INFO severity if it's a result of a chronyc command or
(re)load of sourcefiles (which are assumed to change over time), and
DEBUG for other contexts, e.g. sources loaded from the config, sources
removed when pruning pools after reaching maxsources, and other parts of
normal operation.
2022-11-16 17:00:39 +01:00
Miroslav Lichvar
b328c8c348 logging: support context-specific severity
Allow messages to have severity set to INFO or DEBUG depending on the
context in which they are made to allow logging important changes made
from chronyc or sourcefile, but not spam the system log if those changes
are normally expected (e.g. specified in the config).
2022-11-16 16:57:49 +01:00
Holger Hoffstätte
7b97668319 getdate: fix various warnings which will be errors with clang-16
These were found by Gentoo's QA while rebuilding the world with
clang-16: https://bugs.gentoo.org/880519

Signed-off-by: Holger Hoffstätte <holger@applied-asynchrony.com>
2022-11-09 09:17:14 +01:00
Miroslav Lichvar
6f5df7e4a4 nts: warn if server started without ntsdumpdir
If an NTS server is configured without ntsdumpdir, keys will not be
saved and reloaded after restart, which will cause existing cookies
to be invalidated and can cause a short-term denial of service if
the server has so many clients that it cannot handle them all
making an NTS-KE session within one polling interval.

Log a warning message if a server key+certificate is specified without
ntsdumpdir.
2022-10-24 16:39:29 +02:00
Miroslav Lichvar
5a39074e01 nts: fix number of extension fields after failed encryption
If the authenticator SIV encryption fails (e.g. due to wrong nonce
length), decrement the number of extension fields to keep the packet
info consistent.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
c8e57f4350 nts: change ntskeys format to support different algorithms
Specify the AEAD ID for each key saved in the ntskeys file instead of
one ID for all keys. Keep support for loading files in the old format.

This will allow servers to save their keys after upgrading to a new
version with AES-128-GCM-SIV support before the loaded AES-SIV-CMAC-256
keys are rotated out.

If an unsupported key is found, don't load any keys. Also, change the
severity of the error message from debug to error.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
b1230efac3 nts: add support for encrypting cookies with AES-128-GCM-SIV
If AES-128-GCM-SIV is available on the server, use it for encryption of
cookies. This makes them shorter by 4 bytes due to shorter nonce and it
might also improve the server performance.

After server upgrade and restart with ntsdumpdir, the switch will happen
on the second rotation of the server key. Clients should accept shorter
cookies without restarting NTS-KE. The first response will have extra
padding in the authenticator field to make the length symmetric.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
4e1ce88981 nts: make server key access more readable
Get a pointer to the server key instead of repeated indexing.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
790a336eb2 nts: add server support for authentication with AES-128-GCM-SIV
Keep a server SIV instance for each available algorithm.

Select AES-128-GCM-SIV if requested by NTS-KE client as the first
supported algorithm.

Instead of encoding the AEAD ID in the cookie, select the algorithm
according to the length of decrypted keys. (This can work as a long as
all supported algorithms use keys with different lengths.)
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
cc706b50b9 nts: add client support for authentication with AES-128-GCM-SIV
If AES-128-GCM-SIV is available on the client, add it to the requested
algorithms in NTS-KE as the first (preferred) entry.

If supported on the server, it will make the cookies shorter, which
will get the length of NTP messages containing only one cookie below
200 octets. This should make NTS more reliable in networks where longer
NTP packets are filtered as a mitigation against amplification attacks
exploiting the ntpd mode 6/7 protocol.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
73042494bd nts: add support for NTP authenticator field using AES-GCM-SIV
Add support for SIV algorithms which have maximum nonce length shorter
than 16 bytes.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
ec89739d50 nts: make sure encrypted S2C and C2S keys have equal length
Don't allow a cookie to contain keys with different lengths to not break
the assumption made in decoding, if there will ever be a case where this
could be requested.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
4baf999cc3 nts: don't connect to server if missing AES-SIV-CMAC-256
Avoid wasting server resources if the client doesn't support
AES-SIV-CMAC-256 (the only algorithm required on servers).
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
9afd19c29b nts: use signed lengths in NNA_DecryptAuthEF()
Make the types consistent with the rest of the file.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
5dd173c050 siv: add functions to return min and max nonce length
While AES-SIV-CMAC allows nonces of any length, AES-GCM-SIV requires
exactly 12 bytes, which is less than the unpadded minimum length of 16
used in the NTS authenticator field. These functions will be needed to
support both ciphers in the NTS code.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
5caf0ad187 siv: add support for AES-128-GCM-SIV in Nettle
This is a newer nonce misuse-resistant cipher specified in RFC 8452,
which is now supported in the development code of the Nettle library.

The advantages over AES-SIV-CMAC-256 are shorter keys and better
performance.
2022-10-19 15:50:31 +02:00
Miroslav Lichvar
17d2291a84 doc: improve ntsrotate description 2022-10-19 15:33:04 +02:00
Miroslav Lichvar
a6179261a7 doc: fix wrong name of authselectmode directive 2022-10-17 15:24:24 +02:00
Miroslav Lichvar
098e0c43fc test: add float-cast-overflow to 003-sanitizers test 2022-09-20 10:56:28 +02:00
Miroslav Lichvar
7b197953e8 update copyright years 2022-08-29 15:04:33 +02:00
Miroslav Lichvar
9dcace0fc4 doc: improve description of server directive 2022-08-29 15:03:59 +02:00
Miroslav Lichvar
a07ac38331 doc: improve description of system time in tracking report 2022-08-29 12:39:22 +02:00
Miroslav Lichvar
166e43b13e cmdmon: add good responses to ntpdata report 2022-08-18 11:59:40 +02:00
Miroslav Lichvar
b84d6759f9 ntp: initialize remote address in ntpdata report
Don't wait for the first response with setting the address.
2022-08-17 16:14:38 +02:00
Miroslav Lichvar
f323c814af doc: update NEWS 2022-08-11 10:32:58 +02:00
Miroslav Lichvar
19b47dcbc9 doc: mention maxdelayquant in FAQ 2022-08-10 15:32:54 +02:00
Miroslav Lichvar
5edeadcbd9 test: extend 106-refclock test 2022-08-09 16:53:12 +02:00
Miroslav Lichvar
d91ae2094f configure: disable arc4random on Linux
In glibc 2.36 was added the arc4random family of functions. However,
unlike on other supported systems, it is not a user-space PRNG
implementation. It just wraps the getrandom() system call with no
buffering, which causes a performance loss on NTP servers due to
the function being called twice for each response to add randomness
to the RX and TX timestamp below the clock precision.

Don't check for arc4random on Linux to keep using the buffered
getrandom().
2022-08-03 15:23:38 +02:00
Miroslav Lichvar
30a5845098 configure: avoid -Wnonnull warnings
Replace NULL in test code of functions which have (at least in glibc) or
could have arguments marked as nonnull to avoid the -Wnonnull warnings,
which breaks the detection with the -Werror option.
2022-08-03 14:53:35 +02:00
Miroslav Lichvar
0f367efac5 doc: suggest self-signed certificates for NTS in FAQ 2022-08-03 13:58:28 +02:00
Miroslav Lichvar
24c011d4a6 test: catch definite leaks with valgrind 2022-08-02 15:09:47 +02:00
Miroslav Lichvar
0c2cdd2fb1 test: fix ntp_core unit test to disable source selection
If the randomly generated timestamps are close to the current time, the
source can be selected for synchronization, which causes a crash when
logging the source name due to uninitialized ntp_sources.

Specify the source with the noselect option to prevent selection.
2022-08-02 14:44:05 +02:00
Miroslav Lichvar
cd1a666e1b test: fix sources unit test to call SRC_ReportSource() correctly
Call the function with current time instead of latest sample of the
first source to avoid undefined conversion of negative double to long
int.

Fixes: 07600cbd71 ("test: extend sources unit test")
2022-08-02 14:44:05 +02:00
Miroslav Lichvar
070b4f69d0 ntp: add maxdelayquant option
Add a new test for maximum delay using a long-term estimate of a
p-quantile of the peer delay. If enabled, it replaces the
maxdelaydevratio test. It's main advantage is that it is not sensitive
to outliers corrupting the minimum delay.

As it can take a large number of samples for the estimate to reach the
expected value and adapt to a new value after a network change, the
option is recommended only for local networks with very short polling
intervals.
2022-07-21 16:05:48 +02:00
Miroslav Lichvar
851c823b42 doc: improve description of maxdelay* options 2022-07-21 15:33:35 +02:00
Miroslav Lichvar
df80274644 quantiles: add function to get minimum k 2022-07-21 15:33:35 +02:00
Miroslav Lichvar
bb2d68ddf9 test: extend 101-poll and 127-filter tests 2022-07-21 15:33:35 +02:00
Miroslav Lichvar
685d8f725b ntp: change minimum allowed poll to -7
Change the minimum poll allowed in configuration from -6 to -7. This
matches some PTP profiles using 128 sync messages per second.
2022-07-21 15:33:35 +02:00
Miroslav Lichvar
4234732b08 ntp: rework filter option to count missing samples
Instead of waiting for the sample filter to accumulate the specified
number of samples and then deciding if the result is acceptable, count
missing samples and get the result after the specified number of polls.

This should work better when samples are dropped at a high rate. The
source and clock update interval will be stable as long as at least
one sample can be collected.
2022-07-21 15:33:08 +02:00
Miroslav Lichvar
a16094adfb samplefilt: add debug message for selected samples 2022-07-21 14:17:22 +02:00
Miroslav Lichvar
a4349b13df samplefilt: add function to get maximum number of samples 2022-07-21 14:17:22 +02:00
Miroslav Lichvar
3556dadea1 ntp: enable sub-second poll sooner with filter option
When the minimum round-trip time is checked to enable a sub-second
polling interval, consider also the last sample in the filter to avoid
waiting for the first sample to be accumulated in sourcestats.
2022-07-21 14:17:22 +02:00
Miroslav Lichvar
220e6d1907 ntp: fix initial poll to follow non-LAN minimum
If a sub-second polling interval is configured, initialize the local
poll to 0 to avoid a shorter interval between the first and second
request in case no response to the first request is received (in time).
2022-07-21 14:17:22 +02:00
Miroslav Lichvar
a738037705 client: check for stdout errors
Return with an error code from chronyc if the command is expected to
print some data and fflush() or ferror() indicates an error. This should
make it easier for scripts to detect missing data when redirected to a
file.
2022-07-21 14:17:11 +02:00
Yury Vostrikov
7daf34675a refclock: remove unused struct MedianFilter
Filtering was moved to a separate source file in commit
c498c21fad ("refclock: split off median filter). It looks like
MedianFilter struct somehow survived the split. Remove it to reduce
confusion.
2022-07-11 10:20:44 +02:00
Miroslav Lichvar
de598c2310 main: add log message for timeout reached with -t option
This should make it more clear why chronyd exits if -q/-Q does not
finish before the timeout is reached.
2022-06-30 14:46:18 +02:00
Miroslav Lichvar
91cc4dbb12 doc: improve description of test A in measurements log 2022-06-30 10:20:49 +02:00
Miroslav Lichvar
0ae6f2485b ntp: don't use first response in interleaved mode
With the first interleaved response coming after a basic response the
client is forced to select the four timestamps covering most of the last
polling interval, which makes measured delay very sensitive to the
frequency offset between server and client. To avoid corrupting the
minimum delay held in sourcestats (which can cause testC failures),
reject the first interleaved response in the client/server mode as
failing the test A.

This does not change anything for the symmetric mode, where both sets of
the four timestamps generally cover a significant part of the polling
interval.
2022-06-30 10:18:48 +02:00
Miroslav Lichvar
52ec694d2b test: fix server interleaved mode in ntp_core unit test 2022-06-28 15:43:25 +02:00
Miroslav Lichvar
e2e07af8a4 doc: improve and add more questions to FAQ 2022-06-23 14:29:21 +02:00
Miroslav Lichvar
2ed88c31c7 sys_generic: damp slew oscillation due to delayed stop
If the computer is overloaded so much that chronyd cannot stop a slew
within one second of the scheduled end and the actual duration is more
than doubled (2 seconds with the minimum duration of 1 second), the
overshoot will be larger than the intended correction. If these
conditions persist, the oscillation will grow up to the maximum offset
allowed by maxslewrate and the delay in stopping.

Monitor the excess duration as an exponentially decaying maximum value
and don't allow any slews shorter than 5 times the value to damp the
oscillation. Ignore delays longer than 100 seconds, assuming they have a
different cause (e.g. the system was suspended and resumed) and are
already handled in the scheduler by triggering cancellation of the
ongoing slew.

This should also make it safer to shorten the minimum duration if
needed.

Reported-by: Daniel Franke <dff@amazon.com>
2022-06-15 17:42:49 +02:00
Miroslav Lichvar
af8e4a5115 sys_generic: rename slew constants 2022-06-14 16:02:06 +02:00
Miroslav Lichvar
f503a9a490 test: improve 133-hwtimestamp test 2022-06-09 16:01:22 +02:00
Miroslav Lichvar
9c64fbb9c4 hwclock: improve filtering of readings
Estimate the 1st and 2nd 10-quantile of the reading delay and accept
only readings between them unless the error of the offset predicted from
previous samples is larger than the minimum reading error. With the 25
PHC readings per ioctl it should combine about 2-3 readings.

This should improve hwclock tracking and synchronization stability when
a PHC reading delay occasionally falls below the normal expected
minimum, or all readings in the batch are delayed significantly (e.g.
due to high PCIe load).
2022-06-09 16:01:22 +02:00
Miroslav Lichvar
b428f901c7 quantiles: add support for quantile estimation
Add estimation of quantiles using the Frugal-2U streaming algorithm
(https://arxiv.org/pdf/1407.1121v1.pdf). It does not need to save
previous samples and adapts to changes in the distribution.

Allow multiple estimates of the same quantile and select the median for
better stability.
2022-06-09 16:01:19 +02:00
Miroslav Lichvar
09b7f77f9a hwclock: refactor processing of PHC readings
Move processing of PHC readings from sys_linux to hwclock, where
statistics can be collected and filtering improved.

In the PHC refclock driver accumulate the samples even if not in the
external timestamping mode to update the context which will be needed
for improved filtering.
2022-06-09 12:04:20 +02:00
Miroslav Lichvar
c23c0b8484 ntp: convert HW timestamp even if PHC reading fails
Reading of PHC can fail occasionally on some hardware. If that happens,
don't abort the conversion of the timestamp that triggered the reading.
2022-06-08 15:30:05 +02:00
Miroslav Lichvar
d530055917 sys_linux: increase number of PHC readings
Increase the number of requested readings from 10 to 25 - the maximum
accepted by the PTP_SYS_OFFSET* ioctls. This should improve stability of
HW clock tracking and PHC refclock.
2022-06-08 14:29:35 +02:00
Miroslav Lichvar
f41d09e19f doc: improve hwtimestamp description
Latest versions of ethtool print only the shorter lower-case names of
capabilities and filters. Explain that chronyd doesn't synchronize the
PHC and refer to the new vclock feature of the kernel, which should be
used by applications that need a synchronized PHC (e.g. ptp4l and
phc2sys) in order to not interfere with chronyd.
2022-05-19 10:50:10 +02:00
Miroslav Lichvar
46030d9d3e sources: add selection log
Add an option to enable selection log, capturing some data from the
selectdata report.
2022-05-19 08:23:05 +02:00
Miroslav Lichvar
02ccd3a3c7 sourcestats: don't load samples from future
When loading a dumped file, make sure there are no sample times in
future relative to the current system time (e.g. after reboot with
missing RTC).
2022-05-18 16:38:41 +02:00
Miroslav Lichvar
9cc609c4b0 local: cancel remaining correction after external step
Instead of the generic clock driver silently zeroing the remaining
offset after detecting an external step, cancel it properly with the
slew handlers in order to correct timestamps that are not reset in
handling of the unknown step (e.g. the NTP local TX).
2022-05-16 16:28:36 +02:00
Miroslav Lichvar
a0a496dcb4 refclock: set minimum maxlockage in local mode
Use 3 as the minimum maxlockage in the local mode to avoid disruptions
due to losing the lock when a single sample is missed, e.g. when the PPS
driver polling interval is slightly longer than the pulse interval and a
pulse is skipped.
2022-05-11 14:28:58 +02:00
Miroslav Lichvar
8d08486edf refclock: restart local mode after losing lock
A refclock in the local mode is locked to itself. When the maxlockage
check failed after missing some samples, it failed permanently and the
refclock was not able to accumulate any new samples.

When the check fails, drop all samples and reset the source to start
from scratch.

Reported-by: Dan Drown <dan-ntp@drown.org>
2022-05-11 14:27:18 +02:00
Miroslav Lichvar
a3b376cf0a refclock: fix invalid warning in local mode
A refclock in the local mode is locked to itself by design.

Reported-by: Dan Drown <dan-ntp@drown.org>
2022-05-11 14:22:41 +02:00
Miroslav Lichvar
e66f1df89d samplefilt: drop last sample in SPF_DropSamples()
When SPF_DropSamples() is called, don't keep the last sample to be
retrieved by SPF_GetLastSample(). It should be kept only after
filtering.
2022-05-11 14:21:09 +02:00
Miroslav Lichvar
35220aac9d siv: set key directly with gnutls
A new function is provided by the latest gnutls (should be in 3.7.5) to
set the key of an AEAD cipher. If available, use it to avoid destroying
and creating a new SIV instance with each key change.

This improves the server NTS-NTP performance if using gnutls for SIV.
2022-05-11 12:22:33 +02:00
Miroslav Lichvar
5b04f3ca90 doc: improve description of chronyc -h option 2022-05-05 14:34:15 +02:00
Miroslav Lichvar
beb1c36136 doc: improve maxchange description 2022-05-05 14:34:08 +02:00
Miroslav Lichvar
da3495c472 nts: don't exit if initialization of priority cache fails
Initialization of the gnutls priority cache can fail depending on the
system crypto policy (e.g. disabled TLS1.3). Log an error mentioning
TLS, but continue to run without the server/client credentials.
2022-05-05 10:27:48 +02:00
Miroslav Lichvar
356771c0c3 client: rework command catenation
Use snprintf() instead of strcat() and don't try to parse commands
longer than 2048 characters to make it consistent with the chrony.conf
parser, avoid memory allocation, and not rely on the system ARG_MAX to
keep the length sane.
2022-05-04 14:17:32 +02:00
Miroslav Lichvar
fca8966ada examples: replace grep command in NM dispatcher script
Some grep implementations detect binary data and return success without
matching whole line. This might be an issue for the DHCPv6 NTP FQDN
check. The GNU grep in the C locale seems to check only for the NUL
character, which cannot be passed in an environment variable, but other
implementations might behave differently and there doesn't seem to be a
portable way to force matching the whole line.

Instead of the grep command, check for invalid characters by comparing
the length of the input passed through "tr -d -c".
2022-03-23 15:36:17 +01:00
Miroslav Lichvar
25f80a1a9d doc: include gnutls in libraries providing SECHASH feature 2022-03-16 14:54:12 +01:00
Miroslav Lichvar
1219f99935 ntp: keep original source IP address
When an added source is specified by IP address, save the original
string instead of formatting a new string from the parsed address, which
can be different (e.g. compressed vs expanded IPv6 address).

This fixes the chronyc sourcename command and -N option to print the IP
address exactly as it was specified in the configuration file or chronyc
add command.
2022-03-10 09:54:31 +01:00
Miroslav Lichvar
33a1fe7a9c ntp: split out conf_id allocation 2022-03-10 09:53:29 +01:00
Miroslav Lichvar
eed0a0de56 test: update 007-cmdmon system test for recent changes
The new unsynchronised source state is now reported in selectdata before
the first measurement.

Fixes: c29f8888c767 ("sources: handle unsynchronized sources in selection")
2022-03-07 16:00:20 +01:00
Miroslav Lichvar
07600cbd71 test: extend sources unit test 2022-03-02 12:17:02 +01:00
Miroslav Lichvar
f2e341b5ed sources: improve debug messages
Print source status as char and print the name instead of index in
combining.
2022-03-02 12:11:24 +01:00
Miroslav Lichvar
55717c1ccd refclock: trim offset in local mode
With the local option, trim offset larger than 1 second to not lose
precision after large steps of the clock.
2022-02-24 11:40:26 +01:00
Miroslav Lichvar
d5e645eb38 samplefilt: add function to correct accumulated offsets
Analogously to SST_CorrectOffset(), add SPF_CorrectOffset() to correct
the offsets accumulated in the filter.
2022-02-24 11:40:01 +01:00
Miroslav Lichvar
3196630fb9 sys_linux: don't require configurable pin for external PPS
Some PHCs that have a PPS input don't have configurable pins (their
function is hardcoded). Accept a negative pin index to skip the pin
configuration before requesting external timestamping.
2022-02-23 14:43:39 +01:00
Miroslav Lichvar
663dde1ad7 refclock: improve precision with large offset
If a SHM or PHC refclock has a very large offset compensated by the
offset option, or ignored with the pps or local option, there is a
persistent loss of precision in the calculation of the sample offset
using the double format.

Rework the code to delay the calculation of the accumulated offset to
include the specificed compensation and remaining correction of the
system clock, where the calculation can be split to improve the
precision. In the pps mode ignore integer seconds competely.

The precision of the SOCK refclock is now limited to 1 nanosecond due to
the extra double->timespec->double conversion.
2022-02-23 14:43:39 +01:00
Miroslav Lichvar
62757cda49 refclock: add local option
Add "local" option to specify that the reference clock is an
unsynchronized clock which is more stable than the system clock (e.g.
TCXO, OCXO, or atomic clock) and it should be used as a local standard
to stabilize the system clock.

Handle the local refclock as a PPS refclock locked to itself which gives
the unsynchronized status to be ignored in the source selection. Wait
for the refclock to get at least minsamples samples and adjust the clock
directly to follow changes in the refclock's sourcestats frequency and
offset.

There should be at most one refclock specified with this option.
2022-02-23 14:43:39 +01:00
Miroslav Lichvar
af6ae9186b reference: allow clock adjustments without updating reference
Add support for accumulating frequency and time offset without changing
the reference parameters and calling the local parameter change
handlers.

This will allow an unsynchronized source to operate below other sources
in order to stabilize the clock.
2022-02-23 14:43:39 +01:00
Miroslav Lichvar
4c29f8888c sources: handle unsynchronized sources in selection
Allow sources to accumulate samples with the leap status set to not
synchronized. Define a new state for them to be ignored in the
selection. This is intended for sources that are never synchronized and
will be used only for stabilization.
2022-02-23 14:42:40 +01:00
Miroslav Lichvar
d06ae4a60e sourcestats: add function to get minsamples 2022-02-10 16:38:50 +01:00
Miroslav Lichvar
f9af2f9733 sourcestats: clamp minsamples and maxsamples in initialization
Don't leave the variables set to values outside their effective range.
This has no functional impact, but makes it clear what is the precedence
of the two settings.
2022-02-10 16:31:46 +01:00
Miroslav Lichvar
43ae0131cd sourcestats: use constant for required number of samples 2022-02-10 15:16:08 +01:00
Michael Hudson-Doyle
8bb8f15a7d sys_linux: allow rseq in seccomp filter
Libc 2.35 will use rseq syscalls [1][2] by default and thereby
break chrony in seccomp isolation.

[1]: https://www.efficios.com/blog/2019/02/08/linux-restartable-sequences/
[2]: https://sourceware.org/pipermail/libc-alpha/2022-February/136040.html

Tested-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
Reviewed-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
Signed-off-by: Michael Hudson-Doyle <michael.hudson@canonical.com>
Signed-off-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2022-02-09 10:46:09 +01:00
Miroslav Lichvar
e55f174bd3 examples: handle more actions in NM dispatcher script
Run the chronyc onoffline command also when the connectivity-change
and dhcp6-change actions are reported by the NetworkManager dispatcher.

The latter should not be necessary, but there currently doesn't seem to
be any action for IPv6 becoming routable after duplicate address
detection, so at least in networks using DHCPv6, IPv6 NTP servers should
not be stuck in the offline state from a previously reported action.
2022-02-07 17:04:19 +01:00
Miroslav Lichvar
5bd13c8d59 examples: support DHCPv6 NTP servers in NM dispatcher script
Latest NetworkManager code provides NTP servers from the DHCPv6 NTP
option (RFC 5908) in the DHCP6_DHCP6_NTP_SERVERS variable to dispatcher
scripts.

Check for invalid characters (which can come from the FQDN suboption)
and include the servers in the interface-specific sources file.
2022-02-07 16:59:10 +01:00
Miroslav Lichvar
759580aa6f client: fix waitsync command to reconnect to server
If chronyc waitsync was started before chronyd, it would try all
addresses (Unix socket, IPv4, IPv6) and get stuck with no address, not
getting any response later when chronyd was running.

Reset the address index in open_io() when returning with failure to
allow the next call to start with the first address again.

Reported-by: Jan Mikkelsen <janm@transactionware.com>
2022-01-26 16:11:01 +01:00
Vincent Blut
b61cbed689 test: ensure awk commands in 008-ntpera return an integer
Some awk interpreters (e.g. mawk) print long integers in exponential
notation skewing the test result.
2022-01-13 09:40:12 +01:00
Miroslav Lichvar
2ac2247756 doc: update NEWS 2021-12-16 13:17:42 +01:00
Miroslav Lichvar
55f48b14b7 update copyright years 2021-12-16 13:17:42 +01:00
Miroslav Lichvar
3dfac33858 ntp: set local address on PTP socket on FreeBSD
Fix the FreeBSD-specific code checking for a bound IPv4 socket to
include the new PTP port. This should fix a multihomed server to respond
to NTP-over-PTP requests from the address which received the request.

Fixes: be3158c4e5 ("ntp: add support for NTP over PTP")
2021-12-16 13:17:42 +01:00
Miroslav Lichvar
d5f2401421 cmdmon: fix transmit_reply() to not read uninitialized data
In the FreeBSD-specific code checking for a bound IPv4 socket, make
sure it is not a Unix domain address to avoid reading uninitialized
IP-specific fields.

This fixes an error reported by valgrind.
2021-12-16 11:49:15 +01:00
Miroslav Lichvar
fb0570cc73 socket: zero sockaddr_un to initialize sa_len
Zero the whole sockaddr struct before calling bind() and connect() to
initialize the FreeBSD-specific sa_len field.

This fixes errors reported by valgrind.
2021-12-16 10:48:31 +01:00
Miroslav Lichvar
43936ba0d1 clientlog: remove unnecessary operation in timestamp conversion 2021-12-14 10:47:26 +01:00
Miroslav Lichvar
f2ba20f293 ntp: avoid unnecessary source lookups
Avoid searching the hash table of sources when a packet in the client
mode is received. It cannot be a response from our source. Analogously,
avoid source lookups for transmitted packets in the server mode. This
doesn't change anything for packets in symmetric modes, which can be
requests and responses at the same time.

This slightly improves the maximum packet rate handled as a server.
2021-12-14 10:47:10 +01:00
Miroslav Lichvar
fcd384523b ntp: fix typo in comment 2021-12-14 10:34:19 +01:00
Miroslav Lichvar
48bce351bf doc: describe use case for leapsecmode ignore option 2021-12-09 17:13:09 +01:00
Miroslav Lichvar
25f93875d9 doc: switch Solaris support to illumos
For a long time, the Solaris support in chrony wasn't tested on a real
Solaris system, but on illumos/OpenIndiana, which was forked from
OpenSolaris when it was discontinued in 2010.

While Solaris and illumos might have not diverged enough to make a
difference for chrony, replace Solaris in the documentation with illumos
to make it clear which system is actually supported by the chrony
project.
2021-12-09 17:03:56 +01:00
Miroslav Lichvar
ebc610fcb3 sys_solaris: disable kernel dosynctodr
The dosynctodr kernel variable needs to be set to 0 to block automatic
synchronization of the system clock to the hardware clock. chronyd used
to disable dosynctodr on Solaris versions before 2.6, but it seems it is
now needed even on current versions as the clock driver sets frequency
only without calling adjtime() or setting the ntp_adjtime() PLL offset.

This issue was reproduced and fix tested on current OpenIndiana.

Fixes: 8feb37df2b ("sys_solaris: use timex driver")
2021-12-07 12:18:56 +01:00
Miroslav Lichvar
264957a443 doc: update NEWS 2021-12-02 11:19:40 +01:00
Miroslav Lichvar
af611b5842 ntp: limit total monotonic offset correction
In addition to the 16s limit in per-response change in the monotonic
offset, don't allow the total accumulated offset injected in sourcestats
to be larger than 16 seconds.
2021-12-02 11:01:01 +01:00
Miroslav Lichvar
1c1ca1d12f test: update and improve 003-sanitizers test 2021-12-01 10:13:14 +01:00
Miroslav Lichvar
c506b9aac8 test: allow another inaccuracy in util unit test
A 1ns error in UTI_AdjustTimespec() was observed with an i686 build.
2021-12-01 09:26:41 +01:00
Miroslav Lichvar
2eefa61f10 test: fix 008-ntpera test for arbitrary NTP era split 2021-12-01 09:24:13 +01:00
Miroslav Lichvar
89a5e21e4d reference: check for unset leap_when in is_leap_close()
Check that the leap_when variable is set before testing a timestamp for
being close to a leap second. This allows the first measurement to be
accepted if starting at the Unix epoch (e.g. in a test).
2021-12-01 09:22:26 +01:00
Miroslav Lichvar
6a79771898 ntp: check for zero timestamp in initial TX timeout
Calculate the delay since the previous transmission only if the
TX timestamp is actually set. This removes an unnecessary delay when
starting at the Unix epoch in 1970 (e.g. in a test).
2021-12-01 09:22:26 +01:00
Miroslav Lichvar
53353529cf rtc: don't drop first sample after initial trim
It seems there is no longer an issue with the first sample after the
initial trim and it can be accumulated. It might have been a workaround
for an unrelated bug which was fixed since then.

This fixes the number of samples reported in rtcdata briefly jumping to
65535 and also brings back the expectation that n_samples is never
negative.
2021-12-01 09:22:26 +01:00
Miroslav Lichvar
22bfdf204f rtc: drop rtc_trim array
It always contained zero values and had no effect on anything.
2021-12-01 09:22:26 +01:00
Miroslav Lichvar
fc28e9ae56 rtc: remove unnecessary variable initializations 2021-12-01 09:22:26 +01:00
Miroslav Lichvar
17e6258694 doc: update FAQ 2021-11-24 15:07:22 +01:00
Miroslav Lichvar
d7a444593f ntp: improve check for PTP socket
Check for INVALID_SOCK_FD in case the PTP port is enabled, but opening
one of the PTP sockets failed.
2021-11-24 15:07:04 +01:00
Miroslav Lichvar
701b9415a5 test: update 110-chronyc test 2021-11-24 15:06:36 +01:00
Miroslav Lichvar
d5894c0738 main: add assertions for timespec signedness
Some of the code (e.g. util and clientlog) may work with negative
values. Require that time_t and the tv_nsec types are signed. This seems
to be the case on all supported systems, but it it is not required by
POSIX.
2021-11-24 11:17:24 +01:00
Miroslav Lichvar
a0a9560258 util: reset GetRandom functions in helpers after fork
Close /dev/urandom and drop cached getrandom() data after forking helper
processes to avoid them getting the same sequence of random numbers
(e.g. two NTS-KE helpers generating cookies with identical nonces).
arc4random() is assumed to be able to detect forks and reseed
automatically.

This is not strictly necessary with the current code, which does not use
the GetRandom functions before the NTS-KE helper processes are forked,
but that could change in future.

Also, call the reset function before exit to close /dev/urandom in order
to avoid valgrind reporting the file object as "still reachable".
2021-11-24 11:17:24 +01:00
Miroslav Lichvar
09067e06d3 ntp: fix exp1 EF search in process_response()
Don't ignore the magic field when searching for the exp1 extension
field in a received response. If there were two exp1 fields in the
packet, and only one of them had the expected magic value, it should
pick the right one.

Fixes: 2319f72b29 ("ntp: add client support for experimental extension field")
2021-11-24 11:17:24 +01:00
Miroslav Lichvar
dbbdd5af06 ntp: make default NTP version with xleave to be always 4
If the xleave option is enabled, ignore the key option and the hash
length. Always use version 4 as the default to get interleaved responses
from new chrony servers.
2021-11-22 17:03:40 +01:00
Miroslav Lichvar
7f984cf7fa ntp: limit interleaved responses to NTPv4
The interleaved modes are being specified for NTPv4 only. As a server,
detect interleaved requests only in NTPv4 packets.

Clients and peers can still send interleaved requests in lower-version
packets if configured with the version option.
2021-11-22 17:02:49 +01:00
Miroslav Lichvar
8df49b799f ntp: suppress monotonic timestamp if smoothing is enabled
Frequency transfer and time smoothing are conflicting features. Set the
monotonic timestamp in the experimental extension field to zero
(invalid) if time smoothing is activated.
2021-11-22 15:52:01 +01:00
Miroslav Lichvar
e7c2f71cea ntp: add special value to experimental root delay/disp
The maximum value of the new 32-bit fields is slightly less than 16,
which can cause the NTP test #7 to pass for a server which has a zero
root delay but maximum root dispersion.

Interpret the maximum value as the maximum value of the original 32-bit
fields (~65536.0 seconds) for better compatibility with NTPv4.
2021-11-22 15:21:29 +01:00
Miroslav Lichvar
219085b8f6 test: add 144-exp1 test 2021-11-16 10:36:26 +01:00
Miroslav Lichvar
2319f72b29 ntp: add client support for experimental extension field
Add "extfield F323" option to include the new extension field in
requests. If the server responds with this field, use the root
delay/dispersion and monotonic timestamp. Accumulate changes in the
offset between the monotonic and real-time receive timestamps and use
it for the correction of previous offsets in sourcestats. In the
interleaved mode, cancel out the latest change in the offset in
timestamps of the previous request and response, which were captured
before the change actually happened.
2021-11-16 10:34:32 +01:00
Miroslav Lichvar
72f7d09f58 sourcestats: add function to correct accumulated offsets
This will be needed to follow server time corrections in order to
better estimate frequency.
2021-11-16 10:23:20 +01:00
Miroslav Lichvar
0bf39c0ab9 ntp: add server support for experimental extension field
Maintain a server monotonic timescale needed for the experimental
extension field. It follows the best estimate of frequency without
time corrections. Implement it as an offset relative to the NTP time,
starting at zero, using a slew handler to cancel time corrections of the
NTP clock. The 32-bit epoch ID is set to a random value on start and
every step of the system clock.
2021-11-16 10:23:20 +01:00
Miroslav Lichvar
2e126ed2b5 util: add functions for converting new root delay/dispersion 2021-11-16 10:23:20 +01:00
Miroslav Lichvar
a652ce7d0e util: add function to subtract NTP timestamps
This will be needed to work with monotonic timestamps, which don't have
a stable epoch and cannot be converted to timespec.
2021-11-16 10:23:20 +01:00
Miroslav Lichvar
a97ca73704 ntp: add pre-NTPv5 experimental extension field
Add an experimental extension field for some features that were proposed
for NTPv5. Higher-resolution root delay and dispersion (using 28-bit
fraction) are added. A monotonic receive timestamp will allow a
frequency transfer between the server and client. The client will be
able to separate the server's time corrections from frequency
corrections by tracking the offset between the real-time and monotonic
receive timestamps.

The field has a type of 0xF323 from the new experimental range proposed
by the NTP working group. Include a magic 32-bit value in the field to
avoid interoperability issues if a different implementation choses the
same type for its own experimental field. The value will be changed on
incompatible changes to avoid issues between two different chrony
versions.
2021-11-16 10:23:15 +01:00
Miroslav Lichvar
125d7a5c32 ntp: prepare for non-authentication extension fields
Add a new variable to the packet info structure with flags for extension
fields included in received packets and add a new parameter to
transmit_packet() to add the fields to transmitted packets.
2021-11-16 10:21:39 +01:00
Miroslav Lichvar
36356ef033 ntp: move initial packet parsing from ntp_auth to ntp_core
Since commit fdfcabd79b ("ntp: drop support for long NTPv4 MACs"), the
parser doesn't need to check validify of MACs in NTPv4 packets to
distinguish them from extension fields. Move the parser to ntp_core to
avoid having a separate iteration looking for non-authentication
extension fields.
2021-11-16 10:00:31 +01:00
Miroslav Lichvar
a2d1569455 socket: increase message buffer length
Add extra space to the socket message buffer to be able to receive
maximum-length NTP-over-PTP SW/HW-timestamped messages from the Linux
error queue (which are looped back as layer-2 frames).
2021-10-27 16:22:12 +02:00
Miroslav Lichvar
952c3b2528 ntp: use previous root delay/disp in interleaved mode
When calculating the root delay and dispersion of a sample measured in
the interleaved mode, use the root delay and dispersion values from
the previous response (to which the TX timestamp corresponds). If the TX
timestamp is combined with the RX timestamp of the latest response (e.g.
in the symmetric mode), use the maximum of the previous and latest root
delay/dispersion.
2021-10-27 16:22:12 +02:00
Miroslav Lichvar
d92d24ad7f test: extend 122-xleave test 2021-10-27 16:22:12 +02:00
Miroslav Lichvar
bc33e1cda1 clientlog: undo clock adjustments in updated TX timestamps
When the server clock was updated between saving of the RX timestamp and
updating the TX timestamp, a client using interleaved mode with the four
timestamps which minimize error in measured delay (e.g. chrony) had the
server clock adjustment included in the measured delay, which could
disrupt the sample filtering and weighting.

Add a handler to track the slew epoch and remember the last offset. Undo
the adjustment in TX timestamps which have their RX timestamp in the
previous epoch to fix the delay observed by the clients.

If an unknown clock step is detected, drop all timestamps.
2021-10-27 16:22:12 +02:00
Miroslav Lichvar
189bf9c536 ntp: don't save timestamps if transmit_packet() failed
Don't save server RX and TX timestamp to clientlog if the transmission
or authentication failed (e.g. packet is handled in ntp_signd). They
will not be needed.
2021-10-27 16:22:08 +02:00
Miroslav Lichvar
c5dde9b66a ntp: initialize saved TX timestamp
Zero the initial TX timestamp which is saved for the interleaved
mode in case there is no previous timestamp saved in clientlog and
transmit_packet() does not generate a new one (e.g. due to failure in
authentication).

Fixes: 5f4cbaab7e ("ntp: optimize detection of clients using interleaved mode")
2021-10-27 12:31:36 +02:00
Miroslav Lichvar
1fb60f8db8 cmdmon: add interleaved stats to serverstats
Report the number of received interleaved requests and current timestamp
count with their span.

Expand the serverstats description in chronyc man page.
2021-10-21 17:04:51 +02:00
Miroslav Lichvar
2f05287e15 test: improve clientlog unit test
Test also timestamp maps with smaller maximum sizes.
2021-10-19 15:15:46 +02:00
Miroslav Lichvar
61226cda8c ntp: don't capture TX timestamps if clientlog is disabled
When responding to a request, don't waste time with TX timestamping
if the timestamp will not be saved (i.e. clientlog is disabled).

Fixes: 5f4cbaab7e ("ntp: optimize detection of clients using interleaved mode")
2021-10-19 15:15:46 +02:00
Miroslav Lichvar
26b51d841e doc: improve clientloglimit description 2021-10-14 17:17:10 +02:00
Miroslav Lichvar
5f4cbaab7e ntp: optimize detection of clients using interleaved mode
Use the lowest bit of the server RX and TX timestamp as a flag
indicating RX timestamp. This allows the server to detect potential
interleaved requests without having to save all its RX timestamps. It
significantly reduces the amount of memory needed to support clients
using the interleaved mode if most of the server's clients are using the
basic mode (e.g. a public server).

Capture the TX timestamp on the first response to the request which has
the flag set to not further delay the first interleaved response.

False positives are possible with broken clients which set the origin
timestamp to something else than zero or the server RX or TX timestamp.
This causes an unnecessary RX timestamp to be saved and TX timestamp
captured and saved.
2021-10-14 17:17:05 +02:00
Miroslav Lichvar
7a80647fb4 ntp: move authentication calls in transmit_packet()
Move the calls resetting and generating authentication data out of the
loop checking for unique TX timestamp. This allows the timestamps to be
manipulated after the check.
2021-10-14 16:42:23 +02:00
Miroslav Lichvar
14b8df3702 clientlog: separate NTP timestamps from IP addresses
Instead of keeping one pair of RX and TX timestamp for each address, add
a separate RX->TX map using an ordered circular buffer. Save the RX
timestamps as 64-bit integers and search them with a combined linear
interpolation and binary algorithm.

This enables the server to support multiple interleaved clients sharing
the same IP address (e.g. NAT) and it will allow other improvements to
be implemented later. A drawback is that a single broken client sending
interleaved requests at a high rate (without spoofing the source
address) can now prevent clients on other addresses from getting
interleaved responses.

The total number of saved timestamps does not change. It's still
determined by the clientloglimit directive. A new option may be added
later if needed. The whole buffer is allocated at once, but only on
first use to not waste memory on client-only configurations.
2021-10-14 16:42:20 +02:00
Miroslav Lichvar
5cb469b204 clientlog: fix debug message for maximum number of records 2021-10-14 15:03:26 +02:00
Miroslav Lichvar
29d7d3176d sys_linux: fix seccomp filter for BINDTODEVICE option
The BINDTODEVICE socket option is the first option in the seccomp filter
setting a string instead of int. Remove the length check from the
setsockopt rules to allow a device name longer than 3 characters.

This was reported in Debian bug #995207.

Fixes: b9f5ce83b0 ("sys_linux: allow BINDTODEVICE option in seccomp filter")
2021-10-06 10:09:35 +02:00
Miroslav Lichvar
76a905d652 examples: improve chronyd service
Allow writing logfiles (enabled by logdir or -l option) to /var/log and
don't require /var/spool to exist.
2021-10-04 10:54:40 +02:00
Miroslav Lichvar
83f96efdfd examples: harden systemd services
Add various settings to the example chronyd and chrony-wait services to
decrease the exposure reported by the "systemd-analyze security"
command. The original exposure was high as the analyzer does not check
the actual process (e.g. that it dropped the root privileges or that it
has its own seccomp filter).

Limit read-write access to /run, /var/lib/chrony, and /var/spool.
Access to /run (instead of /run/chrony) is needed for the refclock
socket expected by gpsd.

The mailonchange directive is most likely to break as it executes
/usr/sbin/sendmail, which can do unexpected operations depending on the
implementation. It should work with a setuid/setgid binary, but it is
not expected to write outside of /var/spool and the private /tmp.
2021-09-29 15:56:55 +02:00
Miroslav Lichvar
127826a399 ntp: check software timestamps on Linux
Apparently some routers with hardware NAT acceleration have a bug
causing the kernel timestamps to be corrupted and break NTP. Similarly
to the sanity check applied to hardware timestamps, require the
kernel/driver timestamps to be within one second of the daemon timestamp
to be accepted.
2021-09-23 15:51:35 +02:00
Miroslav Lichvar
7ee5f4888e ntp: print stratum 1 refid in ASCII in debug message 2021-09-23 15:51:35 +02:00
Miroslav Lichvar
9ed1d1afc2 doc: show arguments of ratelimit options 2021-09-23 15:51:35 +02:00
Miroslav Lichvar
d0d9a3fa43 use round() for rounding
Replace casting of values incremented by +0.5/-0.5 with round().
2021-09-23 15:51:35 +02:00
Miroslav Lichvar
9600993c28 test: fix incorrect use of RAND_MAX
On some systems (e.g. Solaris/OpenIndiana) rand() and random() have
different ranges. RAND_MAX is the maximum value returned by rand(),
but random() should always have a range of 0 through 2^31-1.

This fixes multiple failures in different tests.
2021-09-23 15:51:35 +02:00
Miroslav Lichvar
5e6f8458ff client: replace allow/deny parser
Use the new cmdparse function for parsing the (cmd)allow/deny commands
and refactor the code a bit to reduce the number of functions needed for
all the (cmd)allow/deny(all) combinations.
2021-09-23 15:50:05 +02:00
Miroslav Lichvar
f5fe5452f6 conf: rework allow/deny parser
Refactor the (cmd)allow/deny parser and make it more strict in what
input it accepts. Check the scanned numbers and require whole input to
be processed.

Move the parser to cmdparse to make it available to the client.
2021-09-23 15:16:33 +02:00
Miroslav Lichvar
3ac6a0c26c cmdmon: move comment to make its scope clearer 2021-09-23 14:48:59 +02:00
Miroslav Lichvar
c2872d1e12 test: extend 110-chronyc test 2021-09-23 14:48:59 +02:00
Miroslav Lichvar
e47e7e3661 test: fix chronyc test with disabled IPv6 support 2021-09-23 14:48:59 +02:00
Miroslav Lichvar
d8f14ec59b test: add 143-manual test 2021-09-23 14:48:59 +02:00
Miroslav Lichvar
274a51bc38 test: enable chronyc to use Unix domain socket 2021-09-23 14:48:59 +02:00
Miroslav Lichvar
92700e194c test: fix 002-scanbuild test 2021-09-23 14:48:59 +02:00
Miroslav Lichvar
87df268723 test: update compilation tests 2021-09-02 16:10:17 +02:00
Miroslav Lichvar
17a9caf5c8 cmac: add gnutls support
Similarly to hashing, add support for AES-CMAC in gnutls to avoid
linking directly with nettle.
2021-09-02 16:10:09 +02:00
Miroslav Lichvar
36441fabde hash: allow non-security MD5 use in FIPS mode
gnutls running in the FIPS140-2 mode does not allow MD5 to be
initialized, which breaks chronyd using MD5 to calculate reference ID
of IPv6 addresses. Specify a new hash algorithm for non-security MD5 use
and temporarily switch to the lax mode when initializing the hash
function.
2021-09-02 15:17:08 +02:00
Miroslav Lichvar
f363998517 hash: add gnutls support
Add support for crypto hash functions in gnutls (internally using
nettle). This can be useful to avoid directly linking with nettle to
avoid ABI breaks.
2021-09-02 15:17:08 +02:00
Miroslav Lichvar
6fc30baba8 configure: fix SIV detection in gnutls
gnutls_aead_cipher_init() is declared in gnutls/crypto.h. If the
compiler handles implicit declarations as errors, the SIV support was
not detected. Fix the check to use the correct header.
2021-09-02 15:17:00 +02:00
Miroslav Lichvar
70a0f18d52 siv: deinit gnutls on unsupported SIV 2021-09-02 13:35:35 +02:00
Miroslav Lichvar
0ad5f5ea89 privops: allow binding to PTP port
Fixes: be3158c4e5 ("ntp: add support for NTP over PTP")
2021-09-02 13:35:33 +02:00
Miroslav Lichvar
d676f39b84 doc: improve ptpport example 2021-09-01 16:56:54 +02:00
Miroslav Lichvar
31690261f5 doc: remove obsolete comment in maxslewrate description 2021-08-19 14:51:42 +02:00
Miroslav Lichvar
93326488a3 doc: shorten lock_all description 2021-08-19 14:51:42 +02:00
Miroslav Lichvar
d5ca98eaaa test: add 142-ptpport test 2021-08-19 14:51:42 +02:00
Miroslav Lichvar
be3158c4e5 ntp: add support for NTP over PTP
Allow NTP messages to be exchanged as a payload of PTP messages to
enable full hardware timestamping on NICs that can timestamp PTP packets
only. Implemented is the protocol described in this draft (version 00):

https://datatracker.ietf.org/doc/draft-mlichvar-ntp-over-ptp/

This is an experimental feature. It can be changed or removed in future.
The used PTP domain is 123 and the NTP TLV type is 0x2023 from the "do
not propagate" experimental range.

The ptpport directive enables NTP-over-PTP as a server and as a client
for all sources that have the port option set to the PTP port. The port
should be the PTP event port (319) to trigger timestamping in the
hardware.

The implementation is contained to ntp_io. It is transparent to
ntp_core.
2021-08-19 14:51:38 +02:00
Miroslav Lichvar
2f1d5d9255 ntp: add PTP rxfilter
Setting rxfilter to ptp enables timestamping of PTPv2 packets (UDP or
all transports). It will be needed for NTP-over-PTP support.
2021-08-18 16:56:09 +02:00
Miroslav Lichvar
b2c2132e4b ntp: provide remote port to NIO_OpenServerSocket()
This will allow selection of different protocols based on the remote
port. Zero means the default (NTP).
2021-08-16 14:53:46 +02:00
Stefan R. Filipek
aab6d1b153 doc: fix chronyd platform support for -P and -m
A while back, support for memory locking and real-time scheduling was
added to more platforms. The chronyd documentation wasn't updated at
that time (chronyd.conf was). This patch fixes that.
2021-08-09 12:08:12 +02:00
Miroslav Lichvar
bbbd80bf03 sys_linux: allow clone3 and pread64 in seccomp filter
These seem to be needed with the latest glibc.
2021-08-09 11:48:21 +02:00
Miroslav Lichvar
f27d719a4e rtc: avoid printing and scanning time_t
With the latest glibc it's now possible to define _TIME_BITS=64 to get
64-bit time_t on 32-bit Linux systems. This breaks the %ld printf/scanf
modifier used with the RTC drift timestamp. Process it as a double.
2021-08-05 14:41:28 +02:00
Miroslav Lichvar
789817cd91 doc: improve ntsserverkey/cert description
The files are read after dropping root privileges. They need to be
readable by the chrony user. The error message "Could not set
credentials : Error while reading file." does not make this requirement
very obvious.
2021-07-07 16:45:46 +02:00
Miroslav Lichvar
885e7774fd doc: update NEWS 2021-05-12 13:06:15 +02:00
Miroslav Lichvar
883b7eed8a update copyright years 2021-05-12 13:06:15 +02:00
Miroslav Lichvar
4049ed8766 test: make 007-cmdmon test more reliable
Reorder the local off command with respect to offline and online to
prevent the client from getting an unsynchronized response.
2021-05-12 13:06:15 +02:00
Miroslav Lichvar
f9f6803b8a test: allow inaccurate math in util unit test
Don't require timespec/timeval-double conversion tests to produce
correctly rounded results to handle x86 and other archs with wider
intermediate results.
2021-05-10 18:15:45 +02:00
Miroslav Lichvar
385f7ebfd9 test: disable privdrop in nts test
They are unrelated features. Not setting privdrop avoids a skip due to
the nobody user not having access to the test directory.
2021-05-10 16:04:34 +02:00
Miroslav Lichvar
f9cbc4803d sys_linux: check if execveat is defined
The syscall is missing on older systems.
2021-05-06 15:43:04 +02:00
Miroslav Lichvar
97973b1833 sys_linux: add second scfilter level
Add level "2" to enable a filter which blocks only specific system calls
like fork and exec* instead of blocking everything unknown. It should
be reliable with respect to changes in libraries, but it provides only a
very limited protection.
2021-05-06 13:37:21 +02:00
Miroslav Lichvar
9cdfc15e31 sys_linux: allow getuid32 in seccomp filter
This was triggered on x86 in an NTS test.
2021-05-06 13:11:10 +02:00
Miroslav Lichvar
fc99317291 sourcestats: check samples loaded from dump files
When loading a dump file with the -r option, check also sanity of the
sample time, offset, peer/root delay/dispersion, and the sample order to
better handle corrupted files.
2021-05-06 13:10:51 +02:00
Miroslav Lichvar
bb9ba3e4bd source: don't print duplicated address in selection message
Don't print the original IP address in parentheses in the "Selected
source ..." message if it is identical to the current address. That is
expected to be the usual case for sources specified by IP address.
2021-05-05 12:41:23 +02:00
Miroslav Lichvar
649f54a1e6 conf: log error when source cannot be added
Log an error message when adding of a source fails, e.g. due to the new
limit on number of sources, or when the same address is specified
multiple times.
2021-05-05 12:41:23 +02:00
Miroslav Lichvar
4070d7ffa6 nts: close file after loading cookies
Don't forget to close the file with cookies in ntsdumpdir if
successfully loaded.

Fixes: 2fa83b541c ("nts: save and load cookies on client")
2021-05-05 12:41:23 +02:00
Miroslav Lichvar
0493abb68a nts: ignore long non-critical records
In the NTS-KE client don't reject the response if it has non-critical
records that are too long for the processing buffer. This is not
expected to happen with the current specification, but it might be
needed with future extensions.

Fixes: 7925ed39b8 ("nts: fix handling of long server negotiation record")
2021-05-05 12:41:22 +02:00
Miroslav Lichvar
8c1e16711d test: fix date use in 010-nts system test
Avoid using nonportable -d option of date.
2021-04-29 15:03:37 +02:00
Miroslav Lichvar
1d03908646 test: remove logs before chronyd start in system tests 2021-04-29 13:26:01 +02:00
Miroslav Lichvar
49d718c025 test: extend configuration in system tests 2021-04-29 13:23:34 +02:00
Miroslav Lichvar
c536b2561b test: rework seccomp testing
Instead of a single test with enabled seccomp, rerun all other
non-destructive and destructive tests for each seccomp level.
2021-04-29 13:23:34 +02:00
Miroslav Lichvar
b9f5ce83b0 sys_linux: allow BINDTODEVICE option in seccomp filter
Fixes: 4ef944b734 ("socket: add support for binding sockets to device")
2021-04-29 12:37:26 +02:00
Miroslav Lichvar
8baab00ae0 doc: warn about -F and mailonchange in chronyd man page 2021-04-29 09:44:32 +02:00
Miroslav Lichvar
d01cb5af46 nts: avoid assumption about cookie record
The cookie record is currently assumed to be the longest record that
needs to be accepted by the client, but that does not have to be always
the case. Define the processing buffer using the maximum body record
constant instead and add an assertion to make sure it's not smaller than
the maximum accepted cookie length.
2021-04-29 09:44:32 +02:00
Miroslav Lichvar
7925ed39b8 nts: fix handling of long server negotiation record
Recent change in handling of the NTPv4 server negotiation record (commit
754097944b) increased the length of the instance name buffer to make
room for the trailing dot. This allowed a record with body truncated in
the processing buffer to be accepted and caused an over-read of 1 byte
in the memcpy() call saving the name to the instance buffer.

Modify the client to accept only records that fit in the processing
buffer.

Fixes: 754097944b ("nts: handle negotiated server as FQDN")
2021-04-29 09:44:18 +02:00
Miroslav Lichvar
9d869d8709 doc: update NEWS 2021-04-22 10:44:50 +02:00
Miroslav Lichvar
4f94e22b4b doc: update README 2021-04-22 10:20:31 +02:00
Miroslav Lichvar
d9b720471d ntp: fix address in error message 2021-04-22 10:20:31 +02:00
Miroslav Lichvar
039b388c82 nameserv: avoid sockaddr_in6 with disabled IPv6 support
Fixes: 10c760a80c ("nameserv: require getaddrinfo() and getnameinfo()")
2021-04-22 10:20:31 +02:00
Miroslav Lichvar
3f6528da77 test: extend 129-reload test 2021-04-22 10:20:31 +02:00
Miroslav Lichvar
4f43c060a3 sources: fix loading of refclock dump files
Allow zero stratum in loaded dump files.

Fixes: f8610d69f0 ("sources: improve handling of dump files and their format")
2021-04-22 10:20:31 +02:00
Miroslav Lichvar
3e55fe6919 sources: don't print NULL string to dump file
For reference clocks, which don't have a name, print "." instead of
NULL.

Fixes: f8610d69f0 ("sources: improve handling of dump files and their format")
2021-04-22 10:20:31 +02:00
Miroslav Lichvar
754097944b nts: handle negotiated server as FQDN
The NTS RFC requires the recipient of the Server Negotiation NTS-KE
record to handle the name as a fully qualified domain name. Add a
trailing dot if not present to force the name to be resolved as one.
2021-04-22 10:20:31 +02:00
Miroslav Lichvar
dd6a25edf2 test: extend 106-refclock test 2021-04-22 10:20:31 +02:00
Miroslav Lichvar
e697833976 doc: improve description of allow directive
Prefer CIDR notation, clarify use of hostnames and order of allow/deny
directives, refer to the accheck command.
2021-04-22 10:20:31 +02:00
Bryan Christianson
40d80624f6 sys_timex: remove workaround for broken ntp_adjtime on macOS
Early beta releases of macOS Big Sur had a signed/unsigned error in
Apple's implementation of ntp_adjtime. Apple have since fixed this error
and the workaround is no longer required.
2021-04-20 15:30:47 +02:00
Miroslav Lichvar
9a716cc284 doc: improve FAQ 2021-04-15 15:17:13 +02:00
Miroslav Lichvar
13a78ecd2f conf: require sourcedir files to be terminated by newline
When reading a *.sources file require that each line is termined by the
newline character to avoid processing an unfinished line, e.g. due to an
unexpected call of the reload command when the file is being written in
place.
2021-04-15 15:17:13 +02:00
Miroslav Lichvar
a9f0c681cb test: make system tests more reliable 2021-04-15 15:17:13 +02:00
Miroslav Lichvar
862aa285a2 test: update and extend 110-chronyc test 2021-04-15 15:17:13 +02:00
Miroslav Lichvar
84d2811800 ntp: add copy option
When separate client and server instances of chronyd are running on one
computer (e.g. for security or performance reasons) and are synchronized
to each other, the server instance provides a reference ID based on the
local address used for synchronization of its NTP clock, which breaks
detection of synchronization loops for its own clients.

Add a "copy" option to specify that the server and client are closely
related, no loop can form between them, and the client should assume the
reference ID and stratum of the server to fix detection of loops between
the server and clients of the client.
2021-04-15 15:17:13 +02:00
Miroslav Lichvar
635a9d3f5a ntp: clamp remote stratum
Don't set the remote stratum (used for polling adjustments) to values
larger than 16.
2021-04-15 15:17:13 +02:00
Miroslav Lichvar
81f7f6ddf0 ntp: don't update source status with unsynchronized data
Don't update the leap and stratum used in source selection if they
indicate an unsynchronized source.

Fixes: 2582be8754 ("sources: separate update of leap status")
2021-04-15 15:16:39 +02:00
Uwe Kleine-König
aa22c515ce refclock: drop return after LOG_FATAL
The LOG_FATAL macro expands to (emitting the message and then) exit(1).
So a return after LOG_FATAL isn't reached. Drop all those to simplify
the code a bit.
2021-04-12 09:24:07 +02:00
Miroslav Lichvar
2ca2c85365 ntp: fix loop test for special reference modes
It is not sufficient to check for disabled server sockets as they are
not open only after the special reference modes end (e.g. initstepslew).

Fixes: 004986310d ("ntp: skip loop test if no server socket is open")
2021-04-07 17:14:22 +02:00
Foster Snowhill
966e6fd939 sys_linux: allow setsockopt(SOL_IP, IP_TOS) in seccomp
This system call is required by the DSCP marking feature introduced in commit
6a5665ca58 ("conf: add dscp directive").

Before this change, enabling seccomp filtering (chronyd -F 1) and specifying a
custom DSCP value in the configuration (for example "dscp 46") caused the
process to be killed by seccomp due to IP_TOS not being allowed by the filter.

Tested before and after the change on Ubuntu 21.04, kernel 5.11.0-13-generic.
IP_TOS is available since Linux 1.0, so I didn't add any ifdefs for it.

Signed-off-by: Foster Snowhill <forst@forstwoof.ru>
2021-04-07 17:14:22 +02:00
Miroslav Lichvar
4f0dd72cf0 doc: improve chrony.conf man page 2021-04-07 17:14:22 +02:00
Miroslav Lichvar
69aa2eff99 doc: improve FAQ
Add new questions, fix typos and version-specific information.
2021-04-07 17:14:09 +02:00
Miroslav Lichvar
3e1ec36ca5 test: extend 103-initstepslew test 2021-04-07 16:55:38 +02:00
Miroslav Lichvar
224ab8ddb1 test: enable valgrind in more tests 2021-03-24 17:50:33 +01:00
Miroslav Lichvar
307c2ec70f test: extend 106-refclock test 2021-03-18 17:41:36 +01:00
Miroslav Lichvar
5381fb4ee9 refclock: increase PPS lock limit
Increase the maximum acceptable offset of the PPS lock reference from
20% to 40% of the PPS interval to not require the refclock offset to be
specified in configuration so accurately, or enable operation with a
highly unstable reference clock.
2021-03-18 17:41:28 +01:00
Miroslav Lichvar
3812ec2aa2 declare variables set from signal handlers as volatile
Make sure variables set from signal handlers are not cached in
registers.
2021-03-18 17:38:18 +01:00
Kamil Dudka
4e7690ebec configure: use well-known file name conftest.c
... for configuration checks.  Compiler wrappers check for this name
in order to skip any instrumentation of the build that is intended
for regular source files only.
2021-03-15 10:42:48 +01:00
Miroslav Lichvar
cf3d976a68 test: extend ntp_sources unit test 2021-03-11 11:47:48 +01:00
Miroslav Lichvar
26fc28c056 test: drop logging suspension
Instead of selectively suspending logging by redirecting messages to
/dev/null, increase the default minimum log severity to FATAL. In the
debug mode, all messages are printed.
2021-03-11 11:47:31 +01:00
Miroslav Lichvar
d2117ab697 cmdmon: return error if doffset command fails 2021-03-04 17:26:00 +01:00
Miroslav Lichvar
52b29f673f cmdmon: convert doffset request to float 2021-03-04 17:26:00 +01:00
Miroslav Lichvar
e86b60a9d7 local: return status from offset accumulation
Change the functions accumulating offset to return success or failure.
2021-03-04 17:26:00 +01:00
Miroslav Lichvar
53501b743f client: report invalid values in doffset and dfreq commands 2021-03-04 17:26:00 +01:00
Miroslav Lichvar
c61ddb70da test: extend util unit test 2021-03-04 17:26:00 +01:00
Miroslav Lichvar
9339766bfe test: use env shebang in all bash scripts
This allows the scripts to be executed on systems that don't have bash
in /bin. This fixes "make check".
2021-03-04 12:36:36 +01:00
Miroslav Lichvar
f60410016a test: extend 007-cmdmon system test 2021-03-04 12:36:36 +01:00
Miroslav Lichvar
7a02371698 util: require inet_pton()
Always use inet_pton() for converting IP addresses. It should be
available on all currently supported systems.
2021-03-04 12:36:36 +01:00
Miroslav Lichvar
579d8c9907 nameserv: avoid unnecessary getaddrinfo() calls
Check if the name passed to DNS_Name2IPAddress() is an IP address
before calling getaddrinfo(), which can be much slower and work
differently on different systems.
2021-03-04 12:36:36 +01:00
Miroslav Lichvar
10c760a80c nameserv: require getaddrinfo() and getnameinfo()
Remove support for the long-deprecated gethostbyname() and
gethostbyaddr() functions.
2021-03-04 12:36:36 +01:00
Miroslav Lichvar
2d39a12f51 cmdmon: fix responding to IPv4 addresses on FreeBSD
On FreeBSD, the source address cannot be specified when sending a
message on a socket bound to a non-any IPv4 address, e.g. in default
configuration 127.0.0.1. In this case, make the address unspecified.

This is similar to commit 6af39d63aa ("ntp: don't use IP_SENDSRCADDR
on bound socket").

Fixes: f06c1cfa97 ("cmdmon: respond from same address")
2021-03-04 12:36:36 +01:00
Miroslav Lichvar
517b1ae29a main: suppress info messages with -p option
Log (to stderr) only warnings and higher when printing the
configuration to suppress the "chronyd starting" message.
2021-03-04 12:36:23 +01:00
Miroslav Lichvar
b7347d931b sys_linux: check if statx syscall is defined
statx seems to be missing in older kernel and libseccomp headers, still
used on some supported systems.
2021-03-03 10:04:07 +01:00
Miroslav Lichvar
4f878ba144 main: warn if running with root privileges
Log a warning message if the main process has not dropped the root
privileges, i.e. when the compiled-in user or user specified by the user
directive or -u option is root.
2021-02-25 17:06:14 +01:00
Miroslav Lichvar
8acdb5d1e2 refclock: warn if lock refid is invalid
Log a warning message if the specified lock refid doesn't match any
existing refclock or it matches the refclock which has the lock option
itself.
2021-02-25 17:06:13 +01:00
Miroslav Lichvar
62f2d5736d refclock: warn if maxlockage is too small
Log a warning message if the interval covered by the maxlockage at the
PPS rate of a refclock is shorter than driver poll of the locked
refclock.

Reported-by: Matt Corallo <ntp-lists@mattcorallo.com>
2021-02-25 17:06:10 +01:00
Miroslav Lichvar
dc22df93f5 ntp: restart resolving on online command
If the online command is received when the resolver is running, start
it again as soon as it finishes instead of waiting for the timer.

This should reduce the time needed to get all sources resolved on boot
if chronyd is started before the network is online and the chronyc
online command is issued before the first round of resolving can finish,
e.g. due to an unreachable DNS server in resolv.conf.
2021-02-25 17:02:58 +01:00
Miroslav Lichvar
d898bd246b test: extend 139-nts test 2021-02-18 17:44:04 +01:00
Miroslav Lichvar
ebf0ff2c0d cmdmon: set certset for new sources
Add the new certset option to the cmdmon protocol.
2021-02-18 17:44:04 +01:00
Miroslav Lichvar
cc77b0e9fd conf: add certset option to NTP sources
Allow the set of trusted certificates to be selected for each NTP
source individually.
2021-02-18 17:44:04 +01:00
Miroslav Lichvar
a8bc25e543 conf: add set selection to ntstrustedcerts
Add an optional set-ID argument to the ntstrustedcerts directive to
enable multiple sets of trusted certificates to be specified.
2021-02-18 17:44:04 +01:00
Miroslav Lichvar
6615bb1b78 nts: add support for multiple sets of trusted certificates
Modify the session, NTS-KE, and NTS-NTP code to support multiple sets of
trusted certificates and identify the sets by a 32-bit ID.
2021-02-18 17:44:04 +01:00
Miroslav Lichvar
f650b8c515 configure: check for O_NOFOLLOW flag
If the O_NOFOLLOW flag used by open() is not defined, try it with
_GNU_SOURCE. This is needed with glibc-2.11 and earlier.

Reported-by: Marius Rohde <marius.rohde@meinberg.de>
2021-02-16 13:59:41 +01:00
Christian Ehrhardt
ae2e0318d1 sys_linux: allow statx and fstatat64 in seccomp filter
With glibc 2.33 on armhf statx and fstatat64 are triggered.
Allow this call to un-break chrony on such platforms.

Without this e.g. test 005-scfilter fails and with ltrace -rTS reports:
a)
  0.001684 SYS_397(11, 0xf75def08, 6144, 2047 <no return ...>
  0.759239 +++ killed by SIGSYS +++
b)
  0.003749 SYS_327(-100, 0xffdbcc3c, 0xffdbcb50, 0)
  0.000821 --- SIGSYS (Bad system call) ---

Current armhf syscalls from:
https://github.com/torvalds/linux/blob/v5.10/arch/arm/tools/syscall.tbl

Signed-off-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2021-02-12 11:01:22 +01:00
Miroslav Lichvar
26ce610155 nts: allow ntstrustedcerts to specify directory
If the specified path is a directory, load all certificates in the
directory.
2021-02-11 16:13:39 +01:00
Miroslav Lichvar
316d47e3b4 nts: allow multiple files with trusted certificates
Allow the ntstrustedcerts directive to be specified multiple times.
2021-02-11 16:13:39 +01:00
Miroslav Lichvar
90557cf1ba nts: allow multiple server keys and certificates
Allow the ntsservercert and ntsserverkey directives to be specified
multiple times to enable the NTS-KE server to operate under multiple
names.
2021-02-11 16:13:39 +01:00
Miroslav Lichvar
80e627c86b nts: define type for credentials
Add a NKSN_Credentials type to avoid referring to it as void *.
2021-02-11 16:13:39 +01:00
Miroslav Lichvar
0e4995e10b nts: split creating server and client credentials 2021-02-11 16:13:39 +01:00
Miroslav Lichvar
a598983f9b client: fix sourcename command to accept ID addresses
Fix the command to print the name corresponding to an unresolved
address.
2021-02-11 16:13:39 +01:00
Miroslav Lichvar
27641876c5 ntp: simplify NSR_Finalise() 2021-02-11 16:13:39 +01:00
Miroslav Lichvar
4d139eeca6 ntp: limit number of sources
Don't rely on assertions and running out of memory to terminate if
an extremely large number of sources is added. Set the maximum number
to 65536 to have a practical limit where chronyd still has a chance to
appear functional with some operations having a quadratic time
complexity.
2021-02-11 16:13:39 +01:00
Miroslav Lichvar
3f2806c19c nts: reset NTP address/port if removed in NTS-KE
When an NTS-KE server stops providing the NTP address or port, change
them to the original values to avoid the client getting stuck
with a non-responding address/port.
2021-02-11 15:24:12 +01:00
Miroslav Lichvar
e297df78e4 nts: load cookies early
Instead of waiting for the first request, try to load the cookies as
soon as the instance is created, or the NTS address is changed.

This enables loading of dump files for servers that are negotiated in
NTS-KE.
2021-02-11 09:52:57 +01:00
Miroslav Lichvar
c1d56ede3f nts: rework update of NTP server address
In the NTS-NTP client instance, maintain a local copy of the NTP address
instead of using a pointer to the NCR's address, which may change at
unexpected times.

Also, change the NNC_CreateInstance() to accept only the NTP port to
make it clear the initial NTP address is the same as the NTS-KE address
and to make it consistent with NNC_ChangeAddress(), which accepts only
one address.
2021-02-11 09:52:57 +01:00
Miroslav Lichvar
2e52aca3bf ntp: avoid recursive update of address
Allow NSR_UpdateSourceNtpAddress() to be (indirectly) called from
NCR_CreateInstance() and NCR_ChangeRemoteAddress(). In these cases, save
the addresses and make the update later when the function calls return.
2021-02-11 09:52:57 +01:00
Miroslav Lichvar
b0fc5832f4 ntp: require port match in address update
In NSR_UpdateSourceNtpAddress() and other updates of the address require
that the old port matches the current source's port.
2021-02-11 09:52:57 +01:00
Miroslav Lichvar
cf6af112e1 test: extend 129-reload test 2021-02-04 17:48:51 +01:00
Miroslav Lichvar
fa3052e776 sources: set reference after loading dump files
After loading the dump files with the -r option, immediately perform a
source selection with forced setting of the reference. This shortens the
interval when a restarted server doesn't respond with synchronized time.
It no longer needs to wait for the first measurement from the best
source (which had to pass all the filters).
2021-02-04 17:48:47 +01:00
Miroslav Lichvar
f8610d69f0 sources: improve handling of dump files and their format
Check for write errors when saving dump files. Don't save files with no
samples. Add more sanity checks for loaded data.

Extend the file format to include an identifier, the reachability
register, leap status, name, and authentication flag. Avoid loading
unauthenticated data after switching authentication on. Change format
and order of some fields to simplify parsing. Drop fields that were kept
only for compatibility.

The dump files now contain all information needed to perform the source
selection and update the reference.

There is no support kept for the old file format. Loading of old dump
files will fail after upgrading to new version.
2021-02-04 17:44:27 +01:00
Miroslav Lichvar
1a8dcce84f sources: update stratum with leap status
Remove stratum from the NTP sample and update it together with the leap
status. This enables a faster update when samples are dropped by the NTP
filters.
2021-02-04 17:43:47 +01:00
Miroslav Lichvar
f74eb67567 sourcestats: move stratum to sources
The stratum value is not needed in sourcestats. Keep it in the source
itself.
2021-02-04 17:43:29 +01:00
Miroslav Lichvar
144fcdde34 main: fix typo in comment 2021-02-03 17:51:47 +01:00
Miroslav Lichvar
3cef7f975c main: cancel clock correction before dumping sources
On exit, cancel the remaining clock correction before measurements are
saved to dumpdir to fix them for the state in which chronyd will start
again.
2021-02-03 11:06:00 +01:00
Baruch Siach
a2372b0c3a sys_linux: fix build with older kernel headers
The renameat2 system call was introduced in kernel version 3.15. Fix
build against older headers.
2021-01-28 15:32:03 +01:00
Miroslav Lichvar
362d7c517d test: improve NTS tests 2021-01-14 18:17:48 +01:00
Miroslav Lichvar
62389b7e50 nts: support servers specified by IP address
Certificates can include IP addresses as alternative names to enable
clients to verify such certificates without knowing the hostname.

Accept an IP address as a name in the NTS-NTP client and modify the
session code to not set the SNI in this case.
2021-01-14 18:17:48 +01:00
Miroslav Lichvar
eb9e6701fd ntp: allow replacement of sources specified by IP address
For sources specified by an IP address, keep the original address as the
source's name and pass it to the NCR instance. Allow the sources to go
through the replacement process if their address has changed.

This will be useful with NTS-KE negotiation.

The IP-based source names are now provided via cmdmon. This means
chronyc -n and -N can show two different addresses for a source.
2021-01-14 18:17:48 +01:00
Miroslav Lichvar
b585954b21 ntp: fix NULL pointer 2021-01-14 18:17:48 +01:00
Miroslav Lichvar
82ddc6a883 test: support ss as netstat replacement
netstat is considered obsolete on Linux. It is replaced by ss from
iproute. Support both tools for the test port selection.
2021-01-14 18:17:48 +01:00
Miroslav Lichvar
624b76e86e test: fix port selection to disable grep output 2021-01-14 18:17:48 +01:00
Miroslav Lichvar
4dd0aece02 test: make 120-selectoptions more reliable
Remove packet interval checks with long delays as the tests are much
more likely to end when the client is waiting for a response. Increase
the base delay to make selection with two sources more reliable.

Reported-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2021-01-14 18:17:48 +01:00
Miroslav Lichvar
e85fb0c25e socket: add debug message for unexpected control message 2021-01-14 18:17:48 +01:00
Miroslav Lichvar
fc8783a933 socket: check length of received control messages
Make sure each processed control messages has the expected length.
Beside improved safety, this should prevent potential issues with broken
timestamps on systems that support both 64-bit and 32-bit time_t.
2021-01-14 18:17:48 +01:00
Miroslav Lichvar
e7897eb9cc sched: stop dispatching timeouts on exit
Check in the dispatch loop whether the need_to_exit flag was set.
2021-01-14 18:17:48 +01:00
Miroslav Lichvar
59e8b79034 sched: improve infinite loop detection
The "infinite loop in scheduling" fatal error was observed on a system
running out of memory. Presumably, the execution of the process slowed
down due to memory thrashing so much that the dispatching loop wasn't
able to break with a single server polled at a 16-second interval.

To allow recovery in such a case, require for the error more than
20 handled timeouts and a rate higher than 100 per second.

Reported-by: Jamie Gruener <jamie.gruener@biospatial.io>
2021-01-14 18:17:39 +01:00
Michael Witten
fb7475bf59 rtc: log error message when driver initialisation fails 2020-12-15 10:49:07 +01:00
Michael Witten
cd98516cae doc: diagnose problem with RTC interrupts on Linux
This commit updates the FAQ with a new entry.

chronyd's Linux RTC driver (rtc_linux.c) requires the following ioctl
requests to be functional:

  RTC_UIE_ON
  RTC_UIE_OFF

However, a Linux system's RTC driver does not necessarily implement them,
as noted in these previous commits:

  d66b2f2b24
  rtc: handle RTCs that don't support interrupts
  Tue Dec 10 17:45:28 2019 +0100

  bff3f51d13
  rtc: extend check for RTCs that don't support interrupts
  Thu Dec 12 12:50:19 2019 +0100

Fortunately, the Linux kernel can be built with software emulation of
these hardware requests, by enabling the following config variable:

  CONFIG_RTC_INTF_DEV_UIE_EMUL
    Provides an emulation for RTC_UIE if the underlying rtc chip
    driver does not expose RTC_UIE ioctls. Those requests generate
    once-per-second update interrupts, used for synchronization.

    The emulation code will read the time from the hardware
    clock several times per second, please enable this option
    only if you know that you really need it.

This commit records these facts for the benefit of the user.
2020-12-15 10:41:53 +01:00
Miroslav Lichvar
e399d8dd1f doc: fix ntsntpserver reference in chrony.conf man page
Fix the name of ntsntpserver directive in ntsrotate description.

Reported-By: Phil Roberts <phil@robertskeys.net>
2020-11-26 15:09:38 +01:00
Miroslav Lichvar
d327cfea5a nts: save new server keys on start
If ntsdumpdir is specified and the server NTS keys are not reloaded from
the file, save the generated keys on start instead of waiting for the
first rotation or exit. This allows the keys to be shared with another
server without having to use the dump command.
2020-10-07 17:27:34 +02:00
Miroslav Lichvar
c94e7c72e7 conf: free refclock strings on exit
Free driver name and parameter of configured refclocks in helpers on
exit.
2020-10-07 17:27:34 +02:00
Miroslav Lichvar
f3aea33ad4 ntp: avoid unnecessary replacement attempts
In the initial resolving of pool sources try to assign each address only
once. If it fails, it means the address is already used (DNS provided
the same address) or the address is not connectable. The same result can
be expected for other unresolved sources of the pool as they don't have
a real address yet.
2020-10-07 17:27:34 +02:00
Miroslav Lichvar
48709d9c4a fix compiler warnings
Fix -Wchar-subscripts warnings on NetBSD and warnings about pointer
aliasing and uninitialized values with an older compiler.
2020-10-07 17:27:32 +02:00
Miroslav Lichvar
4779adcb50 doc: improve FAQ 2020-10-05 18:56:37 +02:00
Miroslav Lichvar
01e29ec685 doc: improve ntsrotate description 2020-10-05 18:56:37 +02:00
Miroslav Lichvar
e4cccc115d sys_netbsd: don't check access to /dev/clockctl with -x
With the -x option there is no need for write access to /dev/clockctl.
2020-10-05 18:56:37 +02:00
Miroslav Lichvar
8e9716d5d4 sys: don't start privops helper for NTS-KE helper
The NTS-KE helper doesn't need to bind sockets or adjust the clock.
Don't start the privops helper, or keep the capabilities, when dropping
root privileges in its context.
2020-10-05 18:56:37 +02:00
Miroslav Lichvar
a96d288027 sys: specify process context for dropping root
Similarly to enabling the syscall filter, specify what kind of chronyd
process is dropping the root privileges.
2020-10-05 18:56:37 +02:00
Miroslav Lichvar
545d2563ef configure: don't check for getrandom when arc4random is present
On FreeBSD 12, both functions seem to be available. Prefer arc4random.
2020-10-05 18:56:37 +02:00
Miroslav Lichvar
1494ef1df3 test: improve sources unit test 2020-10-05 18:56:37 +02:00
Miroslav Lichvar
698f270b5b cmdmon: add leap status to selectdata report 2020-10-05 18:56:37 +02:00
Miroslav Lichvar
f15f6a86b0 sched: include unexpected jumps in monotonic time
Update the monotonic time before the timestamps are corrected for
unexpected jumps, e.g. due to the computer being suspended and resumed,
and switch to the raw timestamps. This should allow the NTS refresh
interval to better follow real time, but it will not be corrected for
a frequency offset if the clock is not synchronized (e.g. with -x).
2020-10-05 18:56:37 +02:00
Miroslav Lichvar
5d60d611ae cmdmon: fix link-local address check
Don't check for a link-local address on path of a Unix domain socket.

Fixes: 4e747da4b4 ("ntp+cmdmon: fix responding to link-local addresses")
2020-10-05 18:56:37 +02:00
Miroslav Lichvar
6e71e902c8 socket: process all message headers
If multiple messages were received, don't stop their processing if some
header fails.

Fixes: 86a3ef9ed1 ("socket: add new socket support")
2020-10-05 18:56:37 +02:00
Miroslav Lichvar
473cb3c968 socket: always process control messages
Even if a received message will not be returned to the caller (e.g.
because it is truncated), process its control messages to avoid leaking
received descriptors.

Fixes: f231efb811 ("socket: add support for sending and receiving descriptors")
2020-10-05 18:56:37 +02:00
Miroslav Lichvar
df43ebe9e0 test: make 007-cmdmon test more reliable 2020-10-01 12:58:17 +02:00
Miroslav Lichvar
642173e864 client: drop unnecessary function
Replace cvt_to_sec_usec() with a UTI_DoubleToTimespec() call.
2020-10-01 12:58:17 +02:00
Miroslav Lichvar
944cf6e318 util: fix UTI_BytesToHex() to handle zero-length input 2020-10-01 12:58:17 +02:00
Miroslav Lichvar
a655eab34f nts: handle invalid algorithm in TLS key export 2020-10-01 12:58:17 +02:00
Miroslav Lichvar
f020d479e0 nts: fix server kod setting
Set the response kod value to zero even if NTS server is disabled.
2020-10-01 12:58:17 +02:00
Miroslav Lichvar
de752b28de nts: save server name in client dump file
Save the NTS-KE server name and require it to match the name of the
instance loading the file.
2020-10-01 12:58:17 +02:00
Miroslav Lichvar
f41d370e6a nts: update client state earlier
Generate a new uniq ID on each client poll to invalidate responses to
the previous request, even if a new request cannot be generated (e.g.
due to missing cookies). Reset the NAK indicator earlier in the request
sequence. Also, drop the cookie even if it's not included in the request
to prevent the client from getting stuck with a cookie that has an
invalid length. Rely on the exponentially increasing interval to avoid
frequent NTS-KE sessions due to a client bug.
2020-10-01 12:57:29 +02:00
Miroslav Lichvar
a97830d9d6 doc+examples: update http links to https 2020-09-23 15:10:43 +02:00
Miroslav Lichvar
ea4fc47cda client: improve help message
Describe all chronyc options in the help message.
2020-09-23 15:10:43 +02:00
Miroslav Lichvar
0e08ca7c89 main: improve help message
Describe all chronyd options in the help message.
2020-09-23 15:10:43 +02:00
Miroslav Lichvar
068cd3c311 doc: document long options
Document the --version and --help options in chronyd and chronyc man
page.
2020-09-23 15:10:43 +02:00
Miroslav Lichvar
455b8e4b44 test: include CMAC keys in ntp_core unit test 2020-09-23 15:10:43 +02:00
Miroslav Lichvar
d9a363606b nts: reset packet length after failed auth encryption
If encryption of the NTS authenticator field fails, don't leave
uninitialized data in the packet in case a bug causes the packet to be
sent.
2020-09-23 15:10:43 +02:00
Miroslav Lichvar
59ad433b6b ntp: improve NTS check in NAU_DestroyInstance()
Check the mode instead of the nts pointer to make it clear the pointer
is not expected to be NULL in an NTS instance (unless the NTS support is
stubbed).
2020-09-23 15:10:37 +02:00
Miroslav Lichvar
35b3a42ed9 ntp: update comments with new RFCs 2020-09-21 14:07:05 +02:00
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
Miroslav Lichvar
ad8fb64276 doc: improve NEWS
Add a notable enhancement from 4.0-pre1 and make the description of
another enhancement less ambiguous.
2020-04-20 15:49:58 +02:00
Miroslav Lichvar
436c1d3ea2 doc: update README 2020-04-20 15:44:12 +02:00
Miroslav Lichvar
7fc5da5f80 check return value of SCK_OpenUnixSocketPair() 2020-04-20 15:44:12 +02:00
Miroslav Lichvar
105b3faa46 samplefilt: remove useless assignment 2020-04-20 15:44:12 +02:00
Miroslav Lichvar
709223826f doc: update documentation for recent NTS changes 2020-04-16 18:09:32 +02:00
Miroslav Lichvar
eace93f2af nts: allow disabling certificate time checks
Add "nocerttimecheck" directive to specify the number of clock updates
that need to be made before the time validation of certificates is
enabled. This makes NTS usable on machines that don't have a RTC.
2020-04-16 18:09:32 +02:00
Miroslav Lichvar
2775846db7 nts: provide time function to gnutls
Use the internal time instead of system time for validation checks in
gnutls.
2020-04-16 18:09:29 +02:00
Miroslav Lichvar
4aff08e95d nts: add server support for NTP server negotiation
Add ntsntpserver directive to specify the hostname of the NTP server
provided in NTS-KE response to clients.
2020-04-16 17:47:27 +02:00
Miroslav Lichvar
958d66f8a7 cmdmon: reload NTS server keys on rekey command
When ntsrotate is set to 0, allow the keys to be reloaded with the rekey
command of chronyc.
2020-04-16 15:25:51 +02:00
Miroslav Lichvar
85fa29c43d nts: enable external management of server keys
If ntsrotate is set to 0, don't generate new server keys and don't save
them to ntsdumpdir. This allows the keys to be managed externally and
shared with other servers.
2020-04-16 15:25:50 +02:00
Miroslav Lichvar
0344b9a9c9 nts: generate cookies from second newest key
Generate one server key in advance to give it time to be distributed to
other servers before it is actually used.
2020-04-16 15:23:25 +02:00
Miroslav Lichvar
04f6329773 nts: encode key ID in cookie in network order
This allows the server cookie to be decoded on different platforms.
2020-04-15 16:30:54 +02:00
Miroslav Lichvar
d690faeb19 cmdmon: save NTS cookies and server keys on dump command
Extend the dump command to save also the server NTS keys and client NTS
cookies. Remove the warning for unset dumpdir.
2020-04-15 16:30:54 +02:00
Miroslav Lichvar
0b2e77ae64 ntp: update auth-specific address sooner
When replacing an NTP source, update the NTS address before the NTP
address to save cookies with the old NTP address instead of the newly
resolved address (which may immediately change to an address provided by
NTS-KE).
2020-04-15 16:30:54 +02:00
Miroslav Lichvar
2a4fd0a5c6 nts: update TLS exporter label
Change the string to "EXPORTER-network-time-security" as specified in
the latest NTS draft.
2020-04-09 17:08:52 +02:00
Miroslav Lichvar
e569e1c9d9 test: extend 139-nts test 2020-04-09 17:08:52 +02:00
Miroslav Lichvar
7be360041c nts: extend server key file format
Include in the key dump file an identifier, the AEAD number, and the
age of the last key to improve robustness and avoid generating a new key
immediately on start.

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

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

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

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

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

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

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

View File

@@ -33,9 +33,11 @@ CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
GETDATE_CFLAGS = @GETDATE_CFLAGS@
EXTRA_OBJS = @EXTRA_OBJS@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
OBJS = array.o cmdparse.o conf.o leapdb.o local.o logging.o main.o memory.o quantiles.o \
reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
@@ -61,6 +63,8 @@ chronyd : $(OBJS)
chronyc : $(CLI_OBJS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
getdate.o: CFLAGS += $(GETDATE_CFLAGS)
distclean : clean
$(MAKE) -C doc distclean
$(MAKE) -C test/unit distclean
@@ -134,4 +138,6 @@ Makefile : Makefile.in configure
.deps/%.d: %.c | .deps
@$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@
ifndef NODEPS
-include $(ALL_OBJS:%.o=.deps/%.d)
endif

163
NEWS
View File

@@ -1,3 +1,132 @@
New in version 4.6
==================
Enhancements
------------
* Add activate option to local directive to set activation threshold
* Add ipv4 and ipv6 options to server/pool/peer directive
* Add kod option to ratelimit directive for server KoD RATE support
* Add leapseclist directive to read NIST/IERS leap-seconds.list file
* Add ptpdomain directive to set PTP domain for NTP over PTP
* Improve copy server option to accept unsynchronised status instantly
* Log one selection failure on start
* Add offset command to modify source offset correction
* Add timestamp sources to ntpdata report
Bug fixes
---------
* Fix crash on sources reload during initstepslew or RTC initialisation
* Fix source refreshment to not repeat failed name resolving attempts
New in version 4.5
==================
Enhancements
------------
* Add support for AES-GCM-SIV in GnuTLS
* Add support for corrections from PTP transparent clocks
* Add support for systemd socket activation
Bug fixes
---------
* Fix presend in interleaved mode
* Fix reloading of modified sources from sourcedir
New in version 4.4
==================
Enhancements
------------
* Add support for AES-GCM-SIV with Nettle >= 3.9 to shorten NTS
cookies to avoid some length-specific blocking of NTP on Internet
* Add support for multiple refclocks using extpps option on one PHC
* Add maxpoll option to hwtimestamp directive to improve PHC tracking
with low packet rates
* Add hwtstimeout directive to configure timeout for late timestamps
* Handle late hardware transmit timestamps of NTP requests on all sockets
* Handle mismatched 32/64-bit time_t in SOCK refclock samples
* Improve source replacement
* Log important changes made by command requests (chronyc)
* Refresh address of NTP sources periodically
* Request nanosecond kernel RX timestamping on FreeBSD
* Set DSCP for IPv6 packets
* Shorten NTS-KE retry interval when network is down
* Update seccomp filter for musl
* Warn if loading keys from file with unexpected permissions
* Warn if source selection fails or falseticker is detected
* Add selectopts command to modify source-specific selection options
* Add timestamp sources to serverstats report and make its fields 64-bit
* Add -e option to chronyc to indicate end of response
New in version 4.3
==================
Enhancements
------------
* Add local option to refclock directive to stabilise system clock
with more stable free-running clock (e.g. TCXO, OCXO)
* Add maxdelayquant option to server/pool/peer directive to replace
maxdelaydevratio filter with long-term quantile-based filtering
* Add selection option to log directive
* Allow external PPS in PHC refclock without configurable pin
* Don't accept first interleaved response to minimise error in delay
* Don't use arc4random on Linux to avoid server performance loss
* Improve filter option to better handle missing NTP samples
* Improve stability with hardware timestamping and PHC refclock
* Update seccomp filter
Bug fixes
---------
* Fix waitsync command to reconnect when not getting response
New in version 4.2
==================
Enhancements
------------
* Add support for NTPv4 extension field improving synchronisation
stability and resolution of root delay and dispersion (experimental)
* Add support for NTP over PTP (experimental)
* Add support for AES-CMAC and hash functions in GnuTLS
* Improve server interleaved mode to be more reliable and support
multiple clients behind NAT
* Update seccomp filter
* Add statistics about interleaved mode to serverstats report
Bug fixes
---------
* Fix RTC support with 64-bit time_t on 32-bit Linux
* Fix seccomp filter to work correctly with bind*device directives
* Suppress kernel adjustments of system clock (dosynctodr) on illumos
Other changes
-------------
* Switch Solaris support to illumos
New in version 4.1
==================
Enhancements
------------
* Add support for NTS servers specified by IP address (matching
Subject Alternative Name in server certificate)
* Add source-specific configuration of trusted certificates
* Allow multiple files and directories with trusted certificates
* Allow multiple pairs of server keys and certificates
* Add copy option to server/pool directive
* Increase PPS lock limit to 40% of pulse interval
* Perform source selection immediately after loading dump files
* Reload dump files for addresses negotiated by NTS-KE server
* Update seccomp filter and add less restrictive level
* Restart ongoing name resolution on online command
Bug fixes
---------
* Fix responding to IPv4 command requests on FreeBSD
* Fix dump files to not include uncorrected offset
* Fix initstepslew to accept time from own NTP clients
* Reset NTP address and port when no longer negotiated by NTS-KE server
New in version 4.0
==================
@@ -5,25 +134,51 @@ Enhancements
------------
* Add support for Network Time Security (NTS) authentication
* 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
* Avoid replacing NTP sources with unreachable addresses
* 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
* 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
* Repeat iburst when NTP source is switched from offline state to online
* Update clock synchronisation status and leap status more frequently
* Update seccomp filter
* 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 -a option to source/sourcestats command to print unresolved sources
* Add reset command to drop all measurements
* Add -a option to some commands to print also unresolved sources
* Add -k, -p, -r options to clients command to select, limit, reset data
Bug fixes
---------
* Don't set interface for NTP responses to allow asymmetric routing
* Handle RTCs that don't support interrupts
* Respond to command requests with correct address on multihomed hosts
Removed features
----------------
* 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
==================

70
README
View File

@@ -12,7 +12,7 @@ a time service to other computers in the network.
It is designed to perform well in a wide range of conditions, including
intermittent network connections, heavily congested networks, changing
temperatures (ordinary computer clocks are sensitive to temperature),
and systems that do not run continuosly, or run on a virtual machine.
and systems that do not run continuously, or run on a virtual machine.
Typical accuracy between two machines synchronised over the Internet is
within a few milliseconds; on a LAN, accuracy is typically in tens of
@@ -28,10 +28,8 @@ What will chrony run on?
========================
The software is known to work on Linux, FreeBSD, NetBSD, macOS and
Solaris. Closely related systems may work too. Any other system will
likely require a porting exercise. You would need to start from one
of the existing system-specific drivers and look into the quirks of
certain system calls and the kernel on your target system.
illumos. Closely related systems may work too. Any other system will
likely require a porting exercise.
How do I set it up?
===================
@@ -49,42 +47,7 @@ Frequently Asked Questions (FAQ).
The documentation is also available on the chrony web pages, accessible
through the URL
https://chrony.tuxfamily.org/
Where are new versions announced?
=================================
There is a low volume mailing list where new versions and other
important news relating to chrony is announced. You can join this list
by sending mail with the subject "subscribe" to
chrony-announce-request@chrony.tuxfamily.org
These messages will be copied to chrony-users (see below).
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
mentioned above. chrony-users is a users' discussion list, e.g. for
general questions and answers about using chrony. chrony-dev is a more
technical list, e.g. for discussing how new features should be
implemented, exchange of information between developers etc. To
subscribe to either of these lists, send a message with the subject
"subscribe" to
chrony-users-request@chrony.tuxfamily.org
or
chrony-dev-request@chrony.tuxfamily.org
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!
https://chrony-project.org/
License
=======
@@ -100,28 +63,35 @@ Miroslav Lichvar <mlichvar@redhat.com>
Acknowledgements
================
In writing the chronyd program, extensive use has been made of RFC 1305
and RFC 5905, written by David Mills. The source code of the NTP reference
implementation has been used to check the details of the protocol.
In writing the chronyd program, extensive use has been made of the NTPv3 (RFC
1305) and NTPv4 (RFC 5905) specification. The source code of the xntpd/ntpd
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
to the program :
to chrony:
Lonnie Abelbeck <lonnie@abelbeck.com>
Benny Lyne Amorsen <benny@amorsen.dk>
Andrew Bishop <amb@gedanken.demon.co.uk>
Vincent Blut <vincent.debian@free.fr>
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
David Bohman <debohman@gmail.com>
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
Leigh Brown <leigh@solinno.co.uk>
Erik Bryer <ebryer@spots.ab.ca>
Jonathan Cameron <jic23@cam.ac.uk>
Bryan Christianson <bryan@whatroute.net>
Juliusz Chroboczek <jch@pps.jussieu.fr>
Dan Drown <dan-ntp@drown.org>
Kamil Dudka <kdudka@redhat.com>
Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com>
Robert Fairley <rfairley@redhat.com>
Stefan R. Filipek <srfilipek@gmail.com>
Andy Fiddaman <illumos@fiddaman.net>
Mike Fleetwood <mike@rockover.demon.co.uk>
Rob Gill <rrobgill@protonmail.com>
Alexander Gretencord <arutha@gmx.de>
Andrew Griffiths <agriffit@redhat.com>
Walter Haidinger <walter.haidinger@gmx.at>
@@ -129,10 +99,12 @@ Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
John Hasler <john@dhh.gt.org>
Tjalling Hattink <t.hattink@fugro.nl>
Liam Hatton <me@liamhatton.com>
Holger Hoffstätte <holger@applied-asynchrony.com>
Jachym Holecek <jakym@volny.cz>
Håkan Johansson <f96hajo@chalmers.se>
Jim Knoble <jmknoble@pobox.com>
Antti Jrvinen <costello@iki.fi>
Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Eric Lammerts <eric@lammerts.org>
Stefan Lucke <stefan@lucke.in-berlin.de>
Victor Lum <viclum@vanu.com>
@@ -141,15 +113,22 @@ Paul Menzel <paulepanter@users.sourceforge.net>
Vladimir Michl <vladimir.michl@seznam.cz>
Victor Moroz <vim@prv.adlum.ru>
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Patrick Oppenlander <patrick.oppenlander@gmail.com>
Frank Otto <sandwichmacher@web.de>
Denny Page <dennypage@me.com>
Rupesh Patel <rupatel@redhat.com>
Chris Perl <cperl@janestreet.com>
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Andreas Piesk <apiesk@virbus.de>
Mike Ryan <msr@hsilop.net>
Baruch Siach <baruch@tkos.co.il>
Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
Foster Snowhill <forst@forstwoof.ru>
Andreas Steinmetz <ast@domdv.de>
NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
Timo Teras <timo.teras@iki.fi>
Bill Unruh <unruh@physics.ubc.ca>
Luke Valenta <lvalenta@cloudflare.com>
Stephen Wadeley <swadeley@redhat.com>
Bernhard Weiss <lisnablagh@web.de>
Wolfgang Weisselberg <weissel@netcologne.de>
@@ -157,6 +136,7 @@ Bernhard M. Wiedemann <bwiedemann@suse.de>
Joachim Wiedorn <ad_debian@joonet.de>
Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de>
Michael Witten <mfwitten@gmail.com>
Doug Woodward <dougw@whistler.com>
Thomas Zajic <zlatko@zlatko.fdns.net>

15
array.c
View File

@@ -116,6 +116,21 @@ ARR_AppendElement(ARR_Instance array, void *element)
memcpy(e, element, array->elem_size);
}
void
ARR_RemoveElement(ARR_Instance array, unsigned int index)
{
void *e, *l;
e = ARR_GetElement(array, index);
l = ARR_GetElement(array, array->used - 1);
if (e < l)
memmove(e, (char *)e + array->elem_size, (char *)l - (char *)e);
array->used--;
realloc_array(array, array->used);
}
void
ARR_SetSize(ARR_Instance array, unsigned int size)
{

View File

@@ -47,6 +47,9 @@ extern void *ARR_GetElements(ARR_Instance array);
/* Add a new element to the end of the array */
extern void ARR_AppendElement(ARR_Instance array, void *element);
/* Remove element with given index */
extern void ARR_RemoveElement(ARR_Instance array, unsigned int index);
/* Set the size of the array */
extern void ARR_SetSize(ARR_Instance array, unsigned int size);

178
candm.h
View File

@@ -103,8 +103,16 @@
#define REQ_ONOFFLINE 63
#define REQ_ADD_SOURCE 64
#define REQ_NTP_SOURCE_NAME 65
#define REQ_RESET 66
#define N_REQUEST_TYPES 67
#define REQ_RESET_SOURCES 66
#define REQ_AUTH_DATA 67
#define REQ_CLIENT_ACCESSES_BY_INDEX3 68
#define REQ_SELECT_DATA 69
#define REQ_RELOAD_SOURCES 70
#define REQ_DOFFSET2 71
#define REQ_MODIFY_SELECTOPTS 72
#define REQ_MODIFY_OFFSET 73
#define REQ_LOCAL3 74
#define N_REQUEST_TYPES 75
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -116,6 +124,12 @@ typedef struct {
/* This is used in tv_sec_high for 32-bit timestamps */
#define TV_NOHIGHSEC 0x7fffffff
/* Structure for 64-bit integers (not requiring 64-bit alignment) */
typedef struct {
uint32_t high;
uint32_t low;
} Integer64;
/* 32-bit floating-point format consisting of 7-bit signed exponent
and 25-bit signed coefficient without hidden bit.
The result is calculated as: 2^(exp - 25) * coef */
@@ -224,6 +238,8 @@ typedef struct {
int32_t stratum;
Float distance;
int32_t orphan;
Float activate;
uint32_t reserved[2];
int32_t EOR;
} REQ_Local;
@@ -264,10 +280,15 @@ typedef struct {
#define REQ_ADDSRC_INTERLEAVED 0x80
#define REQ_ADDSRC_BURST 0x100
#define REQ_ADDSRC_NTS 0x200
#define REQ_ADDSRC_COPY 0x400
#define REQ_ADDSRC_EF_EXP_MONO_ROOT 0x800
#define REQ_ADDSRC_EF_EXP_NET_CORRECTION 0x1000
#define REQ_ADDSRC_IPV4 0x2000
#define REQ_ADDSRC_IPV6 0x4000
typedef struct {
uint32_t type;
int8_t name[256];
uint8_t name[256];
uint32_t port;
int32_t minpoll;
int32_t maxpoll;
@@ -288,7 +309,9 @@ typedef struct {
Float offset;
uint32_t flags;
int32_t filter_length;
uint32_t reserved[3];
uint32_t cert_set;
Float max_delay_quant;
uint32_t reserved[1];
int32_t EOR;
} REQ_NTP_Source;
@@ -303,8 +326,7 @@ typedef struct {
} REQ_Dfreq;
typedef struct {
int32_t sec;
int32_t usec;
Float doffset;
int32_t EOR;
} REQ_Doffset;
@@ -320,6 +342,8 @@ typedef struct {
typedef struct {
uint32_t first_index;
uint32_t n_clients;
uint32_t min_hits;
uint32_t reset;
int32_t EOR;
} REQ_ClientAccessesByIndex;
@@ -351,6 +375,32 @@ typedef struct {
int32_t EOR;
} REQ_NTPSourceName;
typedef struct {
IPAddr ip_addr;
int32_t EOR;
} REQ_AuthData;
typedef struct {
uint32_t index;
int32_t EOR;
} REQ_SelectData;
/* Mask and options reuse the REQ_ADDSRC flags */
typedef struct {
IPAddr address;
uint32_t ref_id;
uint32_t mask;
uint32_t options;
int32_t EOR;
} REQ_Modify_SelectOpts;
typedef struct {
IPAddr address;
uint32_t ref_id;
Float new_offset;
int32_t EOR;
} REQ_Modify_Offset;
/* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1
@@ -387,9 +437,10 @@ typedef struct {
domain socket.
Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types) and manual timestamp, added new fields and
(two times), delta offset, and manual timestamp, added new fields and
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
@@ -403,8 +454,8 @@ typedef struct {
#define PROTO_VERSION_PADDING 6
/* The maximum length of padding in request packet, currently
defined by MANUAL_LIST */
#define MAX_PADDING_LENGTH 396
defined by CLIENT_ACCESSES_BY_INDEX3 */
#define MAX_PADDING_LENGTH 484
/* ================================================== */
@@ -453,7 +504,11 @@ typedef struct {
REQ_ReselectDistance reselect_distance;
REQ_SmoothTime smoothtime;
REQ_NTPData ntp_data;
REQ_NTPData ntp_source_name;
REQ_NTPSourceName ntp_source_name;
REQ_AuthData auth_data;
REQ_SelectData select_data;
REQ_Modify_SelectOpts modify_select_opts;
REQ_Modify_Offset modify_offset;
} data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the
@@ -491,7 +546,14 @@ typedef struct {
#define RPY_MANUAL_TIMESTAMP2 17
#define RPY_MANUAL_LIST2 18
#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 RPY_SERVER_STATS3 24
#define RPY_SERVER_STATS4 25
#define RPY_NTP_DATA2 26
#define N_REPLY_TYPES 27
/* Status codes */
#define STT_SUCCESS 0
@@ -504,8 +566,7 @@ typedef struct {
#define STT_BADSUBNET 7
#define STT_ACCESSALLOWED 8
#define STT_ACCESSDENIED 9
/* Deprecated */
#define STT_NOHOSTACCESS 10
#define STT_NOHOSTACCESS 10 /* Deprecated */
#define STT_SOURCEALREADYKNOWN 11
#define STT_TOOMANYSOURCES 12
#define STT_NORTC 13
@@ -530,17 +591,12 @@ typedef struct {
#define RPY_SD_MD_PEER 1
#define RPY_SD_MD_REF 2
#define RPY_SD_ST_SYNC 0
#define RPY_SD_ST_UNREACH 1
#define RPY_SD_ST_SELECTED 0
#define RPY_SD_ST_NONSELECTABLE 1
#define RPY_SD_ST_FALSETICKER 2
#define RPY_SD_ST_JITTERY 3
#define RPY_SD_ST_CANDIDATE 4
#define RPY_SD_ST_OUTLIER 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
#define RPY_SD_ST_UNSELECTED 4
#define RPY_SD_ST_SELECTABLE 5
typedef struct {
IPAddr ip_addr;
@@ -609,14 +665,17 @@ typedef struct {
typedef struct {
IPAddr ip;
uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits;
uint32_t ntp_drops;
uint32_t nke_drops;
uint32_t cmd_drops;
int8_t ntp_interval;
int8_t nke_interval;
int8_t cmd_interval;
int8_t ntp_timeout_interval;
int8_t pad;
uint32_t last_ntp_hit_ago;
uint32_t last_nke_hit_ago;
uint32_t last_cmd_hit_ago;
} RPY_ClientAccesses_Client;
@@ -629,11 +688,24 @@ typedef struct {
} RPY_ClientAccessesByIndex;
typedef struct {
uint32_t ntp_hits;
uint32_t cmd_hits;
uint32_t ntp_drops;
uint32_t cmd_drops;
uint32_t log_drops;
Integer64 ntp_hits;
Integer64 nke_hits;
Integer64 cmd_hits;
Integer64 ntp_drops;
Integer64 nke_drops;
Integer64 cmd_drops;
Integer64 log_drops;
Integer64 ntp_auth_hits;
Integer64 ntp_interleaved_hits;
Integer64 ntp_timestamps;
Integer64 ntp_span_seconds;
Integer64 ntp_daemon_rx_timestamps;
Integer64 ntp_daemon_tx_timestamps;
Integer64 ntp_kernel_rx_timestamps;
Integer64 ntp_kernel_tx_timestamps;
Integer64 ntp_hw_rx_timestamps;
Integer64 ntp_hw_tx_timestamps;
Integer64 reserved[4];
int32_t EOR;
} RPY_ServerStats;
@@ -703,15 +775,59 @@ typedef struct {
uint32_t total_tx_count;
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t total_good_count;
uint32_t total_kernel_tx_ts;
uint32_t total_kernel_rx_ts;
uint32_t total_hw_tx_ts;
uint32_t total_hw_rx_ts;
uint32_t reserved[4];
int32_t EOR;
} RPY_NTPData;
typedef struct {
int8_t name[256];
uint8_t name[256];
int32_t EOR;
} 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 leap;
uint8_t pad;
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 {
uint8_t version;
uint8_t pkt_type;
@@ -742,6 +858,8 @@ typedef struct {
RPY_Smoothing smoothing;
RPY_NTPData ntp_data;
RPY_NTPSourceName ntp_source_name;
RPY_AuthData auth_data;
RPY_SelectData select_data;
} data; /* Reply specific parameters */
} CMD_Reply;

1025
client.c

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -31,20 +31,43 @@
#include "sysincl.h"
#include "reports.h"
typedef enum {
CLG_NTP = 0,
CLG_NTSKE,
CLG_CMDMON,
} CLG_Service;
typedef enum {
CLG_PASS = 0,
CLG_DROP,
CLG_KOD,
} CLG_Limit;
extern void CLG_Initialise(void);
extern void CLG_Finalise(void);
extern int CLG_GetClientIndex(IPAddr *client);
extern int CLG_LogNTPAccess(IPAddr *client, struct timespec *now);
extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now);
extern int CLG_LimitNTPResponseRate(int index);
extern int CLG_LimitCommandResponseRate(int index);
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
extern CLG_Limit CLG_LimitServiceRate(CLG_Service service, int index);
extern void CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src,
NTP_Timestamp_Source tx_ts_src);
extern int CLG_GetNtpMinPoll(void);
/* Functions to save and retrieve timestamps for server interleaved mode */
extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source tx_src);
extern void CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source tx_src);
extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source *tx_src);
extern void CLG_DisableNtpTimestamps(NTP_int64 *rx_ts);
/* And some reporting functions, for use by chronyc. */
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);
#endif /* GOT_CLIENTLOG_H */

17
cmac.h
View File

@@ -28,13 +28,20 @@
#ifndef 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;
extern unsigned int CMC_GetKeyLength(const char *cipher);
extern CMC_Instance CMC_CreateInstance(const char *cipher, const unsigned char *key,
unsigned int length);
extern unsigned int CMC_Hash(CMC_Instance inst, const unsigned char *in, unsigned int in_len,
unsigned char *out, unsigned int out_len);
extern int CMC_GetKeyLength(CMC_Algorithm algorithm);
extern CMC_Instance CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key,
int length);
extern int CMC_Hash(CMC_Instance inst, const void *in, int in_len,
unsigned char *out, int out_len);
extern void CMC_DestroyInstance(CMC_Instance inst);
#endif

189
cmac_gnutls.c Normal file
View File

@@ -0,0 +1,189 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2021
*
* 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.
*
**********************************************************************
=======================================================================
CMAC using the GnuTLS library
*/
#include "config.h"
#include "sysincl.h"
#include <gnutls/crypto.h>
#include "cmac.h"
#include "hash.h"
#include "logging.h"
#include "memory.h"
struct CMC_Instance_Record {
gnutls_mac_algorithm_t algorithm;
gnutls_hmac_hd_t mac;
};
/* ================================================== */
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_mac_algorithm_t
get_mac_algorithm(CMC_Algorithm algorithm)
{
switch (algorithm) {
case CMC_AES128:
return GNUTLS_MAC_AES_CMAC_128;
case CMC_AES256:
return GNUTLS_MAC_AES_CMAC_256;
default:
return GNUTLS_MAC_UNKNOWN;
}
}
/* ================================================== */
int
CMC_GetKeyLength(CMC_Algorithm algorithm)
{
gnutls_mac_algorithm_t malgo = get_mac_algorithm(algorithm);
int len;
if (malgo == GNUTLS_MAC_UNKNOWN)
return 0;
len = gnutls_hmac_get_key_size(malgo);
if (len < 0)
return 0;
return len;
}
/* ================================================== */
CMC_Instance
CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, int length)
{
gnutls_hmac_hd_t handle;
CMC_Instance inst;
int r;
if (instance_counter == 0)
init_gnutls();
if (length <= 0 || length != CMC_GetKeyLength(algorithm))
goto error;
r = gnutls_hmac_init(&handle, get_mac_algorithm(algorithm), key, length);
if (r < 0) {
DEBUG_LOG("Could not initialise %s : %s", "mac", gnutls_strerror(r));
goto error;
}
inst = MallocNew(struct CMC_Instance_Record);
inst->algorithm = get_mac_algorithm(algorithm);
inst->mac = handle;
instance_counter++;
return inst;
error:
if (instance_counter == 0)
deinit_gnutls();
return NULL;
}
/* ================================================== */
int
CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len)
{
unsigned char buf[MAX_HASH_LENGTH];
int hash_len;
if (in_len < 0 || out_len < 0)
return 0;
hash_len = gnutls_hmac_get_len(inst->algorithm);
if (out_len > hash_len)
out_len = hash_len;
if (hash_len > sizeof (buf))
return 0;
if (gnutls_hmac(inst->mac, in, in_len) < 0) {
/* Reset the state */
gnutls_hmac_output(inst->mac, buf);
return 0;
}
gnutls_hmac_output(inst->mac, buf);
memcpy(out, buf, out_len);
return out_len;
}
/* ================================================== */
void
CMC_DestroyInstance(CMC_Instance inst)
{
gnutls_hmac_deinit(inst->mac, NULL);
Free(inst);
instance_counter--;
if (instance_counter == 0)
deinit_gnutls();
}

View File

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

437
cmdmon.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016, 2018
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2023
*
* 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
@@ -44,6 +44,7 @@
#include "reference.h"
#include "manual.h"
#include "memory.h"
#include "nts_ke_server.h"
#include "local.h"
#include "addrfilt.h"
#include "conf.h"
@@ -61,6 +62,9 @@ static int sock_fdu;
static int sock_fd4;
static int sock_fd6;
/* Flag indicating the IPv4 socket is bound to an address */
static int bound_sock_fd4;
/* Flag indicating whether this module has been initialised or not */
static int initialised = 0;
@@ -134,7 +138,15 @@ static const char permissions[] = {
PERMIT_AUTH, /* ONOFFLINE */
PERMIT_AUTH, /* ADD_SOURCE */
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 */
PERMIT_AUTH, /* DOFFSET2 */
PERMIT_AUTH, /* MODIFY_SELECTOPTS */
PERMIT_AUTH, /* MODIFY_OFFSET */
PERMIT_AUTH, /* LOCAL3 */
};
/* ================================================== */
@@ -152,29 +164,31 @@ static void read_from_cmd_socket(int sock_fd, int event, void *anything);
static int
open_socket(int family)
{
const char *local_path, *iface;
IPSockAddr local_addr;
const char *local_path;
int sock_fd, port;
switch (family) {
case IPADDR_INET4:
case IPADDR_INET6:
port = CNF_GetCommandPort();
if (port == 0 || !SCK_IsFamilySupported(family))
if (port == 0 || !SCK_IsIpFamilyEnabled(family))
return INVALID_SOCK_FD;
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;
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) {
LOG(LOGS_ERR, "Could not open command socket on %s",
UTI_IPSockAddrToString(&local_addr));
return INVALID_SOCK_FD;
}
if (family == IPADDR_INET4)
bound_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
break;
case IPADDR_UNSPEC:
local_path = CNF_GetBindCommandPath();
@@ -232,22 +246,19 @@ do_size_checks(void)
/* ================================================== */
void
CAM_Initialise(int family)
CAM_Initialise(void)
{
assert(!initialised);
assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
do_size_checks();
initialised = 1;
bound_sock_fd4 = 0;
sock_fdu = INVALID_SOCK_FD;
sock_fd4 = INVALID_SOCK_FD;
sock_fd6 = INVALID_SOCK_FD;
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);
sock_fd4 = open_socket(IPADDR_INET4);
sock_fd6 = open_socket(IPADDR_INET6);
access_auth_table = ADF_CreateTable();
}
@@ -288,17 +299,36 @@ CAM_OpenUnixSocket(void)
{
/* This is separated from CAM_Initialise() as it needs to be called when
the process has already dropped the root privileges */
if (CNF_GetBindCommandPath()[0])
if (CNF_GetBindCommandPath())
sock_fdu = open_socket(IPADDR_UNSPEC);
}
/* ================================================== */
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);
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 (message->addr_type == SCK_ADDR_IP &&
!SCK_IsLinkLocalIPAddress(&message->remote_addr.ip.ip_addr))
message->if_index = INVALID_IF_INDEX;
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
if (message->addr_type == SCK_ADDR_IP && message->local_addr.ip.family == IPADDR_INET4 &&
(sock_fd != sock_fd4 || bound_sock_fd4))
message->local_addr.ip.family = IPADDR_UNSPEC;
#endif
if (!SCK_SendMessage(sock_fd, message, 0))
return;
}
@@ -309,6 +339,8 @@ static void
handle_dump(CMD_Request *rx_message, CMD_Reply *tx_message)
{
SRC_DumpSources();
NSR_DumpAuthData();
NKS_DumpKeys();
}
/* ================================================== */
@@ -500,7 +532,8 @@ handle_local(CMD_Request *rx_message, CMD_Reply *tx_message)
if (ntohl(rx_message->data.local.on_off)) {
REF_EnableLocal(ntohl(rx_message->data.local.stratum),
UTI_FloatNetworkToHost(rx_message->data.local.distance),
ntohl(rx_message->data.local.orphan));
ntohl(rx_message->data.local.orphan),
UTI_FloatNetworkToHost(rx_message->data.local.activate));
} else {
REF_DisableLocal();
}
@@ -566,11 +599,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.poll = htons(report.poll);
switch (report.state) {
case RPT_SYNC:
tx_message->data.source_data.state = htons(RPY_SD_ST_SYNC);
break;
case RPT_UNREACH:
tx_message->data.source_data.state = htons(RPY_SD_ST_UNREACH);
case RPT_NONSELECTABLE:
tx_message->data.source_data.state = htons(RPY_SD_ST_NONSELECTABLE);
break;
case RPT_FALSETICKER:
tx_message->data.source_data.state = htons(RPY_SD_ST_FALSETICKER);
@@ -578,11 +608,14 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
case RPT_JITTERY:
tx_message->data.source_data.state = htons(RPY_SD_ST_JITTERY);
break;
case RPT_CANDIDATE:
tx_message->data.source_data.state = htons(RPY_SD_ST_CANDIDATE);
case RPT_SELECTABLE:
tx_message->data.source_data.state = htons(RPY_SD_ST_SELECTABLE);
break;
case RPT_OUTLIER:
tx_message->data.source_data.state = htons(RPY_SD_ST_OUTLIER);
case RPT_UNSELECTED:
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;
}
switch (report.mode) {
@@ -596,11 +629,7 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.source_data.mode = htons(RPY_SD_MD_REF);
break;
}
tx_message->data.source_data.flags =
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.flags = htons(0);
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.orig_latest_meas = UTI_FloatHostToNetwork(report.orig_latest_meas);
@@ -617,6 +646,7 @@ static void
handle_rekey(CMD_Request *rx_message, CMD_Reply *tx_message)
{
KEY_Reload();
NKS_ReloadKeys();
}
/* ================================================== */
@@ -677,14 +707,26 @@ handle_cmdaccheck(CMD_Request *rx_message, CMD_Reply *tx_message)
/* ================================================== */
static int
convert_addsrc_select_options(int flags)
{
return (flags & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
(flags & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
(flags & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
(flags & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
}
/* ================================================== */
static void
handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
{
NTP_Source_Type type;
SourceParameters params;
int family, pool, port;
NSR_Status status;
uint32_t flags;
char *name;
int pool, port;
switch (ntohl(rx_message->data.ntp_source.type)) {
case REQ_ADDSRC_SERVER:
@@ -712,7 +754,11 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
return;
}
port = (unsigned short)(ntohl(rx_message->data.ntp_source.port));
flags = ntohl(rx_message->data.ntp_source.flags);
family = flags & REQ_ADDSRC_IPV4 ? IPADDR_INET4 :
flags & REQ_ADDSRC_IPV6 ? IPADDR_INET6 : IPADDR_UNSPEC;
port = ntohl(rx_message->data.ntp_source.port);
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll);
@@ -725,29 +771,31 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
params.nts_port = ntohl(rx_message->data.ntp_source.nts_port);
params.cert_set = ntohl(rx_message->data.ntp_source.cert_set);
params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
params.max_delay_ratio =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
params.max_delay_dev_ratio =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
params.max_delay_quant =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_quant);
params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay);
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
params.connectivity = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ?
SRC_ONLINE : SRC_OFFLINE;
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0;
params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0;
params.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0;
params.sel_options =
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 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);
params.connectivity = flags & REQ_ADDSRC_ONLINE ? SRC_ONLINE : SRC_OFFLINE;
params.auto_offline = !!(flags & REQ_ADDSRC_AUTOOFFLINE);
params.iburst = !!(flags & REQ_ADDSRC_IBURST);
params.interleaved = !!(flags & REQ_ADDSRC_INTERLEAVED);
params.burst = !!(flags & REQ_ADDSRC_BURST);
params.nts = !!(flags & REQ_ADDSRC_NTS);
params.copy = !!(flags & REQ_ADDSRC_COPY);
params.ext_fields = (flags & REQ_ADDSRC_EF_EXP_MONO_ROOT ? NTP_EF_FLAG_EXP_MONO_ROOT : 0) |
(flags & REQ_ADDSRC_EF_EXP_NET_CORRECTION ?
NTP_EF_FLAG_EXP_NET_CORRECTION : 0);
params.sel_options = convert_addsrc_select_options(ntohl(rx_message->data.ntp_source.flags));
status = NSR_AddSourceByName(name, port, pool, type, &params);
status = NSR_AddSourceByName(name, family, port, pool, type, &params, NULL);
switch (status) {
case NSR_Success:
break;
@@ -765,6 +813,8 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->status = htons(STT_INVALIDNAME);
break;
case NSR_InvalidAF:
tx_message->status = htons(STT_INVALIDAF);
break;
case NSR_NoSuchSource:
assert(0);
break;
@@ -776,13 +826,12 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message)
{
NTP_Remote_Address rem_addr;
NSR_Status status;
IPAddr ip_addr;
UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &rem_addr.ip_addr);
rem_addr.port = 0;
UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &ip_addr);
status = NSR_RemoveSource(&rem_addr);
status = NSR_RemoveSource(&ip_addr);
switch (status) {
case NSR_Success:
break;
@@ -832,13 +881,14 @@ handle_dfreq(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message)
{
long sec, usec;
double doffset;
sec = (int32_t)ntohl(rx_message->data.doffset.sec);
usec = (int32_t)ntohl(rx_message->data.doffset.usec);
doffset = (double) sec + 1.0e-6 * (double) usec;
LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset);
LCL_AccumulateOffset(doffset, 0.0);
doffset = UTI_FloatNetworkToHost(rx_message->data.doffset.doffset);
if (!LCL_AccumulateOffset(doffset, 0.0)) {
tx_message->status = htons(STT_FAILED);
} else {
LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset);
}
}
/* ================================================== */
@@ -996,7 +1046,7 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ClientAccessByIndex_Report report;
RPY_ClientAccesses_Client *client;
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;
SCH_GetLastEventTime(&now, NULL, NULL);
@@ -1005,6 +1055,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);
if (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();
if (n_indices < 0) {
@@ -1012,24 +1064,28 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
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);
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;
client = &tx_message->data.client_accesses_by_index.clients[j++];
UTI_IPHostToNetwork(&report.ip_addr, &client->ip);
client->ntp_hits = htonl(report.ntp_hits);
client->nke_hits = htonl(report.nke_hits);
client->cmd_hits = htonl(report.cmd_hits);
client->ntp_drops = htonl(report.ntp_drops);
client->nke_drops = htonl(report.nke_drops);
client->cmd_drops = htonl(report.cmd_drops);
client->ntp_interval = report.ntp_interval;
client->nke_interval = report.nke_interval;
client->cmd_interval = report.cmd_interval;
client->ntp_timeout_interval = report.ntp_timeout_interval;
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);
}
@@ -1131,12 +1187,36 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ServerStatsReport report;
CLG_GetServerStatsReport(&report);
tx_message->reply = htons(RPY_SERVER_STATS);
tx_message->data.server_stats.ntp_hits = htonl(report.ntp_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.cmd_drops = htonl(report.cmd_drops);
tx_message->data.server_stats.log_drops = htonl(report.log_drops);
tx_message->reply = htons(RPY_SERVER_STATS4);
tx_message->data.server_stats.ntp_hits = UTI_Integer64HostToNetwork(report.ntp_hits);
tx_message->data.server_stats.nke_hits = UTI_Integer64HostToNetwork(report.nke_hits);
tx_message->data.server_stats.cmd_hits = UTI_Integer64HostToNetwork(report.cmd_hits);
tx_message->data.server_stats.ntp_drops = UTI_Integer64HostToNetwork(report.ntp_drops);
tx_message->data.server_stats.nke_drops = UTI_Integer64HostToNetwork(report.nke_drops);
tx_message->data.server_stats.cmd_drops = UTI_Integer64HostToNetwork(report.cmd_drops);
tx_message->data.server_stats.log_drops = UTI_Integer64HostToNetwork(report.log_drops);
tx_message->data.server_stats.ntp_auth_hits =
UTI_Integer64HostToNetwork(report.ntp_auth_hits);
tx_message->data.server_stats.ntp_interleaved_hits =
UTI_Integer64HostToNetwork(report.ntp_interleaved_hits);
tx_message->data.server_stats.ntp_timestamps =
UTI_Integer64HostToNetwork(report.ntp_timestamps);
tx_message->data.server_stats.ntp_span_seconds =
UTI_Integer64HostToNetwork(report.ntp_span_seconds);
tx_message->data.server_stats.ntp_daemon_rx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_daemon_rx_timestamps);
tx_message->data.server_stats.ntp_daemon_tx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_daemon_tx_timestamps);
tx_message->data.server_stats.ntp_kernel_rx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_kernel_rx_timestamps);
tx_message->data.server_stats.ntp_kernel_tx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_kernel_tx_timestamps);
tx_message->data.server_stats.ntp_hw_rx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_hw_rx_timestamps);
tx_message->data.server_stats.ntp_hw_tx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_hw_tx_timestamps);
memset(tx_message->data.server_stats.reserved, 0xff,
sizeof (tx_message->data.server_stats.reserved));
}
/* ================================================== */
@@ -1153,7 +1233,7 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
return;
}
tx_message->reply = htons(RPY_NTP_DATA);
tx_message->reply = htons(RPY_NTP_DATA2);
UTI_IPHostToNetwork(&report.remote_addr, &tx_message->data.ntp_data.remote_addr);
UTI_IPHostToNetwork(&report.local_addr, &tx_message->data.ntp_data.local_addr);
tx_message->data.ntp_data.remote_port = htons(report.remote_port);
@@ -1180,6 +1260,11 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
tx_message->data.ntp_data.total_good_count = htonl(report.total_good_count);
tx_message->data.ntp_data.total_kernel_tx_ts = htonl(report.total_kernel_tx_ts);
tx_message->data.ntp_data.total_kernel_rx_ts = htonl(report.total_kernel_rx_ts);
tx_message->data.ntp_data.total_hw_tx_ts = htonl(report.total_hw_tx_ts);
tx_message->data.ntp_data.total_hw_rx_ts = htonl(report.total_hw_rx_ts);
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
}
@@ -1200,7 +1285,7 @@ handle_ntp_source_name(CMD_Request *rx_message, CMD_Reply *tx_message)
IPAddr addr;
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);
if (!name) {
@@ -1222,7 +1307,15 @@ handle_ntp_source_name(CMD_Request *rx_message, CMD_Reply *tx_message)
/* ================================================== */
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;
@@ -1231,41 +1324,157 @@ handle_reset(CMD_Request *rx_message, CMD_Reply *tx_message)
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_sd_sel_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.leap = report.leap;
tx_message->data.select_data.conf_options = htons(convert_sd_sel_options(report.conf_options));
tx_message->data.select_data.eff_options = htons(convert_sd_sel_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);
}
/* ================================================== */
static void
handle_modify_selectopts(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int mask, options;
uint32_t ref_id;
IPAddr ip_addr;
UTI_IPNetworkToHost(&rx_message->data.modify_select_opts.address, &ip_addr);
ref_id = ntohl(rx_message->data.modify_select_opts.ref_id);
mask = ntohl(rx_message->data.modify_select_opts.mask);
options = convert_addsrc_select_options(ntohl(rx_message->data.modify_select_opts.options));
if (!SRC_ModifySelectOptions(&ip_addr, ref_id, options, mask))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_modify_offset(CMD_Request *rx_message, CMD_Reply *tx_message)
{
uint32_t ref_id;
IPAddr ip_addr;
double offset;
UTI_IPNetworkToHost(&rx_message->data.modify_offset.address, &ip_addr);
ref_id = ntohl(rx_message->data.modify_offset.ref_id);
offset = UTI_FloatNetworkToHost(rx_message->data.modify_offset.new_offset);
if ((ip_addr.family != IPADDR_UNSPEC && !NSR_ModifyOffset(&ip_addr, offset)) ||
(ip_addr.family == IPADDR_UNSPEC && !RCL_ModifyOffset(ref_id, offset)))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
/* Read a packet and process it */
static void
read_from_cmd_socket(int sock_fd, int event, void *anything)
{
SCK_Message sck_message;
SCK_Message *sck_message;
CMD_Request rx_message;
CMD_Reply tx_message;
IPAddr loopback_addr, remote_ip;
int read_length, expected_length;
int localhost, allowed, log_index;
unsigned short rx_command;
uint16_t rx_command;
struct timespec now, cooked_now;
if (!SCK_ReceiveMessage(sock_fd, &sck_message, 0))
sck_message = SCK_ReceiveMessage(sock_fd, 0);
if (!sck_message)
return;
read_length = sck_message.length;
read_length = sck_message->length;
/* Get current time cheaply */
SCH_GetLastEventTime(&cooked_now, NULL, &now);
/* Check if it's from localhost (127.0.0.1, ::1, or Unix domain),
or an authorised address */
switch (sck_message.addr_type) {
switch (sck_message->addr_type) {
case SCK_ADDR_IP:
assert(sock_fd == sock_fd4 || sock_fd == sock_fd6);
remote_ip = sck_message.remote_addr.ip.ip_addr;
remote_ip = sck_message->remote_addr.ip.ip_addr;
SCK_GetLoopbackIPAddress(remote_ip.family, &loopback_addr);
localhost = UTI_CompareIPs(&remote_ip, &loopback_addr, NULL) == 0;
if (!localhost && !ADF_IsAllowed(access_auth_table, &remote_ip)) {
DEBUG_LOG("Unauthorised host %s",
UTI_IPSockAddrToString(&sck_message.remote_addr.ip));
UTI_IPSockAddrToString(&sck_message->remote_addr.ip));
return;
}
@@ -1291,7 +1500,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
return;
}
memcpy(&rx_message, sck_message.data, read_length);
memcpy(&rx_message, sck_message->data, read_length);
if (rx_message.pkt_type != PKT_TYPE_CMD_REQUEST ||
rx_message.res1 != 0 ||
@@ -1300,21 +1509,22 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
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
is excessive */
if (!localhost && log_index >= 0 && CLG_LimitCommandResponseRate(log_index)) {
DEBUG_LOG("Command packet discarded to limit response rate");
return;
if (!localhost && log_index >= 0 &&
CLG_LimitServiceRate(CLG_CMDMON, log_index) != CLG_PASS) {
DEBUG_LOG("Command packet discarded to limit response rate");
return;
}
expected_length = PKL_CommandLength(&rx_message);
rx_command = ntohs(rx_message.command);
memset(&tx_message, 0, sizeof (tx_message));
sck_message.data = &tx_message;
sck_message.length = 0;
sck_message->data = &tx_message;
sck_message->length = 0;
tx_message.version = PROTO_VERSION_NUMBER;
tx_message.pkt_type = PKT_TYPE_CMD_REPLY;
@@ -1329,7 +1539,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
tx_message.status = htons(STT_BADPKTVERSION);
transmit_reply(sock_fd, &sck_message);
transmit_reply(sock_fd, read_length, sck_message);
}
return;
}
@@ -1339,7 +1549,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
DEBUG_LOG("Command packet has invalid command %d", rx_command);
tx_message.status = htons(STT_INVALID);
transmit_reply(sock_fd, &sck_message);
transmit_reply(sock_fd, read_length, sck_message);
return;
}
@@ -1348,7 +1558,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
expected_length);
tx_message.status = htons(STT_BADPKTLENGTH);
transmit_reply(sock_fd, &sck_message);
transmit_reply(sock_fd, read_length, sck_message);
return;
}
@@ -1382,6 +1592,8 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
}
if (allowed) {
LOG_SetContext(LOGC_Command);
switch(rx_command) {
case REQ_NULL:
/* Do nothing */
@@ -1439,8 +1651,8 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
case REQ_SETTIME:
handle_settime(&rx_message, &tx_message);
break;
case REQ_LOCAL2:
case REQ_LOCAL3:
handle_local(&rx_message, &tx_message);
break;
@@ -1516,7 +1728,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_dfreq(&rx_message, &tx_message);
break;
case REQ_DOFFSET:
case REQ_DOFFSET2:
handle_doffset(&rx_message, &tx_message);
break;
@@ -1548,7 +1760,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_cyclelogs(&rx_message, &tx_message);
break;
case REQ_CLIENT_ACCESSES_BY_INDEX2:
case REQ_CLIENT_ACCESSES_BY_INDEX3:
handle_client_accesses_by_index(&rx_message, &tx_message);
break;
@@ -1608,8 +1820,28 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_ntp_source_name(&rx_message, &tx_message);
break;
case REQ_RESET:
handle_reset(&rx_message, &tx_message);
case REQ_RESET_SOURCES:
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;
case REQ_MODIFY_SELECTOPTS:
handle_modify_selectopts(&rx_message, &tx_message);
break;
case REQ_MODIFY_OFFSET:
handle_modify_offset(&rx_message, &tx_message);
break;
default:
@@ -1617,25 +1849,15 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
tx_message.status = htons(STT_FAILED);
break;
}
LOG_UnsetContext(LOGC_Command);
} else {
tx_message.status = htons(STT_UNAUTH);
}
}
/* Transmit the response */
{
/* 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
}
transmit_reply(sock_fd, read_length, sck_message);
}
/* ================================================== */
@@ -1662,6 +1884,9 @@ CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
if (status == ADF_BADSUBNET) {
return 0;
} else if (status == ADF_SUCCESS) {
LOG(LOG_GetContextSeverity(LOGC_Command), "%s%s %s access from %s",
allow ? "Allowed" : "Denied", all ? " all" : "", "command",
UTI_IPSubnetToString(ip_addr, subnet_bits));
return 1;
} else {
return 0;

View File

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

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2013-2014, 2016
* Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021
*
* 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
@@ -43,8 +43,10 @@ int
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{
char *hostname, *cmd;
int n;
uint32_t ef_type;
int n, sel_option;
src->family = IPADDR_UNSPEC;
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
@@ -64,10 +66,14 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.sel_options = 0;
src->params.nts = 0;
src->params.nts_port = SRC_DEFAULT_NTSPORT;
src->params.copy = 0;
src->params.ext_fields = 0;
src->params.authkey = INACTIVE_AUTHKEY;
src->params.cert_set = SRC_DEFAULT_CERTSET;
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
src->params.max_delay_quant = 0.0;
src->params.min_delay = 0.0;
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
src->params.offset = 0.0;
@@ -90,18 +96,15 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.auto_offline = 1;
} else if (!strcasecmp(cmd, "burst")) {
src->params.burst = 1;
} else if (!strcasecmp(cmd, "copy")) {
src->params.copy = 1;
} else if (!strcasecmp(cmd, "iburst")) {
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) {
src->params.connectivity = SRC_OFFLINE;
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
src->params.sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "require")) {
src->params.sel_options |= SRC_SELECT_REQUIRE;
} else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "certset")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY)
@@ -109,9 +112,26 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "asymmetry")) {
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "extfield")) {
if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
return 0;
switch (ef_type) {
case NTP_EF_EXP_MONO_ROOT:
src->params.ext_fields |= NTP_EF_FLAG_EXP_MONO_ROOT;
break;
case NTP_EF_EXP_NET_CORRECTION:
src->params.ext_fields |= NTP_EF_FLAG_EXP_NET_CORRECTION;
break;
default:
return 0;
}
} else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "ipv4")) {
src->family = IPADDR_INET4;
} else if (!strcasecmp(cmd, "ipv6")) {
src->family = IPADDR_INET6;
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
return 0;
@@ -121,6 +141,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelayquant")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_quant, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
return 0;
@@ -151,7 +174,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
return 0;
} 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;
} else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
@@ -164,6 +187,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
return 0;
} else if (!strcasecmp(cmd, "xleave")) {
src->params.interleaved = 1;
} else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
src->params.sel_options |= sel_option;
} else {
return 0;
}
@@ -175,13 +200,110 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
/* ================================================== */
int
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
CPS_GetSelectOption(char *option)
{
if (!strcasecmp(option, "noselect")) {
return SRC_SELECT_NOSELECT;
} else if (!strcasecmp(option, "prefer")) {
return SRC_SELECT_PREFER;
} else if (!strcasecmp(option, "require")) {
return SRC_SELECT_REQUIRE;
} else if (!strcasecmp(option, "trust")) {
return SRC_SELECT_TRUST;
}
return 0;
}
/* ================================================== */
int
CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
{
char *p, *net, *slash;
uint32_t a, b, c;
int bits, len, n;
p = CPS_SplitWord(line);
if (strcmp(line, "all") == 0) {
*all = 1;
net = p;
p = CPS_SplitWord(p);
} else {
*all = 0;
net = line;
}
/* Make sure there are no other arguments */
if (*p)
return 0;
/* No specified address or network means all IPv4 and IPv6 addresses */
if (!*net) {
ip->family = IPADDR_UNSPEC;
*subnet_bits = 0;
return 1;
}
slash = strchr(net, '/');
if (slash) {
if (sscanf(slash + 1, "%d%n", &bits, &len) != 1 || slash[len + 1] || bits < 0)
return 0;
*slash = '\0';
} else {
bits = -1;
}
if (UTI_StringToIP(net, ip)) {
if (bits >= 0)
*subnet_bits = bits;
else
*subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
return 1;
}
/* Check for a shortened IPv4 network notation using only 1, 2, or 3 decimal
numbers. This is different than the numbers-and-dots notation accepted
by inet_aton()! */
a = b = c = 0;
n = sscanf(net, "%"PRIu32"%n.%"PRIu32"%n.%"PRIu32"%n", &a, &len, &b, &len, &c, &len);
if (n > 0 && !net[len]) {
if (a > 255 || b > 255 || c > 255)
return 0;
ip->family = IPADDR_INET4;
ip->addr.in4 = (a << 24) | (b << 16) | (c << 8);
if (bits >= 0)
*subnet_bits = bits;
else
*subnet_bits = n * 8;
return 1;
}
/* The last possibility is a hostname */
if (bits < 0 && DNS_Name2IPAddress(net, ip, 1) == DNS_Success) {
*subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
return 1;
}
return 0;
}
/* ================================================== */
int
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate)
{
int n;
char *cmd;
*stratum = 10;
*distance = 1.0;
*activate = 0.0;
*orphan = 0;
while (*line) {
@@ -198,6 +320,9 @@ CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
} else if (!strcasecmp(cmd, "distance")) {
if (sscanf(line, "%lf%n", distance, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "activate")) {
if (sscanf(line, "%lf%n", activate, &n) != 1)
return 0;
} else {
return 0;
}
@@ -294,3 +419,19 @@ CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
return 1;
}
/* ================================================== */
int
CPS_ParseRefid(char *line, uint32_t *ref_id)
{
int i;
for (i = *ref_id = 0; line[i] && !isspace((unsigned char)line[i]); i++) {
if (i >= 4)
return 0;
*ref_id |= (uint32_t)line[i] << (24 - i * 8);
}
return i;
}

View File

@@ -32,15 +32,22 @@
typedef struct {
char *name;
unsigned short port;
int family;
int port;
SourceParameters params;
} CPS_NTP_Source;
/* Parse a command to add an NTP server or peer */
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Get an NTP/refclock select option */
extern int CPS_GetSelectOption(char *option);
/* Parse a command to allow/deny access */
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
/* Parse a command to enable local reference */
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate);
/* Remove extra white-space and comments */
extern void CPS_NormalizeLine(char *line);
@@ -51,4 +58,7 @@ extern char *CPS_SplitWord(char *line);
/* Parse a key from keyfile */
extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
/* Parse a refclock reference ID (returns number of characters) */
extern int CPS_ParseRefid(char *line, uint32_t *ref_id);
#endif /* GOT_CMDPARSE_H */

910
conf.c

File diff suppressed because it is too large Load Diff

37
conf.h
View File

@@ -30,10 +30,13 @@
#include "addressing.h"
#include "reference.h"
#include "sources.h"
extern void CNF_Initialise(int restarted, int client_only);
extern void CNF_Finalise(void);
extern void CNF_EnablePrint(void);
extern char *CNF_GetRtcDevice(void);
extern void CNF_ReadFile(const char *filename);
@@ -41,11 +44,15 @@ extern void CNF_ParseLine(const char *filename, int number, char *line);
extern void CNF_CreateDirs(uid_t uid, gid_t gid);
extern void CNF_CheckReadOnlyAccess(void);
extern void CNF_AddInitSources(void);
extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void);
extern void CNF_AddRefclocks(void);
extern void CNF_ReloadSources(void);
extern int CNF_GetAcquisitionPort(void);
extern int CNF_GetNTPPort(void);
extern char *CNF_GetDriftFile(void);
@@ -53,6 +60,7 @@ extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(void);
extern int CNF_GetLogBanner(void);
extern int CNF_GetLogMeasurements(int *raw);
extern int CNF_GetLogSelection(void);
extern int CNF_GetLogStatistics(void);
extern int CNF_GetLogTracking(void);
extern int CNF_GetLogRtc(void);
@@ -74,11 +82,16 @@ extern void CNF_GetFallbackDrifts(int *min, int *max);
extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(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 int CNF_GetNtpDscp(void);
extern char *CNF_GetNtpSigndSocket(void);
extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
extern char *CNF_GetLeapSecTimezone(void);
extern char *CNF_GetLeapSecList(void);
/* Value returned in ppm, as read from file */
extern double CNF_GetMaxUpdateSkew(void);
@@ -86,21 +99,24 @@ extern double CNF_GetMaxClockError(void);
extern double CNF_GetMaxDrift(void);
extern double CNF_GetCorrectionTimeRatio(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_GetMaxJitter(void);
extern double CNF_GetReselectDistance(void);
extern double CNF_GetStratumWeight(void);
extern double CNF_GetCombineLimit(void);
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance);
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate);
extern void CNF_SetupAccessRestrictions(void);
extern int CNF_GetSchedPriority(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, int *kod);
extern int CNF_GetNtsRateLimit(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_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2);
@@ -122,12 +138,14 @@ typedef enum {
CNF_HWTS_RXFILTER_ANY,
CNF_HWTS_RXFILTER_NONE,
CNF_HWTS_RXFILTER_NTP,
CNF_HWTS_RXFILTER_PTP,
CNF_HWTS_RXFILTER_ALL,
} CNF_HwTs_RxFilter;
typedef struct {
char *name;
int minpoll;
int maxpoll;
int min_samples;
int max_samples;
int nocrossts;
@@ -138,16 +156,23 @@ typedef struct {
} CNF_HwTsInterface;
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern double CNF_GetHwTsTimeout(void);
extern char *CNF_GetNtsCacheDir(void);
extern char *CNF_GetNtsServerCertFile(void);
extern char *CNF_GetNtsServerKeyFile(void);
extern int CNF_GetPtpPort(void);
extern int CNF_GetPtpDomain(void);
extern int CNF_GetRefresh(void);
extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void);
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
extern int CNF_GetNtsServerPort(void);
extern int CNF_GetNtsServerProcesses(void);
extern int CNF_GetNtsServerConnections(void);
extern int CNF_GetNtsRefresh(void);
extern int CNF_GetNtsRotate(void);
extern char *CNF_GetNtsTrustedCertFile(void);
extern int CNF_GetNtsTrustedCertsPaths(const char ***paths, uint32_t **ids);
extern int CNF_GetNoSystemCert(void);
extern int CNF_GetNoCertTimeCheck(void);
#endif /* GOT_CONF_H */

243
configure vendored
View File

@@ -5,7 +5,7 @@
#
# Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Bryan Christianson 2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2018
# Copyright (C) Miroslav Lichvar 2009, 2012-2022
# Copyright (C) Stefan R. Filipek 2019
#
# =======================================================================
@@ -33,13 +33,13 @@ test_code () {
echo "int main(int argc, char **argv) {"
echo "$code"
echo "return 0; }"
) > docheck.c
) > conftest.c
echo "docheck.c:" >> config.log
cat docheck.c >> config.log
echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \
echo "conftest.c:" >> config.log
cat conftest.c >> config.log
echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o conftest conftest.c $ldflags \
$MYLDFLAGS >> config.log
$MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \
$MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o conftest conftest.c $ldflags \
$MYLDFLAGS >> config.log 2>&1
if [ $? -eq 0 ]
@@ -50,7 +50,7 @@ test_code () {
echo "No"
result=1
fi
rm -f docheck.c docheck
rm -f conftest.c conftest
echo >> config.log
return $result
}
@@ -108,17 +108,13 @@ for instance \`--prefix=$HOME'.
For better control, use the options below.
--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
--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
--without-nettle Don't use nettle even if it is available
--without-gnutls Don't use gnutls even if it is available
--without-nss Don't use NSS even if it is available
--without-tomcrypt Don't use libtomcrypt even if it is available
--disable-nts Disable NTS support
--without-gnutls Don't use gnutls even if it is available
--disable-cmdmon Disable command and monitoring support
--disable-ntp Disable NTP support
--disable-refclock Disable reference clock support
@@ -132,6 +128,7 @@ For better control, use the options below.
--without-seccomp Don't use seccomp even if it is available
--disable-asyncdns Disable asynchronous name resolving
--disable-forcednsretry Don't retry on permanent DNS error
--without-aes-gcm-siv Don't use AES-GCM-SIV for NTS even if it is available
--without-clock-gettime Don't use clock_gettime() even if it is available
--disable-timestamping Disable support for SW/HW timestamping
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba
@@ -225,7 +222,6 @@ feat_cmdmon=1
feat_ntp=1
feat_refclock=1
feat_readline=1
try_readline=1
try_editline=1
feat_sechash=1
try_nettle=1
@@ -241,9 +237,6 @@ try_clockctl=0
feat_scfilter=0
try_seccomp=-1
priv_ops=""
readline_lib=""
readline_inc=""
ncurses_lib=""
feat_ipv6=1
feat_phc=1
try_phc=0
@@ -252,7 +245,9 @@ try_setsched=0
try_lockmem=0
feat_asyncdns=1
feat_forcednsretry=1
try_aes_gcm_siv=1
try_clock_gettime=1
try_arc4random=1
try_recvmmsg=1
feat_timestamping=1
try_timestamping=0
@@ -274,21 +269,9 @@ do
--disable-readline )
feat_readline=0
;;
--without-readline )
try_readline=0
;;
--without-editline )
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=* )
SETPREFIX=`echo $option | sed -e 's/[^=]*=//;'`
;;
@@ -364,6 +347,9 @@ do
--disable-forcednsretry)
feat_forcednsretry=0
;;
--without-aes-gcm-siv)
try_aes_gcm_siv=0
;;
--without-clock-gettime)
try_clock_gettime=0
;;
@@ -441,6 +427,7 @@ case $OPERATINGSYSTEM in
try_setsched=1
try_lockmem=1
try_phc=1
try_arc4random=0
add_def LINUX
echo "Configuring for " $SYSTEM
;;
@@ -487,7 +474,7 @@ case $OPERATINGSYSTEM in
;;
SunOS)
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
LIBS="$LIBS -lsocket -lnsl -lresolv"
LIBS="$LIBS -lsocket -lnsl -lkvm -lelf -lresolv"
try_setsched=1
try_lockmem=1
add_def SOLARIS
@@ -499,7 +486,7 @@ case $OPERATINGSYSTEM in
add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
fi
echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")"
echo "Configuring for illumos (" $SYSTEM "SunOS version" $VERSION ")"
;;
* )
echo "error: $SYSTEM is not supported (yet?)"
@@ -583,6 +570,13 @@ if [ "x$MYCFLAGS" = "x" ]; then
fi
fi
TESTCFLAGS="-fwrapv"
if test_code '-fwrapv' '' "$TESTCFLAGS" '' ''; then
GETDATE_CFLAGS="-fwrapv"
else
GETDATE_CFLAGS=""
fi
if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
fi
@@ -657,6 +651,7 @@ if [ $feat_ipv6 = "1" ] && \
struct sockaddr_in6 n;
char p[100];
n.sin6_addr = in6addr_any;
n.sin6_scope_id = 0;
return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));'
then
add_def FEAT_IPV6
@@ -674,14 +669,28 @@ then
fi
fi
if ! test_code 'O_NOFOLLOW flag' 'sys/types.h sys/stat.h fcntl.h' '' "$LIBS" \
'return open("/dev/null", O_NOFOLLOW);'
then
if test_code 'O_NOFOLLOW flag with _GNU_SOURCE' 'sys/types.h sys/stat.h fcntl.h' \
'-D_GNU_SOURCE' "$LIBS" \
'return open("/dev/null", O_NOFOLLOW);'
then
add_def _GNU_SOURCE
else
echo "error: open() does not support O_NOFOLLOW flag"
exit 1
fi
fi
if [ $try_clock_gettime = "1" ]; then
if test_code 'clock_gettime()' 'time.h' '' '' \
'clock_gettime(CLOCK_REALTIME, NULL);'
'clock_gettime(CLOCK_REALTIME, (void *)1);'
then
add_def HAVE_CLOCK_GETTIME
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(CLOCK_REALTIME, NULL);'
'clock_gettime(CLOCK_REALTIME, (void *)1);'
then
add_def HAVE_CLOCK_GETTIME
EXTRA_LIBS="$EXTRA_LIBS -lrt"
@@ -689,10 +698,11 @@ if [ $try_clock_gettime = "1" ]; then
fi
fi
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$LIBS" \
if ! test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$LIBS" \
'return getaddrinfo(0, 0, 0, 0);'
then
add_def HAVE_GETADDRINFO
echo "error: getaddrinfo() not found"
exit 1
fi
if [ $feat_asyncdns = "1" ] && \
@@ -706,13 +716,16 @@ then
use_pthread=1
fi
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
if [ $try_arc4random = "1" ] && \
test_code 'arc4random_buf()' 'stdlib.h' '' '' \
'arc4random_buf((void *)1, 1);'
then
add_def HAVE_ARC4RANDOM
fi
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
'return getrandom(NULL, 256, 0);'; then
add_def HAVE_GETRANDOM
else
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
'return getrandom((void *)1, 1, 0);'; then
add_def HAVE_GETRANDOM
fi
fi
RECVMMSG_CODE='
@@ -803,10 +816,12 @@ if [ $feat_scfilter = "1" ] && [ $try_seccomp = "1" ] && \
'seccomp_init(SCMP_ACT_KILL);'
then
add_def FEAT_SCFILTER
# NAME2IPADDRESS shouldn't be enabled with other operations as the helper
# process works on one request at the time and the async resolver could
# block the main thread
priv_ops="NAME2IPADDRESS RELOADDNS"
if [ $feat_ntp = "1" ]; then
# NAME2IPADDRESS shouldn't be enabled together with a privops operation
# used by the main thread as the helper process works on one request at
# a time and the async resolver would block the main thread
priv_ops="NAME2IPADDRESS RELOADDNS"
fi
EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
fi
@@ -875,37 +890,11 @@ fi
READLINE_LINK=""
if [ $feat_readline = "1" ]; then
if [ $try_editline = "1" ]; then
if test_code editline 'stdio.h editline/readline.h' \
"$readline_inc" "$readline_lib -ledit" \
if test_code editline 'stdio.h editline/readline.h' '' '-ledit' \
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
add_def USE_EDITLINE
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"
READLINE_LINK="-ledit"
fi
fi
@@ -928,7 +917,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
add_def FEAT_SECHASH
if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
'cmac128_update(NULL, NULL, NULL, 0, NULL);'
'cmac128_update((void *)1, (void *)2, (void *)3, 1, (void *)4);'
then
add_def HAVE_CMAC
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
@@ -937,9 +926,31 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
fi
fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ]; then
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link" '
return gnutls_hash((void *)1, (void *)2, 1);'
then
HASH_OBJ="hash_gnutls.o"
HASH_LINK="$test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
'return gnutls_hmac_init((void *)1, GNUTLS_MAC_AES_CMAC_128, (void *)2, 0);'
then
add_def HAVE_CMAC
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_gnutls.o"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_gnutls.o"
fi
fi
fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then
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' \
"$test_cflags" "$test_link" \
'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));'
@@ -953,7 +964,7 @@ fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \
'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);'
'hash_memory_multi(find_hash("md5"), (void *)1, (void *)2, (void *)3, 1, (void *)4, 1);'
then
HASH_OBJ="hash_tomcrypt.o"
HASH_LINK="-ltomcrypt"
@@ -966,31 +977,70 @@ EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
LIBS="$LIBS $HASH_LINK"
if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ] && \
echo "$HASH_LINK" | grep 'nettle' > /dev/null; then
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/gnutls.h' \
"$test_cflags" "$test_link" '
return gnutls_init(NULL, 0) +
gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);' &&
test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
'aes128_set_encrypt_key(NULL, NULL);'
if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
if [ "$HASH_OBJ" = "hash_gnutls.o" ]; then
test_cflags=""
test_link=""
else
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
fi
if test_code 'TLS1.3 in gnutls' 'gnutls/gnutls.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_init((void *)1, 0) + GNUTLS_TLS1_3 +
gnutls_priority_init2((void *)1, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);'
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 [ $try_nettle = "1" ] && test_code 'AES-SIV-CMAC in nettle' \
'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key(NULL, NULL);'
'siv_cmac_aes128_set_key((void *)1, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC
if [ $try_aes_gcm_siv = "1" ] && test_code 'AES-GCM-SIV in nettle' \
'nettle/siv-gcm.h' "" "$LIBS" \
'siv_gcm_aes128_encrypt_message((void *)1, 0, NULL, 0, (void *)2, 16, (void *)3,
(void *)4);'
then
add_def HAVE_NETTLE_SIV_GCM
fi
else
if test_code 'AES-SIV-CMAC in gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
add_def HAVE_SIV
if [ $try_aes_gcm_siv = "1" ] && test_code 'AES-GCM-SIV in gnutls' \
'gnutls/crypto.h' "$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV_GCM,
(void *)2);'
then
add_def HAVE_GNUTLS_SIV_GCM
fi
if test_code 'gnutls_aead_cipher_set_key()' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_set_key((void *)1, (void *)2);'
then
add_def HAVE_GNUTLS_AEAD_CIPHER_SET_KEY
fi
else
if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
'aes128_set_encrypt_key((void *)1, (void *)2);'
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
@@ -1086,6 +1136,7 @@ do
s%@CFLAGS@%${MYCFLAGS}%;\
s%@CPPFLAGS@%${MYCPPFLAGS}%;\
s%@LDFLAGS@%${MYLDFLAGS}%;\
s%@GETDATE_CFLAGS@%${GETDATE_CFLAGS}%;\
s%@LIBS@%${LIBS}%;\
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\

View File

@@ -60,8 +60,8 @@ Support files
Dates and sizes may differ
-rw-r--r-- 1 yourname staff 2084 4 Aug 22:54 README.txt
-rwxr-xr-x 1 yourname staff 676 4 Aug 21:18 chronylogrotate.sh
-rw-r--r-- 1 yourname staff 543 18 Jul 20:10 org.tuxfamily.chronyc.plist
-rw-r--r-- 1 yourname staff 511 19 Jun 18:30 org.tuxfamily.chronyd.plist
-rw-r--r-- 1 yourname staff 543 18 Jul 20:10 org.chrony-project.chronyc.plist
-rw-r--r-- 1 yourname staff 511 19 Jun 18:30 org.chrony-project.chronyd.plist
If you have used chrony support directories other than those suggested, you
will need to edit each file and make the appropriate changes.
@@ -83,21 +83,21 @@ sudo chmod +x /usr/local/bin/chronylogrotate.sh
sudo chown root:wheel /usr/local/bin/chronylogrotate.sh
2. org.tuxfamily.chronyc.plist
2. org.chrony-project.chronyc.plist
This file is the launchd plist that runs logrotation each day. You may
wish to edit this file to change the time of day at which the rotation
will run, currently 04:05 am
sudo cp org.tuxfamily.chronyc.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
sudo cp org.chrony-project.chronyc.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.chrony-project.chronyc.plist
sudo chmod 0644 /Library/LaunchDaemons/org.chrony-project.chronyc.plist
sudo launchctl load -w /Library/LaunchDaemons/org.chrony-project.chronyc.plist
3. org.tuxfamily.chronyd.plist
3. org.chrony-project.chronyd.plist
This file is the launchd plist that runs chronyd when the Macintosh starts.
sudo cp org.tuxfamily.chronyd.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
sudo cp org.chrony-project.chronyd.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.chrony-project.chronyd.plist
sudo chmod 0644 /Library/LaunchDaemons/org.chrony-project.chronyd.plist
sudo launchctl load -w /Library/LaunchDaemons/org.chrony-project.chronyd.plist

View File

@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.tuxfamily.logrotate</string>
<string>org.chrony-project.logrotate</string>
<key>KeepAlive</key>
<false/>
<key>ProgramArguments</key>

View File

@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.tuxfamily.chronyd</string>
<string>org.chrony-project.chronyd</string>
<key>Program</key>
<string>/usr/local/sbin/chronyd</string>
<key>ProgramArguments</key>

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2017
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2023
//
// 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
@@ -39,7 +39,7 @@ running.
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 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
Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is
@@ -51,7 +51,8 @@ running under a non-root user), it will try to connect to 127.0.0.1 and then
Only the following monitoring commands, which do not affect the behaviour of
*chronyd*, are allowed from the network: *activity*, *manual list*,
*rtcdata*, *smoothing*, *sources*, *sourcestats*, *tracking*, *waitsync*. The
*rtcdata*, *smoothing*, *sourcename*, *sources*, *sourcestats*, *tracking*,
*waitsync*. The
set of hosts from which *chronyd* will accept these commands can be configured
with the <<chrony.conf.adoc#cmdallow,*cmdallow*>> directive in the *chronyd*'s
configuration file or the <<cmdallow,*cmdallow*>> command in *chronyc*. By
@@ -59,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
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
from the network if they were authenticated with a password, but that is no
longer supported.
if it is from localhost.
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.
@@ -79,11 +78,10 @@ This option disables resolving of IP addresses to hostnames, e.g. to avoid slow
DNS lookups. Long addresses will not be truncated to fit into the column.
*-N*::
This option enables printing of the original names of NTP sources that were
specified in the configuration file, or *chronyc* commands, and are internally
used by *chronyd*. Without the *-n* and *-N* option, the names of NTP sources
are obtained from reverse DNS lookups and can be different from the original
names.
This option enables printing of original hostnames or IP addresses of NTP
sources that were specified in the configuration file, or *chronyc* commands.
Without the *-n* and *-N* option, the printed hostnames are obtained from
reverse DNS lookups and can be different from the specified hostnames.
*-c*::
This option enables printing of reports in a comma-separated values (CSV)
@@ -91,6 +89,10 @@ format. Reverse DNS lookups will be disabled, time will be printed as number of
seconds since the epoch, and values in seconds will not be converted to other
units.
*-e*::
With this option each *chronyc* response will end with a line containing a
single dot.
*-d*::
This option enables printing of debugging messages if *chronyc* was compiled
with debugging support.
@@ -101,12 +103,15 @@ With this option multiple commands can be specified. Each argument will be
interpreted as a whole command.
*-h* _host_::
This option allows the user to specify which host (or comma-separated list of
addresses) running the *chronyd* program is to be contacted. This allows for
remote monitoring, without having to connect over SSH to the other host first.
This option specifies the host to be contacted by *chronyc*. It can be
specified with a hostname, IP address, or path to the local Unix domain socket.
Multiple values can be specified as a comma-separated list to provide a
fallback.
+
The default is to contact *chronyd* running on the same host where
*chronyc* is being run.
The default value is _@CHRONYRUNDIR@/chronyd.sock,127.0.0.1,::1_, i.e. the host
where *chronyc* is being run. First, it tries to connect to the Unix domain
socket and if that fails (e.g. due to running under a non-root user), it
will try to connect to 127.0.0.1 and then ::1.
*-p* _port_::
This option allows the user to specify the UDP port number which the target
@@ -119,10 +124,14 @@ This option is ignored and is provided only for compatibility.
*-a*::
This option is ignored and is provided only for compatibility.
*-v*::
*-v*, *--version*::
With this option *chronyc* displays its version number on the terminal and
exits.
*--help*::
With this option *chronyc* displays a help message on the terminal and
exits.
== COMMANDS
This section describes each of the commands available within the *chronyc*
@@ -135,7 +144,7 @@ The *tracking* command displays parameters about the system's clock
performance. An example of the output is shown below.
+
----
Reference ID : CB00710F (foo.example.net)
Reference ID : CB00710F (ntp1.example.net)
Stratum : 3
Ref time (UTC) : Fri Jan 27 09:49:17 2017
System time : 0.000006523 seconds slow of NTP time
@@ -169,23 +178,29 @@ with an IPv4 address.
*Stratum*:::
The stratum indicates how many hops away from a computer with an attached
reference clock we are. Such a computer is a stratum-1 computer, so the
computer in the example is two hops away (i.e. _foo.example.net_ is a
computer in the example is two hops away (i.e. _ntp1.example.net_ is a
stratum-2 and is synchronised from a stratum-1).
*Ref time*:::
This is the time (UTC) at which the last measurement from the reference
source was processed.
*System time*:::
In normal operation, *chronyd* by default never steps the system clock, because
any jump in the time can have adverse consequences for certain application
programs. Instead, any error in the system clock is corrected by slightly
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
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
reports to NTP clients when it is operating in server mode). The value reported
on this line is the difference due to this effect.
This is the current offset between the NTP clock and system clock. The NTP
clock is a software (virtual) clock maintained by *chronyd*, which is
synchronised to the configured time sources and provides time to NTP clients.
The system clock is synchronised to the NTP clock. To avoid steps in the
system time, which might have adverse consequences for certain applications,
the system clock is normally corrected only by speeding up or slowing down (up
to the rate configured by the <<chrony.conf.adoc#maxslewrate,*maxslewrate*>>
directive). If the offset is too large, this correction will take a very long
time. A step can be forced by the <<makestep,*makestep*>> command, or the
<<chrony.conf.adoc#makestep,*makestep*>> directive in the configuration file.
+
Note that all other offsets reported by *chronyc* and most offsets in the log
files are relative to the NTP clock, not the system clock.
*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*:::
This is a long-term average of the offset value.
*Frequency*:::
@@ -306,8 +321,8 @@ extra caption lines are shown as a reminder of the meanings of the columns.
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
#* GPS0 0 4 377 11 -479ns[ -621ns] +/- 134ns
^? foo.example.net 2 6 377 23 -923us[ -924us] +/- 43ms
^+ bar.example.net 1 6 377 21 -2629us[-2619us] +/- 86ms
^? ntp1.example.net 2 6 377 23 -923us[ -924us] +/- 43ms
^+ ntp2.example.net 1 6 377 21 -2629us[-2619us] +/- 86ms
----
+
The columns are as follows:
@@ -316,18 +331,23 @@ The columns are as follows:
This indicates the mode of the source. _^_ means a server, _=_ means a peer
and _#_ indicates a locally connected reference clock.
*S*:::
This column indicates the state of the source.
* _*_ indicates the source to which *chronyd* is currently synchronised.
* _+_ indicates acceptable sources which are combined with the selected
source.
* _-_ indicates acceptable sources which are excluded by the combining
algorithm.
* _?_ indicates sources to which connectivity has been lost or whose packets
do not pass all tests. It is also shown at start-up, until at least 3 samples
have been gathered from it.
* _x_ indicates a clock which *chronyd* thinks is a falseticker (i.e. its
time is inconsistent with a majority of other sources).
This column indicates the selection state of the source.
* _*_ indicates the best source which is currently selected for
synchronisation.
* _+_ indicates other sources selected for synchronisation, which are combined
with the best source.
* _-_ indicates a source which is considered to be selectable for
synchronisation, but not currently selected.
* _x_ indicates a source which *chronyd* thinks is a falseticker (i.e. its
time is inconsistent with a majority of other sources, or sources specified
with the *trust* option).
* _~_ 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*:::
This shows the name or the IP address of the source, or reference ID for reference
clocks.
@@ -359,9 +379,9 @@ offset. This can be suffixed by _ns_ (indicating nanoseconds), _us_
(indicating microseconds), _ms_ (indicating milliseconds), or _s_ (indicating
seconds). The number to the left of the square brackets shows the original
measurement, adjusted to allow for any slews applied to the local clock
since. The number following the _+/-_ indicator shows the margin of error in
the measurement. Positive offsets indicate that the local clock is ahead of
the source.
since. Positive offsets indicate that the local clock is ahead of the source.
The number following the _+/-_ indicator shows the margin of error in the
measurement (NTP root distance).
[[sourcestats]]*sourcestats* [*-a*] [*-v*]::
The *sourcestats* command displays information about the drift rate and offset
@@ -380,7 +400,7 @@ An example report is:
----
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
===============================================================================
foo.example.net 11 5 46m -0.001 0.045 1us 25us
ntp1.example.net 11 5 46m -0.001 0.045 1us 25us
----
+
The columns are as follows:
@@ -413,6 +433,116 @@ This is the estimated offset of the source.
*Std Dev*:::
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 Leap
=======================================================================
D ntp1.example.net Y ----- --TR- 4 1.0 -61ms +62ms N
* ntp2.example.net N ----- ----- 0 1.0 -6846us +7305us N
+ ntp3.example.net N ----- ----- 10 1.0 -7381us +7355us N
----
+
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.
* _s_ - is not synchronised.
* _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 indicates 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#authselectmode,*authselectmode*>> 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.
*Leap*:::
This column displays the current leap status of the source.
* _N_ indicates the normal status (no leap second).
* _+_ indicates that a leap second will be inserted at the end of the month.
* _-_ indicates that a leap second will be deleted at the end of the month.
* _?_ indicates the unknown status (i.e. no valid measurement was made).
[[selectopts]]*selectopts* _address|refid_ [_+|-option_]...::
The *selectopts* command modifies the configured selection options of an NTP
source specified by IP address (or the _ID#XXXXXXXXXX_ identifier used for
unknown addresses), or a reference clock specified by reference ID as a string.
+
The selection options can be added with the *+* symbol or removed with the *-*
symbol. The *selectdata* command can be used to verify the configuration. The
modified options will be applied in the next source selection, e.g. when a new
measurement is made, or the *reselect* command is executed.
+
An example of using this command is shown below.
+
----
selectopts 1.2.3.4 -noselect +prefer
selectopts GPS +trust
----
[[reselect]]*reselect*::
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
@@ -426,6 +556,13 @@ The *reselectdist* command sets the reselection distance. It is equivalent to
the <<chrony.conf.adoc#reselectdist,*reselectdist*>> directive in the
configuration file.
[[offset]]*offset* _address|refid_ _offset_::
The *offset* command modifies the offset correction of an NTP source specified
by IP address (or the _ID#XXXXXXXXXX_ identifier used for unknown addresses),
or a reference clock specified by reference ID as a string. It is equivalent to
the *offset* option in the <<chrony.conf.adoc#server,*server*>> or
<<chrony.conf.adoc#refclock,*refclock*>> directive respectively.
=== NTP sources
[[activity]]*activity*::
@@ -453,6 +590,78 @@ the offline state.
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.
[[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
=========================================================================
ntp1.example.net NTS 1 15 256 135m 0 0 8 100
ntp2.example.net SK 30 13 128 - 0 0 0 0
ntp3.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
* 30: AEAD-AES-128-GCM-SIV
*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_]::
The *ntpdata* command displays the last valid measurement and other
NTP-specific information about the specified NTP source, or all NTP sources
@@ -486,6 +695,11 @@ RX timestamping : Kernel
Total TX : 24
Total RX : 24
Total valid RX : 24
Total good RX : 22
Total kernel TX : 24
Total kernel RX : 24
Total HW TX : 0
Total HW RX : 0
----
+
The fields are explained as follows:
@@ -523,7 +737,8 @@ packets sent to the source is more variable than the delay of packets sent
from the source back.
*NTP tests*:::
Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
delay, delay ratio, delay dev ratio, and synchronisation loop.
delay, delay ratio, delay dev ratio (or delay quantile), and synchronisation
loop.
*Interleaved*:::
This shows if the response was in the interleaved mode.
*Authenticated*:::
@@ -538,7 +753,22 @@ The number of packets sent to the source.
*Total RX*:::
The number of all packets received from the source.
*Total valid RX*:::
The number of valid packets received from the source.
The number of packets which passed the first two groups of NTP tests.
*Total good RX*:::
The number of packets which passed all three groups of NTP tests, i.e. the NTP
measurement was accepted.
*Total kernel TX*:::
The number of packets sent to the source for which a timestamp was captured by
the kernel.
*Total kernel RX*:::
The number of packets received from the source for which a timestamp was
captured by the kernel.
*Total HW TX*:::
The number of packets sent to the source for which a timestamp was captured by
the NIC.
*Total HW RX*:::
The number of packets received from the source for which a timestamp was
captured by the NIC.
[[add_peer]]*add peer* _name_ [_option_]...::
The *add peer* command allows a new NTP peer to be added whilst
@@ -551,7 +781,7 @@ parameters and options is identical to that for the
An example of using this command is shown below.
+
----
add peer foo.example.net minpoll 6 maxpoll 10 key 25
add peer ntp1.example.net minpoll 6 maxpoll 10 key 25
----
[[add_pool]]*add pool* _name_ [_option_]...::
@@ -565,7 +795,7 @@ directive in the configuration file.
An example of using this command is shown below:
+
----
add pool foo.example.net maxsources 3 iburst
add pool ntp1.example.net maxsources 3 iburst
----
[[add_server]]*add server* _name_ [_option_]...::
@@ -579,7 +809,7 @@ directive in the configuration file.
An example of using this command is shown below:
+
----
add server foo.example.net minpoll 6 maxpoll 10 key 25
add server ntp1.example.net minpoll 6 maxpoll 10 key 25
----
[[delete]]*delete* _address_::
@@ -625,7 +855,7 @@ alternative to the form with mask.
_address_:::
This is an IP address or a hostname. The burst command is applied only to
that source.
::
{blank}::
+
If no _mask_ or _masked-address_ arguments are provided, every source will be
matched.
@@ -655,7 +885,7 @@ IPv6 addresses have first 48 bits equal to _2001:db8:789a_.
Example of the three-argument form of the command is:
+
----
burst 2/10 foo.example.net
burst 2/10 ntp1.example.net
----
[[maxdelay]]*maxdelay* _address_ _delay_::
@@ -721,7 +951,7 @@ uses an IP address or a hostname. These forms are illustrated below.
offline
offline 255.255.255.0/1.2.3.0
offline 2001:db8:789a::/48
offline foo.example.net
offline ntp1.example.net
----
+
The second form means that the *offline* command is to be applied to any source
@@ -763,12 +993,26 @@ current set of sources. It is equivalent to the *polltarget* option in the
[[refresh]]*refresh*::
The *refresh* command can be used to force *chronyd* to resolve the names of
configured sources to IP addresses again, e.g. after suspending and resuming
the machine in a different network.
configured NTP sources to IP addresses again and replace any addresses missing
in the list of resolved addresses.
+
Sources that stop responding will be replaced with newly resolved addresses
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.
Sources that stop responding are replaced with newly resolved addresses
automatically after 8 polling intervals. This command can be used to replace
them immediately, e.g. after suspending and resuming the machine in a different
network.
+
Note that with pools which have more than 16 addresses, or not all IPv4 or IPv6
addresses are included in a single DNS response (e.g. pool.ntp.org), this
command might replace the addresses even if they are still in the pool.
[[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.
+
Note that modified sources (e.g. specified with a new option) are not modified
in memory. They are removed and added again, which causes them to lose old
measurements and reset the selection state.
[[sourcename]]*sourcename* _address_::
The *sourcename* command prints the original hostname or address that was
@@ -815,7 +1059,7 @@ The columns are as as follows:
. 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*
command.
::
{blank}::
+
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
@@ -877,7 +1121,7 @@ particular host.
Examples of use, showing a named host and a numeric IP address, are as follows:
+
----
accheck foo.example.net
accheck ntp1.example.net
accheck 1.2.3.4
accheck 2001:db8::1
----
@@ -886,10 +1130,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
*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
either the NTP or command ports. It does not include accesses over
the Unix domain command socket. There are no arguments.
the NTP, command, or NTS-KE port. It does not include accesses over the Unix
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:
+
@@ -897,7 +1148,7 @@ An example of the output is:
Hostname NTP Drop Int IntL Last Cmd Drop Int Last
===============================================================================
localhost 2 0 2 - 133 15 0 -1 7
foo.example.net 12 0 6 - 23 0 0 - -
ntp1.example.net 12 0 6 - 23 0 0 - -
----
+
Each row shows the data for a single host. Only hosts that have passed the host
@@ -914,20 +1165,18 @@ The columns are as follows:
. The average interval between NTP packets.
. The average interval between NTP packets after limiting the response rate.
. Time since the last NTP packet was received
. The number of command packets received from the client.
. The number of command packets dropped to limit the response rate.
. The average interval between command packets.
. Time since the last command packet was received.
. The number of command packets or NTS-KE connections received/accepted from
the client.
. The number of command packets or NTS-KE connections dropped to limit the
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*::
The *serverstats* command displays how many valid NTP and command requests
*chronyd* as a server received from clients, how many of them were dropped to
limit the response rate as configured by the
<<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
<<chrony.conf.adoc#clientloglimit,*clientloglimit*>> directive. An example of
the output is shown below.
The *serverstats* command displays NTP and command server statistics.
+
An example of the output is shown below.
+
----
NTP packets received : 1598
@@ -935,7 +1184,70 @@ NTP packets dropped : 8
Command packets received : 19
Command packets dropped : 0
Client log records dropped : 0
NTS-KE connections accepted: 3
NTS-KE connections dropped : 0
Authenticated NTP packets : 189
Interleaved NTP packets : 43
NTP timestamps held : 44
NTP timestamp span : 120
NTP daemon RX timestamps : 0
NTP daemon TX timestamps : 1537
NTP kernel RX timestamps : 1590
NTP kernel TX timestamps : 43
NTP hardware RX timestamps : 0
NTP hardware TX timestamps : 0
----
+
The fields have the following meaning:
+
*NTP packets received*:::
The number of valid NTP requests received by the server.
*NTP packets dropped*:::
The number of NTP requests dropped by the server due to rate limiting
(configured by the <<chrony.conf.adoc#ratelimit,*ratelimit*>> directive).
*Command packets received*:::
The number of command requests received by the server.
*Command packets dropped*:::
The number of command requests dropped by the server due to rate limiting
(configured by the <<chrony.conf.adoc#cmdratelimit,*cmdratelimit*>> directive).
*Client log records dropped*:::
The number of client log records dropped by the server to limit the memory use
(configured by the <<chrony.conf.adoc#clientloglimit,*clientloglimit*>>
directive).
*NTS-KE connections accepted*:::
The number of NTS-KE connections accepted by the server.
*NTS-KE connections dropped*:::
The number of NTS-KE connections dropped by the server due to rate limiting
(configured by the <<chrony.conf.adoc#ntsratelimit,*ntsratelimit*>> directive).
*Authenticated NTP packets*:::
The number of received NTP requests that were authenticated (with a symmetric
key or NTS).
*Interleaved NTP packets*:::
The number of received NTP requests that were detected to be in the interleaved
mode.
*NTP timestamps held*:::
The number of pairs of receive and transmit timestamps that the server is
currently holding in memory for clients using the interleaved mode.
*NTP timestamp span*:::
The interval (in seconds) covered by the currently held NTP timestamps.
*NTP daemon RX timestamps*:::
The number of NTP responses which included a receive timestamp captured by the
daemon.
*NTP daemon TX timestamps*:::
The number of NTP responses which included a transmit timestamp captured by the
daemon.
*NTP kernel RX timestamps*:::
The number of NTP responses which included a receive timestamp captured by the
kernel.
*NTP kernel TX timestamps*:::
The number of NTP responses (in the interleaved mode) which included a transmit
timestamp captured by the kernel.
*NTP hardware RX timestamps*:::
The number of NTP responses which included a receive timestamp captured by the
NIC.
*NTP hardware TX timestamps*:::
The number of NTP responses (in the interleaved mode) which included a transmit
timestamp captured by the NIC.
[[allow]]*allow* [*all*] [_subnet_]::
The effect of the allow command is identical to the
@@ -944,11 +1256,8 @@ The effect of the allow command is identical to the
The syntax is illustrated in the following examples:
+
----
allow foo.example.net
allow all 1.2
allow 3.4.5
allow 6.7.8/22
allow 6.7.8.9/22
allow 1.2.3.4
allow all 3.4.5.0/24
allow 2001:db8:789a::/48
allow 0/0
allow ::/0
@@ -963,11 +1272,8 @@ The effect of the allow command is identical to the
The syntax is illustrated in the following examples:
+
----
deny foo.example.net
deny all 1.2
deny 3.4.5
deny 6.7.8/22
deny 6.7.8.9/22
deny 1.2.3.4
deny all 3.4.5.0/24
deny 2001:db8:789a::/48
deny 0/0
deny ::/0
@@ -980,8 +1286,8 @@ deny all
*local* *off*::
The *local* command allows *chronyd* to be told that it is to appear as a
reference source, even if it is not itself properly synchronised to an external
source. (This can be used on isolated networks, to allow one computer to be a
master time server with the other computers slaving to it.)
source. This can be used on isolated networks, to allow a computer to be the
primary time server for other computers.
+
The first form enables the local reference mode on the host. The syntax is
identical to the <<chrony.conf.adoc#local,*local*>> directive in the
@@ -1042,7 +1348,7 @@ used to check whether monitoring access is permitted from a named host.
Examples of use are as follows:
+
----
cmdaccheck foo.example.net
cmdaccheck ntp1.example.net
cmdaccheck 1.2.3.4
cmdaccheck 2001:db8::1
----
@@ -1120,7 +1426,7 @@ more than 1 second away from the system clock):
error).
. Save the RTC parameters to the RTC file (specified with the
<<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
failure before either the daemon exits or the <<writertc,*writertc*>> command
@@ -1153,30 +1459,34 @@ purged. An example of how to do this is shown below.
----
# mv /var/log/chrony/measurements.log /var/log/chrony/measurements1.log
# chronyc cyclelogs
# ls -l /var/log/chrony
-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
# rm /var/log/chrony/measurements1.log
----
[[dump]]*dump*::
The *dump* command causes *chronyd* to write its current history of
measurements for each of its sources to dump files in the directory specified
in the configuration file by the <<chrony.conf.adoc#dumpdir,*dumpdir*>>
directive and also write server NTS keys and client NTS cookies to the
directory specified by the <<chrony.conf.adoc#ntsdumpdir1,*ntsdumpdir*>>
directive. Note that *chronyd* does this automatically when it exits. This
command is mainly useful for inspection of the history whilst *chronyd* is
running.
command is mainly useful for inspection whilst *chronyd* is running.
[[rekey]]*rekey*::
The *rekey* command causes *chronyd* to re-read the key file specified in the
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive.
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive. It
also re-reads the server NTS keys if
<<chrony.conf.adoc#ntsdumpdir2,*ntsdumpdir*>> is specified and
<<chrony.conf.adoc#ntsrotate,automatic rotation>> is disabled in the
configuration file.
[[reset]]*reset*::
The *reset* command causes *chronyd* to drop all measurements and switch to the
unsynchronised state. This command can help *chronyd* with recovery when the
measurements are known to be no longer valid or accurate, e.g. due to moving
the computer to a different network, or resuming the computer from a low-power
state (which resets the system clock).
[[reset]]*reset* *sources*::
The *reset sources* command causes *chronyd* to drop all measurements and
switch to the unsynchronised state. This command can help *chronyd* with
recovery when the measurements are known to be no longer valid or accurate,
e.g. due to moving the computer to a different network, or resuming the
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*::
The *shutdown* command causes *chronyd* to exit. This is equivalent to sending
@@ -1243,7 +1553,13 @@ keygen 73 SHA1 256
+
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
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*::
[[quit]]*quit*::
@@ -1259,7 +1575,7 @@ The *help* command displays a summary of the commands and their arguments.
== BUGS
For instructions on how to report bugs, please visit
https://chrony.tuxfamily.org/.
https://chrony-project.org/.
== AUTHORS

View File

@@ -55,7 +55,7 @@ IPv6 sockets will be created.
*-f* _file_::
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*::
When run in this mode, the program will not detach itself from the terminal.
@@ -72,9 +72,15 @@ terminal.
*-L* _level_::
This option specifies the minimum severity level of messages to be written to
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
default value is 0.
the log file, syslog, or terminal. The following levels can be specified: -1
(debug, if compiled with enabled support for debugging), 0 (informational), 1
(warning), 2 (non-fatal error), and 3 (fatal error). The 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*::
When run in this mode, *chronyd* will set the system clock once and exit. It
@@ -82,8 +88,10 @@ will not detach from the terminal.
*-Q*::
This option is similar to the *-q* option, except it only prints the offset
without making any corrections of the clock and it allows *chronyd* to be
started without root privileges.
without making any corrections of the clock and disables server ports to allow
*chronyd* to be started without root privileges, assuming the configuration
does not have any directives which would require them (e.g. *refclock*,
*hwtimestamp*, *rtcfile*, etc).
*-r*::
This option will try to reload and then delete files containing sample
@@ -94,7 +102,7 @@ directive in the configuration file. This option is useful if you want to stop
and restart *chronyd* briefly for any reason, e.g. to install a new version.
However, it should be used only on systems where the kernel can maintain clock
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
Solaris, and macOS 10.13 or later).
illumos, and macOS 10.13 or later).
*-R*::
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
@@ -131,51 +139,84 @@ running, but still allow it to adjust the frequency of the system clock.
*-u* _user_::
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
<<chrony.conf.adoc#user,*user*>> directive. The default value is
<<chrony.conf.adoc#user,*user*>> directive. The compiled-in default value is
_@DEFAULT_USER@_.
+
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
On macOS, FreeBSD, NetBSD, and illumos *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited
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_::
This option configures a system call filter when *chronyd* is compiled with
support for the Linux secure computing (seccomp) facility. In level 1 the
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
value is 0.
This option configures system call filters loaded by *chronyd* processes if it
was compiled with support for the Linux secure computing (seccomp) facility.
Three levels are defined: 0, 1, 2. The filters are disabled at level 0. At
levels 1 and 2, *chronyd* will be killed if it makes a system call which is
blocked by the filters. The level can be specified as a negative number to
trigger the SIGSYS signal instead of SIGKILL, which can be useful for
debugging. The default value is 0.
+
It's recommended to enable the filter only when it's known to work on the
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
different versions or implementations of the libraries may make different
system calls. If the filter is missing some system call, *chronyd* could be
killed even in normal operation.
At level 1, the filters allow only selected system calls that are normally
expected to be made by *chronyd*. Other system calls are blocked. This level is
recommended only if it is known to work on the version of the system where
*chrony* is installed. The filters need to allow also system calls made by
libraries that *chronyd* is using (e.g. libc), but different versions or
implementations of the libraries might make different system calls. If the
filters are missing a system call, *chronyd* could be killed even in normal
operation.
+
At level 2, the filters block only a small number of specific system calls
(e.g. fork and exec). This approach should avoid false positives, but the
protection of the system against a compromised *chronyd* process is much more
limited.
+
The filters cannot be enabled with the *mailonchange* directive.
*-P* _priority_::
On Linux, this option will select the SCHED_FIFO real-time scheduler at the
specified priority (which must be between 0 and 100). On macOS, this option
must have either a value of 0 to disable the thread time
constraint policy or 1 for the policy to be enabled. Other systems do not
On Linux, FreeBSD, NetBSD, and illumos this option will select the SCHED_FIFO
real-time scheduler at the specified priority (which must be between 0 and
100). On macOS, this option must have either a value of 0 to disable the thread
time constraint policy or 1 for the policy to be enabled. Other systems do not
support this option. The default value is 0.
*-m*::
This option will lock *chronyd* into RAM so that it will never be paged out.
This mode is only supported on Linux.
This mode is only supported on Linux, FreeBSD, NetBSD, and illumos.
*-x*::
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
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
system clock (e.g. in some containers) in order to operate as an NTP server. It
is not recommended to run *chronyd* (with or without *-x*) when another process
is controlling the system clock.
option allows *chronyd* to be started without the capability to adjust or set
the system clock (e.g. in some containers) to operate as an NTP server.
*-v*::
*-v*, *--version*::
With this option *chronyd* will print version number to the terminal and exit.
*-h*, *--help*::
With this option *chronyd* will print a help message to the terminal and exit.
== ENVIRONMENT VARIABLES
*LISTEN_FDS*::
On Linux systems, the systemd service manager may pass file descriptors for
pre-initialised sockets to *chronyd*. The service manager allocates and binds
the file descriptors, and passes a copy to each spawned instance of the
service. This allows for zero-downtime service restarts as the sockets buffer
client requests until the service is able to handle them. The service manager
sets the LISTEN_FDS environment variable to the number of passed file
descriptors.
== FILES
_@SYSCONFDIR@/chrony.conf_
@@ -187,7 +228,7 @@ _@SYSCONFDIR@/chrony.conf_
== BUGS
For instructions on how to report bugs, please visit
https://chrony.tuxfamily.org/.
https://chrony-project.org/.
== AUTHORS

74
doc/contributing.adoc Normal file
View File

@@ -0,0 +1,74 @@
// This file is part of chrony
//
// Copyright (C) Miroslav Lichvar 2024
//
// 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.
= Contributing
== Patches
The source code of `chrony` is maintained in a git repository at
https://gitlab.com/chrony/chrony. Patches can be submitted to the `chrony-dev`
mailing list, or as a merge request on gitlab. Before spending a lot of time
implementing a new major feature, it is recommended to ask on the mailing list
for comments about its design and whether such feature fits the goals of the
project.
Each commit should be a self-contained logical change, which does not break
the build or tests. New functionality and fixed bugs should be covered by a new
test or an extended existing test in the test suite. The test can be included
in the same commit or added as a separate commit. The same rule applies to
documentation. All command-line options, configuration directives, and
`chronyc` commands should be documented.
The most important tests can be executed by running `make check` or `make
quickcheck`. The unit and system tests run on all supported systems. The system
tests require root privileges. The simulation tests run only on Linux and
require https://gitlab.com/chrony/clknetsim[clknetsim] to be compiled in the
directory containing the tests, but they are executed with a merge request on
gitlab.
The commit message should explain any non-trivial changes, e.g. what problem is
the commit solving and how. The commit subject (first line of the message)
should be written in an imperative form, prefixed with the component name if it
is not a more general change, starting in lower case, and no period at the end.
See the git log for examples.
Simpler code is better. Less code is better. Security is a top priority.
Assertions should catch only bugs in the `chrony` code. Unexpected values in
external input (e.g. anything received from network) must be handled correctly
without crashing and memory corruption. Fuzzing support is available at
https://gitlab.com/chrony/chrony-fuzz. The fuzzing coverage is checked by the
project maintainer before each release.
The code should mostly be self-documenting. Comments should explain the
less obvious things.
== Coding style
The code uses two spaces for indentation. No tabs. The line length should
normally not exceed 95 characters. Too much indentation indicates the code will
not be very readable.
Function names are in an imperative form. Names of static functions use
lowercase characters and underscores. Public functions, structures, typedefs
are in CamelCase with a prefix specific to the module (e.g. LCL - local, NCR
- NTP core, NKS - NTS-KE server, SST - sourcestats).
Function names are not followed by space, but keywords of the language (e.g.
`if`, `for`, `while`, `sizeof`) are followed by space.
Have a look at the existing code to get a better idea what is expected.

File diff suppressed because it is too large Load Diff

View File

@@ -27,7 +27,7 @@ The following libraries with their development files, and programs, are needed
to enable optional features:
* pkg-config: detection of development libraries
* Nettle, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
* Nettle, GnuTLS, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
* libcap: dropping root privileges on Linux (`DROPROOT`)
* libseccomp: system call filter on Linux (`SCFILTER`)
* GnuTLS and Nettle: Network Time Security (`NTS`)
@@ -94,13 +94,13 @@ want to enable the support, specify the `--disable-asyncdns` flag to
If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle],
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or
http://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
https://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
`chronyd` will be built with support for other cryptographic hash functions
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
`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,
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`
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
The `configure` and `make` procedures have some extra options that may be

View File

@@ -8,11 +8,39 @@ Wants=time-sync.target
[Service]
Type=oneshot
# Wait up to ~10 minutes for chronyd to synchronize and the remaining
# clock 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
# Wait for chronyd to update the clock and the remaining
# correction to be less than 0.1 seconds
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
StandardOutput=null
CapabilityBoundingSet=
DevicePolicy=closed
DynamicUser=yes
IPAddressAllow=localhost
IPAddressDeny=any
LockPersonality=yes
MemoryDenyWriteExecute=yes
PrivateDevices=yes
PrivateUsers=yes
ProtectClock=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
ProtectSystem=strict
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=yes
RestrictRealtime=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources
UMask=0777
[Install]
WantedBy=multi-user.target

View File

@@ -1,5 +1,5 @@
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
# Please consider joining the pool (https://www.pool.ntp.org/join.html).
pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time.
@@ -25,11 +25,20 @@ rtcsync
# Serve time even if not synchronized to a time source.
#local stratum 10
# Require authentication (nts or key option) for all NTP sources.
#authselectmode require
# Specify file containing keys for NTP authentication.
#keyfile /etc/chrony.keys
# Get TAI-UTC offset and leap seconds from the system tz database.
#leapsectz right/UTC
# Save NTS keys and cookies.
ntsdumpdir /var/lib/chrony
# Insert/delete leap seconds by slewing instead of stepping.
#leapsecmode slew
# Set the TAI-UTC offset of the system clock.
#leapseclist /usr/share/zoneinfo/leap-seconds.list
# Specify directory for log files.
logdir /var/log/chrony

View File

@@ -27,9 +27,9 @@
# you can access at http://support.ntp.org/bin/view/Servers/WebHome or
# you can use servers from the pool.ntp.org project.
! server foo.example.net iburst
! server bar.example.net iburst
! server baz.example.net iburst
! server ntp1.example.net iburst
! server ntp2.example.net iburst
! server ntp3.example.net iburst
! pool pool.ntp.org iburst
@@ -57,6 +57,20 @@
! 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
# 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
# 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/.../nts-server.crt
! ntsserverkey /etc/.../nts-server.key
# 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
# 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
# useful (the real time clock is used to 'flywheel' chronyd between
# boots).
#
# Enable these two options to use this.
# Uncomment the following line to use this.
! dumponexit
! dumpdir /var/lib/chrony
# chronyd writes its process ID to a file. If you try to start a second
@@ -97,11 +126,11 @@ driftfile /var/lib/chrony/drift
! pidfile /var/run/chrony/chronyd.pid
# If the system timezone database is kept up to date and includes the
# right/UTC timezone, chronyd can use it to determine the current
# TAI-UTC offset and when will the next leap second occur.
# The system timezone database usually comes with a list of leap seconds and
# corresponding TAI-UTC offsets. chronyd can use it to set the offset of the
# system TAI clock and have an additional source of leap seconds.
! leapsectz right/UTC
! leapseclist /usr/share/zoneinfo/leap-seconds.list
#######################################################################
### INITIAL CLOCK CORRECTION
@@ -116,6 +145,18 @@ driftfile /var/lib/chrony/drift
! 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
# 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
# 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
# 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
# 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
# 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
@@ -208,7 +238,7 @@ driftfile /var/lib/chrony/drift
# several people, you need to set up a mailing list or sendmail alias
# for them and use the address of that.)
! mailonchange wibble@foo.example.net 0.5
! mailonchange wibble@example.net 0.5
#######################################################################
### COMMAND ACCESS

View File

@@ -11,3 +11,5 @@
#1 MD5 AVeryLongAndRandomPassword
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995
#4 AES128 HEX:2DA837C4B6573748CA692B8C828E4891
#5 AES256 HEX:2666B8099BFF2D5BA20876121788ED24D2BE59111B8FFB562F0F56AE6EC7246E

View File

@@ -1,15 +0,0 @@
#!/bin/sh
# This is a NetworkManager dispatcher / networkd-dispatcher script for
# chronyd to set its NTP sources online or offline when a network interface
# is configured or removed
export LC_ALL=C
# For NetworkManager consider only up/down events
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
chronyc onoffline > /dev/null 2>&1
exit 0

View File

@@ -0,0 +1,49 @@
#!/bin/sh
# This is a NetworkManager dispatcher script for chronyd to update
# its NTP sources with servers from DHCP options passed by NetworkManager
# in the DHCP4_NTP_SERVERS and DHCP6_DHCP6_NTP_SERVERS environment variables.
export LC_ALL=C
interface=$1
action=$2
chronyc=/usr/bin/chronyc
server_options=iburst
server_dir=/var/run/chrony-dhcp
dhcp_server_file=$server_dir/$interface.sources
dhcp_ntp_servers="$DHCP4_NTP_SERVERS $DHCP6_DHCP6_NTP_SERVERS"
add_servers_from_dhcp() {
rm -f "$dhcp_server_file"
for server in $dhcp_ntp_servers; do
# Check for invalid characters (from the DHCPv6 NTP FQDN suboption)
len1=$(printf '%s' "$server" | wc -c)
len2=$(printf '%s' "$server" | tr -d -c 'A-Za-z0-9:.-' | wc -c)
if [ "$len1" -ne "$len2" ] || [ "$len2" -lt 1 ] || [ "$len2" -gt 255 ]; then
continue
fi
printf 'server %s %s\n' "$server" "$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
case "$action" in
up|dhcp4-change|dhcp6-change)
add_servers_from_dhcp;;
down)
clear_servers_from_dhcp;;
esac
exit 0

View File

@@ -0,0 +1,29 @@
#!/bin/sh
# This is a NetworkManager dispatcher / networkd-dispatcher script for
# chronyd to set its NTP sources online or offline when a network interface
# is configured or removed
export LC_ALL=C
chronyc=/usr/bin/chronyc
# For NetworkManager consider only selected events
if [ $# -ge 2 ]; then
case "$2" in
up|down|connectivity-change)
;;
dhcp4-change|dhcp6-change)
# Actions "up" and "connectivity-change" in some cases do not
# guarantee that the interface has a route (e.g. a bond).
# dhcp(x)-change handles at least cases that use DHCP.
;;
*)
exit 0;;
esac
fi
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
$chronyc onoffline > /dev/null 2>&1
exit 0

View File

@@ -0,0 +1,58 @@
# This is a more restricted version of the chronyd service intended for
# minimal NTP/NTS client configurations. The daemon is started without root
# privileges and is allowed to write only to its own runtime, state, and log
# directories. It cannot bind to privileged ports in order to operate as an
# NTP server, or provide monitoring access over IPv4/IPv6. It cannot use
# reference clocks, HW timestamping, RTC tracking, and other features.
[Unit]
Description=NTP client (restricted)
Documentation=man:chronyd(8) man:chrony.conf(5)
After=chronyd.service ntpdate.service sntp.service ntpd.service
Conflicts=chronyd.service ntpd.service systemd-timesyncd.service
ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking
PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd -U $OPTIONS
User=chrony
LogsDirectory=chrony
LogsDirectoryMode=0750
RuntimeDirectory=chrony
RuntimeDirectoryMode=0750
RuntimeDirectoryPreserve=restart
StateDirectory=chrony
StateDirectoryMode=0750
AmbientCapabilities=CAP_SYS_TIME
CapabilityBoundingSet=CAP_SYS_TIME
DevicePolicy=closed
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
PrivateDevices=yes
PrivateTmp=yes
# This breaks adjtimex()
#PrivateUsers=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
ProtectSystem=strict
RemoveIPC=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallFilter=~@cpu-emulation @debug @module @mount @obsolete @raw-io
SystemCallFilter=~@reboot @resources @swap
UMask=0077
[Install]
WantedBy=multi-user.target

View File

@@ -10,9 +10,39 @@ Type=forking
PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS
CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE
CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE CAP_MKNOD CAP_SYS_ADMIN
CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_CHROOT CAP_SYS_MODULE CAP_SYS_PACCT
CapabilityBoundingSet=~CAP_SYS_PTRACE CAP_SYS_RAWIO CAP_SYS_TTY_CONFIG CAP_WAKE_ALARM
DeviceAllow=char-pps rw
DeviceAllow=char-ptp rw
DeviceAllow=char-rtc rw
DevicePolicy=closed
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
PrivateTmp=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectSystem=full
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
ProtectSystem=strict
ReadWritePaths=/run /var/lib/chrony -/var/log
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallFilter=~@cpu-emulation @debug @module @mount @obsolete @raw-io @reboot @swap
# Adjust restrictions for /usr/sbin/sendmail (mailonchange directive)
NoNewPrivileges=no
ReadWritePaths=-/var/spool
RestrictAddressFamilies=AF_NETLINK
[Install]
WantedBy=multi-user.target

View File

@@ -448,9 +448,9 @@ o_merid : /* NULL */
the same signature as the function definition does. */
#include "getdate.h"
extern struct tm *gmtime ();
extern struct tm *localtime ();
extern time_t mktime ();
extern struct tm *gmtime (const time_t *timep);
extern struct tm *localtime (const time_t *timep);
extern time_t mktime (struct tm *tm);
/* Month and day table. */
static TABLE const MonthDayTable[] = {
@@ -641,16 +641,13 @@ static TABLE const MilitaryTable[] = {
/* ARGSUSED */
static int
yyerror (s)
char *s ATTRIBUTE_UNUSED;
yyerror (char *s ATTRIBUTE_UNUSED)
{
return 0;
}
static int
ToHour (Hours, Meridian)
int Hours;
MERIDIAN Meridian;
ToHour (int Hours, MERIDIAN Meridian)
{
switch (Meridian)
{
@@ -677,8 +674,7 @@ ToHour (Hours, Meridian)
}
static int
ToYear (Year)
int Year;
ToYear (int Year)
{
if (Year < 0)
Year = -Year;
@@ -694,8 +690,7 @@ ToYear (Year)
}
static int
LookupWord (buff)
char *buff;
LookupWord (char *buff)
{
register char *p;
register char *q;
@@ -948,6 +943,7 @@ get_date (const char *p, const time_t *now)
tm.tm_hour += yyRelHour;
tm.tm_min += yyRelMinutes;
tm.tm_sec += yyRelSeconds;
tm.tm_wday = 0;
/* Let mktime deduce tm_isdst if we have an absolute timestamp,
or if the relative timestamp mentions days, months, or years. */

24
hash.h
View File

@@ -31,12 +31,26 @@
/* length of hash values produced by SHA512 */
#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_MD5_NONCRYPTO = 10000, /* For NTPv4 reference ID */
} HSH_Algorithm;
extern unsigned int HSH_Hash(int id,
const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len);
extern int HSH_GetHashId(HSH_Algorithm algorithm);
extern int HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
unsigned char *out, int out_len);
extern void HSH_Finalise(void);

145
hash_gnutls.c Normal file
View File

@@ -0,0 +1,145 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2021
*
* 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.
*
**********************************************************************
=======================================================================
Crypto hashing using the GnuTLS library
*/
#include "config.h"
#include "sysincl.h"
#include <gnutls/crypto.h>
#include "hash.h"
#include "logging.h"
struct hash {
const HSH_Algorithm algorithm;
const gnutls_digest_algorithm_t type;
gnutls_hash_hd_t handle;
};
static struct hash hashes[] = {
{ HSH_MD5_NONCRYPTO, GNUTLS_DIG_MD5, NULL },
{ HSH_MD5, GNUTLS_DIG_MD5, NULL },
{ HSH_SHA1, GNUTLS_DIG_SHA1, NULL },
{ HSH_SHA256, GNUTLS_DIG_SHA256, NULL },
{ HSH_SHA384, GNUTLS_DIG_SHA384, NULL },
{ HSH_SHA512, GNUTLS_DIG_SHA512, NULL },
{ HSH_SHA3_224, GNUTLS_DIG_SHA3_224, NULL },
{ HSH_SHA3_256, GNUTLS_DIG_SHA3_256, NULL },
{ HSH_SHA3_384, GNUTLS_DIG_SHA3_384, NULL },
{ HSH_SHA3_512, GNUTLS_DIG_SHA3_512, NULL },
{ 0, 0, NULL }
};
static int gnutls_initialised = 0;
int
HSH_GetHashId(HSH_Algorithm algorithm)
{
int id, r;
if (!gnutls_initialised) {
r = gnutls_global_init();
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
gnutls_initialised = 1;
}
for (id = 0; hashes[id].algorithm != 0; id++) {
if (hashes[id].algorithm == algorithm)
break;
}
if (hashes[id].algorithm == 0)
return -1;
if (hashes[id].handle)
return id;
if (algorithm == HSH_MD5_NONCRYPTO)
GNUTLS_FIPS140_SET_LAX_MODE();
r = gnutls_hash_init(&hashes[id].handle, hashes[id].type);
if (algorithm == HSH_MD5_NONCRYPTO)
GNUTLS_FIPS140_SET_STRICT_MODE();
if (r < 0) {
DEBUG_LOG("Could not initialise %s : %s", "hash", gnutls_strerror(r));
hashes[id].handle = NULL;
return -1;
}
return id;
}
int
HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
unsigned char *out, int out_len)
{
unsigned char buf[MAX_HASH_LENGTH];
gnutls_hash_hd_t handle;
int hash_len;
if (in1_len < 0 || in2_len < 0 || out_len < 0)
return 0;
handle = hashes[id].handle;
hash_len = gnutls_hash_get_len(hashes[id].type);
if (out_len > hash_len)
out_len = hash_len;
if (hash_len > sizeof (buf))
return 0;
if (gnutls_hash(handle, in1, in1_len) < 0 ||
(in2 && gnutls_hash(handle, in2, in2_len) < 0)) {
/* Reset the state */
gnutls_hash_output(handle, buf);
return 0;
}
gnutls_hash_output(handle, buf);
memcpy(out, buf, out_len);
return out_len;
}
void
HSH_Finalise(void)
{
int i;
if (!gnutls_initialised)
return;
for (i = 0; hashes[i].algorithm != 0; i++) {
if (hashes[i].handle)
gnutls_hash_deinit(hashes[i].handle, NULL);
}
gnutls_global_deinit();
}

View File

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

View File

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

View File

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

View File

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

111
hwclock.c
View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2018
* Copyright (C) Miroslav Lichvar 2016-2018, 2022
*
* 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
@@ -33,6 +33,7 @@
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "quantiles.h"
#include "regress.h"
#include "util.h"
@@ -43,6 +44,13 @@
/* Maximum acceptable frequency offset of the clock */
#define MAX_FREQ_OFFSET (2.0 / 3.0)
/* Quantiles for filtering readings by delay */
#define DELAY_QUANT_MIN_K 1
#define DELAY_QUANT_MAX_K 2
#define DELAY_QUANT_Q 10
#define DELAY_QUANT_REPEAT 7
#define DELAY_QUANT_MIN_STEP 1.0e-9
struct HCL_Instance_Record {
/* HW and local reference timestamp */
struct timespec hw_ref;
@@ -64,12 +72,18 @@ struct HCL_Instance_Record {
/* Minimum interval between samples */
double min_separation;
/* Expected precision of readings */
double precision;
/* Flag indicating the offset and frequency values are valid */
int valid_coefs;
/* Estimated offset and frequency of HW clock relative to local clock */
double offset;
double frequency;
/* Estimated quantiles of reading delay */
QNT_Instance delay_quants;
};
/* ================================================== */
@@ -92,7 +106,7 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
/* ================================================== */
HCL_Instance
HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
HCL_CreateInstance(int min_samples, int max_samples, double min_separation, double precision)
{
HCL_Instance clock;
@@ -110,6 +124,10 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
clock->n_samples = 0;
clock->valid_coefs = 0;
clock->min_separation = min_separation;
clock->precision = precision;
clock->delay_quants = QNT_CreateInstance(DELAY_QUANT_MIN_K, DELAY_QUANT_MAX_K,
DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
DELAY_QUANT_MIN_STEP);
LCL_AddParameterChangeHandler(handle_slew, clock);
@@ -121,6 +139,7 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
void HCL_DestroyInstance(HCL_Instance clock)
{
LCL_RemoveParameterChangeHandler(handle_slew, clock);
QNT_DestroyInstance(clock->delay_quants);
Free(clock->y_data);
Free(clock->x_data);
Free(clock);
@@ -140,6 +159,94 @@ HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
/* ================================================== */
int
HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
struct timespec *hw_ts, struct timespec *local_ts, double *err)
{
double delay, raw_delay, min_delay, low_delay, high_delay, e, pred_err;
double delay_sum, hw_sum, local_sum, local_prec, freq;
int i, min_reading, combined;
struct timespec ts1, ts2;
if (n_readings < 1)
return 0;
/* Work out the current correction multiplier needed to get cooked delays */
LCL_CookTime(&tss[0][0], &ts1, NULL);
LCL_CookTime(&tss[n_readings - 1][2], &ts2, NULL);
if (UTI_CompareTimespecs(&tss[0][0], &tss[n_readings - 1][2]) < 0)
freq = UTI_DiffTimespecsToDouble(&ts1, &ts2) /
UTI_DiffTimespecsToDouble(&tss[0][0], &tss[n_readings - 1][2]);
else
freq = 1.0;
for (i = 0; i < n_readings; i++) {
delay = freq * UTI_DiffTimespecsToDouble(&tss[i][2], &tss[i][0]);
if (delay < 0.0) {
/* Step in the middle of a reading? */
DEBUG_LOG("Bad reading delay=%e", delay);
return 0;
}
if (i == 0 || min_delay > delay) {
min_delay = delay;
min_reading = i;
}
QNT_Accumulate(clock->delay_quants, delay);
}
local_prec = LCL_GetSysPrecisionAsQuantum();
low_delay = QNT_GetQuantile(clock->delay_quants, DELAY_QUANT_MIN_K);
high_delay = QNT_GetQuantile(clock->delay_quants, DELAY_QUANT_MAX_K);
low_delay = MIN(low_delay, high_delay);
high_delay = MAX(high_delay, low_delay + local_prec);
/* Combine readings with delay in the expected interval */
for (i = combined = 0, delay_sum = hw_sum = local_sum = 0.0; i < n_readings; i++) {
raw_delay = UTI_DiffTimespecsToDouble(&tss[i][2], &tss[i][0]);
delay = freq * raw_delay;
if (delay < low_delay || delay > high_delay)
continue;
delay_sum += delay;
hw_sum += UTI_DiffTimespecsToDouble(&tss[i][1], &tss[0][1]);
local_sum += UTI_DiffTimespecsToDouble(&tss[i][0], &tss[0][0]) + raw_delay / 2.0;
combined++;
}
DEBUG_LOG("Combined %d readings lo=%e hi=%e", combined, low_delay, high_delay);
if (combined > 0) {
UTI_AddDoubleToTimespec(&tss[0][1], hw_sum / combined, hw_ts);
UTI_AddDoubleToTimespec(&tss[0][0], local_sum / combined, local_ts);
*err = MAX(delay_sum / combined / 2.0, clock->precision);
return 1;
}
/* Accept the reading with minimum delay if its interval does not contain
the current offset predicted from previous samples */
*hw_ts = tss[min_reading][1];
UTI_AddDoubleToTimespec(&tss[min_reading][0], min_delay / freq / 2.0, local_ts);
*err = MAX(min_delay / 2.0, clock->precision);
pred_err = 0.0;
LCL_CookTime(local_ts, &ts1, NULL);
if (!HCL_CookTime(clock, hw_ts, &ts2, &e) ||
((pred_err = UTI_DiffTimespecsToDouble(&ts1, &ts2)) > *err)) {
DEBUG_LOG("Accepted reading err=%e prerr=%e", *err, pred_err);
return 1;
}
return 0;
}
/* ================================================== */
void
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err)

View File

@@ -30,7 +30,7 @@ typedef struct HCL_Instance_Record *HCL_Instance;
/* Create a new HW clock instance */
extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
double min_separation);
double min_separation, double precision);
/* Destroy a HW clock instance */
extern void HCL_DestroyInstance(HCL_Instance clock);
@@ -38,6 +38,11 @@ extern void HCL_DestroyInstance(HCL_Instance clock);
/* Check if a new sample should be accumulated at this time */
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
/* Process new readings of the HW clock in form of (sys, hw, sys) triplets and
produce a sample which can be accumulated */
extern int HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
struct timespec *hw_ts, struct timespec *local_ts, double *err);
/* Accumulate a new sample */
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err);

128
keys.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* 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
* it under the terms of version 2 of the GNU General Public License as
@@ -50,16 +50,16 @@ typedef enum {
typedef struct {
uint32_t id;
int type;
int length;
KeyClass class;
union {
struct {
unsigned char *value;
int length;
int hash_id;
} ntp_mac;
CMC_Instance cmac;
} data;
int auth_delay;
} Key;
static ARR_Instance keys;
@@ -121,38 +121,6 @@ get_key(unsigned int 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 */
@@ -201,6 +169,8 @@ KEY_Reload(void)
FILE *in;
char line[2048], *key_file, *key_value;
const char *key_type;
HSH_Algorithm hash_algorithm;
CMC_Algorithm cmac_algorithm;
int hash_id;
Key key;
@@ -212,6 +182,9 @@ KEY_Reload(void)
if (!key_file)
return;
if (!UTI_CheckFilePermissions(key_file, 0771))
;
in = UTI_OpenFile(NULL, key_file, NULL, 'r', 0);
if (!in) {
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
@@ -238,27 +211,40 @@ KEY_Reload(void)
continue;
}
hash_id = HSH_GetHashId(key_type);
cmac_key_length = CMC_GetKeyLength(key_type);
hash_algorithm = UTI_HashNameToAlgorithm(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.type = hash_algorithm;
key.length = key_length;
key.data.ntp_mac.value = MallocArray(unsigned char, 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;
} else if (cmac_key_length > 0) {
if (cmac_key_length != key_length) {
} else if (cmac_algorithm != 0) {
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)",
key_type, key.id, 8 * cmac_key_length);
continue;
}
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);
} 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;
}
@@ -272,6 +258,8 @@ KEY_Reload(void)
more careful! */
qsort(ARR_GetElements(keys), ARR_GetSize(keys), sizeof (Key), compare_keys_by_id);
LOG(LOGS_INFO, "Loaded %u symmetric keys", ARR_GetSize(keys));
/* Check for duplicates */
for (i = 1; i < ARR_GetSize(keys); i++) {
if (get_key(i - 1)->id == get_key(i)->id)
@@ -280,9 +268,6 @@ KEY_Reload(void)
/* Erase any passwords from stack */
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 +323,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
KEY_GetAuthLength(uint32_t key_id)
{
@@ -387,24 +357,36 @@ KEY_CheckKeyLength(uint32_t key_id)
if (!key)
return 0;
switch (key->class) {
case NTP_MAC:
return key->data.ntp_mac.length >= MIN_SECURE_KEY_LENGTH;
default:
return 1;
}
return key->length >= MIN_SECURE_KEY_LENGTH;
}
/* ================================================== */
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
generate_auth(Key *key, const unsigned char *data, int data_len,
unsigned char *auth, int auth_len)
generate_auth(Key *key, const void *data, int data_len, unsigned char *auth, int auth_len)
{
switch (key->class) {
case NTP_MAC:
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:
return CMC_Hash(key->data.cmac, data, data_len, auth, auth_len);
default:
@@ -415,7 +397,7 @@ generate_auth(Key *key, const unsigned char *data, int data_len,
/* ================================================== */
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)
{
unsigned char buf[MAX_HASH_LENGTH];
@@ -429,7 +411,7 @@ check_auth(Key *key, const unsigned char *data, int data_len,
/* ================================================== */
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)
{
Key *key;
@@ -445,7 +427,7 @@ KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
/* ================================================== */
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)
{
Key *key;

9
keys.h
View File

@@ -34,15 +34,14 @@ extern void KEY_Finalise(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_GetAuthDelay(uint32_t key_id);
extern int KEY_GetAuthLength(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,
int data_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_GenerateAuth(uint32_t key_id, const void *data, int data_len,
unsigned char *auth, int auth_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);
#endif /* GOT_KEYS_H */

272
leapdb.c Normal file
View File

@@ -0,0 +1,272 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
* Copyright (C) Patrick Oppenlander 2023, 2024
*
* 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.
*
**********************************************************************
=======================================================================
This module provides leap second information. */
#include "config.h"
#include "sysincl.h"
#include "conf.h"
#include "leapdb.h"
#include "logging.h"
#include "util.h"
/* ================================================== */
/* Source of leap second data */
enum {
SRC_NONE,
SRC_TIMEZONE,
SRC_LIST,
} leap_src;
/* Offset between leap-seconds.list timestamp epoch and Unix epoch.
leap-seconds.list epoch is 1 Jan 1900, 00:00:00 */
#define LEAP_SEC_LIST_OFFSET 2208988800
/* ================================================== */
static NTP_Leap
get_tz_leap(time_t when, int *tai_offset)
{
struct tm stm, *tm;
time_t t;
char *tz_env, tz_orig[128];
NTP_Leap tz_leap = LEAP_Normal;
tm = gmtime(&when);
if (!tm)
return tz_leap;
stm = *tm;
/* Temporarily switch to the timezone containing leap seconds */
tz_env = getenv("TZ");
if (tz_env) {
if (strlen(tz_env) >= sizeof (tz_orig))
return tz_leap;
strcpy(tz_orig, tz_env);
}
setenv("TZ", CNF_GetLeapSecTimezone(), 1);
tzset();
/* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
t = mktime(&stm);
if (t != -1)
*tai_offset = t - when + 10;
/* Set the time to 23:59:60 and see how it overflows in mktime() */
stm.tm_sec = 60;
stm.tm_min = 59;
stm.tm_hour = 23;
t = mktime(&stm);
if (tz_env)
setenv("TZ", tz_orig, 1);
else
unsetenv("TZ");
tzset();
if (t == -1)
return tz_leap;
if (stm.tm_sec == 60)
tz_leap = LEAP_InsertSecond;
else if (stm.tm_sec == 1)
tz_leap = LEAP_DeleteSecond;
return tz_leap;
}
/* ================================================== */
static NTP_Leap
get_list_leap(time_t when, int *tai_offset)
{
FILE *f;
char line[1024];
NTP_Leap ret_leap = LEAP_Normal;
int ret_tai_offset = 0, prev_lsl_tai_offset = 10;
int64_t when1900, lsl_updated = 0, lsl_expiry = 0;
const char *leap_sec_list = CNF_GetLeapSecList();
if (!(f = UTI_OpenFile(NULL, leap_sec_list, NULL, 'r', 0))) {
LOG(LOGS_ERR, "Failed to open leap seconds list %s", leap_sec_list);
goto out;
}
/* Leap second happens at midnight */
when = (when / (24 * 3600) + 1) * (24 * 3600);
/* leap-seconds.list timestamps are relative to 1 Jan 1900, 00:00:00 */
when1900 = (int64_t)when + LEAP_SEC_LIST_OFFSET;
while (fgets(line, sizeof line, f) > 0) {
int64_t lsl_when;
int lsl_tai_offset;
char *p;
/* Ignore blank lines */
for (p = line; *p && isspace(*p); ++p)
;
if (!*p)
continue;
if (*line == '#') {
/* Update time line starts with #$ */
if (line[1] == '$' && sscanf(line + 2, "%"SCNd64, &lsl_updated) != 1)
goto error;
/* Expiration time line starts with #@ */
if (line[1] == '@' && sscanf(line + 2, "%"SCNd64, &lsl_expiry) != 1)
goto error;
/* Comment or a special comment we don't care about */
continue;
}
/* Leap entry */
if (sscanf(line, "%"SCNd64" %d", &lsl_when, &lsl_tai_offset) != 2)
goto error;
if (when1900 == lsl_when) {
if (lsl_tai_offset > prev_lsl_tai_offset)
ret_leap = LEAP_InsertSecond;
else if (lsl_tai_offset < prev_lsl_tai_offset)
ret_leap = LEAP_DeleteSecond;
/* When is rounded to the end of the day, so offset hasn't changed yet! */
ret_tai_offset = prev_lsl_tai_offset;
} else if (when1900 > lsl_when) {
ret_tai_offset = lsl_tai_offset;
}
prev_lsl_tai_offset = lsl_tai_offset;
}
/* Make sure the file looks sensible */
if (!feof(f) || !lsl_updated || !lsl_expiry)
goto error;
if (when1900 >= lsl_expiry)
LOG(LOGS_WARN, "Leap second list %s needs update", leap_sec_list);
goto out;
error:
if (f)
fclose(f);
LOG(LOGS_ERR, "Failed to parse leap seconds list %s", leap_sec_list);
return LEAP_Normal;
out:
if (f)
fclose(f);
*tai_offset = ret_tai_offset;
return ret_leap;
}
/* ================================================== */
static int
check_leap_source(NTP_Leap (*src)(time_t when, int *tai_offset))
{
int tai_offset = 0;
/* Check that the leap second source has good data for Jun 30 2012 and Dec 31 2012 */
if (src(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
src(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35)
return 1;
return 0;
}
/* ================================================== */
void
LDB_Initialise(void)
{
const char *leap_tzname, *leap_sec_list;
leap_tzname = CNF_GetLeapSecTimezone();
if (leap_tzname && !check_leap_source(get_tz_leap)) {
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
leap_tzname = NULL;
}
leap_sec_list = CNF_GetLeapSecList();
if (leap_sec_list && !check_leap_source(get_list_leap)) {
LOG(LOGS_WARN, "Leap second list %s failed check, ignoring", leap_sec_list);
leap_sec_list = NULL;
}
if (leap_sec_list) {
LOG(LOGS_INFO, "Using leap second list %s", leap_sec_list);
leap_src = SRC_LIST;
} else if (leap_tzname) {
LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
leap_src = SRC_TIMEZONE;
}
}
/* ================================================== */
NTP_Leap
LDB_GetLeap(time_t when, int *tai_offset)
{
static time_t last_ldb_leap_check;
static NTP_Leap ldb_leap;
static int ldb_tai_offset;
/* Do this check at most twice a day */
when = when / (12 * 3600) * (12 * 3600);
if (last_ldb_leap_check == when)
goto out;
last_ldb_leap_check = when;
ldb_leap = LEAP_Normal;
ldb_tai_offset = 0;
switch (leap_src) {
case SRC_NONE:
break;
case SRC_TIMEZONE:
ldb_leap = get_tz_leap(when, &ldb_tai_offset);
break;
case SRC_LIST:
ldb_leap = get_list_leap(when, &ldb_tai_offset);
break;
}
out:
*tai_offset = ldb_tai_offset;
return ldb_leap;
}
/* ================================================== */
void
LDB_Finalise(void)
{
/* Nothing to do */
}

37
leapdb.h Normal file
View File

@@ -0,0 +1,37 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Patrick Oppenlander 2023
*
* 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.
*
**********************************************************************
=======================================================================
This module provides leap second information.
*/
#ifndef GOT_LEAPDB_H
#define GOT_LEAPDB_H
#include "ntp.h"
extern void LDB_Initialise(void);
extern NTP_Leap LDB_GetLeap(time_t when, int *tai_offset);
extern void LDB_Finalise(void);
#endif /* GOT_LEAPDB_H */

85
local.c
View File

@@ -108,8 +108,8 @@ static double max_clock_error;
#define NSEC_PER_SEC 1000000000
static void
calculate_sys_precision(void)
static double
measure_clock_precision(void)
{
struct timespec ts, old_ts;
int iters, diff, best;
@@ -135,18 +135,7 @@ calculate_sys_precision(void)
assert(best > 0);
precision_quantum = 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);
return 1.0e-9 * best;
}
/* ================================================== */
@@ -170,7 +159,16 @@ LCL_Initialise(void)
current_freq_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
never stop or run backwards */
@@ -185,13 +183,11 @@ LCL_Initialise(void)
void
LCL_Finalise(void)
{
while (change_list.next != &change_list)
LCL_RemoveParameterChangeHandler(change_list.next->handler,
change_list.next->anything);
while (dispersion_notify_list.next != &dispersion_notify_list)
LCL_RemoveDispersionNotifyHandler(dispersion_notify_list.next->handler,
dispersion_notify_list.next->anything);
/* Make sure all handlers have been removed */
if (change_list.next != &change_list)
assert(0);
if (dispersion_notify_list.next != &dispersion_notify_list)
assert(0);
}
/* ================================================== */
@@ -509,7 +505,7 @@ LCL_AccumulateDeltaFrequency(double dfreq)
/* ================================================== */
void
int
LCL_AccumulateOffset(double offset, double corr_rate)
{
struct timespec raw, cooked;
@@ -521,12 +517,14 @@ LCL_AccumulateOffset(double offset, double corr_rate)
LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&cooked, offset))
return;
return 0;
(*drv_accrue_offset)(offset, corr_rate);
/* Dispatch to all handlers */
invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust);
return 1;
}
/* ================================================== */
@@ -565,6 +563,8 @@ void
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion)
{
LCL_CancelOffsetCorrection();
/* Dispatch to all handlers */
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
@@ -590,7 +590,7 @@ LCL_NotifyLeap(int leap)
/* ================================================== */
void
int
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{
struct timespec raw, cooked;
@@ -602,7 +602,7 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&cooked, doffset))
return;
return 0;
old_freq_ppm = current_freq_ppm;
@@ -624,6 +624,26 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
/* Dispatch to all handlers */
invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust);
return 1;
}
/* ================================================== */
int
LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset, double corr_rate)
{
ChangeListEntry *first_handler;
int r;
first_handler = change_list.next;
change_list.next = &change_list;
r = LCL_AccumulateFrequencyAndOffset(dfreq, doffset, corr_rate);
change_list.next = first_handler;
return r;
}
/* ================================================== */
@@ -691,6 +711,19 @@ LCL_MakeStep(void)
/* ================================================== */
void
LCL_CancelOffsetCorrection(void)
{
struct timespec raw;
double correction;
LCL_ReadRawTime(&raw);
LCL_GetOffsetCorrection(&raw, &correction, NULL);
LCL_AccumulateOffset(correction, 0.0);
}
/* ================================================== */
int
LCL_CanSystemLeap(void)
{

12
local.h
View File

@@ -149,7 +149,7 @@ extern void LCL_AccumulateDeltaFrequency(double dfreq);
forwards (i.e. it is currently slow of true time). Provided is also
a suggested correction rate (correction time * offset). */
extern void LCL_AccumulateOffset(double offset, double corr_rate);
extern int LCL_AccumulateOffset(double offset, double corr_rate);
/* Routine to apply an immediate offset by doing a sudden step if
possible. (Intended for use after an initial estimate of offset has
@@ -171,7 +171,12 @@ extern void LCL_NotifyLeap(int leap);
/* Perform the combination of modifying the frequency and applying
a slew, in one easy step */
extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
extern int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
/* Same as the routine above, except it does not call the registered
parameter change handlers */
extern int LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset,
double corr_rate);
/* Routine to read the system precision as a log to base 2 value. */
extern int LCL_GetSysPrecisionAsLog(void);
@@ -197,6 +202,9 @@ extern void LCL_Finalise(void);
to a timezone problem. */
extern int LCL_MakeStep(void);
/* Routine to cancel the outstanding system clock correction */
extern void LCL_CancelOffsetCorrection(void);
/* Check if the system driver supports leap seconds, i.e. LCL_SetSystemLeap
does something */
extern int LCL_CanSystemLeap(void);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* 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
* it under the terms of version 2 of the GNU General Public License as
@@ -33,11 +33,15 @@
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "util.h"
/* This is used by DEBUG_LOG macro */
LOG_Severity log_min_severity = LOGS_INFO;
/* Current logging contexts */
static LOG_Context log_contexts;
/* ================================================== */
/* Flag indicating we have initialised */
static int initialised = 0;
@@ -61,12 +65,18 @@ static int n_filelogs = 0;
static struct LogFile logfiles[MAX_FILELOGS];
/* Global prefix for debug messages */
static char *debug_prefix;
/* ================================================== */
/* Init function */
void
LOG_Initialise(void)
{
debug_prefix = Strdup("");
log_contexts = 0;
initialised = 1;
LOG_OpenFileLog(NULL);
}
@@ -85,6 +95,8 @@ LOG_Finalise(void)
LOG_CycleLogFiles();
Free(debug_prefix);
initialised = 0;
}
@@ -132,6 +144,9 @@ void LOG_Message(LOG_Severity severity,
time_t t;
struct tm *tm;
assert(initialised);
severity = CLAMP(LOGS_DEBUG, severity, LOGS_FATAL);
if (!system_log && file_log && severity >= log_min_severity) {
/* Don't clutter up syslog with timestamps and internal debugging info */
time(&t);
@@ -141,8 +156,13 @@ void LOG_Message(LOG_Severity severity,
fprintf(file_log, "%s ", buf);
}
#if DEBUG > 0
if (log_min_severity <= LOGS_DEBUG)
fprintf(file_log, "%s:%d:(%s) ", filename, line_number, function_name);
if (log_min_severity <= LOGS_DEBUG) {
/* Log severity to character mapping (debug, info, warn, err, fatal) */
const char severity_chars[LOGS_FATAL - LOGS_DEBUG + 1] = {'D', 'I', 'W', 'E', 'F'};
fprintf(file_log, "%c:%s%s:%d:(%s) ", severity_chars[severity - LOGS_DEBUG],
debug_prefix, filename, line_number, function_name);
}
#endif
}
@@ -165,7 +185,7 @@ void LOG_Message(LOG_Severity severity,
/* Send the message also to the foreground process if it is
still running, or stderr if it is still open */
if (parent_fd > 0) {
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
if (!LOG_NotifyParent(buf))
; /* Not much we can do here */
} else if (system_log && parent_fd == 0) {
system_log = 0;
@@ -220,6 +240,47 @@ void LOG_SetMinSeverity(LOG_Severity severity)
/* ================================================== */
LOG_Severity
LOG_GetMinSeverity(void)
{
return log_min_severity;
}
/* ================================================== */
void
LOG_SetContext(LOG_Context context)
{
log_contexts |= context;
}
/* ================================================== */
void
LOG_UnsetContext(LOG_Context context)
{
log_contexts &= ~context;
}
/* ================================================== */
LOG_Severity
LOG_GetContextSeverity(LOG_Context contexts)
{
return log_contexts & contexts ? LOGS_INFO : LOGS_DEBUG;
}
/* ================================================== */
void
LOG_SetDebugPrefix(const char *prefix)
{
Free(debug_prefix);
debug_prefix = Strdup(prefix);
}
/* ================================================== */
void
LOG_SetParentFd(int fd)
{
@@ -230,6 +291,17 @@ LOG_SetParentFd(int fd)
/* ================================================== */
int
LOG_NotifyParent(const char *message)
{
if (parent_fd <= 0)
return 1;
return write(parent_fd, message, strlen(message) + 1) > 0;
}
/* ================================================== */
void
LOG_CloseParentFd()
{
@@ -243,7 +315,10 @@ LOG_CloseParentFd()
LOG_FileID
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].banner = banner;
@@ -267,7 +342,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
if (!logfiles[id].file) {
char *logdir = CNF_GetLogDir();
if (logdir[0] == '\0') {
if (!logdir) {
LOG(LOGS_WARN, "logdir not specified");
logfiles[id].name = NULL;
return;

View File

@@ -97,6 +97,26 @@ extern void LOG_Message(LOG_Severity severity, const char *format, ...);
prefixed with the filename, line number, and function name. */
extern void LOG_SetMinSeverity(LOG_Severity severity);
/* Get the minimum severity */
extern LOG_Severity LOG_GetMinSeverity(void);
/* Flags for info messages that should be logged only in specific contexts */
typedef enum {
LOGC_Command = 1,
LOGC_SourceFile = 2,
} LOG_Context;
/* Modify current contexts */
extern void LOG_SetContext(LOG_Context context);
extern void LOG_UnsetContext(LOG_Context context);
/* Get severity depending on the current active contexts: INFO if they contain
at least one of the specified contexts, DEBUG otherwise */
extern LOG_Severity LOG_GetContextSeverity(LOG_Context contexts);
/* 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 */
extern void LOG_OpenFileLog(const char *log_file);
@@ -106,7 +126,10 @@ extern void LOG_OpenSystemLog(void);
/* Stop using stderr and send fatal message to the foreground process */
extern void LOG_SetParentFd(int fd);
/* Close the pipe to the foreground process so it can exit */
/* Send a message to the foreground process */
extern int LOG_NotifyParent(const char *message);
/* Close the pipe to the foreground process */
extern void LOG_CloseParentFd(void);
/* File logging functions */

137
main.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* 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
* it under the terms of version 2 of the GNU General Public License as
@@ -32,13 +32,13 @@
#include "main.h"
#include "sched.h"
#include "leapdb.h"
#include "local.h"
#include "sys.h"
#include "ntp_io.h"
#include "ntp_signd.h"
#include "ntp_sources.h"
#include "ntp_core.h"
#include "nts_ke_client.h"
#include "nts_ke_server.h"
#include "nts_ntp_server.h"
#include "socket.h"
@@ -77,11 +77,18 @@ static REF_Mode ref_mode = REF_ModeNormal;
static void
do_platform_checks(void)
{
struct timespec ts;
/* Require at least 32-bit integers, two's complement representation and
the usual implementation of conversion of unsigned integers */
assert(sizeof (int) >= 4);
assert(-1 == ~0);
assert((int32_t)4294967295U == (int32_t)-1);
/* Require time_t and tv_nsec in timespec to be signed */
ts.tv_sec = -1;
ts.tv_nsec = -1;
assert(ts.tv_sec < 0 && ts.tv_nsec < 0);
}
/* ================================================== */
@@ -91,7 +98,7 @@ delete_pidfile(void)
{
const char *pidfile = CNF_GetPidFile();
if (!pidfile[0])
if (!pidfile)
return;
if (!UTI_RemoveFile(NULL, pidfile, NULL))
@@ -105,9 +112,8 @@ MAI_CleanupAndExit(void)
{
if (!initialised) exit(exit_status);
if (CNF_GetDumpDir()[0] != '\0') {
SRC_DumpSources();
}
LCL_CancelOffsetCorrection();
SRC_DumpSources();
/* Don't update clock when removing sources */
REF_SetMode(REF_ModeIgnore);
@@ -116,7 +122,6 @@ MAI_CleanupAndExit(void)
TMC_Finalise();
MNL_Finalise();
CLG_Finalise();
NKC_Finalise();
NKS_Finalise();
NNS_Finalise();
NSD_Finalise();
@@ -125,13 +130,16 @@ MAI_CleanupAndExit(void)
NCR_Finalise();
NIO_Finalise();
CAM_Finalise();
SCK_Finalise();
KEY_Finalise();
RCL_Finalise();
SRC_Finalise();
REF_Finalise();
LDB_Finalise();
RTC_Finalise();
SYS_Finalise();
SCK_Finalise();
SCH_Finalise();
LCL_Finalise();
PRV_Finalise();
@@ -142,6 +150,8 @@ MAI_CleanupAndExit(void)
HSH_Finalise();
LOG_Finalise();
UTI_ResetGetRandomFunctions();
exit(exit_status);
}
@@ -150,7 +160,6 @@ MAI_CleanupAndExit(void)
static void
signal_cleanup(int x)
{
if (!initialised) exit(0);
SCH_QuitProgram();
}
@@ -159,6 +168,8 @@ signal_cleanup(int x)
static void
quit_timeout(void *arg)
{
LOG(LOGS_INFO, "Timeout reached");
/* Return with non-zero status if the clock is not synchronised */
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
SCH_QuitProgram();
@@ -186,7 +197,7 @@ ntp_source_resolving_end(void)
NSR_AutoStartSources();
/* Special modes can end only when sources update their reachability.
Give up immediatelly if there are no active sources. */
Give up immediately if there are no active sources. */
if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) {
REF_SetUnsynchronised();
}
@@ -204,7 +215,10 @@ post_init_ntp_hook(void *anything)
REF_SetMode(ref_mode);
}
/* Close the pipe to the foreground process so it can exit */
/* Send an empty message to the foreground process so it can exit.
If that fails, indicating the process was killed, exit too. */
if (!LOG_NotifyParent(""))
SCH_QuitProgram();
LOG_CloseParentFd();
CNF_AddSources();
@@ -261,7 +275,7 @@ check_pidfile(void)
FILE *in;
int pid, count;
if (!pidfile[0])
if (!pidfile)
return;
in = UTI_OpenFile(NULL, pidfile, NULL, 'r', 0);
@@ -289,7 +303,7 @@ write_pidfile(void)
const char *pidfile = CNF_GetPidFile();
FILE *out;
if (!pidfile[0])
if (!pidfile)
return;
out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644);
@@ -322,10 +336,13 @@ go_daemon(void)
char message[1024];
int r;
/* Don't exit before the 'parent' */
waitpid(pid, NULL, 0);
close(pipefd[1]);
r = read(pipefd[0], message, sizeof (message));
if (r) {
if (r > 0) {
if (r != 1 || message[0] != '\0') {
if (r > 1) {
/* Print the error message from the child */
message[sizeof (message) - 1] = '\0';
fprintf(stderr, "%s\n", message);
@@ -344,7 +361,9 @@ go_daemon(void)
if (pid < 0) {
LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) {
exit(0); /* In the 'parent' */
/* In the 'parent' */
close(pipefd[1]);
exit(0);
} else {
/* In the child we want to leave running as the daemon */
@@ -354,9 +373,9 @@ go_daemon(void)
}
/* Don't keep stdin/out/err from before. But don't close
the parent pipe yet. */
the parent pipe yet, or reusable file descriptors. */
for (fd=0; fd<1024; fd++) {
if (fd != pipefd[1])
if (fd != pipefd[1] && !SCK_IsReusable(fd))
close(fd);
}
@@ -377,8 +396,34 @@ go_daemon(void)
static void
print_help(const char *progname)
{
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
progname);
printf("Usage: %s [OPTION]... [DIRECTIVE]...\n\n"
"Options:\n"
" -4\t\tUse IPv4 addresses only\n"
" -6\t\tUse IPv6 addresses only\n"
" -f FILE\tSpecify configuration file (%s)\n"
" -n\t\tDon't run as daemon\n"
" -d\t\tDon't run as daemon and log to stderr\n"
#if DEBUG > 0
" -d -d\t\tEnable debug messages\n"
#endif
" -l FILE\tLog to file\n"
" -L LEVEL\tSet logging threshold (0)\n"
" -p\t\tPrint configuration and exit\n"
" -q\t\tSet clock and exit\n"
" -Q\t\tLog offset and exit\n"
" -r\t\tReload dump files\n"
" -R\t\tAdapt configuration for restart\n"
" -s\t\tSet clock from RTC\n"
" -t SECONDS\tExit after elapsed time\n"
" -u USER\tSpecify user (%s)\n"
" -U\t\tDon't check for root\n"
" -F LEVEL\tSet system call filter level (0)\n"
" -P PRIORITY\tSet process priority (0)\n"
" -m\t\tLock memory\n"
" -x\t\tDon't control clock\n"
" -v, --version\tPrint version and exit\n"
" -h, --help\tPrint usage and exit\n",
progname, DEFAULT_CONF_FILE, DEFAULT_USER);
}
/* ================================================== */
@@ -414,13 +459,13 @@ int main
int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = -1;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
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();
LOG_Initialise();
/* Parse (undocumented) long command-line options */
/* Parse long command-line options */
for (optind = 1; optind < argc; optind++) {
if (!strcmp("--help", argv[optind])) {
print_help(progname);
@@ -434,7 +479,7 @@ int main
optind = 1;
/* 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) {
case '4':
case '6':
@@ -463,6 +508,13 @@ int main
case 'n':
nofork = 1;
break;
case 'p':
print_config = 1;
user_check = 0;
nofork = 1;
system_log = 0;
log_severity = LOGS_WARN;
break;
case 'P':
sched_priority = parse_int_arg(optarg);
break;
@@ -476,6 +528,7 @@ int main
ref_mode = REF_ModePrintOnce;
nofork = 1;
client_only = 1;
user_check = 0;
clock_control = 0;
system_log = 0;
break;
@@ -494,6 +547,9 @@ int main
case 'u':
user = optarg;
break;
case 'U':
user_check = 0;
break;
case 'v':
print_version();
return 0;
@@ -506,9 +562,12 @@ int main
}
}
if (getuid() && !client_only)
if (user_check && getuid() != 0)
LOG_FATAL("Not superuser");
/* Initialise reusable file descriptors before fork */
SCK_PreInitialise();
/* Turn into a daemon */
if (!nofork) {
go_daemon();
@@ -527,6 +586,8 @@ int main
DNS_SetAddressFamily(address_family);
CNF_Initialise(restarted, client_only);
if (print_config)
CNF_EnablePrint();
/* Parse the config file or the remaining command line arguments */
config_args = argc - optind;
@@ -537,6 +598,9 @@ int main
CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
}
if (print_config)
return 0;
/* Check whether another chronyd may already be running */
check_pidfile();
@@ -556,16 +620,20 @@ int main
PRV_Initialise();
LCL_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);
RTC_Initialise(do_init_rtc);
SRC_Initialise();
RCL_Initialise();
KEY_Initialise();
SCK_Initialise();
/* Open privileged ports before dropping root */
CAM_Initialise(address_family);
NIO_Initialise(address_family);
CAM_Initialise();
NIO_Initialise();
NCR_Initialise();
CNF_SetupAccessRestrictions();
@@ -582,16 +650,23 @@ int main
}
/* Drop root privileges if the specified user has a non-zero UID */
if (!geteuid() && (pw->pw_uid || pw->pw_gid))
SYS_DropRoot(pw->pw_uid, pw->pw_gid);
if (!geteuid() && (pw->pw_uid || pw->pw_gid)) {
SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
/* Warn if missing read access or having write access to keys */
CNF_CheckReadOnlyAccess();
}
if (!geteuid())
LOG(LOGS_WARN, "Running with root privileges");
LDB_Initialise();
REF_Initialise();
SST_Initialise();
NSR_Initialise();
NSD_Initialise();
NNS_Initialise();
NKS_Initialise(scfilter_level);
NKC_Initialise();
NKS_Initialise();
CLG_Initialise();
MNL_Initialise();
TMC_Initialise();

View File

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

15
md5.c
View File

@@ -117,8 +117,7 @@ inline UINT4 ROTATE_LEFT(UINT4 x, int n)
/* The routine MD5Init initializes the message-digest context
mdContext. All fields are set to zero.
*/
void MD5Init (mdContext)
MD5_CTX *mdContext;
void MD5Init (MD5_CTX *mdContext)
{
mdContext->i[0] = mdContext->i[1] = (UINT4)0;
@@ -134,10 +133,7 @@ MD5_CTX *mdContext;
account for the presence of each of the characters inBuf[0..inLen-1]
in the message whose digest is being computed.
*/
void MD5Update (mdContext, inBuf, inLen)
MD5_CTX *mdContext;
unsigned const char *inBuf;
unsigned int inLen;
void MD5Update (MD5_CTX *mdContext, unsigned const char *inBuf, unsigned int inLen)
{
UINT4 in[16];
int mdi;
@@ -173,8 +169,7 @@ unsigned int inLen;
ends with the desired message digest in mdContext->digest[0...15].
*/
void MD5Final (mdContext)
MD5_CTX *mdContext;
void MD5Final (MD5_CTX *mdContext)
{
UINT4 in[16];
int mdi;
@@ -214,9 +209,7 @@ MD5_CTX *mdContext;
/* Basic MD5 step. Transforms buf based on in.
*/
static void Transform (buf, in)
UINT4 *buf;
UINT4 *in;
static void Transform (UINT4 *buf, UINT4 *in)
{
UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];

View File

@@ -47,8 +47,13 @@ Realloc(void *ptr, size_t size)
{
void *r;
if (size == 0) {
Free(ptr);
return NULL;
}
r = realloc(ptr, size);
if (!r && size)
if (!r)
LOG_FATAL("Could not allocate memory");
return r;

View File

@@ -50,12 +50,24 @@ DNS_SetAddressFamily(int family)
DNS_Status
DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
{
#ifdef HAVE_GETADDRINFO
struct addrinfo hints, *res, *ai;
int i, result;
IPAddr ip;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
for (i = 0; i < max_addrs; i++)
ip_addrs[i].family = IPADDR_UNSPEC;
/* Avoid calling getaddrinfo() if the name is an IP address */
if (UTI_StringToIP(name, &ip)) {
if (address_family != IPADDR_UNSPEC && ip.family != address_family)
return DNS_Failure;
if (max_addrs >= 1)
ip_addrs[0] = ip;
return DNS_Success;
}
memset(&hints, 0, sizeof (hints));
switch (address_family) {
@@ -95,6 +107,9 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
case AF_INET6:
if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET6)
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;
memcpy(&ip_addrs[i].addr.in6, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr,
sizeof (ip_addrs->addr.in6));
@@ -104,48 +119,9 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
}
}
for (; i < max_addrs; i++)
ip_addrs[i].family = IPADDR_UNSPEC;
freeaddrinfo(res);
return !max_addrs || ip_addrs[0].family != IPADDR_UNSPEC ? DNS_Success : DNS_Failure;
#else
struct hostent *host;
int i;
if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET4)
return DNS_Failure;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
host = gethostbyname(name);
if (host == NULL) {
if (h_errno == TRY_AGAIN)
return DNS_TryAgain;
} else {
if (host->h_addrtype != AF_INET || !host->h_addr_list[0])
return DNS_Failure;
for (i = 0; host->h_addr_list[i] && i < max_addrs; i++) {
ip_addrs[i].family = IPADDR_INET4;
ip_addrs[i].addr.in4 = ntohl(*(uint32_t *)host->h_addr_list[i]);
}
for (; i < max_addrs; i++)
ip_addrs[i].family = IPADDR_UNSPEC;
return DNS_Success;
}
#ifdef FORCE_DNSRETRY
return DNS_TryAgain;
#else
return DNS_Failure;
#endif
#endif
}
/* ================================================== */
@@ -154,9 +130,11 @@ int
DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len)
{
char *result = NULL;
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
struct sockaddr_in6 saddr;
#else
struct sockaddr_in saddr;
#endif
IPSockAddr ip_saddr;
socklen_t slen;
char hbuf[NI_MAXHOST];
@@ -164,29 +142,9 @@ DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len)
ip_saddr.ip_addr = *ip_addr;
ip_saddr.port = 0;
slen = SCK_IPSockAddrToSockaddr(&ip_saddr, (struct sockaddr *)&in6, sizeof (in6));
if (!getnameinfo((struct sockaddr *)&in6, slen, hbuf, sizeof (hbuf), NULL, 0, 0))
slen = SCK_IPSockAddrToSockaddr(&ip_saddr, (struct sockaddr *)&saddr, sizeof (saddr));
if (!getnameinfo((struct sockaddr *)&saddr, slen, hbuf, sizeof (hbuf), NULL, 0, 0))
result = hbuf;
#else
struct hostent *host;
uint32_t addr;
switch (ip_addr->family) {
case IPADDR_INET4:
addr = htonl(ip_addr->addr.in4);
host = gethostbyaddr((const char *) &addr, sizeof (ip_addr), AF_INET);
break;
#ifdef FEAT_IPV6
case IPADDR_INET6:
host = gethostbyaddr((const void *) ip_addr->addr.in6, sizeof (ip_addr->addr.in6), AF_INET6);
break;
#endif
default:
host = NULL;
}
if (host)
result = host->h_name;
#endif
if (result == NULL)
result = UTI_IPToString(ip_addr);

49
ntp.h
View File

@@ -113,13 +113,49 @@ typedef struct {
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
/* Non-authentication extension fields and corresponding internal flags */
#define NTP_EF_EXP_MONO_ROOT 0xF323
#define NTP_EF_EXP_NET_CORRECTION 0xF324
#define NTP_EF_FLAG_EXP_MONO_ROOT 0x1
#define NTP_EF_FLAG_EXP_NET_CORRECTION 0x2
/* Pre-NTPv5 experimental extension field */
typedef struct {
uint32_t magic;
NTP_int32 root_delay;
NTP_int32 root_dispersion;
NTP_int64 mono_receive_ts;
uint32_t mono_epoch;
} NTP_EFExpMonoRoot;
#define NTP_EF_EXP_MONO_ROOT_MAGIC 0xF5BEDD9AU
/* Experimental extension field to provide PTP corrections */
typedef struct {
uint32_t magic;
NTP_int64 correction;
uint32_t reserved[3];
} NTP_EFExpNetCorrection;
#define NTP_EF_EXP_NET_CORRECTION_MAGIC 0x07AC2CEBU
/* Authentication extension fields */
#define NTP_EF_NTS_UNIQUE_IDENTIFIER 0x0104
#define NTP_EF_NTS_COOKIE 0x0204
#define NTP_EF_NTS_COOKIE_PLACEHOLDER 0x0304
#define NTP_EF_NTS_AUTH_AND_EEF 0x0404
/* Enumeration for authentication modes of NTP packets */
typedef enum {
NTP_AUTH_NONE = 0, /* No authentication */
NTP_AUTH_SYMMETRIC, /* MAC using symmetric key (RFC 1305, RFC 5905) */
NTP_AUTH_SYMMETRIC, /* NTP MAC or CMAC using a symmetric key
(RFC 1305, RFC 5905, RFC 8573) */
NTP_AUTH_MSSNTP, /* MS-SNTP authenticator field */
NTP_AUTH_MSSNTP_EXT, /* MS-SNTP extended authenticator field */
NTP_AUTH_NTS, /* Network Time Security (RFC ????) */
NTP_AUTH_NTS, /* Network Time Security (RFC 8915) */
} NTP_AuthMode;
/* Structure describing an NTP packet */
@@ -129,6 +165,7 @@ typedef struct {
NTP_Mode mode;
int ext_fields;
int ext_field_flags;
struct {
NTP_AuthMode mode;
@@ -151,7 +188,13 @@ typedef struct {
double peer_dispersion;
double root_delay;
double root_dispersion;
int stratum;
} NTP_Sample;
/* Possible sources of timestamps */
typedef enum {
NTP_TS_DAEMON = 0,
NTP_TS_KERNEL,
NTP_TS_HARDWARE
} NTP_Timestamp_Source;
#endif /* GOT_NTP_H */

View File

@@ -2,7 +2,7 @@
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
* it under the terms of version 2 of the GNU General Public License as
@@ -32,7 +32,6 @@
#include "logging.h"
#include "memory.h"
#include "ntp_auth.h"
#include "ntp_ext.h"
#include "ntp_signd.h"
#include "nts_ntp.h"
#include "nts_ntp_client.h"
@@ -55,20 +54,29 @@ generate_symmetric_auth(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *inf
{
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
of extension fields (RFC 7822) */
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);
if (!auth_len) {
if (auth_len < NTP_MIN_MAC_LENGTH - 4) {
DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id);
return 0;
}
*(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;
}
@@ -86,7 +94,7 @@ check_symmetric_auth(NTP_Packet *packet, NTP_PacketInfo *info)
trunc_len = info->version == 4 && info->auth.mac.length <= NTP_MAX_V4_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,
info->auth.mac.length - 4, trunc_len - 4))
return 0;
@@ -96,37 +104,6 @@ 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
is_zero_data(unsigned char *data, int length)
{
int i;
for (i = 0; i < length; i++)
if (data[i])
return 0;
return 1;
}
/* ================================================== */
static NAU_Instance
create_instance(NTP_AuthMode mode)
{
@@ -170,11 +147,12 @@ NAU_CreateSymmetricInstance(uint32_t key_id)
/* ================================================== */
NAU_Instance
NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *ntp_address)
NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set,
uint16_t ntp_port)
{
NAU_Instance instance = create_instance(NTP_AUTH_NTS);
instance->nts = NNC_CreateInstance(nts_address, name, ntp_address);
instance->nts = NNC_CreateInstance(nts_address, name, cert_set, ntp_port);
return instance;
}
@@ -184,7 +162,7 @@ NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, const IPSockAdd
void
NAU_DestroyInstance(NAU_Instance instance)
{
if (instance->nts)
if (instance->mode == NTP_AUTH_NTS)
NNC_DestroyInstance(instance->nts);
Free(instance);
}
@@ -230,14 +208,6 @@ NAU_PrepareRequestAuth(NAU_Instance instance)
/* ================================================== */
void
NAU_AdjustRequestTimestamp(NAU_Instance instance, struct timespec *ts)
{
adjust_timestamp(instance->mode, instance->key_id, ts);
}
/* ================================================== */
int
NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketInfo *info)
{
@@ -256,119 +226,13 @@ NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketIn
assert(0);
}
info->auth.mode = instance->mode;
return 1;
}
/* ================================================== */
int
NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info)
{
int parsed, remainder, ef_length, ef_type;
unsigned char *data;
data = (void *)packet;
parsed = NTP_HEADER_LENGTH;
remainder = info->length - parsed;
info->ext_fields = 0;
/* Check if this is a plain NTP packet with no extension fields or MAC */
if (remainder <= 0)
return 1;
/* In NTPv3 and older packets don't have extension fields. Anything after
the header is assumed to be a MAC. */
if (info->version <= 3) {
info->auth.mode = NTP_AUTH_SYMMETRIC;
info->auth.mac.start = parsed;
info->auth.mac.length = remainder;
info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed));
/* Check if it is an MS-SNTP authenticator field or extended authenticator
field with zeroes as digest */
if (info->version == 3 && info->auth.mac.key_id) {
if (remainder == 20 && is_zero_data(data + parsed + 4, remainder - 4))
info->auth.mode = NTP_AUTH_MSSNTP;
else if (remainder == 72 && is_zero_data(data + parsed + 8, remainder - 8))
info->auth.mode = NTP_AUTH_MSSNTP_EXT;
}
return 1;
}
/* Check for a crypto NAK */
if (remainder == 4 && ntohl(*(uint32_t *)(data + parsed)) == 0) {
info->auth.mode = NTP_AUTH_SYMMETRIC;
info->auth.mac.start = parsed;
info->auth.mac.length = remainder;
info->auth.mac.key_id = 0;
return 1;
}
/* Parse the rest of the NTPv4 packet */
while (remainder > 0) {
/* Check if the remaining data is a MAC */
if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_V4_MAC_LENGTH)
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 */
if (!NEF_ParseField(packet, info->length, parsed, &ef_length, &ef_type, NULL, NULL)) {
/* Invalid MAC or format error */
DEBUG_LOG("Invalid format or MAC");
return 0;
}
assert(ef_length > 0);
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
case NTP_EF_NTS_COOKIE:
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
case NTP_EF_NTS_AUTH_AND_EEF:
info->auth.mode = NTP_AUTH_NTS;
break;
default:
DEBUG_LOG("Unknown extension field type=%x", (unsigned int)ef_type);
}
info->ext_fields++;
parsed += ef_length;
remainder = info->length - parsed;
}
if (remainder == 0) {
/* No MAC */
return 1;
} else if (remainder >= NTP_MIN_MAC_LENGTH) {
/* This is not 100% reliable as a MAC could fail to authenticate and could
pass as an extension field, leaving reminder smaller than the minimum MAC
length */
info->auth.mode = NTP_AUTH_SYMMETRIC;
info->auth.mac.start = parsed;
info->auth.mac.length = remainder;
info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed));
return 1;
}
DEBUG_LOG("Invalid format");
return 0;
}
/* ================================================== */
int
NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
{
@@ -384,6 +248,9 @@ NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
case NTP_AUTH_MSSNTP:
/* MS-SNTP requests are not authenticated */
break;
case NTP_AUTH_MSSNTP_EXT:
/* Not supported yet */
return 0;
case NTP_AUTH_NTS:
if (!NNS_CheckRequestAuth(request, info, kod))
return 0;
@@ -397,14 +264,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
NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
NTP_Packet *response, NTP_PacketInfo *response_info,
@@ -434,6 +293,8 @@ NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
return 0;
}
response_info->auth.mode = request_info->auth.mode;
return 1;
}
@@ -484,3 +345,42 @@ NAU_ChangeAddress(NAU_Instance instance, IPAddr *address)
assert(0);
}
}
/* ================================================== */
void
NAU_DumpData(NAU_Instance instance)
{
switch (instance->mode) {
case NTP_AUTH_NTS:
NNC_DumpData(instance->nts);
break;
default:
break;
}
}
/* ================================================== */
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 "ntp.h"
#include "reports.h"
typedef struct NAU_Instance_Record *NAU_Instance;
@@ -36,7 +37,7 @@ typedef struct NAU_Instance_Record *NAU_Instance;
extern NAU_Instance NAU_CreateNoneInstance(void);
extern NAU_Instance NAU_CreateSymmetricInstance(uint32_t key_id);
extern NAU_Instance NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name,
const IPSockAddr *ntp_address);
uint32_t cert_set, uint16_t ntp_port);
/* Destroy an instance */
extern void NAU_DestroyInstance(NAU_Instance instance);
@@ -50,26 +51,14 @@ extern int NAU_GetSuggestedNtpVersion(NAU_Instance instance);
/* Perform operations necessary for NAU_GenerateRequestAuth() */
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 */
extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request,
NTP_PacketInfo *info);
/* Parse a request or response to detect the authentication mode */
extern int NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info);
/* Verify that a request is authentic. If it is not authentic and a non-zero
kod code is returned, a KoD response should be sent back. */
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
function can be called only if the previous call of NAU_CheckRequestAuth()
was on the same request. */
@@ -86,4 +75,10 @@ extern int NAU_CheckResponseAuth(NAU_Instance instance, NTP_Packet *response,
/* Change an authentication-specific address (e.g. after replacing a source) */
extern void NAU_ChangeAddress(NAU_Instance instance, IPAddr *address);
/* Save authentication-specific data to speed up the next start */
extern void NAU_DumpData(NAU_Instance instance);
/* Provide a report about the current authentication state */
extern void NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report);
#endif

1115
ntp_core.c

File diff suppressed because it is too large Load Diff

View File

@@ -38,16 +38,12 @@ typedef enum {
NTP_SERVER, NTP_PEER
} NTP_Source_Type;
typedef enum {
NTP_TS_DAEMON = 0,
NTP_TS_KERNEL,
NTP_TS_HARDWARE
} NTP_Timestamp_Source;
typedef struct {
struct timespec ts;
double err;
NTP_Timestamp_Source source;
double rx_duration;
double net_correction;
} NTP_Local_Timestamp;
/* This is a private data type used for storing the instance record for
@@ -117,11 +113,14 @@ extern void NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_d
extern void NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum);
extern void NCR_ModifyOffset(NCR_Instance inst, double new_offset);
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_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 int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all);
@@ -136,6 +135,8 @@ extern uint32_t NCR_GetLocalRefid(NCR_Instance inst);
extern int NCR_IsSyncPeer(NCR_Instance instance);
extern void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval);
extern void NCR_DumpAuthData(NCR_Instance inst);
extern void NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval);
#endif /* GOT_NTP_CORE_H */

View File

@@ -2,7 +2,7 @@
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
* it under the terms of version 2 of the GNU General Public License as

270
ntp_io.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2021
*
* 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
@@ -30,9 +30,11 @@
#include "sysincl.h"
#include "memory.h"
#include "ntp_io.h"
#include "ntp_core.h"
#include "ntp_sources.h"
#include "ptp.h"
#include "sched.h"
#include "socket.h"
#include "local.h"
@@ -70,6 +72,16 @@ static int permanent_server_sockets;
/* Flag indicating the server IPv4 socket is bound to an address */
static int bound_server_sock_fd4;
/* PTP event port, or 0 if disabled */
static int ptp_port;
/* Shared server/client sockets for NTP-over-PTP */
static int ptp_sock_fd4;
static int ptp_sock_fd6;
/* Buffer for transmitted NTP-over-PTP messages */
static PTP_NtpMessage *ptp_message;
/* Flag indicating that we have been initialised */
static int initialised=0;
@@ -83,19 +95,20 @@ static void read_from_socket(int sock_fd, int event, void *anything);
static int
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;
char *iface;
if (!SCK_IsFamilySupported(family))
if (!SCK_IsIpFamilyEnabled(family))
return INVALID_SOCK_FD;
if (!client_only)
if (!client_only) {
CNF_GetBindAddress(family, &local_addr.ip_addr);
else
iface = CNF_GetBindNtpInterface();
} else {
CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr);
if (local_addr.ip_addr.family != family)
SCK_GetAnyLocalIPAddress(family, &local_addr.ip_addr);
iface = CNF_GetBindAcquisitionInterface();
}
local_addr.port = local_port;
@@ -103,13 +116,27 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
if (!client_only)
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 (!client_only)
LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr));
return INVALID_SOCK_FD;
}
dscp = CNF_GetNtpDscp();
if (dscp > 0 && dscp < 64) {
#ifdef IP_TOS
if (family == IPADDR_INET4)
if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
;
#endif
#if defined(FEAT_IPV6) && defined(IPV6_TCLASS)
if (family == IPADDR_INET6)
if (!SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_TCLASS, dscp << 2))
;
#endif
}
if (!client_only && family == IPADDR_INET4 && local_addr.port > 0)
bound_server_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
@@ -142,9 +169,6 @@ close_socket(int sock_fd)
if (sock_fd == INVALID_SOCK_FD)
return;
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_NotifySocketClosing(sock_fd);
#endif
SCH_RemoveFileHandler(sock_fd);
SCK_CloseSocket(sock_fd);
}
@@ -152,7 +176,7 @@ close_socket(int sock_fd)
/* ================================================== */
void
NIO_Initialise(int family)
NIO_Initialise(void)
{
int server_port, client_port;
@@ -191,25 +215,18 @@ NIO_Initialise(int family)
server_sock_ref4 = 0;
server_sock_ref6 = 0;
if (family == IPADDR_UNSPEC || family == IPADDR_INET4) {
if (permanent_server_sockets && server_port)
server_sock_fd4 = open_socket(IPADDR_INET4, 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 (permanent_server_sockets && server_port) {
server_sock_fd4 = open_socket(IPADDR_INET4, server_port, 0, NULL);
server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL);
}
if (family == IPADDR_UNSPEC || family == IPADDR_INET6) {
if (permanent_server_sockets && server_port)
server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL);
if (!separate_client_sockets) {
if (client_port != server_port || !server_port)
client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL);
else
client_sock_fd6 = server_sock_fd6;
if (!separate_client_sockets) {
if (client_port != server_port || !server_port) {
client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL);
client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL);
} else {
client_sock_fd4 = server_sock_fd4;
client_sock_fd6 = server_sock_fd6;
}
}
@@ -219,6 +236,17 @@ NIO_Initialise(int family)
client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) {
LOG_FATAL("Could not open NTP sockets");
}
ptp_port = CNF_GetPtpPort();
ptp_sock_fd4 = INVALID_SOCK_FD;
ptp_sock_fd6 = INVALID_SOCK_FD;
ptp_message = NULL;
if (ptp_port > 0) {
ptp_sock_fd4 = open_socket(IPADDR_INET4, ptp_port, 0, NULL);
ptp_sock_fd6 = open_socket(IPADDR_INET6, ptp_port, 0, NULL);
ptp_message = MallocNew(PTP_NtpMessage);
}
}
/* ================================================== */
@@ -236,6 +264,11 @@ NIO_Finalise(void)
close_socket(server_sock_fd6);
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
close_socket(ptp_sock_fd4);
close_socket(ptp_sock_fd6);
ptp_sock_fd4 = ptp_sock_fd6 = INVALID_SOCK_FD;
Free(ptp_message);
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Finalise();
#endif
@@ -245,20 +278,36 @@ NIO_Finalise(void)
/* ================================================== */
int
NIO_IsHwTsEnabled(void)
{
#ifdef HAVE_LINUX_TIMESTAMPING
return NIO_Linux_IsHwTsEnabled();
#else
return 0;
#endif
}
/* ================================================== */
int
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
{
if (separate_client_sockets) {
return open_separate_client_socket(remote_addr);
} else {
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
return client_sock_fd4;
case IPADDR_INET6:
return client_sock_fd6;
default:
return INVALID_SOCK_FD;
}
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
if (ptp_port > 0 && remote_addr->port == ptp_port)
return ptp_sock_fd4;
if (separate_client_sockets)
return open_separate_client_socket(remote_addr);
return client_sock_fd4;
case IPADDR_INET6:
if (ptp_port > 0 && remote_addr->port == ptp_port)
return ptp_sock_fd6;
if (separate_client_sockets)
return open_separate_client_socket(remote_addr);
return client_sock_fd6;
default:
return INVALID_SOCK_FD;
}
}
@@ -269,6 +318,8 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
{
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
if (ptp_port > 0 && remote_addr->port == ptp_port)
return ptp_sock_fd4;
if (permanent_server_sockets)
return server_sock_fd4;
if (server_sock_fd4 == INVALID_SOCK_FD)
@@ -277,6 +328,8 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
server_sock_ref4++;
return server_sock_fd4;
case IPADDR_INET6:
if (ptp_port > 0 && remote_addr->port == ptp_port)
return ptp_sock_fd6;
if (permanent_server_sockets)
return server_sock_fd6;
if (server_sock_fd6 == INVALID_SOCK_FD)
@@ -291,9 +344,21 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
/* ================================================== */
static int
is_ptp_socket(int sock_fd)
{
return ptp_port > 0 && sock_fd != INVALID_SOCK_FD &&
(sock_fd == ptp_sock_fd4 || sock_fd == ptp_sock_fd6);
}
/* ================================================== */
void
NIO_CloseClientSocket(int sock_fd)
{
if (is_ptp_socket(sock_fd))
return;
if (separate_client_sockets)
close_socket(sock_fd);
}
@@ -303,7 +368,7 @@ NIO_CloseClientSocket(int sock_fd)
void
NIO_CloseServerSocket(int sock_fd)
{
if (permanent_server_sockets || sock_fd == INVALID_SOCK_FD)
if (permanent_server_sockets || sock_fd == INVALID_SOCK_FD || is_ptp_socket(sock_fd))
return;
if (sock_fd == server_sock_fd4) {
@@ -327,7 +392,7 @@ int
NIO_IsServerSocket(int sock_fd)
{
return sock_fd != INVALID_SOCK_FD &&
(sock_fd == server_sock_fd4 || sock_fd == server_sock_fd6);
(sock_fd == server_sock_fd4 || sock_fd == server_sock_fd6 || is_ptp_socket(sock_fd));
}
/* ================================================== */
@@ -335,7 +400,8 @@ NIO_IsServerSocket(int sock_fd)
int
NIO_IsServerSocketOpen(void)
{
return server_sock_fd4 != INVALID_SOCK_FD || server_sock_fd6 != INVALID_SOCK_FD;
return server_sock_fd4 != INVALID_SOCK_FD || server_sock_fd6 != INVALID_SOCK_FD ||
ptp_sock_fd4 != INVALID_SOCK_FD || ptp_sock_fd6 != INVALID_SOCK_FD;
}
/* ================================================== */
@@ -365,6 +431,9 @@ process_message(SCK_Message *message, int sock_fd, int event)
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
local_ts.source = NTP_TS_DAEMON;
local_ts.rx_duration = 0.0;
local_ts.net_correction = 0.0;
sched_ts = local_ts.ts;
if (message->addr_type != SCK_ADDR_IP) {
@@ -390,6 +459,9 @@ process_message(SCK_Message *message, int sock_fd, int event)
DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u",
UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source);
if (!NIO_UnwrapMessage(message, sock_fd, &local_ts.net_correction))
return;
/* Just ignore the packet if it's not of a recognized length */
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) {
DEBUG_LOG("Unexpected length");
@@ -404,17 +476,9 @@ process_message(SCK_Message *message, int sock_fd, int event)
static void
read_from_socket(int sock_fd, int event, void *anything)
{
/* This should only be called when there is something
to read, otherwise it may block */
SCK_Message messages[SCK_MAX_RECV_MESSAGES];
SCK_Message *messages;
int i, received, flags = 0;
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessEvent(sock_fd, event))
return;
#endif
if (event == SCH_FILE_EXCEPTION) {
#ifdef HAVE_LINUX_TIMESTAMPING
flags |= SCK_FLAG_MSG_ERRQUEUE;
@@ -423,14 +487,99 @@ read_from_socket(int sock_fd, int event, void *anything)
#endif
}
received = SCK_ReceiveMessages(sock_fd, messages, SCK_MAX_RECV_MESSAGES, flags);
if (received <= 0)
messages = SCK_ReceiveMessages(sock_fd, flags, &received);
if (!messages)
return;
for (i = 0; i < received; i++)
process_message(&messages[i], sock_fd, event);
}
/* ================================================== */
int
NIO_UnwrapMessage(SCK_Message *message, int sock_fd, double *net_correction)
{
double ptp_correction;
PTP_NtpMessage *msg;
if (!is_ptp_socket(sock_fd))
return 1;
if (message->length <= PTP_NTP_PREFIX_LENGTH) {
DEBUG_LOG("Unexpected length");
return 0;
}
msg = message->data;
if ((msg->header.type != PTP_TYPE_DELAY_REQ && msg->header.type != PTP_TYPE_SYNC) ||
(msg->header.version != PTP_VERSION_2 &&
(msg->header.version != PTP_VERSION_2_1 || msg->header.min_sdoid != 0)) ||
ntohs(msg->header.length) != message->length ||
msg->header.domain != CNF_GetPtpDomain() ||
ntohs(msg->header.flags) != PTP_FLAG_UNICAST ||
ntohs(msg->tlv_header.type) != PTP_TLV_NTP ||
ntohs(msg->tlv_header.length) != message->length - PTP_NTP_PREFIX_LENGTH) {
DEBUG_LOG("Unexpected PTP message");
return 0;
}
message->data = (char *)message->data + PTP_NTP_PREFIX_LENGTH;
message->length -= PTP_NTP_PREFIX_LENGTH;
ptp_correction = UTI_Integer64NetworkToHost(*(Integer64 *)msg->header.correction) /
((1 << 16) * 1.0e9);
/* Use the correction only if the RX duration is known (i.e. HW timestamp) */
if (*net_correction > 0.0)
*net_correction += ptp_correction;
DEBUG_LOG("Unwrapped PTP->NTP len=%d corr=%.9f", message->length, ptp_correction);
return 1;
}
/* ================================================== */
static int
wrap_message(SCK_Message *message, int sock_fd)
{
static uint16_t sequence_id = 0;
assert(PTP_NTP_PREFIX_LENGTH == 48);
if (!is_ptp_socket(sock_fd))
return 1;
if (!ptp_message)
return 0;
if (message->length < NTP_HEADER_LENGTH ||
message->length + PTP_NTP_PREFIX_LENGTH > sizeof (*ptp_message)) {
DEBUG_LOG("Unexpected length");
return 0;
}
memset(ptp_message, 0, PTP_NTP_PREFIX_LENGTH);
ptp_message->header.type = PTP_TYPE_DELAY_REQ;
ptp_message->header.version = PTP_VERSION_2;
ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
ptp_message->header.domain = CNF_GetPtpDomain();
ptp_message->header.flags = htons(PTP_FLAG_UNICAST);
ptp_message->header.sequence_id = htons(sequence_id++);
ptp_message->tlv_header.type = htons(PTP_TLV_NTP);
ptp_message->tlv_header.length = htons(message->length);
memcpy((char *)ptp_message + PTP_NTP_PREFIX_LENGTH, message->data, message->length);
message->data = ptp_message;
message->length += PTP_NTP_PREFIX_LENGTH;
DEBUG_LOG("Wrapped NTP->PTP len=%d", message->length - PTP_NTP_PREFIX_LENGTH);
return 1;
}
/* ================================================== */
/* Send a packet to remote address from local address */
@@ -452,19 +601,26 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
message.data = packet;
message.length = length;
if (!wrap_message(&message, local_addr->sock_fd))
return 0;
/* Specify remote address if the socket is not connected */
if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
message.remote_addr.ip.ip_addr = remote_addr->ip_addr;
message.remote_addr.ip.port = remote_addr->port;
}
message.if_index = local_addr->if_index;
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)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
if (message.local_addr.ip.family == IPADDR_INET4 &&
(local_addr->sock_fd != server_sock_fd4 || bound_server_sock_fd4))
(bound_server_sock_fd4 || !NIO_IsServerSocket(local_addr->sock_fd)))
message.local_addr.ip.family = IPADDR_UNSPEC;
#endif

View File

@@ -31,13 +31,17 @@
#include "ntp.h"
#include "addressing.h"
#include "socket.h"
/* Function to initialise the module. */
extern void NIO_Initialise(int family);
extern void NIO_Initialise(void);
/* Function to finalise the module */
extern void NIO_Finalise(void);
/* Function to check if HW timestamping is enabled on any interface */
extern int NIO_IsHwTsEnabled(void);
/* Function to obtain a socket for sending client packets */
extern int NIO_OpenClientSocket(NTP_Remote_Address *remote_addr);
@@ -59,6 +63,9 @@ extern int NIO_IsServerSocketOpen(void);
/* Function to check if client packets can be sent to a server */
extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);
/* Function to unwrap an NTP message from non-native transport (e.g. PTP) */
extern int NIO_UnwrapMessage(SCK_Message *message, int sock_fd, double *net_correction);
/* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2019
* Copyright (C) Miroslav Lichvar 2016-2019, 2021-2023
*
* 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
@@ -39,6 +39,7 @@
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "ntp_core.h"
#include "ntp_io.h"
#include "ntp_io_linux.h"
@@ -59,21 +60,22 @@ struct Interface {
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
int l2_udp4_ntp_start;
int l2_udp6_ntp_start;
/* Precision of PHC readings */
double precision;
/* Compensation of errors in TX and RX timestamping */
double tx_comp;
double rx_comp;
HCL_Instance clock;
int maxpoll;
SCH_TimeoutID poll_timeout_id;
};
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 10
#define PHC_READINGS 25
/* Minimum interval between PHC readings */
/* Minimum and maximum interval between PHC readings */
#define MIN_PHC_POLL -6
#define MAX_PHC_POLL 20
/* Maximum acceptable offset between HW and daemon/kernel timestamp */
/* Maximum acceptable offset between SW/HW and daemon timestamp */
#define MAX_TS_DELAY 1.0
/* Array of Interfaces */
@@ -86,19 +88,6 @@ static int ts_tx_flags;
/* Flag indicating the socket options can't be changed in control messages */
static int permanent_ts_options;
/* When sending client requests to a close and fast server, it is possible that
a response will be received before the HW transmit timestamp of the request
itself. To avoid processing of the response without the HW timestamp, we
monitor events returned by select() and suspend reading of packets from the
receive queue for up to 200 microseconds. As the requests are normally
separated by at least 200 milliseconds, it is sufficient to monitor and
suspend one socket at a time. */
static int monitored_socket;
static int suspended_socket;
static SCH_TimeoutID resume_timeout_id;
#define RESUME_TIMEOUT 200.0e-6
/* Unbound socket keeping the kernel RX timestamping permanently enabled
in order to avoid a race condition between receiving a server response
and the kernel actually starting to timestamp received packets after
@@ -109,13 +98,17 @@ static int dummy_rxts_socket;
/* ================================================== */
static void poll_phc(struct Interface *iface, struct timespec *now);
/* ================================================== */
static int
add_interface(CNF_HwTsInterface *conf_iface)
{
int sock_fd, if_index, minpoll, phc_fd, req_hwts_flags, rx_filter;
struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config;
struct ifreq req;
int sock_fd, if_index, phc_fd, req_hwts_flags, rx_filter;
unsigned int i;
struct Interface *iface;
@@ -125,7 +118,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
return 1;
}
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return 0;
@@ -189,6 +182,14 @@ add_interface(CNF_HwTsInterface *conf_iface)
rx_filter = HWTSTAMP_FILTER_NTP_ALL;
break;
#endif
case CNF_HWTS_RXFILTER_PTP:
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT))
rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
else if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_PTP_V2_EVENT))
rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
else
rx_filter = HWTSTAMP_FILTER_NONE;
break;
default:
rx_filter = HWTSTAMP_FILTER_ALL;
break;
@@ -200,7 +201,8 @@ add_interface(CNF_HwTsInterface *conf_iface)
req.ifr_data = (char *)&ts_config;
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
allows only reading of the configuration and it was already configured
@@ -235,12 +237,18 @@ add_interface(CNF_HwTsInterface *conf_iface)
iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62;
iface->precision = conf_iface->precision;
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
minpoll = CLAMP(MIN_PHC_POLL, conf_iface->minpoll, MAX_PHC_POLL);
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
UTI_Log2ToDouble(minpoll), conf_iface->precision);
iface->maxpoll = CLAMP(minpoll, conf_iface->maxpoll, MAX_PHC_POLL);
/* Do not schedule the first poll timeout here! The argument (interface) can
move until all interfaces are added. Wait for the first HW timestamp. */
iface->poll_timeout_id = 0;
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
@@ -285,7 +293,7 @@ update_interface_speed(struct Interface *iface)
struct ifreq req;
int sock_fd, link_speed;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return;
@@ -320,7 +328,7 @@ check_timestamping_option(int option)
{
int sock_fd;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return 0;
@@ -341,7 +349,7 @@ open_dummy_socket(void)
{
int sock_fd, events = 0;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return INVALID_SOCK_FD;
@@ -405,8 +413,6 @@ NIO_Linux_Initialise(void)
/* Kernels before 4.7 ignore timestamping flags set in control messages */
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
monitored_socket = INVALID_SOCK_FD;
suspended_socket = INVALID_SOCK_FD;
dummy_rxts_socket = INVALID_SOCK_FD;
}
@@ -423,6 +429,7 @@ NIO_Linux_Finalise(void)
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
SCH_RemoveTimeout(iface->poll_timeout_id);
HCL_DestroyInstance(iface->clock);
close(iface->phc_fd);
}
@@ -432,6 +439,14 @@ NIO_Linux_Finalise(void)
/* ================================================== */
int
NIO_Linux_IsHwTsEnabled(void)
{
return ARR_GetSize(interfaces) > 0;
}
/* ================================================== */
int
NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
{
@@ -465,73 +480,6 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
/* ================================================== */
static void
resume_socket(int sock_fd)
{
if (monitored_socket == sock_fd)
monitored_socket = INVALID_SOCK_FD;
if (sock_fd == INVALID_SOCK_FD || sock_fd != suspended_socket)
return;
suspended_socket = INVALID_SOCK_FD;
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_INPUT, 1);
DEBUG_LOG("Resumed RX processing %s timeout fd=%d",
resume_timeout_id ? "before" : "on", sock_fd);
if (resume_timeout_id) {
SCH_RemoveTimeout(resume_timeout_id);
resume_timeout_id = 0;
}
}
/* ================================================== */
static void
resume_timeout(void *arg)
{
resume_timeout_id = 0;
resume_socket(suspended_socket);
}
/* ================================================== */
static void
suspend_socket(int sock_fd)
{
resume_socket(suspended_socket);
suspended_socket = sock_fd;
SCH_SetFileHandlerEvent(suspended_socket, SCH_FILE_INPUT, 0);
resume_timeout_id = SCH_AddTimeoutByDelay(RESUME_TIMEOUT, resume_timeout, NULL);
DEBUG_LOG("Suspended RX processing fd=%d", sock_fd);
}
/* ================================================== */
int
NIO_Linux_ProcessEvent(int sock_fd, int event)
{
if (sock_fd != monitored_socket)
return 0;
if (event == SCH_FILE_INPUT) {
suspend_socket(monitored_socket);
monitored_socket = INVALID_SOCK_FD;
/* Don't process the message yet */
return 1;
}
return 0;
}
/* ================================================== */
static struct Interface *
get_interface(int if_index)
{
@@ -551,26 +499,70 @@ get_interface(int if_index)
/* ================================================== */
static void
poll_timeout(void *arg)
{
struct Interface *iface = arg;
struct timespec now;
iface->poll_timeout_id = 0;
SCH_GetLastEventTime(&now, NULL, NULL);
poll_phc(iface, &now);
}
/* ================================================== */
static void
poll_phc(struct Interface *iface, struct timespec *now)
{
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts;
struct timespec phc_readings[PHC_READINGS][3];
double phc_err, local_err, interval;
int n_readings;
if (!HCL_NeedsNewSample(iface->clock, now))
return;
DEBUG_LOG("Polling PHC on %s%s",
iface->name, iface->poll_timeout_id != 0 ? " before timeout" : "");
n_readings = SYS_Linux_GetPHCReadings(iface->phc_fd, iface->phc_nocrossts,
&iface->phc_mode, PHC_READINGS, phc_readings);
/* Add timeout for the next poll in case no HW timestamp will be captured
between the minpoll and maxpoll. Separate reading of different PHCs to
avoid long intervals between handling I/O events. */
SCH_RemoveTimeout(iface->poll_timeout_id);
interval = UTI_Log2ToDouble(iface->maxpoll);
iface->poll_timeout_id = SCH_AddTimeoutInClass(interval, interval /
ARR_GetSize(interfaces) / 4, 0.1,
SCH_PhcPollClass, poll_timeout, iface);
if (n_readings <= 0)
return;
if (!HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
&sample_phc_ts, &sample_sys_ts, &phc_err))
return;
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts, phc_err + local_err);
update_interface_speed(iface);
}
/* ================================================== */
static void
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
int l2_length)
{
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
double rx_correction, ts_delay, phc_err, local_err;
double rx_correction = 0.0, ts_delay, local_err;
struct timespec ts;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
&phc_err))
return;
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
phc_err + local_err);
update_interface_speed(iface);
}
poll_phc(iface, &local_ts->ts);
/* We need to transpose RX timestamps as hardware timestamps are normally
preamble timestamps and RX timestamps in NTP are supposed to be trailer
@@ -608,6 +600,32 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
local_ts->ts = ts;
local_ts->err = local_err;
local_ts->source = NTP_TS_HARDWARE;
local_ts->rx_duration = rx_correction;
/* Network correction needs to include the RX duration to avoid
asymmetric correction with asymmetric link speeds */
local_ts->net_correction = rx_correction;
}
/* ================================================== */
static void
process_sw_timestamp(struct timespec *sw_ts, NTP_Local_Timestamp *local_ts)
{
double ts_delay, local_err;
struct timespec ts;
LCL_CookTime(sw_ts, &ts, &local_err);
ts_delay = UTI_DiffTimespecsToDouble(&local_ts->ts, &ts);
if (fabs(ts_delay) > MAX_TS_DELAY) {
DEBUG_LOG("Unacceptable timestamp delay %.9f", ts_delay);
return;
}
local_ts->ts = ts;
local_ts->err = local_err;
local_ts->source = NTP_TS_KERNEL;
}
/* ================================================== */
@@ -709,6 +727,7 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
{
struct Interface *iface;
int is_tx, ts_if_index, l2_length;
double c = 0.0;
is_tx = event == SCH_FILE_EXCEPTION;
iface = NULL;
@@ -726,17 +745,11 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
} else {
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
}
/* If a HW transmit timestamp was received, resume processing
of non-error messages on this socket */
if (is_tx)
resume_socket(local_addr->sock_fd);
}
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) &&
(!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) {
LCL_CookTime(&message->timestamp.kernel, &local_ts->ts, &local_ts->err);
local_ts->source = NTP_TS_KERNEL;
process_sw_timestamp(&message->timestamp.kernel, local_ts);
}
/* If the kernel is slow with enabling RX timestamping, open a dummy
@@ -757,7 +770,7 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
l2_length = 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),
local_addr->sock_fd, message->length);
@@ -775,7 +788,10 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
return 1;
}
if (message->length < NTP_HEADER_LENGTH)
if (!NIO_UnwrapMessage(message, local_addr->sock_fd, &c))
return 1;
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet))
return 1;
NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length);
@@ -791,23 +807,9 @@ NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd)
if (!ts_flags)
return;
/* If a HW transmit timestamp is requested on a client socket, monitor
events on the socket in order to avoid processing of a fast response
without the HW timestamp of the request */
if (ts_tx_flags & SOF_TIMESTAMPING_TX_HARDWARE && !NIO_IsServerSocket(sock_fd))
monitored_socket = sock_fd;
/* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return;
message->timestamp.tx_flags = ts_tx_flags;
}
/* ================================================== */
void
NIO_Linux_NotifySocketClosing(int sock_fd)
{
resume_socket(sock_fd);
}

View File

@@ -33,15 +33,13 @@ extern void NIO_Linux_Initialise(void);
extern void NIO_Linux_Finalise(void);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
extern int NIO_Linux_IsHwTsEnabled(void);
extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, int event);
extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
extern void NIO_Linux_NotifySocketClosing(int sock_fd);
#endif

View File

@@ -96,17 +96,12 @@ static unsigned int queue_tail;
/* Unix domain socket connected to ntp_signd */
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 */
static int enabled;
/* Flag limiting logging of connection error messages */
static int logged_connection_error;
/* ================================================== */
static void read_write_socket(int sock_fd, int event, void *anything);
@@ -142,6 +137,14 @@ open_socket(void)
sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
if (sock_fd < 0) {
sock_fd = INVALID_SOCK_FD;
/* Log an error only once before a successful exchange to avoid
flooding the system log */
if (!logged_connection_error) {
LOG(LOGS_ERR, "Could not connect to signd socket %s : %s", path, strerror(errno));
logged_connection_error = 1;
}
return 0;
}
@@ -168,6 +171,8 @@ process_response(SignInstance *inst)
return;
}
logged_connection_error = 0;
/* Check if the file descriptor is still valid */
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
DEBUG_LOG("Invalid NTP socket");
@@ -183,10 +188,6 @@ process_response(SignInstance *inst)
NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
ntohl(inst->response.length) + sizeof (inst->response.length) -
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 +275,6 @@ void
NSD_Initialise()
{
sock_fd = INVALID_SOCK_FD;
auth_delay = MIN_AUTH_DELAY;
enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0];
if (!enabled)
@@ -301,13 +301,6 @@ NSD_Finalise()
/* ================================================== */
extern int NSD_GetAuthDelay(uint32_t key_id)
{
return 1.0e9 * auth_delay;
}
/* ================================================== */
int
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)

View File

@@ -35,9 +35,6 @@ extern void NSD_Initialise(void);
/* Finalisation function */
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 */
extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr);

File diff suppressed because it is too large Load Diff

View File

@@ -50,14 +50,20 @@ typedef enum {
} NSR_Status;
/* 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
name instead of address. The name is resolved in exponentially increasing
intervals until it succeeds or fails with a non-temporary error. If the
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,
SourceParameters *params);
intervals until it succeeds or fails with a non-temporary error. The
specified family filters resolved addresses. If the name is an address
and its family does not conflict with the specified family, it is equivalent
to NSR_AddSource(). */
extern NSR_Status NSR_AddSourceByName(char *name, int family, int port, int pool,
NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id);
extern const char *NSR_StatusToString(NSR_Status status);
/* Function type for handlers to be called back when an attempt
* (possibly unsuccessful) to resolve unresolved sources ends */
@@ -76,7 +82,10 @@ extern void NSR_StartSources(void);
extern void NSR_AutoStartSources(void);
/* 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 */
extern void NSR_RemoveAllSources(void);
@@ -87,15 +96,16 @@ extern void NSR_HandleBadSource(IPAddr *address);
/* Procedure to resolve all names again */
extern void NSR_RefreshAddresses(void);
/* Procedure to update the address of a source */
/* Procedure to update the address of a source. The update may be
postponed. */
extern NSR_Status NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr,
NTP_Remote_Address *new_addr);
/* Procedure to get local reference ID corresponding to a source */
extern uint32_t NSR_GetLocalRefid(IPAddr *address);
/* Procedure to get the name of a source. If the source doesn't have a name,
it returns a temporary string containing formatted address. */
/* Procedure to get the name of a source as it was specified (it may be
an IP address) */
extern char *NSR_GetName(IPAddr *address);
/* This routine is called by ntp_io when a new packet arrives off the network */
@@ -130,14 +140,20 @@ extern int NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_rati
extern int NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum);
extern int NSR_ModifyOffset(IPAddr *address, double new_offset);
extern int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target);
extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address);
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 void NSR_GetActivityReport(RPT_ActivityReport *report);
extern void NSR_DumpAuthData(void);
#endif /* GOT_NTP_SOURCES_H */

View File

@@ -29,6 +29,8 @@
#include "siv.h"
#define NKE_PORT 4460
#define NKE_RECORD_CRITICAL_BIT (1U << 15)
#define NKE_RECORD_END_OF_MESSAGE 0
#define NKE_RECORD_NEXT_PROTOCOL 1
@@ -46,7 +48,7 @@
#define NKE_ERROR_INTERNAL_SERVER_ERROR 2
#define NKE_ALPN_NAME "ntske/1"
#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security/1"
#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security"
#define NKE_EXPORTER_CONTEXT_C2S "\x0\x0\x0\xf\x0"
#define NKE_EXPORTER_CONTEXT_S2C "\x0\x0\x0\xf\x1"
@@ -56,11 +58,21 @@
#define NKE_MAX_COOKIES 8
#define NKE_MAX_KEY_LENGTH SIV_MAX_KEY_LENGTH
#define NKE_RETRY_FACTOR2_CONNECT 4
#define NKE_RETRY_FACTOR2_TLS 10
#define NKE_MAX_RETRY_INTERVAL2 19
typedef struct {
int length;
unsigned char key[NKE_MAX_KEY_LENGTH];
} NKE_Key;
typedef struct {
SIV_Algorithm algorithm;
NKE_Key c2s;
NKE_Key s2c;
} NKE_Context;
typedef struct {
int length;
unsigned char cookie[NKE_MAX_COOKIE_LENGTH];

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
* Copyright (C) Miroslav Lichvar 2020-2021
*
* 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
@@ -44,22 +44,23 @@
struct NKC_Instance_Record {
char *name;
IPSockAddr address;
NKSN_Credentials credentials;
NKSN_Instance session;
int destroying;
int got_response;
int resolving_name;
SIV_Algorithm siv_algorithm;
NKE_Key c2s, s2c;
NKE_Context context;
NKE_Cookie cookies[NKE_MAX_COOKIES];
int num_cookies;
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 1];
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
IPSockAddr ntp_address;
};
/* ================================================== */
static void *client_credentials;
static NKSN_Credentials default_credentials = NULL;
static int default_credentials_refs = 0;
/* ================================================== */
@@ -72,7 +73,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg
inst->resolving_name = 0;
if (inst->destroying) {
NKC_DestroyInstance(inst);
Free(inst);
return;
}
@@ -85,7 +86,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg
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++) {
DEBUG_LOG("%s resolved to %s", inst->server_name, UTI_IPToString(&ip_addrs[i]));
if (ip_addrs[i].family == inst->address.ip_addr.family) {
@@ -101,16 +102,22 @@ static int
prepare_request(NKC_Instance inst)
{
NKSN_Instance session = inst->session;
uint16_t datum;
uint16_t data[2];
int length;
NKSN_BeginMessage(session);
datum = htons(NKE_NEXT_PROTOCOL_NTPV4);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
return 0;
datum = htons(AEAD_AES_SIV_CMAC_256);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
length = 0;
if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0)
data[length++] = htons(AEAD_AES_128_GCM_SIV);
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0)
data[length++] = htons(AEAD_AES_SIV_CMAC_256);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
length * sizeof (data[0])))
return 0;
if (!NKSN_EndMessage(session))
@@ -126,9 +133,10 @@ process_response(NKC_Instance inst)
{
int next_protocol = -1, aead_algorithm = -1, error = 0;
int i, critical, type, length;
uint16_t data[NKE_MAX_COOKIE_LENGTH / sizeof (uint16_t)];
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
assert(NKE_MAX_COOKIE_LENGTH % sizeof (uint16_t) == 0);
assert(NKE_MAX_COOKIE_LENGTH <= NKE_MAX_RECORD_BODY_LENGTH);
assert(sizeof (data) % sizeof (uint16_t) == 0);
assert(sizeof (uint16_t) == 2);
inst->num_cookies = 0;
@@ -140,6 +148,13 @@ process_response(NKC_Instance inst)
if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data)))
break;
if (length > sizeof (data)) {
DEBUG_LOG("Record too long type=%d length=%d critical=%d", type, length, critical);
if (critical)
error = 1;
continue;
}
switch (type) {
case NKE_RECORD_NEXT_PROTOCOL:
if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) {
@@ -150,13 +165,15 @@ process_response(NKC_Instance inst)
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
break;
case NKE_RECORD_AEAD_ALGORITHM:
if (length != 2 || ntohs(data[0]) != AEAD_AES_SIV_CMAC_256) {
if (length != 2 || (ntohs(data[0]) != AEAD_AES_SIV_CMAC_256 &&
ntohs(data[0]) != AEAD_AES_128_GCM_SIV) ||
SIV_GetKeyLength(ntohs(data[0])) <= 0) {
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
error = 1;
break;
}
aead_algorithm = AEAD_AES_SIV_CMAC_256;
inst->siv_algorithm = aead_algorithm;
aead_algorithm = ntohs(data[0]);
inst->context.algorithm = aead_algorithm;
break;
case NKE_RECORD_ERROR:
if (length == 2)
@@ -169,13 +186,21 @@ process_response(NKC_Instance inst)
error = 1;
break;
case NKE_RECORD_COOKIE:
DEBUG_LOG("Got cookie #%d length=%d", inst->num_cookies + 1, 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) {
inst->cookies[inst->num_cookies].length = length;
memcpy(inst->cookies[inst->num_cookies].cookie, data, length);
inst->num_cookies++;
DEBUG_LOG("Got cookie length=%d", length);
if (length < 1 || length > NKE_MAX_COOKIE_LENGTH || length % 4 != 0 ||
inst->num_cookies >= NKE_MAX_COOKIES) {
DEBUG_LOG("Unexpected length/cookie");
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;
case NKE_RECORD_NTPV4_SERVER_NEGOTIATION:
if (length < 1 || length >= sizeof (inst->server_name)) {
@@ -188,7 +213,7 @@ process_response(NKC_Instance inst)
inst->server_name[length] = '\0';
/* Make sure the name is printable and has no spaces */
for (i = 0; i < length && isgraph(inst->server_name[i]); i++)
for (i = 0; i < length && isgraph((unsigned char)inst->server_name[i]); i++)
;
if (i != length) {
DEBUG_LOG("Invalid server name");
@@ -219,7 +244,7 @@ process_response(NKC_Instance inst)
if (error || inst->num_cookies == 0 ||
next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
aead_algorithm != AEAD_AES_SIV_CMAC_256)
aead_algorithm < 0)
return 0;
return 1;
@@ -237,13 +262,25 @@ handle_message(void *arg)
return 0;
}
if (!NKSN_GetKeys(inst->session, inst->siv_algorithm, &inst->c2s, &inst->s2c))
if (!NKSN_GetKeys(inst->session, inst->context.algorithm,
&inst->context.c2s, &inst->context.s2c))
return 0;
if (inst->server_name[0] != '\0') {
if (inst->resolving_name)
return 0;
if (!UTI_StringToIP(inst->server_name, &inst->ntp_address.ip_addr)) {
int length = strlen(inst->server_name);
/* Add a trailing dot if not present to force the name to be
resolved as a fully qualified domain name */
if (length < 1 || length + 1 >= sizeof (inst->server_name))
return 0;
if (inst->server_name[length - 1] != '.') {
inst->server_name[length] = '.';
inst->server_name[length + 1] = '\0';
}
DNS_Name2IPAddressAsync(inst->server_name, name_resolve_handler, inst);
inst->resolving_name = 1;
}
@@ -256,27 +293,13 @@ 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_CreateInstance(IPSockAddr *address, const char *name)
NKC_CreateInstance(IPSockAddr *address, const char *name, uint32_t cert_set)
{
const char **trusted_certs;
uint32_t *certs_ids;
NKC_Instance inst;
int n_certs;
inst = MallocNew(struct NKC_Instance_Record);
@@ -287,10 +310,21 @@ NKC_CreateInstance(IPSockAddr *address, const char *name)
inst->destroying = 0;
inst->got_response = 0;
/* Create the credentials with the first client instance and share them
with other instances */
if (!client_credentials)
client_credentials = NKSN_CreateCertCredentials(NULL, NULL, CNF_GetNtsTrustedCertFile());
n_certs = CNF_GetNtsTrustedCertsPaths(&trusted_certs, &certs_ids);
/* Share the credentials among clients using the default set of trusted
certificates, which likely contains most certificates */
if (cert_set == 0) {
if (!default_credentials)
default_credentials = NKSN_CreateClientCertCredentials(trusted_certs, certs_ids,
n_certs, cert_set);
inst->credentials = default_credentials;
if (default_credentials)
default_credentials_refs++;
} else {
inst->credentials = NKSN_CreateClientCertCredentials(trusted_certs, certs_ids,
n_certs, cert_set);
}
return inst;
}
@@ -300,15 +334,29 @@ NKC_CreateInstance(IPSockAddr *address, const char *name)
void
NKC_DestroyInstance(NKC_Instance inst)
{
/* If the resolver is running, destroy the instance later when finished */
NKSN_DestroyInstance(inst->session);
Free(inst->name);
if (inst->credentials) {
if (inst->credentials == default_credentials) {
default_credentials_refs--;
if (default_credentials_refs <= 0) {
NKSN_DestroyCertCredentials(default_credentials);
default_credentials = NULL;
}
} else {
NKSN_DestroyCertCredentials(inst->credentials);
}
}
/* If the asynchronous resolver is running, let the handler free
the instance later */
if (inst->resolving_name) {
inst->destroying = 1;
return;
}
NKSN_DestroyInstance(inst->session);
Free(inst->name);
Free(inst);
}
@@ -318,28 +366,43 @@ int
NKC_Start(NKC_Instance inst)
{
IPSockAddr local_addr;
char label[512], *iface;
int sock_fd;
assert(!NKC_IsActive(inst));
if (!client_credentials) {
inst->got_response = 0;
if (!inst->credentials) {
DEBUG_LOG("Missing client credentials");
return 0;
}
/* Follow the bindacqaddress setting */
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;
sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, 0);
if (sock_fd < 0)
/* Don't try to connect if missing the algorithm which all servers
are required to support */
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) <= 0) {
LOG(LOGS_ERR, "Missing AES-SIV-CMAC-256");
return 0;
}
/* Start a NTS-KE session */
if (!NKSN_StartSession(inst->session, sock_fd, client_credentials, CLIENT_TIMEOUT)) {
/* Follow the bindacqaddress and bindacqdevice settings */
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
local_addr.port = 0;
iface = CNF_GetBindAcquisitionInterface();
/* Make a label containing both the address and name of the server */
if (snprintf(label, sizeof (label), "%s (%s)",
UTI_IPSockAddrToString(&inst->address), inst->name) >= sizeof (label))
;
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, inst->credentials, CLIENT_TIMEOUT)) {
SCK_CloseSocket(sock_fd);
return 0;
}
@@ -365,8 +428,7 @@ NKC_IsActive(NKC_Instance inst)
/* ================================================== */
int
NKC_GetNtsData(NKC_Instance inst,
SIV_Algorithm *siv_algorithm, NKE_Key *c2s, NKE_Key *s2c,
NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address)
{
@@ -375,9 +437,7 @@ NKC_GetNtsData(NKC_Instance inst,
if (!inst->got_response || inst->resolving_name)
return 0;
*siv_algorithm = inst->siv_algorithm;
*c2s = inst->c2s;
*s2c = inst->s2c;
*context = inst->context;
for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
cookies[i] = inst->cookies[i];
@@ -385,5 +445,13 @@ NKC_GetNtsData(NKC_Instance inst,
*ntp_address = inst->ntp_address;
return i;
return 1;
}
/* ================================================== */
int
NKC_GetRetryFactor(NKC_Instance inst)
{
return NKSN_GetRetryFactor(inst->session);
}

View File

@@ -32,12 +32,8 @@
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 */
extern NKC_Instance NKC_CreateInstance(IPSockAddr *address, const char *name);
extern NKC_Instance NKC_CreateInstance(IPSockAddr *address, const char *name, uint32_t cert_set);
/* Destroy an instance */
extern void NKC_DestroyInstance(NKC_Instance inst);
@@ -50,9 +46,11 @@ extern int NKC_Start(NKC_Instance inst);
extern int NKC_IsActive(NKC_Instance inst);
/* Get the NTS data if the session was successful */
extern int NKC_GetNtsData(NKC_Instance inst,
SIV_Algorithm *siv_algorithm, NKE_Key *c2s, NKE_Key *s2c,
extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address);
/* Get a factor to calculate retry interval (in log2 seconds) */
extern int NKC_GetRetryFactor(NKC_Instance inst);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -30,13 +30,20 @@
#include "nts_ke.h"
/* 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);
/* Generate a new NTS cookie containing the C2S and S2C keys */
extern int NKS_GenerateCookie(NKE_Key *c2s, NKE_Key *s2c, NKE_Cookie *cookie);
/* Save the current server keys */
extern void NKS_DumpKeys(void);
/* Validate a cookie and extract the C2S and S2C keys */
extern int NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Key *c2s, NKE_Key *s2c);
/* Reload the keys */
extern void NKS_ReloadKeys(void);
/* Generate an NTS cookie with a given context */
extern int NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie);
/* Validate a cookie and decode the context */
extern int NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context);
#endif

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
* Copyright (C) Miroslav Lichvar 2020-2021
*
* 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
@@ -31,6 +31,7 @@
#include "nts_ke_session.h"
#include "conf.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "siv.h"
@@ -39,6 +40,7 @@
#include "util.h"
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#define INVALID_SOCK_FD (-8)
@@ -66,18 +68,19 @@ typedef enum {
struct NKSN_Instance_Record {
int server;
char *name;
char *server_name;
NKSN_MessageHandler handler;
void *handler_arg;
KeState state;
int sock_fd;
char *label;
gnutls_session_t tls_session;
SCH_TimeoutID timeout_id;
int retry_factor;
struct Message message;
int new_message;
int ended_message;
};
/* ================================================== */
@@ -86,6 +89,8 @@ static gnutls_priority_t priority_cache;
static int credentials_counter = 0;
static int clock_updates = 0;
/* ================================================== */
static void
@@ -104,6 +109,8 @@ add_record(struct Message *message, int critical, int type, const void *body, in
{
struct RecordHeader header;
assert(message->length <= sizeof (message->data));
if (body_length < 0 || body_length > 0xffff || type < 0 || type > 0x7fff ||
message->length + sizeof (header) + body_length > sizeof (message->data))
return 0;
@@ -147,6 +154,7 @@ get_record(struct Message *message, int *critical, int *type, int *body_length,
blen = ntohs(header.body_length);
rlen = sizeof (header) + blen;
assert(blen >= 0 && rlen > 0);
if (message->length < message->parsed + rlen)
return 0;
@@ -206,19 +214,33 @@ create_tls_session(int server_mode, int sock_fd, const char *server_name,
unsigned char alpn_name[sizeof (NKE_ALPN_NAME)];
gnutls_session_t session;
gnutls_datum_t alpn;
unsigned int flags;
int r;
r = gnutls_init(&session, GNUTLS_NONBLOCK | (server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
r = gnutls_init(&session, GNUTLS_NONBLOCK | GNUTLS_NO_TICKETS |
(server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
if (r < 0) {
LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r));
return NULL;
}
if (!server_mode) {
r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name));
if (r < 0)
goto error;
gnutls_session_set_verify_cert(session, server_name, 0);
assert(server_name);
if (!UTI_IsStringIP(server_name)) {
r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name));
if (r < 0)
goto error;
}
flags = 0;
if (clock_updates < CNF_GetNoCertTimeCheck()) {
flags |= GNUTLS_VERIFY_DISABLE_TIME_CHECKS | GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS;
DEBUG_LOG("Disabled time checks");
}
gnutls_session_set_verify_cert(session, server_name, flags);
}
r = gnutls_priority_set(session, priority);
@@ -261,6 +283,9 @@ stop_session(NKSN_Instance inst)
SCK_CloseSocket(inst->sock_fd);
inst->sock_fd = INVALID_SOCK_FD;
Free(inst->label);
inst->label = NULL;
gnutls_deinit(inst->tls_session);
inst->tls_session = NULL;
@@ -275,7 +300,7 @@ session_timeout(void *arg)
{
NKSN_Instance inst = arg;
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE session with %s timed out", inst->name);
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE session with %s timed out", inst->label);
inst->timeout_id = 0;
stop_session(inst);
@@ -284,33 +309,25 @@ session_timeout(void *arg)
/* ================================================== */
static int
get_socket_error(int sock_fd)
check_alpn(NKSN_Instance inst)
{
int optval;
socklen_t optlen = sizeof (optval);
gnutls_datum_t alpn;
if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
DEBUG_LOG("getsockopt() failed : %s", strerror(errno));
return EINVAL;
}
if (gnutls_alpn_get_selected_protocol(inst->tls_session, &alpn) < 0 ||
alpn.size != sizeof (NKE_ALPN_NAME) - 1 ||
memcmp(alpn.data, NKE_ALPN_NAME, sizeof (NKE_ALPN_NAME) - 1) != 0)
return 0;
return optval;
return 1;
}
/* ================================================== */
static int
check_alpn(NKSN_Instance inst)
static void
set_input_output(NKSN_Instance inst, int output)
{
gnutls_datum_t alpn;
int r;
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;
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_INPUT, !output);
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, output);
}
/* ================================================== */
@@ -336,7 +353,7 @@ change_state(NKSN_Instance inst, KeState state)
assert(0);
}
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, output);
set_input_output(inst, output);
inst->state = state;
}
@@ -357,15 +374,17 @@ handle_event(NKSN_Instance inst, int event)
if (event != SCH_FILE_OUTPUT)
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) {
LOG(LOGS_ERR, "Could not connect to %s : %s", inst->name, strerror(r));
if (r != 0) {
LOG(LOGS_ERR, "Could not connect to %s : %s", inst->label, strerror(r));
stop_session(inst);
return 0;
}
DEBUG_LOG("Connected to %s", inst->name);
DEBUG_LOG("Connected to %s", inst->label);
change_state(inst, KE_HANDSHAKE);
return 0;
@@ -375,27 +394,48 @@ handle_event(NKSN_Instance inst, int event)
if (r < 0) {
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,
"TLS handshake with %s failed : %s", inst->name, 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);
/* Increase the retry interval if the handshake did not fail due
to the other end closing the connection */
if (r != GNUTLS_E_PULL_ERROR && r != GNUTLS_E_PREMATURE_TERMINATION)
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
return 0;
}
/* Disable output when the handshake is trying to receive data */
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT,
gnutls_record_get_direction(inst->tls_session));
set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
return 0;
}
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
if (DEBUG) {
char *description = gnutls_session_get_desc(inst->tls_session);
DEBUG_LOG("Handshake with %s completed %s",
inst->name, description ? description : "");
inst->label, description ? description : "");
gnutls_free(description);
}
if (!check_alpn(inst)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE not supported by %s", inst->name);
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE not supported by %s", inst->label);
stop_session(inst);
return 0;
}
@@ -406,6 +446,7 @@ handle_event(NKSN_Instance inst, int event)
case KE_SEND:
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],
message->length - message->sent);
@@ -413,13 +454,13 @@ handle_event(NKSN_Instance inst, int event)
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"Could not send NTS-KE message to %s : %s", inst->name, gnutls_strerror(r));
"Could not send NTS-KE message to %s : %s", inst->label, gnutls_strerror(r));
stop_session(inst);
}
return 0;
}
DEBUG_LOG("Sent %d bytes to %s", r, inst->name);
DEBUG_LOG("Sent %d bytes to %s", r, inst->label);
message->sent += r;
if (message->sent < message->length)
@@ -448,13 +489,13 @@ handle_event(NKSN_Instance inst, int event)
if (gnutls_error_is_fatal(r) || r == GNUTLS_E_REHANDSHAKE) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"Could not receive NTS-KE message from %s : %s",
inst->name, gnutls_strerror(r));
inst->label, gnutls_strerror(r));
stop_session(inst);
}
return 0;
}
DEBUG_LOG("Received %d bytes from %s", r, inst->name);
DEBUG_LOG("Received %d bytes from %s", r, inst->label);
message->length += r;
@@ -462,7 +503,7 @@ handle_event(NKSN_Instance inst, int event)
if (!check_message_format(message, r == 0)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"Received invalid NTS-KE message from %s", inst->name);
"Received invalid NTS-KE message from %s", inst->label);
stop_session(inst);
return 0;
}
@@ -473,21 +514,22 @@ handle_event(NKSN_Instance inst, int event)
/* Server will send a response to the client */
change_state(inst, inst->server ? KE_SEND : KE_SHUTDOWN);
break;
/* Return success to process the received message */
return 1;
case KE_SHUTDOWN:
r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR);
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
DEBUG_LOG("Shutdown with %s failed : %s", inst->name, gnutls_strerror(r));
DEBUG_LOG("Shutdown with %s failed : %s", inst->label, gnutls_strerror(r));
stop_session(inst);
return 0;
}
/* Disable output when the TLS shutdown is trying to receive data */
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT,
gnutls_record_get_direction(inst->tls_session));
set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
return 0;
}
@@ -499,9 +541,8 @@ handle_event(NKSN_Instance inst, int event)
default:
assert(0);
return 0;
}
return 1;
}
/* ================================================== */
@@ -514,6 +555,9 @@ read_write_socket(int fd, int event, void *arg)
if (!handle_event(inst, event))
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);
if (!(inst->handler)(inst->handler_arg)) {
@@ -524,27 +568,65 @@ read_write_socket(int fd, int event, void *arg)
/* ================================================== */
static int gnutls_initialised = 0;
static time_t
get_time(time_t *t)
{
struct timespec now;
LCL_ReadCookedTime(&now, NULL);
if (t)
*t = now.tv_sec;
return now.tv_sec;
}
/* ================================================== */
static void
handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
if (change_type != LCL_ChangeUnknownStep && clock_updates < INT_MAX)
clock_updates++;
}
/* ================================================== */
static int gnutls_initialised = 0;
static int
init_gnutls(void)
{
int r;
if (gnutls_initialised)
return;
return 1;
r = gnutls_global_init();
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
/* NTS specification requires TLS1.2 or later */
r = gnutls_priority_init2(&priority_cache, "-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1",
/* 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,
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r));
if (r < 0) {
LOG(LOGS_ERR, "Could not initialise %s : %s",
"priority cache for TLS", gnutls_strerror(r));
gnutls_global_deinit();
return 0;
}
/* Use our clock instead of the system clock in certificate verification */
gnutls_global_set_time_function(get_time);
gnutls_initialised = 1;
DEBUG_LOG("Initialised");
LCL_AddParameterChangeHandler(handle_step, NULL);
return 1;
}
/* ================================================== */
@@ -552,75 +634,120 @@ init_gnutls(void)
static void
deinit_gnutls(void)
{
assert(gnutls_initialised);
if (!gnutls_initialised || credentials_counter > 0)
return;
LCL_RemoveParameterChangeHandler(handle_step, NULL);
gnutls_priority_deinit(priority_cache);
gnutls_global_deinit();
gnutls_initialised = 0;
DEBUG_LOG("Deinitialised");
}
/* ================================================== */
void *
NKSN_CreateCertCredentials(char *cert, char *key, char *trusted_certs)
static NKSN_Credentials
create_credentials(const char **certs, const char **keys, int n_certs_keys,
const char **trusted_certs, uint32_t *trusted_certs_ids,
int n_trusted_certs, uint32_t trusted_cert_set)
{
gnutls_certificate_credentials_t credentials = NULL;
int r;
int i, r;
init_gnutls();
if (!init_gnutls())
return NULL;
r = gnutls_certificate_allocate_credentials(&credentials);
if (r < 0)
goto error;
if (cert && key) {
r = gnutls_certificate_set_x509_key_file(credentials, cert, key,
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
if (certs && keys) {
if (trusted_certs || trusted_certs_ids)
assert(0);
for (i = 0; i < n_certs_keys; i++) {
if (!UTI_CheckFilePermissions(keys[i], 0771))
;
r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
}
} else {
if (!CNF_GetNoSystemCert()) {
if (certs || keys || n_certs_keys > 0)
assert(0);
if (trusted_cert_set == 0 && !CNF_GetNoSystemCert()) {
r = gnutls_certificate_set_x509_system_trust(credentials);
if (r < 0)
goto error;
}
if (trusted_certs) {
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs,
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
if (trusted_certs && trusted_certs_ids) {
for (i = 0; i < n_trusted_certs; i++) {
struct stat buf;
if (trusted_certs_ids[i] != trusted_cert_set)
continue;
if (stat(trusted_certs[i], &buf) == 0 && S_ISDIR(buf.st_mode))
r = gnutls_certificate_set_x509_trust_dir(credentials, trusted_certs[i],
GNUTLS_X509_FMT_PEM);
else
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
DEBUG_LOG("Added %d trusted certs from %s", r, trusted_certs[i]);
}
}
}
credentials_counter++;
return credentials;
return (NKSN_Credentials)credentials;
error:
LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
if (credentials)
gnutls_certificate_free_credentials(credentials);
deinit_gnutls();
return NULL;
}
/* ================================================== */
void
NKSN_DestroyCertCredentials(void *credentials)
NKSN_Credentials
NKSN_CreateServerCertCredentials(const char **certs, const char **keys, int n_certs_keys)
{
gnutls_certificate_free_credentials(credentials);
credentials_counter--;
if (credentials_counter != 0)
return;
return create_credentials(certs, keys, n_certs_keys, NULL, NULL, 0, 0);
}
/* ================================================== */
NKSN_Credentials
NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids,
int n_certs_ids, uint32_t trusted_cert_set)
{
return create_credentials(NULL, NULL, 0, certs, ids, n_certs_ids, trusted_cert_set);
}
/* ================================================== */
void
NKSN_DestroyCertCredentials(NKSN_Credentials credentials)
{
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)credentials);
credentials_counter--;
deinit_gnutls();
}
/* ================================================== */
NKSN_Instance
NKSN_CreateInstance(int server_mode, const char *name,
NKSN_CreateInstance(int server_mode, const char *server_name,
NKSN_MessageHandler handler, void *handler_arg)
{
NKSN_Instance inst;
@@ -628,17 +755,19 @@ NKSN_CreateInstance(int server_mode, const char *name,
inst = MallocNew(struct NKSN_Instance_Record);
inst->server = server_mode;
inst->name = Strdup(name);
inst->server_name = server_name ? Strdup(server_name) : NULL;
inst->handler = handler;
inst->handler_arg = handler_arg;
/* Replace NULL arg with the session itself */
/* Replace a NULL argument with the session itself */
if (!inst->handler_arg)
inst->handler_arg = inst;
inst->state = KE_STOPPED;
inst->sock_fd = INVALID_SOCK_FD;
inst->label = NULL;
inst->tls_session = NULL;
inst->timeout_id = 0;
inst->retry_factor = NKE_RETRY_FACTOR2_CONNECT;
return inst;
}
@@ -650,31 +779,33 @@ NKSN_DestroyInstance(NKSN_Instance inst)
{
stop_session(inst);
Free(inst->name);
Free(inst->server_name);
Free(inst);
}
/* ================================================== */
int
NKSN_StartSession(NKSN_Instance inst, int sock_fd, void *credentials, double timeout)
NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
NKSN_Credentials credentials, double timeout)
{
assert(inst->state == KE_STOPPED);
inst->tls_session = create_tls_session(inst->server, sock_fd,
inst->server ? NULL : inst->name,
credentials, priority_cache);
inst->tls_session = create_tls_session(inst->server, sock_fd, inst->server_name,
(gnutls_certificate_credentials_t)credentials,
priority_cache);
if (!inst->tls_session)
return 0;
inst->sock_fd = sock_fd;
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, inst);
inst->label = Strdup(label);
inst->timeout_id = SCH_AddTimeoutByDelay(timeout, session_timeout, inst);
inst->retry_factor = NKE_RETRY_FACTOR2_CONNECT;
reset_message(&inst->message);
inst->new_message = 0;
inst->ended_message = 0;
change_state(inst, inst->server ? KE_HANDSHAKE : KE_WAIT_CONNECT);
@@ -708,6 +839,7 @@ NKSN_EndMessage(NKSN_Instance inst)
{
assert(!inst->message.complete);
/* Terminate the message */
if (!add_record(&inst->message, 1, NKE_RECORD_END_OF_MESSAGE, NULL, 0))
return 0;
@@ -726,9 +858,13 @@ NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
assert(inst->message.complete);
if (body_length)
*body_length = 0;
if (!get_record(&inst->message, critical, &type2, body_length, body, buffer_length))
return 0;
/* Hide the end-of-message record */
if (type2 == NKE_RECORD_END_OF_MESSAGE)
return 0;
@@ -743,21 +879,27 @@ NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
int
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
{
c2s->length = SIV_GetKeyLength(siv);
s2c->length = SIV_GetKeyLength(siv);
assert(c2s->length <= sizeof (c2s->key));
assert(s2c->length <= sizeof (s2c->key));
int length = SIV_GetKeyLength(siv);
if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) {
DEBUG_LOG("Invalid algorithm");
return 0;
}
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S,
c2s->length, (char *)c2s->key) < 0)
return 0;
if (gnutls_prf_rfc5705(inst->tls_session,
length, (char *)c2s->key) < 0 ||
gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C,
s2c->length, (char *)s2c->key) < 0)
length, (char *)s2c->key) < 0) {
DEBUG_LOG("Could not export key");
return 0;
}
c2s->length = length;
s2c->length = length;
return 1;
}
@@ -777,3 +919,11 @@ NKSN_StopSession(NKSN_Instance inst)
{
stop_session(inst);
}
/* ================================================== */
int
NKSN_GetRetryFactor(NKSN_Instance inst)
{
return inst->retry_factor;
}

View File

@@ -30,30 +30,36 @@
#include "nts_ke.h"
#include "siv.h"
typedef struct NKSN_Credentials_Record *NKSN_Credentials;
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. */
typedef int (*NKSN_MessageHandler)(void *arg);
/* Get client or server credentials using certificates of trusted CAs,
or a server certificate and key. The credentials may be shared between
/* Get server or client credentials using a server certificate and key,
or certificates of trusted CAs. The credentials may be shared between
different clients or servers. */
extern void *NKSN_CreateCertCredentials(char *cert, char *key, char *trusted_certs);
extern NKSN_Credentials NKSN_CreateServerCertCredentials(const char **certs, const char **keys,
int n_certs_keys);
extern NKSN_Credentials NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids,
int n_certs_ids,
uint32_t trusted_cert_set);
/* Destroy the credentials */
extern void NKSN_DestroyCertCredentials(void *credentials);
extern void NKSN_DestroyCertCredentials(NKSN_Credentials credentials);
/* Create an instance */
extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *name,
extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *server_name,
NKSN_MessageHandler handler, void *handler_arg);
/* Destroy an instance */
extern void NKSN_DestroyInstance(NKSN_Instance inst);
/* Start a new NTS-KE session */
extern int NKSN_StartSession(NKSN_Instance inst, int sock_fd, void *credentials,
double timeout);
extern int NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
NKSN_Credentials credentials, double timeout);
/* Begin an NTS-KE message. A request should be made right after starting
the session and response should be made in the message handler. */
@@ -80,4 +86,8 @@ extern int NKSN_IsStopped(NKSN_Instance inst);
/* Stop the session */
extern void NKSN_StopSession(NKSN_Instance inst);
/* Get a factor to calculate retry interval (in log2 seconds)
based on the session state or how it was terminated */
extern int NKSN_GetRetryFactor(NKSN_Instance inst);
#endif

View File

@@ -27,11 +27,6 @@
#ifndef GOT_NTS_NTP_H
#define GOT_NTS_NTP_H
#define NTP_EF_NTS_UNIQUE_IDENTIFIER 0x0104
#define NTP_EF_NTS_COOKIE 0x0204
#define NTP_EF_NTS_COOKIE_PLACEHOLDER 0x0304
#define NTP_EF_NTS_AUTH_AND_EEF 0x0404
#define NTP_KOD_NTS_NAK 0x4e54534e
#define NTS_MIN_UNIQ_ID_LENGTH 32

View File

@@ -61,23 +61,25 @@ get_padded_length(int length)
int
NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
const unsigned char *nonce, int nonce_length,
const unsigned char *nonce, int max_nonce_length,
const unsigned char *plaintext, int plaintext_length,
int min_ef_length)
{
int auth_length, ciphertext_length, assoc_length;
int auth_length, ciphertext_length, assoc_length, nonce_length, max_siv_nonce_length;
int nonce_padding, ciphertext_padding, additional_padding;
unsigned char *ciphertext, *body;
struct AuthHeader *header;
assert(sizeof (*header) == 4);
if (nonce_length <= 0 || plaintext_length < 0) {
if (max_nonce_length <= 0 || plaintext_length < 0) {
DEBUG_LOG("Invalid nonce/plaintext length");
return 0;
}
assoc_length = info->length;
max_siv_nonce_length = SIV_GetMaxNonceLength(siv);
nonce_length = MIN(max_nonce_length, max_siv_nonce_length);
ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
nonce_padding = get_padding_length(nonce_length);
ciphertext_padding = get_padding_length(ciphertext_length);
@@ -86,8 +88,8 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
auth_length = sizeof (*header) + nonce_length + nonce_padding +
ciphertext_length + ciphertext_padding;
additional_padding = MAX(min_ef_length - auth_length - 4, 0);
additional_padding = MAX(NTS_MIN_UNPADDED_NONCE_LENGTH - nonce_length - nonce_padding,
additional_padding);
additional_padding = MAX(MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) -
nonce_length - nonce_padding, additional_padding);
auth_length += additional_padding;
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
@@ -102,12 +104,18 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
body = (unsigned char *)(header + 1);
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);
memset(body + nonce_length, 0, nonce_padding);
if (!SIV_Encrypt(siv, nonce, nonce_length, packet, assoc_length,
plaintext, plaintext_length, ciphertext, ciphertext_length)) {
DEBUG_LOG("SIV encrypt failed");
info->length = assoc_length;
info->ext_fields--;
return 0;
}
@@ -122,17 +130,20 @@ int
NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
unsigned char *plaintext, int buffer_length, int *plaintext_length)
{
unsigned int siv_tag_length, nonce_length, ciphertext_length;
int siv_tag_length, max_siv_nonce_length, nonce_length, ciphertext_length;
unsigned char *nonce, *ciphertext;
int ef_type, ef_body_length;
void *ef_body;
struct AuthHeader *header;
if (buffer_length < 0)
return 0;
if (!NEF_ParseField(packet, info->length, ef_start,
NULL, &ef_type, &ef_body, &ef_body_length))
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;
header = ef_body;
@@ -145,8 +156,9 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
return 0;
nonce = (unsigned char *)(header + 1);
ciphertext = (unsigned char *)(header + 1) + get_padded_length(nonce_length);
ciphertext = nonce + get_padded_length(nonce_length);
max_siv_nonce_length = SIV_GetMaxNonceLength(siv);
siv_tag_length = SIV_GetTagLength(siv);
if (nonce_length < 1 ||
@@ -156,15 +168,16 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
return 0;
}
if (ef_body_length < sizeof (*header) +
NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_length)) {
if (sizeof (*header) + MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) +
get_padded_length(ciphertext_length) > ef_body_length) {
DEBUG_LOG("Missing padding");
return 0;
}
*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)) {
DEBUG_LOG("SIV decrypt failed");
return 0;

View File

@@ -32,7 +32,7 @@
#include "siv.h"
extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
const unsigned char *nonce, int nonce_length,
const unsigned char *nonce, int max_nonce_length,
const unsigned char *plaintext, int plaintext_length,
int min_ef_length);

View File

@@ -43,22 +43,40 @@
#include "siv.h"
#include "util.h"
/* Maximum length of all cookies to avoid IP fragmentation */
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
#define MIN_NKE_RETRY_INTERVAL 1000
/* Retry interval for NTS-KE start (which doesn't generate network traffic) */
#define RETRY_INTERVAL_KE_START 2.0
/* Magic string of files containing keys and cookies */
#define DUMP_IDENTIFIER "NNC0\n"
struct NNC_Instance_Record {
const IPSockAddr *ntp_address;
/* Address of NTS-KE server */
IPSockAddr nts_address;
/* Hostname or IP address for certificate verification */
char *name;
SIV_Instance siv_c2s;
SIV_Instance siv_s2c;
NKC_Instance nke;
/* ID of trusted certificates */
uint32_t cert_set;
/* Configured NTP port */
uint16_t default_ntp_port;
/* Address of NTP server (can be negotiated in NTS-KE) */
IPSockAddr ntp_address;
double last_nke_attempt;
NKC_Instance nke;
SIV_Instance siv;
int nke_attempts;
double next_nke_attempt;
double last_nke_success;
NKE_Context context;
unsigned int context_id;
NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies;
int cookie_index;
int auth_ready;
int nak_response;
int ok_response;
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
@@ -67,13 +85,31 @@ struct NNC_Instance_Record {
/* ================================================== */
static void save_cookies(NNC_Instance inst);
static void load_cookies(NNC_Instance inst);
/* ================================================== */
static void
reset_instance(NNC_Instance inst)
{
inst->last_nke_attempt = -MIN_NKE_RETRY_INTERVAL;
if (inst->nke)
NKC_DestroyInstance(inst->nke);
inst->nke = NULL;
if (inst->siv)
SIV_DestroyInstance(inst->siv);
inst->siv = NULL;
inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0;
inst->last_nke_success = 0.0;
memset(&inst->context, 0, sizeof (inst->context));
inst->context_id = 0;
memset(inst->cookies, 0, sizeof (inst->cookies));
inst->num_cookies = 0;
inst->cookie_index = 0;
inst->auth_ready = 0;
inst->nak_response = 0;
inst->ok_response = 1;
memset(inst->nonce, 0, sizeof (inst->nonce));
@@ -83,21 +119,26 @@ reset_instance(NNC_Instance inst)
/* ================================================== */
NNC_Instance
NNC_CreateInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *ntp_address)
NNC_CreateInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set, uint16_t ntp_port)
{
NNC_Instance inst;
inst = MallocNew(struct NNC_Instance_Record);
inst->ntp_address = ntp_address;
inst->nts_address = *nts_address;
inst->name = name ? Strdup(name) : NULL;
inst->siv_c2s = NULL;
inst->siv_s2c = NULL;
inst->name = Strdup(name);
inst->cert_set = cert_set;
inst->default_ntp_port = ntp_port;
inst->ntp_address.ip_addr = nts_address->ip_addr;
inst->ntp_address.port = ntp_port;
inst->siv = NULL;
inst->nke = NULL;
reset_instance(inst);
/* Try to reload saved keys and cookies */
load_cookies(inst);
return inst;
}
@@ -106,12 +147,9 @@ NNC_CreateInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *
void
NNC_DestroyInstance(NNC_Instance inst)
{
if (inst->nke)
NKC_DestroyInstance(inst->nke);
if (inst->siv_c2s)
SIV_DestroyInstance(inst->siv_c2s);
if (inst->siv_s2c)
SIV_DestroyInstance(inst->siv_s2c);
save_cookies(inst);
reset_instance(inst);
Free(inst->name);
Free(inst);
@@ -120,20 +158,18 @@ NNC_DestroyInstance(NNC_Instance inst)
/* ================================================== */
static int
is_nke_needed(NNC_Instance inst)
check_cookies(NNC_Instance inst)
{
/* Force NKE if a NAK was received since last valid auth */
if (inst->nak_response && !inst->ok_response && inst->num_cookies > 0) {
/* Force a new NTS-KE session if a NAK was received without a valid response,
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;
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;
}
/* ================================================== */
@@ -143,13 +179,13 @@ set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
{
NTP_Remote_Address old_address, new_address;
old_address = *inst->ntp_address;
old_address = inst->ntp_address;
new_address = *negotiated_address;
if (new_address.ip_addr.family == IPADDR_UNSPEC)
new_address.ip_addr = old_address.ip_addr;
new_address.ip_addr = inst->nts_address.ip_addr;
if (new_address.port == 0)
new_address.port = old_address.port;
new_address.port = inst->default_ntp_port;
if (UTI_CompareIPs(&old_address.ip_addr, &new_address.ip_addr, NULL) == 0 &&
old_address.port == new_address.port)
@@ -162,83 +198,94 @@ set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
return 0;
}
inst->ntp_address = new_address;
return 1;
}
/* ================================================== */
static void
update_next_nke_attempt(NNC_Instance inst, int failed_start, double now)
{
int factor, interval;
if (failed_start) {
inst->next_nke_attempt = now + RETRY_INTERVAL_KE_START;
return;
}
if (!inst->nke)
return;
factor = NKC_GetRetryFactor(inst->nke);
interval = MIN(factor + inst->nke_attempts - 1, NKE_MAX_RETRY_INTERVAL2);
inst->next_nke_attempt = now + UTI_Log2ToDouble(interval);
}
/* ================================================== */
static int
get_nke_data(NNC_Instance inst)
get_cookies(NNC_Instance inst)
{
NTP_Remote_Address ntp_address;
SIV_Algorithm siv;
NKE_Key c2s, s2c;
int got_data, failed_start = 0;
double now;
int got_data;
assert(is_nke_needed(inst));
assert(inst->num_cookies == 0);
now = SCH_GetLastEventMonoTime();
/* Create and start a new NTS-KE session if not already present */
if (!inst->nke) {
if (now - inst->last_nke_attempt < MIN_NKE_RETRY_INTERVAL) {
DEBUG_LOG("Limiting NTS-KE request rate");
if (now < inst->next_nke_attempt) {
DEBUG_LOG("Limiting NTS-KE request rate (%f seconds)",
inst->next_nke_attempt - now);
return 0;
}
if (!inst->name) {
LOG(LOGS_ERR, "Missing name of %s for NTS-KE",
UTI_IPToString(&inst->nts_address.ip_addr));
return 0;
}
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set);
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name);
inst->nke_attempts++;
if (!NKC_Start(inst->nke))
return 0;
inst->last_nke_attempt = now;
failed_start = 1;
}
update_next_nke_attempt(inst, failed_start, now);
/* Wait until the session stops */
if (NKC_IsActive(inst->nke))
return 0;
got_data = NKC_GetNtsData(inst->nke, &siv, &c2s, &s2c,
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,
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
&ntp_address);
NKC_DestroyInstance(inst->nke);
inst->nke = NULL;
if (!got_data)
return 0;
if (inst->siv)
SIV_DestroyInstance(inst->siv);
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)) {
inst->num_cookies = 0;
return 0;
}
inst->cookie_index = 0;
if (inst->siv_c2s)
SIV_DestroyInstance(inst->siv_c2s);
if (inst->siv_s2c)
SIV_DestroyInstance(inst->siv_s2c);
inst->siv_c2s = SIV_CreateInstance(siv);
inst->siv_s2c = SIV_CreateInstance(siv);
if (!inst->siv_c2s || !inst->siv_s2c ||
!SIV_SetKey(inst->siv_c2s, c2s.key, c2s.length) ||
!SIV_SetKey(inst->siv_s2c, s2c.key, s2c.length)) {
DEBUG_LOG("Could not initialise SIV");
inst->num_cookies = 0;
return 0;
}
inst->nak_response = 0;
inst->last_nke_success = now;
inst->cookie_index = 0;
return 1;
}
@@ -248,13 +295,31 @@ get_nke_data(NNC_Instance inst)
int
NNC_PrepareForAuth(NNC_Instance inst)
{
if (is_nke_needed(inst)) {
if (!get_nke_data(inst))
inst->auth_ready = 0;
/* Prepare data for the next request and invalidate any responses to the
previous request */
UTI_GetRandomBytes(inst->uniq_id, sizeof (inst->uniq_id));
UTI_GetRandomBytes(inst->nonce, sizeof (inst->nonce));
/* Get new cookies if there are not any, or they are no longer usable */
if (!check_cookies(inst)) {
if (!get_cookies(inst))
return 0;
}
UTI_GetRandomBytes(&inst->uniq_id, sizeof (inst->uniq_id));
UTI_GetRandomBytes(&inst->nonce, sizeof (inst->nonce));
inst->nak_response = 0;
if (!inst->siv)
inst->siv = SIV_CreateInstance(inst->context.algorithm);
if (!inst->siv ||
!SIV_SetKey(inst->siv, inst->context.c2s.key, inst->context.c2s.length)) {
DEBUG_LOG("Could not set SIV key");
return 0;
}
inst->auth_ready = 1;
return 1;
}
@@ -267,19 +332,28 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
{
NKE_Cookie *cookie;
int i, req_cookies;
void *ef_body;
if (inst->num_cookies == 0 || !inst->siv_c2s)
if (!inst->auth_ready)
return 0;
inst->auth_ready = 0;
if (inst->num_cookies <= 0 || !inst->siv)
return 0;
if (info->mode != MODE_CLIENT)
return 0;
cookie = &inst->cookies[inst->cookie_index];
req_cookies = MIN(NTS_MAX_COOKIES - inst->num_cookies + 1,
inst->num_cookies--;
inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES;
req_cookies = MIN(NTS_MAX_COOKIES - inst->num_cookies,
MAX_TOTAL_COOKIE_LENGTH / (cookie->length + 4));
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;
if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE,
@@ -287,17 +361,16 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
return 0;
for (i = 0; i < req_cookies - 1; i++) {
if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE_PLACEHOLDER,
cookie->cookie, cookie->length))
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_COOKIE_PLACEHOLDER,
cookie->length, &ef_body))
return 0;
memset(ef_body, 0, cookie->length);
}
if (!NNA_GenerateAuthEF(packet, info, inst->siv_c2s, inst->nonce, sizeof (inst->nonce),
if (!NNA_GenerateAuthEF(packet, info, inst->siv, inst->nonce, sizeof (inst->nonce),
(const unsigned char *)"", 0, NTP_MAX_V4_MAC_LENGTH + 4))
return 0;
inst->num_cookies--;
inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES;
inst->ok_response = 0;
return 1;
@@ -305,6 +378,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
extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
{
@@ -316,7 +406,7 @@ extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
for (parsed = 0; parsed < length; parsed += ef_length) {
if (!NEF_ParseSingleField(plaintext, length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
break;
return 0;
if (ef_type != NTP_EF_NTS_COOKIE)
continue;
@@ -332,6 +422,9 @@ extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
continue;
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);
inst->cookies[index].length = ef_body_length;
inst->num_cookies++;
@@ -358,17 +451,21 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
if (info->ext_fields == 0 || info->mode != MODE_SERVER)
return 0;
/* Accept only one response per request */
if (inst->ok_response)
/* Accept at most one response per request */
if (inst->ok_response || inst->auth_ready)
return 0;
if (!inst->siv_s2c)
if (!inst->siv ||
!SIV_SetKey(inst->siv, inst->context.s2c.key, inst->context.s2c.length)) {
DEBUG_LOG("Could not set SIV key");
return 0;
}
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
break;
/* This is not expected as the packet already passed parsing */
return 0;
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
@@ -388,10 +485,13 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
return 0;
}
if (!NNA_DecryptAuthEF(packet, info, inst->siv_s2c, parsed,
if (!NNA_DecryptAuthEF(packet, info, inst->siv, parsed,
plaintext, sizeof (plaintext), &plaintext_length))
return 0;
if (!parse_encrypted_efs(inst, plaintext, plaintext_length))
return 0;
has_valid_auth = 1;
break;
default:
@@ -418,7 +518,8 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
/* At this point we know the client interoperates with the server. Allow a
new NTS-KE session to be started as soon as the cookies run out. */
inst->last_nke_attempt = -MIN_NKE_RETRY_INTERVAL;
inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0;
return 1;
}
@@ -428,14 +529,189 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
void
NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
{
if (inst->nke)
NKC_DestroyInstance(inst->nke);
save_cookies(inst);
inst->nke = NULL;
inst->num_cookies = 0;
inst->nts_address.ip_addr = *address;
inst->ntp_address.ip_addr = *address;
reset_instance(inst);
DEBUG_LOG("NTS reset");
load_cookies(inst);
}
/* ================================================== */
static void
save_cookies(NNC_Instance inst)
{
char buf[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename;
struct timespec now;
double context_time;
FILE *f;
int i;
if (inst->num_cookies < 1 || !UTI_IsIPReal(&inst->nts_address.ip_addr))
return;
dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir)
return;
filename = UTI_IPToString(&inst->nts_address.ip_addr);
f = UTI_OpenFile(dump_dir, filename, ".tmp", 'w', 0600);
if (!f)
return;
SCH_GetLastEventTime(&now, NULL, NULL);
context_time = inst->last_nke_success - SCH_GetLastEventMonoTime();
context_time += UTI_TimespecToDouble(&now);
if (fprintf(f, "%s%s\n%.1f\n%s %d\n%u %d ",
DUMP_IDENTIFIER, inst->name, context_time,
UTI_IPToString(&inst->ntp_address.ip_addr), 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)) ||
fprintf(f, "%s ", buf) < 0 ||
!UTI_BytesToHex(inst->context.c2s.key, inst->context.c2s.length, buf, sizeof (buf)) ||
fprintf(f, "%s\n", buf) < 0)
goto error;
for (i = 0; i < inst->num_cookies; i++) {
if (!UTI_BytesToHex(inst->cookies[i].cookie, inst->cookies[i].length, buf, sizeof (buf)) ||
fprintf(f, "%s\n", buf) < 0)
goto error;
}
fclose(f);
if (!UTI_RenameTempFile(dump_dir, filename, ".tmp", ".nts"))
;
return;
error:
DEBUG_LOG("Could not %s cookies for %s", "save", filename);
fclose(f);
if (!UTI_RemoveFile(dump_dir, filename, ".nts"))
;
}
/* ================================================== */
#define MAX_WORDS 4
static void
load_cookies(NNC_Instance inst)
{
char line[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename, *words[MAX_WORDS];
unsigned int context_id;
int i, algorithm, port;
double context_time;
struct timespec now;
IPSockAddr ntp_addr;
FILE *f;
dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir)
return;
filename = UTI_IPToString(&inst->nts_address.ip_addr);
f = UTI_OpenFile(dump_dir, filename, ".nts", 'r', 0);
if (!f)
return;
/* Don't load this file again */
if (!UTI_RemoveFile(dump_dir, filename, ".nts"))
;
if (inst->siv)
SIV_DestroyInstance(inst->siv);
inst->siv = NULL;
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
strcmp(words[0], inst->name) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
sscanf(words[0], "%lf", &context_time) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
!UTI_StringToIP(words[0], &ntp_addr.ip_addr) || sscanf(words[1], "%d", &port) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 4 ||
sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
goto error;
inst->context.algorithm = algorithm;
inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
sizeof (inst->context.s2c.key));
inst->context.c2s.length = UTI_HexToBytes(words[3], inst->context.c2s.key,
sizeof (inst->context.c2s.key));
if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
inst->context.s2c.length <= 0 ||
inst->context.c2s.length != inst->context.s2c.length)
goto error;
for (i = 0; i < NTS_MAX_COOKIES && fgets(line, sizeof (line), f); i++) {
if (UTI_SplitString(line, words, MAX_WORDS) != 1)
goto error;
inst->cookies[i].length = UTI_HexToBytes(words[0], inst->cookies[i].cookie,
sizeof (inst->cookies[i].cookie));
if (inst->cookies[i].length == 0)
goto error;
}
inst->num_cookies = i;
ntp_addr.port = port;
if (!set_ntp_address(inst, &ntp_addr))
goto error;
SCH_GetLastEventTime(&now, NULL, NULL);
context_time -= UTI_TimespecToDouble(&now);
if (context_time > 0)
context_time = 0;
inst->last_nke_success = context_time + SCH_GetLastEventMonoTime();
inst->context_id = context_id;
fclose(f);
DEBUG_LOG("Loaded %d cookies for %s", i, filename);
return;
error:
DEBUG_LOG("Could not %s cookies for %s", "load", filename);
fclose(f);
memset(&inst->context, 0, sizeof (inst->context));
inst->num_cookies = 0;
}
/* ================================================== */
void
NNC_DumpData(NNC_Instance inst)
{
save_cookies(inst);
}
/* ================================================== */
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,11 +29,12 @@
#include "addressing.h"
#include "ntp.h"
#include "reports.h"
typedef struct NNC_Instance_Record *NNC_Instance;
extern NNC_Instance NNC_CreateInstance(IPSockAddr *nts_address, const char *name,
const IPSockAddr *ntp_address);
uint32_t cert_set, uint16_t ntp_port);
extern void NNC_DestroyInstance(NNC_Instance inst);
extern int NNC_PrepareForAuth(NNC_Instance inst);
extern int NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
@@ -43,4 +44,8 @@ extern int NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
extern void NNC_ChangeAddress(NNC_Instance inst, IPAddr *address);
extern void NNC_DumpData(NNC_Instance inst);
extern void NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report);
#endif

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
* Copyright (C) Miroslav Lichvar 2020, 2022
*
* 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
@@ -41,11 +41,15 @@
#include "siv.h"
#include "util.h"
#define MAX_SERVER_SIVS 2
struct NtsServer {
SIV_Instance siv;
SIV_Instance sivs[MAX_SERVER_SIVS];
SIV_Algorithm siv_algorithms[MAX_SERVER_SIVS];
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies;
int siv_index;
NTP_int64 req_tx;
};
@@ -57,14 +61,27 @@ struct NtsServer *server;
void
NNS_Initialise(void)
{
const char **certs, **keys;
int i;
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */
if (!CNF_GetNtsServerCertFile() || !CNF_GetNtsServerKeyFile()) {
if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) {
server = NULL;
return;
}
server = Malloc(sizeof (struct NtsServer));
server->siv = SIV_CreateInstance(AEAD_AES_SIV_CMAC_256);
server->siv_algorithms[0] = AEAD_AES_SIV_CMAC_256;
server->siv_algorithms[1] = AEAD_AES_128_GCM_SIV;
assert(MAX_SERVER_SIVS == 2);
for (i = 0; i < 2; i++)
server->sivs[i] = SIV_CreateInstance(server->siv_algorithms[i]);
/* AES-SIV-CMAC-256 is required on servers */
if (!server->sivs[0])
LOG_FATAL("Missing AES-SIV-CMAC-256");
}
/* ================================================== */
@@ -72,10 +89,15 @@ NNS_Initialise(void)
void
NNS_Finalise(void)
{
int i;
if (!server)
return;
SIV_DestroyInstance(server->siv);
for (i = 0; i < MAX_SERVER_SIVS; i++) {
if (server->sivs[i])
SIV_DestroyInstance(server->sivs[i]);
}
Free(server);
server = NULL;
}
@@ -88,16 +110,18 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
int ef_type, ef_body_length, ef_length, has_uniq_id = 0, has_auth = 0, has_cookie = 0;
int i, plaintext_length, parsed, requested_cookies, cookie_length = -1, auth_start = 0;
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
NKE_Context context;
NKE_Cookie cookie;
NKE_Key c2s, s2c;
SIV_Instance siv;
void *ef_body;
*kod = 0;
if (!server)
return 0;
*kod = 0;
server->num_cookies = 0;
server->siv_index = -1;
server->req_tx = packet->transmit_ts;
if (info->ext_fields == 0 || info->mode != MODE_CLIENT)
@@ -108,15 +132,18 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed,
&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) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
has_uniq_id = 1;
break;
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;
}
cookie.length = ef_body_length;
memcpy(cookie.cookie, ef_body, ef_body_length);
has_cookie = 1;
@@ -131,6 +158,11 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
cookie_length = ef_body_length;
break;
case NTP_EF_NTS_AUTH_AND_EEF:
if (parsed + ef_length != info->length) {
DEBUG_LOG("Auth not last EF");
return 0;
}
auth_start = parsed;
has_auth = 1;
break;
@@ -144,17 +176,27 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
return 0;
}
if (!NKS_DecodeCookie(&cookie, &c2s, &s2c)) {
if (!NKS_DecodeCookie(&cookie, &context)) {
*kod = NTP_KOD_NTS_NAK;
return 0;
}
if (!SIV_SetKey(server->siv, c2s.key, c2s.length)) {
/* Find the SIV instance needed for authentication */
for (i = 0; i < MAX_SERVER_SIVS && context.algorithm != server->siv_algorithms[i]; i++)
;
if (i == MAX_SERVER_SIVS || !server->sivs[i]) {
DEBUG_LOG("Unexpected SIV");
return 0;
}
server->siv_index = i;
siv = server->sivs[i];
if (!SIV_SetKey(siv, context.c2s.key, context.c2s.length)) {
DEBUG_LOG("Could not set C2S key");
return 0;
}
if (!NNA_DecryptAuthEF(packet, info, server->siv, auth_start,
if (!NNA_DecryptAuthEF(packet, info, siv, auth_start,
plaintext, sizeof (plaintext), &plaintext_length)) {
*kod = NTP_KOD_NTS_NAK;
return 0;
@@ -162,8 +204,10 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
for (parsed = 0; parsed < plaintext_length; parsed += ef_length) {
if (!NEF_ParseSingleField(plaintext, plaintext_length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
break;
&ef_length, &ef_type, &ef_body, &ef_body_length)) {
DEBUG_LOG("Could not parse encrypted EF");
return 0;
}
switch (ef_type) {
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
@@ -178,18 +222,23 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
}
}
if (!SIV_SetKey(server->siv, s2c.key, s2c.length)) {
if (!SIV_SetKey(siv, context.s2c.key, context.s2c.length)) {
DEBUG_LOG("Could not set S2C key");
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));
server->num_cookies = MIN(NTS_MAX_COOKIES, requested_cookies);
for (i = 0; i < server->num_cookies; i++)
if (!NKS_GenerateCookie(&c2s, &s2c, &server->cookies[i]))
assert(sizeof (server->cookies) / sizeof (server->cookies[0]) == NTS_MAX_COOKIES);
for (i = 0; i < NTS_MAX_COOKIES && i < requested_cookies; i++)
if (!NKS_GenerateCookie(&context, &server->cookies[i]))
return 0;
server->num_cookies = i;
return 1;
}
@@ -208,14 +257,16 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
if (!server || req_info->mode != MODE_CLIENT || res_info->mode != MODE_SERVER)
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)
assert(0);
for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) {
if (!NEF_ParseField(request, req_info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
break;
/* This is not expected as the packet already passed parsing */
return 0;
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
@@ -228,12 +279,12 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
}
/* NTS NAK response does not have any other fields */
if (kod)
if (kod == NTP_KOD_NTS_NAK)
return 1;
for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) {
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))
return 0;
@@ -243,7 +294,12 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
server->num_cookies = 0;
if (!NNA_GenerateAuthEF(response, res_info, server->siv,
if (server->siv_index < 0)
return 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->sivs[server->siv_index],
server->nonce, sizeof (server->nonce),
plaintext, plaintext_length,
req_info->length - res_info->length))

View File

@@ -87,7 +87,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */
REQ_LENGTH_ENTRY(null, null), /* WRITERTC */
REQ_LENGTH_ENTRY(dfreq, null), /* DFREQ */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET */
{ 0, 0 }, /* DOFFSET - not supported */
REQ_LENGTH_ENTRY(null, tracking), /* TRACKING */
REQ_LENGTH_ENTRY(sourcestats, sourcestats), /* SOURCESTATS */
REQ_LENGTH_ENTRY(null, rtc), /* RTCREPORT */
@@ -110,9 +110,8 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(smoothtime, null), /* SMOOTHTIME */
REQ_LENGTH_ENTRY(null, null), /* REFRESH */
REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */
REQ_LENGTH_ENTRY(client_accesses_by_index,
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
{ 0, 0 }, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
{ 0, 0 }, /* LOCAL2 - not supported */
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
{ 0, 0 }, /* ADD_SERVER2 */
{ 0, 0 }, /* ADD_PEER2 */
@@ -123,7 +122,16 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SOURCE */
REQ_LENGTH_ENTRY(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 */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
REQ_LENGTH_ENTRY(modify_select_opts, null), /* MODIFY_SELECTOPTS */
REQ_LENGTH_ENTRY(modify_offset, null), /* MODIFY_OFFSET */
REQ_LENGTH_ENTRY(local, null), /* LOCAL3 */
};
static const uint16_t reply_lengths[] = {
@@ -141,12 +149,19 @@ static const uint16_t reply_lengths[] = {
0, /* MANUAL_LIST - not supported */
RPY_LENGTH_ENTRY(activity), /* ACTIVITY */
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
0, /* SERVER_STATS - not supported */
0, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
0, /* NTP_DATA - not supported */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
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 */
0, /* SERVER_STATS2 - not supported */
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
0, /* SERVER_STATS3 - not supported */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS4 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA2 */
};
/* ================================================== */

View File

@@ -171,22 +171,22 @@ send_response(int fd, const PrvResponse *res)
static int
receive_from_daemon(int fd, PrvRequest *req)
{
SCK_Message message;
SCK_Message *message;
if (!SCK_ReceiveMessage(fd, &message, SCK_FLAG_MSG_DESCRIPTOR) ||
message.length != sizeof (*req))
message = SCK_ReceiveMessage(fd, SCK_FLAG_MSG_DESCRIPTOR);
if (!message || message->length != sizeof (*req))
return 0;
memcpy(req, message.data, sizeof (*req));
memcpy(req, message->data, sizeof (*req));
if (req->op == OP_BINDSOCKET) {
req->data.bind_socket.sock = message.descriptor;
req->data.bind_socket.sock = message->descriptor;
/* return error if valid descriptor not found */
if (req->data.bind_socket.sock < 0)
return 0;
} else if (message.descriptor >= 0) {
SCK_CloseSocket(message.descriptor);
} else if (message->descriptor >= 0) {
SCK_CloseSocket(message->descriptor);
return 0;
}
@@ -255,7 +255,7 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
ip_saddr.port != CNF_GetAcquisitionPort()) {
ip_saddr.port != CNF_GetAcquisitionPort() && ip_saddr.port != CNF_GetPtpPort()) {
SCK_CloseSocket(sock_fd);
res_fatal(res, "Invalid port %d", ip_saddr.port);
return;
@@ -547,7 +547,7 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
ip_saddr.port != CNF_GetAcquisitionPort())
ip_saddr.port != CNF_GetAcquisitionPort() && ip_saddr.port != CNF_GetPtpPort())
assert(0);
if (!have_helper())
@@ -644,6 +644,8 @@ PRV_StartHelper(void)
LOG_FATAL("Helper already running");
sock_fd1 = SCK_OpenUnixSocketPair(SCK_FLAG_BLOCK, &sock_fd2);
if (sock_fd1 < 0)
LOG_FATAL("Could not open socket pair");
pid = fork();
if (pid < 0)
@@ -660,6 +662,8 @@ PRV_StartHelper(void)
close(fd);
}
UTI_ResetGetRandomFunctions();
/* ignore signals, the process will exit on OP_QUIT request */
UTI_SetQuitSignalsHandler(SIG_IGN, 1);

70
ptp.h Normal file
View File

@@ -0,0 +1,70 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2021
*
* 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.
*
**********************************************************************
=======================================================================
This is the header file for the Precision Time Protocol (PTP).
*/
#ifndef GOT_PTP_H
#define GOT_PTP_H
#include "sysincl.h"
#include "ntp.h"
#define PTP_VERSION_2 2
#define PTP_VERSION_2_1 (2 | 1 << 4)
#define PTP_TYPE_SYNC 0
#define PTP_TYPE_DELAY_REQ 1
#define PTP_FLAG_UNICAST (1 << (2 + 8))
#define PTP_TLV_NTP 0x2023
typedef struct {
uint8_t type;
uint8_t version;
uint16_t length;
uint8_t domain;
uint8_t min_sdoid;
uint16_t flags;
uint8_t correction[8];
uint8_t msg_specific[4];
uint8_t port_id[10];
uint16_t sequence_id;
uint8_t control;
int8_t interval;
} PTP_Header;
typedef struct {
uint16_t type;
uint16_t length;
} PTP_TlvHeader;
typedef struct {
PTP_Header header;
uint8_t origin_ts[10];
PTP_TlvHeader tlv_header;
NTP_Packet ntp_msg;
} PTP_NtpMessage;
#define PTP_NTP_PREFIX_LENGTH (int)offsetof(PTP_NtpMessage, ntp_msg)
#endif

209
quantiles.c Normal file
View File

@@ -0,0 +1,209 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2022
*
* 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.
*
**********************************************************************
=======================================================================
Estimation of quantiles using the Frugal-2U streaming algorithm
(https://arxiv.org/pdf/1407.1121v1.pdf)
*/
#include "config.h"
#include "logging.h"
#include "memory.h"
#include "quantiles.h"
#include "regress.h"
#include "util.h"
/* Maximum number of repeated estimates for stabilisation */
#define MAX_REPEAT 64
struct Quantile {
double est;
double step;
int sign;
};
struct QNT_Instance_Record {
struct Quantile *quants;
int n_quants;
int repeat;
int q;
int min_k;
double min_step;
int n_set;
};
/* ================================================== */
QNT_Instance
QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step)
{
QNT_Instance inst;
long seed;
if (q < 2 || min_k > max_k || min_k < 1 || max_k >= q ||
repeat < 1 || repeat > MAX_REPEAT || min_step <= 0.0)
assert(0);
inst = MallocNew(struct QNT_Instance_Record);
inst->n_quants = (max_k - min_k + 1) * repeat;
inst->quants = MallocArray(struct Quantile, inst->n_quants);
inst->repeat = repeat;
inst->q = q;
inst->min_k = min_k;
inst->min_step = min_step;
QNT_Reset(inst);
/* Seed the random number generator, which will not be isolated from
other instances and other random() users */
UTI_GetRandomBytes(&seed, sizeof (seed));
srandom(seed);
return inst;
}
/* ================================================== */
void
QNT_DestroyInstance(QNT_Instance inst)
{
Free(inst->quants);
Free(inst);
}
/* ================================================== */
void
QNT_Reset(QNT_Instance inst)
{
int i;
inst->n_set = 0;
for (i = 0; i < inst->n_quants; i++) {
inst->quants[i].est = 0.0;
inst->quants[i].step = inst->min_step;
inst->quants[i].sign = 1;
}
}
/* ================================================== */
static void
insert_initial_value(QNT_Instance inst, double value)
{
int i, j, r = inst->repeat;
if (inst->n_set * r >= inst->n_quants)
assert(0);
/* Keep the initial estimates repeated and ordered */
for (i = inst->n_set; i > 0 && inst->quants[(i - 1) * r].est > value; i--) {
for (j = 0; j < r; j++)
inst->quants[i * r + j].est = inst->quants[(i - 1) * r].est;
}
for (j = 0; j < r; j++)
inst->quants[i * r + j].est = value;
inst->n_set++;
/* Duplicate the largest value in unset quantiles */
for (i = inst->n_set * r; i < inst->n_quants; i++)
inst->quants[i].est = inst->quants[i - 1].est;
}
/* ================================================== */
static void
update_estimate(struct Quantile *quantile, double value, double p, double rand,
double min_step)
{
if (value > quantile->est && rand > (1.0 - p)) {
quantile->step += quantile->sign > 0 ? min_step : -min_step;
quantile->est += quantile->step > 0.0 ? fabs(quantile->step) : min_step;
if (quantile->est > value) {
quantile->step += value - quantile->est;
quantile->est = value;
}
if (quantile->sign < 0 && quantile->step > min_step)
quantile->step = min_step;
quantile->sign = 1;
} else if (value < quantile->est && rand > p) {
quantile->step += quantile->sign < 0 ? min_step : -min_step;
quantile->est -= quantile->step > 0.0 ? fabs(quantile->step) : min_step;
if (quantile->est < value) {
quantile->step += quantile->est - value;
quantile->est = value;
}
if (quantile->sign > 0 && quantile->step > min_step)
quantile->step = min_step;
quantile->sign = -1;
}
}
/* ================================================== */
void
QNT_Accumulate(QNT_Instance inst, double value)
{
double p, rand;
int i;
/* Initialise the estimates with first received values */
if (inst->n_set * inst->repeat < inst->n_quants) {
insert_initial_value(inst, value);
return;
}
for (i = 0; i < inst->n_quants; i++) {
p = (double)(i / inst->repeat + inst->min_k) / inst->q;
rand = (double)random() / ((1U << 31) - 1);
update_estimate(&inst->quants[i], value, p, rand, inst->min_step);
}
}
/* ================================================== */
int
QNT_GetMinK(QNT_Instance inst)
{
return inst->min_k;
}
/* ================================================== */
double
QNT_GetQuantile(QNT_Instance inst, int k)
{
double estimates[MAX_REPEAT];
int i;
if (k < inst->min_k || k - inst->min_k >= inst->n_quants)
assert(0);
for (i = 0; i < inst->repeat; i++)
estimates[i] = inst->quants[(k - inst->min_k) * inst->repeat + i].est;
return RGR_FindMedian(estimates, inst->repeat);
}

41
quantiles.h Normal file
View File

@@ -0,0 +1,41 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2022
*
* 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.
*
**********************************************************************
=======================================================================
Header file for estimation of quantiles.
*/
#ifndef GOT_QUANTILES_H
#define GOT_QUANTILES_H
typedef struct QNT_Instance_Record *QNT_Instance;
extern QNT_Instance QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step);
extern void QNT_DestroyInstance(QNT_Instance inst);
extern void QNT_Reset(QNT_Instance inst);
extern void QNT_Accumulate(QNT_Instance inst, double value);
extern int QNT_GetMinK(QNT_Instance inst);
extern double QNT_GetQuantile(QNT_Instance inst, int k);
#endif

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019, 2022
*
* 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
@@ -40,6 +40,9 @@
#include "samplefilt.h"
#include "sched.h"
/* Maximum offset of locked reference as a fraction of the PPS interval */
#define PPS_LOCK_LIMIT 0.4
/* list of refclock drivers */
extern RefclockDriver RCL_SHM_driver;
extern RefclockDriver RCL_SOCK_driver;
@@ -52,21 +55,6 @@ struct FilterSample {
struct timespec sample_time;
};
struct MedianFilter {
int length;
int index;
int used;
int last;
int avg_var_n;
double avg_var;
double max_var;
struct FilterSample *samples;
int *selected;
double *x_data;
double *y_data;
double *w_data;
};
struct RCL_Instance_Record {
RefclockDriver *driver;
void *data;
@@ -76,6 +64,7 @@ struct RCL_Instance_Record {
int driver_polled;
int poll;
int leap_status;
int local;
int pps_forced;
int pps_rate;
int pps_active;
@@ -177,16 +166,17 @@ RCL_AddRefclock(RefclockParameters *params)
if (!inst->driver->init && !inst->driver->poll)
LOG_FATAL("refclock driver %s is not compiled in", params->driver_name);
if (params->tai && !CNF_GetLeapSecTimezone())
LOG_FATAL("refclock tai option requires leapsectz");
if (params->tai && !CNF_GetLeapSecList() && !CNF_GetLeapSecTimezone())
LOG_FATAL("refclock tai option requires leapseclist or leapsectz");
inst->data = NULL;
inst->driver_parameter = params->driver_parameter;
inst->driver_parameter = Strdup(params->driver_parameter);
inst->driver_parameter_length = 0;
inst->driver_poll = params->driver_poll;
inst->poll = params->poll;
inst->driver_polled = 0;
inst->leap_status = LEAP_Normal;
inst->local = params->local;
inst->pps_forced = params->pps_forced;
inst->pps_rate = params->pps_rate;
inst->pps_active = 0;
@@ -228,6 +218,13 @@ RCL_AddRefclock(RefclockParameters *params)
inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
}
if (inst->local) {
inst->pps_forced = 1;
inst->lock_ref = inst->ref_id;
inst->leap_status = LEAP_Unsynchronised;
inst->max_lock_age = MAX(inst->max_lock_age, 3);
}
if (inst->driver->poll) {
int max_samples;
@@ -253,22 +250,21 @@ RCL_AddRefclock(RefclockParameters *params)
inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length,
params->max_dispersion, 0.6);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL,
params->min_samples, params->max_samples, 0.0, 0.0);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, 0, params->sel_options,
NULL, params->min_samples, params->max_samples,
0.0, 0.0);
DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
params->driver_name, UTI_RefidToString(inst->ref_id),
inst->poll, inst->driver_poll, params->filter_length);
Free(params->driver_name);
return 1;
}
void
RCL_StartRefclocks(void)
{
unsigned int i, j, n;
unsigned int i, j, n, lock_index;
n = ARR_GetSize(refclocks);
@@ -278,13 +274,31 @@ RCL_StartRefclocks(void)
SRC_SetActive(inst->source);
inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst);
if (inst->lock_ref) {
/* Replace lock refid with index to refclocks */
for (j = 0; j < n && get_refclock(j)->ref_id != inst->lock_ref; j++)
;
inst->lock_ref = j < n ? j : -1;
} else
inst->lock_ref = -1;
/* Replace lock refid with the refclock's index, or -1 if not valid */
lock_index = -1;
if (inst->lock_ref != 0) {
for (j = 0; j < n; j++) {
RCL_Instance inst2 = get_refclock(j);
if (inst->lock_ref != inst2->ref_id)
continue;
if (inst->driver->poll && inst2->driver->poll &&
(double)inst->max_lock_age / inst->pps_rate < UTI_Log2ToDouble(inst2->driver_poll))
LOG(LOGS_WARN, "%s maxlockage too small for %s",
UTI_RefidToString(inst->ref_id), UTI_RefidToString(inst2->ref_id));
lock_index = j;
break;
}
if (lock_index == -1 || (lock_index == i && !inst->local))
LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref));
}
inst->lock_ref = lock_index;
}
}
@@ -307,6 +321,22 @@ RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
}
}
int
RCL_ModifyOffset(uint32_t ref_id, double offset)
{
unsigned int i;
for (i = 0; i < ARR_GetSize(refclocks); i++) {
RCL_Instance inst = get_refclock(i);
if (inst->ref_id == ref_id) {
inst->offset = offset;
LOG(LOGS_INFO, "Source %s new offset %f", UTI_RefidToString(ref_id), offset);
return 1;
}
}
return 0;
}
void
RCL_SetDriverData(RCL_Instance instance, void *data)
{
@@ -416,30 +446,28 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
sample.peer_dispersion = dispersion;
sample.root_dispersion = dispersion;
/* Handle special case when PPS is used with the local reference */
if (instance->pps_active && instance->lock_ref == -1)
sample.stratum = pps_stratum(instance, &sample.time);
else
sample.stratum = instance->stratum;
return SPF_AccumulateSample(instance->filter, &sample);
}
int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
struct timespec *ref_time, int leap)
{
double correction, dispersion;
double correction, dispersion, raw_offset, offset;
struct timespec cooked_time;
if (instance->pps_forced)
return RCL_AddPulse(instance, sample_time, -offset);
return RCL_AddPulse(instance, sample_time,
1.0e-9 * (sample_time->tv_nsec - ref_time->tv_nsec));
raw_offset = UTI_DiffTimespecsToDouble(ref_time, sample_time);
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
dispersion += instance->precision;
/* Make sure the timestamp and offset provided by the driver are sane */
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
if (!UTI_IsTimeOffsetSane(sample_time, raw_offset) ||
!valid_sample_time(instance, &cooked_time))
return 0;
@@ -454,18 +482,24 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
return 0;
}
/* Calculate offset = raw_offset - correction + instance->offset
in parts to avoid loss of precision if there are large differences */
offset = ref_time->tv_sec - sample_time->tv_sec -
(time_t)correction + (time_t)instance->offset;
offset += 1.0e-9 * (ref_time->tv_nsec - sample_time->tv_nsec) -
(correction - (time_t)correction) + (instance->offset - (time_t)instance->offset);
if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
DEBUG_LOG("refclock sample ignored unknown TAI offset");
return 0;
}
if (!accumulate_sample(instance, &cooked_time,
offset - correction + instance->offset, dispersion))
if (!accumulate_sample(instance, &cooked_time, offset, dispersion))
return 0;
instance->pps_active = 0;
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
log_sample(instance, &cooked_time, 0, 0, raw_offset, offset, dispersion);
/* for logging purposes */
if (!instance->driver->poll)
@@ -544,29 +578,40 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
lock_refclock = get_refclock(instance->lock_ref);
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
DEBUG_LOG("refclock pulse ignored no ref sample");
return 0;
if (instance->local) {
/* Make the first sample in order to lock to itself */
ref_sample.time = *cooked_time;
ref_sample.offset = offset;
ref_sample.peer_delay = ref_sample.peer_dispersion = 0;
ref_sample.root_delay = ref_sample.root_dispersion = 0;
} else {
DEBUG_LOG("refclock pulse ignored no ref sample");
return 0;
}
}
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
sample_diff);
DEBUG_LOG("refclock pulse ignored samplediff=%.9f", sample_diff);
/* Restart the local mode */
if (instance->local) {
LOG(LOGS_WARN, "Local refclock lost lock");
SPF_DropSamples(instance->filter);
SRC_ResetInstance(instance->source);
}
return 0;
}
/* Align the offset to the reference sample */
if ((ref_sample.offset - offset) >= 0.0)
shift = (long)((ref_sample.offset - offset) * rate + 0.5) / (double)rate;
else
shift = (long)((ref_sample.offset - offset) * rate - 0.5) / (double)rate;
shift = round((ref_sample.offset - offset) * rate) / rate;
offset += shift;
if (fabs(ref_sample.offset - offset) +
ref_sample.root_dispersion + dispersion >= 0.2 / rate) {
ref_sample.root_dispersion + dispersion > PPS_LOCK_LIMIT / rate) {
DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
ref_sample.offset - offset, ref_sample.root_dispersion, dispersion);
return 0;
@@ -682,11 +727,57 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
return 0;
}
static void
get_local_stats(RCL_Instance inst, struct timespec *ref, double *freq, double *offset)
{
double offset_sd, freq_sd, skew, root_delay, root_disp;
SST_Stats stats = SRC_GetSourcestats(inst->source);
if (SST_Samples(stats) < SST_GetMinSamples(stats)) {
UTI_ZeroTimespec(ref);
return;
}
SST_GetTrackingData(stats, ref, offset, &offset_sd, freq, &freq_sd,
&skew, &root_delay, &root_disp);
}
static void
follow_local(RCL_Instance inst, struct timespec *prev_ref_time, double prev_freq,
double prev_offset)
{
SST_Stats stats = SRC_GetSourcestats(inst->source);
double freq, dfreq, offset, doffset, elapsed;
struct timespec now, ref_time;
get_local_stats(inst, &ref_time, &freq, &offset);
if (UTI_IsZeroTimespec(prev_ref_time) || UTI_IsZeroTimespec(&ref_time))
return;
dfreq = (freq - prev_freq) / (1.0 - prev_freq);
elapsed = UTI_DiffTimespecsToDouble(&ref_time, prev_ref_time);
doffset = offset - elapsed * prev_freq - prev_offset;
if (!REF_AdjustReference(doffset, dfreq))
return;
LCL_ReadCookedTime(&now, NULL);
SST_SlewSamples(stats, &now, dfreq, doffset);
SPF_SlewSamples(inst->filter, &now, dfreq, doffset);
/* Keep the offset close to zero to not lose precision */
if (fabs(offset) >= 1.0) {
SST_CorrectOffset(stats, -round(offset));
SPF_CorrectOffset(inst->filter, -round(offset));
}
}
static void
poll_timeout(void *arg)
{
NTP_Sample sample;
int poll;
int poll, stratum;
RCL_Instance inst = (RCL_Instance)arg;
@@ -702,11 +793,28 @@ poll_timeout(void *arg)
inst->driver_polled = 0;
if (SPF_GetFilteredSample(inst->filter, &sample)) {
double local_freq, local_offset;
struct timespec local_ref_time;
/* Handle special case when PPS is used with the local reference */
if (inst->pps_active && inst->lock_ref == -1)
stratum = pps_stratum(inst, &sample.time);
else
stratum = inst->stratum;
if (inst->local) {
get_local_stats(inst, &local_ref_time, &local_freq, &local_offset);
inst->leap_status = LEAP_Unsynchronised;
}
SRC_UpdateReachability(inst->source, 1);
SRC_SetLeapStatus(inst->source, inst->leap_status);
SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
SRC_AccumulateSample(inst->source, &sample);
SRC_SelectSource(inst->source);
if (inst->local)
follow_local(inst, &local_ref_time, local_freq, local_offset);
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
} else {
SRC_UpdateReachability(inst->source, 0);

View File

@@ -37,6 +37,7 @@ typedef struct {
int driver_poll;
int poll;
int filter_length;
int local;
int pps_forced;
int pps_rate;
int min_samples;
@@ -67,6 +68,7 @@ extern void RCL_Finalise(void);
extern int RCL_AddRefclock(RefclockParameters *params);
extern void RCL_StartRefclocks(void);
extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now);
extern int RCL_ModifyOffset(uint32_t ref_id, double offset);
/* functions used by drivers */
extern void RCL_SetDriverData(RCL_Instance instance, void *data);
@@ -74,7 +76,8 @@ extern void *RCL_GetDriverData(RCL_Instance instance);
extern char *RCL_GetDriverParameter(RCL_Instance instance);
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap);
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
struct timespec *ref_time, int leap);
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2013, 2017
* Copyright (C) Miroslav Lichvar 2013, 2017, 2023
*
* 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
@@ -33,6 +33,9 @@
#include "sysincl.h"
#include <sys/sysmacros.h>
#include "array.h"
#include "refclock.h"
#include "hwclock.h"
#include "local.h"
@@ -44,14 +47,19 @@
struct phc_instance {
int fd;
int dev_index;
int mode;
int nocrossts;
int extpps;
int pin;
int channel;
struct timespec last_extts;
HCL_Instance clock;
};
/* Array of RCL_Instance with enabled extpps */
static ARR_Instance extts_phcs = NULL;
static void read_ext_pulse(int sockfd, int event, void *anything);
static int phc_initialise(RCL_Instance instance)
@@ -59,6 +67,7 @@ static int phc_initialise(RCL_Instance instance)
const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
struct phc_instance *phc;
int phc_fd, rising_edge;
struct stat st;
char *path, *s;
RCL_CheckDriverOptions(instance, options);
@@ -66,16 +75,20 @@ static int phc_initialise(RCL_Instance instance)
path = RCL_GetDriverParameter(instance);
phc_fd = SYS_Linux_OpenPHC(path, 0);
if (phc_fd < 0) {
if (phc_fd < 0)
LOG_FATAL("Could not open PHC");
return 0;
}
phc = MallocNew(struct phc_instance);
phc->fd = phc_fd;
if (fstat(phc_fd, &st) < 0 || !S_ISCHR(st.st_mode))
LOG_FATAL("Could not get PHC index");
phc->dev_index = minor(st.st_rdev);
phc->mode = 0;
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
UTI_ZeroTimespec(&phc->last_extts);
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
RCL_GetPrecision(instance));
if (phc->extpps) {
s = RCL_GetDriverOption(instance, "pin");
@@ -83,16 +96,18 @@ static int phc_initialise(RCL_Instance instance)
s = RCL_GetDriverOption(instance, "channel");
phc->channel = s ? atoi(s) : 0;
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
rising_edge, !rising_edge, 1))
LOG_FATAL("Could not enable external PHC timestamping");
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
if (!extts_phcs)
extts_phcs = ARR_CreateInstance(sizeof (RCL_Instance));
ARR_AppendElement(extts_phcs, &instance);
} else {
phc->pin = phc->channel = 0;
phc->clock = NULL;
}
RCL_SetDriverData(instance, phc);
@@ -102,68 +117,106 @@ static int phc_initialise(RCL_Instance instance)
static void phc_finalise(RCL_Instance instance)
{
struct phc_instance *phc;
unsigned int i;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (phc->extpps) {
SCH_RemoveFileHandler(phc->fd);
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
HCL_DestroyInstance(phc->clock);
for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
if ((*(RCL_Instance *)ARR_GetElement(extts_phcs, i)) == instance)
ARR_RemoveElement(extts_phcs, i--);
}
if (ARR_GetSize(extts_phcs) == 0) {
ARR_DestroyInstance(extts_phcs);
extts_phcs = NULL;
}
}
HCL_DestroyInstance(phc->clock);
close(phc->fd);
Free(phc);
}
static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
{
struct phc_instance *phc;
struct timespec local_ts;
double local_err;
phc = RCL_GetDriverData(instance);
if (UTI_CompareTimespecs(&phc->last_extts, phc_ts) == 0) {
DEBUG_LOG("Ignoring duplicated PHC timestamp");
return;
}
phc->last_extts = *phc_ts;
if (!HCL_CookTime(phc->clock, phc_ts, &local_ts, &local_err))
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
UTI_DiffTimespecsToDouble(phc_ts, &local_ts));
}
static void read_ext_pulse(int fd, int event, void *anything)
{
RCL_Instance instance;
struct phc_instance *phc;
struct timespec phc_ts, local_ts;
double local_err;
struct phc_instance *phc1, *phc2;
struct timespec phc_ts;
unsigned int i;
int channel;
if (!SYS_Linux_ReadPHCExtTimestamp(fd, &phc_ts, &channel))
return;
instance = anything;
phc = RCL_GetDriverData(instance);
phc1 = RCL_GetDriverData(instance);
if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
return;
/* Linux versions before 6.7 had one shared queue of timestamps for all
descriptors of the same PHC. Search for all refclocks that expect
the timestamp. */
if (channel != phc->channel) {
DEBUG_LOG("Unexpected extts channel %d\n", channel);
return;
for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
instance = *(RCL_Instance *)ARR_GetElement(extts_phcs, i);
phc2 = RCL_GetDriverData(instance);
if (!phc2->extpps || phc2->dev_index != phc1->dev_index || phc2->channel != channel)
continue;
process_ext_pulse(instance, &phc_ts);
}
if (!HCL_CookTime(phc->clock, &phc_ts, &local_ts, &local_err))
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
}
#define PHC_READINGS 25
static int phc_poll(RCL_Instance instance)
{
struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3];
struct phc_instance *phc;
struct timespec phc_ts, sys_ts, local_ts;
double offset, phc_err, local_err;
double phc_err, local_err;
int n_readings;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
&phc->mode, &phc_ts, &sys_ts, &phc_err))
n_readings = SYS_Linux_GetPHCReadings(phc->fd, phc->nocrossts, &phc->mode,
PHC_READINGS, readings);
if (n_readings < 1)
return 0;
if (phc->extpps) {
LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
return 0;
}
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err);
if (phc->extpps)
return 0;
return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal);
DEBUG_LOG("PHC offset: %+.9f err: %.9f",
UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts), phc_err);
return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal);
}
RefclockDriver RCL_PHC_driver = {

View File

@@ -61,49 +61,36 @@ static int pps_initialise(RCL_Instance instance) {
edge_clear = RCL_GetDriverOption(instance, "clear") ? 1 : 0;
fd = open(path, O_RDWR);
if (fd < 0) {
if (fd < 0)
LOG_FATAL("Could not open %s : %s", path, strerror(errno));
return 0;
}
UTI_FdSetCloexec(fd);
if (time_pps_create(fd, &handle) < 0) {
if (time_pps_create(fd, &handle) < 0)
LOG_FATAL("time_pps_create() failed on %s : %s", path, strerror(errno));
return 0;
}
if (time_pps_getcap(handle, &mode) < 0) {
if (time_pps_getcap(handle, &mode) < 0)
LOG_FATAL("time_pps_getcap() failed on %s : %s", path, strerror(errno));
return 0;
}
if (time_pps_getparams(handle, &params) < 0) {
if (time_pps_getparams(handle, &params) < 0)
LOG_FATAL("time_pps_getparams() failed on %s : %s", path, strerror(errno));
return 0;
}
if (!edge_clear) {
if (!(mode & PPS_CAPTUREASSERT)) {
if (!(mode & PPS_CAPTUREASSERT))
LOG_FATAL("CAPTUREASSERT not supported on %s", path);
return 0;
}
params.mode |= PPS_CAPTUREASSERT;
params.mode &= ~PPS_CAPTURECLEAR;
} else {
if (!(mode & PPS_CAPTURECLEAR)) {
if (!(mode & PPS_CAPTURECLEAR))
LOG_FATAL("CAPTURECLEAR not supported on %s", path);
return 0;
}
params.mode |= PPS_CAPTURECLEAR;
params.mode &= ~PPS_CAPTUREASSERT;
}
if (time_pps_setparams(handle, &params) < 0) {
if (time_pps_setparams(handle, &params) < 0)
LOG_FATAL("time_pps_setparams() failed on %s : %s", path, strerror(errno));
return 0;
}
pps = MallocNew(struct pps_instance);
pps->handle = handle;

View File

@@ -95,7 +95,6 @@ static int shm_poll(RCL_Instance instance)
{
struct timespec receive_ts, clock_ts;
struct shmTime t, *shm;
double offset;
shm = (struct shmTime *)RCL_GetDriverData(instance);
@@ -124,9 +123,8 @@ static int shm_poll(RCL_Instance instance)
UTI_NormaliseTimespec(&clock_ts);
UTI_NormaliseTimespec(&receive_ts);
offset = UTI_DiffTimespecsToDouble(&clock_ts, &receive_ts);
return RCL_AddSample(instance, &receive_ts, offset, t.leap);
return RCL_AddSample(instance, &receive_ts, &clock_ts, t.leap);
}
RefclockDriver RCL_SHM_driver = {

View File

@@ -58,23 +58,63 @@ struct sock_sample {
int magic;
};
/* On 32-bit glibc-based systems enable conversion between timevals using
32-bit and 64-bit time_t to support SOCK clients compiled with different
time_t size than chrony */
#ifdef __GLIBC_PREREQ
#if __GLIBC_PREREQ(2, 34) && __TIMESIZE == 32
#define CONVERT_TIMEVAL 1
#if defined(_TIME_BITS) && _TIME_BITS == 64
typedef int32_t alt_time_t;
typedef int32_t alt_suseconds_t;
#else
typedef int64_t alt_time_t;
typedef int64_t alt_suseconds_t;
#endif
struct alt_timeval {
alt_time_t tv_sec;
alt_suseconds_t tv_usec;
};
#endif
#endif
static void read_sample(int sockfd, int event, void *anything)
{
char buf[sizeof (struct sock_sample) + 16];
struct timespec sys_ts, ref_ts;
struct sock_sample sample;
struct timespec ts;
RCL_Instance instance;
int s;
instance = (RCL_Instance)anything;
s = recv(sockfd, &sample, sizeof (sample), 0);
s = recv(sockfd, buf, sizeof (buf), 0);
if (s < 0) {
DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
return;
}
if (s != sizeof (sample)) {
if (s == sizeof (sample)) {
memcpy(&sample, buf, sizeof (sample));
#ifdef CONVERT_TIMEVAL
} else if (s == sizeof (sample) - sizeof (struct timeval) + sizeof (struct alt_timeval)) {
struct alt_timeval atv;
memcpy(&atv, buf, sizeof (atv));
#ifndef HAVE_LONG_TIME_T
if (atv.tv_sec > INT32_MAX || atv.tv_sec < INT32_MIN ||
atv.tv_usec > INT32_MAX || atv.tv_usec < INT32_MIN) {
DEBUG_LOG("Could not convert 64-bit timeval");
return;
}
#endif
sample.tv.tv_sec = atv.tv_sec;
sample.tv.tv_usec = atv.tv_usec;
DEBUG_LOG("Converted %d-bit timeval", 8 * (int)sizeof (alt_time_t));
memcpy((char *)&sample + sizeof (struct timeval), buf + sizeof (struct alt_timeval),
sizeof (sample) - sizeof (struct timeval));
#endif
} else {
DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
s, (long)sizeof (sample));
return;
@@ -86,13 +126,18 @@ static void read_sample(int sockfd, int event, void *anything)
return;
}
UTI_TimevalToTimespec(&sample.tv, &ts);
UTI_NormaliseTimespec(&ts);
UTI_TimevalToTimespec(&sample.tv, &sys_ts);
UTI_NormaliseTimespec(&sys_ts);
if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset))
return;
UTI_AddDoubleToTimespec(&sys_ts, sample.offset, &ref_ts);
if (sample.pulse) {
RCL_AddPulse(instance, &ts, sample.offset);
RCL_AddPulse(instance, &sys_ts, sample.offset);
} else {
RCL_AddSample(instance, &ts, sample.offset, sample.leap);
RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap);
}
}

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2018
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
*
* 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
@@ -33,6 +33,7 @@
#include "reference.h"
#include "util.h"
#include "conf.h"
#include "leapdb.h"
#include "logging.h"
#include "local.h"
#include "sched.h"
@@ -53,6 +54,8 @@ static int enable_local_stratum;
static int local_stratum;
static int local_orphan;
static double local_distance;
static int local_activate_ok;
static double local_activate;
static struct timespec local_ref_time;
static NTP_Leap our_leap_status;
static int our_leap_sec;
@@ -112,6 +115,9 @@ static void update_drift_file(double, double);
/* Leap second handling mode */
static REF_LeapMode leap_mode;
/* Time of UTC midnight of the upcoming or previous leap second */
static time_t leap_when;
/* Flag indicating the clock was recently corrected for leap second and it may
not have correct time yet (missing 23:59:60 in the UTC time scale) */
static int leap_in_progress;
@@ -119,9 +125,6 @@ static int leap_in_progress;
/* Timer for the leap second handler */
static SCH_TimeoutID leap_timeout_id;
/* Name of a system timezone containing leap seconds occuring at midnight */
static char *leap_tzname;
/* ================================================== */
static LOG_FileID logfileid;
@@ -147,9 +150,11 @@ static SCH_TimeoutID fb_drift_timeout_id;
static double last_ref_update;
static double last_ref_update_interval;
static double last_ref_adjustment;
static int ref_adjustments;
/* ================================================== */
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
/* ================================================== */
@@ -189,7 +194,6 @@ REF_Initialise(void)
FILE *in;
double file_freq_ppm, file_skew_ppm;
double our_frequency_ppm;
int tai_offset;
mode = REF_ModeNormal;
are_we_synchronised = 0;
@@ -205,6 +209,7 @@ REF_Initialise(void)
our_frequency_sd = 0.0;
our_offset_sd = 0.0;
drift_file_age = 0.0;
local_activate_ok = 0;
/* Now see if we can get the drift file opened */
drift_file = CNF_GetDriftFile();
@@ -243,9 +248,11 @@ REF_Initialise(void)
correction_time_ratio = CNF_GetCorrectionTimeRatio();
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan, &local_distance);
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan,
&local_distance, &local_activate);
UTI_ZeroTimespec(&local_ref_time);
leap_when = 0;
leap_timeout_id = 0;
leap_in_progress = 0;
leap_mode = CNF_GetLeapSecMode();
@@ -253,18 +260,6 @@ REF_Initialise(void)
if (leap_mode == REF_LeapModeSystem && !LCL_CanSystemLeap())
leap_mode = REF_LeapModeStep;
leap_tzname = CNF_GetLeapSecTimezone();
if (leap_tzname) {
/* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
if (get_tz_leap(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
get_tz_leap(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35) {
LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
} else {
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
leap_tzname = NULL;
}
}
CNF_GetMakeStep(&make_step_limit, &make_step_threshold);
CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset);
CNF_GetMailOnChange(&do_mail_change, &mail_change_threshold, &mail_change_user);
@@ -282,6 +277,8 @@ REF_Initialise(void)
UTI_ZeroTimespec(&our_ref_time);
last_ref_update = 0.0;
last_ref_update_interval = 0.0;
last_ref_adjustment = 0.0;
ref_adjustments = 0;
LCL_AddParameterChangeHandler(handle_slew, NULL);
@@ -300,6 +297,8 @@ REF_Finalise(void)
update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew);
}
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
Free(fb_drifts);
initialised = 0;
@@ -485,8 +484,7 @@ maybe_log_offset(double offset, time_t now)
abs_offset = fabs(offset);
if (abs_offset > log_change_threshold) {
LOG(LOGS_WARN, "System clock wrong by %.6f seconds, adjustment started",
-offset);
LOG(LOGS_WARN, "System clock wrong by %.6f seconds", -offset);
}
if (do_mail_change &&
@@ -552,8 +550,7 @@ is_offset_ok(double offset)
return 1;
}
offset = fabs(offset);
if (offset > max_offset) {
if (fabs(offset) > max_offset) {
LOG(LOGS_WARN,
"Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ",
-offset, max_offset, !max_offset_ignore ? "exiting" : "ignored");
@@ -584,77 +581,6 @@ is_leap_second_day(time_t when)
/* ================================================== */
static NTP_Leap
get_tz_leap(time_t when, int *tai_offset)
{
static time_t last_tz_leap_check;
static NTP_Leap tz_leap;
static int tz_tai_offset;
struct tm stm, *tm;
time_t t;
char *tz_env, tz_orig[128];
*tai_offset = tz_tai_offset;
/* Do this check at most twice a day */
when = when / (12 * 3600) * (12 * 3600);
if (last_tz_leap_check == when)
return tz_leap;
last_tz_leap_check = when;
tz_leap = LEAP_Normal;
tz_tai_offset = 0;
tm = gmtime(&when);
if (!tm)
return tz_leap;
stm = *tm;
/* Temporarily switch to the timezone containing leap seconds */
tz_env = getenv("TZ");
if (tz_env) {
if (strlen(tz_env) >= sizeof (tz_orig))
return tz_leap;
strcpy(tz_orig, tz_env);
}
setenv("TZ", leap_tzname, 1);
tzset();
/* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
t = mktime(&stm);
if (t != -1)
tz_tai_offset = t - when + 10;
/* Set the time to 23:59:60 and see how it overflows in mktime() */
stm.tm_sec = 60;
stm.tm_min = 59;
stm.tm_hour = 23;
t = mktime(&stm);
if (tz_env)
setenv("TZ", tz_orig, 1);
else
unsetenv("TZ");
tzset();
if (t == -1)
return tz_leap;
if (stm.tm_sec == 60)
tz_leap = LEAP_InsertSecond;
else if (stm.tm_sec == 1)
tz_leap = LEAP_DeleteSecond;
*tai_offset = tz_tai_offset;
return tz_leap;
}
/* ================================================== */
static void
leap_end_timeout(void *arg)
{
@@ -720,10 +646,12 @@ set_leap_timeout(time_t now)
if (!our_leap_sec)
return;
leap_when = (now / (24 * 3600) + 1) * (24 * 3600);
/* Insert leap second at 0:00:00 UTC, delete at 23:59:59 UTC. If the clock
will be corrected by the system, timeout slightly sooner to be sure it
will happen before the system correction. */
when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600);
when.tv_sec = leap_when;
when.tv_nsec = 0;
if (our_leap_sec < 0)
when.tv_sec--;
@@ -740,16 +668,16 @@ set_leap_timeout(time_t now)
static void
update_leap_status(NTP_Leap leap, time_t now, int reset)
{
NTP_Leap tz_leap;
NTP_Leap ldb_leap;
int leap_sec, tai_offset;
leap_sec = 0;
tai_offset = 0;
if (leap_tzname && now) {
tz_leap = get_tz_leap(now, &tai_offset);
if (now) {
ldb_leap = LDB_GetLeap(now, &tai_offset);
if (leap == LEAP_Normal)
leap = tz_leap;
leap = ldb_leap;
}
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
@@ -767,7 +695,7 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
}
if ((leap_sec != our_leap_sec || tai_offset != our_tai_offset)
&& !REF_IsLeapSecondClose()) {
&& !REF_IsLeapSecondClose(NULL, 0.0)) {
our_leap_sec = leap_sec;
our_tai_offset = tai_offset;
@@ -954,6 +882,27 @@ fuzz_ref_time(struct timespec *ts)
/* ================================================== */
static double
get_correction_rate(double offset_sd, double update_interval)
{
/* We want to correct the offset quickly, but we also want to keep the
frequency error caused by the correction itself low.
Define correction rate as the area of the region bounded by the graph of
offset corrected in time. Set the rate so that the time needed to correct
an offset equal to the current sourcestats stddev will be equal to the
update interval multiplied by the correction time ratio (assuming linear
adjustment). The offset and the time needed to make the correction are
inversely proportional.
This is only a suggestion and it's up to the system driver how the
adjustment will be executed. */
return correction_time_ratio * 0.5 * offset_sd * update_interval;
}
/* ================================================== */
void
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
@@ -963,7 +912,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
{
double uncorrected_offset, accumulate_offset, step_offset;
double residual_frequency, local_abs_frequency;
double elapsed, mono_now, update_interval, correction_rate, orig_root_distance;
double elapsed, mono_now, update_interval, orig_root_distance;
struct timespec now, raw_now;
int manual;
@@ -1012,27 +961,12 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
our_residual_freq = residual_frequency;
our_root_delay = root_delay;
our_root_dispersion = root_dispersion;
our_frequency_sd = offset_sd;
our_frequency_sd = frequency_sd;
our_offset_sd = offset_sd;
last_ref_update = mono_now;
last_ref_update_interval = update_interval;
last_offset = offset;
/* We want to correct the offset quickly, but we also want to keep the
frequency error caused by the correction itself low.
Define correction rate as the area of the region bounded by the graph of
offset corrected in time. Set the rate so that the time needed to correct
an offset equal to the current sourcestats stddev will be equal to the
update interval multiplied by the correction time ratio (assuming linear
adjustment). The offset and the time needed to make the correction are
inversely proportional.
This is only a suggestion and it's up to the system driver how the
adjustment will be executed. */
correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
/* Check if the clock should be stepped */
if (is_step_limit_reached(offset, uncorrected_offset)) {
/* Cancel the uncorrected offset and correct the total offset by step */
@@ -1044,7 +978,8 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
}
/* Adjust the clock */
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate);
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset,
get_correction_rate(offset_sd, update_interval));
maybe_log_offset(offset, raw_now.tv_sec);
@@ -1089,6 +1024,27 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
avg2_moving = 1;
avg2_offset = SQUARE(offset);
}
ref_adjustments = 0;
}
/* ================================================== */
int
REF_AdjustReference(double offset, double frequency)
{
double adj_corr_rate, ref_corr_rate, mono_now;
mono_now = SCH_GetLastEventMonoTime();
ref_adjustments++;
adj_corr_rate = get_correction_rate(fabs(offset), mono_now - last_ref_adjustment);
ref_corr_rate = get_correction_rate(our_offset_sd, last_ref_update_interval) /
ref_adjustments;
last_ref_adjustment = mono_now;
return LCL_AccumulateFrequencyAndOffsetNoHandlers(frequency, offset,
MAX(adj_corr_rate, ref_corr_rate));
}
/* ================================================== */
@@ -1180,7 +1136,7 @@ REF_GetReferenceParams
double *root_dispersion
)
{
double dispersion, delta;
double dispersion, delta, distance;
assert(initialised);
@@ -1190,11 +1146,16 @@ REF_GetReferenceParams
dispersion = 0.0;
}
distance = our_root_delay / 2 + dispersion;
if (local_activate == 0.0 || (are_we_synchronised && distance < local_activate))
local_activate_ok = 1;
/* Local reference is active when enabled and the clock is not synchronised
or the root distance exceeds the threshold */
if (are_we_synchronised &&
!(enable_local_stratum && our_root_delay / 2 + dispersion > local_distance)) {
!(enable_local_stratum && local_activate_ok && distance > local_distance)) {
*is_synchronised = 1;
@@ -1206,7 +1167,7 @@ REF_GetReferenceParams
*root_delay = our_root_delay;
*root_dispersion = dispersion;
} else if (enable_local_stratum) {
} else if (enable_local_stratum && local_activate_ok) {
*is_synchronised = 0;
@@ -1290,6 +1251,7 @@ void
REF_ModifyMaxupdateskew(double new_max_update_skew)
{
max_update_skew = new_max_update_skew * 1.0e-6;
LOG(LOGS_INFO, "New maxupdateskew %f ppm", new_max_update_skew);
}
/* ================================================== */
@@ -1299,17 +1261,20 @@ REF_ModifyMakestep(int limit, double threshold)
{
make_step_limit = limit;
make_step_threshold = threshold;
LOG(LOGS_INFO, "New makestep %f %d", threshold, limit);
}
/* ================================================== */
void
REF_EnableLocal(int stratum, double distance, int orphan)
REF_EnableLocal(int stratum, double distance, int orphan, double activate)
{
enable_local_stratum = 1;
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
local_distance = distance;
local_orphan = !!orphan;
local_activate = activate;
LOG(LOGS_INFO, "%s local reference mode", "Enabled");
}
/* ================================================== */
@@ -1318,28 +1283,32 @@ void
REF_DisableLocal(void)
{
enable_local_stratum = 0;
LOG(LOGS_INFO, "%s local reference mode", "Disabled");
}
/* ================================================== */
#define LEAP_SECOND_CLOSE 5
int REF_IsLeapSecondClose(void)
static int
is_leap_close(double t)
{
return leap_when != 0 &&
t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE;
}
/* ================================================== */
int REF_IsLeapSecondClose(struct timespec *ts, double offset)
{
struct timespec now, now_raw;
time_t t;
if (!our_leap_sec)
return 0;
SCH_GetLastEventTime(&now, NULL, &now_raw);
t = now.tv_sec > 0 ? now.tv_sec : -now.tv_sec;
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
if (is_leap_close(now.tv_sec) || is_leap_close(now_raw.tv_sec))
return 1;
t = now_raw.tv_sec > 0 ? now_raw.tv_sec : -now_raw.tv_sec;
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
if (ts && (is_leap_close(ts->tv_sec) || is_leap_close(ts->tv_sec + offset)))
return 1;
return 0;
@@ -1352,7 +1321,7 @@ REF_GetTaiOffset(struct timespec *ts)
{
int tai_offset;
get_tz_leap(ts->tv_sec, &tai_offset);
LDB_GetLeap(ts->tv_sec, &tai_offset);
return tai_offset;
}

View File

@@ -162,6 +162,10 @@ extern void REF_SetManualReference
extern void
REF_SetUnsynchronised(void);
/* Make a small correction of the clock without updating the reference
parameters and calling the clock change handlers */
extern int REF_AdjustReference(double offset, double frequency);
/* Announce a leap second before the full reference update */
extern void REF_UpdateLeapStatus(NTP_Leap leap);
@@ -181,12 +185,12 @@ extern void REF_ModifyMaxupdateskew(double new_max_update_skew);
/* Modify makestep settings */
extern void REF_ModifyMakestep(int limit, double threshold);
extern void REF_EnableLocal(int stratum, double distance, int orphan);
extern void REF_EnableLocal(int stratum, double distance, int orphan, double activate);
extern void REF_DisableLocal(void);
/* Check if current raw or cooked time is close to a leap second
and is better to discard any measurements */
extern int REF_IsLeapSecondClose(void);
/* Check if either of the current raw and cooked time, and optionally a
provided timestamp with an offset, is close to a leap second */
extern int REF_IsLeapSecondClose(struct timespec *ts, double offset);
/* Return TAI-UTC offset corresponding to a time in UTC if available */
extern int REF_GetTaiOffset(struct timespec *ts);

View File

@@ -377,7 +377,7 @@ find_ordered_entry_with_flags(double *x, int n, int index, char *flags)
r = v;
do {
while (l < v && x[l] < piv) l++;
while (x[r] > piv) r--;
while (r > 0 && x[r] > piv) r--;
if (r <= l) break;
EXCH(x[l], x[r]);
l++;

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