Compare commits

..

413 Commits

Author SHA1 Message Date
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
202 changed files with 11027 additions and 3538 deletions

View File

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

79
NEWS
View File

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

47
README
View File

@@ -28,10 +28,8 @@ What will chrony run on?
======================== ========================
The software is known to work on Linux, FreeBSD, NetBSD, macOS and The software is known to work on Linux, FreeBSD, NetBSD, macOS and
Solaris. Closely related systems may work too. Any other system will illumos. Closely related systems may work too. Any other system will
likely require a porting exercise. You would need to start from one likely require a porting exercise.
of the existing system-specific drivers and look into the quirks of
certain system calls and the kernel on your target system.
How do I set it up? How do I set it up?
=================== ===================
@@ -55,24 +53,20 @@ Where are new versions announced?
================================= =================================
There is a low volume mailing list where new versions and other There is a low volume mailing list where new versions and other
important news relating to chrony is announced. You can join this list important news relating to chrony are announced. You can join this list
by sending mail with the subject "subscribe" to by sending mail with the subject "subscribe" to
chrony-announce-request@chrony.tuxfamily.org chrony-announce-request@chrony.tuxfamily.org
These messages will be copied to chrony-users (see below).
How can I get support for chrony? How can I get support for chrony?
and where can I discuss new features, possible bugs etc? =================================
========================================================
There are 3 mailing lists relating to chrony. chrony-announce was There are two other mailing lists relating to chrony. chrony-users is a
mentioned above. chrony-users is a users' discussion list, e.g. for discussion list for users, e.g. for questions about chrony configuration
general questions and answers about using chrony. chrony-dev is a more and bug reports. chrony-dev is a more technical list for developers,
technical list, e.g. for discussing how new features should be e.g. for submitting patches and discussing how new features should be
implemented, exchange of information between developers etc. To implemented. To subscribe to either of these lists, send a message with
subscribe to either of these lists, send a message with the subject the subject "subscribe" to
"subscribe" to
chrony-users-request@chrony.tuxfamily.org chrony-users-request@chrony.tuxfamily.org
or or
@@ -80,12 +74,6 @@ chrony-dev-request@chrony.tuxfamily.org
as applicable. as applicable.
When you are reporting a bug, please send us all the information you can.
Unfortunately, chrony has proven to be one of those programs where it is very
difficult to reproduce bugs in a different environment. So we may have to
interact with you quite a lot to obtain enough extra logging and tracing to
pin-point the problem in some cases. Please be patient and plan for this!
License License
======= =======
@@ -100,12 +88,13 @@ Miroslav Lichvar <mlichvar@redhat.com>
Acknowledgements Acknowledgements
================ ================
In writing the chronyd program, extensive use has been made of RFC 1305 In writing the chronyd program, extensive use has been made of the NTPv3 (RFC
and RFC 5905, written by David Mills. The source code of the NTP reference 1305) and NTPv4 (RFC 5905) specification. The source code of the xntpd/ntpd
implementation has been used to check the details of the protocol. implementation written by Dennis Fergusson, Lars Mathiesen, David Mills, and
others has been used to check the details of the protocol.
The following people have provided patches and other major contributions The following people have provided patches and other major contributions
to the program : to chrony:
Lonnie Abelbeck <lonnie@abelbeck.com> Lonnie Abelbeck <lonnie@abelbeck.com>
Benny Lyne Amorsen <benny@amorsen.dk> Benny Lyne Amorsen <benny@amorsen.dk>
@@ -119,8 +108,10 @@ Erik Bryer <ebryer@spots.ab.ca>
Jonathan Cameron <jic23@cam.ac.uk> Jonathan Cameron <jic23@cam.ac.uk>
Bryan Christianson <bryan@whatroute.net> Bryan Christianson <bryan@whatroute.net>
Juliusz Chroboczek <jch@pps.jussieu.fr> Juliusz Chroboczek <jch@pps.jussieu.fr>
Kamil Dudka <kdudka@redhat.com>
Christian Ehrhardt <christian.ehrhardt@canonical.com> Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com> Paul Elliott <pelliott@io.com>
Robert Fairley <rfairley@redhat.com>
Stefan R. Filipek <srfilipek@gmail.com> Stefan R. Filipek <srfilipek@gmail.com>
Mike Fleetwood <mike@rockover.demon.co.uk> Mike Fleetwood <mike@rockover.demon.co.uk>
Alexander Gretencord <arutha@gmx.de> Alexander Gretencord <arutha@gmx.de>
@@ -134,6 +125,7 @@ Jachym Holecek <jakym@volny.cz>
Håkan Johansson <f96hajo@chalmers.se> Håkan Johansson <f96hajo@chalmers.se>
Jim Knoble <jmknoble@pobox.com> Jim Knoble <jmknoble@pobox.com>
Antti Jrvinen <costello@iki.fi> Antti Jrvinen <costello@iki.fi>
Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Eric Lammerts <eric@lammerts.org> Eric Lammerts <eric@lammerts.org>
Stefan Lucke <stefan@lucke.in-berlin.de> Stefan Lucke <stefan@lucke.in-berlin.de>
Victor Lum <viclum@vanu.com> Victor Lum <viclum@vanu.com>
@@ -147,6 +139,8 @@ Denny Page <dennypage@me.com>
Chris Perl <cperl@janestreet.com> Chris Perl <cperl@janestreet.com>
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr> Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Andreas Piesk <apiesk@virbus.de> Andreas Piesk <apiesk@virbus.de>
Baruch Siach <baruch@tkos.co.il>
Foster Snowhill <forst@forstwoof.ru>
Andreas Steinmetz <ast@domdv.de> Andreas Steinmetz <ast@domdv.de>
NAKAMURA Takumi <takumi@ps.sakura.ne.jp> NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
Timo Teras <timo.teras@iki.fi> Timo Teras <timo.teras@iki.fi>
@@ -158,6 +152,7 @@ Bernhard M. Wiedemann <bwiedemann@suse.de>
Joachim Wiedorn <ad_debian@joonet.de> Joachim Wiedorn <ad_debian@joonet.de>
Ralf Wildenhues <Ralf.Wildenhues@gmx.de> Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> Ulrich Windl <ulrich.windl@rz.uni-regensburg.de>
Michael Witten <mfwitten@gmail.com>
Doug Woodward <dougw@whistler.com> Doug Woodward <dougw@whistler.com>
Thomas Zajic <zlatko@zlatko.fdns.net> Thomas Zajic <zlatko@zlatko.fdns.net>

121
candm.h
View File

@@ -103,8 +103,13 @@
#define REQ_ONOFFLINE 63 #define REQ_ONOFFLINE 63
#define REQ_ADD_SOURCE 64 #define REQ_ADD_SOURCE 64
#define REQ_NTP_SOURCE_NAME 65 #define REQ_NTP_SOURCE_NAME 65
#define REQ_RESET 66 #define REQ_RESET_SOURCES 66
#define N_REQUEST_TYPES 67 #define REQ_AUTH_DATA 67
#define REQ_CLIENT_ACCESSES_BY_INDEX3 68
#define REQ_SELECT_DATA 69
#define REQ_RELOAD_SOURCES 70
#define REQ_DOFFSET2 71
#define N_REQUEST_TYPES 72
/* Structure used to exchange timespecs independent of time_t size */ /* Structure used to exchange timespecs independent of time_t size */
typedef struct { typedef struct {
@@ -264,10 +269,12 @@ typedef struct {
#define REQ_ADDSRC_INTERLEAVED 0x80 #define REQ_ADDSRC_INTERLEAVED 0x80
#define REQ_ADDSRC_BURST 0x100 #define REQ_ADDSRC_BURST 0x100
#define REQ_ADDSRC_NTS 0x200 #define REQ_ADDSRC_NTS 0x200
#define REQ_ADDSRC_COPY 0x400
#define REQ_ADDSRC_EF_EXP1 0x800
typedef struct { typedef struct {
uint32_t type; uint32_t type;
int8_t name[256]; uint8_t name[256];
uint32_t port; uint32_t port;
int32_t minpoll; int32_t minpoll;
int32_t maxpoll; int32_t maxpoll;
@@ -288,7 +295,8 @@ typedef struct {
Float offset; Float offset;
uint32_t flags; uint32_t flags;
int32_t filter_length; int32_t filter_length;
uint32_t reserved[3]; uint32_t cert_set;
uint32_t reserved[2];
int32_t EOR; int32_t EOR;
} REQ_NTP_Source; } REQ_NTP_Source;
@@ -303,8 +311,7 @@ typedef struct {
} REQ_Dfreq; } REQ_Dfreq;
typedef struct { typedef struct {
int32_t sec; Float doffset;
int32_t usec;
int32_t EOR; int32_t EOR;
} REQ_Doffset; } REQ_Doffset;
@@ -320,6 +327,8 @@ typedef struct {
typedef struct { typedef struct {
uint32_t first_index; uint32_t first_index;
uint32_t n_clients; uint32_t n_clients;
uint32_t min_hits;
uint32_t reset;
int32_t EOR; int32_t EOR;
} REQ_ClientAccessesByIndex; } REQ_ClientAccessesByIndex;
@@ -351,6 +360,16 @@ typedef struct {
int32_t EOR; int32_t EOR;
} REQ_NTPSourceName; } REQ_NTPSourceName;
typedef struct {
IPAddr ip_addr;
int32_t EOR;
} REQ_AuthData;
typedef struct {
uint32_t index;
int32_t EOR;
} REQ_SelectData;
/* ================================================== */ /* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1 #define PKT_TYPE_CMD_REQUEST 1
@@ -387,9 +406,10 @@ typedef struct {
domain socket. domain socket.
Version 6 (no authentication) : changed format of client accesses by index Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types) and manual timestamp, added new fields and (two times), delta offset, and manual timestamp, added new fields and
flags to NTP source request and report, made length of manual list constant, flags to NTP source request and report, made length of manual list constant,
added new commands: ntpdata, refresh, serverstats, shutdown added new commands: authdata, ntpdata, onoffline, refresh, reset,
selectdata, serverstats, shutdown, sourcename
*/ */
#define PROTO_VERSION_NUMBER 6 #define PROTO_VERSION_NUMBER 6
@@ -403,8 +423,8 @@ typedef struct {
#define PROTO_VERSION_PADDING 6 #define PROTO_VERSION_PADDING 6
/* The maximum length of padding in request packet, currently /* The maximum length of padding in request packet, currently
defined by MANUAL_LIST */ defined by CLIENT_ACCESSES_BY_INDEX3 */
#define MAX_PADDING_LENGTH 396 #define MAX_PADDING_LENGTH 484
/* ================================================== */ /* ================================================== */
@@ -453,7 +473,9 @@ typedef struct {
REQ_ReselectDistance reselect_distance; REQ_ReselectDistance reselect_distance;
REQ_SmoothTime smoothtime; REQ_SmoothTime smoothtime;
REQ_NTPData ntp_data; REQ_NTPData ntp_data;
REQ_NTPData ntp_source_name; REQ_NTPSourceName ntp_source_name;
REQ_AuthData auth_data;
REQ_SelectData select_data;
} data; /* Command specific parameters */ } data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the /* Padding used to prevent traffic amplification. It only defines the
@@ -491,7 +513,12 @@ typedef struct {
#define RPY_MANUAL_TIMESTAMP2 17 #define RPY_MANUAL_TIMESTAMP2 17
#define RPY_MANUAL_LIST2 18 #define RPY_MANUAL_LIST2 18
#define RPY_NTP_SOURCE_NAME 19 #define RPY_NTP_SOURCE_NAME 19
#define N_REPLY_TYPES 20 #define RPY_AUTH_DATA 20
#define RPY_CLIENT_ACCESSES_BY_INDEX3 21
#define RPY_SERVER_STATS2 22
#define RPY_SELECT_DATA 23
#define RPY_SERVER_STATS3 24
#define N_REPLY_TYPES 25
/* Status codes */ /* Status codes */
#define STT_SUCCESS 0 #define STT_SUCCESS 0
@@ -504,8 +531,7 @@ typedef struct {
#define STT_BADSUBNET 7 #define STT_BADSUBNET 7
#define STT_ACCESSALLOWED 8 #define STT_ACCESSALLOWED 8
#define STT_ACCESSDENIED 9 #define STT_ACCESSDENIED 9
/* Deprecated */ #define STT_NOHOSTACCESS 10 /* Deprecated */
#define STT_NOHOSTACCESS 10
#define STT_SOURCEALREADYKNOWN 11 #define STT_SOURCEALREADYKNOWN 11
#define STT_TOOMANYSOURCES 12 #define STT_TOOMANYSOURCES 12
#define STT_NORTC 13 #define STT_NORTC 13
@@ -530,17 +556,12 @@ typedef struct {
#define RPY_SD_MD_PEER 1 #define RPY_SD_MD_PEER 1
#define RPY_SD_MD_REF 2 #define RPY_SD_MD_REF 2
#define RPY_SD_ST_SYNC 0 #define RPY_SD_ST_SELECTED 0
#define RPY_SD_ST_UNREACH 1 #define RPY_SD_ST_NONSELECTABLE 1
#define RPY_SD_ST_FALSETICKER 2 #define RPY_SD_ST_FALSETICKER 2
#define RPY_SD_ST_JITTERY 3 #define RPY_SD_ST_JITTERY 3
#define RPY_SD_ST_CANDIDATE 4 #define RPY_SD_ST_UNSELECTED 4
#define RPY_SD_ST_OUTLIER 5 #define RPY_SD_ST_SELECTABLE 5
#define RPY_SD_FLAG_NOSELECT 0x1
#define RPY_SD_FLAG_PREFER 0x2
#define RPY_SD_FLAG_TRUST 0x4
#define RPY_SD_FLAG_REQUIRE 0x8
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
@@ -609,14 +630,17 @@ typedef struct {
typedef struct { typedef struct {
IPAddr ip; IPAddr ip;
uint32_t ntp_hits; uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits; uint32_t cmd_hits;
uint32_t ntp_drops; uint32_t ntp_drops;
uint32_t nke_drops;
uint32_t cmd_drops; uint32_t cmd_drops;
int8_t ntp_interval; int8_t ntp_interval;
int8_t nke_interval;
int8_t cmd_interval; int8_t cmd_interval;
int8_t ntp_timeout_interval; int8_t ntp_timeout_interval;
int8_t pad;
uint32_t last_ntp_hit_ago; uint32_t last_ntp_hit_ago;
uint32_t last_nke_hit_ago;
uint32_t last_cmd_hit_ago; uint32_t last_cmd_hit_ago;
} RPY_ClientAccesses_Client; } RPY_ClientAccesses_Client;
@@ -630,10 +654,16 @@ typedef struct {
typedef struct { typedef struct {
uint32_t ntp_hits; uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits; uint32_t cmd_hits;
uint32_t ntp_drops; uint32_t ntp_drops;
uint32_t nke_drops;
uint32_t cmd_drops; uint32_t cmd_drops;
uint32_t log_drops; uint32_t log_drops;
uint32_t ntp_auth_hits;
uint32_t ntp_interleaved_hits;
uint32_t ntp_timestamps;
uint32_t ntp_span_seconds;
int32_t EOR; int32_t EOR;
} RPY_ServerStats; } RPY_ServerStats;
@@ -708,10 +738,49 @@ typedef struct {
} RPY_NTPData; } RPY_NTPData;
typedef struct { typedef struct {
int8_t name[256]; uint8_t name[256];
int32_t EOR; int32_t EOR;
} RPY_NTPSourceName; } RPY_NTPSourceName;
#define RPY_AD_MD_NONE 0
#define RPY_AD_MD_SYMMETRIC 1
#define RPY_AD_MD_NTS 2
typedef struct {
uint16_t mode;
uint16_t key_type;
uint32_t key_id;
uint16_t key_length;
uint16_t ke_attempts;
uint32_t last_ke_ago;
uint16_t cookies;
uint16_t cookie_length;
uint16_t nak;
uint16_t pad;
int32_t EOR;
} RPY_AuthData;
#define RPY_SD_OPTION_NOSELECT 0x1
#define RPY_SD_OPTION_PREFER 0x2
#define RPY_SD_OPTION_TRUST 0x4
#define RPY_SD_OPTION_REQUIRE 0x8
typedef struct {
uint32_t ref_id;
IPAddr ip_addr;
uint8_t state_char;
uint8_t authentication;
uint8_t 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 { typedef struct {
uint8_t version; uint8_t version;
uint8_t pkt_type; uint8_t pkt_type;
@@ -742,6 +811,8 @@ typedef struct {
RPY_Smoothing smoothing; RPY_Smoothing smoothing;
RPY_NTPData ntp_data; RPY_NTPData ntp_data;
RPY_NTPSourceName ntp_source_name; RPY_NTPSourceName ntp_source_name;
RPY_AuthData auth_data;
RPY_SelectData select_data;
} data; /* Reply specific parameters */ } data; /* Reply specific parameters */
} CMD_Reply; } CMD_Reply;

694
client.c

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009, 2015-2017 * Copyright (C) Miroslav Lichvar 2009, 2015-2017, 2021
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -38,28 +38,24 @@
#include "array.h" #include "array.h"
#include "clientlog.h" #include "clientlog.h"
#include "conf.h" #include "conf.h"
#include "local.h"
#include "memory.h" #include "memory.h"
#include "ntp.h" #include "ntp.h"
#include "reports.h" #include "reports.h"
#include "util.h" #include "util.h"
#include "logging.h" #include "logging.h"
#define MAX_SERVICES 3
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
uint32_t last_ntp_hit; uint32_t last_hit[MAX_SERVICES];
uint32_t last_cmd_hit; uint32_t hits[MAX_SERVICES];
uint32_t ntp_hits; uint16_t drops[MAX_SERVICES];
uint32_t cmd_hits; uint16_t tokens[MAX_SERVICES];
uint16_t ntp_drops; int8_t rate[MAX_SERVICES];
uint16_t cmd_drops;
uint16_t ntp_tokens;
uint16_t cmd_tokens;
int8_t ntp_rate;
int8_t cmd_rate;
int8_t ntp_timeout_rate; int8_t ntp_timeout_rate;
uint8_t flags; uint8_t drop_flags;
NTP_int64 ntp_rx_ts;
NTP_int64 ntp_tx_ts;
} Record; } Record;
/* Hash table of records, there is a fixed number of records per slot */ /* Hash table of records, there is a fixed number of records per slot */
@@ -104,15 +100,12 @@ static uint32_t ts_offset;
#define MIN_LIMIT_BURST 1 #define MIN_LIMIT_BURST 1
#define MAX_LIMIT_BURST 255 #define MAX_LIMIT_BURST 255
static uint16_t max_ntp_tokens; static uint16_t max_tokens[MAX_SERVICES];
static uint16_t max_cmd_tokens; static uint16_t tokens_per_hit[MAX_SERVICES];
static uint16_t ntp_tokens_per_packet;
static uint16_t cmd_tokens_per_packet;
/* Reduction of token rates to avoid overflow of 16-bit counters. Negative /* Reduction of token rates to avoid overflow of 16-bit counters. Negative
shift is used for coarse limiting with intervals shorter than -TS_FRAC. */ shift is used for coarse limiting with intervals shorter than -TS_FRAC. */
static int ntp_token_shift; static int token_shift[MAX_SERVICES];
static int cmd_token_shift;
/* Rates at which responses are randomly allowed (in log2) when the /* Rates at which responses are randomly allowed (in log2) when the
buckets don't have enough tokens. This is necessary in order to buckets don't have enough tokens. This is necessary in order to
@@ -122,23 +115,51 @@ static int cmd_token_shift;
#define MIN_LEAK_RATE 1 #define MIN_LEAK_RATE 1
#define MAX_LEAK_RATE 4 #define MAX_LEAK_RATE 4
static int ntp_leak_rate; static int leak_rate[MAX_SERVICES];
static int cmd_leak_rate;
/* Flag indicating whether the last response was dropped */ /* Limit intervals in log2 */
#define FLAG_NTP_DROPPED 0x1 static int limit_interval[MAX_SERVICES];
/* NTP limit interval in log2 */
static int ntp_limit_interval;
/* Flag indicating whether facility is turned on or not */ /* Flag indicating whether facility is turned on or not */
static int active; static int active;
/* RX and TX timestamp saved for clients using interleaved mode */
typedef struct {
uint64_t rx_ts;
uint16_t flags;
uint16_t slew_epoch;
int32_t tx_ts_offset;
} NtpTimestamps;
/* Flags for NTP timestamps */
#define NTPTS_DISABLED 1
#define NTPTS_VALID_TX 2
/* RX->TX map using a circular buffer with ordered timestamps */
typedef struct {
ARR_Instance timestamps;
uint32_t first;
uint32_t size;
uint32_t max_size;
uint32_t cached_index;
uint64_t cached_rx_ts;
uint16_t slew_epoch;
double slew_offset;
} NtpTimestampMap;
static NtpTimestampMap ntp_ts_map;
/* Maximum interval of NTP timestamps in future after a backward step */
#define NTPTS_FUTURE_LIMIT (1LL << 32) /* 1 second */
/* Maximum number of timestamps moved in the array to insert a new timestamp */
#define NTPTS_INSERT_LIMIT 64
/* Global statistics */ /* Global statistics */
static uint32_t total_ntp_hits; static uint32_t total_hits[MAX_SERVICES];
static uint32_t total_cmd_hits; static uint32_t total_drops[MAX_SERVICES];
static uint32_t total_ntp_drops; static uint32_t total_ntp_auth_hits;
static uint32_t total_cmd_drops; static uint32_t total_ntp_interleaved_hits;
static uint32_t total_record_drops; static uint32_t total_record_drops;
#define NSEC_PER_SEC 1000000000U #define NSEC_PER_SEC 1000000000U
@@ -146,6 +167,8 @@ static uint32_t total_record_drops;
/* ================================================== */ /* ================================================== */
static int expand_hashtable(void); static int expand_hashtable(void);
static void handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything);
/* ================================================== */ /* ================================================== */
@@ -161,12 +184,28 @@ compare_ts(uint32_t x, uint32_t y)
/* ================================================== */ /* ================================================== */
static int
compare_total_hits(Record *x, Record *y)
{
uint32_t x_hits, y_hits;
int i;
for (i = 0, x_hits = y_hits = 0; i < MAX_SERVICES; i++) {
x_hits += x->hits[i];
y_hits += y->hits[i];
}
return x_hits > y_hits ? 1 : -1;
}
/* ================================================== */
static Record * static Record *
get_record(IPAddr *ip) get_record(IPAddr *ip)
{ {
unsigned int first, i; uint32_t last_hit = 0, oldest_hit = 0;
time_t last_hit, oldest_hit = 0;
Record *record, *oldest_record; Record *record, *oldest_record;
unsigned int first, i, j;
if (!active || (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6)) if (!active || (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6))
return NULL; return NULL;
@@ -184,12 +223,13 @@ get_record(IPAddr *ip)
if (record->ip_addr.family == IPADDR_UNSPEC) if (record->ip_addr.family == IPADDR_UNSPEC)
break; break;
last_hit = compare_ts(record->last_ntp_hit, record->last_cmd_hit) > 0 ? for (j = 0; j < MAX_SERVICES; j++) {
record->last_ntp_hit : record->last_cmd_hit; if (j == 0 || compare_ts(last_hit, record->last_hit[j]) < 0)
last_hit = record->last_hit[j];
}
if (!oldest_record || compare_ts(oldest_hit, last_hit) > 0 || if (!oldest_record || compare_ts(oldest_hit, last_hit) > 0 ||
(oldest_hit == last_hit && record->ntp_hits + record->cmd_hits < (oldest_hit == last_hit && compare_total_hits(oldest_record, record) > 0)) {
oldest_record->ntp_hits + oldest_record->cmd_hits)) {
oldest_record = record; oldest_record = record;
oldest_hit = last_hit; oldest_hit = last_hit;
} }
@@ -211,16 +251,18 @@ get_record(IPAddr *ip)
} }
record->ip_addr = *ip; record->ip_addr = *ip;
record->last_ntp_hit = record->last_cmd_hit = INVALID_TS; for (i = 0; i < MAX_SERVICES; i++)
record->ntp_hits = record->cmd_hits = 0; record->last_hit[i] = INVALID_TS;
record->ntp_drops = record->cmd_drops = 0; for (i = 0; i < MAX_SERVICES; i++)
record->ntp_tokens = max_ntp_tokens; record->hits[i] = 0;
record->cmd_tokens = max_cmd_tokens; for (i = 0; i < MAX_SERVICES; i++)
record->ntp_rate = record->cmd_rate = INVALID_RATE; record->drops[i] = 0;
for (i = 0; i < MAX_SERVICES; i++)
record->tokens[i] = max_tokens[i];
for (i = 0; i < MAX_SERVICES; i++)
record->rate[i] = INVALID_RATE;
record->ntp_timeout_rate = INVALID_RATE; record->ntp_timeout_rate = INVALID_RATE;
record->flags = 0; record->drop_flags = 0;
UTI_ZeroNtp64(&record->ntp_rx_ts);
UTI_ZeroNtp64(&record->ntp_tx_ts);
return record; return record;
} }
@@ -306,39 +348,56 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
void void
CLG_Initialise(void) CLG_Initialise(void)
{ {
int interval, burst, leak_rate; int i, interval, burst, lrate, slots2;
max_ntp_tokens = max_cmd_tokens = 0; for (i = 0; i < MAX_SERVICES; i++) {
ntp_tokens_per_packet = cmd_tokens_per_packet = 0; max_tokens[i] = 0;
ntp_token_shift = cmd_token_shift = 0; tokens_per_hit[i] = 0;
ntp_leak_rate = cmd_leak_rate = 0; token_shift[i] = 0;
ntp_limit_interval = MIN_LIMIT_INTERVAL; leak_rate[i] = 0;
limit_interval[i] = MIN_LIMIT_INTERVAL;
if (CNF_GetNTPRateLimit(&interval, &burst, &leak_rate)) { switch (i) {
set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet, case CLG_NTP:
&ntp_token_shift); if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate))
ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE); continue;
ntp_limit_interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL); break;
} case CLG_NTSKE:
if (!CNF_GetNtsRateLimit(&interval, &burst, &lrate))
continue;
break;
case CLG_CMDMON:
if (!CNF_GetCommandRateLimit(&interval, &burst, &lrate))
continue;
break;
default:
assert(0);
}
if (CNF_GetCommandRateLimit(&interval, &burst, &leak_rate)) { set_bucket_params(interval, burst, &max_tokens[i], &tokens_per_hit[i], &token_shift[i]);
set_bucket_params(interval, burst, &max_cmd_tokens, &cmd_tokens_per_packet, leak_rate[i] = CLAMP(MIN_LEAK_RATE, lrate, MAX_LEAK_RATE);
&cmd_token_shift); limit_interval[i] = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
cmd_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
} }
active = !CNF_GetNoClientLog(); active = !CNF_GetNoClientLog();
if (!active) { if (!active) {
if (ntp_leak_rate || cmd_leak_rate) for (i = 0; i < MAX_SERVICES; i++) {
LOG_FATAL("ratelimit cannot be used with noclientlog"); if (leak_rate[i] != 0)
LOG_FATAL("Rate limiting cannot be enabled with noclientlog");
}
return; return;
} }
/* Calculate the maximum number of slots that can be allocated in the /* Calculate the maximum number of slots that can be allocated in the
configured memory limit. Take into account expanding of the hash configured memory limit. Take into account expanding of the hash
table where two copies exist at the same time. */ table where two copies exist at the same time. */
max_slots = CNF_GetClientLogLimit() / (sizeof (Record) * SLOT_SIZE * 3 / 2); max_slots = CNF_GetClientLogLimit() /
((sizeof (Record) + sizeof (NtpTimestamps)) * SLOT_SIZE * 3 / 2);
max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS); max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS);
for (slots2 = 0; 1U << (slots2 + 1) <= max_slots; slots2++)
;
DEBUG_LOG("Max records %u", 1U << (slots2 + SLOT_BITS));
slots = 0; slots = 0;
records = NULL; records = NULL;
@@ -347,6 +406,17 @@ CLG_Initialise(void)
UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset)); UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC); ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC);
ntp_ts_map.timestamps = NULL;
ntp_ts_map.first = 0;
ntp_ts_map.size = 0;
ntp_ts_map.max_size = 1U << (slots2 + SLOT_BITS);
ntp_ts_map.cached_index = 0;
ntp_ts_map.cached_rx_ts = 0ULL;
ntp_ts_map.slew_epoch = 0;
ntp_ts_map.slew_offset = 0.0;
LCL_AddParameterChangeHandler(handle_slew, NULL);
} }
/* ================================================== */ /* ================================================== */
@@ -358,6 +428,10 @@ CLG_Finalise(void)
return; return;
ARR_DestroyInstance(records); ARR_DestroyInstance(records);
if (ntp_ts_map.timestamps)
ARR_DestroyInstance(ntp_ts_map.timestamps);
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
} }
/* ================================================== */ /* ================================================== */
@@ -380,30 +454,33 @@ get_ts_from_timespec(struct timespec *ts)
/* ================================================== */ /* ================================================== */
static void static void
update_record(struct timespec *now, uint32_t *last_hit, uint32_t *hits, update_record(CLG_Service service, Record *record, struct timespec *now)
uint16_t *tokens, uint32_t max_tokens, int token_shift, int8_t *rate)
{ {
uint32_t interval, now_ts, prev_hit, new_tokens; uint32_t interval, now_ts, prev_hit, tokens;
int interval2; int interval2, tshift, mtokens;
int8_t *rate;
now_ts = get_ts_from_timespec(now); now_ts = get_ts_from_timespec(now);
prev_hit = *last_hit; prev_hit = record->last_hit[service];
*last_hit = now_ts; record->last_hit[service] = now_ts;
(*hits)++; record->hits[service]++;
interval = now_ts - prev_hit; interval = now_ts - prev_hit;
if (prev_hit == INVALID_TS || (int32_t)interval < 0) if (prev_hit == INVALID_TS || (int32_t)interval < 0)
return; return;
if (token_shift >= 0) tshift = token_shift[service];
new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift); mtokens = max_tokens[service];
else if (now_ts - prev_hit > max_tokens)
new_tokens = max_tokens; if (tshift >= 0)
tokens = (now_ts >> tshift) - (prev_hit >> tshift);
else if (now_ts - prev_hit > mtokens)
tokens = mtokens;
else else
new_tokens = (now_ts - prev_hit) << -token_shift; tokens = (now_ts - prev_hit) << -tshift;
*tokens = MIN(*tokens + new_tokens, max_tokens); record->tokens[service] = MIN(record->tokens[service] + tokens, mtokens);
/* Convert the interval to scaled and rounded log2 */ /* Convert the interval to scaled and rounded log2 */
if (interval) { if (interval) {
@@ -418,6 +495,11 @@ update_record(struct timespec *now, uint32_t *last_hit, uint32_t *hits,
interval2 = -RATE_SCALE * (TS_FRAC + 1); interval2 = -RATE_SCALE * (TS_FRAC + 1);
} }
/* For the NTP service, update one of the two rates depending on whether
the previous request of the client had a reply or it timed out */
rate = service == CLG_NTP && record->drop_flags & (1U << service) ?
&record->ntp_timeout_rate : &record->rate[service];
/* Update the rate in a rough approximation of exponential moving average */ /* Update the rate in a rough approximation of exponential moving average */
if (*rate == INVALID_RATE) { if (*rate == INVALID_RATE) {
*rate = -interval2; *rate = -interval2;
@@ -457,50 +539,33 @@ CLG_GetClientIndex(IPAddr *client)
/* ================================================== */ /* ================================================== */
int static void
CLG_LogNTPAccess(IPAddr *client, struct timespec *now) check_service_number(CLG_Service service)
{ {
Record *record; assert(service >= 0 && service <= MAX_SERVICES);
total_ntp_hits++;
record = get_record(client);
if (record == NULL)
return -1;
/* Update one of the two rates depending on whether the previous request
of the client had a reply or it timed out */
update_record(now, &record->last_ntp_hit, &record->ntp_hits,
&record->ntp_tokens, max_ntp_tokens, ntp_token_shift,
record->flags & FLAG_NTP_DROPPED ?
&record->ntp_timeout_rate : &record->ntp_rate);
DEBUG_LOG("NTP hits %"PRIu32" rate %d trate %d tokens %d",
record->ntp_hits, record->ntp_rate, record->ntp_timeout_rate,
record->ntp_tokens);
return get_index(record);
} }
/* ================================================== */ /* ================================================== */
int int
CLG_LogCommandAccess(IPAddr *client, struct timespec *now) CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now)
{ {
Record *record; Record *record;
total_cmd_hits++; check_service_number(service);
total_hits[service]++;
record = get_record(client); record = get_record(client);
if (record == NULL) if (record == NULL)
return -1; return -1;
update_record(now, &record->last_cmd_hit, &record->cmd_hits, update_record(service, record, now);
&record->cmd_tokens, max_cmd_tokens, cmd_token_shift,
&record->cmd_rate);
DEBUG_LOG("Cmd hits %"PRIu32" rate %d tokens %d", DEBUG_LOG("service %d hits %"PRIu32" rate %d trate %d tokens %d",
record->cmd_hits, record->cmd_rate, record->cmd_tokens); (int)service, record->hits[service], record->rate[service],
service == CLG_NTP ? record->ntp_timeout_rate : INVALID_RATE,
record->tokens[service]);
return get_index(record); return get_index(record);
} }
@@ -530,83 +595,53 @@ limit_response_random(int leak_rate)
/* ================================================== */ /* ================================================== */
int int
CLG_LimitNTPResponseRate(int index) CLG_LimitServiceRate(CLG_Service service, int index)
{ {
Record *record; Record *record;
int drop; int drop;
if (!ntp_tokens_per_packet) check_service_number(service);
if (tokens_per_hit[service] == 0)
return 0; return 0;
record = ARR_GetElement(records, index); record = ARR_GetElement(records, index);
record->flags &= ~FLAG_NTP_DROPPED; record->drop_flags &= ~(1U << service);
if (record->ntp_tokens >= ntp_tokens_per_packet) { if (record->tokens[service] >= tokens_per_hit[service]) {
record->ntp_tokens -= ntp_tokens_per_packet; record->tokens[service] -= tokens_per_hit[service];
return 0; return 0;
} }
drop = limit_response_random(ntp_leak_rate); drop = limit_response_random(leak_rate[service]);
/* Poorly implemented clients may send new requests at even a higher rate /* Poorly implemented NTP clients can send requests at a higher rate
when they are not getting replies. If the request rate seems to be more when they are not getting replies. If the request rate seems to be more
than twice as much as when replies are sent, give up on rate limiting to than twice as much as when replies are sent, give up on rate limiting to
reduce the amount of traffic. Invert the sense of the leak to respond to reduce the amount of traffic. Invert the sense of the leak to respond to
most of the requests, but still keep the estimated rate updated. */ most of the requests, but still keep the estimated rate updated. */
if (record->ntp_timeout_rate != INVALID_RATE && if (service == CLG_NTP && record->ntp_timeout_rate != INVALID_RATE &&
record->ntp_timeout_rate > record->ntp_rate + RATE_SCALE) record->ntp_timeout_rate > record->rate[service] + RATE_SCALE)
drop = !drop; drop = !drop;
if (!drop) { if (!drop) {
record->ntp_tokens = 0; record->tokens[service] = 0;
return 0; return 0;
} }
record->flags |= FLAG_NTP_DROPPED; record->drop_flags |= 1U << service;
record->ntp_drops++; record->drops[service]++;
total_ntp_drops++; total_drops[service]++;
return 1; return 1;
} }
/* ================================================== */ /* ================================================== */
int void
CLG_LimitCommandResponseRate(int index) CLG_LogAuthNtpRequest(void)
{ {
Record *record; total_ntp_auth_hits++;
if (!cmd_tokens_per_packet)
return 0;
record = ARR_GetElement(records, index);
if (record->cmd_tokens >= cmd_tokens_per_packet) {
record->cmd_tokens -= cmd_tokens_per_packet;
return 0;
}
if (!limit_response_random(cmd_leak_rate)) {
record->cmd_tokens = 0;
return 0;
}
record->cmd_drops++;
total_cmd_drops++;
return 1;
}
/* ================================================== */
void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
{
Record *record;
record = ARR_GetElement(records, index);
*rx_ts = &record->ntp_rx_ts;
*tx_ts = &record->ntp_tx_ts;
} }
/* ================================================== */ /* ================================================== */
@@ -614,7 +649,331 @@ void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
int int
CLG_GetNtpMinPoll(void) CLG_GetNtpMinPoll(void)
{ {
return ntp_limit_interval; return limit_interval[CLG_NTP];
}
/* ================================================== */
static NtpTimestamps *
get_ntp_tss(uint32_t index)
{
return ARR_GetElement(ntp_ts_map.timestamps,
(ntp_ts_map.first + index) & (ntp_ts_map.max_size - 1));
}
/* ================================================== */
static int
find_ntp_rx_ts(uint64_t rx_ts, uint32_t *index)
{
uint64_t rx_x, rx_lo, rx_hi, step;
uint32_t i, x, lo, hi;
if (ntp_ts_map.cached_rx_ts == rx_ts && rx_ts != 0ULL) {
*index = ntp_ts_map.cached_index;
return 1;
}
if (ntp_ts_map.size == 0) {
*index = 0;
return 0;
}
lo = 0;
hi = ntp_ts_map.size - 1;
rx_lo = get_ntp_tss(lo)->rx_ts;
rx_hi = get_ntp_tss(hi)->rx_ts;
/* Check for ts < lo before ts > hi to trim timestamps from "future" later
if both conditions are true to not break the order of the endpoints.
Compare timestamps by their difference to allow adjacent NTP eras. */
if ((int64_t)(rx_ts - rx_lo) < 0) {
*index = 0;
return 0;
} else if ((int64_t)(rx_ts - rx_hi) > 0) {
*index = ntp_ts_map.size;
return 0;
}
/* Perform a combined linear interpolation and binary search */
for (i = 0; ; i++) {
if (rx_ts == rx_hi) {
*index = ntp_ts_map.cached_index = hi;
ntp_ts_map.cached_rx_ts = rx_ts;
return 1;
} else if (rx_ts == rx_lo) {
*index = ntp_ts_map.cached_index = lo;
ntp_ts_map.cached_rx_ts = rx_ts;
return 1;
} else if (lo + 1 == hi) {
*index = hi;
return 0;
}
if (hi - lo > 3 && i % 2 == 0) {
step = (rx_hi - rx_lo) / (hi - lo);
if (step == 0)
step = 1;
x = lo + (rx_ts - rx_lo) / step;
} else {
x = lo + (hi - lo) / 2;
}
if (x <= lo)
x = lo + 1;
else if (x >= hi)
x = hi - 1;
rx_x = get_ntp_tss(x)->rx_ts;
if ((int64_t)(rx_x - rx_ts) <= 0) {
lo = x;
rx_lo = rx_x;
} else {
hi = x;
rx_hi = rx_x;
}
}
}
/* ================================================== */
static uint64_t
ntp64_to_int64(NTP_int64 *ts)
{
return (uint64_t)ntohl(ts->hi) << 32 | ntohl(ts->lo);
}
/* ================================================== */
static void
int64_to_ntp64(uint64_t ts, NTP_int64 *ntp_ts)
{
ntp_ts->hi = htonl(ts >> 32);
ntp_ts->lo = htonl(ts);
}
/* ================================================== */
static uint32_t
push_ntp_tss(uint32_t index)
{
if (ntp_ts_map.size < ntp_ts_map.max_size) {
ntp_ts_map.size++;
} else {
ntp_ts_map.first = (ntp_ts_map.first + 1) % (ntp_ts_map.max_size);
if (index > 0)
index--;
}
return index;
}
/* ================================================== */
static void
set_ntp_tx_offset(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts)
{
struct timespec ts;
if (!tx_ts) {
tss->flags &= ~NTPTS_VALID_TX;
return;
}
UTI_Ntp64ToTimespec(rx_ts, &ts);
UTI_DiffTimespecs(&ts, tx_ts, &ts);
if (ts.tv_sec < -2 || ts.tv_sec > 1) {
tss->flags &= ~NTPTS_VALID_TX;
return;
}
tss->tx_ts_offset = (int32_t)ts.tv_nsec + (int32_t)ts.tv_sec * (int32_t)NSEC_PER_SEC;
tss->flags |= NTPTS_VALID_TX;
}
/* ================================================== */
static void
get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts)
{
int32_t offset = tss->tx_ts_offset;
NTP_int64 ntp_ts;
if (tss->flags & NTPTS_VALID_TX) {
int64_to_ntp64(tss->rx_ts, &ntp_ts);
UTI_Ntp64ToTimespec(&ntp_ts, tx_ts);
if (offset >= (int32_t)NSEC_PER_SEC) {
offset -= NSEC_PER_SEC;
tx_ts->tv_sec++;
}
tx_ts->tv_nsec += offset;
UTI_NormaliseTimespec(tx_ts);
} else {
UTI_ZeroTimespec(tx_ts);
}
}
/* ================================================== */
void
CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts)
{
NtpTimestamps *tss;
uint32_t i, index;
uint64_t rx;
if (!active)
return;
/* Allocate the array on first use */
if (!ntp_ts_map.timestamps) {
ntp_ts_map.timestamps = ARR_CreateInstance(sizeof (NtpTimestamps));
ARR_SetSize(ntp_ts_map.timestamps, ntp_ts_map.max_size);
}
rx = ntp64_to_int64(rx_ts);
if (rx == 0ULL)
return;
/* Disable the RX timestamp if it already exists to avoid responding
with a wrong TX timestamp */
if (find_ntp_rx_ts(rx, &index)) {
get_ntp_tss(index)->flags |= NTPTS_DISABLED;
return;
}
assert(index <= ntp_ts_map.size);
if (index == ntp_ts_map.size) {
/* Increase the size or drop the oldest timestamp to make room for
the new timestamp */
index = push_ntp_tss(index);
} else {
/* Trim timestamps in distant future after backward step */
while (index < ntp_ts_map.size &&
get_ntp_tss(ntp_ts_map.size - 1)->rx_ts - rx > NTPTS_FUTURE_LIMIT)
ntp_ts_map.size--;
/* Insert the timestamp if it is close to the latest timestamp.
Otherwise, replace the closest older or the oldest timestamp. */
if (index + NTPTS_INSERT_LIMIT >= ntp_ts_map.size) {
index = push_ntp_tss(index);
for (i = ntp_ts_map.size - 1; i > index; i--)
*get_ntp_tss(i) = *get_ntp_tss(i - 1);
} else {
if (index > 0)
index--;
}
}
ntp_ts_map.cached_index = index;
ntp_ts_map.cached_rx_ts = rx;
tss = get_ntp_tss(index);
tss->rx_ts = rx;
tss->flags = 0;
tss->slew_epoch = ntp_ts_map.slew_epoch;
set_ntp_tx_offset(tss, rx_ts, tx_ts);
DEBUG_LOG("Saved RX+TX index=%"PRIu32" first=%"PRIu32" size=%"PRIu32,
index, ntp_ts_map.first, ntp_ts_map.size);
}
/* ================================================== */
static void
handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
/* Drop all timestamps on unknown step */
if (change_type == LCL_ChangeUnknownStep) {
ntp_ts_map.size = 0;
ntp_ts_map.cached_rx_ts = 0ULL;
}
ntp_ts_map.slew_epoch++;
ntp_ts_map.slew_offset = doffset;
}
/* ================================================== */
void
CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts)
{
uint32_t index;
if (!ntp_ts_map.timestamps)
return;
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
return;
/* If the RX timestamp was captured before the last correction of the clock,
remove the adjustment from the TX timestamp */
if ((uint16_t)(get_ntp_tss(index)->slew_epoch + 1U) == ntp_ts_map.slew_epoch)
UTI_AddDoubleToTimespec(tx_ts, ntp_ts_map.slew_offset, tx_ts);
}
/* ================================================== */
void
CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
{
uint32_t index;
if (!ntp_ts_map.timestamps)
return;
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
return;
set_ntp_tx_offset(get_ntp_tss(index), rx_ts, tx_ts);
}
/* ================================================== */
int
CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
{
NtpTimestamps *tss;
uint32_t index;
if (!ntp_ts_map.timestamps)
return 0;
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
return 0;
tss = get_ntp_tss(index);
if (tss->flags & NTPTS_DISABLED)
return 0;
get_ntp_tx(tss, tx_ts);
return 1;
}
/* ================================================== */
void
CLG_DisableNtpTimestamps(NTP_int64 *rx_ts)
{
uint32_t index;
if (!ntp_ts_map.timestamps)
return;
if (find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
get_ntp_tss(index)->flags |= NTPTS_DISABLED;
/* This assumes the function is called only to prevent multiple
interleaved responses to the same timestamp */
total_ntp_interleaved_hits++;
} }
/* ================================================== */ /* ================================================== */
@@ -653,10 +1012,12 @@ static uint32_t get_last_ago(uint32_t x, uint32_t y)
/* ================================================== */ /* ================================================== */
int int
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now) CLG_GetClientAccessReportByIndex(int index, int reset, uint32_t min_hits,
RPT_ClientAccessByIndex_Report *report, struct timespec *now)
{ {
Record *record; Record *record;
uint32_t now_ts; uint32_t now_ts;
int i, r;
if (!active || index < 0 || index >= ARR_GetSize(records)) if (!active || index < 0 || index >= ARR_GetSize(records))
return 0; return 0;
@@ -666,20 +1027,44 @@ CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *repo
if (record->ip_addr.family == IPADDR_UNSPEC) if (record->ip_addr.family == IPADDR_UNSPEC)
return 0; return 0;
now_ts = get_ts_from_timespec(now); if (min_hits == 0) {
r = 1;
} else {
for (i = r = 0; i < MAX_SERVICES; i++) {
if (record->hits[i] >= min_hits) {
r = 1;
break;
}
}
}
report->ip_addr = record->ip_addr; if (r) {
report->ntp_hits = record->ntp_hits; now_ts = get_ts_from_timespec(now);
report->cmd_hits = record->cmd_hits;
report->ntp_drops = record->ntp_drops;
report->cmd_drops = record->cmd_drops;
report->ntp_interval = get_interval(record->ntp_rate);
report->cmd_interval = get_interval(record->cmd_rate);
report->ntp_timeout_interval = get_interval(record->ntp_timeout_rate);
report->last_ntp_hit_ago = get_last_ago(now_ts, record->last_ntp_hit);
report->last_cmd_hit_ago = get_last_ago(now_ts, record->last_cmd_hit);
return 1; report->ip_addr = record->ip_addr;
report->ntp_hits = record->hits[CLG_NTP];
report->nke_hits = record->hits[CLG_NTSKE];
report->cmd_hits = record->hits[CLG_CMDMON];
report->ntp_drops = record->drops[CLG_NTP];
report->nke_drops = record->drops[CLG_NTSKE];
report->cmd_drops = record->drops[CLG_CMDMON];
report->ntp_interval = get_interval(record->rate[CLG_NTP]);
report->nke_interval = get_interval(record->rate[CLG_NTSKE]);
report->cmd_interval = get_interval(record->rate[CLG_CMDMON]);
report->ntp_timeout_interval = get_interval(record->ntp_timeout_rate);
report->last_ntp_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_NTP]);
report->last_nke_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_NTSKE]);
report->last_cmd_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_CMDMON]);
}
if (reset) {
for (i = 0; i < MAX_SERVICES; i++) {
record->hits[i] = 0;
record->drops[i] = 0;
}
}
return r;
} }
/* ================================================== */ /* ================================================== */
@@ -687,9 +1072,17 @@ CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *repo
void void
CLG_GetServerStatsReport(RPT_ServerStatsReport *report) CLG_GetServerStatsReport(RPT_ServerStatsReport *report)
{ {
report->ntp_hits = total_ntp_hits; report->ntp_hits = total_hits[CLG_NTP];
report->cmd_hits = total_cmd_hits; report->nke_hits = total_hits[CLG_NTSKE];
report->ntp_drops = total_ntp_drops; report->cmd_hits = total_hits[CLG_CMDMON];
report->cmd_drops = total_cmd_drops; report->ntp_drops = total_drops[CLG_NTP];
report->nke_drops = total_drops[CLG_NTSKE];
report->cmd_drops = total_drops[CLG_CMDMON];
report->log_drops = total_record_drops; report->log_drops = total_record_drops;
report->ntp_auth_hits = total_ntp_auth_hits;
report->ntp_interleaved_hits = total_ntp_interleaved_hits;
report->ntp_timestamps = ntp_ts_map.size;
report->ntp_span_seconds = ntp_ts_map.size > 1 ?
(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
get_ntp_tss(0)->rx_ts) >> 32 : 0;
} }

View File

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

17
cmac.h
View File

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

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

271
cmdmon.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016, 2018 * Copyright (C) Miroslav Lichvar 2009-2016, 2018-2021
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -62,6 +62,9 @@ static int sock_fdu;
static int sock_fd4; static int sock_fd4;
static int sock_fd6; 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 */ /* Flag indicating whether this module has been initialised or not */
static int initialised = 0; static int initialised = 0;
@@ -135,7 +138,12 @@ static const char permissions[] = {
PERMIT_AUTH, /* ONOFFLINE */ PERMIT_AUTH, /* ONOFFLINE */
PERMIT_AUTH, /* ADD_SOURCE */ PERMIT_AUTH, /* ADD_SOURCE */
PERMIT_OPEN, /* NTP_SOURCE_NAME */ PERMIT_OPEN, /* NTP_SOURCE_NAME */
PERMIT_AUTH, /* RESET */ PERMIT_AUTH, /* RESET_SOURCES */
PERMIT_AUTH, /* AUTH_DATA */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX3 */
PERMIT_AUTH, /* SELECT_DATA */
PERMIT_AUTH, /* RELOAD_SOURCES */
PERMIT_AUTH, /* DOFFSET2 */
}; };
/* ================================================== */ /* ================================================== */
@@ -153,29 +161,31 @@ static void read_from_cmd_socket(int sock_fd, int event, void *anything);
static int static int
open_socket(int family) open_socket(int family)
{ {
const char *local_path, *iface;
IPSockAddr local_addr; IPSockAddr local_addr;
const char *local_path;
int sock_fd, port; int sock_fd, port;
switch (family) { switch (family) {
case IPADDR_INET4: case IPADDR_INET4:
case IPADDR_INET6: case IPADDR_INET6:
port = CNF_GetCommandPort(); port = CNF_GetCommandPort();
if (port == 0 || !SCK_IsFamilySupported(family)) if (port == 0 || !SCK_IsIpFamilyEnabled(family))
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
CNF_GetBindCommandAddress(family, &local_addr.ip_addr); CNF_GetBindCommandAddress(family, &local_addr.ip_addr);
if (local_addr.ip_addr.family != family)
SCK_GetLoopbackIPAddress(family, &local_addr.ip_addr);
local_addr.port = port; local_addr.port = port;
iface = CNF_GetBindCommandInterface();
sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, SCK_FLAG_RX_DEST_ADDR); sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, iface, SCK_FLAG_RX_DEST_ADDR);
if (sock_fd < 0) { if (sock_fd < 0) {
LOG(LOGS_ERR, "Could not open command socket on %s", LOG(LOGS_ERR, "Could not open command socket on %s",
UTI_IPSockAddrToString(&local_addr)); UTI_IPSockAddrToString(&local_addr));
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
if (family == IPADDR_INET4)
bound_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
break; break;
case IPADDR_UNSPEC: case IPADDR_UNSPEC:
local_path = CNF_GetBindCommandPath(); local_path = CNF_GetBindCommandPath();
@@ -233,22 +243,19 @@ do_size_checks(void)
/* ================================================== */ /* ================================================== */
void void
CAM_Initialise(int family) CAM_Initialise(void)
{ {
assert(!initialised); assert(!initialised);
assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES); assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
do_size_checks(); do_size_checks();
initialised = 1; initialised = 1;
bound_sock_fd4 = 0;
sock_fdu = INVALID_SOCK_FD; sock_fdu = INVALID_SOCK_FD;
sock_fd4 = INVALID_SOCK_FD; sock_fd4 = open_socket(IPADDR_INET4);
sock_fd6 = INVALID_SOCK_FD; sock_fd6 = open_socket(IPADDR_INET6);
if (family == IPADDR_UNSPEC || family == IPADDR_INET4)
sock_fd4 = open_socket(IPADDR_INET4);
if (family == IPADDR_UNSPEC || family == IPADDR_INET6)
sock_fd6 = open_socket(IPADDR_INET6);
access_auth_table = ADF_CreateTable(); access_auth_table = ADF_CreateTable();
} }
@@ -289,17 +296,36 @@ CAM_OpenUnixSocket(void)
{ {
/* This is separated from CAM_Initialise() as it needs to be called when /* This is separated from CAM_Initialise() as it needs to be called when
the process has already dropped the root privileges */ the process has already dropped the root privileges */
if (CNF_GetBindCommandPath()[0]) if (CNF_GetBindCommandPath())
sock_fdu = open_socket(IPADDR_UNSPEC); sock_fdu = open_socket(IPADDR_UNSPEC);
} }
/* ================================================== */ /* ================================================== */
static void static void
transmit_reply(int sock_fd, SCK_Message *message) transmit_reply(int sock_fd, int request_length, SCK_Message *message)
{ {
message->length = PKL_ReplyLength((CMD_Reply *)message->data); message->length = PKL_ReplyLength((CMD_Reply *)message->data);
if (request_length < message->length) {
DEBUG_LOG("Response longer than request req_len=%d res_len=%d",
request_length, message->length);
return;
}
/* Don't require responses to non-link-local addresses to use the same
interface */
if (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)) if (!SCK_SendMessage(sock_fd, message, 0))
return; return;
} }
@@ -569,11 +595,8 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.source_data.stratum = htons(report.stratum); tx_message->data.source_data.stratum = htons(report.stratum);
tx_message->data.source_data.poll = htons(report.poll); tx_message->data.source_data.poll = htons(report.poll);
switch (report.state) { switch (report.state) {
case RPT_SYNC: case RPT_NONSELECTABLE:
tx_message->data.source_data.state = htons(RPY_SD_ST_SYNC); tx_message->data.source_data.state = htons(RPY_SD_ST_NONSELECTABLE);
break;
case RPT_UNREACH:
tx_message->data.source_data.state = htons(RPY_SD_ST_UNREACH);
break; break;
case RPT_FALSETICKER: case RPT_FALSETICKER:
tx_message->data.source_data.state = htons(RPY_SD_ST_FALSETICKER); tx_message->data.source_data.state = htons(RPY_SD_ST_FALSETICKER);
@@ -581,11 +604,14 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
case RPT_JITTERY: case RPT_JITTERY:
tx_message->data.source_data.state = htons(RPY_SD_ST_JITTERY); tx_message->data.source_data.state = htons(RPY_SD_ST_JITTERY);
break; break;
case RPT_CANDIDATE: case RPT_SELECTABLE:
tx_message->data.source_data.state = htons(RPY_SD_ST_CANDIDATE); tx_message->data.source_data.state = htons(RPY_SD_ST_SELECTABLE);
break; break;
case RPT_OUTLIER: case RPT_UNSELECTED:
tx_message->data.source_data.state = htons(RPY_SD_ST_OUTLIER); tx_message->data.source_data.state = htons(RPY_SD_ST_UNSELECTED);
break;
case RPT_SELECTED:
tx_message->data.source_data.state = htons(RPY_SD_ST_SELECTED);
break; break;
} }
switch (report.mode) { switch (report.mode) {
@@ -599,11 +625,7 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.source_data.mode = htons(RPY_SD_MD_REF); tx_message->data.source_data.mode = htons(RPY_SD_MD_REF);
break; break;
} }
tx_message->data.source_data.flags = tx_message->data.source_data.flags = htons(0);
htons((report.sel_options & SRC_SELECT_PREFER ? RPY_SD_FLAG_PREFER : 0) |
(report.sel_options & SRC_SELECT_NOSELECT ? RPY_SD_FLAG_NOSELECT : 0) |
(report.sel_options & SRC_SELECT_TRUST ? RPY_SD_FLAG_TRUST : 0) |
(report.sel_options & SRC_SELECT_REQUIRE ? RPY_SD_FLAG_REQUIRE : 0));
tx_message->data.source_data.reachability = htons(report.reachability); tx_message->data.source_data.reachability = htons(report.reachability);
tx_message->data.source_data.since_sample = htonl(report.latest_meas_ago); tx_message->data.source_data.since_sample = htonl(report.latest_meas_ago);
tx_message->data.source_data.orig_latest_meas = UTI_FloatHostToNetwork(report.orig_latest_meas); tx_message->data.source_data.orig_latest_meas = UTI_FloatHostToNetwork(report.orig_latest_meas);
@@ -716,7 +738,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
return; return;
} }
port = (unsigned short)(ntohl(rx_message->data.ntp_source.port)); port = ntohl(rx_message->data.ntp_source.port);
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll); params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll); params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll); params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll);
@@ -729,6 +751,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
params.filter_length = ntohl(rx_message->data.ntp_source.filter_length); params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
params.authkey = ntohl(rx_message->data.ntp_source.authkey); params.authkey = ntohl(rx_message->data.ntp_source.authkey);
params.nts_port = ntohl(rx_message->data.ntp_source.nts_port); 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 = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
params.max_delay_ratio = params.max_delay_ratio =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio); UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
@@ -745,13 +768,16 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 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.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.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0;
params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
params.ext_fields =
ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP1 ? NTP_EF_FLAG_EXP1 : 0;
params.sel_options = 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_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_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_TRUST ? SRC_SELECT_TRUST : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0); (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
status = NSR_AddSourceByName(name, port, pool, type, &params); status = NSR_AddSourceByName(name, port, pool, type, &params, NULL);
switch (status) { switch (status) {
case NSR_Success: case NSR_Success:
break; break;
@@ -780,13 +806,12 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
static void static void
handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message) handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
NTP_Remote_Address rem_addr;
NSR_Status status; NSR_Status status;
IPAddr ip_addr;
UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &rem_addr.ip_addr); UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &ip_addr);
rem_addr.port = 0;
status = NSR_RemoveSource(&rem_addr); status = NSR_RemoveSource(&ip_addr);
switch (status) { switch (status) {
case NSR_Success: case NSR_Success:
break; break;
@@ -836,13 +861,14 @@ handle_dfreq(CMD_Request *rx_message, CMD_Reply *tx_message)
static void static void
handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message) handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
long sec, usec;
double doffset; double doffset;
sec = (int32_t)ntohl(rx_message->data.doffset.sec);
usec = (int32_t)ntohl(rx_message->data.doffset.usec); doffset = UTI_FloatNetworkToHost(rx_message->data.doffset.doffset);
doffset = (double) sec + 1.0e-6 * (double) usec; if (!LCL_AccumulateOffset(doffset, 0.0)) {
LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset); tx_message->status = htons(STT_FAILED);
LCL_AccumulateOffset(doffset, 0.0); } else {
LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset);
}
} }
/* ================================================== */ /* ================================================== */
@@ -1000,7 +1026,7 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ClientAccessByIndex_Report report; RPT_ClientAccessByIndex_Report report;
RPY_ClientAccesses_Client *client; RPY_ClientAccesses_Client *client;
int n_indices; int n_indices;
uint32_t i, j, req_first_index, req_n_clients; uint32_t i, j, req_first_index, req_n_clients, req_min_hits, req_reset;
struct timespec now; struct timespec now;
SCH_GetLastEventTime(&now, NULL, NULL); SCH_GetLastEventTime(&now, NULL, NULL);
@@ -1009,6 +1035,8 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
req_n_clients = ntohl(rx_message->data.client_accesses_by_index.n_clients); req_n_clients = ntohl(rx_message->data.client_accesses_by_index.n_clients);
if (req_n_clients > MAX_CLIENT_ACCESSES) if (req_n_clients > MAX_CLIENT_ACCESSES)
req_n_clients = MAX_CLIENT_ACCESSES; req_n_clients = MAX_CLIENT_ACCESSES;
req_min_hits = ntohl(rx_message->data.client_accesses_by_index.min_hits);
req_reset = ntohl(rx_message->data.client_accesses_by_index.reset);
n_indices = CLG_GetNumberOfIndices(); n_indices = CLG_GetNumberOfIndices();
if (n_indices < 0) { if (n_indices < 0) {
@@ -1016,24 +1044,28 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
return; return;
} }
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX2); tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX3);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices); tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices);
for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) { for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) {
if (!CLG_GetClientAccessReportByIndex(i, &report, &now)) if (!CLG_GetClientAccessReportByIndex(i, req_reset, req_min_hits, &report, &now))
continue; continue;
client = &tx_message->data.client_accesses_by_index.clients[j++]; client = &tx_message->data.client_accesses_by_index.clients[j++];
UTI_IPHostToNetwork(&report.ip_addr, &client->ip); UTI_IPHostToNetwork(&report.ip_addr, &client->ip);
client->ntp_hits = htonl(report.ntp_hits); client->ntp_hits = htonl(report.ntp_hits);
client->nke_hits = htonl(report.nke_hits);
client->cmd_hits = htonl(report.cmd_hits); client->cmd_hits = htonl(report.cmd_hits);
client->ntp_drops = htonl(report.ntp_drops); client->ntp_drops = htonl(report.ntp_drops);
client->nke_drops = htonl(report.nke_drops);
client->cmd_drops = htonl(report.cmd_drops); client->cmd_drops = htonl(report.cmd_drops);
client->ntp_interval = report.ntp_interval; client->ntp_interval = report.ntp_interval;
client->nke_interval = report.nke_interval;
client->cmd_interval = report.cmd_interval; client->cmd_interval = report.cmd_interval;
client->ntp_timeout_interval = report.ntp_timeout_interval; client->ntp_timeout_interval = report.ntp_timeout_interval;
client->last_ntp_hit_ago = htonl(report.last_ntp_hit_ago); client->last_ntp_hit_ago = htonl(report.last_ntp_hit_ago);
client->last_nke_hit_ago = htonl(report.last_nke_hit_ago);
client->last_cmd_hit_ago = htonl(report.last_cmd_hit_ago); client->last_cmd_hit_ago = htonl(report.last_cmd_hit_ago);
} }
@@ -1135,12 +1167,18 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ServerStatsReport report; RPT_ServerStatsReport report;
CLG_GetServerStatsReport(&report); CLG_GetServerStatsReport(&report);
tx_message->reply = htons(RPY_SERVER_STATS); tx_message->reply = htons(RPY_SERVER_STATS3);
tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits); tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits);
tx_message->data.server_stats.nke_hits = htonl(report.nke_hits);
tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits); tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits);
tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops); tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops);
tx_message->data.server_stats.nke_drops = htonl(report.nke_drops);
tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops); tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops);
tx_message->data.server_stats.log_drops = htonl(report.log_drops); tx_message->data.server_stats.log_drops = htonl(report.log_drops);
tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits);
tx_message->data.server_stats.ntp_interleaved_hits = htonl(report.ntp_interleaved_hits);
tx_message->data.server_stats.ntp_timestamps = htonl(report.ntp_timestamps);
tx_message->data.server_stats.ntp_span_seconds = htonl(report.ntp_span_seconds);
} }
/* ================================================== */ /* ================================================== */
@@ -1204,7 +1242,7 @@ handle_ntp_source_name(CMD_Request *rx_message, CMD_Reply *tx_message)
IPAddr addr; IPAddr addr;
char *name; char *name;
UTI_IPNetworkToHost(&rx_message->data.ntp_data.ip_addr, &addr); UTI_IPNetworkToHost(&rx_message->data.ntp_source_name.ip_addr, &addr);
name = NSR_GetName(&addr); name = NSR_GetName(&addr);
if (!name) { if (!name) {
@@ -1226,7 +1264,15 @@ handle_ntp_source_name(CMD_Request *rx_message, CMD_Reply *tx_message)
/* ================================================== */ /* ================================================== */
static void static void
handle_reset(CMD_Request *rx_message, CMD_Reply *tx_message) handle_reload_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
{
CNF_ReloadSources();
}
/* ================================================== */
static void
handle_reset_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
struct timespec cooked_now, now; struct timespec cooked_now, now;
@@ -1235,6 +1281,85 @@ handle_reset(CMD_Request *rx_message, CMD_Reply *tx_message)
LCL_NotifyExternalTimeStep(&now, &cooked_now, 0.0, 0.0); LCL_NotifyExternalTimeStep(&now, &cooked_now, 0.0, 0.0);
} }
/* ================================================== */
static void
handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_AuthReport report;
IPAddr ip_addr;
UTI_IPNetworkToHost(&rx_message->data.auth_data.ip_addr, &ip_addr);
if (!NSR_GetAuthReport(&ip_addr, &report)) {
tx_message->status = htons(STT_NOSUCHSOURCE);
return;
}
tx_message->reply = htons(RPY_AUTH_DATA);
switch (report.mode) {
case NTP_AUTH_NONE:
tx_message->data.auth_data.mode = htons(RPY_AD_MD_NONE);
break;
case NTP_AUTH_SYMMETRIC:
tx_message->data.auth_data.mode = htons(RPY_AD_MD_SYMMETRIC);
break;
case NTP_AUTH_NTS:
tx_message->data.auth_data.mode = htons(RPY_AD_MD_NTS);
break;
default:
break;
}
tx_message->data.auth_data.key_type = htons(report.key_type);
tx_message->data.auth_data.key_id = htonl(report.key_id);
tx_message->data.auth_data.key_length = htons(report.key_length);
tx_message->data.auth_data.ke_attempts = htons(report.ke_attempts);
tx_message->data.auth_data.last_ke_ago = htonl(report.last_ke_ago);
tx_message->data.auth_data.cookies = htons(report.cookies);
tx_message->data.auth_data.cookie_length = htons(report.cookie_length);
tx_message->data.auth_data.nak = htons(report.nak);
}
/* ================================================== */
static uint16_t
convert_select_options(int options)
{
return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
(options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
(options & SRC_SELECT_TRUST ? RPY_SD_OPTION_TRUST : 0) |
(options & SRC_SELECT_REQUIRE ? RPY_SD_OPTION_REQUIRE : 0);
}
/* ================================================== */
static void
handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_SelectReport report;
if (!SRC_GetSelectReport(ntohl(rx_message->data.select_data.index), &report)) {
tx_message->status = htons(STT_NOSUCHSOURCE);
return;
}
tx_message->reply = htons(RPY_SELECT_DATA);
tx_message->data.select_data.ref_id = htonl(report.ref_id);
UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.select_data.ip_addr);
tx_message->data.select_data.state_char = report.state_char;
tx_message->data.select_data.authentication = report.authentication;
tx_message->data.select_data.leap = report.leap;
tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options));
tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options));
tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);
tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score);
tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit);
}
/* ================================================== */ /* ================================================== */
/* Read a packet and process it */ /* Read a packet and process it */
@@ -1247,7 +1372,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
IPAddr loopback_addr, remote_ip; IPAddr loopback_addr, remote_ip;
int read_length, expected_length; int read_length, expected_length;
int localhost, allowed, log_index; int localhost, allowed, log_index;
unsigned short rx_command; uint16_t rx_command;
struct timespec now, cooked_now; struct timespec now, cooked_now;
sck_message = SCK_ReceiveMessage(sock_fd, 0); sck_message = SCK_ReceiveMessage(sock_fd, 0);
@@ -1305,11 +1430,11 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
return; return;
} }
log_index = CLG_LogCommandAccess(&remote_ip, &cooked_now); log_index = CLG_LogServiceAccess(CLG_CMDMON, &remote_ip, &cooked_now);
/* Don't reply to all requests from hosts other than localhost if the rate /* Don't reply to all requests from hosts other than localhost if the rate
is excessive */ is excessive */
if (!localhost && log_index >= 0 && CLG_LimitCommandResponseRate(log_index)) { if (!localhost && log_index >= 0 && CLG_LimitServiceRate(CLG_CMDMON, log_index)) {
DEBUG_LOG("Command packet discarded to limit response rate"); DEBUG_LOG("Command packet discarded to limit response rate");
return; return;
} }
@@ -1334,7 +1459,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) { if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
tx_message.status = htons(STT_BADPKTVERSION); tx_message.status = htons(STT_BADPKTVERSION);
transmit_reply(sock_fd, sck_message); transmit_reply(sock_fd, read_length, sck_message);
} }
return; return;
} }
@@ -1344,7 +1469,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
DEBUG_LOG("Command packet has invalid command %d", rx_command); DEBUG_LOG("Command packet has invalid command %d", rx_command);
tx_message.status = htons(STT_INVALID); tx_message.status = htons(STT_INVALID);
transmit_reply(sock_fd, sck_message); transmit_reply(sock_fd, read_length, sck_message);
return; return;
} }
@@ -1353,7 +1478,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
expected_length); expected_length);
tx_message.status = htons(STT_BADPKTLENGTH); tx_message.status = htons(STT_BADPKTLENGTH);
transmit_reply(sock_fd, sck_message); transmit_reply(sock_fd, read_length, sck_message);
return; return;
} }
@@ -1521,7 +1646,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_dfreq(&rx_message, &tx_message); handle_dfreq(&rx_message, &tx_message);
break; break;
case REQ_DOFFSET: case REQ_DOFFSET2:
handle_doffset(&rx_message, &tx_message); handle_doffset(&rx_message, &tx_message);
break; break;
@@ -1553,7 +1678,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_cyclelogs(&rx_message, &tx_message); handle_cyclelogs(&rx_message, &tx_message);
break; break;
case REQ_CLIENT_ACCESSES_BY_INDEX2: case REQ_CLIENT_ACCESSES_BY_INDEX3:
handle_client_accesses_by_index(&rx_message, &tx_message); handle_client_accesses_by_index(&rx_message, &tx_message);
break; break;
@@ -1613,8 +1738,20 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_ntp_source_name(&rx_message, &tx_message); handle_ntp_source_name(&rx_message, &tx_message);
break; break;
case REQ_RESET: case REQ_RESET_SOURCES:
handle_reset(&rx_message, &tx_message); handle_reset_sources(&rx_message, &tx_message);
break;
case REQ_AUTH_DATA:
handle_auth_data(&rx_message, &tx_message);
break;
case REQ_SELECT_DATA:
handle_select_data(&rx_message, &tx_message);
break;
case REQ_RELOAD_SOURCES:
handle_reload_sources(&rx_message, &tx_message);
break; break;
default: default:
@@ -1628,19 +1765,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
} }
/* Transmit the response */ /* Transmit the response */
{ transmit_reply(sock_fd, read_length, sck_message);
/* Include a simple way to lose one message in three to test resend */
static int do_it=1;
if (do_it) {
transmit_reply(sock_fd, sck_message);
}
#if 0
do_it = ((do_it + 1) % 3);
#endif
}
} }
/* ================================================== */ /* ================================================== */

View File

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

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * 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 * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -43,6 +43,7 @@ int
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{ {
char *hostname, *cmd; char *hostname, *cmd;
uint32_t ef_type;
int n; int n;
src->port = SRC_DEFAULT_PORT; src->port = SRC_DEFAULT_PORT;
@@ -64,7 +65,10 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.sel_options = 0; src->params.sel_options = 0;
src->params.nts = 0; src->params.nts = 0;
src->params.nts_port = SRC_DEFAULT_NTSPORT; src->params.nts_port = SRC_DEFAULT_NTSPORT;
src->params.copy = 0;
src->params.ext_fields = 0;
src->params.authkey = INACTIVE_AUTHKEY; src->params.authkey = INACTIVE_AUTHKEY;
src->params.cert_set = SRC_DEFAULT_CERTSET;
src->params.max_delay = SRC_DEFAULT_MAXDELAY; src->params.max_delay = SRC_DEFAULT_MAXDELAY;
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO; src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO; src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
@@ -90,6 +94,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.auto_offline = 1; src->params.auto_offline = 1;
} else if (!strcasecmp(cmd, "burst")) { } else if (!strcasecmp(cmd, "burst")) {
src->params.burst = 1; src->params.burst = 1;
} else if (!strcasecmp(cmd, "copy")) {
src->params.copy = 1;
} else if (!strcasecmp(cmd, "iburst")) { } else if (!strcasecmp(cmd, "iburst")) {
src->params.iburst = 1; src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) { } else if (!strcasecmp(cmd, "offline")) {
@@ -102,6 +108,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.sel_options |= SRC_SELECT_REQUIRE; src->params.sel_options |= SRC_SELECT_REQUIRE;
} else if (!strcasecmp(cmd, "trust")) { } else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_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")) { } else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 || if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY) src->params.authkey == INACTIVE_AUTHKEY)
@@ -109,6 +118,16 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "asymmetry")) { } else if (!strcasecmp(cmd, "asymmetry")) {
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1) if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
return 0; return 0;
} else if (!strcasecmp(cmd, "extfield")) {
if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
return 0;
switch (ef_type) {
case NTP_EF_EXP1:
src->params.ext_fields |= NTP_EF_FLAG_EXP1;
break;
default:
return 0;
}
} else if (!strcasecmp(cmd, "filter")) { } else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1) if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
return 0; return 0;
@@ -151,7 +170,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1) if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
return 0; return 0;
} else if (!strcasecmp(cmd, "port")) { } else if (!strcasecmp(cmd, "port")) {
if (sscanf(line, "%hu%n", &src->port, &n) != 1) if (sscanf(line, "%d%n", &src->port, &n) != 1)
return 0; return 0;
} else if (!strcasecmp(cmd, "polltarget")) { } else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
@@ -174,6 +193,85 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
/* ================================================== */ /* ================================================== */
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 int
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance) CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
{ {

View File

@@ -32,13 +32,16 @@
typedef struct { typedef struct {
char *name; char *name;
unsigned short port; int port;
SourceParameters params; SourceParameters params;
} CPS_NTP_Source; } CPS_NTP_Source;
/* Parse a command to add an NTP server or peer */ /* Parse a command to add an NTP server or peer */
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src); extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* 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 */ /* 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);

728
conf.c

File diff suppressed because it is too large Load Diff

20
conf.h
View File

@@ -30,10 +30,13 @@
#include "addressing.h" #include "addressing.h"
#include "reference.h" #include "reference.h"
#include "sources.h"
extern void CNF_Initialise(int restarted, int client_only); extern void CNF_Initialise(int restarted, int client_only);
extern void CNF_Finalise(void); extern void CNF_Finalise(void);
extern void CNF_EnablePrint(void);
extern char *CNF_GetRtcDevice(void); extern char *CNF_GetRtcDevice(void);
extern void CNF_ReadFile(const char *filename); extern void CNF_ReadFile(const char *filename);
@@ -46,6 +49,8 @@ extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void); extern void CNF_AddBroadcasts(void);
extern void CNF_AddRefclocks(void); extern void CNF_AddRefclocks(void);
extern void CNF_ReloadSources(void);
extern int CNF_GetAcquisitionPort(void); extern int CNF_GetAcquisitionPort(void);
extern int CNF_GetNTPPort(void); extern int CNF_GetNTPPort(void);
extern char *CNF_GetDriftFile(void); extern char *CNF_GetDriftFile(void);
@@ -74,7 +79,11 @@ extern void CNF_GetFallbackDrifts(int *min, int *max);
extern void CNF_GetBindAddress(int family, IPAddr *addr); extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr); extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr); extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
extern char *CNF_GetBindNtpInterface(void);
extern char *CNF_GetBindAcquisitionInterface(void);
extern char *CNF_GetBindCommandInterface(void);
extern char *CNF_GetBindCommandPath(void); extern char *CNF_GetBindCommandPath(void);
extern int CNF_GetNtpDscp(void);
extern char *CNF_GetNtpSigndSocket(void); extern char *CNF_GetNtpSigndSocket(void);
extern char *CNF_GetPidFile(void); extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void); extern REF_LeapMode CNF_GetLeapSecMode(void);
@@ -86,7 +95,9 @@ extern double CNF_GetMaxClockError(void);
extern double CNF_GetMaxDrift(void); extern double CNF_GetMaxDrift(void);
extern double CNF_GetCorrectionTimeRatio(void); extern double CNF_GetCorrectionTimeRatio(void);
extern double CNF_GetMaxSlewRate(void); extern double CNF_GetMaxSlewRate(void);
extern double CNF_GetClockPrecision(void);
extern SRC_AuthSelectMode CNF_GetAuthSelectMode(void);
extern double CNF_GetMaxDistance(void); extern double CNF_GetMaxDistance(void);
extern double CNF_GetMaxJitter(void); extern double CNF_GetMaxJitter(void);
extern double CNF_GetReselectDistance(void); extern double CNF_GetReselectDistance(void);
@@ -101,6 +112,7 @@ extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void); extern int CNF_GetLockMemory(void);
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak); extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
extern int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak);
extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak); extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only); extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2); extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2);
@@ -122,6 +134,7 @@ typedef enum {
CNF_HWTS_RXFILTER_ANY, CNF_HWTS_RXFILTER_ANY,
CNF_HWTS_RXFILTER_NONE, CNF_HWTS_RXFILTER_NONE,
CNF_HWTS_RXFILTER_NTP, CNF_HWTS_RXFILTER_NTP,
CNF_HWTS_RXFILTER_PTP,
CNF_HWTS_RXFILTER_ALL, CNF_HWTS_RXFILTER_ALL,
} CNF_HwTs_RxFilter; } CNF_HwTs_RxFilter;
@@ -139,16 +152,17 @@ typedef struct {
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface); extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern int CNF_GetPtpPort(void);
extern char *CNF_GetNtsDumpDir(void); extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void); extern char *CNF_GetNtsNtpServer(void);
extern char *CNF_GetNtsServerCertFile(void); extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
extern char *CNF_GetNtsServerKeyFile(void);
extern int CNF_GetNtsServerPort(void); extern int CNF_GetNtsServerPort(void);
extern int CNF_GetNtsServerProcesses(void); extern int CNF_GetNtsServerProcesses(void);
extern int CNF_GetNtsServerConnections(void); extern int CNF_GetNtsServerConnections(void);
extern int CNF_GetNtsRefresh(void); extern int CNF_GetNtsRefresh(void);
extern int CNF_GetNtsRotate(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_GetNoSystemCert(void);
extern int CNF_GetNoCertTimeCheck(void); extern int CNF_GetNoCertTimeCheck(void);

187
configure vendored
View File

@@ -5,7 +5,7 @@
# #
# Copyright (C) Richard P. Curnow 1997-2003 # Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Bryan Christianson 2016 # Copyright (C) Bryan Christianson 2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2018 # Copyright (C) Miroslav Lichvar 2009, 2012-2021
# Copyright (C) Stefan R. Filipek 2019 # Copyright (C) Stefan R. Filipek 2019
# #
# ======================================================================= # =======================================================================
@@ -33,13 +33,13 @@ test_code () {
echo "int main(int argc, char **argv) {" echo "int main(int argc, char **argv) {"
echo "$code" echo "$code"
echo "return 0; }" echo "return 0; }"
) > docheck.c ) > conftest.c
echo "docheck.c:" >> config.log echo "conftest.c:" >> config.log
cat docheck.c >> config.log cat conftest.c >> config.log
echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \ echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o conftest conftest.c $ldflags \
$MYLDFLAGS >> config.log $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 $MYLDFLAGS >> config.log 2>&1
if [ $? -eq 0 ] if [ $? -eq 0 ]
@@ -50,7 +50,7 @@ test_code () {
echo "No" echo "No"
result=1 result=1
fi fi
rm -f docheck.c docheck rm -f conftest.c conftest
echo >> config.log echo >> config.log
return $result return $result
} }
@@ -108,11 +108,7 @@ for instance \`--prefix=$HOME'.
For better control, use the options below. For better control, use the options below.
--disable-readline Disable line editing support --disable-readline Disable line editing support
--without-readline Don't use GNU readline even if it is available
--without-editline Don't use editline even if it is available --without-editline Don't use editline even if it is available
--with-readline-includes=DIR Specify where readline include directory is
--with-readline-library=DIR Specify where readline lib directory is
--with-ncurses-library=DIR Specify where ncurses lib directory is
--disable-sechash Disable support for hashes other than MD5 --disable-sechash Disable support for hashes other than MD5
--without-nettle Don't use nettle even if it is available --without-nettle Don't use nettle even if it is available
--without-nss Don't use NSS even if it is available --without-nss Don't use NSS even if it is available
@@ -225,7 +221,6 @@ feat_cmdmon=1
feat_ntp=1 feat_ntp=1
feat_refclock=1 feat_refclock=1
feat_readline=1 feat_readline=1
try_readline=1
try_editline=1 try_editline=1
feat_sechash=1 feat_sechash=1
try_nettle=1 try_nettle=1
@@ -241,9 +236,6 @@ try_clockctl=0
feat_scfilter=0 feat_scfilter=0
try_seccomp=-1 try_seccomp=-1
priv_ops="" priv_ops=""
readline_lib=""
readline_inc=""
ncurses_lib=""
feat_ipv6=1 feat_ipv6=1
feat_phc=1 feat_phc=1
try_phc=0 try_phc=0
@@ -274,21 +266,9 @@ do
--disable-readline ) --disable-readline )
feat_readline=0 feat_readline=0
;; ;;
--without-readline )
try_readline=0
;;
--without-editline ) --without-editline )
try_editline=0 try_editline=0
;; ;;
--with-readline-library=* )
readline_lib=-L`echo $option | sed -e 's/^.*=//;'`
;;
--with-readline-includes=* )
readline_inc=-I`echo $option | sed -e 's/^.*=//;'`
;;
--with-ncurses-library=* )
ncurses_lib=-L`echo $option | sed -e 's/^.*=//;'`
;;
--prefix=* | --install_prefix=* ) --prefix=* | --install_prefix=* )
SETPREFIX=`echo $option | sed -e 's/[^=]*=//;'` SETPREFIX=`echo $option | sed -e 's/[^=]*=//;'`
;; ;;
@@ -487,7 +467,7 @@ case $OPERATINGSYSTEM in
;; ;;
SunOS) SunOS)
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o" 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_setsched=1
try_lockmem=1 try_lockmem=1
add_def SOLARIS add_def SOLARIS
@@ -499,7 +479,7 @@ case $OPERATINGSYSTEM in
add_def FEAT_PRIVDROP add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET" priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
fi fi
echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")" echo "Configuring for illumos (" $SYSTEM "SunOS version" $VERSION ")"
;; ;;
* ) * )
echo "error: $SYSTEM is not supported (yet?)" echo "error: $SYSTEM is not supported (yet?)"
@@ -657,6 +637,7 @@ if [ $feat_ipv6 = "1" ] && \
struct sockaddr_in6 n; struct sockaddr_in6 n;
char p[100]; char p[100];
n.sin6_addr = in6addr_any; n.sin6_addr = in6addr_any;
n.sin6_scope_id = 0;
return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));' return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));'
then then
add_def FEAT_IPV6 add_def FEAT_IPV6
@@ -674,6 +655,20 @@ then
fi fi
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 [ $try_clock_gettime = "1" ]; then
if test_code 'clock_gettime()' 'time.h' '' '' \ if test_code 'clock_gettime()' 'time.h' '' '' \
'clock_gettime(CLOCK_REALTIME, NULL);' 'clock_gettime(CLOCK_REALTIME, NULL);'
@@ -689,10 +684,11 @@ if [ $try_clock_gettime = "1" ]; then
fi fi
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);' 'return getaddrinfo(0, 0, 0, 0);'
then then
add_def HAVE_GETADDRINFO echo "error: getaddrinfo() not found"
exit 1
fi fi
if [ $feat_asyncdns = "1" ] && \ if [ $feat_asyncdns = "1" ] && \
@@ -708,11 +704,11 @@ fi
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
add_def HAVE_ARC4RANDOM add_def HAVE_ARC4RANDOM
fi else
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \ 'return getrandom(NULL, 256, 0);'; then
'return getrandom(NULL, 256, 0);'; then add_def HAVE_GETRANDOM
add_def HAVE_GETRANDOM fi
fi fi
RECVMMSG_CODE=' RECVMMSG_CODE='
@@ -803,10 +799,12 @@ if [ $feat_scfilter = "1" ] && [ $try_seccomp = "1" ] && \
'seccomp_init(SCMP_ACT_KILL);' 'seccomp_init(SCMP_ACT_KILL);'
then then
add_def FEAT_SCFILTER add_def FEAT_SCFILTER
# NAME2IPADDRESS shouldn't be enabled with other operations as the helper if [ $feat_ntp = "1" ]; then
# process works on one request at the time and the async resolver could # NAME2IPADDRESS shouldn't be enabled together with a privops operation
# block the main thread # used by the main thread as the helper process works on one request at
priv_ops="NAME2IPADDRESS RELOADDNS" # a time and the async resolver would block the main thread
priv_ops="NAME2IPADDRESS RELOADDNS"
fi
EXTRA_LIBS="$EXTRA_LIBS -lseccomp" EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
fi fi
@@ -875,37 +873,11 @@ fi
READLINE_LINK="" READLINE_LINK=""
if [ $feat_readline = "1" ]; then if [ $feat_readline = "1" ]; then
if [ $try_editline = "1" ]; then if [ $try_editline = "1" ]; then
if test_code editline 'stdio.h editline/readline.h' \ if test_code editline 'stdio.h editline/readline.h' '' '-ledit' \
"$readline_inc" "$readline_lib -ledit" \
'add_history(readline("prompt"));' 'add_history(readline("prompt"));'
then then
add_def FEAT_READLINE add_def FEAT_READLINE
add_def USE_EDITLINE READLINE_LINK="-ledit"
MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib -ledit"
fi
fi
if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then
if test_code readline 'stdio.h readline/readline.h readline/history.h' \
"$readline_inc" "$readline_lib -lreadline" \
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib -lreadline"
fi
fi
if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then
if test_code 'readline with -lncurses' \
'stdio.h readline/readline.h readline/history.h' \
"$readline_inc" "$readline_lib $ncurses_lib -lreadline -lncurses" \
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses"
fi fi
fi fi
@@ -939,7 +911,7 @@ fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then
test_cflags="`pkg_config --cflags nss`" test_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3" test_link="`pkg_config --libs-only-L nss` -lfreebl3 -lnssutil3"
if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \ if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \
"$test_cflags" "$test_link" \ "$test_cflags" "$test_link" \
'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));' 'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));'
@@ -962,35 +934,76 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]
fi fi
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(NULL, NULL, 0);'
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(NULL, GNUTLS_MAC_AES_CMAC_128, NULL, 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
EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ" EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ" EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
LIBS="$LIBS $HASH_LINK" LIBS="$LIBS $HASH_LINK"
if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ] && \ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
echo "$HASH_LINK" | grep 'nettle' > /dev/null; then if [ "$HASH_OBJ" = "hash_gnutls.o" ]; then
test_cflags="`pkg_config --cflags gnutls`" test_cflags=""
test_link="`pkg_config --libs gnutls`" test_link=""
if test_code 'gnutls' 'gnutls/gnutls.h' \ else
"$test_cflags" "$test_link" ' test_cflags="`pkg_config --cflags gnutls`"
return gnutls_init(NULL, 0) + 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(NULL, 0) + GNUTLS_TLS1_3 +
gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) + gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);' && gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);'
test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
'aes128_set_encrypt_key(NULL, NULL);'
then then
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
LIBS="$LIBS $test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_NTS
add_def HAVE_SIV
if test_code 'SIV in nettle' \ if test_code 'SIV in nettle' \
'nettle/siv-cmac.h' "" "$LIBS" \ 'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key(NULL, NULL);' 'siv_cmac_aes128_set_key(NULL, NULL);'
then then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC add_def HAVE_NETTLE_SIV_CMAC
else
if test_code 'SIV in gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_init(NULL, GNUTLS_CIPHER_AES_128_SIV, NULL);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
add_def HAVE_SIV
else
if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
'aes128_set_encrypt_key(NULL, NULL);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
fi
fi
fi
if grep '#define HAVE_SIV' config.h > /dev/null; then
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
LIBS="$LIBS $test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_NTS
fi fi
fi fi
fi fi

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

View File

@@ -55,7 +55,7 @@ IPv6 sockets will be created.
*-f* _file_:: *-f* _file_::
This option can be used to specify an alternate location for the configuration This option can be used to specify an alternate location for the configuration
file. The default value is _@SYSCONFDIR@/chrony.conf_. file. The compiled-in default value is _@SYSCONFDIR@/chrony.conf_.
*-n*:: *-n*::
When run in this mode, the program will not detach itself from the terminal. When run in this mode, the program will not detach itself from the terminal.
@@ -76,6 +76,12 @@ the log file, syslog, or terminal. The following levels can be specified:
0 (informational), 1 (warning), 2 (non-fatal error), and 3 (fatal error). The 0 (informational), 1 (warning), 2 (non-fatal error), and 3 (fatal error). The
default value is 0. default value is 0.
*-p*::
When run in this mode, *chronyd* will print the configuration and exit. It will
not detach from the terminal. This option can be used to verify the syntax of
the configuration and get the whole configuration, even if it is split into
multiple files and read by the *include* or *confdir* directive.
*-q*:: *-q*::
When run in this mode, *chronyd* will set the system clock once and exit. It When run in this mode, *chronyd* will set the system clock once and exit. It
will not detach from the terminal. will not detach from the terminal.
@@ -94,7 +100,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. 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 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, 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*:: *-R*::
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>> When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
@@ -131,51 +137,73 @@ running, but still allow it to adjust the frequency of the system clock.
*-u* _user_:: *-u* _user_::
This option sets the name of the system user to which *chronyd* will switch This option sets the name of the system user to which *chronyd* will switch
after start in order to drop root privileges. It overrides the after start in order to drop root privileges. It overrides the
<<chrony.conf.adoc#user,*user*>> directive. The default value is <<chrony.conf.adoc#user,*user*>> directive. The compiled-in default value is
_@DEFAULT_USER@_. _@DEFAULT_USER@_.
+ +
On Linux, *chronyd* needs to be compiled with support for the *libcap* library. On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
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 The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent. range of privileged system calls on behalf of the parent.
*-U*::
This option disables a check for root privileges to allow *chronyd* to be
started under a non-root user, assuming the process will have all capabilities
(e.g. provided by the service manager) and access to all files, directories,
and devices, needed to operate correctly in the specified configuration. Note
that different capabilities might be needed with different configurations and
different Linux kernel versions. Starting *chronyd* under a non-root user is
not recommended when the configuration is not known, or at least limited to
specific directives.
*-F* _level_:: *-F* _level_::
This option configures a system call filter when *chronyd* is compiled with This option configures system call filters loaded by *chronyd* processes if it
support for the Linux secure computing (seccomp) facility. In level 1 the was compiled with support for the Linux secure computing (seccomp) facility.
process is killed when a forbidden system call is made, in level -1 the SIGSYS Three levels are defined: 0, 1, 2. The filters are disabled at level 0. At
signal is thrown instead and in level 0 the filter is disabled. The default levels 1 and 2, *chronyd* will be killed if it makes a system call which is
value is 0. 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 At level 1, the filters allow only selected system calls that are normally
version of the system where *chrony* is installed as the filter needs to allow expected to be made by *chronyd*. Other system calls are blocked. This level is
also system calls made from libraries that *chronyd* is using (e.g. libc) and recommended only if it is known to work on the version of the system where
different versions or implementations of the libraries may make different *chrony* is installed. The filters need to allow also system calls made by
system calls. If the filter is missing some system call, *chronyd* could be libraries that *chronyd* is using (e.g. libc), but different versions or
killed even in normal operation. 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_:: *-P* _priority_::
On Linux, this option will select the SCHED_FIFO real-time scheduler at the On Linux, FreeBSD, NetBSD, and illumos this option will select the SCHED_FIFO
specified priority (which must be between 0 and 100). On macOS, this option real-time scheduler at the specified priority (which must be between 0 and
must have either a value of 0 to disable the thread time 100). On macOS, this option must have either a value of 0 to disable the thread
constraint policy or 1 for the policy to be enabled. Other systems do not time constraint policy or 1 for the policy to be enabled. Other systems do not
support this option. The default value is 0. support this option. The default value is 0.
*-m*:: *-m*::
This option will lock *chronyd* into RAM so that it will never be paged out. 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*:: *-x*::
This option disables the control of the system clock. *chronyd* will not try to This option disables the control of the system clock. *chronyd* will not try to
make any adjustments of the clock. It will assume the clock is free running and make any adjustments of the clock. It will assume the clock is free running and
still track its offset and frequency relative to the estimated true time. This still track its offset and frequency relative to the estimated true time. This
option allows *chronyd* to run without the capability to adjust or set the option allows *chronyd* to be started without the capability to adjust or set
system clock (e.g. in some containers) in order to operate as an NTP server. It the system clock (e.g. in some containers) to operate as an NTP server.
is not recommended to run *chronyd* (with or without *-x*) when another process
is controlling the system clock.
*-v*:: *-v*, *--version*::
With this option *chronyd* will print version number to the terminal and exit. With this option *chronyd* will print version number to the terminal and exit.
*-h*, *--help*::
With this option *chronyd* will print a help message to the terminal and exit.
== FILES == FILES
_@SYSCONFDIR@/chrony.conf_ _@SYSCONFDIR@/chrony.conf_

View File

@@ -1,7 +1,7 @@
// This file is part of chrony // This file is part of chrony
// //
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2014-2016 // Copyright (C) Miroslav Lichvar 2014-2016, 2020-2021
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as // it under the terms of version 2 of the GNU General Public License as
@@ -24,17 +24,20 @@
=== How does `chrony` compare to `ntpd`? === How does `chrony` compare to `ntpd`?
`chronyd` was designed to work well in a wide range of conditions and it can `chrony` and `ntpd` are two different implementations of the Network Time
usually synchronise the system clock faster and with better time accuracy. It Protocol (NTP).
doesn't implement some of the less useful NTP modes like broadcast client or
multicast server/client. `chrony` is a newer implementation, which was designed to work well in a wider
range of conditions. It can usually synchronise the system clock faster and
with better time accuracy. It has many features, but it does not implement some
of the less useful NTP modes like broadcast client or multicast server/client.
If your computer is connected to the Internet only for few minutes at a time, If your computer is connected to the Internet only for few minutes at a time,
the network connection is often congested, you turn your computer off or the network connection is often congested, you turn your computer off or
suspend it frequently, the clock is not very stable (e.g. there are rapid suspend it frequently, the clock is not very stable (e.g. there are rapid
changes in the temperature or it's a virtual machine), or you want to use NTP changes in the temperature or it is a virtual machine), or you want to use NTP
on an isolated network with no hardware reference clocks in sight, `chrony` on an isolated network with no hardware reference clocks in sight, `chrony`
will probably work much better for you. will probably work better for you.
For a more detailed comparison of features and performance, see the For a more detailed comparison of features and performance, see the
https://chrony.tuxfamily.org/comparison.html[comparison page] on the `chrony` https://chrony.tuxfamily.org/comparison.html[comparison page] on the `chrony`
@@ -46,9 +49,11 @@ website.
First, the client needs to know which NTP servers it should ask for the current First, the client needs to know which NTP servers it should ask for the current
time. They are specified by the `server` or `pool` directive. The `pool` time. They are specified by the `server` or `pool` directive. The `pool`
directive can be used for names that resolve to multiple addresses. For good directive is used with names that resolve to multiple addresses of different
reliability the client should have at least three servers. The `iburst` option servers. For reliable operation, the client should have at least three servers.
speeds up the initial synchronisation.
The `iburst` option enables a burst of requests to speed up the initial
synchronisation.
To stabilise the initial synchronisation on the next start, the estimated drift To stabilise the initial synchronisation on the next start, the estimated drift
of the system clock is saved to a file specified by the `driftfile` directive. of the system clock is saved to a file specified by the `driftfile` directive.
@@ -59,13 +64,13 @@ slewing, which would take a very long time. The `makestep` directive does
that. that.
In order to keep the real-time clock (RTC) close to the true time, so the In order to keep the real-time clock (RTC) close to the true time, so the
system time is reasonably close to the true time when it's initialised on the system time is reasonably close to the true time when it is initialised on the
next boot from the RTC, the `rtcsync` directive enables a mode in which the next boot from the RTC, the `rtcsync` directive enables a mode in which the
system time is periodically copied to the RTC. It is supported on Linux and system time is periodically copied to the RTC. It is supported on Linux and
macOS. macOS.
If you want to use public NTP servers from the If you wanted to use public NTP servers from the
http://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file https://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file
could be: could be:
---- ----
@@ -75,52 +80,43 @@ makestep 1 3
rtcsync rtcsync
---- ----
=== How do I make an NTP server from an NTP client? === How do I make an NTP server?
You need to add an `allow` directive to the _chrony.conf_ file in order to open By default, `chronyd` does not operate as an NTP server. You need to add an
the NTP port and allow `chronyd` to reply to client requests. `allow` with no `allow` directive to the _chrony.conf_ file in order for `chronyd` to open the
specified subnet allows access from all IPv4 and IPv6 addresses. server NTP port and respond to client requests.
=== I have several computers on a LAN. Should be all clients of an external server? ----
allow 192.168.1.0/24
----
The best configuration is usually to make one computer the server, with An `allow` directive with no specified subnet allows access from all IPv4 and
the others as clients of it. Add a `local` directive to the server's IPv6 addresses.
_chrony.conf_ file. This configuration will be better because
=== Should all computers on a LAN be clients of an external server?
It depends on the requirements. Usually, the best configuration is to make one
computer the server, with the others as clients of it. Add a `local` directive
to the server's _chrony.conf_ file. This configuration will be better because
* the load on the external connection is less * the load on the external connection is less
* the load on the external NTP server(s) is less * the load on the external NTP server(s) is less
* if your external connection goes down, the computers on the LAN * if your external connection goes down, the computers on the LAN
will maintain a common time with each other. will maintain a common time with each other.
=== Must I specify servers by IP address if DNS is not available on chronyd start? === Must I specify servers by IP address if DNS is not available on `chronyd` start?
No. Starting from version 1.25, `chronyd` will keep trying to resolve No, `chronyd` will keep trying to resolve
the names specified by the `server`, `pool`, and `peer` directives in an the names specified by the `server`, `pool`, and `peer` directives in an
increasing interval until it succeeds. The `online` command can be issued from increasing interval until it succeeds. The `online` command can be issued from
`chronyc` to force `chronyd` to try to resolve the names immediately. `chronyc` to force `chronyd` to try to resolve the names immediately.
=== How can I make `chronyd` more secure? === How can I make `chronyd` more secure?
If you don't need to serve time to NTP clients or peers, you can add `port 0` If you do not need to use `chronyc`, or you want to run `chronyc` only
to the _chrony.conf_ file to completely disable the NTP server functionality
and prevent NTP requests from reaching `chronyd`. Starting from version 2.0,
the NTP server port is open only when client access is allowed by the `allow`
directive or command, an NTP peer is configured, or the `broadcast` directive
is used.
If you don't need to use `chronyc` remotely, you can add the following
directives to the configuration file to bind the command sockets to the
loopback interface. This is done by default since version 2.0.
----
bindcmdaddress 127.0.0.1
bindcmdaddress ::1
----
If you don't need to use `chronyc` at all or you need to run `chronyc` only
under the root or _chrony_ user (which can access `chronyd` through a Unix under the root or _chrony_ user (which can access `chronyd` through a Unix
domain socket since version 2.2), you can disable the internet command sockets domain socket), you can disable the IPv4 and IPv6 command sockets (by default
completely by adding `cmdport 0` to the configuration file. listening on localhost) by adding `cmdport 0` to the configuration file.
You can specify an unprivileged user with the `-u` option, or the `user` You can specify an unprivileged user with the `-u` option, or the `user`
directive in the _chrony.conf_ file, to which `chronyd` will switch after start directive in the _chrony.conf_ file, to which `chronyd` will switch after start
@@ -133,29 +129,144 @@ a very limited range of privileged system calls on behalf of the parent.
Also, if `chronyd` is compiled with support for the Linux secure computing Also, if `chronyd` is compiled with support for the Linux secure computing
(seccomp) facility, you can enable a system call filter with the `-F` option. (seccomp) facility, you can enable a system call filter with the `-F` option.
It will significantly reduce the kernel attack surface and possibly prevent It will significantly reduce the kernel attack surface and possibly prevent
kernel exploits from the `chronyd` process if it's compromised. It's kernel exploits from the `chronyd` process if it is compromised. It is
recommended to enable the filter only when it's known to work on the version of recommended to enable the filter only when it is known to work on the version of
the system where `chrony` is installed as the filter needs to allow also system the system where `chrony` is installed as the filter needs to allow also system
calls made from libraries that `chronyd` is using (e.g. libc) and different calls made from libraries that `chronyd` is using (e.g. libc) and different
versions or implementations of the libraries may make different system calls. versions or implementations of the libraries might make different system calls.
If the filter is missing some system call, `chronyd` could be killed even in If the filter is missing some system call, `chronyd` could be killed even in
normal operation. normal operation.
=== How can I make the system clock more secure?
An NTP client synchronising the system clock to an NTP server is susceptible to
various attacks, which can break applications and network protocols relying on
accuracy of the clock (e.g. DNSSEC, Kerberos, TLS, WireGuard).
Generally, a man-in-the-middle (MITM) attacker between the client and server
can
* make fake responses, or modify real responses from the server, to create an
arbitrarily large time and frequency offset, make the server appear more
accurate, insert a leap second, etc.
* delay the requests and/or responses to create a limited time offset and
temporarily also a limited frequency offset
* drop the requests or responses to prevent updates of the clock with new
measurements
* redirect the requests to a different server
The attacks can be combined for a greater effect. The attacker can delay
packets to create a significant frequency offset first and then drop all
subsequent packets to let the clock quickly drift away from the true time.
The attacker might also be able to control the server's clock.
Some attacks cannot be prevented. Monitoring is needed for detection, e.g. the
reachability register in the `sources` report shows missing packets. The extent
to which the attacker can control the client's clock depends on its
configuration.
Enable authentication to prevent `chronyd` from accepting modified, fake, or
redirected packets. It can be enabled with a symmetric key specified by the
`key` option, or Network Time Security (NTS) by the `nts` option (supported
since `chrony` version 4.0). The server needs to support the selected
authentication mechanism. Symmetric keys have to be configured on both client
and server, and each client must have its own key (one per server).
The maximum offset that the attacker can insert in an NTP measurement by
delaying packets can be limited by the `maxdelay` option. The default value is
3 seconds. The measured delay is reported as the peer delay in the `ntpdata`
report and `measurements` log. Set the `maxdelay` option to a value larger than
the maximum value that is normally observed. Note that the delay can increase
significantly even when not under an attack, e.g. when the network is congested
or the routing has changed.
The maximum accepted change in time offset between clock updates can be limited
by the `maxchange` directive. Larger changes in the offset will be ignored or
cause `chronyd` to exit. Note that the attacker can get around this limit by
splitting the offset into multiple smaller offsets and/or creating a large
frequency offset. When this directive is used, `chronyd` will have to be
restarted after a successful attack. It will not be able to recover on its own.
It must not be restarted automatically (e.g. by the service manager).
The impact of a large accepted time offset can be reduced by disabling clock
steps, i.e. by not using the `makestep` and `initstepslew` directives. The
offset will be slowly corrected by speeding up or slowing down the clock at a
rate which can be limited by the `maxslewrate` directive. Disabling clock steps
completely is practical only if the clock cannot gain a larger error on its
own, e.g. when the computer is shut down or suspended, and the `maxslewrate`
limit is large enough to correct an expected error in an acceptable time. The
`rtcfile` directive with the `-s` option can be used to compensate for the RTC
drift.
A more practical approach is to enable `makestep` for a limited number of clock
updates (the 2nd argument of the directive) and limit the offset change in all
updates by the `maxchange` directive. The attacker will be able to make only a
limited step and only if the attack starts in a short window after booting the
computer, or when `chronyd` is restarted without the `-R` option.
The frequency offset can be limited by the `maxdrift` directive. The measured
frequency offset is reported in the drift file, `tracking` report, and
`tracking` log. Set `maxdrift` to a value larger than the maximum absolute
value that is normally observed. Note that the frequency of the clock can
change due to aging of the crystal, differences in calibration of the clock
source between reboots, migrated virtual machine, etc. A typical computer clock
has a drift smaller than 100 parts per million (ppm), but much larger drifts
are possible (e.g. in some virtual machines).
Use only trusted servers, which you expect to be well configured and managed,
using authentication for their own servers, etc. Use multiple servers, ideally
in different locations. The attacker will have to deal with a majority of the
servers in order to pass the source selection and update the clock with a large
offset. Use the `minsources` directive to increase the required number of
selectable sources to make the selection more robust.
Do not specify servers as peers. The symmetric mode is less secure than the
client/server mode. If not authenticated, it is vulnerable to off-path
denial-of-service attacks, and even when it is authenticated, it is still
susceptible to replay attacks.
Mixing of authenticated and unauthenticated servers should generally be
avoided. If mixing is necessary (e.g. for a more accurate and stable
synchronisation to a closer server which does not support authentication), the
authenticated servers should be configured as trusted and required to not allow
the unauthenticated servers to override the authenticated servers in the source
selection. Since `chrony` version 4.0, the selection options are enabled in
such a case automatically. This behaviour can be disabled or modified by the
`authselmode` directive.
An example of a client configuration limiting the impact of the attacks could
be
----
server foo.example.net iburst nts maxdelay 0.1
server bar.example.net iburst nts maxdelay 0.2
server baz.example.net iburst nts maxdelay 0.05
server qux.example.net iburst nts maxdelay 0.1
server quux.example.net iburst nts maxdelay 0.1
minsources 3
maxchange 100 0 0
makestep 0.001 1
maxdrift 100
maxslewrate 100
driftfile /var/lib/chrony/drift
ntsdumpdir /var/lib/chrony
rtcsync
----
=== How can I improve the accuracy of the system clock with NTP sources? === How can I improve the accuracy of the system clock with NTP sources?
Select NTP servers that are well synchronised, stable and close to your Select NTP servers that are well synchronised, stable and close to your
network. It's better to use more than one server, three or four is usually network. It is better to use more than one server. Three or four is usually
recommended as the minimum, so `chronyd` can detect servers that serve false recommended as the minimum, so `chronyd` can detect servers that serve false
time and combine measurements from multiple sources. time and combine measurements from multiple sources.
If you have a network card with hardware timestamping supported on Linux, it If you have a network card with hardware timestamping supported on Linux, it
can be enabled by the *hwtimestamp* directive in the _chrony.conf_ file. It can be enabled by the `hwtimestamp` directive. It should make local receive and
should make local receive and transmit timestamps of NTP packets much more transmit timestamps of NTP packets much more stable and accurate.
accurate.
There are also useful options which can be set in the `server` directive, they The `server` directive has some useful options: `minpoll`, `maxpoll`,
are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio`, `polltarget`, `maxdelay`, `maxdelayratio`, `maxdelaydevratio`, `xleave`,
`maxdelaydevratio`, and `xleave`. `filter`.
The first three options set the minimum and maximum allowed polling interval, The first three options set the minimum and maximum allowed polling interval,
and how should be the actual interval adjusted in the specified range. Their and how should be the actual interval adjusted in the specified range. Their
@@ -163,8 +274,8 @@ default values are 6 (64 seconds) for `minpoll`, 10 (1024 seconds) for
`maxpoll` and 8 (samples) for `polltarget`. The default values should be used `maxpoll` and 8 (samples) for `polltarget`. The default values should be used
for general servers on the Internet. With your own NTP servers, or if you have for general servers on the Internet. With your own NTP servers, or if you have
permission to poll some servers more frequently, setting these options for permission to poll some servers more frequently, setting these options for
shorter polling intervals may significantly improve the accuracy of the system shorter polling intervals might significantly improve the accuracy of the
clock. system clock.
The optimal polling interval depends mainly on two factors, stability of the The optimal polling interval depends mainly on two factors, stability of the
network latency and stability of the system clock (which mainly depends on the network latency and stability of the system clock (which mainly depends on the
@@ -174,7 +285,7 @@ temperature change).
Generally, if the `sourcestats` command usually reports a small number of Generally, if the `sourcestats` command usually reports a small number of
samples retained for a source (e.g. fewer than 16), a shorter polling interval samples retained for a source (e.g. fewer than 16), a shorter polling interval
should be considered. If the number of samples is usually at the maximum of 64, should be considered. If the number of samples is usually at the maximum of 64,
a longer polling interval may work better. a longer polling interval might work better.
An example of the directive for an NTP server on the Internet that you are An example of the directive for an NTP server on the Internet that you are
allowed to poll frequently could be allowed to poll frequently could be
@@ -190,7 +301,7 @@ LAN could be
server ntp.local minpoll 2 maxpoll 4 polltarget 30 server ntp.local minpoll 2 maxpoll 4 polltarget 30
---- ----
The maxdelay options are useful to ignore measurements with an unusally large The maxdelay options are useful to ignore measurements with an unusually large
delay (e.g. due to congestion in the network) and improve the stability of the delay (e.g. due to congestion in the network) and improve the stability of the
synchronisation. The `maxdelaydevratio` option could be added to the example synchronisation. The `maxdelaydevratio` option could be added to the example
with local NTP server with local NTP server
@@ -200,8 +311,8 @@ server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
---- ----
If your server supports the interleaved mode (e.g. it is running `chronyd`), If your server supports the interleaved mode (e.g. it is running `chronyd`),
the `xleave` option should be added to the `server` directive in order to allow the `xleave` option should be added to the `server` directive to enable the
the server to send the client more accurate transmit timestamps (kernel or server to provide the client with more accurate transmit timestamps (kernel or
preferably hardware). For example: preferably hardware). For example:
---- ----
@@ -210,7 +321,7 @@ server ntp.local minpoll 2 maxpoll 4 xleave
When combined with local hardware timestamping, good network switches, and even When combined with local hardware timestamping, good network switches, and even
shorter polling intervals, a sub-microsecond accuracy and stability of a few shorter polling intervals, a sub-microsecond accuracy and stability of a few
tens of nanoseconds may be possible. For example: tens of nanoseconds might be possible. For example:
---- ----
server ntp.local minpoll 0 maxpoll 0 xleave server ntp.local minpoll 0 maxpoll 0 xleave
@@ -221,10 +332,11 @@ For best stability, the CPU should be running at a constant frequency (i.e.
disabled power saving and performance boosting). Energy-Efficient Ethernet disabled power saving and performance boosting). Energy-Efficient Ethernet
(EEE) should be disabled in the network. The switches should be configured to (EEE) should be disabled in the network. The switches should be configured to
prioritize NTP packets, especially if the network is expected to be heavily prioritize NTP packets, especially if the network is expected to be heavily
loaded. loaded. The `dscp` directive can be used to set the Differentiated Services
Code Point in transmitted NTP packets if needed.
If it is acceptable for NTP clients in the network to send requests at an If it is acceptable for NTP clients in the network to send requests at a high
excessive rate, a sub-second polling interval may be specified. A median filter rate, a sub-second polling interval can be specified. A median filter
can be enabled in order to update the clock at a reduced rate with more stable can be enabled in order to update the clock at a reduced rate with more stable
measurements. For example: measurements. For example:
@@ -233,11 +345,20 @@ server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
hwtimestamp eth0 minpoll -6 hwtimestamp eth0 minpoll -6
---- ----
As an experimental feature added in version 4.2, `chronyd` supports an NTPv4
extension field containing an additional timestamp to enable frequency transfer
and significantly improve stability of synchronisation. It can be enabled by
the `extfield F323` option. For example:
----
server ntp.local minpoll 0 maxpoll 0 xleave extfield F323
----
=== Does `chronyd` have an ntpdate mode? === Does `chronyd` have an ntpdate mode?
Yes. With the `-q` option `chronyd` will set the system clock once and exit. Yes. With the `-q` option `chronyd` will set the system clock once and exit.
With the `-Q` option it will print the measured offset without setting the With the `-Q` option it will print the measured offset without setting the
clock. If you don't want to use a configuration file, NTP servers can be clock. If you do not want to use a configuration file, NTP servers can be
specified on the command line. For example: specified on the command line. For example:
---- ----
@@ -249,17 +370,28 @@ well synchronised and responding to all requests. If not synchronised or
responding, it would take about 10 seconds for `chronyd` to give up and exit responding, it would take about 10 seconds for `chronyd` to give up and exit
with a non-zero status. A faster configuration is possible. A single server can with a non-zero status. A faster configuration is possible. A single server can
be used instead of four servers, the number of measurements can be reduced with be used instead of four servers, the number of measurements can be reduced with
the `maxsamples` option, and a timeout can be specified with the `-t` option. the `maxsamples` option to one (supported since `chrony` version 4.0), and a
The following command would take only up to about 1 second. timeout can be specified with the `-t` option. The following command would take
only up to about one second.
---- ----
# chronyd -q -t 1 'server pool.ntp.org iburst maxsamples 1' # chronyd -q -t 1 'server pool.ntp.org iburst maxsamples 1'
---- ----
It is not recommended to run `chronyd` with the `-q` option periodically (e.g.
from a cron job) as a replacement for the daemon mode, because it performs
significantly worse (e.g. the clock is stepped and its frequency is not
corrected). If you must run it this way and you are using a public NTP server,
make sure `chronyd` does not always start around the first second of a minute,
e.g. by adding a random sleep before the `chronyd` command. Public servers
typically receive large bursts of requests around the first second as there is
a large number of NTP clients started from cron with no delay.
=== Can `chronyd` be configured to control the clock like `ntpd`? === Can `chronyd` be configured to control the clock like `ntpd`?
It is not possible to perfectly emulate `ntpd`, but there are some options that It is not possible to perfectly emulate `ntpd`, but there are some options that
can configure `chronyd` to behave more like `ntpd`. can configure `chronyd` to behave more like `ntpd` if there is a reason to
prefer that.
In the following example the `minsamples` directive slows down the response to In the following example the `minsamples` directive slows down the response to
changes in the frequency and offset of the clock. The `maxslewrate` and changes in the frequency and offset of the clock. The `maxslewrate` and
@@ -279,10 +411,93 @@ maxchange 1000 1 1
maxclockerror 15 maxclockerror 15
---- ----
Note that increasing `minsamples` may cause the offsets in the `tracking` and Note that increasing `minsamples` might cause the offsets in the `tracking` and
`sourcestats` reports/logs to be significantly smaller than the actual offsets `sourcestats` reports/logs to be significantly smaller than the actual offsets
and be unsuitable for monitoring. and be unsuitable for monitoring.
=== Can NTP server be separated from NTP client?
Yes, it is possible to run multiple instances of `chronyd` on a computer at the
same time. One can operate primarily as an NTP client to synchronise the system
clock and another as a server for other computers. If they use the same
filesystem, they need to be configured with different pidfiles, Unix domain
command sockets, and any other file or directory specified in the configuration
file. If they run in the same network namespace, they need to use different NTP
and command ports, or bind the ports to different addresses or interfaces.
The server instance should be started with the `-x` option to prevent it from
adjusting the system clock and interfering with the client instance. It can be
configured as a client to synchronise its NTP clock to other servers, or the
client instance running on the same computer. In the latter case, the `copy`
option (added in `chrony` version 4.1) can be used to assume the reference ID
and stratum of the client instance, which enables detection of synchronisation
loops with its own clients.
On Linux, starting with `chrony` version 4.0, it is possible to run multiple
server instances sharing a port to better utilise multiple cores of the CPU.
Note that for rate limiting and client/server interleaved mode to work well
it is necessary that all packets received from the same address are handled by
the same server instance.
An example configuration of the client instance could be
----
pool pool.ntp.org iburst
allow 127.0.0.1
port 11123
driftfile /var/lib/chrony/drift
makestep 1 3
rtcsync
----
and configuration of the first server instance could be
----
server 127.0.0.1 port 11123 minpoll 0 maxpoll 0 copy
allow
cmdport 11323
bindcmdaddress /var/run/chrony/chronyd-server1.sock
pidfile /var/run/chronyd-server1.pid
driftfile /var/lib/chrony/drift-server1
----
=== Should be a leap smear enabled on NTP server?
With the `smoothtime` and `leapsecmode` directives it is possible to enable a
server leap smear in order to hide leap seconds from clients and force them to
follow a slow server's adjustment instead.
This feature should be used only in local networks and only when necessary,
e.g. when the clients cannot be configured to handle the leap seconds as
needed, or their number is so large that configuring them all would be
impractical. The clients should use only one leap-smearing server, or multiple
identically configured leap-smearing servers. Note that some clients can get
leap seconds from other sources (e.g. with the `leapsectz` directive in
`chrony`) and they will not work correctly with a leap smearing server.
=== Does `chrony` support PTP?
No, the Precision Time Protocol (PTP) is not supported as a protocol for
synchronisation of clocks and there are no plans
to support it. It is a complex protocol, which shares some issues with the
NTP broadcast mode. One of the main differences between NTP and PTP is that PTP
was designed to be easily supported in hardware (e.g. network switches and
routers) in order to make more stable and accurate measurements. PTP relies on
the hardware support. NTP does not rely on any support in the hardware, but if
it had the same support as PTP, it could perform equally well.
On Linux, `chrony` supports hardware clocks that some NICs have for PTP. They
are called PTP hardware clocks (PHC). They can be used as reference clocks
(specified by the `refclock` directive) and for hardware timestamping of NTP
packets (enabled by the `hwtimestamp` directive) if the NIC can timestamp other
packets than PTP, which is usually the case at least for transmitted packets.
The `ethtool -T` command can be used to verify the timestamping support.
As an experimental feature added in version 4.2, `chrony` can use PTP as a
transport for NTP messages (NTP over PTP) to enable hardware timestamping on
hardware which can timestamp PTP packets only. It can be enabled by the
`ptpport` directive.
=== What happened to the `commandkey` and `generatecommandkey` directives? === What happened to the `commandkey` and `generatecommandkey` directives?
They were removed in version 2.2. Authentication is no longer supported in the They were removed in version 2.2. Authentication is no longer supported in the
@@ -299,18 +514,17 @@ following questions.
=== Behind a firewall? === Behind a firewall?
Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it's Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it
zero, it means `chronyd` did not get any valid responses from the NTP server is zero, it means `chronyd` did not get any valid responses from the NTP server
you are trying to use. If there is a firewall between you and the server, the you are trying to use. If there is a firewall between you and the server, the
packets may be blocked. Try using a tool like `wireshark` or `tcpdump` to see packets might be blocked. Try using a tool like `wireshark` or `tcpdump` to see
if you're getting any responses from the server. if you are getting any responses from the server.
When `chronyd` is receiving responses from the servers, the output of the When `chronyd` is receiving responses from the servers, the output of the
`sources` command issued few minutes after `chronyd` start might look like `sources` command issued few minutes after `chronyd` start might look like
this: this:
---- ----
210 Number of sources = 3
MS Name/IP address Stratum Poll Reach LastRx Last sample MS Name/IP address Stratum Poll Reach LastRx Last sample
=============================================================================== ===============================================================================
^* foo.example.net 2 6 377 34 +484us[ -157us] +/- 30ms ^* foo.example.net 2 6 377 34 +484us[ -157us] +/- 30ms
@@ -320,9 +534,10 @@ MS Name/IP address Stratum Poll Reach LastRx Last sample
=== Are NTP servers specified with the `offline` option? === Are NTP servers specified with the `offline` option?
Check that you're using ``chronyc``'s `online` and `offline` commands Check that the ``chronyc``'s `online` and `offline` commands are used
appropriately. The `activity` command prints the number of sources that are appropriately (e.g. in the system networking scripts). The `activity` command
currently online and offline. For example: prints the number of sources that are currently online and offline. For
example:
---- ----
200 OK 200 OK
@@ -333,6 +548,19 @@ currently online and offline. For example:
0 sources with unknown address 0 sources with unknown address
---- ----
=== Is name resolution working correctly?
NTP servers specified by their hostname (instead of an IP address) have to have
their names resolved before `chronyd` can send any requests to them. If the
`activity` command prints a non-zero number of sources with unknown address,
there is an issue with the resolution. Typically, a DNS server is specified in
_/etc/resolv.conf_. Make sure it is working correctly.
Since `chrony` version 4.0, you can run `chronyc -N sources -a` command to
print all sources, even those that do not have a known address yet, with their
names as they were specified in the configuration. This can be useful to verify
that the names specified in the configuration are used as expected.
=== Is `chronyd` allowed to step the system clock? === Is `chronyd` allowed to step the system clock?
By default, `chronyd` adjusts the clock gradually by slowing it down or By default, `chronyd` adjusts the clock gradually by slowing it down or
@@ -349,9 +577,9 @@ makestep 1 3
---- ----
the clock would be stepped in the first three updates if its offset was larger the clock would be stepped in the first three updates if its offset was larger
than one second. Normally, it's recommended to allow the step only in the first than one second. Normally, it is recommended to allow the step only in the first
few updates, but in some cases (e.g. a computer without an RTC or virtual few updates, but in some cases (e.g. a computer without an RTC or virtual
machine which can be suspended and resumed with an incorrect time) it may be machine which can be suspended and resumed with an incorrect time) it might be
necessary to allow the step on any clock update. The example above would change necessary to allow the step on any clock update. The example above would change
to to
@@ -359,11 +587,55 @@ to
makestep 1 -1 makestep 1 -1
---- ----
=== Using NTS?
The Network Time Security (NTS) mechanism uses Transport Layer Security (TLS)
to establish the keys needed for authentication of NTP packets.
Run the `authdata` command to check whether the key establishment was
successful:
----
# chronyc -N authdata
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
foo.example.net NTS 1 15 256 33m 0 0 8 100
bar.example.net NTS 1 15 256 33m 0 0 8 100
baz.example.net NTS 1 15 256 33m 0 0 8 100
----
The KeyID, Type, and KLen columns should have non-zero values. If they are
zero, check the system log for error messages from `chronyd`. One possible
cause of failure is a firewall blocking the client's connection to the server's
TCP port 4460.
Another possible cause of failure is a certificate that is failing to verify
because the client's clock is wrong. This is a chicken-and-egg problem with NTS.
You might need to manually correct the date, or temporarily disable NTS, in
order to get NTS working. If your computer has an RTC and it is backed up by a
good battery, this operation should be needed only once, assuming the RTC will
be set periodically with the `rtcsync` directive, or compensated with the
`rtcfile` directive and the `-s` option.
If the computer does not have an RTC or battery, you can use the `-s` option
without `rtcfile` directive to restore time of the last shutdown or reboot from
the drift file. The clock will start behind the true time, but if the computer
was not shut down for too long and the server's certificate was not renewed too
close to its expiration, it should be sufficient for the time checks to
succeed.
As a last resort, you can disable the time checks by the `nocerttimecheck`
directive. This has some important security implications. To reduce the
security risk, you can use the `nosystemcert` and `ntstrustedcerts` directives
to disable the system's default trusted certificate authorities and trust only
a minimal set of selected authorities needed to validate the certificates of
used NTP servers.
=== Using a Windows NTP server? === Using a Windows NTP server?
A common issue with Windows NTP servers is that they report a very large root A common issue with Windows NTP servers is that they report a very large root
dispersion (e.g. three seconds or more), which causes `chronyd` to ignore the dispersion (e.g. three seconds or more), which causes `chronyd` to ignore the
server for being too inaccurate. The `sources` command may show a valid server for being too inaccurate. The `sources` command might show a valid
measurement, but the server is not selected for synchronisation. You can check measurement, but the server is not selected for synchronisation. You can check
the root dispersion of the server with the ``chronyc``'s `ntpdata` command. the root dispersion of the server with the ``chronyc``'s `ntpdata` command.
@@ -374,6 +646,52 @@ synchronisation to such a server. For example:
maxdistance 16.0 maxdistance 16.0
---- ----
=== An unreachable source is selected?
When `chronyd` is configured with multiple time sources, it tries to select the
most accurate and stable sources for synchronisation of the system clock. They
are marked with the _*_ or _+_ symbol in the report printed by the `sources`
command.
When the best source (marked with the _*_ symbol) becomes unreachable (e.g. NTP
server stops responding), `chronyd` will not immediately switch
to the second best source in an attempt to minimise the error of the clock. It
will let the clock run free for as long as its estimated error (in terms of
root distance) based on previous measurements is smaller than the estimated
error of the second source, and there is still an interval which contains some
measurements from both sources.
If the first source was significantly better than the second source, it can
take many hours before the second source is selected, depending on its polling
interval. You can force a faster reselection by increasing the clock error rate
(`maxclockerror` directive), shortening the polling interval (`maxpoll`
option), or reducing the number of samples (`maxsamples` option).
=== Does selected source drop new measurements?
`chronyd` can drop a large number of successive NTP measurements if they are
not passing some of the NTP tests. The `sources` command can report for a
selected source the fully-reachable value of 377 in the Reach column and at the
same time a LastRx value that is much larger than the current polling interval.
If the source is online, this indicates that a number of measurements was
dropped. You can use the `ntpdata` command to check the NTP tests for the last
measurement. Usually, it is the test C which fails.
This can be an issue when there is a long-lasting increase in the measured
delay, e.g. due to a routing change in the network. Unfortunately, `chronyd`
does not know for how long it should wait for the delay to come back to the
original values, or whether it is a permanent increase and it should start from
scratch.
The test C is an adaptive filter. It can take many hours before it accepts
a measurement with the larger delay, and even much longer before it drops all
measurements with smaller delay, which determine an expected delay used by the
test. You can use the `reset sources` command to drop all measurements
immediately (available in chrony 4.0 and later). If this issue happens
frequently, you can effectively disable the test by setting the
`maxdelaydevratio` option to a very large value (e.g. 1000000), or speed up the
recovery by increasing the clock error rate with the `maxclockerror` directive.
=== Using a PPS reference clock? === Using a PPS reference clock?
A pulse-per-second (PPS) reference clock requires a non-PPS time source to A pulse-per-second (PPS) reference clock requires a non-PPS time source to
@@ -409,31 +727,33 @@ to be used for synchronisation.
When accessing `chronyd` remotely, make sure that the _chrony.conf_ file (on When accessing `chronyd` remotely, make sure that the _chrony.conf_ file (on
the computer where `chronyd` is running) has a `cmdallow` entry for the the computer where `chronyd` is running) has a `cmdallow` entry for the
computer you are running `chronyc` on and an appropriate `bindcmdaddress` computer you are running `chronyc` on and an appropriate `bindcmdaddress`
directive. This isn't necessary for localhost. directive. This is not necessary for localhost.
Perhaps `chronyd` is not running. Try using the `ps` command (e.g. on Linux, Perhaps `chronyd` is not running. Try using the `ps` command (e.g. on Linux,
`ps -auxw`) to see if it's running. Or try `netstat -a` and see if the ports `ps -auxw`) to see if it is running. Or try `netstat -a` and see if the UDP
123/udp and 323/udp are listening. If `chronyd` is not running, you may have a port 323 is listening. If `chronyd` is not running, you might have a problem
problem with the way you are trying to start it (e.g. at boot time). with the way you are trying to start it (e.g. at boot time).
Perhaps you have a firewall set up in a way that blocks packets on port Perhaps you have a firewall set up in a way that blocks packets on the UDP
323/udp. You need to amend the firewall configuration in this case. port 323. You need to amend the firewall configuration in this case.
=== I keep getting the error `501 Not authorised` === I keep getting the error `501 Not authorised`
Since version 2.2, the `password` command doesn't do anything and `chronyc` This error indicates that `chronyc` sent the command to `chronyd` using a UDP
needs to run locally under the root or _chrony_ user, which are allowed to socket instead of the Unix domain socket (e.g. _/var/run/chrony/chronyd.sock_),
access the ``chronyd``'s Unix domain command socket. which is required for some commands. For security reasons, only the root and
_chrony_ users are allowed to access the socket.
With older versions, you need to authenticate with the `password` command first It is also possible that the socket does not exist. `chronyd` will not create
or use the `-a` option to authenticate automatically on start. The the socket if the directory has a wrong owner or permissions. In this case
configuration file needs to specify a file which contains keys (`keyfile` there should be an error message from `chronyd` in the system log.
directive) and which key in the key file should be used for `chronyc`
authentication (`commandkey` directive).
=== Why does `chronyc tracking` always print an IPv4 address as reference ID? === What is the reference ID reported by the `tracking` command?
The reference ID is a 32-bit value and in versions before 3.0 it was printed in The reference ID is a 32-bit value used in NTP to prevent synchronisation
loops.
In `chrony` versions before 3.0 it was printed in the
quad-dotted notation, even if the reference source did not actually have an quad-dotted notation, even if the reference source did not actually have an
IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but
for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For
@@ -457,12 +777,12 @@ Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
=== What is the real-time clock (RTC)? === What is the real-time clock (RTC)?
This is the clock which keeps the time even when your computer is turned off. This is the clock which keeps the time even when your computer is turned off.
It is used to initialise the system clock on boot. It normally doesn't drift It is used to initialise the system clock on boot. It normally does not drift
more than few seconds per day. more than few seconds per day.
There are two approaches how `chronyd` can work with it. One is to use the There are two approaches how `chronyd` can work with it. One is to use the
`rtcsync` directive, which tells `chronyd` to enable a kernel mode which sets `rtcsync` directive, which tells `chronyd` to enable a kernel mode which sets
the RTC from the system clock every 11 minutes. `chronyd` itself won't touch the RTC from the system clock every 11 minutes. `chronyd` itself will not touch
the RTC. If the computer is not turned off for a long time, the RTC should the RTC. If the computer is not turned off for a long time, the RTC should
still be close to the true time when the system clock will be initialised from still be close to the true time when the system clock will be initialised from
it on the next boot. it on the next boot.
@@ -472,17 +792,17 @@ monitor the rate at which the RTC gains or loses time. When `chronyd` is
started with the `-s` option on the next boot, it will set the system time from started with the `-s` option on the next boot, it will set the system time from
the RTC and also compensate for the drift it has measured previously. The the RTC and also compensate for the drift it has measured previously. The
`rtcautotrim` directive can be used to keep the RTC close to the true time, but `rtcautotrim` directive can be used to keep the RTC close to the true time, but
it's not strictly necessary if its only purpose is to set the system clock when it is not strictly necessary if its only purpose is to set the system clock when
`chronyd` is started on boot. See the documentation for details. `chronyd` is started on boot. See the documentation for details.
=== I want to use ``chronyd``'s RTC support. Must I disable `hwclock`? === Does `hwclock` have to be disabled?
The `hwclock` program is often set-up by default in the boot and shutdown The `hwclock` program is run by default in the boot and/or shutdown
scripts with many Linux installations. With the kernel RTC synchronisation scripts in some Linux installations. With the kernel RTC synchronisation
(`rtcsync` directive), the RTC will be set also every 11 minutes as long as the (`rtcsync` directive), the RTC will be set also every 11 minutes as long as the
system clock is synchronised. If you want to use ``chronyd``'s RTC monitoring system clock is synchronised. If you want to use ``chronyd``'s RTC monitoring
(`rtcfile` directive), it's important to disable `hwclock` in the shutdown (`rtcfile` directive), it is important to disable `hwclock` in the shutdown
procedure. If you don't, it will over-write the RTC with a new value, unknown procedure. If you do not do that, it will overwrite the RTC with a new value, unknown
to `chronyd`. At the next reboot, `chronyd` started with the `-s` option will to `chronyd`. At the next reboot, `chronyd` started with the `-s` option will
compensate this (wrong) time with its estimate of how far the RTC has drifted compensate this (wrong) time with its estimate of how far the RTC has drifted
whilst the power was off, giving a meaningless initial system time. whilst the power was off, giving a meaningless initial system time.
@@ -501,7 +821,20 @@ things
=== I get `Could not open /dev/rtc, Device or resource busy` in my syslog file === I get `Could not open /dev/rtc, Device or resource busy` in my syslog file
Some other program running on the system may be using the device. Some other program running on the system might be using the device.
=== When I start `chronyd`, the log says `Could not enable RTC interrupt : Invalid argument` (or it may say `disable`)
Your real-time clock hardware might not support the required ioctl requests:
* `RTC_UIE_ON`
* `RTC_UIE_OFF`
A possible solution could be to build the Linux kernel with support for software
emulation instead; try enabling the following configuration option when building
the Linux kernel:
* `CONFIG_RTC_INTF_DEV_UIE_EMUL`
=== What if my computer does not have an RTC or backup battery? === What if my computer does not have an RTC or backup battery?
@@ -516,7 +849,7 @@ observe backward steps.
=== Can `chronyd` be driven from broadcast/multicast NTP servers? === Can `chronyd` be driven from broadcast/multicast NTP servers?
No, the broadcast/multicast client mode is not supported and there is currently No, the broadcast/multicast client mode is not supported and there is currently
no plan to implement it. While the mode may be useful to simplify configuration no plan to implement it. While this mode can simplify configuration
of clients in large networks, it is inherently less accurate and less secure of clients in large networks, it is inherently less accurate and less secure
(even with authentication) than the ordinary client/server mode. (even with authentication) than the ordinary client/server mode.
@@ -533,7 +866,8 @@ thousands of clients using the ordinary client/server mode.
Yes, the `broadcast` directive can be used to enable the broadcast server mode Yes, the `broadcast` directive can be used to enable the broadcast server mode
to serve time to clients in the network which support the broadcast client mode to serve time to clients in the network which support the broadcast client mode
(it's not supported in `chronyd`, see the previous question). (it is not supported in `chronyd`). Note that this mode should generally be
avoided. See the previous question.
=== Can `chronyd` keep the system clock a fixed offset away from real time? === Can `chronyd` keep the system clock a fixed offset away from real time?
@@ -549,9 +883,21 @@ offline, `chronyd` would make new measurements immediately after issuing the
`online` command. `online` command.
Unless the network connection lasts only few minutes (less than the maximum Unless the network connection lasts only few minutes (less than the maximum
polling interval), the delay is usually not a problem, and it may be acceptable polling interval), the delay is usually not a problem, and it might be acceptable
to keep all sources online all the time. to keep all sources online all the time.
=== Why is an offset measured between two computers synchronised to each another?
When two computers are synchronised to each other using the client/server or
symmetric NTP mode, there is an expectation that NTP measurements between the
two computers made on both ends show an average offset close to zero.
With `chronyd` that can be expected only when the interleaved mode is enabled
by the `xleave` option. Otherwise, `chronyd` will use different transmit
timestamps (e.g. daemon timestamp vs kernel timestamp) for serving time and
synchronisation of its own clock, which will cause the other computer to
measure a significant offset.
== Operating systems == Operating systems
=== Does `chrony` support Windows? === Does `chrony` support Windows?

View File

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

View File

@@ -8,11 +8,40 @@ Wants=time-sync.target
[Service] [Service]
Type=oneshot Type=oneshot
# Wait up to ~10 minutes for chronyd to synchronize and the remaining # Wait for chronyd to update the clock and the remaining
# clock correction to be less than 0.1 seconds # correction to be less than 0.1 seconds
ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 600 0.1 0.0 1 ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 0 0.1 0.0 1
# Wait for at most 3 minutes
TimeoutStartSec=180
RemainAfterExit=yes RemainAfterExit=yes
StandardOutput=null StandardOutput=null
CapabilityBoundingSet=
DevicePolicy=closed
DynamicUser=yes
IPAddressAllow=localhost
IPAddressDeny=any
LockPersonality=yes
MemoryDenyWriteExecute=yes
PrivateDevices=yes
PrivateUsers=yes
ProcSubset=pid
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] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@@ -1,5 +1,5 @@
# Use public servers from the pool.ntp.org project. # 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 pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time. # Record the rate at which the system clock gains/losses time.
@@ -25,9 +25,18 @@ rtcsync
# Serve time even if not synchronized to a time source. # Serve time even if not synchronized to a time source.
#local stratum 10 #local stratum 10
# Require authentication (nts or key option) for all NTP sources.
#authselectmode require
# Specify file containing keys for NTP authentication. # Specify file containing keys for NTP authentication.
#keyfile /etc/chrony.keys #keyfile /etc/chrony.keys
# Save NTS keys and cookies.
ntsdumpdir /var/lib/chrony
# Insert/delete leap seconds by slewing instead of stepping.
#leapsecmode slew
# Get TAI-UTC offset and leap seconds from the system tz database. # Get TAI-UTC offset and leap seconds from the system tz database.
#leapsectz right/UTC #leapsectz right/UTC

View File

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

View File

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

View File

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

View File

@@ -10,9 +10,40 @@ Type=forking
PIDFile=/run/chrony/chronyd.pid PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS 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 PrivateTmp=yes
ProcSubset=pid
ProtectControlGroups=yes
ProtectHome=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] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

24
hash.h
View File

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

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

View File

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

View File

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

View File

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

123
keys.c
View File

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

9
keys.h
View File

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

65
local.c
View File

@@ -108,8 +108,8 @@ static double max_clock_error;
#define NSEC_PER_SEC 1000000000 #define NSEC_PER_SEC 1000000000
static void static double
calculate_sys_precision(void) measure_clock_precision(void)
{ {
struct timespec ts, old_ts; struct timespec ts, old_ts;
int iters, diff, best; int iters, diff, best;
@@ -135,18 +135,7 @@ calculate_sys_precision(void)
assert(best > 0); assert(best > 0);
precision_quantum = 1.0e-9 * best; return 1.0e-9 * best;
/* Get rounded log2 value of the measured precision */
precision_log = 0;
while (best < 707106781) {
precision_log--;
best *= 2;
}
assert(precision_log >= -30);
DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
} }
/* ================================================== */ /* ================================================== */
@@ -170,7 +159,16 @@ LCL_Initialise(void)
current_freq_ppm = 0.0; current_freq_ppm = 0.0;
temp_comp_ppm = 0.0; temp_comp_ppm = 0.0;
calculate_sys_precision(); precision_quantum = CNF_GetClockPrecision();
if (precision_quantum <= 0.0)
precision_quantum = measure_clock_precision();
precision_quantum = CLAMP(1.0e-9, precision_quantum, 1.0);
precision_log = round(log(precision_quantum) / log(2.0));
/* NTP code doesn't support smaller log than -30 */
assert(precision_log >= -30);
DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
/* This is the maximum allowed frequency offset in ppm, the time must /* This is the maximum allowed frequency offset in ppm, the time must
never stop or run backwards */ never stop or run backwards */
@@ -185,13 +183,11 @@ LCL_Initialise(void)
void void
LCL_Finalise(void) LCL_Finalise(void)
{ {
while (change_list.next != &change_list) /* Make sure all handlers have been removed */
LCL_RemoveParameterChangeHandler(change_list.next->handler, if (change_list.next != &change_list)
change_list.next->anything); assert(0);
if (dispersion_notify_list.next != &dispersion_notify_list)
while (dispersion_notify_list.next != &dispersion_notify_list) assert(0);
LCL_RemoveDispersionNotifyHandler(dispersion_notify_list.next->handler,
dispersion_notify_list.next->anything);
} }
/* ================================================== */ /* ================================================== */
@@ -509,7 +505,7 @@ LCL_AccumulateDeltaFrequency(double dfreq)
/* ================================================== */ /* ================================================== */
void int
LCL_AccumulateOffset(double offset, double corr_rate) LCL_AccumulateOffset(double offset, double corr_rate)
{ {
struct timespec raw, cooked; struct timespec raw, cooked;
@@ -521,12 +517,14 @@ LCL_AccumulateOffset(double offset, double corr_rate)
LCL_CookTime(&raw, &cooked, NULL); LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&cooked, offset)) if (!check_offset(&cooked, offset))
return; return 0;
(*drv_accrue_offset)(offset, corr_rate); (*drv_accrue_offset)(offset, corr_rate);
/* Dispatch to all handlers */ /* Dispatch to all handlers */
invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust); invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust);
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -590,7 +588,7 @@ LCL_NotifyLeap(int leap)
/* ================================================== */ /* ================================================== */
void int
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate) LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{ {
struct timespec raw, cooked; struct timespec raw, cooked;
@@ -602,7 +600,7 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
LCL_CookTime(&raw, &cooked, NULL); LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&cooked, doffset)) if (!check_offset(&cooked, doffset))
return; return 0;
old_freq_ppm = current_freq_ppm; old_freq_ppm = current_freq_ppm;
@@ -624,6 +622,8 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
/* Dispatch to all handlers */ /* Dispatch to all handlers */
invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust); invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust);
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -691,6 +691,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 int
LCL_CanSystemLeap(void) LCL_CanSystemLeap(void)
{ {

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 forwards (i.e. it is currently slow of true time). Provided is also
a suggested correction rate (correction time * offset). */ 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 /* Routine to apply an immediate offset by doing a sudden step if
possible. (Intended for use after an initial estimate of offset has possible. (Intended for use after an initial estimate of offset has
@@ -171,7 +171,7 @@ extern void LCL_NotifyLeap(int leap);
/* Perform the combination of modifying the frequency and applying /* Perform the combination of modifying the frequency and applying
a slew, in one easy step */ 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);
/* Routine to read the system precision as a log to base 2 value. */ /* Routine to read the system precision as a log to base 2 value. */
extern int LCL_GetSysPrecisionAsLog(void); extern int LCL_GetSysPrecisionAsLog(void);
@@ -197,6 +197,9 @@ extern void LCL_Finalise(void);
to a timezone problem. */ to a timezone problem. */
extern int LCL_MakeStep(void); 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 /* Check if the system driver supports leap seconds, i.e. LCL_SetSystemLeap
does something */ does something */
extern int LCL_CanSystemLeap(void); extern int LCL_CanSystemLeap(void);

View File

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

View File

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

103
main.c
View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009 * Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2012-2018 * Copyright (C) Miroslav Lichvar 2012-2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -38,7 +38,6 @@
#include "ntp_signd.h" #include "ntp_signd.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "nts_ke_client.h"
#include "nts_ke_server.h" #include "nts_ke_server.h"
#include "nts_ntp_server.h" #include "nts_ntp_server.h"
#include "socket.h" #include "socket.h"
@@ -77,11 +76,18 @@ static REF_Mode ref_mode = REF_ModeNormal;
static void static void
do_platform_checks(void) do_platform_checks(void)
{ {
struct timespec ts;
/* Require at least 32-bit integers, two's complement representation and /* Require at least 32-bit integers, two's complement representation and
the usual implementation of conversion of unsigned integers */ the usual implementation of conversion of unsigned integers */
assert(sizeof (int) >= 4); assert(sizeof (int) >= 4);
assert(-1 == ~0); assert(-1 == ~0);
assert((int32_t)4294967295U == (int32_t)-1); 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 +97,7 @@ delete_pidfile(void)
{ {
const char *pidfile = CNF_GetPidFile(); const char *pidfile = CNF_GetPidFile();
if (!pidfile[0]) if (!pidfile)
return; return;
if (!UTI_RemoveFile(NULL, pidfile, NULL)) if (!UTI_RemoveFile(NULL, pidfile, NULL))
@@ -105,9 +111,8 @@ MAI_CleanupAndExit(void)
{ {
if (!initialised) exit(exit_status); if (!initialised) exit(exit_status);
if (CNF_GetDumpDir()[0] != '\0') { LCL_CancelOffsetCorrection();
SRC_DumpSources(); SRC_DumpSources();
}
/* Don't update clock when removing sources */ /* Don't update clock when removing sources */
REF_SetMode(REF_ModeIgnore); REF_SetMode(REF_ModeIgnore);
@@ -116,7 +121,6 @@ MAI_CleanupAndExit(void)
TMC_Finalise(); TMC_Finalise();
MNL_Finalise(); MNL_Finalise();
CLG_Finalise(); CLG_Finalise();
NKC_Finalise();
NKS_Finalise(); NKS_Finalise();
NNS_Finalise(); NNS_Finalise();
NSD_Finalise(); NSD_Finalise();
@@ -125,13 +129,15 @@ MAI_CleanupAndExit(void)
NCR_Finalise(); NCR_Finalise();
NIO_Finalise(); NIO_Finalise();
CAM_Finalise(); CAM_Finalise();
SCK_Finalise();
KEY_Finalise(); KEY_Finalise();
RCL_Finalise(); RCL_Finalise();
SRC_Finalise(); SRC_Finalise();
REF_Finalise(); REF_Finalise();
RTC_Finalise(); RTC_Finalise();
SYS_Finalise(); SYS_Finalise();
SCK_Finalise();
SCH_Finalise(); SCH_Finalise();
LCL_Finalise(); LCL_Finalise();
PRV_Finalise(); PRV_Finalise();
@@ -142,6 +148,8 @@ MAI_CleanupAndExit(void)
HSH_Finalise(); HSH_Finalise();
LOG_Finalise(); LOG_Finalise();
UTI_ResetGetRandomFunctions();
exit(exit_status); exit(exit_status);
} }
@@ -150,7 +158,6 @@ MAI_CleanupAndExit(void)
static void static void
signal_cleanup(int x) signal_cleanup(int x)
{ {
if (!initialised) exit(0);
SCH_QuitProgram(); SCH_QuitProgram();
} }
@@ -186,7 +193,7 @@ ntp_source_resolving_end(void)
NSR_AutoStartSources(); NSR_AutoStartSources();
/* Special modes can end only when sources update their reachability. /* 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()) { if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) {
REF_SetUnsynchronised(); REF_SetUnsynchronised();
} }
@@ -261,7 +268,7 @@ check_pidfile(void)
FILE *in; FILE *in;
int pid, count; int pid, count;
if (!pidfile[0]) if (!pidfile)
return; return;
in = UTI_OpenFile(NULL, pidfile, NULL, 'r', 0); in = UTI_OpenFile(NULL, pidfile, NULL, 'r', 0);
@@ -289,7 +296,7 @@ write_pidfile(void)
const char *pidfile = CNF_GetPidFile(); const char *pidfile = CNF_GetPidFile();
FILE *out; FILE *out;
if (!pidfile[0]) if (!pidfile)
return; return;
out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644); out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644);
@@ -377,8 +384,34 @@ go_daemon(void)
static void static void
print_help(const char *progname) print_help(const char *progname)
{ {
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n", printf("Usage: %s [OPTION]... [DIRECTIVE]...\n\n"
progname); "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 +447,13 @@ int main
int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = -1; int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = -1;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0; int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
int clock_control = 1, system_log = 1, log_severity = LOGS_INFO; int clock_control = 1, system_log = 1, log_severity = LOGS_INFO;
int config_args = 0; int user_check = 1, config_args = 0, print_config = 0;
do_platform_checks(); do_platform_checks();
LOG_Initialise(); LOG_Initialise();
/* Parse (undocumented) long command-line options */ /* Parse long command-line options */
for (optind = 1; optind < argc; optind++) { for (optind = 1; optind < argc; optind++) {
if (!strcmp("--help", argv[optind])) { if (!strcmp("--help", argv[optind])) {
print_help(progname); print_help(progname);
@@ -434,7 +467,7 @@ int main
optind = 1; optind = 1;
/* Parse short command-line options */ /* Parse short command-line options */
while ((opt = getopt(argc, argv, "46df:F:hl:L:mnP:qQrRst:u:vx")) != -1) { while ((opt = getopt(argc, argv, "46df:F:hl:L:mnpP:qQrRst:u:Uvx")) != -1) {
switch (opt) { switch (opt) {
case '4': case '4':
case '6': case '6':
@@ -463,6 +496,13 @@ int main
case 'n': case 'n':
nofork = 1; nofork = 1;
break; break;
case 'p':
print_config = 1;
user_check = 0;
nofork = 1;
system_log = 0;
log_severity = LOGS_WARN;
break;
case 'P': case 'P':
sched_priority = parse_int_arg(optarg); sched_priority = parse_int_arg(optarg);
break; break;
@@ -476,6 +516,7 @@ int main
ref_mode = REF_ModePrintOnce; ref_mode = REF_ModePrintOnce;
nofork = 1; nofork = 1;
client_only = 1; client_only = 1;
user_check = 0;
clock_control = 0; clock_control = 0;
system_log = 0; system_log = 0;
break; break;
@@ -494,6 +535,9 @@ int main
case 'u': case 'u':
user = optarg; user = optarg;
break; break;
case 'U':
user_check = 0;
break;
case 'v': case 'v':
print_version(); print_version();
return 0; return 0;
@@ -506,7 +550,7 @@ int main
} }
} }
if (getuid() && !client_only) if (user_check && getuid() != 0)
LOG_FATAL("Not superuser"); LOG_FATAL("Not superuser");
/* Turn into a daemon */ /* Turn into a daemon */
@@ -527,6 +571,8 @@ int main
DNS_SetAddressFamily(address_family); DNS_SetAddressFamily(address_family);
CNF_Initialise(restarted, client_only); CNF_Initialise(restarted, client_only);
if (print_config)
CNF_EnablePrint();
/* Parse the config file or the remaining command line arguments */ /* Parse the config file or the remaining command line arguments */
config_args = argc - optind; config_args = argc - optind;
@@ -537,6 +583,9 @@ int main
CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]); CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
} }
if (print_config)
return 0;
/* Check whether another chronyd may already be running */ /* Check whether another chronyd may already be running */
check_pidfile(); check_pidfile();
@@ -556,16 +605,20 @@ int main
PRV_Initialise(); PRV_Initialise();
LCL_Initialise(); LCL_Initialise();
SCH_Initialise(); SCH_Initialise();
SCK_Initialise(address_family);
/* Start helper processes if needed */
NKS_PreInitialise(pw->pw_uid, pw->pw_gid, scfilter_level);
SYS_Initialise(clock_control); SYS_Initialise(clock_control);
RTC_Initialise(do_init_rtc); RTC_Initialise(do_init_rtc);
SRC_Initialise(); SRC_Initialise();
RCL_Initialise(); RCL_Initialise();
KEY_Initialise(); KEY_Initialise();
SCK_Initialise();
/* Open privileged ports before dropping root */ /* Open privileged ports before dropping root */
CAM_Initialise(address_family); CAM_Initialise();
NIO_Initialise(address_family); NIO_Initialise();
NCR_Initialise(); NCR_Initialise();
CNF_SetupAccessRestrictions(); CNF_SetupAccessRestrictions();
@@ -583,15 +636,17 @@ int main
/* Drop root privileges if the specified user has a non-zero UID */ /* Drop root privileges if the specified user has a non-zero UID */
if (!geteuid() && (pw->pw_uid || pw->pw_gid)) if (!geteuid() && (pw->pw_uid || pw->pw_gid))
SYS_DropRoot(pw->pw_uid, pw->pw_gid); SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
if (!geteuid())
LOG(LOGS_WARN, "Running with root privileges");
REF_Initialise(); REF_Initialise();
SST_Initialise(); SST_Initialise();
NSR_Initialise(); NSR_Initialise();
NSD_Initialise(); NSD_Initialise();
NNS_Initialise(); NNS_Initialise();
NKS_Initialise(scfilter_level); NKS_Initialise();
NKC_Initialise();
CLG_Initialise(); CLG_Initialise();
MNL_Initialise(); MNL_Initialise();
TMC_Initialise(); TMC_Initialise();

View File

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

View File

@@ -50,12 +50,24 @@ DNS_SetAddressFamily(int family)
DNS_Status DNS_Status
DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs) DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
{ {
#ifdef HAVE_GETADDRINFO
struct addrinfo hints, *res, *ai; struct addrinfo hints, *res, *ai;
int i, result; int i, result;
IPAddr ip;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES); 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)); memset(&hints, 0, sizeof (hints));
switch (address_family) { switch (address_family) {
@@ -95,6 +107,9 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
case AF_INET6: case AF_INET6:
if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET6) if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET6)
continue; continue;
/* Don't return an address that would lose a scope ID */
if (((struct sockaddr_in6 *)ai->ai_addr)->sin6_scope_id != 0)
continue;
ip_addrs[i].family = IPADDR_INET6; ip_addrs[i].family = IPADDR_INET6;
memcpy(&ip_addrs[i].addr.in6, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr, memcpy(&ip_addrs[i].addr.in6, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr,
sizeof (ip_addrs->addr.in6)); sizeof (ip_addrs->addr.in6));
@@ -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); freeaddrinfo(res);
return !max_addrs || ip_addrs[0].family != IPADDR_UNSPEC ? DNS_Success : DNS_Failure; 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) DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len)
{ {
char *result = NULL; char *result = NULL;
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
struct sockaddr_in6 in6; struct sockaddr_in6 saddr;
#else
struct sockaddr_in saddr;
#endif
IPSockAddr ip_saddr; IPSockAddr ip_saddr;
socklen_t slen; socklen_t slen;
char hbuf[NI_MAXHOST]; 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.ip_addr = *ip_addr;
ip_saddr.port = 0; ip_saddr.port = 0;
slen = SCK_IPSockAddrToSockaddr(&ip_saddr, (struct sockaddr *)&in6, sizeof (in6)); slen = SCK_IPSockAddrToSockaddr(&ip_saddr, (struct sockaddr *)&saddr, sizeof (saddr));
if (!getnameinfo((struct sockaddr *)&in6, slen, hbuf, sizeof (hbuf), NULL, 0, 0)) if (!getnameinfo((struct sockaddr *)&saddr, slen, hbuf, sizeof (hbuf), NULL, 0, 0))
result = hbuf; 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) if (result == NULL)
result = UTI_IPToString(ip_addr); result = UTI_IPToString(ip_addr);

31
ntp.h
View File

@@ -113,13 +113,38 @@ typedef struct {
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */ #define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */ #define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
/* Non-authentication extension fields and corresponding internal flags */
#define NTP_EF_EXP1 0xF323
#define NTP_EF_FLAG_EXP1 0x1
/* 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_ExtFieldExp1;
#define NTP_EF_EXP1_MAGIC 0xF5BEDD9AU
/* 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 */ /* Enumeration for authentication modes of NTP packets */
typedef enum { typedef enum {
NTP_AUTH_NONE = 0, /* No authentication */ 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, /* MS-SNTP authenticator field */
NTP_AUTH_MSSNTP_EXT, /* MS-SNTP extended 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; } NTP_AuthMode;
/* Structure describing an NTP packet */ /* Structure describing an NTP packet */
@@ -129,6 +154,7 @@ typedef struct {
NTP_Mode mode; NTP_Mode mode;
int ext_fields; int ext_fields;
int ext_field_flags;
struct { struct {
NTP_AuthMode mode; NTP_AuthMode mode;
@@ -151,7 +177,6 @@ typedef struct {
double peer_dispersion; double peer_dispersion;
double root_delay; double root_delay;
double root_dispersion; double root_dispersion;
int stratum;
} NTP_Sample; } NTP_Sample;
#endif /* GOT_NTP_H */ #endif /* GOT_NTP_H */

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2019 * Copyright (C) Miroslav Lichvar 2019-2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -32,7 +32,6 @@
#include "logging.h" #include "logging.h"
#include "memory.h" #include "memory.h"
#include "ntp_auth.h" #include "ntp_auth.h"
#include "ntp_ext.h"
#include "ntp_signd.h" #include "ntp_signd.h"
#include "nts_ntp.h" #include "nts_ntp.h"
#include "nts_ntp_client.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; int auth_len, max_auth_len;
if (info->length + NTP_MIN_MAC_LENGTH > sizeof (*packet)) {
DEBUG_LOG("Packet too long");
return 0;
}
/* Truncate long MACs in NTPv4 packets to allow deterministic parsing /* Truncate long MACs in NTPv4 packets to allow deterministic parsing
of extension fields (RFC 7822) */ of extension fields (RFC 7822) */
max_auth_len = (info->version == 4 ? NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH) - 4; max_auth_len = (info->version == 4 ? NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH) - 4;
max_auth_len = MIN(max_auth_len, sizeof (NTP_Packet) - info->length - 4); max_auth_len = MIN(max_auth_len, sizeof (*packet) - info->length - 4);
auth_len = KEY_GenerateAuth(key_id, (unsigned char *)packet, info->length, auth_len = KEY_GenerateAuth(key_id, packet, info->length,
(unsigned char *)packet + info->length + 4, max_auth_len); (unsigned char *)packet + info->length + 4, max_auth_len);
if (!auth_len) { if (auth_len < NTP_MIN_MAC_LENGTH - 4) {
DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id); DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id);
return 0; return 0;
} }
*(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id); *(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id);
info->length += 4 + auth_len;
info->auth.mac.start = info->length;
info->auth.mac.length = 4 + auth_len;
info->auth.mac.key_id = key_id;
info->length += info->auth.mac.length;
return 1; return 1;
} }
@@ -86,7 +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 ? trunc_len = info->version == 4 && info->auth.mac.length <= NTP_MAX_V4_MAC_LENGTH ?
NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH; NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH;
if (!KEY_CheckAuth(info->auth.mac.key_id, (void *)packet, info->auth.mac.start, if (!KEY_CheckAuth(info->auth.mac.key_id, packet, info->auth.mac.start,
(unsigned char *)packet + info->auth.mac.start + 4, (unsigned char *)packet + info->auth.mac.start + 4,
info->auth.mac.length - 4, trunc_len - 4)) info->auth.mac.length - 4, trunc_len - 4))
return 0; return 0;
@@ -96,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 static NAU_Instance
create_instance(NTP_AuthMode mode) create_instance(NTP_AuthMode mode)
{ {
@@ -170,11 +147,12 @@ NAU_CreateSymmetricInstance(uint32_t key_id)
/* ================================================== */ /* ================================================== */
NAU_Instance 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); 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; return instance;
} }
@@ -184,7 +162,7 @@ NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, const IPSockAdd
void void
NAU_DestroyInstance(NAU_Instance instance) NAU_DestroyInstance(NAU_Instance instance)
{ {
if (instance->nts) if (instance->mode == NTP_AUTH_NTS)
NNC_DestroyInstance(instance->nts); NNC_DestroyInstance(instance->nts);
Free(instance); 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 int
NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketInfo *info) 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); assert(0);
} }
info->auth.mode = instance->mode;
return 1; 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 int
NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod) 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: case NTP_AUTH_MSSNTP:
/* MS-SNTP requests are not authenticated */ /* MS-SNTP requests are not authenticated */
break; break;
case NTP_AUTH_MSSNTP_EXT:
/* Not supported yet */
return 0;
case NTP_AUTH_NTS: case NTP_AUTH_NTS:
if (!NNS_CheckRequestAuth(request, info, kod)) if (!NNS_CheckRequestAuth(request, info, kod))
return 0; return 0;
@@ -397,14 +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 int
NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info, NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
NTP_Packet *response, NTP_PacketInfo *response_info, NTP_Packet *response, NTP_PacketInfo *response_info,
@@ -434,6 +293,8 @@ NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
return 0; return 0;
} }
response_info->auth.mode = request_info->auth.mode;
return 1; return 1;
} }
@@ -498,3 +359,28 @@ NAU_DumpData(NAU_Instance instance)
break; break;
} }
} }
/* ================================================== */
void
NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report)
{
memset(report, 0, sizeof (*report));
report->mode = instance->mode;
report->last_ke_ago = -1;
switch (instance->mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
report->key_id = instance->key_id;
KEY_GetKeyInfo(instance->key_id, &report->key_type, &report->key_length);
break;
case NTP_AUTH_NTS:
NNC_GetReport(instance->nts, report);
break;
default:
assert(0);
}
}

View File

@@ -29,6 +29,7 @@
#include "addressing.h" #include "addressing.h"
#include "ntp.h" #include "ntp.h"
#include "reports.h"
typedef struct NAU_Instance_Record *NAU_Instance; typedef struct NAU_Instance_Record *NAU_Instance;
@@ -36,7 +37,7 @@ typedef struct NAU_Instance_Record *NAU_Instance;
extern NAU_Instance NAU_CreateNoneInstance(void); extern NAU_Instance NAU_CreateNoneInstance(void);
extern NAU_Instance NAU_CreateSymmetricInstance(uint32_t key_id); extern NAU_Instance NAU_CreateSymmetricInstance(uint32_t key_id);
extern NAU_Instance NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, 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 */ /* Destroy an instance */
extern void NAU_DestroyInstance(NAU_Instance 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() */ /* Perform operations necessary for NAU_GenerateRequestAuth() */
extern int NAU_PrepareRequestAuth(NAU_Instance instance); extern int NAU_PrepareRequestAuth(NAU_Instance instance);
/* Adjust a transmit timestamp for an estimated minimum time it takes to call
NAU_GenerateRequestAuth() */
extern void NAU_AdjustRequestTimestamp(NAU_Instance instance, struct timespec *ts);
/* Extend a request with data required by the authentication mode */ /* Extend a request with data required by the authentication mode */
extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request,
NTP_PacketInfo *info); NTP_PacketInfo *info);
/* 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 /* 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. */ kod code is returned, a KoD response should be sent back. */
extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod); extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod);
/* Adjust a transmit timestamp for an estimated minimum time it takes to call
NAU_GenerateResponseAuth() */
extern void NAU_AdjustResponseTimestamp(NTP_Packet *request, NTP_PacketInfo *info,
struct timespec *ts);
/* Extend a response with data required by the authentication mode. This /* Extend a response with data required by the authentication mode. This
function can be called only if the previous call of NAU_CheckRequestAuth() function can be called only if the previous call of NAU_CheckRequestAuth()
was on the same request. */ was on the same request. */
@@ -89,4 +78,7 @@ extern void NAU_ChangeAddress(NAU_Instance instance, IPAddr *address);
/* Save authentication-specific data to speed up the next start */ /* Save authentication-specific data to speed up the next start */
extern void NAU_DumpData(NAU_Instance instance); extern void NAU_DumpData(NAU_Instance instance);
/* Provide a report about the current authentication state */
extern void NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report);
#endif #endif

File diff suppressed because it is too large Load Diff

View File

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

View File

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

219
ntp_io.c
View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009 * Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018 * Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2021
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -30,9 +30,11 @@
#include "sysincl.h" #include "sysincl.h"
#include "memory.h"
#include "ntp_io.h" #include "ntp_io.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "ptp.h"
#include "sched.h" #include "sched.h"
#include "socket.h" #include "socket.h"
#include "local.h" #include "local.h"
@@ -70,6 +72,16 @@ static int permanent_server_sockets;
/* Flag indicating the server IPv4 socket is bound to an address */ /* Flag indicating the server IPv4 socket is bound to an address */
static int bound_server_sock_fd4; 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 */ /* Flag indicating that we have been initialised */
static int initialised=0; static int initialised=0;
@@ -83,19 +95,20 @@ static void read_from_socket(int sock_fd, int event, void *anything);
static int static int
open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr) open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr)
{ {
int sock_fd, sock_flags, events = SCH_FILE_INPUT; int sock_fd, sock_flags, dscp, events = SCH_FILE_INPUT;
IPSockAddr local_addr; IPSockAddr local_addr;
char *iface;
if (!SCK_IsFamilySupported(family)) if (!SCK_IsIpFamilyEnabled(family))
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
if (!client_only) if (!client_only) {
CNF_GetBindAddress(family, &local_addr.ip_addr); CNF_GetBindAddress(family, &local_addr.ip_addr);
else iface = CNF_GetBindNtpInterface();
} else {
CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr); CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr);
iface = CNF_GetBindAcquisitionInterface();
if (local_addr.ip_addr.family != family) }
SCK_GetAnyLocalIPAddress(family, &local_addr.ip_addr);
local_addr.port = local_port; local_addr.port = local_port;
@@ -103,13 +116,21 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
if (!client_only) if (!client_only)
sock_flags |= SCK_FLAG_BROADCAST; sock_flags |= SCK_FLAG_BROADCAST;
sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, sock_flags); sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, iface, sock_flags);
if (sock_fd < 0) { if (sock_fd < 0) {
if (!client_only) if (!client_only)
LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr)); LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr));
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
dscp = CNF_GetNtpDscp();
if (dscp > 0 && dscp < 64) {
#ifdef IP_TOS
if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
;
#endif
}
if (!client_only && family == IPADDR_INET4 && local_addr.port > 0) if (!client_only && family == IPADDR_INET4 && local_addr.port > 0)
bound_server_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY; bound_server_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
@@ -152,7 +173,7 @@ close_socket(int sock_fd)
/* ================================================== */ /* ================================================== */
void void
NIO_Initialise(int family) NIO_Initialise(void)
{ {
int server_port, client_port; int server_port, client_port;
@@ -191,25 +212,18 @@ NIO_Initialise(int family)
server_sock_ref4 = 0; server_sock_ref4 = 0;
server_sock_ref6 = 0; server_sock_ref6 = 0;
if (family == IPADDR_UNSPEC || family == IPADDR_INET4) { if (permanent_server_sockets && server_port) {
if (permanent_server_sockets && server_port) server_sock_fd4 = open_socket(IPADDR_INET4, server_port, 0, NULL);
server_sock_fd4 = open_socket(IPADDR_INET4, server_port, 0, NULL); server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL);
if (!separate_client_sockets) {
if (client_port != server_port || !server_port)
client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL);
else
client_sock_fd4 = server_sock_fd4;
}
} }
if (family == IPADDR_UNSPEC || family == IPADDR_INET6) { if (!separate_client_sockets) {
if (permanent_server_sockets && server_port) if (client_port != server_port || !server_port) {
server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL); client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL);
if (!separate_client_sockets) { client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL);
if (client_port != server_port || !server_port) } else {
client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL); client_sock_fd4 = server_sock_fd4;
else client_sock_fd6 = server_sock_fd6;
client_sock_fd6 = server_sock_fd6;
} }
} }
@@ -219,6 +233,17 @@ NIO_Initialise(int family)
client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) { client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) {
LOG_FATAL("Could not open NTP sockets"); 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 +261,11 @@ NIO_Finalise(void)
close_socket(server_sock_fd6); close_socket(server_sock_fd6);
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD; 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 #ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Finalise(); NIO_Linux_Finalise();
#endif #endif
@@ -248,17 +278,21 @@ NIO_Finalise(void)
int int
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr) NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
{ {
if (separate_client_sockets) { switch (remote_addr->ip_addr.family) {
return open_separate_client_socket(remote_addr); case IPADDR_INET4:
} else { if (ptp_port > 0 && remote_addr->port == ptp_port)
switch (remote_addr->ip_addr.family) { return ptp_sock_fd4;
case IPADDR_INET4: if (separate_client_sockets)
return client_sock_fd4; return open_separate_client_socket(remote_addr);
case IPADDR_INET6: return client_sock_fd4;
return client_sock_fd6; case IPADDR_INET6:
default: if (ptp_port > 0 && remote_addr->port == ptp_port)
return INVALID_SOCK_FD; 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 +303,8 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
{ {
switch (remote_addr->ip_addr.family) { switch (remote_addr->ip_addr.family) {
case IPADDR_INET4: case IPADDR_INET4:
if (ptp_port > 0 && remote_addr->port == ptp_port)
return ptp_sock_fd4;
if (permanent_server_sockets) if (permanent_server_sockets)
return server_sock_fd4; return server_sock_fd4;
if (server_sock_fd4 == INVALID_SOCK_FD) if (server_sock_fd4 == INVALID_SOCK_FD)
@@ -277,6 +313,8 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
server_sock_ref4++; server_sock_ref4++;
return server_sock_fd4; return server_sock_fd4;
case IPADDR_INET6: case IPADDR_INET6:
if (ptp_port > 0 && remote_addr->port == ptp_port)
return ptp_sock_fd6;
if (permanent_server_sockets) if (permanent_server_sockets)
return server_sock_fd6; return server_sock_fd6;
if (server_sock_fd6 == INVALID_SOCK_FD) if (server_sock_fd6 == INVALID_SOCK_FD)
@@ -291,9 +329,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 void
NIO_CloseClientSocket(int sock_fd) NIO_CloseClientSocket(int sock_fd)
{ {
if (is_ptp_socket(sock_fd))
return;
if (separate_client_sockets) if (separate_client_sockets)
close_socket(sock_fd); close_socket(sock_fd);
} }
@@ -303,7 +353,7 @@ NIO_CloseClientSocket(int sock_fd)
void void
NIO_CloseServerSocket(int sock_fd) 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; return;
if (sock_fd == server_sock_fd4) { if (sock_fd == server_sock_fd4) {
@@ -327,7 +377,7 @@ int
NIO_IsServerSocket(int sock_fd) NIO_IsServerSocket(int sock_fd)
{ {
return sock_fd != INVALID_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 +385,8 @@ NIO_IsServerSocket(int sock_fd)
int int
NIO_IsServerSocketOpen(void) 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;
} }
/* ================================================== */ /* ================================================== */
@@ -390,6 +441,9 @@ process_message(SCK_Message *message, int sock_fd, int event)
DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u", DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u",
UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source); UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source);
if (!NIO_UnwrapMessage(message, sock_fd))
return;
/* Just ignore the packet if it's not of a recognized length */ /* Just ignore the packet if it's not of a recognized length */
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) { if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) {
DEBUG_LOG("Unexpected length"); DEBUG_LOG("Unexpected length");
@@ -428,6 +482,78 @@ read_from_socket(int sock_fd, int event, void *anything)
process_message(&messages[i], sock_fd, event); process_message(&messages[i], sock_fd, event);
} }
/* ================================================== */
int
NIO_UnwrapMessage(SCK_Message *message, int sock_fd)
{
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.version != PTP_VERSION ||
ntohs(msg->header.length) != message->length ||
msg->header.domain != PTP_DOMAIN_NTP ||
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;
DEBUG_LOG("Unwrapped PTP->NTP len=%d", message->length);
return 1;
}
/* ================================================== */
static int
wrap_message(SCK_Message *message, int sock_fd)
{
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;
ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
ptp_message->header.domain = PTP_DOMAIN_NTP;
ptp_message->header.flags = htons(PTP_FLAG_UNICAST);
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 */ /* Send a packet to remote address from local address */
@@ -449,19 +575,26 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
message.data = packet; message.data = packet;
message.length = length; message.length = length;
if (!wrap_message(&message, local_addr->sock_fd))
return 0;
/* Specify remote address if the socket is not connected */ /* Specify remote address if the socket is not connected */
if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) { if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
message.remote_addr.ip.ip_addr = remote_addr->ip_addr; message.remote_addr.ip.ip_addr = remote_addr->ip_addr;
message.remote_addr.ip.port = remote_addr->port; message.remote_addr.ip.port = remote_addr->port;
} }
message.if_index = local_addr->if_index;
message.local_addr.ip = local_addr->ip_addr; message.local_addr.ip = local_addr->ip_addr;
/* Don't require responses to non-link-local addresses to use the same
interface */
message.if_index = SCK_IsLinkLocalIPAddress(&message.remote_addr.ip.ip_addr) ?
local_addr->if_index : INVALID_IF_INDEX;
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR) #if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */ /* On FreeBSD a local IPv4 address cannot be specified on bound socket */
if (message.local_addr.ip.family == IPADDR_INET4 && if (message.local_addr.ip.family == IPADDR_INET4 &&
(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; message.local_addr.ip.family = IPADDR_UNSPEC;
#endif #endif

View File

@@ -31,9 +31,10 @@
#include "ntp.h" #include "ntp.h"
#include "addressing.h" #include "addressing.h"
#include "socket.h"
/* Function to initialise the module. */ /* Function to initialise the module. */
extern void NIO_Initialise(int family); extern void NIO_Initialise(void);
/* Function to finalise the module */ /* Function to finalise the module */
extern void NIO_Finalise(void); extern void NIO_Finalise(void);
@@ -59,6 +60,9 @@ extern int NIO_IsServerSocketOpen(void);
/* Function to check if client packets can be sent to a server */ /* Function to check if client packets can be sent to a server */
extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr); 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);
/* Function to transmit a packet */ /* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx); NTP_Local_Address *local_addr, int length, int process_tx);

View File

@@ -73,7 +73,7 @@ struct Interface {
/* Minimum interval between PHC readings */ /* Minimum interval between PHC readings */
#define MIN_PHC_POLL -6 #define MIN_PHC_POLL -6
/* Maximum acceptable offset between HW and daemon/kernel timestamp */ /* Maximum acceptable offset between SW/HW and daemon timestamp */
#define MAX_TS_DELAY 1.0 #define MAX_TS_DELAY 1.0
/* Array of Interfaces */ /* Array of Interfaces */
@@ -125,7 +125,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
return 1; return 1;
} }
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0); sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0) if (sock_fd < 0)
return 0; return 0;
@@ -189,6 +189,14 @@ add_interface(CNF_HwTsInterface *conf_iface)
rx_filter = HWTSTAMP_FILTER_NTP_ALL; rx_filter = HWTSTAMP_FILTER_NTP_ALL;
break; break;
#endif #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: default:
rx_filter = HWTSTAMP_FILTER_ALL; rx_filter = HWTSTAMP_FILTER_ALL;
break; break;
@@ -200,7 +208,8 @@ add_interface(CNF_HwTsInterface *conf_iface)
req.ifr_data = (char *)&ts_config; req.ifr_data = (char *)&ts_config;
if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) { if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno)); LOG(errno == EPERM ? LOGS_ERR : LOGS_DEBUG,
"ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
/* Check the current timestamping configuration in case this interface /* Check the current timestamping configuration in case this interface
allows only reading of the configuration and it was already configured allows only reading of the configuration and it was already configured
@@ -285,7 +294,7 @@ update_interface_speed(struct Interface *iface)
struct ifreq req; struct ifreq req;
int sock_fd, link_speed; int sock_fd, link_speed;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0); sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0) if (sock_fd < 0)
return; return;
@@ -320,7 +329,7 @@ check_timestamping_option(int option)
{ {
int sock_fd; int sock_fd;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0); sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0) if (sock_fd < 0)
return 0; return 0;
@@ -341,7 +350,7 @@ open_dummy_socket(void)
{ {
int sock_fd, events = 0; int sock_fd, events = 0;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0); sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0) if (sock_fd < 0)
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
@@ -610,6 +619,28 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
local_ts->source = NTP_TS_HARDWARE; local_ts->source = NTP_TS_HARDWARE;
} }
/* ================================================== */
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;
}
/* ================================================== */ /* ================================================== */
/* Extract UDP data from a layer 2 message. Supported is Ethernet /* Extract UDP data from a layer 2 message. Supported is Ethernet
with optional VLAN tags. */ with optional VLAN tags. */
@@ -735,8 +766,7 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) && if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) &&
(!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) { (!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) {
LCL_CookTime(&message->timestamp.kernel, &local_ts->ts, &local_ts->err); process_sw_timestamp(&message->timestamp.kernel, local_ts);
local_ts->source = NTP_TS_KERNEL;
} }
/* If the kernel is slow with enabling RX timestamping, open a dummy /* If the kernel is slow with enabling RX timestamping, open a dummy
@@ -757,7 +787,7 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
l2_length = message->length; l2_length = message->length;
message->length = extract_udp_data(message->data, &message->remote_addr.ip, message->length); message->length = extract_udp_data(message->data, &message->remote_addr.ip, message->length);
DEBUG_LOG("Extracted message for %s fd=%d len=%u", DEBUG_LOG("Extracted message for %s fd=%d len=%d",
UTI_IPSockAddrToString(&message->remote_addr.ip), UTI_IPSockAddrToString(&message->remote_addr.ip),
local_addr->sock_fd, message->length); local_addr->sock_fd, message->length);
@@ -775,7 +805,10 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
return 1; return 1;
} }
if (message->length < NTP_HEADER_LENGTH) if (!NIO_UnwrapMessage(message, local_addr->sock_fd))
return 1;
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet))
return 1; return 1;
NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length); NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length);

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -50,14 +50,15 @@ typedef enum {
} NSR_Status; } NSR_Status;
/* Procedure to add a new server or peer source. */ /* Procedure to add a new server or peer source. */
extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params); extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id);
/* Procedure to add a new server, peer source, or pool of servers specified by /* Procedure to add a new server, peer source, or pool of servers specified by
name instead of address. The name is resolved in exponentially increasing name instead of address. The name is resolved in exponentially increasing
intervals until it succeeds or fails with a non-temporary error. If the intervals until it succeeds or fails with a non-temporary error. If the
name is an address, it is equivalent to NSR_AddSource(). */ name is an address, it is equivalent to NSR_AddSource(). */
extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
SourceParameters *params); SourceParameters *params, uint32_t *conf_id);
/* Function type for handlers to be called back when an attempt /* Function type for handlers to be called back when an attempt
* (possibly unsuccessful) to resolve unresolved sources ends */ * (possibly unsuccessful) to resolve unresolved sources ends */
@@ -76,7 +77,10 @@ extern void NSR_StartSources(void);
extern void NSR_AutoStartSources(void); extern void NSR_AutoStartSources(void);
/* Procedure to remove a source */ /* Procedure to remove a source */
extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr); extern NSR_Status NSR_RemoveSource(IPAddr *address);
/* Procedure to remove all sources matching a configuration ID */
extern void NSR_RemoveSourcesById(uint32_t conf_id);
/* Procedure to remove all sources */ /* Procedure to remove all sources */
extern void NSR_RemoveAllSources(void); extern void NSR_RemoveAllSources(void);
@@ -87,15 +91,16 @@ extern void NSR_HandleBadSource(IPAddr *address);
/* Procedure to resolve all names again */ /* Procedure to resolve all names again */
extern void NSR_RefreshAddresses(void); 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, extern NSR_Status NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr,
NTP_Remote_Address *new_addr); NTP_Remote_Address *new_addr);
/* Procedure to get local reference ID corresponding to a source */ /* Procedure to get local reference ID corresponding to a source */
extern uint32_t NSR_GetLocalRefid(IPAddr *address); extern uint32_t NSR_GetLocalRefid(IPAddr *address);
/* Procedure to get the name of a source. If the source doesn't have a name, /* Procedure to get the name of a source as it was specified (it may be
it returns a temporary string containing formatted address. */ an IP address) */
extern char *NSR_GetName(IPAddr *address); extern char *NSR_GetName(IPAddr *address);
/* This routine is called by ntp_io when a new packet arrives off the network */ /* This routine is called by ntp_io when a new packet arrives off the network */
@@ -136,6 +141,8 @@ extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAd
extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now); extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now);
extern int NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report);
extern int NSR_GetNTPReport(RPT_NTPReport *report); extern int NSR_GetNTPReport(RPT_NTPReport *report);
extern void NSR_GetActivityReport(RPT_ActivityReport *report); extern void NSR_GetActivityReport(RPT_ActivityReport *report);

View File

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

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2020 * Copyright (C) Miroslav Lichvar 2020-2021
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -44,6 +44,7 @@
struct NKC_Instance_Record { struct NKC_Instance_Record {
char *name; char *name;
IPSockAddr address; IPSockAddr address;
NKSN_Credentials credentials;
NKSN_Instance session; NKSN_Instance session;
int destroying; int destroying;
int got_response; int got_response;
@@ -52,13 +53,14 @@ struct NKC_Instance_Record {
NKE_Context context; NKE_Context context;
NKE_Cookie cookies[NKE_MAX_COOKIES]; NKE_Cookie cookies[NKE_MAX_COOKIES];
int num_cookies; int num_cookies;
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 1]; char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
IPSockAddr ntp_address; IPSockAddr ntp_address;
}; };
/* ================================================== */ /* ================================================== */
static void *client_credentials; static NKSN_Credentials default_credentials = NULL;
static int default_credentials_refs = 0;
/* ================================================== */ /* ================================================== */
@@ -71,7 +73,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg
inst->resolving_name = 0; inst->resolving_name = 0;
if (inst->destroying) { if (inst->destroying) {
NKC_DestroyInstance(inst); Free(inst);
return; return;
} }
@@ -84,7 +86,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg
inst->ntp_address.ip_addr = ip_addrs[0]; inst->ntp_address.ip_addr = ip_addrs[0];
/* Prefer an address of the same family as NTS-KE */ /* Prefer an address in the same family as the NTS-KE server */
for (i = 0; i < n_addrs; i++) { for (i = 0; i < n_addrs; i++) {
DEBUG_LOG("%s resolved to %s", inst->server_name, UTI_IPToString(&ip_addrs[i])); DEBUG_LOG("%s resolved to %s", inst->server_name, UTI_IPToString(&ip_addrs[i]));
if (ip_addrs[i].family == inst->address.ip_addr.family) { if (ip_addrs[i].family == inst->address.ip_addr.family) {
@@ -125,9 +127,10 @@ process_response(NKC_Instance inst)
{ {
int next_protocol = -1, aead_algorithm = -1, error = 0; int next_protocol = -1, aead_algorithm = -1, error = 0;
int i, critical, type, length; 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); assert(sizeof (uint16_t) == 2);
inst->num_cookies = 0; inst->num_cookies = 0;
@@ -139,6 +142,13 @@ process_response(NKC_Instance inst)
if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data))) if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data)))
break; 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) { switch (type) {
case NKE_RECORD_NEXT_PROTOCOL: case NKE_RECORD_NEXT_PROTOCOL:
if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) { if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) {
@@ -168,13 +178,21 @@ process_response(NKC_Instance inst)
error = 1; error = 1;
break; break;
case NKE_RECORD_COOKIE: case NKE_RECORD_COOKIE:
DEBUG_LOG("Got cookie #%d length=%d", inst->num_cookies + 1, length); DEBUG_LOG("Got cookie length=%d", length);
assert(NKE_MAX_COOKIE_LENGTH == sizeof (inst->cookies[inst->num_cookies].cookie));
if (length <= NKE_MAX_COOKIE_LENGTH && inst->num_cookies < NKE_MAX_COOKIES) { if (length < 1 || length > NKE_MAX_COOKIE_LENGTH || length % 4 != 0 ||
inst->cookies[inst->num_cookies].length = length; inst->num_cookies >= NKE_MAX_COOKIES) {
memcpy(inst->cookies[inst->num_cookies].cookie, data, length); DEBUG_LOG("Unexpected length/cookie");
inst->num_cookies++; break;
} }
assert(NKE_MAX_COOKIE_LENGTH == sizeof (inst->cookies[inst->num_cookies].cookie));
assert(NKE_MAX_COOKIES == sizeof (inst->cookies) /
sizeof (inst->cookies[inst->num_cookies]));
inst->cookies[inst->num_cookies].length = length;
memcpy(inst->cookies[inst->num_cookies].cookie, data, length);
inst->num_cookies++;
break; break;
case NKE_RECORD_NTPV4_SERVER_NEGOTIATION: case NKE_RECORD_NTPV4_SERVER_NEGOTIATION:
if (length < 1 || length >= sizeof (inst->server_name)) { if (length < 1 || length >= sizeof (inst->server_name)) {
@@ -187,7 +205,7 @@ process_response(NKC_Instance inst)
inst->server_name[length] = '\0'; inst->server_name[length] = '\0';
/* Make sure the name is printable and has no spaces */ /* 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) { if (i != length) {
DEBUG_LOG("Invalid server name"); DEBUG_LOG("Invalid server name");
@@ -244,6 +262,17 @@ handle_message(void *arg)
if (inst->resolving_name) if (inst->resolving_name)
return 0; return 0;
if (!UTI_StringToIP(inst->server_name, &inst->ntp_address.ip_addr)) { 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); DNS_Name2IPAddressAsync(inst->server_name, name_resolve_handler, inst);
inst->resolving_name = 1; inst->resolving_name = 1;
} }
@@ -256,27 +285,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_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; NKC_Instance inst;
int n_certs;
inst = MallocNew(struct NKC_Instance_Record); inst = MallocNew(struct NKC_Instance_Record);
@@ -287,10 +302,21 @@ NKC_CreateInstance(IPSockAddr *address, const char *name)
inst->destroying = 0; inst->destroying = 0;
inst->got_response = 0; inst->got_response = 0;
/* Create the credentials with the first client instance and share them n_certs = CNF_GetNtsTrustedCertsPaths(&trusted_certs, &certs_ids);
with other instances */
if (!client_credentials) /* Share the credentials among clients using the default set of trusted
client_credentials = NKSN_CreateCertCredentials(NULL, NULL, CNF_GetNtsTrustedCertFile()); 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; return inst;
} }
@@ -300,15 +326,29 @@ NKC_CreateInstance(IPSockAddr *address, const char *name)
void void
NKC_DestroyInstance(NKC_Instance inst) NKC_DestroyInstance(NKC_Instance inst)
{ {
/* If the resolver is running, destroy the instance later when finished */ NKSN_DestroyInstance(inst->session);
Free(inst->name);
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) { if (inst->resolving_name) {
inst->destroying = 1; inst->destroying = 1;
return; return;
} }
NKSN_DestroyInstance(inst->session);
Free(inst->name);
Free(inst); Free(inst);
} }
@@ -318,34 +358,36 @@ int
NKC_Start(NKC_Instance inst) NKC_Start(NKC_Instance inst)
{ {
IPSockAddr local_addr; IPSockAddr local_addr;
char label[512]; char label[512], *iface;
int sock_fd; int sock_fd;
assert(!NKC_IsActive(inst)); assert(!NKC_IsActive(inst));
if (!client_credentials) { inst->got_response = 0;
if (!inst->credentials) {
DEBUG_LOG("Missing client credentials"); DEBUG_LOG("Missing client credentials");
return 0; return 0;
} }
/* Follow the bindacqaddress setting */ /* Follow the bindacqaddress and bindacqdevice settings */
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr); CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
if (local_addr.ip_addr.family != inst->address.ip_addr.family)
SCK_GetAnyLocalIPAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
local_addr.port = 0; local_addr.port = 0;
iface = CNF_GetBindAcquisitionInterface();
sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, 0);
if (sock_fd < 0)
return 0;
/* Make a label containing both the address and name of the server */ /* Make a label containing both the address and name of the server */
if (snprintf(label, sizeof (label), "%s (%s)", if (snprintf(label, sizeof (label), "%s (%s)",
UTI_IPSockAddrToString(&inst->address), inst->name) >= sizeof (label)) UTI_IPSockAddrToString(&inst->address), inst->name) >= sizeof (label))
; ;
/* Start a NTS-KE session */ sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, iface, 0);
if (!NKSN_StartSession(inst->session, sock_fd, label, client_credentials, CLIENT_TIMEOUT)) { 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); SCK_CloseSocket(sock_fd);
return 0; return 0;
} }
@@ -388,7 +430,7 @@ NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
*ntp_address = inst->ntp_address; *ntp_address = inst->ntp_address;
return i; return 1;
} }
/* ================================================== */ /* ================================================== */

View File

@@ -32,12 +32,8 @@
typedef struct NKC_Instance_Record *NKC_Instance; typedef struct NKC_Instance_Record *NKC_Instance;
/* Init and fini functions */
extern void NKC_Initialise(void);
extern void NKC_Finalise(void);
/* Create a client NTS-KE instance */ /* Create a client NTS-KE instance */
extern NKC_Instance NKC_CreateInstance(IPSockAddr *address, const char *name); extern NKC_Instance NKC_CreateInstance(IPSockAddr *address, const char *name, uint32_t cert_set);
/* Destroy an instance */ /* Destroy an instance */
extern void NKC_DestroyInstance(NKC_Instance inst); extern void NKC_DestroyInstance(NKC_Instance inst);

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2020 * Copyright (C) Miroslav Lichvar 2020-2021
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -81,7 +81,6 @@ struct NKSN_Instance_Record {
struct Message message; struct Message message;
int new_message; int new_message;
int ended_message;
}; };
/* ================================================== */ /* ================================================== */
@@ -110,6 +109,8 @@ add_record(struct Message *message, int critical, int type, const void *body, in
{ {
struct RecordHeader header; struct RecordHeader header;
assert(message->length <= sizeof (message->data));
if (body_length < 0 || body_length > 0xffff || type < 0 || type > 0x7fff || if (body_length < 0 || body_length > 0xffff || type < 0 || type > 0x7fff ||
message->length + sizeof (header) + body_length > sizeof (message->data)) message->length + sizeof (header) + body_length > sizeof (message->data))
return 0; return 0;
@@ -153,6 +154,7 @@ get_record(struct Message *message, int *critical, int *type, int *body_length,
blen = ntohs(header.body_length); blen = ntohs(header.body_length);
rlen = sizeof (header) + blen; rlen = sizeof (header) + blen;
assert(blen >= 0 && rlen > 0);
if (message->length < message->parsed + rlen) if (message->length < message->parsed + rlen)
return 0; return 0;
@@ -215,16 +217,21 @@ create_tls_session(int server_mode, int sock_fd, const char *server_name,
unsigned int flags; unsigned int flags;
int r; int r;
r = gnutls_init(&session, GNUTLS_NONBLOCK | (server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT)); r = gnutls_init(&session, GNUTLS_NONBLOCK | GNUTLS_NO_TICKETS |
(server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
if (r < 0) { if (r < 0) {
LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r)); LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r));
return NULL; return NULL;
} }
if (!server_mode) { if (!server_mode) {
r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name)); assert(server_name);
if (r < 0)
goto error; 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; flags = 0;
@@ -302,33 +309,25 @@ session_timeout(void *arg)
/* ================================================== */ /* ================================================== */
static int static int
get_socket_error(int sock_fd) check_alpn(NKSN_Instance inst)
{ {
int optval; gnutls_datum_t alpn;
socklen_t optlen = sizeof (optval);
if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) { if (gnutls_alpn_get_selected_protocol(inst->tls_session, &alpn) < 0 ||
DEBUG_LOG("getsockopt() failed : %s", strerror(errno)); alpn.size != sizeof (NKE_ALPN_NAME) - 1 ||
return EINVAL; memcmp(alpn.data, NKE_ALPN_NAME, sizeof (NKE_ALPN_NAME) - 1) != 0)
} return 0;
return optval; return 1;
} }
/* ================================================== */ /* ================================================== */
static int static void
check_alpn(NKSN_Instance inst) set_input_output(NKSN_Instance inst, int output)
{ {
gnutls_datum_t alpn; SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_INPUT, !output);
int r; SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, output);
r = gnutls_alpn_get_selected_protocol(inst->tls_session, &alpn);
if (r < 0 || alpn.size != sizeof (NKE_ALPN_NAME) - 1 ||
strncmp((const char *)alpn.data, NKE_ALPN_NAME, sizeof (NKE_ALPN_NAME) - 1))
return 0;
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -354,7 +353,7 @@ change_state(NKSN_Instance inst, KeState state)
assert(0); assert(0);
} }
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, output); set_input_output(inst, output);
inst->state = state; inst->state = state;
} }
@@ -375,9 +374,11 @@ handle_event(NKSN_Instance inst, int event)
if (event != SCH_FILE_OUTPUT) if (event != SCH_FILE_OUTPUT)
return 0; return 0;
r = get_socket_error(inst->sock_fd); /* Get the socket error */
if (!SCK_GetIntOption(inst->sock_fd, SOL_SOCKET, SO_ERROR, &r))
r = EINVAL;
if (r) { if (r != 0) {
LOG(LOGS_ERR, "Could not connect to %s : %s", inst->label, strerror(r)); LOG(LOGS_ERR, "Could not connect to %s : %s", inst->label, strerror(r));
stop_session(inst); stop_session(inst);
return 0; return 0;
@@ -393,8 +394,22 @@ handle_event(NKSN_Instance inst, int event)
if (r < 0) { if (r < 0) {
if (gnutls_error_is_fatal(r)) { if (gnutls_error_is_fatal(r)) {
gnutls_datum_t cert_error;
/* Get a description of verification errors */
if (r != GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR ||
gnutls_certificate_verification_status_print(
gnutls_session_get_verify_cert_status(inst->tls_session),
gnutls_certificate_type_get(inst->tls_session), &cert_error, 0) < 0)
cert_error.data = NULL;
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"TLS handshake with %s failed : %s", inst->label, gnutls_strerror(r)); "TLS handshake with %s failed : %s%s%s", inst->label, gnutls_strerror(r),
cert_error.data ? " " : "", cert_error.data ? (const char *)cert_error.data : "");
if (cert_error.data)
gnutls_free(cert_error.data);
stop_session(inst); stop_session(inst);
/* Increase the retry interval if the handshake did not fail due /* Increase the retry interval if the handshake did not fail due
@@ -406,8 +421,7 @@ handle_event(NKSN_Instance inst, int event)
} }
/* Disable output when the handshake is trying to receive data */ /* Disable output when the handshake is trying to receive data */
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
gnutls_record_get_direction(inst->tls_session));
return 0; return 0;
} }
@@ -432,6 +446,7 @@ handle_event(NKSN_Instance inst, int event)
case KE_SEND: case KE_SEND:
assert(inst->new_message && message->complete); assert(inst->new_message && message->complete);
assert(message->length <= sizeof (message->data) && message->length > message->sent);
r = gnutls_record_send(inst->tls_session, &message->data[message->sent], r = gnutls_record_send(inst->tls_session, &message->data[message->sent],
message->length - message->sent); message->length - message->sent);
@@ -499,7 +514,9 @@ handle_event(NKSN_Instance inst, int event)
/* Server will send a response to the client */ /* Server will send a response to the client */
change_state(inst, inst->server ? KE_SEND : KE_SHUTDOWN); change_state(inst, inst->server ? KE_SEND : KE_SHUTDOWN);
break;
/* Return success to process the received message */
return 1;
case KE_SHUTDOWN: case KE_SHUTDOWN:
r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR); r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR);
@@ -512,8 +529,7 @@ handle_event(NKSN_Instance inst, int event)
} }
/* Disable output when the TLS shutdown is trying to receive data */ /* Disable output when the TLS shutdown is trying to receive data */
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
gnutls_record_get_direction(inst->tls_session));
return 0; return 0;
} }
@@ -525,9 +541,8 @@ handle_event(NKSN_Instance inst, int event)
default: default:
assert(0); assert(0);
return 0;
} }
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -540,6 +555,9 @@ read_write_socket(int fd, int event, void *arg)
if (!handle_event(inst, event)) if (!handle_event(inst, event))
return; return;
/* A valid message was received. Call the handler to process the message,
and prepare a response if it is a server. */
reset_message_parsing(&inst->message); reset_message_parsing(&inst->message);
if (!(inst->handler)(inst->handler_arg)) { if (!(inst->handler)(inst->handler_arg)) {
@@ -588,16 +606,19 @@ init_gnutls(void)
if (r < 0) if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r)); LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
/* NTS specification requires TLS1.3 or later */ /* Prepare a priority cache for server and client NTS-KE sessions
(the NTS specification requires TLS1.3 or later) */
r = gnutls_priority_init2(&priority_cache, r = gnutls_priority_init2(&priority_cache,
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2", "-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND); NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
if (r < 0) if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r)); LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r));
/* Use our clock instead of the system clock in certificate verification */
gnutls_global_set_time_function(get_time); gnutls_global_set_time_function(get_time);
gnutls_initialised = 1; gnutls_initialised = 1;
DEBUG_LOG("Initialised");
LCL_AddParameterChangeHandler(handle_step, NULL); LCL_AddParameterChangeHandler(handle_step, NULL);
} }
@@ -607,22 +628,26 @@ init_gnutls(void)
static void static void
deinit_gnutls(void) deinit_gnutls(void)
{ {
assert(gnutls_initialised); if (!gnutls_initialised || credentials_counter > 0)
return;
LCL_RemoveParameterChangeHandler(handle_step, NULL); LCL_RemoveParameterChangeHandler(handle_step, NULL);
gnutls_priority_deinit(priority_cache); gnutls_priority_deinit(priority_cache);
gnutls_global_deinit(); gnutls_global_deinit();
gnutls_initialised = 0; gnutls_initialised = 0;
DEBUG_LOG("Deinitialised");
} }
/* ================================================== */ /* ================================================== */
void * static NKSN_Credentials
NKSN_CreateCertCredentials(char *cert, char *key, char *trusted_certs) 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; gnutls_certificate_credentials_t credentials = NULL;
int r; int i, r;
init_gnutls(); init_gnutls();
@@ -630,47 +655,83 @@ NKSN_CreateCertCredentials(char *cert, char *key, char *trusted_certs)
if (r < 0) if (r < 0)
goto error; goto error;
if (cert && key) { if (certs && keys) {
r = gnutls_certificate_set_x509_key_file(credentials, cert, key, if (trusted_certs || trusted_certs_ids)
GNUTLS_X509_FMT_PEM); assert(0);
if (r < 0)
goto error; for (i = 0; i < n_certs_keys; i++) {
r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
}
} else { } 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); r = gnutls_certificate_set_x509_system_trust(credentials);
if (r < 0) if (r < 0)
goto error; goto error;
} }
if (trusted_certs) { if (trusted_certs && trusted_certs_ids) {
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs, for (i = 0; i < n_trusted_certs; i++) {
GNUTLS_X509_FMT_PEM); struct stat buf;
if (r < 0)
goto error; 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++; credentials_counter++;
return credentials; return (NKSN_Credentials)credentials;
error: error:
LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r)); LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
if (credentials) if (credentials)
gnutls_certificate_free_credentials(credentials); gnutls_certificate_free_credentials(credentials);
deinit_gnutls();
return NULL; return NULL;
} }
/* ================================================== */ /* ================================================== */
void NKSN_Credentials
NKSN_DestroyCertCredentials(void *credentials) NKSN_CreateServerCertCredentials(const char **certs, const char **keys, int n_certs_keys)
{ {
gnutls_certificate_free_credentials(credentials); return create_credentials(certs, keys, n_certs_keys, NULL, NULL, 0, 0);
credentials_counter--; }
if (credentials_counter != 0)
return;
/* ================================================== */
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(); deinit_gnutls();
} }
@@ -688,7 +749,7 @@ NKSN_CreateInstance(int server_mode, const char *server_name,
inst->server_name = server_name ? Strdup(server_name) : NULL; inst->server_name = server_name ? Strdup(server_name) : NULL;
inst->handler = handler; inst->handler = handler;
inst->handler_arg = handler_arg; inst->handler_arg = handler_arg;
/* Replace NULL arg with the session itself */ /* Replace a NULL argument with the session itself */
if (!inst->handler_arg) if (!inst->handler_arg)
inst->handler_arg = inst; inst->handler_arg = inst;
@@ -717,12 +778,13 @@ NKSN_DestroyInstance(NKSN_Instance inst)
int int
NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label, NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
void *credentials, double timeout) NKSN_Credentials credentials, double timeout)
{ {
assert(inst->state == KE_STOPPED); assert(inst->state == KE_STOPPED);
inst->tls_session = create_tls_session(inst->server, sock_fd, inst->server_name, inst->tls_session = create_tls_session(inst->server, sock_fd, inst->server_name,
credentials, priority_cache); (gnutls_certificate_credentials_t)credentials,
priority_cache);
if (!inst->tls_session) if (!inst->tls_session)
return 0; return 0;
@@ -735,7 +797,6 @@ NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
reset_message(&inst->message); reset_message(&inst->message);
inst->new_message = 0; inst->new_message = 0;
inst->ended_message = 0;
change_state(inst, inst->server ? KE_HANDSHAKE : KE_WAIT_CONNECT); change_state(inst, inst->server ? KE_HANDSHAKE : KE_WAIT_CONNECT);
@@ -769,6 +830,7 @@ NKSN_EndMessage(NKSN_Instance inst)
{ {
assert(!inst->message.complete); assert(!inst->message.complete);
/* Terminate the message */
if (!add_record(&inst->message, 1, NKE_RECORD_END_OF_MESSAGE, NULL, 0)) if (!add_record(&inst->message, 1, NKE_RECORD_END_OF_MESSAGE, NULL, 0))
return 0; return 0;
@@ -787,9 +849,13 @@ NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
assert(inst->message.complete); assert(inst->message.complete);
if (body_length)
*body_length = 0;
if (!get_record(&inst->message, critical, &type2, body_length, body, buffer_length)) if (!get_record(&inst->message, critical, &type2, body_length, body, buffer_length))
return 0; return 0;
/* Hide the end-of-message record */
if (type2 == NKE_RECORD_END_OF_MESSAGE) if (type2 == NKE_RECORD_END_OF_MESSAGE)
return 0; return 0;
@@ -804,21 +870,27 @@ NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
int int
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c) NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
{ {
c2s->length = SIV_GetKeyLength(siv); int length = SIV_GetKeyLength(siv);
s2c->length = SIV_GetKeyLength(siv);
assert(c2s->length <= sizeof (c2s->key)); if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) {
assert(s2c->length <= sizeof (s2c->key)); DEBUG_LOG("Invalid algorithm");
return 0;
}
if (gnutls_prf_rfc5705(inst->tls_session, if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S, sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S,
c2s->length, (char *)c2s->key) < 0) length, (char *)c2s->key) < 0 ||
return 0; gnutls_prf_rfc5705(inst->tls_session,
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C, 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; return 0;
}
c2s->length = length;
s2c->length = length;
return 1; return 1;
} }

View File

@@ -30,19 +30,25 @@
#include "nts_ke.h" #include "nts_ke.h"
#include "siv.h" #include "siv.h"
typedef struct NKSN_Credentials_Record *NKSN_Credentials;
typedef struct NKSN_Instance_Record *NKSN_Instance; typedef struct NKSN_Instance_Record *NKSN_Instance;
/* Handler for received NTS-KE messages. A non-zero return code stops /* Handler for received NTS-KE messages. A zero return code stops
the session. */ the session. */
typedef int (*NKSN_MessageHandler)(void *arg); typedef int (*NKSN_MessageHandler)(void *arg);
/* Get client or server credentials using certificates of trusted CAs, /* Get server or client credentials using a server certificate and key,
or a server certificate and key. The credentials may be shared between or certificates of trusted CAs. The credentials may be shared between
different clients or servers. */ 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 */ /* Destroy the credentials */
extern void NKSN_DestroyCertCredentials(void *credentials); extern void NKSN_DestroyCertCredentials(NKSN_Credentials credentials);
/* Create an instance */ /* Create an instance */
extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *server_name, extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *server_name,
@@ -53,7 +59,7 @@ extern void NKSN_DestroyInstance(NKSN_Instance inst);
/* Start a new NTS-KE session */ /* Start a new NTS-KE session */
extern int NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label, extern int NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
void *credentials, double timeout); NKSN_Credentials credentials, double timeout);
/* Begin an NTS-KE message. A request should be made right after starting /* Begin an NTS-KE message. A request should be made right after starting
the session and response should be made in the message handler. */ the session and response should be made in the message handler. */

View File

@@ -27,11 +27,6 @@
#ifndef GOT_NTS_NTP_H #ifndef GOT_NTS_NTP_H
#define 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 NTP_KOD_NTS_NAK 0x4e54534e
#define NTS_MIN_UNIQ_ID_LENGTH 32 #define NTS_MIN_UNIQ_ID_LENGTH 32

View File

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

View File

@@ -43,26 +43,37 @@
#include "siv.h" #include "siv.h"
#include "util.h" #include "util.h"
/* Maximum length of all cookies to avoid IP fragmentation */
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108) #define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
/* Magic string of files containing keys and cookies */
#define DUMP_IDENTIFIER "NNC0\n" #define DUMP_IDENTIFIER "NNC0\n"
struct NNC_Instance_Record { struct NNC_Instance_Record {
const IPSockAddr *ntp_address; /* Address of NTS-KE server */
IPSockAddr nts_address; IPSockAddr nts_address;
/* Hostname or IP address for certificate verification */
char *name; char *name;
/* 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;
NKC_Instance nke; NKC_Instance nke;
SIV_Instance siv; SIV_Instance siv;
int load_attempt;
int nke_attempts; int nke_attempts;
double next_nke_attempt; double next_nke_attempt;
double last_nke_success; double last_nke_success;
NKE_Context context; NKE_Context context;
unsigned int context_id;
NKE_Cookie cookies[NTS_MAX_COOKIES]; NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies; int num_cookies;
int cookie_index; int cookie_index;
int auth_ready;
int nak_response; int nak_response;
int ok_response; int ok_response;
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH]; unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
@@ -79,14 +90,23 @@ static void load_cookies(NNC_Instance inst);
static void static void
reset_instance(NNC_Instance inst) reset_instance(NNC_Instance inst)
{ {
inst->load_attempt = 0; 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->nke_attempts = 0;
inst->next_nke_attempt = 0.0; inst->next_nke_attempt = 0.0;
inst->last_nke_success = 0.0; inst->last_nke_success = 0.0;
memset(&inst->context, 0, sizeof (inst->context)); memset(&inst->context, 0, sizeof (inst->context));
inst->context_id = 0;
memset(inst->cookies, 0, sizeof (inst->cookies));
inst->num_cookies = 0; inst->num_cookies = 0;
inst->cookie_index = 0; inst->cookie_index = 0;
inst->auth_ready = 0;
inst->nak_response = 0; inst->nak_response = 0;
inst->ok_response = 1; inst->ok_response = 1;
memset(inst->nonce, 0, sizeof (inst->nonce)); memset(inst->nonce, 0, sizeof (inst->nonce));
@@ -96,20 +116,26 @@ reset_instance(NNC_Instance inst)
/* ================================================== */ /* ================================================== */
NNC_Instance 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; NNC_Instance inst;
inst = MallocNew(struct NNC_Instance_Record); inst = MallocNew(struct NNC_Instance_Record);
inst->ntp_address = ntp_address;
inst->nts_address = *nts_address; inst->nts_address = *nts_address;
inst->name = name ? Strdup(name) : 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->siv = NULL;
inst->nke = NULL; inst->nke = NULL;
reset_instance(inst); reset_instance(inst);
/* Try to reload saved keys and cookies */
load_cookies(inst);
return inst; return inst;
} }
@@ -120,10 +146,7 @@ NNC_DestroyInstance(NNC_Instance inst)
{ {
save_cookies(inst); save_cookies(inst);
if (inst->nke) reset_instance(inst);
NKC_DestroyInstance(inst->nke);
if (inst->siv)
SIV_DestroyInstance(inst->siv);
Free(inst->name); Free(inst->name);
Free(inst); Free(inst);
@@ -134,17 +157,15 @@ NNC_DestroyInstance(NNC_Instance inst)
static int static int
check_cookies(NNC_Instance inst) check_cookies(NNC_Instance inst)
{ {
/* Force NKE if a NAK was received since last valid auth */ /* Force a new NTS-KE session if a NAK was received without a valid response,
if (inst->nak_response && !inst->ok_response && inst->num_cookies > 0) { or the keys encrypting the cookies need to be refreshed */
if (inst->num_cookies > 0 &&
((inst->nak_response && !inst->ok_response) ||
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
inst->num_cookies = 0; inst->num_cookies = 0;
DEBUG_LOG("Dropped cookies"); DEBUG_LOG("Dropped cookies");
} }
/* Force NKE if the keys encrypting the cookies are too old */
if (inst->num_cookies > 0 &&
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())
inst->num_cookies = 0;
return inst->num_cookies > 0; return inst->num_cookies > 0;
} }
@@ -155,13 +176,13 @@ set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
{ {
NTP_Remote_Address old_address, new_address; NTP_Remote_Address old_address, new_address;
old_address = *inst->ntp_address; old_address = inst->ntp_address;
new_address = *negotiated_address; new_address = *negotiated_address;
if (new_address.ip_addr.family == IPADDR_UNSPEC) 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) 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 && if (UTI_CompareIPs(&old_address.ip_addr, &new_address.ip_addr, NULL) == 0 &&
old_address.port == new_address.port) old_address.port == new_address.port)
@@ -174,6 +195,8 @@ set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
return 0; return 0;
} }
inst->ntp_address = new_address;
return 1; return 1;
} }
@@ -201,10 +224,11 @@ get_cookies(NNC_Instance inst)
double now; double now;
int got_data; int got_data;
assert(!check_cookies(inst)); assert(inst->num_cookies == 0);
now = SCH_GetLastEventMonoTime(); now = SCH_GetLastEventMonoTime();
/* Create and start a new NTS-KE session if not already present */
if (!inst->nke) { if (!inst->nke) {
if (now < inst->next_nke_attempt) { if (now < inst->next_nke_attempt) {
DEBUG_LOG("Limiting NTS-KE request rate (%f seconds)", DEBUG_LOG("Limiting NTS-KE request rate (%f seconds)",
@@ -212,13 +236,7 @@ get_cookies(NNC_Instance inst)
return 0; return 0;
} }
if (!inst->name) { inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set);
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->nke_attempts++; inst->nke_attempts++;
update_next_nke_attempt(inst, now); update_next_nke_attempt(inst, now);
@@ -229,9 +247,13 @@ get_cookies(NNC_Instance inst)
update_next_nke_attempt(inst, now); update_next_nke_attempt(inst, now);
/* Wait until the session stops */
if (NKC_IsActive(inst->nke)) if (NKC_IsActive(inst->nke))
return 0; return 0;
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
/* Get the new keys, cookies and NTP address if the session was successful */
got_data = NKC_GetNtsData(inst->nke, &inst->context, got_data = NKC_GetNtsData(inst->nke, &inst->context,
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES, inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
&ntp_address); &ntp_address);
@@ -246,16 +268,17 @@ get_cookies(NNC_Instance inst)
SIV_DestroyInstance(inst->siv); SIV_DestroyInstance(inst->siv);
inst->siv = NULL; inst->siv = NULL;
inst->context_id++;
/* Force a new session if the NTP address is used by another source, with
an expectation that it will eventually get a non-conflicting address */
if (!set_ntp_address(inst, &ntp_address)) { if (!set_ntp_address(inst, &ntp_address)) {
inst->num_cookies = 0; inst->num_cookies = 0;
return 0; return 0;
} }
inst->cookie_index = 0;
inst->nak_response = 0;
inst->last_nke_success = now; inst->last_nke_success = now;
inst->cookie_index = 0;
return 1; return 1;
} }
@@ -265,16 +288,21 @@ get_cookies(NNC_Instance inst)
int int
NNC_PrepareForAuth(NNC_Instance inst) NNC_PrepareForAuth(NNC_Instance inst)
{ {
if (!inst->load_attempt) { inst->auth_ready = 0;
load_cookies(inst);
inst->load_attempt = 1;
}
/* 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 (!check_cookies(inst)) {
if (!get_cookies(inst)) if (!get_cookies(inst))
return 0; return 0;
} }
inst->nak_response = 0;
if (!inst->siv) if (!inst->siv)
inst->siv = SIV_CreateInstance(inst->context.algorithm); inst->siv = SIV_CreateInstance(inst->context.algorithm);
@@ -284,8 +312,7 @@ NNC_PrepareForAuth(NNC_Instance inst)
return 0; return 0;
} }
UTI_GetRandomBytes(&inst->uniq_id, sizeof (inst->uniq_id)); inst->auth_ready = 1;
UTI_GetRandomBytes(&inst->nonce, sizeof (inst->nonce));
return 1; return 1;
} }
@@ -300,18 +327,26 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
int i, req_cookies; int i, req_cookies;
void *ef_body; void *ef_body;
if (inst->num_cookies == 0 || !inst->siv) if (!inst->auth_ready)
return 0;
inst->auth_ready = 0;
if (inst->num_cookies <= 0 || !inst->siv)
return 0; return 0;
if (info->mode != MODE_CLIENT) if (info->mode != MODE_CLIENT)
return 0; return 0;
cookie = &inst->cookies[inst->cookie_index]; 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)); MAX_TOTAL_COOKIE_LENGTH / (cookie->length + 4));
if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER, if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER,
&inst->uniq_id, sizeof (inst->uniq_id))) inst->uniq_id, sizeof (inst->uniq_id)))
return 0; return 0;
if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE, if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE,
@@ -329,8 +364,6 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
(const unsigned char *)"", 0, NTP_MAX_V4_MAC_LENGTH + 4)) (const unsigned char *)"", 0, NTP_MAX_V4_MAC_LENGTH + 4))
return 0; return 0;
inst->num_cookies--;
inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES;
inst->ok_response = 0; inst->ok_response = 0;
return 1; return 1;
@@ -338,6 +371,23 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
/* ================================================== */ /* ================================================== */
static int
parse_encrypted_efs(NNC_Instance inst, unsigned char *plaintext, int length)
{
int ef_length, parsed;
for (parsed = 0; parsed < length; parsed += ef_length) {
if (!NEF_ParseSingleField(plaintext, length, parsed, &ef_length, NULL, NULL, NULL)) {
DEBUG_LOG("Could not parse encrypted EF");
return 0;
}
}
return 1;
}
/* ================================================== */
static int static int
extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length) extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
{ {
@@ -349,7 +399,7 @@ extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
for (parsed = 0; parsed < length; parsed += ef_length) { for (parsed = 0; parsed < length; parsed += ef_length) {
if (!NEF_ParseSingleField(plaintext, length, parsed, if (!NEF_ParseSingleField(plaintext, length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length)) &ef_length, &ef_type, &ef_body, &ef_body_length))
break; return 0;
if (ef_type != NTP_EF_NTS_COOKIE) if (ef_type != NTP_EF_NTS_COOKIE)
continue; continue;
@@ -365,6 +415,9 @@ extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
continue; continue;
index = (inst->cookie_index + inst->num_cookies) % NTS_MAX_COOKIES; index = (inst->cookie_index + inst->num_cookies) % NTS_MAX_COOKIES;
assert(index >= 0 && index < NTS_MAX_COOKIES);
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
memcpy(inst->cookies[index].cookie, ef_body, ef_body_length); memcpy(inst->cookies[index].cookie, ef_body, ef_body_length);
inst->cookies[index].length = ef_body_length; inst->cookies[index].length = ef_body_length;
inst->num_cookies++; inst->num_cookies++;
@@ -391,8 +444,8 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
if (info->ext_fields == 0 || info->mode != MODE_SERVER) if (info->ext_fields == 0 || info->mode != MODE_SERVER)
return 0; return 0;
/* Accept only one response per request */ /* Accept at most one response per request */
if (inst->ok_response) if (inst->ok_response || inst->auth_ready)
return 0; return 0;
if (!inst->siv || if (!inst->siv ||
@@ -404,7 +457,8 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) { for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed, if (!NEF_ParseField(packet, info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length)) &ef_length, &ef_type, &ef_body, &ef_body_length))
break; /* This is not expected as the packet already passed parsing */
return 0;
switch (ef_type) { switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER: case NTP_EF_NTS_UNIQUE_IDENTIFIER:
@@ -428,6 +482,9 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
plaintext, sizeof (plaintext), &plaintext_length)) plaintext, sizeof (plaintext), &plaintext_length))
return 0; return 0;
if (!parse_encrypted_efs(inst, plaintext, plaintext_length))
return 0;
has_valid_auth = 1; has_valid_auth = 1;
break; break;
default: default:
@@ -467,16 +524,14 @@ NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
{ {
save_cookies(inst); save_cookies(inst);
if (inst->nke)
NKC_DestroyInstance(inst->nke);
inst->nke = NULL;
inst->num_cookies = 0;
inst->nts_address.ip_addr = *address; inst->nts_address.ip_addr = *address;
inst->ntp_address.ip_addr = *address;
reset_instance(inst); reset_instance(inst);
DEBUG_LOG("NTS reset"); DEBUG_LOG("NTS reset");
load_cookies(inst);
} }
/* ================================================== */ /* ================================================== */
@@ -507,9 +562,10 @@ save_cookies(NNC_Instance inst)
context_time = inst->last_nke_success - SCH_GetLastEventMonoTime(); context_time = inst->last_nke_success - SCH_GetLastEventMonoTime();
context_time += UTI_TimespecToDouble(&now); context_time += UTI_TimespecToDouble(&now);
if (fprintf(f, "%s%.1f\n%s %d\n%d ", if (fprintf(f, "%s%s\n%.1f\n%s %d\n%u %d ",
DUMP_IDENTIFIER, context_time, UTI_IPToString(&inst->ntp_address->ip_addr), DUMP_IDENTIFIER, inst->name, context_time,
inst->ntp_address->port, (int)inst->context.algorithm) < 0 || 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)) || !UTI_BytesToHex(inst->context.s2c.key, inst->context.s2c.length, buf, sizeof (buf)) ||
fprintf(f, "%s ", buf) < 0 || fprintf(f, "%s ", buf) < 0 ||
!UTI_BytesToHex(inst->context.c2s.key, inst->context.c2s.length, buf, sizeof (buf)) || !UTI_BytesToHex(inst->context.c2s.key, inst->context.c2s.length, buf, sizeof (buf)) ||
@@ -538,12 +594,13 @@ error:
/* ================================================== */ /* ================================================== */
#define MAX_WORDS 3 #define MAX_WORDS 4
static void static void
load_cookies(NNC_Instance inst) load_cookies(NNC_Instance inst)
{ {
char line[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename, *words[MAX_WORDS]; char line[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename, *words[MAX_WORDS];
unsigned int context_id;
int i, algorithm, port; int i, algorithm, port;
double context_time; double context_time;
struct timespec now; struct timespec now;
@@ -569,18 +626,20 @@ load_cookies(NNC_Instance inst)
inst->siv = NULL; inst->siv = NULL;
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 || 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 || !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
sscanf(words[0], "%lf", &context_time) != 1 || sscanf(words[0], "%lf", &context_time) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 || !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
!UTI_StringToIP(words[0], &ntp_addr.ip_addr) || sscanf(words[1], "%d", &port) != 1 || !UTI_StringToIP(words[0], &ntp_addr.ip_addr) || sscanf(words[1], "%d", &port) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 3 || !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 4 ||
sscanf(words[0], "%d", &algorithm) != 1) sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
goto error; goto error;
inst->context.algorithm = algorithm; inst->context.algorithm = algorithm;
inst->context.s2c.length = UTI_HexToBytes(words[1], inst->context.s2c.key, inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
sizeof (inst->context.s2c.key)); sizeof (inst->context.s2c.key));
inst->context.c2s.length = UTI_HexToBytes(words[2], inst->context.c2s.key, inst->context.c2s.length = UTI_HexToBytes(words[3], inst->context.c2s.key,
sizeof (inst->context.c2s.key)); sizeof (inst->context.c2s.key));
if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) || if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
@@ -608,6 +667,9 @@ load_cookies(NNC_Instance inst)
if (context_time > 0) if (context_time > 0)
context_time = 0; context_time = 0;
inst->last_nke_success = context_time + SCH_GetLastEventMonoTime(); inst->last_nke_success = context_time + SCH_GetLastEventMonoTime();
inst->context_id = context_id;
fclose(f);
DEBUG_LOG("Loaded %d cookies for %s", i, filename); DEBUG_LOG("Loaded %d cookies for %s", i, filename);
return; return;
@@ -627,3 +689,21 @@ NNC_DumpData(NNC_Instance inst)
{ {
save_cookies(inst); save_cookies(inst);
} }
/* ================================================== */
void
NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report)
{
report->key_id = inst->context_id;
report->key_type = inst->context.algorithm;
report->key_length = 8 * inst->context.s2c.length;
report->ke_attempts = inst->nke_attempts;
if (report->key_length > 0)
report->last_ke_ago = SCH_GetLastEventMonoTime() - inst->last_nke_success;
else
report->last_ke_ago = -1;
report->cookies = inst->num_cookies;
report->cookie_length = inst->num_cookies > 0 ? inst->cookies[inst->cookie_index].length : 0;
report->nak = inst->nak_response;
}

View File

@@ -29,11 +29,12 @@
#include "addressing.h" #include "addressing.h"
#include "ntp.h" #include "ntp.h"
#include "reports.h"
typedef struct NNC_Instance_Record *NNC_Instance; typedef struct NNC_Instance_Record *NNC_Instance;
extern NNC_Instance NNC_CreateInstance(IPSockAddr *nts_address, const char *name, 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 void NNC_DestroyInstance(NNC_Instance inst);
extern int NNC_PrepareForAuth(NNC_Instance inst); extern int NNC_PrepareForAuth(NNC_Instance inst);
extern int NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet, extern int NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
@@ -45,4 +46,6 @@ extern void NNC_ChangeAddress(NNC_Instance inst, IPAddr *address);
extern void NNC_DumpData(NNC_Instance inst); extern void NNC_DumpData(NNC_Instance inst);
extern void NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report);
#endif #endif

View File

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

View File

@@ -87,7 +87,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */ REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */
REQ_LENGTH_ENTRY(null, null), /* WRITERTC */ REQ_LENGTH_ENTRY(null, null), /* WRITERTC */
REQ_LENGTH_ENTRY(dfreq, null), /* DFREQ */ 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(null, tracking), /* TRACKING */
REQ_LENGTH_ENTRY(sourcestats, sourcestats), /* SOURCESTATS */ REQ_LENGTH_ENTRY(sourcestats, sourcestats), /* SOURCESTATS */
REQ_LENGTH_ENTRY(null, rtc), /* RTCREPORT */ REQ_LENGTH_ENTRY(null, rtc), /* RTCREPORT */
@@ -110,8 +110,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(smoothtime, null), /* SMOOTHTIME */ REQ_LENGTH_ENTRY(smoothtime, null), /* SMOOTHTIME */
REQ_LENGTH_ENTRY(null, null), /* REFRESH */ REQ_LENGTH_ENTRY(null, null), /* REFRESH */
REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */ REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */
REQ_LENGTH_ENTRY(client_accesses_by_index, { 0, 0 }, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */ REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */ REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
{ 0, 0 }, /* ADD_SERVER2 */ { 0, 0 }, /* ADD_SERVER2 */
@@ -123,7 +122,13 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SOURCE */ REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SOURCE */
REQ_LENGTH_ENTRY(ntp_source_name, REQ_LENGTH_ENTRY(ntp_source_name,
ntp_source_name), /* NTP_SOURCE_NAME */ ntp_source_name), /* NTP_SOURCE_NAME */
REQ_LENGTH_ENTRY(null, null), /* RESET */ REQ_LENGTH_ENTRY(null, null), /* RESET_SOURCES */
REQ_LENGTH_ENTRY(auth_data, auth_data), /* AUTH_DATA */
REQ_LENGTH_ENTRY(client_accesses_by_index,
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
}; };
static const uint16_t reply_lengths[] = { static const uint16_t reply_lengths[] = {
@@ -141,12 +146,17 @@ static const uint16_t reply_lengths[] = {
0, /* MANUAL_LIST - not supported */ 0, /* MANUAL_LIST - not supported */
RPY_LENGTH_ENTRY(activity), /* ACTIVITY */ RPY_LENGTH_ENTRY(activity), /* ACTIVITY */
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */ RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */ 0, /* SERVER_STATS - not supported */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */ 0, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */ RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */ RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */ RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */ RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */
RPY_LENGTH_ENTRY(auth_data), /* AUTH_DATA */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
0, /* SERVER_STATS2 - not supported */
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS3 */
}; };
/* ================================================== */ /* ================================================== */

View File

@@ -255,7 +255,7 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr); SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() && 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); SCK_CloseSocket(sock_fd);
res_fatal(res, "Invalid port %d", ip_saddr.port); res_fatal(res, "Invalid port %d", ip_saddr.port);
return; return;
@@ -547,7 +547,7 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr); SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() && 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); assert(0);
if (!have_helper()) if (!have_helper())
@@ -662,6 +662,8 @@ PRV_StartHelper(void)
close(fd); close(fd);
} }
UTI_ResetGetRandomFunctions();
/* ignore signals, the process will exit on OP_QUIT request */ /* ignore signals, the process will exit on OP_QUIT request */
UTI_SetQuitSignalsHandler(SIG_IGN, 1); UTI_SetQuitSignalsHandler(SIG_IGN, 1);

64
ptp.h Normal file
View File

@@ -0,0 +1,64 @@
/*
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
#define PTP_TYPE_DELAY_REQ 1
#define PTP_DOMAIN_NTP 123
#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 rest[26];
} 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

View File

@@ -40,6 +40,9 @@
#include "samplefilt.h" #include "samplefilt.h"
#include "sched.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 */ /* list of refclock drivers */
extern RefclockDriver RCL_SHM_driver; extern RefclockDriver RCL_SHM_driver;
extern RefclockDriver RCL_SOCK_driver; extern RefclockDriver RCL_SOCK_driver;
@@ -181,7 +184,7 @@ RCL_AddRefclock(RefclockParameters *params)
LOG_FATAL("refclock tai option requires leapsectz"); LOG_FATAL("refclock tai option requires leapsectz");
inst->data = NULL; inst->data = NULL;
inst->driver_parameter = params->driver_parameter; inst->driver_parameter = Strdup(params->driver_parameter);
inst->driver_parameter_length = 0; inst->driver_parameter_length = 0;
inst->driver_poll = params->driver_poll; inst->driver_poll = params->driver_poll;
inst->poll = params->poll; inst->poll = params->poll;
@@ -253,22 +256,21 @@ RCL_AddRefclock(RefclockParameters *params)
inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length, inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length,
params->max_dispersion, 0.6); params->max_dispersion, 0.6);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL, inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, 0, params->sel_options,
params->min_samples, params->max_samples, 0.0, 0.0); NULL, params->min_samples, params->max_samples,
0.0, 0.0);
DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d", DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
params->driver_name, UTI_RefidToString(inst->ref_id), params->driver_name, UTI_RefidToString(inst->ref_id),
inst->poll, inst->driver_poll, params->filter_length); inst->poll, inst->driver_poll, params->filter_length);
Free(params->driver_name);
return 1; return 1;
} }
void void
RCL_StartRefclocks(void) RCL_StartRefclocks(void)
{ {
unsigned int i, j, n; unsigned int i, j, n, lock_index;
n = ARR_GetSize(refclocks); n = ARR_GetSize(refclocks);
@@ -278,13 +280,31 @@ RCL_StartRefclocks(void)
SRC_SetActive(inst->source); SRC_SetActive(inst->source);
inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst); inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst);
if (inst->lock_ref) { /* Replace lock refid with the refclock's index, or -1 if not valid */
/* Replace lock refid with index to refclocks */
for (j = 0; j < n && get_refclock(j)->ref_id != inst->lock_ref; j++) lock_index = -1;
;
inst->lock_ref = j < n ? j : -1; if (inst->lock_ref != 0) {
} else for (j = 0; j < n; j++) {
inst->lock_ref = -1; 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)
LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref));
}
inst->lock_ref = lock_index;
} }
} }
@@ -416,12 +436,6 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
sample.peer_dispersion = dispersion; sample.peer_dispersion = dispersion;
sample.root_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); return SPF_AccumulateSample(instance->filter, &sample);
} }
@@ -558,15 +572,12 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
} }
/* Align the offset to the reference sample */ /* Align the offset to the reference sample */
if ((ref_sample.offset - offset) >= 0.0) shift = round((ref_sample.offset - offset) * rate) / rate;
shift = (long)((ref_sample.offset - offset) * rate + 0.5) / (double)rate;
else
shift = (long)((ref_sample.offset - offset) * rate - 0.5) / (double)rate;
offset += shift; offset += shift;
if (fabs(ref_sample.offset - offset) + 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", DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
ref_sample.offset - offset, ref_sample.root_dispersion, dispersion); ref_sample.offset - offset, ref_sample.root_dispersion, dispersion);
return 0; return 0;
@@ -686,7 +697,7 @@ static void
poll_timeout(void *arg) poll_timeout(void *arg)
{ {
NTP_Sample sample; NTP_Sample sample;
int poll; int poll, stratum;
RCL_Instance inst = (RCL_Instance)arg; RCL_Instance inst = (RCL_Instance)arg;
@@ -702,8 +713,14 @@ poll_timeout(void *arg)
inst->driver_polled = 0; inst->driver_polled = 0;
if (SPF_GetFilteredSample(inst->filter, &sample)) { if (SPF_GetFilteredSample(inst->filter, &sample)) {
/* 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;
SRC_UpdateReachability(inst->source, 1); 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_AccumulateSample(inst->source, &sample);
SRC_SelectSource(inst->source); SRC_SelectSource(inst->source);

View File

@@ -66,10 +66,8 @@ static int phc_initialise(RCL_Instance instance)
path = RCL_GetDriverParameter(instance); path = RCL_GetDriverParameter(instance);
phc_fd = SYS_Linux_OpenPHC(path, 0); phc_fd = SYS_Linux_OpenPHC(path, 0);
if (phc_fd < 0) { if (phc_fd < 0)
LOG_FATAL("Could not open PHC"); LOG_FATAL("Could not open PHC");
return 0;
}
phc = MallocNew(struct phc_instance); phc = MallocNew(struct phc_instance);
phc->fd = phc_fd; phc->fd = phc_fd;

View File

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

View File

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

View File

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

2
rtc.c
View File

@@ -148,6 +148,8 @@ RTC_Initialise(int initial_set)
if (driver.init) { if (driver.init) {
if ((driver.init)()) { if ((driver.init)()) {
driver_initialised = 1; driver_initialised = 1;
} else {
LOG(LOGS_ERR, "RTC driver could not be initialised");
} }
} else { } else {
LOG(LOGS_ERR, "RTC not supported on this operating system"); LOG(LOGS_ERR, "RTC not supported on this operating system");

View File

@@ -64,7 +64,7 @@ static OperatingMode operating_mode = OM_NORMAL;
/* ================================================== */ /* ================================================== */
static int fd = -1; static int fd;
#define LOWEST_MEASUREMENT_PERIOD 15 #define LOWEST_MEASUREMENT_PERIOD 15
#define HIGHEST_MEASUREMENT_PERIOD 480 #define HIGHEST_MEASUREMENT_PERIOD 480
@@ -82,16 +82,12 @@ static int skip_interrupts;
#define MAX_SAMPLES 64 #define MAX_SAMPLES 64
/* Real time clock samples. We store the seconds count as originally /* Real time clock samples. We store the seconds count as originally
measured, together with a 'trim' that compensates these values for measured. */
any steps made to the RTC to bring it back into line
occasionally. The trim is in seconds. */
static time_t *rtc_sec = NULL; static time_t *rtc_sec = NULL;
static double *rtc_trim = NULL;
/* Reference time, against which delta times on the RTC scale are measured */ /* Reference time, against which delta times on the RTC scale are measured */
static time_t rtc_ref; static time_t rtc_ref;
/* System clock samples associated with the above samples. */ /* System clock samples associated with the above samples. */
static struct timespec *system_times = NULL; static struct timespec *system_times = NULL;
@@ -145,7 +141,7 @@ static double file_ref_offset, file_rate_ppm;
/* ================================================== */ /* ================================================== */
/* Flag to remember whether to assume the RTC is running on UTC */ /* Flag to remember whether to assume the RTC is running on UTC */
static int rtc_on_utc = 1; static int rtc_on_utc;
/* ================================================== */ /* ================================================== */
@@ -168,7 +164,6 @@ discard_samples(int new_first)
n_to_save = n_samples - new_first; n_to_save = n_samples - new_first;
memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t)); memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t));
memmove(rtc_trim, rtc_trim + new_first, n_to_save * sizeof(double));
memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timespec)); memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timespec));
n_samples = n_to_save; n_samples = n_to_save;
@@ -188,21 +183,16 @@ accumulate_sample(time_t rtc, struct timespec *sys)
} }
/* Discard all samples if the RTC was stepped back (not our trim) */ /* Discard all samples if the RTC was stepped back (not our trim) */
if (n_samples > 0 && rtc_sec[n_samples - 1] - rtc >= rtc_trim[n_samples - 1]) { if (n_samples > 0 && rtc_sec[n_samples - 1] >= rtc) {
DEBUG_LOG("RTC samples discarded"); DEBUG_LOG("RTC samples discarded");
n_samples = 0; n_samples = 0;
} }
/* Always use most recent sample as reference */ /* Always use most recent sample as reference */
/* use sample only if n_sample is not negative*/
if(n_samples >=0)
{
rtc_ref = rtc; rtc_ref = rtc;
rtc_sec[n_samples] = rtc; rtc_sec[n_samples] = rtc;
rtc_trim[n_samples] = 0.0;
system_times[n_samples] = *sys; system_times[n_samples] = *sys;
++n_samples_since_regression; ++n_samples_since_regression;
}
++n_samples; ++n_samples;
} }
@@ -227,7 +217,7 @@ run_regression(int new_sample,
if (n_samples > 0) { if (n_samples > 0) {
for (i=0; i<n_samples; i++) { for (i=0; i<n_samples; i++) {
rtc_rel[i] = rtc_trim[i] + (double)(rtc_sec[i] - rtc_ref); rtc_rel[i] = (double)(rtc_sec[i] - rtc_ref);
offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) - offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) -
(1.0e-9 * system_times[i].tv_nsec) + (1.0e-9 * system_times[i].tv_nsec) +
rtc_rel[i]); rtc_rel[i]);
@@ -434,6 +424,7 @@ setup_config(void)
static void static void
read_coefs_from_file(void) read_coefs_from_file(void)
{ {
double ref_time;
FILE *in; FILE *in;
if (!tried_to_load_coefs) { if (!tried_to_load_coefs) {
@@ -444,11 +435,12 @@ read_coefs_from_file(void)
if (coefs_file_name && if (coefs_file_name &&
(in = UTI_OpenFile(NULL, coefs_file_name, NULL, 'r', 0))) { (in = UTI_OpenFile(NULL, coefs_file_name, NULL, 'r', 0))) {
if (fscanf(in, "%d%ld%lf%lf", if (fscanf(in, "%d%lf%lf%lf",
&valid_coefs_from_file, &valid_coefs_from_file,
&file_ref_time, &ref_time,
&file_ref_offset, &file_ref_offset,
&file_rate_ppm) == 4) { &file_rate_ppm) == 4) {
file_ref_time = ref_time;
} else { } else {
LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name); LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name);
} }
@@ -472,7 +464,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
return RTC_ST_BADFILE; return RTC_ST_BADFILE;
/* Gain rate is written out in ppm */ /* Gain rate is written out in ppm */
fprintf(out, "%1d %ld %.6f %.3f\n", valid, ref_time, offset, 1.0e6 * rate); fprintf(out, "%1d %.0f %.6f %.3f\n", valid, (double)ref_time, offset, 1.0e6 * rate);
fclose(out); fclose(out);
/* Rename the temporary file to the correct location */ /* Rename the temporary file to the correct location */
@@ -525,7 +517,6 @@ RTC_Linux_Initialise(void)
UTI_FdSetCloexec(fd); UTI_FdSetCloexec(fd);
rtc_sec = MallocArray(time_t, MAX_SAMPLES); rtc_sec = MallocArray(time_t, MAX_SAMPLES);
rtc_trim = MallocArray(double, MAX_SAMPLES);
system_times = MallocArray(struct timespec, MAX_SAMPLES); system_times = MallocArray(struct timespec, MAX_SAMPLES);
/* Setup details depending on configuration options */ /* Setup details depending on configuration options */
@@ -573,8 +564,11 @@ RTC_Linux_Finalise(void)
(void) RTC_Linux_WriteParameters(); (void) RTC_Linux_WriteParameters();
} }
if (rtc_sec)
LCL_RemoveParameterChangeHandler(slew_samples, NULL);
Free(rtc_sec); Free(rtc_sec);
Free(rtc_trim);
Free(system_times); Free(system_times);
} }
@@ -635,11 +629,7 @@ handle_initial_trim(void)
run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate); run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate);
n_samples_since_regression = 0; n_samples_since_regression = 0;
n_samples = 0;
/* Set sample number to -1 so the next sample is not used, as it will not yet be corrected for System Trim*/
n_samples = -1;
read_coefs_from_file(); read_coefs_from_file();
@@ -1024,8 +1014,7 @@ RTC_Linux_GetReport(RPT_RTC_Report *report)
report->n_samples = n_samples; report->n_samples = n_samples;
report->n_runs = n_runs; report->n_runs = n_runs;
if (n_samples > 1) { if (n_samples > 1) {
report->span_seconds = ((rtc_sec[n_samples-1] - rtc_sec[0]) + report->span_seconds = rtc_sec[n_samples - 1] - rtc_sec[0];
(long)(rtc_trim[n_samples-1] - rtc_trim[0]));
} else { } else {
report->span_seconds = 0; report->span_seconds = 0;
} }

View File

@@ -386,7 +386,6 @@ combine_selected_samples(SPF_Instance filter, int n, NTP_Sample *result)
result->root_dispersion = MAX(disp, mean_root_dispersion); result->root_dispersion = MAX(disp, mean_root_dispersion);
result->peer_delay = mean_peer_delay; result->peer_delay = mean_peer_delay;
result->root_delay = mean_root_delay; result->root_delay = mean_root_delay;
result->stratum = last_sample->stratum;
return 1; return 1;
} }

51
sched.c
View File

@@ -111,7 +111,8 @@ static struct timespec last_class_dispatch[SCH_NumberOfClasses];
/* ================================================== */ /* ================================================== */
static int need_to_exit; /* Flag terminating the main loop, which can be set from a signal handler */
static volatile int need_to_exit;
/* ================================================== */ /* ================================================== */
@@ -490,20 +491,6 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
assert(0); assert(0);
} }
/* ================================================== */
void
SCH_Reset(void)
{
while (n_timer_queue_entries > 0)
SCH_RemoveTimeout(timer_queue.next->id);
while (one_highest_fd > 0) {
close(one_highest_fd - 1);
SCH_RemoveFileHandler(one_highest_fd - 1);
}
}
/* ================================================== */ /* ================================================== */
/* Try to dispatch any timeouts that have already gone by, and /* Try to dispatch any timeouts that have already gone by, and
keep going until all are done. (The earlier ones may take so keep going until all are done. (The earlier ones may take so
@@ -512,12 +499,15 @@ SCH_Reset(void)
static void static void
dispatch_timeouts(struct timespec *now) { dispatch_timeouts(struct timespec *now) {
unsigned long n_done, n_entries_on_start;
TimerQueueEntry *ptr; TimerQueueEntry *ptr;
SCH_TimeoutHandler handler; SCH_TimeoutHandler handler;
SCH_ArbitraryArgument arg; SCH_ArbitraryArgument arg;
int n_done = 0, n_entries_on_start = n_timer_queue_entries;
while (1) { n_entries_on_start = n_timer_queue_entries;
n_done = 0;
do {
LCL_ReadRawTime(now); LCL_ReadRawTime(now);
if (!(n_timer_queue_entries > 0 && if (!(n_timer_queue_entries > 0 &&
@@ -540,16 +530,21 @@ dispatch_timeouts(struct timespec *now) {
/* Increment count of timeouts handled */ /* Increment count of timeouts handled */
++n_done; ++n_done;
/* If more timeouts were handled than there were in the timer queue on /* If the number of dispatched timeouts is significantly larger than the
start and there are now, assume some code is scheduling timeouts with length of the queue on start and now, assume there is a bug causing
negative delays and abort. Make the actual limit higher in case the an infinite loop by constantly adding a timeout with a zero or negative
machine is temporarily overloaded and dispatching the handlers takes delay. Check the actual rate of timeouts to avoid false positives in
more time than was delay of a scheduled timeout. */ case the execution slowed down so much (e.g. due to memory thrashing)
if (n_done > n_timer_queue_entries * 4 && that it repeatedly takes more time to handle the timeout than is its
n_done > n_entries_on_start * 4) { delay. This is a safety mechanism intended to stop a full-speed flood
of NTP requests due to a bug in the NTP polling. */
if (n_done > 20 &&
n_done > 4 * MAX(n_timer_queue_entries, n_entries_on_start) &&
fabs(UTI_DiffTimespecsToDouble(now, &last_select_ts_raw)) / n_done < 0.01)
LOG_FATAL("Possible infinite loop in scheduling"); LOG_FATAL("Possible infinite loop in scheduling");
}
} } while (!need_to_exit);
} }
/* ================================================== */ /* ================================================== */
@@ -813,14 +808,14 @@ SCH_MainLoop(void)
LCL_ReadRawTime(&now); LCL_ReadRawTime(&now);
LCL_CookTime(&now, &cooked, &err); LCL_CookTime(&now, &cooked, &err);
update_monotonic_time(&now, &last_select_ts_raw);
/* Check if the time didn't jump unexpectedly */ /* Check if the time didn't jump unexpectedly */
if (!check_current_time(&saved_now, &now, status == 0, &saved_tv, ptv)) { if (!check_current_time(&saved_now, &now, status == 0, &saved_tv, ptv)) {
/* Cook the time again after handling the step */ /* Cook the time again after handling the step */
LCL_CookTime(&now, &cooked, &err); LCL_CookTime(&now, &cooked, &err);
} }
update_monotonic_time(&cooked, &last_select_ts);
last_select_ts_raw = now; last_select_ts_raw = now;
last_select_ts = cooked; last_select_ts = cooked;
last_select_ts_err = err; last_select_ts_err = err;

View File

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

259
siv_gnutls.c Normal file
View File

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

View File

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

View File

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

268
socket.c
View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009 * Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2019 * Copyright (C) Miroslav Lichvar 2009, 2013-2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -40,6 +40,7 @@
#include "array.h" #include "array.h"
#include "logging.h" #include "logging.h"
#include "privops.h" #include "privops.h"
#include "ptp.h"
#include "util.h" #include "util.h"
#define INVALID_SOCK_FD (-4) #define INVALID_SOCK_FD (-4)
@@ -58,10 +59,16 @@ struct Message {
union sockaddr_all name; union sockaddr_all name;
struct iovec iov; struct iovec iov;
/* Buffer of sufficient length for all expected messages */ /* Buffer of sufficient length for all expected messages */
union { struct {
NTP_Packet ntp_msg; /* Extra space for Ethernet, IPv4/IPv6, and UDP headers in
CMD_Request cmd_request; timestamped messages received from the Linux error queue */
CMD_Reply cmd_reply; uint8_t l234_headers[64];
union {
NTP_Packet ntp_msg;
PTP_NtpMessage ptp_msg;
CMD_Request cmd_request;
CMD_Reply cmd_reply;
} msg;
} msg_buf; } msg_buf;
/* Aligned buffer for control messages */ /* Aligned buffer for control messages */
struct cmsghdr cmsg_buf[CMSG_BUF_SIZE / sizeof (struct cmsghdr)]; struct cmsghdr cmsg_buf[CMSG_BUF_SIZE / sizeof (struct cmsghdr)];
@@ -82,6 +89,10 @@ struct MessageHeader {
static int initialised; static int initialised;
/* Flags indicating in which IP families sockets can be requested */
static int ip4_enabled;
static int ip6_enabled;
/* Flags supported by socket() */ /* Flags supported by socket() */
static int supported_socket_flags; static int supported_socket_flags;
@@ -114,7 +125,7 @@ prepare_buffers(unsigned int n)
hdr->msg_hdr.msg_namelen = sizeof (msg->name); hdr->msg_hdr.msg_namelen = sizeof (msg->name);
hdr->msg_hdr.msg_iov = &msg->iov; hdr->msg_hdr.msg_iov = &msg->iov;
hdr->msg_hdr.msg_iovlen = 1; hdr->msg_hdr.msg_iovlen = 1;
hdr->msg_hdr.msg_control = &msg->cmsg_buf; hdr->msg_hdr.msg_control = msg->cmsg_buf;
hdr->msg_hdr.msg_controllen = sizeof (msg->cmsg_buf); hdr->msg_hdr.msg_controllen = sizeof (msg->cmsg_buf);
hdr->msg_hdr.msg_flags = 0; hdr->msg_hdr.msg_flags = 0;
hdr->msg_len = 0; hdr->msg_len = 0;
@@ -172,7 +183,7 @@ check_socket_flag(int sock_flag, int fd_flag, int fs_flag)
static int static int
set_socket_nonblock(int sock_fd) set_socket_nonblock(int sock_fd)
{ {
if (fcntl(sock_fd, F_SETFL, O_NONBLOCK)) { if (fcntl(sock_fd, F_SETFL, O_NONBLOCK) < 0) {
DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno)); DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno));
return 0; return 0;
} }
@@ -332,6 +343,23 @@ is_any_address(IPAddr *addr)
/* ================================================== */ /* ================================================== */
static int
bind_device(int sock_fd, const char *iface)
{
#ifdef SO_BINDTODEVICE
if (setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, iface, strlen(iface) + 1) < 0) {
DEBUG_LOG("Could not bind socket to %s : %s", iface, strerror(errno));
return 0;
}
return 1;
#else
DEBUG_LOG("Could not bind socket to %s : %s", iface, "Not supported");
return 0;
#endif
}
/* ================================================== */
static int static int
bind_ip_address(int sock_fd, IPSockAddr *addr, int flags) bind_ip_address(int sock_fd, IPSockAddr *addr, int flags)
{ {
@@ -399,7 +427,8 @@ connect_ip_address(int sock_fd, IPSockAddr *addr)
/* ================================================== */ /* ================================================== */
static int static int
open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int type, int flags) open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface,
int type, int flags)
{ {
int domain, family, sock_fd; int domain, family, sock_fd;
@@ -412,10 +441,14 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int type, int fl
switch (family) { switch (family) {
case IPADDR_INET4: case IPADDR_INET4:
if (!ip4_enabled)
return INVALID_SOCK_FD;
domain = AF_INET; domain = AF_INET;
break; break;
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
case IPADDR_INET6: case IPADDR_INET6:
if (!ip6_enabled)
return INVALID_SOCK_FD;
domain = AF_INET6; domain = AF_INET6;
break; break;
#endif #endif
@@ -434,6 +467,9 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int type, int fl
if (!set_ip_options(sock_fd, family, flags)) if (!set_ip_options(sock_fd, family, flags))
goto error; goto error;
if (iface && !bind_device(sock_fd, iface))
goto error;
/* Bind the socket if a non-any local address/port was specified */ /* Bind the socket if a non-any local address/port was specified */
if (local_addr && local_addr->ip_addr.family != IPADDR_UNSPEC && if (local_addr && local_addr->ip_addr.family != IPADDR_UNSPEC &&
(local_addr->port != 0 || !is_any_address(&local_addr->ip_addr)) && (local_addr->port != 0 || !is_any_address(&local_addr->ip_addr)) &&
@@ -469,6 +505,8 @@ bind_unix_address(int sock_fd, const char *addr, int flags)
{ {
union sockaddr_all saddr; union sockaddr_all saddr;
memset(&saddr, 0, sizeof (saddr));
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >= if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
sizeof (saddr.un.sun_path)) { sizeof (saddr.un.sun_path)) {
DEBUG_LOG("Unix socket path %s too long", addr); DEBUG_LOG("Unix socket path %s too long", addr);
@@ -476,8 +514,8 @@ bind_unix_address(int sock_fd, const char *addr, int flags)
} }
saddr.un.sun_family = AF_UNIX; saddr.un.sun_family = AF_UNIX;
if (!UTI_RemoveFile(NULL, addr, NULL)) if (unlink(addr) < 0)
; DEBUG_LOG("Could not remove %s : %s", addr, strerror(errno));
/* PRV_BindSocket() doesn't support Unix sockets yet */ /* PRV_BindSocket() doesn't support Unix sockets yet */
if (bind(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) { if (bind(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) {
@@ -501,6 +539,8 @@ connect_unix_address(int sock_fd, const char *addr)
{ {
union sockaddr_all saddr; union sockaddr_all saddr;
memset(&saddr, 0, sizeof (saddr));
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >= if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
sizeof (saddr.un.sun_path)) { sizeof (saddr.un.sun_path)) {
DEBUG_LOG("Unix socket path %s too long", addr); DEBUG_LOG("Unix socket path %s too long", addr);
@@ -627,9 +667,8 @@ log_message(int sock_fd, int direction, SCK_Message *message, const char *prefix
case SCK_ADDR_IP: case SCK_ADDR_IP:
if (message->remote_addr.ip.ip_addr.family != IPADDR_UNSPEC) if (message->remote_addr.ip.ip_addr.family != IPADDR_UNSPEC)
remote_addr = UTI_IPSockAddrToString(&message->remote_addr.ip); remote_addr = UTI_IPSockAddrToString(&message->remote_addr.ip);
if (message->local_addr.ip.family != IPADDR_UNSPEC) { if (message->local_addr.ip.family != IPADDR_UNSPEC)
local_addr = UTI_IPToString(&message->local_addr.ip); local_addr = UTI_IPToString(&message->local_addr.ip);
}
break; break;
case SCK_ADDR_UNIX: case SCK_ADDR_UNIX:
remote_addr = message->remote_addr.path; remote_addr = message->remote_addr.path;
@@ -655,7 +694,7 @@ log_message(int sock_fd, int direction, SCK_Message *message, const char *prefix
snprintf(tslen, sizeof (tslen), " tslen=%d", message->timestamp.l2_length); snprintf(tslen, sizeof (tslen), " tslen=%d", message->timestamp.l2_length);
} }
DEBUG_LOG("%s message%s%s%s%s fd=%d len=%u%s%s%s%s%s%s", DEBUG_LOG("%s message%s%s%s%s fd=%d len=%d%s%s%s%s%s%s",
prefix, prefix,
remote_addr ? (direction > 0 ? " from " : " to ") : "", remote_addr ? (direction > 0 ? " from " : " to ") : "",
remote_addr ? remote_addr : "", remote_addr ? remote_addr : "",
@@ -710,22 +749,25 @@ init_message_nonaddress(SCK_Message *message)
/* ================================================== */ /* ================================================== */
static int static int
process_header(struct msghdr *msg, unsigned int msg_length, int sock_fd, int flags, match_cmsg(struct cmsghdr *cmsg, int level, int type, size_t length)
{
if (cmsg->cmsg_type == type && cmsg->cmsg_level == level &&
(length == 0 || cmsg->cmsg_len == CMSG_LEN(length)))
return 1;
return 0;
}
/* ================================================== */
static int
process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
SCK_Message *message) SCK_Message *message)
{ {
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
int r = 1;
if (msg->msg_iovlen != 1) { if (msg->msg_namelen <= sizeof (union sockaddr_all) &&
DEBUG_LOG("Unexpected iovlen"); msg->msg_namelen > sizeof (((struct sockaddr *)msg->msg_name)->sa_family)) {
return 0;
}
if (msg->msg_namelen > sizeof (union sockaddr_all)) {
DEBUG_LOG("Truncated source address");
return 0;
}
if (msg->msg_namelen > sizeof (((struct sockaddr *)msg->msg_name)->sa_family)) {
switch (((struct sockaddr *)msg->msg_name)->sa_family) { switch (((struct sockaddr *)msg->msg_name)->sa_family) {
case AF_INET: case AF_INET:
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
@@ -739,31 +781,45 @@ process_header(struct msghdr *msg, unsigned int msg_length, int sock_fd, int fla
message->remote_addr.path = ((struct sockaddr_un *)msg->msg_name)->sun_path; message->remote_addr.path = ((struct sockaddr_un *)msg->msg_name)->sun_path;
break; break;
default: default:
init_message_addresses(message, SCK_ADDR_UNSPEC);
DEBUG_LOG("Unexpected address"); DEBUG_LOG("Unexpected address");
return 0; r = 0;
break;
} }
} else { } else {
init_message_addresses(message, SCK_ADDR_UNSPEC); init_message_addresses(message, SCK_ADDR_UNSPEC);
if (msg->msg_namelen > sizeof (union sockaddr_all)) {
DEBUG_LOG("Truncated source address");
r = 0;
}
} }
init_message_nonaddress(message); init_message_nonaddress(message);
message->data = msg->msg_iov[0].iov_base; if (msg->msg_iovlen == 1) {
message->length = msg_length; message->data = msg->msg_iov[0].iov_base;
message->length = msg_length;
} else {
DEBUG_LOG("Unexpected iovlen");
r = 0;
}
if (msg->msg_flags & MSG_TRUNC) { if (msg->msg_flags & MSG_TRUNC) {
log_message(sock_fd, 1, message, "Truncated", NULL); log_message(sock_fd, 1, message, "Truncated", NULL);
return 0; r = 0;
} }
if (msg->msg_flags & MSG_CTRUNC) { if (msg->msg_flags & MSG_CTRUNC) {
log_message(sock_fd, 1, message, "Truncated cmsg in", NULL); log_message(sock_fd, 1, message, "Truncated cmsg in", NULL);
return 0; r = 0;
} }
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (0) {
}
#ifdef HAVE_IN_PKTINFO #ifdef HAVE_IN_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { else if (match_cmsg(cmsg, IPPROTO_IP, IP_PKTINFO, sizeof (struct in_pktinfo))) {
struct in_pktinfo ipi; struct in_pktinfo ipi;
if (message->addr_type != SCK_ADDR_IP) if (message->addr_type != SCK_ADDR_IP)
@@ -775,7 +831,7 @@ process_header(struct msghdr *msg, unsigned int msg_length, int sock_fd, int fla
message->if_index = ipi.ipi_ifindex; message->if_index = ipi.ipi_ifindex;
} }
#elif defined(IP_RECVDSTADDR) #elif defined(IP_RECVDSTADDR)
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { else if (match_cmsg(cmsg, IPPROTO_IP, IP_RECVDSTADDR, sizeof (struct in_addr))) {
struct in_addr addr; struct in_addr addr;
if (message->addr_type != SCK_ADDR_IP) if (message->addr_type != SCK_ADDR_IP)
@@ -786,9 +842,8 @@ process_header(struct msghdr *msg, unsigned int msg_length, int sock_fd, int fla
message->local_addr.ip.family = IPADDR_INET4; message->local_addr.ip.family = IPADDR_INET4;
} }
#endif #endif
#ifdef HAVE_IN6_PKTINFO #ifdef HAVE_IN6_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { else if (match_cmsg(cmsg, IPPROTO_IPV6, IPV6_PKTINFO, sizeof (struct in6_pktinfo))) {
struct in6_pktinfo ipi; struct in6_pktinfo ipi;
if (message->addr_type != SCK_ADDR_IP) if (message->addr_type != SCK_ADDR_IP)
@@ -801,25 +856,23 @@ process_header(struct msghdr *msg, unsigned int msg_length, int sock_fd, int fla
message->if_index = ipi.ipi6_ifindex; message->if_index = ipi.ipi6_ifindex;
} }
#endif #endif
#ifdef SCM_TIMESTAMP #ifdef SCM_TIMESTAMP
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) { else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMP, sizeof (struct timeval))) {
struct timeval tv; struct timeval tv;
memcpy(&tv, CMSG_DATA(cmsg), sizeof (tv)); memcpy(&tv, CMSG_DATA(cmsg), sizeof (tv));
UTI_TimevalToTimespec(&tv, &message->timestamp.kernel); UTI_TimevalToTimespec(&tv, &message->timestamp.kernel);
} }
#endif #endif
#ifdef SCM_TIMESTAMPNS #ifdef SCM_TIMESTAMPNS
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) { else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPNS, sizeof (message->timestamp.kernel))) {
memcpy(&message->timestamp.kernel, CMSG_DATA(cmsg), sizeof (message->timestamp.kernel)); memcpy(&message->timestamp.kernel, CMSG_DATA(cmsg), sizeof (message->timestamp.kernel));
} }
#endif #endif
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO #ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING_PKTINFO) { else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPING_PKTINFO,
sizeof (struct scm_ts_pktinfo))) {
struct scm_ts_pktinfo ts_pktinfo; struct scm_ts_pktinfo ts_pktinfo;
memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo)); memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo));
@@ -827,17 +880,17 @@ process_header(struct msghdr *msg, unsigned int msg_length, int sock_fd, int fla
message->timestamp.l2_length = ts_pktinfo.pkt_length; message->timestamp.l2_length = ts_pktinfo.pkt_length;
} }
#endif #endif
else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPING,
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) { sizeof (struct scm_timestamping))) {
struct scm_timestamping ts3; struct scm_timestamping ts3;
memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3)); memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
message->timestamp.kernel = ts3.ts[0]; message->timestamp.kernel = ts3.ts[0];
message->timestamp.hw = ts3.ts[2]; message->timestamp.hw = ts3.ts[2];
} }
else if ((match_cmsg(cmsg, SOL_IP, IP_RECVERR, 0) ||
if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) || match_cmsg(cmsg, SOL_IPV6, IPV6_RECVERR, 0)) &&
(cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR)) { cmsg->cmsg_len >= CMSG_LEN(sizeof (struct sock_extended_err))) {
struct sock_extended_err err; struct sock_extended_err err;
memcpy(&err, CMSG_DATA(cmsg), sizeof (err)); memcpy(&err, CMSG_DATA(cmsg), sizeof (err));
@@ -845,25 +898,34 @@ process_header(struct msghdr *msg, unsigned int msg_length, int sock_fd, int fla
if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND || if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
log_message(sock_fd, 1, message, "Unexpected extended error in", NULL); log_message(sock_fd, 1, message, "Unexpected extended error in", NULL);
return 0; r = 0;
} }
} }
#endif #endif
else if (match_cmsg(cmsg, SOL_SOCKET, SCM_RIGHTS, 0)) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
if (!(flags & SCK_FLAG_MSG_DESCRIPTOR) || cmsg->cmsg_len != CMSG_LEN(sizeof (int))) { if (!(flags & SCK_FLAG_MSG_DESCRIPTOR) || cmsg->cmsg_len != CMSG_LEN(sizeof (int))) {
unsigned int i; int i, fd;
DEBUG_LOG("Unexpected SCM_RIGHTS"); DEBUG_LOG("Unexpected SCM_RIGHTS");
for (i = 0; CMSG_LEN((i + 1) * sizeof (int)) <= cmsg->cmsg_len; i++) for (i = 0; CMSG_LEN((i + 1) * sizeof (int)) <= cmsg->cmsg_len; i++) {
close(((int *)CMSG_DATA(cmsg))[i]); memcpy(&fd, (char *)CMSG_DATA(cmsg) + i * sizeof (int), sizeof (fd));
return 0; close(fd);
}
r = 0;
} else {
memcpy(&message->descriptor, CMSG_DATA(cmsg), sizeof (message->descriptor));
} }
message->descriptor = *(int *)CMSG_DATA(cmsg); }
else {
DEBUG_LOG("Unexpected control message level=%d type=%d len=%d",
cmsg->cmsg_level, cmsg->cmsg_type, (int)cmsg->cmsg_len);
} }
} }
return 1; if (!r && message->descriptor != INVALID_SOCK_FD)
close(message->descriptor);
return r;
} }
/* ================================================== */ /* ================================================== */
@@ -873,7 +935,7 @@ receive_messages(int sock_fd, int flags, int max_messages, int *num_messages)
{ {
struct MessageHeader *hdr; struct MessageHeader *hdr;
SCK_Message *messages; SCK_Message *messages;
unsigned int i, n; unsigned int i, n, n_ok;
int ret, recv_flags = 0; int ret, recv_flags = 0;
assert(initialised); assert(initialised);
@@ -892,7 +954,10 @@ receive_messages(int sock_fd, int flags, int max_messages, int *num_messages)
hdr = ARR_GetElements(recv_headers); hdr = ARR_GetElements(recv_headers);
n = ARR_GetSize(recv_headers); n = ARR_GetSize(recv_headers);
n = MIN(n, max_messages); n = MIN(n, max_messages);
assert(n >= 1);
if (n < 1 || n > MAX_RECV_MESSAGES ||
n > ARR_GetSize(recv_messages) || n > ARR_GetSize(recv_sck_messages))
assert(0);
recv_flags = get_recv_flags(flags); recv_flags = get_recv_flags(flags);
@@ -914,18 +979,20 @@ receive_messages(int sock_fd, int flags, int max_messages, int *num_messages)
received_messages = n; received_messages = n;
for (i = 0; i < n; i++) { for (i = n_ok = 0; i < n; i++) {
hdr = ARR_GetElement(recv_headers, i); hdr = ARR_GetElement(recv_headers, i);
if (!process_header(&hdr->msg_hdr, hdr->msg_len, sock_fd, flags, &messages[i])) if (!process_header(&hdr->msg_hdr, hdr->msg_len, sock_fd, flags, &messages[n_ok]))
return NULL; continue;
log_message(sock_fd, 1, &messages[i], log_message(sock_fd, 1, &messages[n_ok],
flags & SCK_FLAG_MSG_ERRQUEUE ? "Received error" : "Received", NULL); flags & SCK_FLAG_MSG_ERRQUEUE ? "Received error" : "Received", NULL);
n_ok++;
} }
*num_messages = n; *num_messages = n_ok;
return messages; return n_ok > 0 ? messages : NULL;
} }
/* ================================================== */ /* ================================================== */
@@ -1001,6 +1068,11 @@ send_message(int sock_fd, SCK_Message *message, int flags)
msg.msg_namelen = 0; msg.msg_namelen = 0;
} }
if (message->length < 0) {
DEBUG_LOG("Invalid length %d", message->length);
return 0;
}
iov.iov_base = message->data; iov.iov_base = message->data;
iov.iov_len = message->length; iov.iov_len = message->length;
msg.msg_iov = &iov; msg.msg_iov = &iov;
@@ -1094,8 +1166,15 @@ send_message(int sock_fd, SCK_Message *message, int flags)
/* ================================================== */ /* ================================================== */
void void
SCK_Initialise(void) SCK_Initialise(int family)
{ {
ip4_enabled = family == IPADDR_INET4 || family == IPADDR_UNSPEC;
#ifdef FEAT_IPV6
ip6_enabled = family == IPADDR_INET6 || family == IPADDR_UNSPEC;
#else
ip6_enabled = 0;
#endif
recv_messages = ARR_CreateInstance(sizeof (struct Message)); recv_messages = ARR_CreateInstance(sizeof (struct Message));
ARR_SetSize(recv_messages, MAX_RECV_MESSAGES); ARR_SetSize(recv_messages, MAX_RECV_MESSAGES);
recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader)); recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader));
@@ -1135,15 +1214,13 @@ SCK_Finalise(void)
/* ================================================== */ /* ================================================== */
int int
SCK_IsFamilySupported(int family) SCK_IsIpFamilyEnabled(int family)
{ {
switch (family) { switch (family) {
case IPADDR_INET4: case IPADDR_INET4:
return 1; return ip4_enabled;
case IPADDR_INET6: case IPADDR_INET6:
#ifdef FEAT_IPV6 return ip6_enabled;
return 1;
#endif
default: default:
return 0; return 0;
} }
@@ -1194,6 +1271,23 @@ SCK_GetLoopbackIPAddress(int family, IPAddr *local_addr)
/* ================================================== */ /* ================================================== */
int
SCK_IsLinkLocalIPAddress(IPAddr *addr)
{
switch (addr->family) {
case IPADDR_INET4:
/* 169.254.0.0/16 */
return (addr->addr.in4 & 0xffff0000) == 0xa9fe0000;
case IPADDR_INET6:
/* fe80::/10 */
return addr->addr.in6[0] == 0xfe && (addr->addr.in6[1] & 0xc0) == 0x80;
default:
return 0;
}
}
/* ================================================== */
void void
SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address, SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
socklen_t address_len)) socklen_t address_len))
@@ -1204,17 +1298,17 @@ SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
/* ================================================== */ /* ================================================== */
int int
SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags) SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface, int flags)
{ {
return open_ip_socket(remote_addr, local_addr, SOCK_DGRAM, flags); return open_ip_socket(remote_addr, local_addr, iface, SOCK_DGRAM, flags);
} }
/* ================================================== */ /* ================================================== */
int int
SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags) SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface, int flags)
{ {
return open_ip_socket(remote_addr, local_addr, SOCK_STREAM, flags); return open_ip_socket(remote_addr, local_addr, iface, SOCK_STREAM, flags);
} }
/* ================================================== */ /* ================================================== */
@@ -1353,10 +1447,15 @@ SCK_ShutdownConnection(int sock_fd)
/* ================================================== */ /* ================================================== */
int int
SCK_Receive(int sock_fd, void *buffer, unsigned int length, int flags) SCK_Receive(int sock_fd, void *buffer, int length, int flags)
{ {
int r; int r;
if (length < 0) {
DEBUG_LOG("Invalid length %d", length);
return -1;
}
r = recv(sock_fd, buffer, length, get_recv_flags(flags)); r = recv(sock_fd, buffer, length, get_recv_flags(flags));
if (r < 0) { if (r < 0) {
@@ -1372,16 +1471,21 @@ SCK_Receive(int sock_fd, void *buffer, unsigned int length, int flags)
/* ================================================== */ /* ================================================== */
int int
SCK_Send(int sock_fd, const void *buffer, unsigned int length, int flags) SCK_Send(int sock_fd, const void *buffer, int length, int flags)
{ {
int r; int r;
assert(flags == 0); assert(flags == 0);
if (length < 0) {
DEBUG_LOG("Invalid length %d", length);
return -1;
}
r = send(sock_fd, buffer, length, 0); r = send(sock_fd, buffer, length, 0);
if (r < 0) { if (r < 0) {
DEBUG_LOG("Could not send data fd=%d len=%u : %s", sock_fd, length, strerror(errno)); DEBUG_LOG("Could not send data fd=%d len=%d : %s", sock_fd, length, strerror(errno));
return r; return r;
} }
@@ -1444,8 +1548,10 @@ SCK_RemoveSocket(int sock_fd)
saddr.sa.sa_family != AF_UNIX) saddr.sa.sa_family != AF_UNIX)
return 0; return 0;
if (!UTI_RemoveFile(NULL, saddr.un.sun_path, NULL)) if (unlink(saddr.un.sun_path) < 0) {
DEBUG_LOG("Could not remove %s : %s", saddr.un.sun_path, strerror(errno));
return 0; return 0;
}
return 1; return 1;
} }
@@ -1468,7 +1574,7 @@ SCK_SockaddrToIPSockAddr(struct sockaddr *sa, int sa_length, IPSockAddr *ip_sa)
switch (sa->sa_family) { switch (sa->sa_family) {
case AF_INET: case AF_INET:
if (sa_length < sizeof (struct sockaddr_in)) if (sa_length < (int)sizeof (struct sockaddr_in))
return; return;
ip_sa->ip_addr.family = IPADDR_INET4; ip_sa->ip_addr.family = IPADDR_INET4;
ip_sa->ip_addr.addr.in4 = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); ip_sa->ip_addr.addr.in4 = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
@@ -1476,7 +1582,7 @@ SCK_SockaddrToIPSockAddr(struct sockaddr *sa, int sa_length, IPSockAddr *ip_sa)
break; break;
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
case AF_INET6: case AF_INET6:
if (sa_length < sizeof (struct sockaddr_in6)) if (sa_length < (int)sizeof (struct sockaddr_in6))
return; return;
ip_sa->ip_addr.family = IPADDR_INET6; ip_sa->ip_addr.family = IPADDR_INET6;
memcpy(&ip_sa->ip_addr.addr.in6, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, memcpy(&ip_sa->ip_addr.addr.in6, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr,
@@ -1496,7 +1602,7 @@ SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length)
{ {
switch (ip_sa->ip_addr.family) { switch (ip_sa->ip_addr.family) {
case IPADDR_INET4: case IPADDR_INET4:
if (sa_length < sizeof (struct sockaddr_in)) if (sa_length < (int)sizeof (struct sockaddr_in))
return 0; return 0;
memset(sa, 0, sizeof (struct sockaddr_in)); memset(sa, 0, sizeof (struct sockaddr_in));
sa->sa_family = AF_INET; sa->sa_family = AF_INET;
@@ -1508,7 +1614,7 @@ SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length)
return sizeof (struct sockaddr_in); return sizeof (struct sockaddr_in);
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
case IPADDR_INET6: case IPADDR_INET6:
if (sa_length < sizeof (struct sockaddr_in6)) if (sa_length < (int)sizeof (struct sockaddr_in6))
return 0; return 0;
memset(sa, 0, sizeof (struct sockaddr_in6)); memset(sa, 0, sizeof (struct sockaddr_in6));
sa->sa_family = AF_INET6; sa->sa_family = AF_INET6;
@@ -1521,7 +1627,7 @@ SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length)
return sizeof (struct sockaddr_in6); return sizeof (struct sockaddr_in6);
#endif #endif
default: default:
if (sa_length < sizeof (struct sockaddr)) if (sa_length < (int)sizeof (struct sockaddr))
return 0; return 0;
memset(sa, 0, sizeof (struct sockaddr)); memset(sa, 0, sizeof (struct sockaddr));
sa->sa_family = AF_UNSPEC; sa->sa_family = AF_UNSPEC;

View File

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

502
sources.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2016, 2018 * Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2021
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -54,7 +54,6 @@ static int initialised = 0;
/* ================================================== */ /* ================================================== */
/* Structure used to hold info for selecting between sources */ /* Structure used to hold info for selecting between sources */
struct SelectInfo { struct SelectInfo {
int stratum;
int select_ok; int select_ok;
double std_dev; double std_dev;
double root_distance; double root_distance;
@@ -118,7 +117,13 @@ struct SRC_Instance_Record {
/* Type of the source */ /* Type of the source */
SRC_Type type; SRC_Type type;
/* Options used when selecting sources */ /* Flag indicating that the source is authenticated */
int authenticated;
/* Configured selection options */
int conf_sel_options;
/* Effective selection options */
int sel_options; int sel_options;
/* Score against currently selected source */ /* Score against currently selected source */
@@ -126,7 +131,10 @@ struct SRC_Instance_Record {
struct SelectInfo sel_info; struct SelectInfo sel_info;
/* Latest leap status */ /* Current stratum */
int stratum;
/* Current leap status */
NTP_Leap leap; NTP_Leap leap;
/* Flag indicating the source has a leap second vote */ /* Flag indicating the source has a leap second vote */
@@ -169,16 +177,17 @@ static double reselect_distance;
static double stratum_weight; static double stratum_weight;
static double combine_limit; static double combine_limit;
/* Identifier of the dump file */
#define DUMP_IDENTIFIER "SRC0\n"
/* ================================================== */ /* ================================================== */
/* Forward prototype */ /* Forward prototype */
static void static void update_sel_options(void);
slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq, static void slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything); double doffset, LCL_ChangeType change_type, void *anything);
static void static void add_dispersion(double dispersion, void *anything);
add_dispersion(double dispersion, void *anything); static char *source_to_string(SRC_Instance inst);
static char *
source_to_string(SRC_Instance inst);
/* ================================================== */ /* ================================================== */
/* Initialisation function */ /* Initialisation function */
@@ -218,9 +227,9 @@ void SRC_Finalise(void)
/* Function to create a new instance. This would be called by one of /* Function to create a new instance. This would be called by one of
the individual source-type instance creation routines. */ the individual source-type instance creation routines. */
SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options, SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated,
IPAddr *addr, int min_samples, int max_samples, int sel_options, IPAddr *addr, int min_samples,
double min_delay, double asymmetry) int max_samples, double min_delay, double asymmetry)
{ {
SRC_Instance result; SRC_Instance result;
@@ -253,6 +262,8 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_optio
result->index = n_sources; result->index = n_sources;
result->type = type; result->type = type;
result->authenticated = authenticated;
result->conf_sel_options = sel_options;
result->sel_options = sel_options; result->sel_options = sel_options;
result->active = 0; result->active = 0;
@@ -261,6 +272,8 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_optio
n_sources++; n_sources++;
update_sel_options();
return result; return result;
} }
@@ -285,6 +298,8 @@ void SRC_DestroyInstance(SRC_Instance instance)
--n_sources; --n_sources;
Free(instance); Free(instance);
update_sel_options();
/* If this was the previous reference source, we have to reselect! */ /* If this was the previous reference source, we have to reselect! */
if (selected_source_index == dead_index) if (selected_source_index == dead_index)
SRC_ReselectSource(); SRC_ReselectSource();
@@ -303,9 +318,12 @@ SRC_ResetInstance(SRC_Instance instance)
instance->distant = 0; instance->distant = 0;
instance->status = SRC_BAD_STATS; instance->status = SRC_BAD_STATS;
instance->sel_score = 1.0; instance->sel_score = 1.0;
instance->stratum = 0;
instance->leap = LEAP_Unsynchronised; instance->leap = LEAP_Unsynchronised;
instance->leap_vote = 0; instance->leap_vote = 0;
memset(&instance->sel_info, 0, sizeof (instance->sel_info));
SST_ResetInstance(instance->stats); SST_ResetInstance(instance->stats);
} }
@@ -359,8 +377,10 @@ get_leap_status(void)
/* ================================================== */ /* ================================================== */
void void
SRC_SetLeapStatus(SRC_Instance inst, NTP_Leap leap) SRC_UpdateStatus(SRC_Instance inst, int stratum, NTP_Leap leap)
{ {
inst->stratum = stratum;
if (REF_IsLeapSecondClose(NULL, 0.0)) if (REF_IsLeapSecondClose(NULL, 0.0))
return; return;
@@ -386,9 +406,9 @@ SRC_AccumulateSample(SRC_Instance inst, NTP_Sample *sample)
assert(initialised); assert(initialised);
DEBUG_LOG("ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d", DEBUG_LOG("src=%s ts=%s offset=%e delay=%e disp=%e",
source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset, source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset,
sample->root_delay, sample->root_dispersion, sample->stratum); sample->root_delay, sample->root_dispersion);
if (REF_IsLeapSecondClose(&sample->time, sample->offset)) { if (REF_IsLeapSecondClose(&sample->time, sample->offset)) {
LOG(LOGS_INFO, "Dropping sample around leap second"); LOG(LOGS_INFO, "Dropping sample around leap second");
@@ -427,7 +447,7 @@ special_mode_end(void)
if (!sources[i]->active) if (!sources[i]->active)
continue; continue;
/* Don't expect more updates than from an offline iburst NTP source */ /* Don't expect more updates than the initial burst of an NTP source */
if (sources[i]->reachability_size >= SOURCE_REACH_BITS - 1) if (sources[i]->reachability_size >= SOURCE_REACH_BITS - 1)
continue; continue;
@@ -482,6 +502,75 @@ SRC_ResetReachability(SRC_Instance inst)
/* ================================================== */ /* ================================================== */
static void
update_sel_options(void)
{
int options, auth_ntp_options, unauth_ntp_options, refclk_options;
int i, auth_ntp_sources, unauth_ntp_sources;
auth_ntp_sources = unauth_ntp_sources = 0;
for (i = 0; i < n_sources; i++) {
if (sources[i]->conf_sel_options & SRC_SELECT_NOSELECT)
continue;
if (sources[i]->type != SRC_NTP)
continue;
if (sources[i]->authenticated)
auth_ntp_sources++;
else
unauth_ntp_sources++;
}
auth_ntp_options = unauth_ntp_options = refclk_options = 0;
/* Determine which selection options need to be added to authenticated NTP
sources, unauthenticated NTP sources, and refclocks, to follow the
configured selection mode */
switch (CNF_GetAuthSelectMode()) {
case SRC_AUTHSELECT_IGNORE:
break;
case SRC_AUTHSELECT_MIX:
if (auth_ntp_sources > 0 && unauth_ntp_sources > 0)
auth_ntp_options = refclk_options = SRC_SELECT_REQUIRE | SRC_SELECT_TRUST;
break;
case SRC_AUTHSELECT_PREFER:
if (auth_ntp_sources > 0)
unauth_ntp_options = SRC_SELECT_NOSELECT;
break;
case SRC_AUTHSELECT_REQUIRE:
unauth_ntp_options = SRC_SELECT_NOSELECT;
break;
default:
assert(0);
}
for (i = 0; i < n_sources; i++) {
options = sources[i]->conf_sel_options;
if (options & SRC_SELECT_NOSELECT)
continue;
switch (sources[i]->type) {
case SRC_NTP:
options |= sources[i]->authenticated ? auth_ntp_options : unauth_ntp_options;
break;
case SRC_REFCLOCK:
options |= refclk_options;
break;
default:
assert(0);
}
if (sources[i]->sel_options != options) {
DEBUG_LOG("changing %s from %x to %x", source_to_string(sources[i]),
(unsigned int)sources[i]->sel_options, (unsigned int)options);
sources[i]->sel_options = options;
}
}
}
/* ================================================== */
static void static void
log_selection_message(const char *format, const char *arg) log_selection_message(const char *format, const char *arg)
{ {
@@ -492,6 +581,24 @@ log_selection_message(const char *format, const char *arg)
/* ================================================== */ /* ================================================== */
static void
log_selection_source(const char *format, SRC_Instance inst)
{
char buf[320], *name, *ntp_name;
name = source_to_string(inst);
ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : NULL;
if (ntp_name && strcmp(name, ntp_name) != 0)
snprintf(buf, sizeof (buf), "%s (%s)", name, ntp_name);
else
snprintf(buf, sizeof (buf), "%s", name);
log_selection_message(format, buf);
}
/* ================================================== */
static int static int
compare_sort_elements(const void *a, const void *b) compare_sort_elements(const void *a, const void *b)
{ {
@@ -529,6 +636,20 @@ source_to_string(SRC_Instance inst)
/* ================================================== */ /* ================================================== */
static void
mark_source(SRC_Instance inst, SRC_Status status)
{
inst->status = status;
DEBUG_LOG("%s status=%d options=%x reach=%o/%d updates=%d distant=%d leap=%d vote=%d lo=%f hi=%f",
source_to_string(inst), (int)inst->status, (unsigned int)inst->sel_options,
(unsigned int)inst->reachability, inst->reachability_size, inst->updates,
inst->distant, (int)inst->leap, inst->leap_vote,
inst->sel_info.lo_limit, inst->sel_info.hi_limit);
}
/* ================================================== */
static void static void
mark_ok_sources(SRC_Status status) mark_ok_sources(SRC_Status status)
{ {
@@ -537,7 +658,7 @@ mark_ok_sources(SRC_Status status)
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
if (sources[i]->status != SRC_OK) if (sources[i]->status != SRC_OK)
continue; continue;
sources[i]->status = status; mark_source(sources[i], status);
} }
} }
@@ -588,12 +709,12 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
} }
if (sources[index]->distant) { if (sources[index]->distant) {
sources[index]->status = SRC_DISTANT; mark_source(sources[index], SRC_DISTANT);
continue; continue;
} }
if (sources[index]->status == SRC_OK) if (sources[index]->status == SRC_OK)
sources[index]->status = SRC_UNSELECTED; mark_source(sources[index], SRC_UNSELECTED);
elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time); elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time);
src_offset += elapsed * src_frequency; src_offset += elapsed * src_frequency;
@@ -642,12 +763,13 @@ SRC_SelectSource(SRC_Instance updated_inst)
struct timespec now, ref_time; struct timespec now, ref_time;
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source; int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source;
int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach; int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach;
int depth, best_depth, trust_depth, best_trust_depth; int depth, best_depth, trust_depth, best_trust_depth, n_sel_trust_sources;
int combined, stratum, min_stratum, max_score_index; int combined, stratum, min_stratum, max_score_index;
int orphan_stratum, orphan_source; int orphan_stratum, orphan_source;
double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew; double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
double src_root_delay, src_root_dispersion; double src_root_delay, src_root_dispersion;
double best_lo, best_hi, distance, sel_src_distance, max_score; double best_lo, best_hi, distance, sel_src_distance, max_score;
double best_trust_lo, best_trust_hi;
double first_sample_ago, max_reach_sample_ago; double first_sample_ago, max_reach_sample_ago;
NTP_Leap leap_status; NTP_Leap leap_status;
@@ -669,7 +791,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Step 1 - build intervals about each source */ /* Step 1 - build intervals about each source */
n_endpoints = 0; n_endpoints = 0;
n_sel_sources = 0; n_sel_sources = n_sel_trust_sources = 0;
n_badstats_sources = 0; n_badstats_sources = 0;
sel_req_source = 0; sel_req_source = 0;
max_sel_reach = max_badstat_reach = 0; max_sel_reach = max_badstat_reach = 0;
@@ -679,6 +801,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
assert(sources[i]->status != SRC_OK); assert(sources[i]->status != SRC_OK);
/* Don't allow the source to vote on leap seconds unless it's selectable */
sources[i]->leap_vote = 0;
/* If some sources are specified with the require option, at least one /* If some sources are specified with the require option, at least one
of them will have to be selectable in order to update the clock */ of them will have to be selectable in order to update the clock */
if (sources[i]->sel_options & SRC_SELECT_REQUIRE) if (sources[i]->sel_options & SRC_SELECT_REQUIRE)
@@ -686,19 +811,19 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Ignore sources which were added with the noselect option */ /* Ignore sources which were added with the noselect option */
if (sources[i]->sel_options & SRC_SELECT_NOSELECT) { if (sources[i]->sel_options & SRC_SELECT_NOSELECT) {
sources[i]->status = SRC_UNSELECTABLE; mark_source(sources[i], SRC_UNSELECTABLE);
continue; continue;
} }
si = &sources[i]->sel_info; si = &sources[i]->sel_info;
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum, SST_GetSelectionData(sources[i]->stats, &now,
&si->lo_limit, &si->hi_limit, &si->root_distance, &si->lo_limit, &si->hi_limit, &si->root_distance,
&si->std_dev, &first_sample_ago, &si->std_dev, &first_sample_ago,
&si->last_sample_ago, &si->select_ok); &si->last_sample_ago, &si->select_ok);
if (!si->select_ok) { if (!si->select_ok) {
++n_badstats_sources; ++n_badstats_sources;
sources[i]->status = SRC_BAD_STATS; mark_source(sources[i], SRC_BAD_STATS);
if (max_badstat_reach < sources[i]->reachability) if (max_badstat_reach < sources[i]->reachability)
max_badstat_reach = sources[i]->reachability; max_badstat_reach = sources[i]->reachability;
continue; continue;
@@ -714,20 +839,20 @@ SRC_SelectSource(SRC_Instance updated_inst)
si->hi_limit += extra_disp; si->hi_limit += extra_disp;
} }
/* Require the root distance to be below the allowed maximum */ /* Require the root distance to be below the allowed maximum and the
if (si->root_distance > max_distance) { endpoints to be in the right order (i.e. a non-negative distance) */
sources[i]->status = SRC_BAD_DISTANCE; if (!(si->root_distance <= max_distance && si->lo_limit <= si->hi_limit)) {
mark_source(sources[i], SRC_BAD_DISTANCE);
continue; continue;
} }
/* And the same applies for the estimated standard deviation */ /* And the same applies for the estimated standard deviation */
if (si->std_dev > max_jitter) { if (si->std_dev > max_jitter) {
sources[i]->status = SRC_JITTERY; mark_source(sources[i], SRC_JITTERY);
continue; continue;
} }
sources[i]->status = SRC_OK; /* For now */ sources[i]->status = SRC_OK; /* For now */
sources[i]->leap_vote = 0;
if (sources[i]->reachability && max_reach_sample_ago < first_sample_ago) if (sources[i]->reachability && max_reach_sample_ago < first_sample_ago)
max_reach_sample_ago = first_sample_ago; max_reach_sample_ago = first_sample_ago;
@@ -752,7 +877,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
can still be selected if its newest sample is not older than the oldest can still be selected if its newest sample is not older than the oldest
sample from reachable sources. */ sample from reachable sources. */
if (!sources[i]->reachability && max_reach_sample_ago < si->last_sample_ago) { if (!sources[i]->reachability && max_reach_sample_ago < si->last_sample_ago) {
sources[i]->status = SRC_STALE; mark_source(sources[i], SRC_STALE);
continue; continue;
} }
@@ -773,10 +898,10 @@ SRC_SelectSource(SRC_Instance updated_inst)
source can settle down to a state where only one server is serving its source can settle down to a state where only one server is serving its
local unsychronised time and others are synchronised to it. */ local unsychronised time and others are synchronised to it. */
if (si->stratum >= orphan_stratum && sources[i]->type == SRC_NTP) { if (sources[i]->stratum >= orphan_stratum && sources[i]->type == SRC_NTP) {
sources[i]->status = SRC_ORPHAN; mark_source(sources[i], SRC_ORPHAN);
if (si->stratum == orphan_stratum && sources[i]->reachability && if (sources[i]->stratum == orphan_stratum && sources[i]->reachability &&
(orphan_source == INVALID_SOURCE || (orphan_source == INVALID_SOURCE ||
sources[i]->ref_id < sources[orphan_source]->ref_id)) sources[i]->ref_id < sources[orphan_source]->ref_id))
orphan_source = i; orphan_source = i;
@@ -804,6 +929,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (sources[i]->status != SRC_OK) if (sources[i]->status != SRC_OK)
continue; continue;
if (sources[i]->sel_options & SRC_SELECT_TRUST)
n_sel_trust_sources++;
si = &sources[i]->sel_info; si = &sources[i]->sel_info;
j1 = n_endpoints; j1 = n_endpoints;
@@ -820,7 +948,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_endpoints += 2; n_endpoints += 2;
} }
DEBUG_LOG("badstat=%d sel=%d badstat_reach=%x sel_reach=%x size=%d max_reach_ago=%f", DEBUG_LOG("badstat=%d sel=%d badstat_reach=%o sel_reach=%o size=%d max_reach_ago=%f",
n_badstats_sources, n_sel_sources, (unsigned int)max_badstat_reach, n_badstats_sources, n_sel_sources, (unsigned int)max_badstat_reach,
(unsigned int)max_sel_reach, max_sel_reach_size, max_reach_sample_ago); (unsigned int)max_sel_reach, max_sel_reach_size, max_reach_sample_ago);
@@ -876,7 +1004,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
trust_depth = best_trust_depth = 0; trust_depth = best_trust_depth = 0;
depth = best_depth = 0; depth = best_depth = 0;
best_lo = best_hi = 0.0; best_lo = best_hi = best_trust_lo = best_trust_hi = 0.0;
for (i = 0; i < n_endpoints; i++) { for (i = 0; i < n_endpoints; i++) {
switch (sort_list[i].tag) { switch (sort_list[i].tag) {
@@ -886,14 +1014,20 @@ SRC_SelectSource(SRC_Instance updated_inst)
trust_depth++; trust_depth++;
if (trust_depth > best_trust_depth || if (trust_depth > best_trust_depth ||
(trust_depth == best_trust_depth && depth > best_depth)) { (trust_depth == best_trust_depth && depth > best_depth)) {
best_trust_depth = trust_depth; if (trust_depth > best_trust_depth) {
best_trust_depth = trust_depth;
best_trust_lo = sort_list[i].offset;
}
best_depth = depth; best_depth = depth;
best_lo = sort_list[i].offset; best_lo = sort_list[i].offset;
} }
break; break;
case HIGH: case HIGH:
if (trust_depth == best_trust_depth && depth == best_depth) if (trust_depth == best_trust_depth) {
best_hi = sort_list[i].offset; if (depth == best_depth)
best_hi = sort_list[i].offset;
best_trust_hi = sort_list[i].offset;
}
if (sources[sort_list[i].index]->sel_options & SRC_SELECT_TRUST) if (sources[sort_list[i].index]->sel_options & SRC_SELECT_TRUST)
trust_depth--; trust_depth--;
depth--; depth--;
@@ -901,11 +1035,16 @@ SRC_SelectSource(SRC_Instance updated_inst)
default: default:
assert(0); assert(0);
} }
assert(trust_depth <= depth);
assert(trust_depth >= 0);
} }
if (best_depth <= n_sel_sources / 2 && !best_trust_depth) { assert(depth == 0 && trust_depth == 0);
/* Could not even get half the reachable sources to agree and there assert(2 * n_sel_sources == n_endpoints);
are no trusted sources - clearly we can't synchronise */
if ((best_trust_depth == 0 && best_depth <= n_sel_sources / 2) ||
(best_trust_depth > 0 && best_trust_depth <= n_sel_trust_sources / 2)) {
/* Could not even get half the reachable (trusted) sources to agree */
if (selected_source_index != INVALID_SOURCE) { if (selected_source_index != INVALID_SOURCE) {
log_selection_message("Can't synchronise: no majority", NULL); log_selection_message("Can't synchronise: no majority", NULL);
@@ -932,24 +1071,28 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue; continue;
/* Check if source's interval contains the best interval, or is wholly /* Check if source's interval contains the best interval, or is wholly
contained within it. If there are any trusted sources the first contained within it. If there are any trusted sources, other sources
condition is applied only to them to not allow non-trusted sources to are required to be wholly contained within the best interval of the
move the final offset outside the interval. */ trusted sources to not allow non-trusted sources to move the final
if (((!best_trust_depth || sources[i]->sel_options & SRC_SELECT_TRUST) && offset outside the trusted interval. */
sources[i]->sel_info.lo_limit <= best_lo && if ((sources[i]->sel_info.lo_limit <= best_lo &&
sources[i]->sel_info.hi_limit >= best_hi) || sources[i]->sel_info.hi_limit >= best_hi) ||
(sources[i]->sel_info.lo_limit >= best_lo && (sources[i]->sel_info.lo_limit >= best_lo &&
sources[i]->sel_info.hi_limit <= best_hi)) { sources[i]->sel_info.hi_limit <= best_hi)) {
if (!(best_trust_depth == 0 || (sources[i]->sel_options & SRC_SELECT_TRUST) ||
(sources[i]->sel_info.lo_limit >= best_trust_lo &&
sources[i]->sel_info.hi_limit <= best_trust_hi))) {
mark_source(sources[i], SRC_UNTRUSTED);
continue;
}
sel_sources[n_sel_sources++] = i; sel_sources[n_sel_sources++] = i;
if (sources[i]->sel_options & SRC_SELECT_REQUIRE) if (sources[i]->sel_options & SRC_SELECT_REQUIRE)
sel_req_source = 0; sel_req_source = 0;
} else if (sources[i]->sel_info.lo_limit <= best_lo &&
sources[i]->sel_info.hi_limit >= best_hi) {
sources[i]->status = SRC_UNTRUSTED;
} else { } else {
sources[i]->status = SRC_FALSETICKER; mark_source(sources[i], SRC_FALSETICKER);
} }
} }
@@ -982,7 +1125,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (i < n_sel_sources) { if (i < n_sel_sources) {
for (i = j = 0; i < n_sel_sources; i++) { for (i = j = 0; i < n_sel_sources; i++) {
if (!(sources[sel_sources[i]]->sel_options & SRC_SELECT_PREFER)) if (!(sources[sel_sources[i]]->sel_options & SRC_SELECT_PREFER))
sources[sel_sources[i]]->status = SRC_NONPREFERRED; mark_source(sources[sel_sources[i]], SRC_NONPREFERRED);
else else
sel_sources[j++] = sel_sources[i]; sel_sources[j++] = sel_sources[i];
} }
@@ -996,10 +1139,10 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Find minimum stratum */ /* Find minimum stratum */
index = sel_sources[0]; index = sel_sources[0];
min_stratum = sources[index]->sel_info.stratum; min_stratum = sources[index]->stratum;
for (i = 1; i < n_sel_sources; i++) { for (i = 1; i < n_sel_sources; i++) {
index = sel_sources[i]; index = sel_sources[i];
stratum = sources[index]->sel_info.stratum; stratum = sources[index]->stratum;
if (stratum < min_stratum) if (stratum < min_stratum)
min_stratum = stratum; min_stratum = stratum;
} }
@@ -1012,7 +1155,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (selected_source_index != INVALID_SOURCE) if (selected_source_index != INVALID_SOURCE)
sel_src_distance = sources[selected_source_index]->sel_info.root_distance + sel_src_distance = sources[selected_source_index]->sel_info.root_distance +
(sources[selected_source_index]->sel_info.stratum - min_stratum) * stratum_weight; (sources[selected_source_index]->stratum - min_stratum) * stratum_weight;
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
/* Reset score for non-selectable sources */ /* Reset score for non-selectable sources */
@@ -1024,7 +1167,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
} }
distance = sources[i]->sel_info.root_distance + distance = sources[i]->sel_info.root_distance +
(sources[i]->sel_info.stratum - min_stratum) * stratum_weight; (sources[i]->stratum - min_stratum) * stratum_weight;
if (sources[i]->type == SRC_NTP) if (sources[i]->type == SRC_NTP)
distance += reselect_distance; distance += reselect_distance;
@@ -1046,10 +1189,8 @@ SRC_SelectSource(SRC_Instance updated_inst)
sources[i]->sel_score = 1.0 / distance; sources[i]->sel_score = 1.0 / distance;
} }
DEBUG_LOG("select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%u dist=%f", DEBUG_LOG("%s score=%f dist=%f",
sources[i]->sel_score, sources[i]->ref_id, source_to_string(sources[i]), sources[i]->sel_score, distance);
updated_inst ? updated_inst->ref_id : 0,
sources[i]->status, distance);
if (max_score < sources[i]->sel_score) { if (max_score < sources[i]->sel_score) {
max_score = sources[i]->sel_score; max_score = sources[i]->sel_score;
@@ -1070,13 +1211,11 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (sources[max_score_index]->updates == 0) { if (sources[max_score_index]->updates == 0) {
selected_source_index = INVALID_SOURCE; selected_source_index = INVALID_SOURCE;
mark_ok_sources(SRC_WAITS_UPDATE); mark_ok_sources(SRC_WAITS_UPDATE);
DEBUG_LOG("best source has no updates");
return; return;
} }
selected_source_index = max_score_index; selected_source_index = max_score_index;
log_selection_message("Selected source %s", log_selection_source("Selected source %s", sources[selected_source_index]);
source_to_string(sources[selected_source_index]));
/* New source has been selected, reset all scores */ /* New source has been selected, reset all scores */
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
@@ -1085,7 +1224,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
} }
} }
sources[selected_source_index]->status = SRC_SELECTED; mark_source(sources[selected_source_index], SRC_SELECTED);
/* Don't update reference when the selected source has no new samples */ /* Don't update reference when the selected source has no new samples */
@@ -1095,8 +1234,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
for (i = 0; i < n_sel_sources; i++) { for (i = 0; i < n_sel_sources; i++) {
index = sel_sources[i]; index = sel_sources[i];
if (sources[index]->status == SRC_OK) if (sources[index]->status == SRC_OK)
sources[index]->status = sources[index]->distant ? mark_source(sources[index], sources[index]->distant ? SRC_DISTANT : SRC_UNSELECTED);
SRC_DISTANT : SRC_UNSELECTED;
} }
return; return;
} }
@@ -1117,7 +1255,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
combined = combine_sources(n_sel_sources, &ref_time, &src_offset, &src_offset_sd, combined = combine_sources(n_sel_sources, &ref_time, &src_offset, &src_offset_sd,
&src_frequency, &src_frequency_sd, &src_skew); &src_frequency, &src_frequency_sd, &src_skew);
REF_SetReference(sources[selected_source_index]->sel_info.stratum, REF_SetReference(sources[selected_source_index]->stratum,
leap_status, combined, leap_status, combined,
sources[selected_source_index]->ref_id, sources[selected_source_index]->ref_id,
sources[selected_source_index]->ip_addr, sources[selected_source_index]->ip_addr,
@@ -1190,25 +1328,60 @@ add_dispersion(double dispersion, void *anything)
/* ================================================== */ /* ================================================== */
static static int
FILE *open_dumpfile(SRC_Instance inst, char mode) get_dumpfile(SRC_Instance inst, char *filename, size_t len)
{ {
char filename[64], *dumpdir; /* Use the IP address, or reference ID with reference clocks */
switch (inst->type) {
dumpdir = CNF_GetDumpDir(); case SRC_NTP:
if (dumpdir[0] == '\0') { if (!UTI_IsIPReal(inst->ip_addr) ||
return NULL; snprintf(filename, len, "%s", source_to_string(inst)) >= len)
return 0;
break;
case SRC_REFCLOCK:
if (snprintf(filename, len, "refid:%08"PRIx32, inst->ref_id) >= len)
return 0;
break;
default:
assert(0);
} }
/* Include IP address in the name for NTP sources, or reference ID in hex */ return 1;
if (inst->type == SRC_NTP && UTI_IsIPReal(inst->ip_addr)) }
snprintf(filename, sizeof (filename), "%s", source_to_string(inst));
else if (inst->type == SRC_REFCLOCK)
snprintf(filename, sizeof (filename), "refid:%08"PRIx32, inst->ref_id);
else
return NULL;
return UTI_OpenFile(dumpdir, filename, ".dat", mode, 0644); /* ================================================== */
static void
save_source(SRC_Instance inst)
{
char filename[64], *dumpdir, *ntp_name;
FILE *f;
dumpdir = CNF_GetDumpDir();
if (!dumpdir)
return;
if (!get_dumpfile(inst, filename, sizeof (filename)))
return;
f = UTI_OpenFile(dumpdir, filename, ".dat", 'w', 0644);
if (!f)
return;
ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : ".";
if (fprintf(f, "%s%s\n%d %o %d %d %d\n",
DUMP_IDENTIFIER, ntp_name, inst->authenticated,
(unsigned int)inst->reachability, inst->reachability_size,
inst->stratum, (int)inst->leap) < 0 ||
!SST_SaveToFile(inst->stats, f)) {
fclose(f);
if (!UTI_RemoveFile(dumpdir, filename, ".dat"))
;
return;
}
fclose(f);
} }
/* ================================================== */ /* ================================================== */
@@ -1217,16 +1390,60 @@ FILE *open_dumpfile(SRC_Instance inst, char mode)
void void
SRC_DumpSources(void) SRC_DumpSources(void)
{ {
FILE *out;
int i; int i;
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++)
out = open_dumpfile(sources[i], 'w'); save_source(sources[i]);
if (!out) }
continue;
SST_SaveToFile(sources[i]->stats, out); /* ================================================== */
fclose(out);
#define MAX_WORDS 1
static void
load_source(SRC_Instance inst)
{
char filename[64], line[256], *dumpdir, *ntp_name, *words[MAX_WORDS];
int auth, leap, reach_size, stratum;
unsigned int reach;
FILE *f;
dumpdir = CNF_GetDumpDir();
if (!dumpdir)
return;
if (!get_dumpfile(inst, filename, sizeof (filename)))
return;
f = UTI_OpenFile(dumpdir, filename, ".dat", 'r', 0);
if (!f)
return;
ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : NULL;
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
(inst->type == SRC_NTP && (!ntp_name || strcmp(words[0], ntp_name) != 0)) ||
!fgets(line, sizeof (line), f) ||
sscanf(words[0], "%d %o %d %d %d",
&auth, &reach, &reach_size, &stratum, &leap) != 5 ||
(!auth && inst->authenticated) ||
stratum < 0 || stratum >= NTP_MAX_STRATUM ||
leap < LEAP_Normal || leap >= LEAP_Unsynchronised ||
!SST_LoadFromFile(inst->stats, f)) {
LOG(LOGS_WARN, "Could not load dump file for %s", source_to_string(inst));
fclose(f);
return;
} }
inst->reachability = reach & ((1U << SOURCE_REACH_BITS) - 1);
inst->reachability_size = CLAMP(0, reach_size, SOURCE_REACH_BITS);
inst->stratum = stratum;
inst->leap = leap;
LOG(LOGS_INFO, "Loaded dump file for %s", source_to_string(inst));
fclose(f);
} }
/* ================================================== */ /* ================================================== */
@@ -1234,21 +1451,17 @@ SRC_DumpSources(void)
void void
SRC_ReloadSources(void) SRC_ReloadSources(void)
{ {
FILE *in;
int i; int i;
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
in = open_dumpfile(sources[i], 'r'); load_source(sources[i]);
if (!in)
continue; /* Allow an immediate update of the reference */
if (!SST_LoadFromFile(sources[i]->stats, in)) sources[i]->updates++;
LOG(LOGS_WARN, "Could not load dump file for %s",
source_to_string(sources[i]));
else
LOG(LOGS_INFO, "Loaded dump file for %s",
source_to_string(sources[i]));
fclose(in);
} }
/* Select sources and set the reference */
SRC_SelectSource(NULL);
} }
/* ================================================== */ /* ================================================== */
@@ -1262,7 +1475,7 @@ SRC_RemoveDumpFiles(void)
size_t i; size_t i;
dumpdir = CNF_GetDumpDir(); dumpdir = CNF_GetDumpDir();
if (dumpdir[0] == '\0' || if (!dumpdir ||
snprintf(pattern, sizeof (pattern), "%s/*.dat", dumpdir) >= sizeof (pattern)) snprintf(pattern, sizeof (pattern), "%s/*.dat", dumpdir) >= sizeof (pattern))
return; return;
@@ -1363,6 +1576,8 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now)
report->ip_addr.family = IPADDR_INET4; report->ip_addr.family = IPADDR_INET4;
} }
report->stratum = src->stratum;
switch (src->status) { switch (src->status) {
case SRC_FALSETICKER: case SRC_FALSETICKER:
report->state = RPT_FALSETICKER; report->state = RPT_FALSETICKER;
@@ -1370,26 +1585,24 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now)
case SRC_JITTERY: case SRC_JITTERY:
report->state = RPT_JITTERY; report->state = RPT_JITTERY;
break; break;
case SRC_UNTRUSTED:
case SRC_WAITS_SOURCES: case SRC_WAITS_SOURCES:
case SRC_NONPREFERRED: case SRC_NONPREFERRED:
case SRC_WAITS_UPDATE: case SRC_WAITS_UPDATE:
case SRC_DISTANT: case SRC_DISTANT:
case SRC_OUTLIER: case SRC_OUTLIER:
report->state = RPT_OUTLIER; report->state = RPT_SELECTABLE;
break; break;
case SRC_UNSELECTED: case SRC_UNSELECTED:
report->state = RPT_CANDIDATE; report->state = RPT_UNSELECTED;
break; break;
case SRC_SELECTED: case SRC_SELECTED:
report->state = RPT_SYNC; report->state = RPT_SELECTED;
break; break;
default: default:
report->state = RPT_UNREACH; report->state = RPT_NONSELECTABLE;
break; break;
} }
report->sel_options = src->sel_options;
report->reachability = src->reachability; report->reachability = src->reachability;
/* Call stats module to fill out estimates */ /* Call stats module to fill out estimates */
@@ -1423,6 +1636,79 @@ SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec
/* ================================================== */ /* ================================================== */
static char
get_status_char(SRC_Status status)
{
switch (status) {
case SRC_UNSELECTABLE:
return 'N';
case SRC_BAD_STATS:
return 'M';
case SRC_BAD_DISTANCE:
return 'd';
case SRC_JITTERY:
return '~';
case SRC_WAITS_STATS:
return 'w';
case SRC_STALE:
return 'S';
case SRC_ORPHAN:
return 'O';
case SRC_UNTRUSTED:
return 'T';
case SRC_FALSETICKER:
return 'x';
case SRC_WAITS_SOURCES:
return 'W';
case SRC_NONPREFERRED:
return 'P';
case SRC_WAITS_UPDATE:
return 'U';
case SRC_DISTANT:
return 'D';
case SRC_OUTLIER:
return 'L';
case SRC_UNSELECTED:
return '+';
case SRC_SELECTED:
return '*';
default:
return '?';
}
}
/* ================================================== */
int
SRC_GetSelectReport(int index, RPT_SelectReport *report)
{
SRC_Instance inst;
if (index >= n_sources || index < 0)
return 0;
inst = sources[index];
report->ref_id = inst->ref_id;
if (inst->ip_addr)
report->ip_addr = *inst->ip_addr;
else
report->ip_addr.family = IPADDR_UNSPEC;
report->state_char = get_status_char(inst->status);
report->authentication = inst->authenticated;
report->leap = inst->leap;
report->conf_options = inst->conf_sel_options;
report->eff_options = inst->sel_options;
report->last_sample_ago = inst->sel_info.last_sample_ago;
report->score = inst->sel_score;
report->lo_limit = inst->sel_info.lo_limit;
report->hi_limit = inst->sel_info.hi_limit;
return 1;
}
/* ================================================== */
SRC_Type SRC_Type
SRC_GetType(int index) SRC_GetType(int index)
{ {

View File

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

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2014, 2016-2018 * Copyright (C) Miroslav Lichvar 2011-2014, 2016-2018, 2021
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -177,9 +177,6 @@ struct SST_Stats_Record {
/* This array contains the root dispersions of each sample at the /* This array contains the root dispersions of each sample at the
time of the measurements */ time of the measurements */
double root_dispersions[MAX_SAMPLES]; double root_dispersions[MAX_SAMPLES];
/* The stratum from the last accumulated sample */
int stratum;
}; };
/* ================================================== */ /* ================================================== */
@@ -321,7 +318,6 @@ SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample)
inst->peer_dispersions[m] = sample->peer_dispersion; inst->peer_dispersions[m] = sample->peer_dispersion;
inst->root_delays[m] = sample->root_delay; inst->root_delays[m] = sample->root_delay;
inst->root_dispersions[m] = sample->root_dispersion; inst->root_dispersions[m] = sample->root_dispersion;
inst->stratum = sample->stratum;
if (inst->peer_delays[n] < inst->fixed_min_delay) if (inst->peer_delays[n] < inst->fixed_min_delay)
inst->peer_delays[n] = 2.0 * inst->fixed_min_delay - inst->peer_delays[n]; inst->peer_delays[n] = 2.0 * inst->fixed_min_delay - inst->peer_delays[n];
@@ -650,7 +646,6 @@ SST_GetFrequencyRange(SST_Stats inst,
void void
SST_GetSelectionData(SST_Stats inst, struct timespec *now, SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
double *offset_lo_limit, double *offset_lo_limit,
double *offset_hi_limit, double *offset_hi_limit,
double *root_distance, double *root_distance,
@@ -670,7 +665,6 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
i = get_runsbuf_index(inst, inst->best_single_sample); i = get_runsbuf_index(inst, inst->best_single_sample);
j = get_buf_index(inst, inst->best_single_sample); j = get_buf_index(inst, inst->best_single_sample);
*stratum = inst->stratum;
*std_dev = inst->std_dev; *std_dev = inst->std_dev;
sample_elapsed = fabs(UTI_DiffTimespecsToDouble(now, &inst->sample_times[i])); sample_elapsed = fabs(UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]));
@@ -785,6 +779,22 @@ SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doff
/* ================================================== */ /* ================================================== */
void
SST_CorrectOffset(SST_Stats inst, double doffset)
{
int i;
if (!inst->n_samples)
return;
for (i = -inst->runs_samples; i < inst->n_samples; i++)
inst->offsets[get_runsbuf_index(inst, i)] += doffset;
inst->estimated_offset += doffset;
}
/* ================================================== */
void void
SST_AddDispersion(SST_Stats inst, double dispersion) SST_AddDispersion(SST_Stats inst, double dispersion)
{ {
@@ -858,38 +868,30 @@ SST_GetDelayTestData(SST_Stats inst, struct timespec *sample_time,
/* This is used to save the register to a file, so that we can reload /* This is used to save the register to a file, so that we can reload
it after restarting the daemon */ it after restarting the daemon */
void int
SST_SaveToFile(SST_Stats inst, FILE *out) SST_SaveToFile(SST_Stats inst, FILE *out)
{ {
int m, i, j; int m, i, j;
fprintf(out, "%d\n", inst->n_samples); if (inst->n_samples < 1)
return 0;
if (fprintf(out, "%d %d\n", inst->n_samples, inst->asymmetry_run) < 0)
return 0;
for(m = 0; m < inst->n_samples; m++) { for(m = 0; m < inst->n_samples; m++) {
i = get_runsbuf_index(inst, m); i = get_runsbuf_index(inst, m);
j = get_buf_index(inst, m); j = get_buf_index(inst, m);
fprintf(out, if (fprintf(out, "%s %.6e %.6e %.6e %.6e %.6e %.6e\n",
#ifdef HAVE_LONG_TIME_T UTI_TimespecToString(&inst->sample_times[i]),
"%08"PRIx64" %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n", inst->offsets[i], inst->orig_offsets[j],
(uint64_t)inst->sample_times[i].tv_sec, inst->peer_delays[i], inst->peer_dispersions[j],
#else inst->root_delays[j], inst->root_dispersions[j]) < 0)
"%08lx %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n", return 0;
(unsigned long)inst->sample_times[i].tv_sec,
#endif
(unsigned long)inst->sample_times[i].tv_nsec / 1000,
inst->offsets[i],
inst->orig_offsets[j],
inst->peer_delays[i],
inst->peer_dispersions[j],
inst->root_delays[j],
inst->root_dispersions[j],
1.0, /* used to be inst->weights[i] */
inst->stratum /* used to be an array */);
} }
fprintf(out, "%d\n", inst->asymmetry_run); return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -898,65 +900,46 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
int int
SST_LoadFromFile(SST_Stats inst, FILE *in) SST_LoadFromFile(SST_Stats inst, FILE *in)
{ {
#ifdef HAVE_LONG_TIME_T int i, n_samples, arun;
uint64_t sec; struct timespec now;
#else double sample_time;
unsigned long sec; char line[256];
#endif
unsigned long usec; if (!fgets(line, sizeof (line), in) ||
int i; sscanf(line, "%d %d", &n_samples, &arun) != 2 ||
char line[1024]; n_samples < 1 || n_samples > MAX_SAMPLES)
double weight; return 0;
SST_ResetInstance(inst); SST_ResetInstance(inst);
if (fgets(line, sizeof(line), in) && LCL_ReadCookedTime(&now, NULL);
sscanf(line, "%d", &inst->n_samples) == 1 &&
inst->n_samples >= 0 && inst->n_samples <= MAX_SAMPLES) {
for (i=0; i<inst->n_samples; i++) { for (i = 0; i < n_samples; i++) {
if (!fgets(line, sizeof(line), in) || if (!fgets(line, sizeof (line), in) ||
(sscanf(line, sscanf(line, "%lf %lf %lf %lf %lf %lf %lf",
#ifdef HAVE_LONG_TIME_T &sample_time, &inst->offsets[i], &inst->orig_offsets[i],
"%"SCNx64"%lx%lf%lf%lf%lf%lf%lf%lf%d\n", &inst->peer_delays[i], &inst->peer_dispersions[i],
#else &inst->root_delays[i], &inst->root_dispersions[i]) != 7)
"%lx%lx%lf%lf%lf%lf%lf%lf%lf%d\n", return 0;
#endif
&(sec), &(usec),
&(inst->offsets[i]),
&(inst->orig_offsets[i]),
&(inst->peer_delays[i]),
&(inst->peer_dispersions[i]),
&(inst->root_delays[i]),
&(inst->root_dispersions[i]),
&weight, /* not used anymore */
&inst->stratum) != 10)) {
/* This is the branch taken if the read FAILED */ if (!UTI_IsTimeOffsetSane(&now, sample_time - UTI_TimespecToDouble(&now)))
return 0;
inst->n_samples = 0; /* Load abandoned if any sign of corruption */ /* Some resolution is lost in the double format, but that's ok */
return 0; UTI_DoubleToTimespec(sample_time, &inst->sample_times[i]);
} else {
/* This is the branch taken if the read is SUCCESSFUL */ /* Make sure the samples are sane and they are in order */
inst->sample_times[i].tv_sec = sec; if (!UTI_IsTimeOffsetSane(&inst->sample_times[i], -inst->offsets[i]) ||
inst->sample_times[i].tv_nsec = 1000 * usec; !(fabs(inst->peer_delays[i]) < 1.0e6 && fabs(inst->peer_dispersions[i]) < 1.0e6 &&
UTI_NormaliseTimespec(&inst->sample_times[i]); fabs(inst->root_delays[i]) < 1.0e6 && fabs(inst->root_dispersions[i]) < 1.0e6) ||
} (i > 0 && UTI_CompareTimespecs(&inst->sample_times[i],
} &inst->sample_times[i - 1]) <= 0))
return 0;
/* This field was not saved in older versions */
if (!fgets(line, sizeof(line), in) || sscanf(line, "%d\n", &inst->asymmetry_run) != 1)
inst->asymmetry_run = 0;
} else {
inst->n_samples = 0; /* Load abandoned if any sign of corruption */
return 0;
} }
if (!inst->n_samples) inst->n_samples = n_samples;
return 1;
inst->last_sample = inst->n_samples - 1; inst->last_sample = inst->n_samples - 1;
inst->asymmetry_run = CLAMP(-MAX_ASYMMETRY_RUN, arun, MAX_ASYMMETRY_RUN);
find_min_delay_sample(inst); find_min_delay_sample(inst);
SST_DoNewRegression(inst); SST_DoNewRegression(inst);
@@ -978,7 +961,6 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *no
report->orig_latest_meas = inst->orig_offsets[j]; report->orig_latest_meas = inst->orig_offsets[j];
report->latest_meas = inst->offsets[i]; report->latest_meas = inst->offsets[i];
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j]; report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
report->stratum = inst->stratum;
/* Align the sample time to reduce the leak of the receive timestamp */ /* Align the sample time to reduce the leak of the receive timestamp */
last_sample_time = inst->sample_times[i]; last_sample_time = inst->sample_times[i];

View File

@@ -69,7 +69,6 @@ extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
/* Get data needed for selection */ /* Get data needed for selection */
extern void extern void
SST_GetSelectionData(SST_Stats inst, struct timespec *now, SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
double *offset_lo_limit, double *offset_lo_limit,
double *offset_hi_limit, double *offset_hi_limit,
double *root_distance, double *root_distance,
@@ -103,6 +102,10 @@ SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
extern void SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doffset); extern void SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doffset);
/* This routine corrects already accumulated samples to improve the
frequency estimate when a new sample is accumulated */
extern void SST_CorrectOffset(SST_Stats inst, double doffset);
/* This routine is called when an indeterminate offset is introduced /* This routine is called when an indeterminate offset is introduced
into the local time. */ into the local time. */
extern void SST_AddDispersion(SST_Stats inst, double dispersion); extern void SST_AddDispersion(SST_Stats inst, double dispersion);
@@ -120,7 +123,7 @@ extern int SST_GetDelayTestData(SST_Stats inst, struct timespec *sample_time,
double *last_sample_ago, double *predicted_offset, double *last_sample_ago, double *predicted_offset,
double *min_delay, double *skew, double *std_dev); double *min_delay, double *skew, double *std_dev);
extern void SST_SaveToFile(SST_Stats inst, FILE *out); extern int SST_SaveToFile(SST_Stats inst, FILE *out);
extern int SST_LoadFromFile(SST_Stats inst, FILE *in); extern int SST_LoadFromFile(SST_Stats inst, FILE *in);

View File

@@ -54,7 +54,10 @@ typedef struct {
int sel_options; int sel_options;
int nts; int nts;
int nts_port; int nts_port;
int copy;
int ext_fields;
uint32_t authkey; uint32_t authkey;
uint32_t cert_set;
double max_delay; double max_delay;
double max_delay_ratio; double max_delay_ratio;
double max_delay_dev_ratio; double max_delay_dev_ratio;
@@ -76,7 +79,8 @@ typedef struct {
#define SRC_DEFAULT_MINSAMPLES (-1) #define SRC_DEFAULT_MINSAMPLES (-1)
#define SRC_DEFAULT_MAXSAMPLES (-1) #define SRC_DEFAULT_MAXSAMPLES (-1)
#define SRC_DEFAULT_ASYMMETRY 1.0 #define SRC_DEFAULT_ASYMMETRY 1.0
#define SRC_DEFAULT_NTSPORT 11443 #define SRC_DEFAULT_NTSPORT 4460
#define SRC_DEFAULT_CERTSET 0
#define INACTIVE_AUTHKEY 0 #define INACTIVE_AUTHKEY 0
/* Flags for source selection */ /* Flags for source selection */

59
stubs.c
View File

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

12
sys.c
View File

@@ -97,16 +97,16 @@ SYS_Finalise(void)
/* ================================================== */ /* ================================================== */
void SYS_DropRoot(uid_t uid, gid_t gid) void SYS_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context)
{ {
#if defined(LINUX) && defined (FEAT_PRIVDROP) #if defined(LINUX) && defined (FEAT_PRIVDROP)
SYS_Linux_DropRoot(uid, gid, !null_driver); SYS_Linux_DropRoot(uid, gid, context, !null_driver);
#elif defined(SOLARIS) && defined(FEAT_PRIVDROP) #elif defined(SOLARIS) && defined(FEAT_PRIVDROP)
SYS_Solaris_DropRoot(uid, gid); SYS_Solaris_DropRoot(uid, gid, context);
#elif (defined(NETBSD) || defined(FREEBSD)) && defined(FEAT_PRIVDROP) #elif (defined(NETBSD) || defined(FREEBSD)) && defined(FEAT_PRIVDROP)
SYS_NetBSD_DropRoot(uid, gid); SYS_NetBSD_DropRoot(uid, gid, context, !null_driver);
#elif defined(MACOSX) && defined(FEAT_PRIVDROP) #elif defined(MACOSX) && defined(FEAT_PRIVDROP)
SYS_MacOSX_DropRoot(uid, gid); SYS_MacOSX_DropRoot(uid, gid, context);
#else #else
LOG_FATAL("dropping root privileges not supported"); LOG_FATAL("dropping root privileges not supported");
#endif #endif
@@ -114,7 +114,7 @@ void SYS_DropRoot(uid_t uid, gid_t gid)
/* ================================================== */ /* ================================================== */
void SYS_EnableSystemCallFilter(int level, SYS_SystemCallContext context) void SYS_EnableSystemCallFilter(int level, SYS_ProcessContext context)
{ {
#if defined(LINUX) && defined(FEAT_SCFILTER) #if defined(LINUX) && defined(FEAT_SCFILTER)
SYS_Linux_EnableSystemCallFilter(level, context); SYS_Linux_EnableSystemCallFilter(level, context);

10
sys.h
View File

@@ -35,17 +35,17 @@ extern void SYS_Initialise(int clock_control);
/* Called at the end of the run to do final clean-up */ /* Called at the end of the run to do final clean-up */
extern void SYS_Finalise(void); extern void SYS_Finalise(void);
/* Drop root privileges to the specified user and group */
extern void SYS_DropRoot(uid_t uid, gid_t gid);
typedef enum { typedef enum {
SYS_MAIN_PROCESS, SYS_MAIN_PROCESS,
SYS_NTSKE_HELPER, SYS_NTSKE_HELPER,
} SYS_SystemCallContext; } SYS_ProcessContext;
/* Switch to the specified user and group in given context */
extern void SYS_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context);
/* Enable a system call filter to allow only system calls /* Enable a system call filter to allow only system calls
which chronyd normally needs after initialization */ which chronyd normally needs after initialization */
extern void SYS_EnableSystemCallFilter(int level, SYS_SystemCallContext context); extern void SYS_EnableSystemCallFilter(int level, SYS_ProcessContext context);
extern void SYS_SetScheduler(int SchedPriority); extern void SYS_SetScheduler(int SchedPriority);
extern void SYS_LockMemory(void); extern void SYS_LockMemory(void);

View File

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

View File

@@ -97,21 +97,6 @@ static int have_setoffset;
updated in the kernel */ updated in the kernel */
static int tick_update_hz; static int tick_update_hz;
/* ================================================== */
inline static long
our_round(double x)
{
long y;
if (x > 0.0)
y = x + 0.5;
else
y = x - 0.5;
return y;
}
/* ================================================== */ /* ================================================== */
/* Positive means currently fast of true time, i.e. jump backwards */ /* Positive means currently fast of true time, i.e. jump backwards */
@@ -149,7 +134,7 @@ set_frequency(double freq_ppm)
double required_freq; double required_freq;
int required_delta_tick; int required_delta_tick;
required_delta_tick = our_round(freq_ppm / dhz); required_delta_tick = round(freq_ppm / dhz);
/* Older kernels (pre-2.6.18) don't apply the frequency offset exactly as /* Older kernels (pre-2.6.18) don't apply the frequency offset exactly as
set by adjtimex() and a scaling constant (that depends on the internal set by adjtimex() and a scaling constant (that depends on the internal
@@ -426,7 +411,7 @@ SYS_Linux_Finalise(void)
#ifdef FEAT_PRIVDROP #ifdef FEAT_PRIVDROP
void void
SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control) SYS_Linux_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context, int clock_control)
{ {
char cap_text[256]; char cap_text[256];
cap_t cap; cap_t cap;
@@ -437,13 +422,23 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control)
UTI_DropRoot(uid, gid); UTI_DropRoot(uid, gid);
/* Keep CAP_NET_BIND_SERVICE only if a server NTP port can be opened /* Keep CAP_NET_BIND_SERVICE if the NTP server sockets may need to be bound
and keep CAP_SYS_TIME only if the clock control is enabled */ to a privileged port.
if (snprintf(cap_text, sizeof (cap_text), "%s %s", Keep CAP_NET_RAW if an NTP socket may need to be bound to a device on
CNF_GetNTPPort() ? "cap_net_bind_service=ep" : "", kernels before 5.7.
Keep CAP_SYS_TIME if the clock control is enabled. */
if (snprintf(cap_text, sizeof (cap_text), "%s %s %s",
(CNF_GetNTPPort() > 0 && CNF_GetNTPPort() < 1024) ?
"cap_net_bind_service=ep" : "",
(CNF_GetBindNtpInterface() || CNF_GetBindAcquisitionInterface()) &&
!SYS_Linux_CheckKernelVersion(5, 7) ? "cap_net_raw=ep" : "",
clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text)) clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text))
assert(0); assert(0);
/* Helpers don't need any capabilities */
if (context != SYS_MAIN_PROCESS)
cap_text[0] = '\0';
if ((cap = cap_from_text(cap_text)) == NULL) { if ((cap = cap_from_text(cap_text)) == NULL) {
LOG_FATAL("cap_from_text() failed"); LOG_FATAL("cap_from_text() failed");
} }
@@ -474,40 +469,154 @@ void check_seccomp_applicability(void)
/* ================================================== */ /* ================================================== */
void void
SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context) SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
{ {
const int syscalls[] = { const int allowed[] = {
/* Clock */ /* Clock */
SCMP_SYS(adjtimex), SCMP_SYS(clock_adjtime), SCMP_SYS(clock_gettime), SCMP_SYS(adjtimex),
SCMP_SYS(gettimeofday), SCMP_SYS(settimeofday), SCMP_SYS(time), SCMP_SYS(clock_adjtime),
#ifdef __NR_clock_adjtime64
SCMP_SYS(clock_adjtime64),
#endif
SCMP_SYS(clock_gettime),
#ifdef __NR_clock_gettime64
SCMP_SYS(clock_gettime64),
#endif
SCMP_SYS(gettimeofday),
SCMP_SYS(settimeofday),
SCMP_SYS(time),
/* Process */ /* Process */
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getpid), SCMP_SYS(clone),
SCMP_SYS(getrlimit), SCMP_SYS(getuid), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn), #ifdef __NR_clone3
SCMP_SYS(rt_sigprocmask), SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn), SCMP_SYS(clone3),
SCMP_SYS(wait4), SCMP_SYS(waitpid), #endif
SCMP_SYS(exit),
SCMP_SYS(exit_group),
SCMP_SYS(getpid),
SCMP_SYS(getrlimit),
SCMP_SYS(getuid),
SCMP_SYS(getuid32),
SCMP_SYS(rt_sigaction),
SCMP_SYS(rt_sigreturn),
SCMP_SYS(rt_sigprocmask),
SCMP_SYS(set_tid_address),
SCMP_SYS(sigreturn),
SCMP_SYS(wait4),
SCMP_SYS(waitpid),
/* Memory */ /* Memory */
SCMP_SYS(brk), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(mmap2), SCMP_SYS(brk),
SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt), SCMP_SYS(madvise),
SCMP_SYS(mmap),
SCMP_SYS(mmap2),
SCMP_SYS(mprotect),
SCMP_SYS(mremap),
SCMP_SYS(munmap),
SCMP_SYS(shmdt),
/* Filesystem */ /* Filesystem */
SCMP_SYS(_llseek), SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown), SCMP_SYS(_llseek),
SCMP_SYS(chown32), SCMP_SYS(faccessat), SCMP_SYS(fchmodat), SCMP_SYS(fchownat), SCMP_SYS(access),
SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(getdents), SCMP_SYS(getdents64), SCMP_SYS(chmod),
SCMP_SYS(lseek), SCMP_SYS(newfstatat), SCMP_SYS(rename), SCMP_SYS(renameat), SCMP_SYS(chown),
SCMP_SYS(renameat2), SCMP_SYS(stat), SCMP_SYS(stat64), SCMP_SYS(statfs), SCMP_SYS(chown32),
SCMP_SYS(statfs64), SCMP_SYS(unlink), SCMP_SYS(unlinkat), SCMP_SYS(faccessat),
SCMP_SYS(fchmodat),
SCMP_SYS(fchownat),
SCMP_SYS(fstat),
SCMP_SYS(fstat64),
SCMP_SYS(fstatat64),
SCMP_SYS(getdents),
SCMP_SYS(getdents64),
SCMP_SYS(lseek),
SCMP_SYS(lstat),
SCMP_SYS(lstat64),
SCMP_SYS(newfstatat),
SCMP_SYS(readlink),
SCMP_SYS(readlinkat),
SCMP_SYS(rename),
SCMP_SYS(renameat),
#ifdef __NR_renameat2
SCMP_SYS(renameat2),
#endif
SCMP_SYS(stat),
SCMP_SYS(stat64),
SCMP_SYS(statfs),
SCMP_SYS(statfs64),
#ifdef __NR_statx
SCMP_SYS(statx),
#endif
SCMP_SYS(unlink),
SCMP_SYS(unlinkat),
/* Socket */ /* Socket */
SCMP_SYS(accept), SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname), SCMP_SYS(accept),
SCMP_SYS(getsockopt), SCMP_SYS(recv), SCMP_SYS(recvfrom), SCMP_SYS(bind),
SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg), SCMP_SYS(send), SCMP_SYS(sendmmsg), SCMP_SYS(connect),
SCMP_SYS(sendmsg), SCMP_SYS(sendto), SCMP_SYS(shutdown), SCMP_SYS(getsockname),
SCMP_SYS(getsockopt),
SCMP_SYS(recv),
SCMP_SYS(recvfrom),
SCMP_SYS(recvmmsg),
#ifdef __NR_recvmmsg_time64
SCMP_SYS(recvmmsg_time64),
#endif
SCMP_SYS(recvmsg),
SCMP_SYS(send),
SCMP_SYS(sendmmsg),
SCMP_SYS(sendmsg),
SCMP_SYS(sendto),
SCMP_SYS(shutdown),
/* TODO: check socketcall arguments */ /* TODO: check socketcall arguments */
SCMP_SYS(socketcall), SCMP_SYS(socketcall),
/* General I/O */ /* General I/O */
SCMP_SYS(_newselect), SCMP_SYS(close), SCMP_SYS(open), SCMP_SYS(openat), SCMP_SYS(pipe), SCMP_SYS(_newselect),
SCMP_SYS(pipe2), SCMP_SYS(poll), SCMP_SYS(ppoll), SCMP_SYS(pselect6), SCMP_SYS(read), SCMP_SYS(close),
SCMP_SYS(futex), SCMP_SYS(select), SCMP_SYS(set_robust_list), SCMP_SYS(write), SCMP_SYS(open),
SCMP_SYS(openat),
SCMP_SYS(pipe),
SCMP_SYS(pipe2),
SCMP_SYS(poll),
SCMP_SYS(ppoll),
#ifdef __NR_ppoll_time64
SCMP_SYS(ppoll_time64),
#endif
SCMP_SYS(pread64),
SCMP_SYS(pselect6),
#ifdef __NR_pselect6_time64
SCMP_SYS(pselect6_time64),
#endif
SCMP_SYS(read),
SCMP_SYS(futex),
#ifdef __NR_futex_time64
SCMP_SYS(futex_time64),
#endif
SCMP_SYS(select),
SCMP_SYS(set_robust_list),
SCMP_SYS(write),
/* Miscellaneous */ /* Miscellaneous */
SCMP_SYS(getrandom), SCMP_SYS(sysinfo), SCMP_SYS(uname), SCMP_SYS(getrandom),
SCMP_SYS(sysinfo),
SCMP_SYS(uname),
};
const int denied_any[] = {
SCMP_SYS(execve),
#ifdef __NR_execveat
SCMP_SYS(execveat),
#endif
SCMP_SYS(fork),
SCMP_SYS(ptrace),
SCMP_SYS(vfork),
};
const int denied_ntske[] = {
SCMP_SYS(ioctl),
SCMP_SYS(setsockopt),
SCMP_SYS(socket),
}; };
const int socket_domains[] = { const int socket_domains[] = {
@@ -518,9 +627,12 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context)
}; };
const static int socket_options[][2] = { const static int socket_options[][2] = {
{ SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, { SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, { SOL_IP, IP_TOS },
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO }, { SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
#endif
#ifdef SO_BINDTODEVICE
{ SOL_SOCKET, SO_BINDTODEVICE },
#endif #endif
{ SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR }, { SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR },
#ifdef SO_REUSEPORT #ifdef SO_REUSEPORT
@@ -559,31 +671,65 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context)
#endif #endif
}; };
unsigned int default_action, deny_action;
scmp_filter_ctx *ctx; scmp_filter_ctx *ctx;
int i; int i;
/* Sign of the level determines the deny action (kill or SIGSYS).
At level 1, selected syscalls are allowed, others are denied.
At level 2, selected syscalls are denied, others are allowed. */
deny_action = level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP;
if (level < 0)
level = -level;
switch (level) {
case 1:
default_action = deny_action;
break;
case 2:
default_action = SCMP_ACT_ALLOW;
break;
default:
LOG_FATAL("Unsupported filter level");
}
if (context == SYS_MAIN_PROCESS) { if (context == SYS_MAIN_PROCESS) {
/* Check if the chronyd configuration is supported */ /* Check if the chronyd configuration is supported */
check_seccomp_applicability(); check_seccomp_applicability();
/* Start the helper process, which will run without any seccomp filter. It /* At level 1, start a helper process which will not have a seccomp filter.
will be used for getaddrinfo(), for which it's difficult to maintain a It will be used for getaddrinfo(), for which it is difficult to maintain
list of required system calls (with glibc it depends on what NSS modules a list of required system calls (with glibc it depends on what NSS
are installed and enabled on the system). */ modules are installed and enabled on the system). */
PRV_StartHelper(); if (default_action != SCMP_ACT_ALLOW)
PRV_StartHelper();
} }
ctx = seccomp_init(level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP); ctx = seccomp_init(default_action);
if (ctx == NULL) if (ctx == NULL)
LOG_FATAL("Failed to initialize seccomp"); LOG_FATAL("Failed to initialize seccomp");
/* Add system calls that are always allowed */ if (default_action != SCMP_ACT_ALLOW) {
for (i = 0; i < (sizeof (syscalls) / sizeof (*syscalls)); i++) { for (i = 0; i < sizeof (allowed) / sizeof (*allowed); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0) < 0) if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, allowed[i], 0) < 0)
goto add_failed; goto add_failed;
}
} else {
for (i = 0; i < sizeof (denied_any) / sizeof (*denied_any); i++) {
if (seccomp_rule_add(ctx, deny_action, denied_any[i], 0) < 0)
goto add_failed;
}
if (context == SYS_NTSKE_HELPER) {
for (i = 0; i < sizeof (denied_ntske) / sizeof (*denied_ntske); i++) {
if (seccomp_rule_add(ctx, deny_action, denied_ntske[i], 0) < 0)
goto add_failed;
}
}
} }
if (context == SYS_MAIN_PROCESS) { if (default_action != SCMP_ACT_ALLOW && context == SYS_MAIN_PROCESS) {
/* Allow opening sockets in selected domains */ /* Allow opening sockets in selected domains */
for (i = 0; i < sizeof (socket_domains) / sizeof (*socket_domains); i++) { for (i = 0; i < sizeof (socket_domains) / sizeof (*socket_domains); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1, if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
@@ -593,10 +739,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context)
/* Allow selected socket options */ /* Allow selected socket options */
for (i = 0; i < sizeof (socket_options) / sizeof (*socket_options); i++) { for (i = 0; i < sizeof (socket_options) / sizeof (*socket_options); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 3, if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 2,
SCMP_A1(SCMP_CMP_EQ, socket_options[i][0]), SCMP_A1(SCMP_CMP_EQ, socket_options[i][0]),
SCMP_A2(SCMP_CMP_EQ, socket_options[i][1]), SCMP_A2(SCMP_CMP_EQ, socket_options[i][1])))
SCMP_A4(SCMP_CMP_LE, sizeof (int))) < 0)
goto add_failed; goto add_failed;
} }
@@ -620,7 +765,8 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context)
if (seccomp_load(ctx) < 0) if (seccomp_load(ctx) < 0)
LOG_FATAL("Failed to load seccomp rules"); LOG_FATAL("Failed to load seccomp rules");
LOG(context == SYS_MAIN_PROCESS ? LOGS_INFO : LOGS_DEBUG, "Loaded seccomp filter"); LOG(context == SYS_MAIN_PROCESS ? LOGS_INFO : LOGS_DEBUG,
"Loaded seccomp filter (level %d)", level);
seccomp_release(ctx); seccomp_release(ctx);
return; return;

View File

@@ -33,9 +33,9 @@ extern void SYS_Linux_Initialise(void);
extern void SYS_Linux_Finalise(void); extern void SYS_Linux_Finalise(void);
extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control); extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context, int clock_control);
extern void SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context); extern void SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context);
extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor); extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);

View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2001 * Copyright (C) Richard P. Curnow 1997-2001
* Copyright (C) J. Hannken-Illjes 2001 * Copyright (C) J. Hannken-Illjes 2001
* Copyright (C) Bryan Christianson 2015, 2017 * Copyright (C) Bryan Christianson 2015, 2017, 2020
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -415,9 +415,10 @@ SYS_MacOSX_SetScheduler(int SchedPriority)
/* ================================================== */ /* ================================================== */
#ifdef FEAT_PRIVDROP #ifdef FEAT_PRIVDROP
void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid) void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context)
{ {
PRV_StartHelper(); if (context == SYS_MAIN_PROCESS)
PRV_StartHelper();
UTI_DropRoot(uid, gid); UTI_DropRoot(uid, gid);
} }

View File

@@ -30,8 +30,10 @@
#ifndef GOT_SYS_MACOSX_H #ifndef GOT_SYS_MACOSX_H
#define GOT_SYS_MACOSX_H #define GOT_SYS_MACOSX_H
#include "sys.h"
void SYS_MacOSX_SetScheduler(int SchedPriority); void SYS_MacOSX_SetScheduler(int SchedPriority);
void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid); void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context);
void SYS_MacOSX_Initialise(void); void SYS_MacOSX_Initialise(void);
void SYS_MacOSX_Finalise(void); void SYS_MacOSX_Finalise(void);

View File

@@ -131,7 +131,7 @@ SYS_NetBSD_Finalise(void)
#ifdef FEAT_PRIVDROP #ifdef FEAT_PRIVDROP
void void
SYS_NetBSD_DropRoot(uid_t uid, gid_t gid) SYS_NetBSD_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context, int clock_control)
{ {
#ifdef NETBSD #ifdef NETBSD
int fd; int fd;
@@ -139,11 +139,15 @@ SYS_NetBSD_DropRoot(uid_t uid, gid_t gid)
/* On NetBSD the helper is used only for socket binding, but on FreeBSD /* On NetBSD the helper is used only for socket binding, but on FreeBSD
it's used also for setting and adjusting the system clock */ it's used also for setting and adjusting the system clock */
PRV_StartHelper(); if (context == SYS_MAIN_PROCESS)
PRV_StartHelper();
UTI_DropRoot(uid, gid); UTI_DropRoot(uid, gid);
#ifdef NETBSD #ifdef NETBSD
if (!clock_control)
return;
/* Check if we have write access to /dev/clockctl */ /* Check if we have write access to /dev/clockctl */
fd = open("/dev/clockctl", O_WRONLY); fd = open("/dev/clockctl", O_WRONLY);
if (fd < 0) if (fd < 0)

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