Compare commits

..

152 Commits

Author SHA1 Message Date
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
105 changed files with 4475 additions and 1527 deletions

View File

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

26
NEWS
View File

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

40
README
View File

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

94
candm.h
View File

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

365
client.c
View File

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

View File

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

View File

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

17
cmac.h
View File

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

View File

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

183
cmdmon.c
View File

@@ -135,7 +135,11 @@ static const char permissions[] = {
PERMIT_AUTH, /* ONOFFLINE */
PERMIT_AUTH, /* ADD_SOURCE */
PERMIT_OPEN, /* NTP_SOURCE_NAME */
PERMIT_AUTH, /* RESET */
PERMIT_AUTH, /* RESET_SOURCES */
PERMIT_AUTH, /* AUTH_DATA */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX3 */
PERMIT_AUTH, /* SELECT_DATA */
PERMIT_AUTH, /* RELOAD_SOURCES */
};
/* ================================================== */
@@ -153,23 +157,22 @@ static void read_from_cmd_socket(int sock_fd, int event, void *anything);
static int
open_socket(int family)
{
const char *local_path, *iface;
IPSockAddr local_addr;
const char *local_path;
int sock_fd, port;
switch (family) {
case IPADDR_INET4:
case IPADDR_INET6:
port = CNF_GetCommandPort();
if (port == 0 || !SCK_IsFamilySupported(family))
if (port == 0 || !SCK_IsIpFamilyEnabled(family))
return INVALID_SOCK_FD;
CNF_GetBindCommandAddress(family, &local_addr.ip_addr);
if (local_addr.ip_addr.family != family)
SCK_GetLoopbackIPAddress(family, &local_addr.ip_addr);
local_addr.port = port;
iface = CNF_GetBindCommandInterface();
sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, SCK_FLAG_RX_DEST_ADDR);
sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, iface, SCK_FLAG_RX_DEST_ADDR);
if (sock_fd < 0) {
LOG(LOGS_ERR, "Could not open command socket on %s",
UTI_IPSockAddrToString(&local_addr));
@@ -233,22 +236,17 @@ do_size_checks(void)
/* ================================================== */
void
CAM_Initialise(int family)
CAM_Initialise(void)
{
assert(!initialised);
assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
do_size_checks();
initialised = 1;
sock_fdu = INVALID_SOCK_FD;
sock_fd4 = INVALID_SOCK_FD;
sock_fd6 = INVALID_SOCK_FD;
if (family == IPADDR_UNSPEC || family == IPADDR_INET4)
sock_fd4 = open_socket(IPADDR_INET4);
if (family == IPADDR_UNSPEC || family == IPADDR_INET6)
sock_fd6 = open_socket(IPADDR_INET6);
sock_fd4 = open_socket(IPADDR_INET4);
sock_fd6 = open_socket(IPADDR_INET6);
access_auth_table = ADF_CreateTable();
}
@@ -289,7 +287,7 @@ CAM_OpenUnixSocket(void)
{
/* This is separated from CAM_Initialise() as it needs to be called when
the process has already dropped the root privileges */
if (CNF_GetBindCommandPath()[0])
if (CNF_GetBindCommandPath())
sock_fdu = open_socket(IPADDR_UNSPEC);
}
@@ -300,6 +298,11 @@ transmit_reply(int sock_fd, SCK_Message *message)
{
message->length = PKL_ReplyLength((CMD_Reply *)message->data);
/* Don't require responses to non-link-local addresses to use the same
interface */
if (!SCK_IsLinkLocalIPAddress(&message->remote_addr.ip.ip_addr))
message->if_index = INVALID_IF_INDEX;
if (!SCK_SendMessage(sock_fd, message, 0))
return;
}
@@ -599,11 +602,7 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.source_data.mode = htons(RPY_SD_MD_REF);
break;
}
tx_message->data.source_data.flags =
htons((report.sel_options & SRC_SELECT_PREFER ? RPY_SD_FLAG_PREFER : 0) |
(report.sel_options & SRC_SELECT_NOSELECT ? RPY_SD_FLAG_NOSELECT : 0) |
(report.sel_options & SRC_SELECT_TRUST ? RPY_SD_FLAG_TRUST : 0) |
(report.sel_options & SRC_SELECT_REQUIRE ? RPY_SD_FLAG_REQUIRE : 0));
tx_message->data.source_data.flags = htons(0);
tx_message->data.source_data.reachability = htons(report.reachability);
tx_message->data.source_data.since_sample = htonl(report.latest_meas_ago);
tx_message->data.source_data.orig_latest_meas = UTI_FloatHostToNetwork(report.orig_latest_meas);
@@ -716,7 +715,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
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.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll);
@@ -751,7 +750,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
status = NSR_AddSourceByName(name, port, pool, type, &params);
status = NSR_AddSourceByName(name, port, pool, type, &params, NULL);
switch (status) {
case NSR_Success:
break;
@@ -780,13 +779,12 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message)
{
NTP_Remote_Address rem_addr;
NSR_Status status;
IPAddr ip_addr;
UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &rem_addr.ip_addr);
rem_addr.port = 0;
UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &ip_addr);
status = NSR_RemoveSource(&rem_addr);
status = NSR_RemoveSource(&ip_addr);
switch (status) {
case NSR_Success:
break;
@@ -1000,7 +998,7 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ClientAccessByIndex_Report report;
RPY_ClientAccesses_Client *client;
int n_indices;
uint32_t i, j, req_first_index, req_n_clients;
uint32_t i, j, req_first_index, req_n_clients, req_min_hits, req_reset;
struct timespec now;
SCH_GetLastEventTime(&now, NULL, NULL);
@@ -1009,6 +1007,8 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
req_n_clients = ntohl(rx_message->data.client_accesses_by_index.n_clients);
if (req_n_clients > MAX_CLIENT_ACCESSES)
req_n_clients = MAX_CLIENT_ACCESSES;
req_min_hits = ntohl(rx_message->data.client_accesses_by_index.min_hits);
req_reset = ntohl(rx_message->data.client_accesses_by_index.reset);
n_indices = CLG_GetNumberOfIndices();
if (n_indices < 0) {
@@ -1016,24 +1016,28 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
return;
}
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX2);
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX3);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices);
for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) {
if (!CLG_GetClientAccessReportByIndex(i, &report, &now))
if (!CLG_GetClientAccessReportByIndex(i, req_reset, req_min_hits, &report, &now))
continue;
client = &tx_message->data.client_accesses_by_index.clients[j++];
UTI_IPHostToNetwork(&report.ip_addr, &client->ip);
client->ntp_hits = htonl(report.ntp_hits);
client->nke_hits = htonl(report.nke_hits);
client->cmd_hits = htonl(report.cmd_hits);
client->ntp_drops = htonl(report.ntp_drops);
client->nke_drops = htonl(report.nke_drops);
client->cmd_drops = htonl(report.cmd_drops);
client->ntp_interval = report.ntp_interval;
client->nke_interval = report.nke_interval;
client->cmd_interval = report.cmd_interval;
client->ntp_timeout_interval = report.ntp_timeout_interval;
client->last_ntp_hit_ago = htonl(report.last_ntp_hit_ago);
client->last_nke_hit_ago = htonl(report.last_nke_hit_ago);
client->last_cmd_hit_ago = htonl(report.last_cmd_hit_ago);
}
@@ -1135,12 +1139,15 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ServerStatsReport report;
CLG_GetServerStatsReport(&report);
tx_message->reply = htons(RPY_SERVER_STATS);
tx_message->reply = htons(RPY_SERVER_STATS2);
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.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.log_drops = htonl(report.log_drops);
tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits);
}
/* ================================================== */
@@ -1204,7 +1211,7 @@ handle_ntp_source_name(CMD_Request *rx_message, CMD_Reply *tx_message)
IPAddr addr;
char *name;
UTI_IPNetworkToHost(&rx_message->data.ntp_data.ip_addr, &addr);
UTI_IPNetworkToHost(&rx_message->data.ntp_source_name.ip_addr, &addr);
name = NSR_GetName(&addr);
if (!name) {
@@ -1226,7 +1233,15 @@ handle_ntp_source_name(CMD_Request *rx_message, CMD_Reply *tx_message)
/* ================================================== */
static void
handle_reset(CMD_Request *rx_message, CMD_Reply *tx_message)
handle_reload_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
{
CNF_ReloadSources();
}
/* ================================================== */
static void
handle_reset_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
{
struct timespec cooked_now, now;
@@ -1235,6 +1250,84 @@ handle_reset(CMD_Request *rx_message, CMD_Reply *tx_message)
LCL_NotifyExternalTimeStep(&now, &cooked_now, 0.0, 0.0);
}
/* ================================================== */
static void
handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_AuthReport report;
IPAddr ip_addr;
UTI_IPNetworkToHost(&rx_message->data.auth_data.ip_addr, &ip_addr);
if (!NSR_GetAuthReport(&ip_addr, &report)) {
tx_message->status = htons(STT_NOSUCHSOURCE);
return;
}
tx_message->reply = htons(RPY_AUTH_DATA);
switch (report.mode) {
case NTP_AUTH_NONE:
tx_message->data.auth_data.mode = htons(RPY_AD_MD_NONE);
break;
case NTP_AUTH_SYMMETRIC:
tx_message->data.auth_data.mode = htons(RPY_AD_MD_SYMMETRIC);
break;
case NTP_AUTH_NTS:
tx_message->data.auth_data.mode = htons(RPY_AD_MD_NTS);
break;
default:
break;
}
tx_message->data.auth_data.key_type = htons(report.key_type);
tx_message->data.auth_data.key_id = htonl(report.key_id);
tx_message->data.auth_data.key_length = htons(report.key_length);
tx_message->data.auth_data.ke_attempts = htons(report.ke_attempts);
tx_message->data.auth_data.last_ke_ago = htonl(report.last_ke_ago);
tx_message->data.auth_data.cookies = htons(report.cookies);
tx_message->data.auth_data.cookie_length = htons(report.cookie_length);
tx_message->data.auth_data.nak = htons(report.nak);
}
/* ================================================== */
static uint16_t
convert_select_options(int options)
{
return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
(options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
(options & SRC_SELECT_TRUST ? RPY_SD_OPTION_TRUST : 0) |
(options & SRC_SELECT_REQUIRE ? RPY_SD_OPTION_REQUIRE : 0);
}
/* ================================================== */
static void
handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_SelectReport report;
if (!SRC_GetSelectReport(ntohl(rx_message->data.select_data.index), &report)) {
tx_message->status = htons(STT_NOSUCHSOURCE);
return;
}
tx_message->reply = htons(RPY_SELECT_DATA);
tx_message->data.select_data.ref_id = htonl(report.ref_id);
UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.select_data.ip_addr);
tx_message->data.select_data.state_char = report.state_char;
tx_message->data.select_data.authentication = report.authentication;
tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options));
tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options));
tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);
tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score);
tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit);
}
/* ================================================== */
/* Read a packet and process it */
@@ -1247,7 +1340,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
IPAddr loopback_addr, remote_ip;
int read_length, expected_length;
int localhost, allowed, log_index;
unsigned short rx_command;
uint16_t rx_command;
struct timespec now, cooked_now;
sck_message = SCK_ReceiveMessage(sock_fd, 0);
@@ -1305,11 +1398,11 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
return;
}
log_index = CLG_LogCommandAccess(&remote_ip, &cooked_now);
log_index = CLG_LogServiceAccess(CLG_CMDMON, &remote_ip, &cooked_now);
/* Don't reply to all requests from hosts other than localhost if the rate
is excessive */
if (!localhost && log_index >= 0 && CLG_LimitCommandResponseRate(log_index)) {
if (!localhost && log_index >= 0 && CLG_LimitServiceRate(CLG_CMDMON, log_index)) {
DEBUG_LOG("Command packet discarded to limit response rate");
return;
}
@@ -1553,7 +1646,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_cyclelogs(&rx_message, &tx_message);
break;
case REQ_CLIENT_ACCESSES_BY_INDEX2:
case REQ_CLIENT_ACCESSES_BY_INDEX3:
handle_client_accesses_by_index(&rx_message, &tx_message);
break;
@@ -1613,8 +1706,20 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_ntp_source_name(&rx_message, &tx_message);
break;
case REQ_RESET:
handle_reset(&rx_message, &tx_message);
case REQ_RESET_SOURCES:
handle_reset_sources(&rx_message, &tx_message);
break;
case REQ_AUTH_DATA:
handle_auth_data(&rx_message, &tx_message);
break;
case REQ_SELECT_DATA:
handle_select_data(&rx_message, &tx_message);
break;
case REQ_RELOAD_SOURCES:
handle_reload_sources(&rx_message, &tx_message);
break;
default:

View File

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

View File

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

View File

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

478
conf.c
View File

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

11
conf.h
View File

@@ -30,10 +30,13 @@
#include "addressing.h"
#include "reference.h"
#include "sources.h"
extern void CNF_Initialise(int restarted, int client_only);
extern void CNF_Finalise(void);
extern void CNF_EnablePrint(void);
extern char *CNF_GetRtcDevice(void);
extern void CNF_ReadFile(const char *filename);
@@ -46,6 +49,8 @@ extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void);
extern void CNF_AddRefclocks(void);
extern void CNF_ReloadSources(void);
extern int CNF_GetAcquisitionPort(void);
extern int CNF_GetNTPPort(void);
extern char *CNF_GetDriftFile(void);
@@ -74,7 +79,11 @@ extern void CNF_GetFallbackDrifts(int *min, int *max);
extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
extern char *CNF_GetBindNtpInterface(void);
extern char *CNF_GetBindAcquisitionInterface(void);
extern char *CNF_GetBindCommandInterface(void);
extern char *CNF_GetBindCommandPath(void);
extern int CNF_GetNtpDscp(void);
extern char *CNF_GetNtpSigndSocket(void);
extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
@@ -87,6 +96,7 @@ extern double CNF_GetMaxDrift(void);
extern double CNF_GetCorrectionTimeRatio(void);
extern double CNF_GetMaxSlewRate(void);
extern SRC_AuthSelectMode CNF_GetAuthSelectMode(void);
extern double CNF_GetMaxDistance(void);
extern double CNF_GetMaxJitter(void);
extern double CNF_GetReselectDistance(void);
@@ -101,6 +111,7 @@ extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void);
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 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);

43
configure vendored
View File

@@ -657,6 +657,7 @@ if [ $feat_ipv6 = "1" ] && \
struct sockaddr_in6 n;
char p[100];
n.sin6_addr = in6addr_any;
n.sin6_scope_id = 0;
return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));'
then
add_def FEAT_IPV6
@@ -939,7 +940,7 @@ fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then
test_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3"
test_link="`pkg_config --libs-only-L nss` -lfreebl3 -lnssutil3"
if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \
"$test_cflags" "$test_link" \
'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));'
@@ -966,31 +967,45 @@ EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
LIBS="$LIBS $HASH_LINK"
if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ] && \
echo "$HASH_LINK" | grep 'nettle' > /dev/null; then
if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/gnutls.h' \
"$test_cflags" "$test_link" '
return gnutls_init(NULL, 0) +
gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);' &&
test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
'aes128_set_encrypt_key(NULL, NULL);'
gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, 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"
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' \
'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key(NULL, NULL);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC
else
if test_code 'SIV in gnutls' 'gnutls/gnutls.h' \
"$test_cflags" "$test_link" '
return gnutls_aead_cipher_init(NULL, GNUTLS_CIPHER_AES_128_SIV, NULL);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
add_def HAVE_SIV
else
if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
'aes128_set_encrypt_key(NULL, NULL);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
fi
fi
fi
if grep '#define HAVE_SIV' config.h > /dev/null; then
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
LIBS="$LIBS $test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_NTS
fi
fi
fi

View File

@@ -82,13 +82,13 @@ interval should stay at or below 9 (512 seconds). The default is 10 (1024
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
months).
*iburst*:::
With this option, the interval between the first four requests sent to the
server will be 2 seconds or less instead of the interval specified by the
*minpoll* option, which allows *chronyd* to make the first update of the clock
shortly after start.
With this option, *chronyd* will start with a burst of 4-8 requests in order to
make the first update of the clock sooner. It will also repeat the burst every
time the source is switched from the offline state to online with the
<<chronyc.adoc#online,*online*>> command in *chronyc*.
*burst*:::
With this option, *chronyd* will shorten the interval between up to four
requests to 2 seconds or less when it cannot get a good measurement from the
With this option, *chronyd* will send a burst of up to 4 requests when it
cannot get a good measurement from the
server. The number of requests in the burst is limited by the current polling
interval to keep the average interval at or above the minimum interval, i.e.
the current interval needs to be at least two times longer than the minimum
@@ -200,10 +200,11 @@ authenticated source to be safely combined with unauthenticated sources in
order to improve the accuracy of the clock. They can be selected and used for
synchronisation only if they agree with the trusted and required source.
*xleave*:::
This option enables an interleaved mode which enables the server to
send transmit timestamps captured after the actual transmission (e.g. when the
server is running *chronyd* with software (kernel) or hardware
timestamping). This can significantly improve the accuracy of the measurements.
This option enables the interleaved mode of NTP. It enables the server to
respond with more accurate transmit timestamps (e.g. kernel or hardware
timestamps), which cannot be contained in the transmitted packet itself and
need to refer to a previous packet instead. This can significantly improve the
accuracy and stability of the measurements.
+
The interleaved mode is compatible with servers that support only the basic
mode. Note that even
@@ -228,7 +229,7 @@ be specified. For normal servers this option should not be required (the
default is 123, the standard NTP port).
*ntsport* _port_:::
This option specifies the TCP port on which the server is listening for NTS-KE
connections when the *nts* option is enabled. The default is 11443.
connections when the *nts* option is enabled. The default is 4460.
*presend* _poll_:::
If the timing measurements being made by *chronyd* are the only network data
passing between two computers, you might find that some measurements are badly
@@ -284,7 +285,7 @@ This option sets the desired number of sources to be used from the pool.
sources responding to requests. The default value is 4 and the maximum value is
16.
+
::
{blank}::
When an NTP source is unreachable,
marked as a falseticker, or has a distance larger than the limit set by the
<<maxdistance,*maxdistance*>> directive, *chronyd* will try to replace the
@@ -412,7 +413,7 @@ By default, the PPS refclock uses assert events (rising edge) for
synchronisation. With this option, it will use clear events (falling edge)
instead.
+
:::
{blank}:::
Examples:
+
----
@@ -431,7 +432,7 @@ supports the following option:
This option specifies the permissions of the shared memory segment created by
*chronyd*. They are specified as a numeric mode. The default value is 0600
(read-write access for owner only).
:::
{blank}:::
+
Examples:
+
@@ -485,7 +486,7 @@ value is 0.
This option enables timestamping of clear events (falling edge) instead of
assert events (rising edge) in the PPS mode. This may not work with some
clocks.
:::
{blank}:::
+
Examples:
+
@@ -495,7 +496,7 @@ refclock PHC /dev/ptp1:nocrossts poll 3 pps
refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2
----
+
::
{blank}::
The *refclock* directive supports the following options:
+
*poll* _poll_:::
@@ -630,7 +631,7 @@ This would change the source port used for client requests to UDP port 1123.
You could then persuade the firewall administrator to open that port.
[[bindacqaddress]]*bindacqaddress* _address_::
The *bindacqaddress* directive sets the network interface to which
The *bindacqaddress* directive specifies a local IP address to which
*chronyd* will bind its NTP client sockets. The syntax is similar to the
<<bindaddress,*bindaddress*>> and <<bindcmdaddress,*bindcmdaddress*>>
directives.
@@ -638,6 +639,32 @@ directives.
For each of the IPv4 and IPv6 protocols, only one *bindacqaddress* directive
can be specified.
[[bindacqdevice]]*bindacqdevice* _interface_::
The *bindacqdevice* directive binds the client sockets to a network device
specified by the interface name. This can be useful when the local address is
dynamic, or to enable an NTP source specified with a link-local IPv6 address.
This directive can specify only one interface and it is supported on Linux
only.
+
An example of the directive is:
+
----
bindacqdevice eth0
----
[[dscp]]*dscp* _point_::
The *dscp* directive sets the Differentiated Services Code Point (DSCP) in
transmitted NTP packets to the specified value. It can improve stability of NTP
measurements in local networks where switches or routers are configured to
prioritise forwarding of packets with specific DSCP values. The default value
is 0 and the maximum value is 63.
+
An example of the directive (setting the Expedited Forwarding class) is:
+
----
dscp 46
----
[[dumpdir]]*dumpdir* _directory_::
To compute the rate of gain or loss of time, *chronyd* has to store a
measurement history for each of the time sources it uses.
@@ -655,6 +682,8 @@ behaviour whilst it is not running). The *dumpdir* directive defines the
directory where the measurement histories are saved when *chronyd* exits,
or the <<chronyc.adoc#dump,*dump*>> command in *chronyc* is issued.
+
If the directory does not exist, it will be created automatically.
+
An example of the directive is:
+
----
@@ -696,6 +725,8 @@ received from the server in order to avoid making an NTS-KE request when
source in files named by the IP address of the NTS-KE server (e.g.
_1.2.3.4.nts_). By default, the client does not save the cookies.
+
If the directory does not exist, it will be created automatically.
+
An example of the directive is:
+
----
@@ -726,7 +757,7 @@ authentication mechanism to be used on computers which start with wrong time
has important security implications, e.g. if an NTP server was ever
compromised, its certificate could be used in an attack after the expiration
time. The default value is 0, which means the time checks are always enabled.
+
An example of the directive is:
+
----
@@ -739,6 +770,78 @@ with correct time.
=== Source selection
[[authselectmode]]*authselectmode* _mode_::
NTP sources can be specified with the *key* or *nts* option to enable
authentication to limit the impact of man-in-the-middle attacks. The
attackers can drop or delay NTP packets (up to the *maxdelay* and
<<maxdistance,*maxdistance*>> limits), but they cannot modify the timestamps
contained in the packets. The attack can cause only a limited slew or step, and
also cause the clock to run faster or slower than real time (up to double of
the <<maxdrift,*maxdrift*>> limit).
+
When authentication is enabled for an NTP source, it is important to disable
unauthenticated NTP sources which could be exploited in the attack, e.g. if
they are not reachable only over a trusted network. Alternatively, the source
selection can be configured with the *require* and *trust* options to
synchronise to the unauthenticated sources only if they agree with the
authenticated sources and might have a positive impact on the accuracy of the
clock. Note that in this case the impact of the attack is higher. The attackers
cannot cause an arbitrarily large step or slew, but they have more control over
the frequency of the clock and can cause *chronyd* to report false information,
e.g. a significantly smaller root delay and dispersion.
+
This directive determines the default selection options for authenticated and
unauthenticated sources in order to simplify the configuration with the
configuration file and *chronyc* commands. It sets a policy for authentication.
+
Sources specified with the *noselect* option are ignored (not counted as either
authenticated or unauthenticated), and they always have only the selection
options specified in the configuration.
+
There are four modes:
+
*require*:::
Authentication is strictly required for NTP sources in this mode. If any
unauthenticated NTP sources are specified, they will automatically get the
*noselect* option to prevent them from being selected for synchronisation.
*prefer*:::
In this mode, authentication is optional and preferred. If it is enabled for at
least one NTP source, all unauthenticated NTP sources will get the *noselect*
option.
*mix*:::
In this mode, authentication is optional and synchronisation to a mix of
authenticated and unauthenticated NTP sources is allowed. If both authenticated
and unauthenticated NTP sources are specified, all authenticated NTP sources
and reference clocks will get the *require* and *trust* options to prevent
synchronisation to unauthenticated NTP sources if they do not agree with a
majority of the authenticated sources and reference clocks. This is the default
mode.
*ignore*:::
In this mode, authentication is ignored in the source selection. All sources
will have only the selection options that were specified in the configuration
file, or *chronyc* command. This was the behaviour of *chronyd* in versions
before 4.0.
{blank}::
+
As an example, the following configuration using the default *mix* mode:
+
----
server foo.example.net nts
server bar.example.net nts
server baz.example.net
refclock SHM 0
----
+
is equivalent to the following configuration using the *ignore* mode:
+
----
authselectmode ignore
server foo.example.net nts require trust
server bar.example.net nts require trust
server baz.example.net
refclock SHM 0 require trust
----
[[combinelimit]]*combinelimit* _limit_::
When *chronyd* has multiple sources available for synchronisation, it has to
select one source as the synchronisation source. The measured offsets and
@@ -913,7 +1016,7 @@ clock will be off for a longer time. On Linux with the default
No correction is applied to the clock for the leap second. The clock will be
corrected later in normal operation when new measurements are made and the
estimated offset includes the one second error.
::
{blank}::
+
When serving time to NTP clients that cannot be configured to correct their
clocks for a leap second by slewing, or to clients that would correct at
@@ -1264,6 +1367,17 @@ Currently, for each of the IPv4 and IPv6 protocols, only one *bindaddress*
directive can be specified. Therefore, it is not useful on computers which
should serve NTP on multiple network interfaces.
[[binddevice]]*binddevice* _interface_::
The *binddevice* directive binds the NTP server sockets to a network device
specified by the interface name. This directive can specify only one interface
and it is supported on Linux only.
+
An example of the directive is:
+
----
binddevice eth0
----
[[broadcast]]*broadcast* _interval_ _address_ [_port_]::
The *broadcast* directive is used to declare a broadcast address to which
chronyd should send packets in the NTP broadcast mode (i.e. make *chronyd* act
@@ -1373,7 +1487,7 @@ synchronised to it. When that server fails, another will take over.
+
The *orphan* mode is compatible with the *ntpd*'s orphan mode (enabled by the
*tos orphan* command).
::
{blank}::
+
An example of the directive is:
+
@@ -1400,7 +1514,7 @@ ntpsigndsocket /var/lib/samba/ntp_signd
[[ntsport]]*ntsport* _port_::
This directive specifies the TCP port on which *chronyd* will provide the NTS
Key Establishment (NTS-KE) service. The default port is 11443.
Key Establishment (NTS-KE) service. The default port is 4460.
+
The port will be open only when a certificate and key is specified by the
*ntsservercert* and *ntsserverkey* directives.
@@ -1516,7 +1630,7 @@ source address from completely blocking responses to that address. The leak
rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at
least every fourth request has a response. The minimum value is 1 and the
maximum value is 4.
::
{blank}::
+
An example use of the directive is:
+
@@ -1528,6 +1642,17 @@ This would reduce the response rate for IP addresses sending packets on average
more than once per 2 seconds, or sending packets in bursts of more than 16
packets, by up to 75% (with default *leak* of 2).
[[ntsratelimit]]*ntsratelimit* [_option_]...::
This directive enables rate limiting of NTS-KE requests. It is similar to the
<<ratelimit,*ratelimit*>> directive, except the default interval is 6
(1 connection per 64 seconds).
+
An example of the use of the directive is:
+
----
ntsratelimit interval 3 burst 1
----
[[smoothtime]]*smoothtime* _max-freq_ _max-wander_ [*leaponly*]::
The *smoothtime* directive can be used to enable smoothing of the time that
*chronyd* serves to its clients to make it easier for them to track it and keep
@@ -1581,8 +1706,8 @@ smoothtime 50000 0.01
=== Command and monitoring access
[[bindcmdaddress]]*bindcmdaddress* _address_::
The *bindcmdaddress* directive allows you to specify an IP address of an
interface on which *chronyd* will listen for monitoring command packets (issued
The *bindcmdaddress* directive specifies a local IP address to which *chronyd*
will bind the UDP socket listening for monitoring command packets (issued
by *chronyc*). On systems other than Linux, the address of the interface needs
to be already configured when *chronyd* is started.
+
@@ -1593,9 +1718,10 @@ directory will be created on start if it does not exist. The compiled-in default
path of the socket is _@CHRONYRUNDIR@/chronyd.sock_. The socket can be
disabled by setting the path to _/_.
+
By default, *chronyd* binds to the loopback interface (with addresses
_127.0.0.1_ and _::1_). This blocks all access except from localhost. To listen
for command packets on all interfaces, you can add the lines:
By default, *chronyd* binds the UDP sockets to the addresses _127.0.0.1_ and
_::1_ (i.e. the loopback interface). This blocks all access except from
localhost. To listen for command packets on all interfaces, you can add the
lines:
+
----
bindcmdaddress 0.0.0.0
@@ -1613,6 +1739,17 @@ An example that sets the path of the Unix domain command socket is:
bindcmdaddress /var/run/chrony/chronyd.sock
----
[[bindcmddevice]]*bindcmddevice* _interface_::
The *bindcmddevice* directive binds the UDP command sockets to a network device
specified by the interface name. This directive can specify only one interface
and it is supported on Linux only.
+
An example of the directive is:
+
----
bindcmddevice eth0
----
[[cmdallow]]*cmdallow* [*all*] [_subnet_]::
This is similar to the <<allow,*allow*>> directive, except that it allows
monitoring access (rather than NTP client access) to a particular subnet or
@@ -1991,7 +2128,7 @@ from the example line above):
. Applied compensation in ppm, positive means the system clock is running
faster than it would be without the compensation. [3.6600e-01]
+
::
{blank}::
An example of the directive is:
+
----
@@ -2024,8 +2161,9 @@ which would cause a syslog message to be generated if a system clock error of ov
0.1 seconds starts to be compensated.
[[logdir]]*logdir* _directory_::
This directive allows the directory where log files are written to be
specified.
This directive specifies the directory for writing log files enabled by the
*log* directive. If the directory does not exist, it will be created
automatically.
+
An example of the use of this directive is:
+
@@ -2053,6 +2191,50 @@ sendmail binary.
=== Miscellaneous
[[confdir]]*confdir* _directory_...::
The *confdir* directive includes configuration files with the _.conf_ suffix
from a directory. The files are included in the lexicographical order of the
file names.
+
Multiple directories (up to 10) can be specified with a single *confdir*
directive. In this case, if multiple directories contain a file with the same
name, only the first file in the order of the specified directories will be
included. This enables a fragmented configuration where existing fragments can
be replaced by adding files to a different directory.
+
An example of the directive is:
+
----
confdir @SYSCONFDIR@/chrony.d
----
[[sourcedir]]*sourcedir* _directory_...::
The *sourcedir* directive is identical to the *confdir* directive, except the
configuration files have the _.sources_ suffix, they can only specify NTP
sources (i.e. use the *server*, *pool*, and *peer* directive), and can be
reloaded by the <<chronyc.adoc#reload,*reload sources*>> command in
*chronyc*. It is particularly useful with dynamic sources like NTP servers
received from a DHCP server, which can be written to a file specific to the
network interface by a networking script.
+
An example of the directive is:
+
----
sourcedir /var/run/chrony-dhcp
----
[[include]]*include* _pattern_::
The *include* directive includes a configuration file, or multiple configuration
files if a wildcard pattern is specified. Unlike with the *confdir* directive,
the full name of the files needs to be specified and at least one file is
required to exist.
+
An example of the directive is:
+
----
include @SYSCONFDIR@/chrony.d/*.conf
----
[[hwtimestamp]]*hwtimestamp* _interface_ [_option_]...::
This directive enables hardware timestamping of NTP packets sent to and
received from the specified network interface. The network interface controller
@@ -2123,14 +2305,14 @@ _ntp_::::
Enables timestamping of received NTP packets.
_none_::::
Disables timestamping of received packets.
:::
{blank}:::
The most specific filter for timestamping NTP packets which is supported by the
NIC is selected by default. Some NICs can timestamp only PTP packets, which
limits the selection to the _none_ filter. Forcing timestamping of all packets
with the _all_ filter when the NIC supports both _all_ and _ntp_ filters can be
useful when packets are received from or on a non-standard UDP port (e.g.
specified by the *port* directive).
::
{blank}::
+
Examples of the directive are:
+
@@ -2140,17 +2322,6 @@ hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
hwtimestamp *
----
[[include]]*include* _pattern_::
The *include* directive includes a configuration file or multiple configuration
files if a wildcard pattern is specified. This can be useful when maintaining
configuration on multiple hosts to keep the differences in separate files.
+
An example of the directive is:
+
----
include @SYSCONFDIR@/chrony.d/*.conf
----
[[keyfile]]*keyfile* _file_::
This directive is used to specify the location of the file containing symmetric
keys which are shared between NTP servers and clients, or peers, in order to

View File

@@ -414,6 +414,92 @@ This is the estimated offset of the source.
*Std Dev*:::
This is the estimated sample standard deviation.
[[selectdata]]*selectdata* [*-a*] [*-v*]::
The *selectdata* command displays information specific to the selection of time
sources. If the *-a* option is specified, all sources are displayed, including
those that do not have a known address yet. With the *-v* option, extra caption
lines are shown as a reminder of the meanings of the columns.
+
An example of the output is shown below.
+
----
S Name/IP Address Auth COpts EOpts Last Score Interval
====================================================================
D foo.example.net Y ----- --TR- 4 1.0 -61ms +62ms
* bar.example.net N ----- ----- 0 1.0 -6846us +7305us
+ baz.example.net N ----- ----- 10 1.0 -7381us +7355us
----
+
The columns are as follows:
+
*S*:::
This column indicates the state of the source after the last source selection.
It is similar to the state reported by the *sources* command, but more
states are reported.
{blank}:::
The following states indicate the source is not considered selectable for
synchronisation:
* _N_ - has the *noselect* option.
* _M_ - does not have enough measurements.
* _d_ - has a root distance larger than the maximum distance (configured by the
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
* _~_ - has a jitter larger than the maximum jitter (configured by the
<<chrony.conf.adoc#maxjitter,*maxjitter*>> directive).
* _w_ - waits for other sources to get out of the _M_ state.
* _S_ - has older measurements than other sources.
* _O_ - has a stratum equal or larger than the orphan stratum (configured by
the <<chrony.conf.adoc#local,*local*>> directive).
* _T_ - does not fully agree with sources that have the *trust* option.
* _x_ - does not agree with other sources (falseticker).
{blank}:::
The following states indicate the source is considered selectable, but it is
not currently used for synchronisation:
* _W_ - waits for other sources to be selectable (required by the
<<chrony.conf.adoc#minsources,*minsources*>> directive, or
the *require* option of another source).
* _P_ - another selectable source is preferred due to the *prefer* option.
* _U_ - waits for a new measurement (after selecting a different best source).
* _D_ - has, or recently had, a root distance which is too large to be combined
with other sources (configured by the
<<chrony.conf.adoc#combinelimit,*combinelimit*>> directive).
{blank}:::
The following states indicate the source is used for synchronisation of the
local clock:
* _+_ - combined with the best source.
* _*_ - selected as the best source to update the reference data (e.g. root
delay, root dispersion).
*Name/IP address*:::
This column shows the name or IP address of the source if it is an NTP server,
or the reference ID if it is a reference clock.
*Auth*:::
This column indicites whether an authentication mechanism is enabled for the
source. _Y_ means yes and _N_ means no.
*COpts*:::
This column displays the configured selection options of the source.
* _N_ indicates the *noselect* option.
* _P_ indicates the *prefer* option.
* _T_ indicates the *trust* option.
* _R_ indicates the *require* option.
*EOpts*:::
This column displays the current effective selection options of the source,
which can be different from the configured options due to the authentication
selection mode (configured by the
<<chrony.conf.adoc#authselmode,*authselmode*>> directive). The symbols are the
same as in the *COpts* column.
*Last*:::
This column displays how long ago was the last measurement of the source made
when the selection was performed.
*Score*:::
This column displays the current score against the source in the _*_ state. The
scoring system avoids frequent reselection when multiple sources have a similar
root distance. A value larger than 1 indicates this source was better than the
_*_ source in recent selections. If the score reaches 10, the best source will
be reselected and the scores will be reset to 1.
*Interval*:::
This column displays the lower and upper endpoint of the interval which was
expected to contain the true offset of the local clock considering the root
distance at the time of the selection.
[[reselect]]*reselect*::
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
@@ -454,6 +540,77 @@ the offline state.
the name of the server or peer was not resolved to an address yet; this source is
not visible in the *sources* and *sourcestats* reports.
[[authdata]]*authdata* [*-a*]::
The *authdata* command displays information specific to authentication of NTP
sources. If the *-a* option is specified, all sources are displayed, including
those that do not have a known address yet. An example of the output is
shown below.
+
----
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
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_]::
The *ntpdata* command displays the last valid measurement and other
NTP-specific information about the specified NTP source, or all NTP sources
@@ -626,7 +783,7 @@ alternative to the form with mask.
_address_:::
This is an IP address or a hostname. The burst command is applied only to
that source.
::
{blank}::
+
If no _mask_ or _masked-address_ arguments are provided, every source will be
matched.
@@ -771,6 +928,11 @@ Sources that stop responding will be replaced with newly resolved addresses
automatically after 8 polling intervals, but this command can still be useful
to replace them immediately and not wait until they are marked as unreachable.
[[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_::
The *sourcename* command prints the original hostname or address that was
specified for an NTP source in the configuration file, or the *add* command.
@@ -816,7 +978,7 @@ The columns are as as follows:
. The regression residual at this point, in seconds. This allows '`outliers`'
to be easily spotted, so that they can be deleted using the *manual delete*
command.
::
{blank}::
+
The *delete* form of the command deletes a single sample. The parameter is the
index of the sample, as shown in the first column of the output from *manual
@@ -887,10 +1049,17 @@ This command can be used to examine the effect of a series of *allow*, *allow
all*, *deny*, and *deny all* commands specified either via *chronyc*, or in
*chronyd*'s configuration file.
[[clients]]*clients*::
[[clients]]*clients* [*-p* _packets_] [*-k*] [*-r*]::
This command shows a list of clients that have accessed the server, through
either the NTP or command ports. It does not include accesses over
the Unix domain command socket. There are no arguments.
the NTP, command, or NTS-KE port. It does not include accesses over the Unix
domain command socket.
+
The *-p* option specifies the minimum number of received NTP or command
packets, or accepted NTS-KE connections, needed to include a client in the
list. The default value is 0, i.e. all clients are reported. With the *-k*
option the last four columns will show the NTS-KE accesses instead of command
accesses. If the *-r* option is specified, *chronyd* will reset the counters of
received and dropped packets or connections after reporting the current values.
+
An example of the output is:
+
@@ -915,20 +1084,22 @@ The columns are as follows:
. The average interval between NTP packets.
. The average interval between NTP packets after limiting the response rate.
. Time since the last NTP packet was received
. The number of command packets received from the client.
. The number of command packets dropped to limit the response rate.
. The average interval between command packets.
. Time since the last command packet was received.
. The number of command packets or NTS-KE connections received/accepted from
the client.
. The number of command packets or NTS-KE connections dropped to limit the
response rate.
. The average interval between command packets or NTS-KE connections.
. Time since the last command packet or NTS-KE connection was
received/accepted.
[[serverstats]]*serverstats*::
The *serverstats* command displays how many valid NTP and command requests
*chronyd* as a server received from clients, how many of them were dropped to
limit the response rate as configured by the
<<chrony.conf.adoc#ratelimit,*ratelimit*>> and
<<chrony.conf.adoc#cmdratelimit,*cmdratelimit*>> directives, and how many
The *serverstats* command displays how many valid NTP and command requests, and
NTS-KE connections, *chronyd* operating as a server received from clients, and
how many of them were dropped due to rate limiting. It also displays 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.
<<chrony.conf.adoc#clientloglimit,*clientloglimit*>> directive and how many of
the NTP requests (from those which were not dropped) were authenticated. An
example of the output is shown below.
+
----
NTP packets received : 1598
@@ -936,6 +1107,9 @@ NTP packets dropped : 8
Command packets received : 19
Command packets dropped : 0
Client log records dropped : 0
NTS-KE connections accepted: 3
NTS-KE connections dropped : 0
Authenticated NTP packets : 189
----
[[allow]]*allow* [*all*] [_subnet_]::
@@ -1121,7 +1295,7 @@ more than 1 second away from the system clock):
error).
. Save the RTC parameters to the RTC file (specified with the
<<chrony.conf.adoc#rtcfile,*rtcfile*>> directive in the configuration file).
::
{blank}::
+
The last step is done as a precaution against the computer suffering a power
failure before either the daemon exits or the <<writertc,*writertc*>> command
@@ -1177,12 +1351,12 @@ also re-reads the server NTS keys if
<<chrony.conf.adoc#ntsrotate,automatic rotation>> is disabled in the
configuration file.
[[reset]]*reset*::
The *reset* command causes *chronyd* to drop all measurements and switch to the
unsynchronised state. This command can help *chronyd* with recovery when the
measurements are known to be no longer valid or accurate, e.g. due to moving
the computer to a different network, or resuming the computer from a low-power
state (which resets the system clock).
[[reset]]*reset* *sources*::
The *reset sources* command causes *chronyd* to drop all measurements and
switch to the unsynchronised state. This command can help *chronyd* with
recovery when the measurements are known to be no longer valid or accurate,
e.g. due to moving the computer to a different network, or resuming the
computer from a low-power state (which resets the system clock).
[[shutdown]]*shutdown*::
The *shutdown* command causes *chronyd* to exit. This is equivalent to sending

View File

@@ -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
default value is 0.
*-p*::
When run in this mode, *chronyd* will print the configuration and exit. It will
not detach from the terminal. This option can be used to verify the syntax of
the configuration and get the whole configuration, even if it is split into
multiple files and read by the *include* or *confdir* directive.
*-q*::
When run in this mode, *chronyd* will set the system clock once and exit. It
will not detach from the terminal.

View File

@@ -421,11 +421,17 @@ Perhaps you have a firewall set up in a way that blocks packets on port
=== I keep getting the error `501 Not authorised`
Since version 2.2, the `password` command doesn't do anything and `chronyc`
needs to run locally under the root or _chrony_ user, which are allowed to
access the ``chronyd``'s Unix domain command socket.
This error indicates that `chronyc` sent the command to `chronyd` using a UDP
socket instead of the Unix domain socket (e.g. _/var/run/chrony/chronyd.sock_),
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 doesn't exist. `chronyd` will not create
the socket if the directory has a wrong owner or permissions. In this case
there should be an error message from `chronyd` in the system log.
With versions older than 2.2, which don't use the Unix domain socket, you need
to authenticate with the `password` command first,
or use the `-a` option to authenticate automatically on start. The
configuration file needs to specify a file which contains keys (`keyfile`
directive) and which key in the key file should be used for `chronyc`

View File

@@ -25,9 +25,18 @@ rtcsync
# Serve time even if not synchronized to a time source.
#local stratum 10
# Require authentication (nts or key option) for all NTP sources.
#authselectmode require
# Specify file containing keys for NTP authentication.
#keyfile /etc/chrony.keys
# 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.
#leapsectz right/UTC

View File

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

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
chronyc=/usr/bin/chronyc
# For NetworkManager consider only up/down events
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
chronyc onoffline > /dev/null 2>&1
$chronyc onoffline > /dev/null 2>&1
exit 0

23
hash.h
View File

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

View File

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

View File

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

View File

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

View File

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

121
keys.c
View File

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

9
keys.h
View File

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

12
local.c
View File

@@ -185,13 +185,11 @@ LCL_Initialise(void)
void
LCL_Finalise(void)
{
while (change_list.next != &change_list)
LCL_RemoveParameterChangeHandler(change_list.next->handler,
change_list.next->anything);
while (dispersion_notify_list.next != &dispersion_notify_list)
LCL_RemoveDispersionNotifyHandler(dispersion_notify_list.next->handler,
dispersion_notify_list.next->anything);
/* Make sure all handlers have been removed */
if (change_list.next != &change_list)
assert(0);
if (dispersion_notify_list.next != &dispersion_notify_list)
assert(0);
}
/* ================================================== */

View File

@@ -33,6 +33,7 @@
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "util.h"
/* This is used by DEBUG_LOG macro */
@@ -61,12 +62,16 @@ static int n_filelogs = 0;
static struct LogFile logfiles[MAX_FILELOGS];
/* Global prefix for debug messages */
static char *debug_prefix;
/* ================================================== */
/* Init function */
void
LOG_Initialise(void)
{
debug_prefix = Strdup("");
initialised = 1;
LOG_OpenFileLog(NULL);
}
@@ -85,6 +90,8 @@ LOG_Finalise(void)
LOG_CycleLogFiles();
Free(debug_prefix);
initialised = 0;
}
@@ -132,6 +139,8 @@ void LOG_Message(LOG_Severity severity,
time_t t;
struct tm *tm;
assert(initialised);
if (!system_log && file_log && severity >= log_min_severity) {
/* Don't clutter up syslog with timestamps and internal debugging info */
time(&t);
@@ -142,7 +151,7 @@ void LOG_Message(LOG_Severity severity,
}
#if DEBUG > 0
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
}
@@ -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
LOG_SetParentFd(int fd)
{
@@ -243,7 +269,10 @@ LOG_CloseParentFd()
LOG_FileID
LOG_FileOpen(const char *name, const char *banner)
{
assert(n_filelogs < MAX_FILELOGS);
if (n_filelogs >= MAX_FILELOGS) {
assert(0);
return -1;
}
logfiles[n_filelogs].name = name;
logfiles[n_filelogs].banner = banner;
@@ -267,7 +296,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
if (!logfiles[id].file) {
char *logdir = CNF_GetLogDir();
if (logdir[0] == '\0') {
if (!logdir) {
LOG(LOGS_WARN, "logdir not specified");
logfiles[id].name = NULL;
return;

View File

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

47
main.c
View File

@@ -38,7 +38,6 @@
#include "ntp_signd.h"
#include "ntp_sources.h"
#include "ntp_core.h"
#include "nts_ke_client.h"
#include "nts_ke_server.h"
#include "nts_ntp_server.h"
#include "socket.h"
@@ -91,7 +90,7 @@ delete_pidfile(void)
{
const char *pidfile = CNF_GetPidFile();
if (!pidfile[0])
if (!pidfile)
return;
if (!UTI_RemoveFile(NULL, pidfile, NULL))
@@ -105,9 +104,7 @@ MAI_CleanupAndExit(void)
{
if (!initialised) exit(exit_status);
if (CNF_GetDumpDir()[0] != '\0') {
SRC_DumpSources();
}
SRC_DumpSources();
/* Don't update clock when removing sources */
REF_SetMode(REF_ModeIgnore);
@@ -116,7 +113,6 @@ MAI_CleanupAndExit(void)
TMC_Finalise();
MNL_Finalise();
CLG_Finalise();
NKC_Finalise();
NKS_Finalise();
NNS_Finalise();
NSD_Finalise();
@@ -125,13 +121,15 @@ MAI_CleanupAndExit(void)
NCR_Finalise();
NIO_Finalise();
CAM_Finalise();
SCK_Finalise();
KEY_Finalise();
RCL_Finalise();
SRC_Finalise();
REF_Finalise();
RTC_Finalise();
SYS_Finalise();
SCK_Finalise();
SCH_Finalise();
LCL_Finalise();
PRV_Finalise();
@@ -150,7 +148,6 @@ MAI_CleanupAndExit(void)
static void
signal_cleanup(int x)
{
if (!initialised) exit(0);
SCH_QuitProgram();
}
@@ -261,7 +258,7 @@ check_pidfile(void)
FILE *in;
int pid, count;
if (!pidfile[0])
if (!pidfile)
return;
in = UTI_OpenFile(NULL, pidfile, NULL, 'r', 0);
@@ -289,7 +286,7 @@ write_pidfile(void)
const char *pidfile = CNF_GetPidFile();
FILE *out;
if (!pidfile[0])
if (!pidfile)
return;
out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644);
@@ -377,7 +374,7 @@ go_daemon(void)
static void
print_help(const char *progname)
{
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
printf("Usage: %s [-4|-6] [-n|-d] [-p|-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
progname);
}
@@ -414,7 +411,7 @@ int main
int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = -1;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
int clock_control = 1, system_log = 1, log_severity = LOGS_INFO;
int config_args = 0;
int config_args = 0, print_config = 0;
do_platform_checks();
@@ -434,7 +431,7 @@ int main
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "46df:F:hl:L:mnP:qQrRst:u:vx")) != -1) {
while ((opt = getopt(argc, argv, "46df:F:hl:L:mnpP:qQrRst:u:vx")) != -1) {
switch (opt) {
case '4':
case '6':
@@ -463,6 +460,12 @@ int main
case 'n':
nofork = 1;
break;
case 'p':
print_config = 1;
client_only = 1;
nofork = 1;
system_log = 0;
break;
case 'P':
sched_priority = parse_int_arg(optarg);
break;
@@ -527,6 +530,8 @@ int main
DNS_SetAddressFamily(address_family);
CNF_Initialise(restarted, client_only);
if (print_config)
CNF_EnablePrint();
/* Parse the config file or the remaining command line arguments */
config_args = argc - optind;
@@ -537,6 +542,9 @@ int main
CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
}
if (print_config)
return 0;
/* Check whether another chronyd may already be running */
check_pidfile();
@@ -556,16 +564,20 @@ int main
PRV_Initialise();
LCL_Initialise();
SCH_Initialise();
SCK_Initialise(address_family);
/* Start helper processes if needed */
NKS_PreInitialise(pw->pw_uid, pw->pw_gid, scfilter_level);
SYS_Initialise(clock_control);
RTC_Initialise(do_init_rtc);
SRC_Initialise();
RCL_Initialise();
KEY_Initialise();
SCK_Initialise();
/* Open privileged ports before dropping root */
CAM_Initialise(address_family);
NIO_Initialise(address_family);
CAM_Initialise();
NIO_Initialise();
NCR_Initialise();
CNF_SetupAccessRestrictions();
@@ -590,8 +602,7 @@ int main
NSR_Initialise();
NSD_Initialise();
NNS_Initialise();
NKS_Initialise(scfilter_level);
NKC_Initialise();
NKS_Initialise();
CLG_Initialise();
MNL_Initialise();
TMC_Initialise();

View File

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

View File

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

View File

@@ -55,20 +55,29 @@ generate_symmetric_auth(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *inf
{
int auth_len, max_auth_len;
if (info->length + NTP_MIN_MAC_LENGTH > sizeof (*packet)) {
DEBUG_LOG("Packet too long");
return 0;
}
/* Truncate long MACs in NTPv4 packets to allow deterministic parsing
of extension fields (RFC 7822) */
max_auth_len = (info->version == 4 ? NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH) - 4;
max_auth_len = MIN(max_auth_len, sizeof (NTP_Packet) - info->length - 4);
max_auth_len = MIN(max_auth_len, sizeof (*packet) - info->length - 4);
auth_len = KEY_GenerateAuth(key_id, (unsigned char *)packet, info->length,
auth_len = KEY_GenerateAuth(key_id, packet, info->length,
(unsigned char *)packet + info->length + 4, max_auth_len);
if (!auth_len) {
if (auth_len < NTP_MIN_MAC_LENGTH - 4) {
DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id);
return 0;
}
*(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id);
info->length += 4 + auth_len;
info->auth.mac.start = info->length;
info->auth.mac.length = 4 + auth_len;
info->auth.mac.key_id = key_id;
info->length += info->auth.mac.length;
return 1;
}
@@ -86,7 +95,7 @@ check_symmetric_auth(NTP_Packet *packet, NTP_PacketInfo *info)
trunc_len = info->version == 4 && info->auth.mac.length <= NTP_MAX_V4_MAC_LENGTH ?
NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH;
if (!KEY_CheckAuth(info->auth.mac.key_id, (void *)packet, info->auth.mac.start,
if (!KEY_CheckAuth(info->auth.mac.key_id, packet, info->auth.mac.start,
(unsigned char *)packet + info->auth.mac.start + 4,
info->auth.mac.length - 4, trunc_len - 4))
return 0;
@@ -96,31 +105,13 @@ check_symmetric_auth(NTP_Packet *packet, NTP_PacketInfo *info)
/* ================================================== */
static void
adjust_timestamp(NTP_AuthMode mode, uint32_t key_id, struct timespec *ts)
{
switch (mode) {
case NTP_AUTH_SYMMETRIC:
ts->tv_nsec += KEY_GetAuthDelay(key_id);
UTI_NormaliseTimespec(ts);
break;
case NTP_AUTH_MSSNTP:
ts->tv_nsec += NSD_GetAuthDelay(key_id);
UTI_NormaliseTimespec(ts);
default:
break;
}
}
/* ================================================== */
static int
is_zero_data(unsigned char *data, int length)
{
int i;
for (i = 0; i < length; i++)
if (data[i])
if (data[i] != 0)
return 0;
return 1;
}
@@ -230,14 +221,6 @@ NAU_PrepareRequestAuth(NAU_Instance instance)
/* ================================================== */
void
NAU_AdjustRequestTimestamp(NAU_Instance instance, struct timespec *ts)
{
adjust_timestamp(instance->mode, instance->key_id, ts);
}
/* ================================================== */
int
NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketInfo *info)
{
@@ -256,6 +239,8 @@ NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketIn
assert(0);
}
info->auth.mode = instance->mode;
return 1;
}
@@ -277,6 +262,8 @@ NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info)
if (remainder <= 0)
return 1;
assert(remainder % 4 == 0);
/* In NTPv3 and older packets don't have extension fields. Anything after
the header is assumed to be a MAC. */
if (info->version <= 3) {
@@ -287,7 +274,7 @@ NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info)
/* Check if it is an MS-SNTP authenticator field or extended authenticator
field with zeroes as digest */
if (info->version == 3 && info->auth.mac.key_id) {
if (info->version == 3 && info->auth.mac.key_id != 0) {
if (remainder == 20 && is_zero_data(data + parsed + 4, remainder - 4))
info->auth.mode = NTP_AUTH_MSSNTP;
else if (remainder == 72 && is_zero_data(data + parsed + 8, remainder - 8))
@@ -321,7 +308,7 @@ NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info)
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))
data + parsed + 4, remainder - 4, NTP_MAX_MAC_LENGTH - 4))
break;
/* Check if this is a valid NTPv4 extension field and skip it */
@@ -331,7 +318,7 @@ NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info)
return 0;
}
assert(ef_length > 0);
assert(ef_length > 0 && ef_length % 4 == 0);
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
@@ -384,6 +371,9 @@ NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
case NTP_AUTH_MSSNTP:
/* MS-SNTP requests are not authenticated */
break;
case NTP_AUTH_MSSNTP_EXT:
/* Not supported yet */
return 0;
case NTP_AUTH_NTS:
if (!NNS_CheckRequestAuth(request, info, kod))
return 0;
@@ -397,14 +387,6 @@ NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
/* ================================================== */
void
NAU_AdjustResponseTimestamp(NTP_Packet *request, NTP_PacketInfo *info, struct timespec *ts)
{
adjust_timestamp(info->auth.mode, info->auth.mac.key_id, ts);
}
/* ================================================== */
int
NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
NTP_Packet *response, NTP_PacketInfo *response_info,
@@ -434,6 +416,8 @@ NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
return 0;
}
response_info->auth.mode = request_info->auth.mode;
return 1;
}
@@ -498,3 +482,28 @@ NAU_DumpData(NAU_Instance instance)
break;
}
}
/* ================================================== */
void
NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report)
{
memset(report, 0, sizeof (*report));
report->mode = instance->mode;
report->last_ke_ago = -1;
switch (instance->mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
report->key_id = instance->key_id;
KEY_GetKeyInfo(instance->key_id, &report->key_type, &report->key_length);
break;
case NTP_AUTH_NTS:
NNC_GetReport(instance->nts, report);
break;
default:
assert(0);
}
}

View File

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

View File

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

View File

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

View File

@@ -83,19 +83,20 @@ static void read_from_socket(int sock_fd, int event, void *anything);
static int
open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr)
{
int sock_fd, sock_flags, events = SCH_FILE_INPUT;
int sock_fd, sock_flags, dscp, events = SCH_FILE_INPUT;
IPSockAddr local_addr;
char *iface;
if (!SCK_IsFamilySupported(family))
if (!SCK_IsIpFamilyEnabled(family))
return INVALID_SOCK_FD;
if (!client_only)
if (!client_only) {
CNF_GetBindAddress(family, &local_addr.ip_addr);
else
iface = CNF_GetBindNtpInterface();
} else {
CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr);
if (local_addr.ip_addr.family != family)
SCK_GetAnyLocalIPAddress(family, &local_addr.ip_addr);
iface = CNF_GetBindAcquisitionInterface();
}
local_addr.port = local_port;
@@ -103,13 +104,21 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
if (!client_only)
sock_flags |= SCK_FLAG_BROADCAST;
sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, sock_flags);
sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, iface, sock_flags);
if (sock_fd < 0) {
if (!client_only)
LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr));
return INVALID_SOCK_FD;
}
dscp = CNF_GetNtpDscp();
if (dscp > 0 && dscp < 64) {
#ifdef IP_TOS
if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
;
#endif
}
if (!client_only && family == IPADDR_INET4 && local_addr.port > 0)
bound_server_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
@@ -152,7 +161,7 @@ close_socket(int sock_fd)
/* ================================================== */
void
NIO_Initialise(int family)
NIO_Initialise(void)
{
int server_port, client_port;
@@ -191,25 +200,18 @@ NIO_Initialise(int family)
server_sock_ref4 = 0;
server_sock_ref6 = 0;
if (family == IPADDR_UNSPEC || family == IPADDR_INET4) {
if (permanent_server_sockets && server_port)
server_sock_fd4 = open_socket(IPADDR_INET4, server_port, 0, NULL);
if (!separate_client_sockets) {
if (client_port != server_port || !server_port)
client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL);
else
client_sock_fd4 = server_sock_fd4;
}
if (permanent_server_sockets && server_port) {
server_sock_fd4 = open_socket(IPADDR_INET4, server_port, 0, NULL);
server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL);
}
if (family == IPADDR_UNSPEC || family == IPADDR_INET6) {
if (permanent_server_sockets && server_port)
server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL);
if (!separate_client_sockets) {
if (client_port != server_port || !server_port)
client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL);
else
client_sock_fd6 = server_sock_fd6;
if (!separate_client_sockets) {
if (client_port != server_port || !server_port) {
client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL);
client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL);
} else {
client_sock_fd4 = server_sock_fd4;
client_sock_fd6 = server_sock_fd6;
}
}
@@ -455,9 +457,13 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
message.remote_addr.ip.port = remote_addr->port;
}
message.if_index = local_addr->if_index;
message.local_addr.ip = local_addr->ip_addr;
/* Don't require responses to non-link-local addresses to use the same
interface */
message.if_index = SCK_IsLinkLocalIPAddress(&message.remote_addr.ip.ip_addr) ?
local_addr->if_index : INVALID_IF_INDEX;
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
if (message.local_addr.ip.family == IPADDR_INET4 &&

View File

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

View File

@@ -125,7 +125,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
return 1;
}
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return 0;
@@ -285,7 +285,7 @@ update_interface_speed(struct Interface *iface)
struct ifreq req;
int sock_fd, link_speed;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return;
@@ -320,7 +320,7 @@ check_timestamping_option(int option)
{
int sock_fd;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return 0;
@@ -341,7 +341,7 @@ open_dummy_socket(void)
{
int sock_fd, events = 0;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return INVALID_SOCK_FD;
@@ -757,7 +757,7 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
l2_length = message->length;
message->length = extract_udp_data(message->data, &message->remote_addr.ip, message->length);
DEBUG_LOG("Extracted message for %s fd=%d len=%u",
DEBUG_LOG("Extracted message for %s fd=%d len=%d",
UTI_IPSockAddrToString(&message->remote_addr.ip),
local_addr->sock_fd, message->length);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -304,6 +304,8 @@ REF_Finalise(void)
update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew);
}
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
Free(fb_drifts);
initialised = 0;
@@ -489,8 +491,7 @@ maybe_log_offset(double offset, time_t now)
abs_offset = fabs(offset);
if (abs_offset > log_change_threshold) {
LOG(LOGS_WARN, "System clock wrong by %.6f seconds, adjustment started",
-offset);
LOG(LOGS_WARN, "System clock wrong by %.6f seconds", -offset);
}
if (do_mail_change &&
@@ -556,8 +557,7 @@ is_offset_ok(double offset)
return 1;
}
offset = fabs(offset);
if (offset > max_offset) {
if (fabs(offset) > max_offset) {
LOG(LOGS_WARN,
"Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ",
-offset, max_offset, !max_offset_ignore ? "exiting" : "ignored");
@@ -1018,7 +1018,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
our_residual_freq = residual_frequency;
our_root_delay = root_delay;
our_root_dispersion = root_dispersion;
our_frequency_sd = offset_sd;
our_frequency_sd = frequency_sd;
our_offset_sd = offset_sd;
last_ref_update = mono_now;
last_ref_update_interval = update_interval;

View File

@@ -37,7 +37,6 @@ typedef struct {
int poll;
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;
int sel_options;
int reachability;
unsigned long latest_meas_ago; /* seconds */
@@ -78,8 +77,8 @@ typedef struct {
typedef struct {
struct timespec ref_time;
unsigned short n_samples;
unsigned short n_runs;
unsigned long n_samples;
unsigned long n_runs;
unsigned long span_seconds;
double rtc_seconds_fast;
double rtc_gain_rate_ppm;
@@ -88,22 +87,29 @@ typedef struct {
typedef struct {
IPAddr ip_addr;
uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits;
uint16_t ntp_drops;
uint16_t nke_drops;
uint16_t cmd_drops;
int8_t ntp_interval;
int8_t nke_interval;
int8_t cmd_interval;
int8_t ntp_timeout_interval;
uint32_t last_ntp_hit_ago;
uint32_t last_nke_hit_ago;
uint32_t last_cmd_hit_ago;
} RPT_ClientAccessByIndex_Report;
typedef struct {
uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits;
uint32_t ntp_drops;
uint32_t nke_drops;
uint32_t cmd_drops;
uint32_t log_drops;
uint32_t ntp_auth_hits;
} RPT_ServerStatsReport;
typedef struct {
@@ -160,4 +166,29 @@ typedef struct {
uint32_t total_valid_count;
} RPT_NTPReport;
typedef struct {
NTP_AuthMode mode;
uint32_t key_id;
int key_type;
int key_length;
int ke_attempts;
uint32_t last_ke_ago;
int cookies;
int cookie_length;
int nak;
} RPT_AuthReport;
typedef struct {
uint32_t ref_id;
IPAddr ip_addr;
char state_char;
int authentication;
int conf_options;
int eff_options;
uint32_t last_sample_ago;
double score;
double lo_limit;
double hi_limit;
} RPT_SelectReport;
#endif /* GOT_REPORTS_H */

View File

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

14
sched.c
View File

@@ -490,20 +490,6 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
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
keep going until all are done. (The earlier ones may take so

View File

@@ -85,9 +85,6 @@ extern SCH_TimeoutID SCH_AddTimeoutInClass(double min_delay, double separation,
/* The next one probably ought to return a status code */
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_QuitProgram(void);

250
siv_gnutls.c Normal file
View File

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

@@ -69,6 +69,8 @@ SIV_DestroyInstance(SIV_Instance instance)
int
SIV_GetKeyLength(SIV_Algorithm algorithm)
{
assert(32 <= SIV_MAX_KEY_LENGTH);
if (algorithm == AEAD_AES_SIV_CMAC_256)
return 32;
return 0;
@@ -92,6 +94,8 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
int
SIV_GetTagLength(SIV_Instance instance)
{
assert(SIV_DIGEST_SIZE <= SIV_MAX_TAG_LENGTH);
return SIV_DIGEST_SIZE;
}

View File

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

128
socket.c
View File

@@ -82,6 +82,10 @@ struct MessageHeader {
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() */
static int supported_socket_flags;
@@ -114,7 +118,7 @@ prepare_buffers(unsigned int n)
hdr->msg_hdr.msg_namelen = sizeof (msg->name);
hdr->msg_hdr.msg_iov = &msg->iov;
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_flags = 0;
hdr->msg_len = 0;
@@ -172,7 +176,7 @@ check_socket_flag(int sock_flag, int fd_flag, int fs_flag)
static int
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));
return 0;
}
@@ -332,6 +336,23 @@ is_any_address(IPAddr *addr)
/* ================================================== */
static int
bind_device(int sock_fd, const char *iface)
{
#ifdef SO_BINDTODEVICE
if (setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, iface, strlen(iface) + 1) < 0) {
DEBUG_LOG("Could not bind socket to %s : %s", iface, strerror(errno));
return 0;
}
return 1;
#else
DEBUG_LOG("Could not bind socket to %s : %s", iface, "Not supported");
return 0;
#endif
}
/* ================================================== */
static int
bind_ip_address(int sock_fd, IPSockAddr *addr, int flags)
{
@@ -399,7 +420,8 @@ connect_ip_address(int sock_fd, IPSockAddr *addr)
/* ================================================== */
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;
@@ -412,10 +434,14 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int type, int fl
switch (family) {
case IPADDR_INET4:
if (!ip4_enabled)
return INVALID_SOCK_FD;
domain = AF_INET;
break;
#ifdef FEAT_IPV6
case IPADDR_INET6:
if (!ip6_enabled)
return INVALID_SOCK_FD;
domain = AF_INET6;
break;
#endif
@@ -434,6 +460,9 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int type, int fl
if (!set_ip_options(sock_fd, family, flags))
goto error;
if (iface && !bind_device(sock_fd, iface))
goto error;
/* Bind the socket if a non-any local address/port was specified */
if (local_addr && local_addr->ip_addr.family != IPADDR_UNSPEC &&
(local_addr->port != 0 || !is_any_address(&local_addr->ip_addr)) &&
@@ -476,8 +505,8 @@ bind_unix_address(int sock_fd, const char *addr, int flags)
}
saddr.un.sun_family = AF_UNIX;
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 */
if (bind(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) {
@@ -627,9 +656,8 @@ log_message(int sock_fd, int direction, SCK_Message *message, const char *prefix
case SCK_ADDR_IP:
if (message->remote_addr.ip.ip_addr.family != IPADDR_UNSPEC)
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);
}
break;
case SCK_ADDR_UNIX:
remote_addr = message->remote_addr.path;
@@ -655,7 +683,7 @@ log_message(int sock_fd, int direction, SCK_Message *message, const char *prefix
snprintf(tslen, sizeof (tslen), " tslen=%d", message->timestamp.l2_length);
}
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,
remote_addr ? (direction > 0 ? " from " : " to ") : "",
remote_addr ? remote_addr : "",
@@ -710,7 +738,7 @@ init_message_nonaddress(SCK_Message *message)
/* ================================================== */
static int
process_header(struct msghdr *msg, unsigned int msg_length, int sock_fd, int flags,
process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
SCK_Message *message)
{
struct cmsghdr *cmsg;
@@ -892,7 +920,10 @@ receive_messages(int sock_fd, int flags, int max_messages, int *num_messages)
hdr = ARR_GetElements(recv_headers);
n = ARR_GetSize(recv_headers);
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);
@@ -1001,6 +1032,11 @@ send_message(int sock_fd, SCK_Message *message, int flags)
msg.msg_namelen = 0;
}
if (message->length < 0) {
DEBUG_LOG("Invalid length %d", message->length);
return 0;
}
iov.iov_base = message->data;
iov.iov_len = message->length;
msg.msg_iov = &iov;
@@ -1094,8 +1130,15 @@ send_message(int sock_fd, SCK_Message *message, int flags)
/* ================================================== */
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));
ARR_SetSize(recv_messages, MAX_RECV_MESSAGES);
recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader));
@@ -1135,15 +1178,13 @@ SCK_Finalise(void)
/* ================================================== */
int
SCK_IsFamilySupported(int family)
SCK_IsIpFamilyEnabled(int family)
{
switch (family) {
case IPADDR_INET4:
return 1;
return ip4_enabled;
case IPADDR_INET6:
#ifdef FEAT_IPV6
return 1;
#endif
return ip6_enabled;
default:
return 0;
}
@@ -1194,6 +1235,23 @@ SCK_GetLoopbackIPAddress(int family, IPAddr *local_addr)
/* ================================================== */
int
SCK_IsLinkLocalIPAddress(IPAddr *addr)
{
switch (addr->family) {
case IPADDR_INET4:
/* 169.254.0.0/16 */
return (addr->addr.in4 & 0xffff0000) == 0xa9fe0000;
case IPADDR_INET6:
/* fe80::/10 */
return addr->addr.in6[0] == 0xfe && (addr->addr.in6[1] & 0xc0) == 0x80;
default:
return 0;
}
}
/* ================================================== */
void
SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
socklen_t address_len))
@@ -1204,17 +1262,17 @@ SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
/* ================================================== */
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
SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags)
SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface, int flags)
{
return open_ip_socket(remote_addr, local_addr, SOCK_STREAM, flags);
return open_ip_socket(remote_addr, local_addr, iface, SOCK_STREAM, flags);
}
/* ================================================== */
@@ -1353,10 +1411,15 @@ SCK_ShutdownConnection(int sock_fd)
/* ================================================== */
int
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;
if (length < 0) {
DEBUG_LOG("Invalid length %d", length);
return -1;
}
r = recv(sock_fd, buffer, length, get_recv_flags(flags));
if (r < 0) {
@@ -1372,16 +1435,21 @@ SCK_Receive(int sock_fd, void *buffer, unsigned int length, int flags)
/* ================================================== */
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;
assert(flags == 0);
if (length < 0) {
DEBUG_LOG("Invalid length %d", length);
return -1;
}
r = send(sock_fd, buffer, length, 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;
}
@@ -1444,8 +1512,10 @@ SCK_RemoveSocket(int sock_fd)
saddr.sa.sa_family != AF_UNIX)
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 1;
}
@@ -1468,7 +1538,7 @@ SCK_SockaddrToIPSockAddr(struct sockaddr *sa, int sa_length, IPSockAddr *ip_sa)
switch (sa->sa_family) {
case AF_INET:
if (sa_length < sizeof (struct sockaddr_in))
if (sa_length < (int)sizeof (struct sockaddr_in))
return;
ip_sa->ip_addr.family = IPADDR_INET4;
ip_sa->ip_addr.addr.in4 = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
@@ -1476,7 +1546,7 @@ SCK_SockaddrToIPSockAddr(struct sockaddr *sa, int sa_length, IPSockAddr *ip_sa)
break;
#ifdef FEAT_IPV6
case AF_INET6:
if (sa_length < sizeof (struct sockaddr_in6))
if (sa_length < (int)sizeof (struct sockaddr_in6))
return;
ip_sa->ip_addr.family = IPADDR_INET6;
memcpy(&ip_sa->ip_addr.addr.in6, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr,
@@ -1496,7 +1566,7 @@ SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length)
{
switch (ip_sa->ip_addr.family) {
case IPADDR_INET4:
if (sa_length < sizeof (struct sockaddr_in))
if (sa_length < (int)sizeof (struct sockaddr_in))
return 0;
memset(sa, 0, sizeof (struct sockaddr_in));
sa->sa_family = AF_INET;
@@ -1508,7 +1578,7 @@ SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length)
return sizeof (struct sockaddr_in);
#ifdef FEAT_IPV6
case IPADDR_INET6:
if (sa_length < sizeof (struct sockaddr_in6))
if (sa_length < (int)sizeof (struct sockaddr_in6))
return 0;
memset(sa, 0, sizeof (struct sockaddr_in6));
sa->sa_family = AF_INET6;
@@ -1521,7 +1591,7 @@ SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length)
return sizeof (struct sockaddr_in6);
#endif
default:
if (sa_length < sizeof (struct sockaddr))
if (sa_length < (int)sizeof (struct sockaddr))
return 0;
memset(sa, 0, sizeof (struct sockaddr));
sa->sa_family = AF_UNSPEC;

View File

@@ -49,7 +49,7 @@ typedef enum {
typedef struct {
void *data;
unsigned int length;
int length;
SCK_AddressType addr_type;
int if_index;
@@ -73,27 +73,33 @@ typedef struct {
int descriptor;
} SCK_Message;
/* Initialisation function */
extern void SCK_Initialise(void);
/* Initialisation function (the specified IP family is enabled,
or all if IPADDR_UNSPEC) */
extern void SCK_Initialise(int family);
/* Finalisation function */
extern void SCK_Finalise(void);
/* Check if support for the IP family was enabled in the build */
extern int SCK_IsFamilySupported(int family);
/* Check if support for the IP family is enabled */
extern int SCK_IsIpFamilyEnabled(int family);
/* 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_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
running in a restricted process (e.g. after dropping root privileges) */
extern void SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
socklen_t address_len));
/* Open socket */
extern int SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags);
extern int SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags);
/* Open a socket (addresses and iface may be NULL) */
extern int SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr,
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,
int flags);
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);
/* 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_Send(int sock_fd, const 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, int length, int flags);
/* Receive a single message or multiple messages. The functions return
a pointer to static buffers, or NULL on error. The buffers are valid until

318
sources.c
View File

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

View File

@@ -51,6 +51,14 @@ extern void SRC_Initialise(void);
/* Finalisation function */
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 {
SRC_NTP, /* NTP client/peer */
SRC_REFCLOCK /* Rerefence clock */
@@ -59,9 +67,9 @@ typedef enum {
/* Function to create a new instance. This would be called by one of
the individual source-type instance creation routines. */
extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options,
IPAddr *addr, int min_samples, int max_samples,
double min_delay, double asymmetry);
extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated,
int sel_options, IPAddr *addr, int min_samples,
int max_samples, double min_delay, double asymmetry);
/* Function to get rid of a source when it is being unconfigured.
This may cause the current reference source to be reselected, if this
@@ -122,9 +130,10 @@ extern int SRC_IsSyncPeer(SRC_Instance inst);
extern int SRC_IsReachable(SRC_Instance inst);
extern int SRC_ReadNumberOfSources(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_GetSelectReport(int index, RPT_SelectReport *report);
extern SRC_Type SRC_GetType(int index);

View File

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

54
stubs.c
View File

@@ -112,7 +112,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
#ifndef FEAT_CMDMON
void
CAM_Initialise(int family)
CAM_Initialise(void)
{
}
@@ -147,7 +147,7 @@ MNL_Finalise(void)
#ifndef FEAT_NTP
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
NIO_Initialise(int family)
NIO_Initialise(void)
{
}
@@ -194,23 +194,30 @@ NSR_Finalise(void)
}
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;
}
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;
}
NSR_Status
NSR_RemoveSource(NTP_Remote_Address *remote_addr)
NSR_RemoveSource(IPAddr *address)
{
return NSR_NoSuchSource;
}
void
NSR_RemoveSourcesById(uint32_t conf_id)
{
}
void
NSR_RemoveAllSources(void)
{
@@ -319,6 +326,12 @@ NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
memset(report, 0, sizeof (*report));
}
int
NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report)
{
return 0;
}
int
NSR_GetNTPReport(RPT_NTPReport *report)
{
@@ -414,12 +427,6 @@ NSD_Finalise(void)
{
}
int
NSD_GetAuthDelay(uint32_t key_id)
{
return 0;
}
int
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
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
unsigned int
CMC_GetKeyLength(const char *cipher)
int
CMC_GetKeyLength(CMC_Algorithm algorithm)
{
return 0;
}
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;
}
unsigned int
CMC_Hash(CMC_Instance inst, const unsigned char *in, unsigned int in_len,
unsigned char *out, unsigned int out_len)
int
CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len)
{
return 0;
}
@@ -504,14 +510,16 @@ NNC_PrepareForAuth(NNC_Instance inst)
int
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;
}
int
NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info)
{
DEBUG_LOG("NTS support disabled");
return 0;
}
@@ -526,17 +534,17 @@ NNC_DumpData(NNC_Instance inst)
}
void
NKC_Initialise(void)
NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report)
{
}
void
NKC_Finalise(void)
NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level)
{
}
void
NKS_Initialise(int scfilter_level)
NKS_Initialise(void)
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

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

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

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

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -137,8 +137,8 @@ static void
send_response(int interleaved, int authenticated, int allow_update, int valid_ts, int valid_auth)
{
NTP_Packet *req, *res;
uint32_t key_id = 0;
int auth_len = 0;
uint32_t key_id;
req = &req_buffer;
res = &res_buffer;
@@ -322,7 +322,7 @@ test_unit(void)
TST_RegisterDummyDrivers();
SCH_Initialise();
SRC_Initialise();
NIO_Initialise(IPADDR_UNSPEC);
NIO_Initialise();
NCR_Initialise();
REF_Initialise();

View File

@@ -44,7 +44,7 @@ test_unit(void)
LCL_Initialise();
SCH_Initialise();
SRC_Initialise();
NIO_Initialise(IPADDR_UNSPEC);
NIO_Initialise();
NCR_Initialise();
NSR_Initialise();
@@ -66,16 +66,16 @@ test_unit(void)
DEBUG_LOG("adding source %s hash %"PRIu32, UTI_IPToString(&addrs[j].ip_addr),
UTI_IPToHash(&addrs[j].ip_addr) % (1U << i));
NSR_AddSource(&addrs[j], random() % 2 ? NTP_SERVER : NTP_PEER, &params);
NSR_AddSource(&addrs[j], random() % 2 ? NTP_SERVER : NTP_PEER, &params, NULL);
for (k = 0; k < j; k++) {
addr = addrs[k];
find_slot(&addr, &slot, &found);
found = find_slot2(&addr, &slot);
TEST_CHECK(found == 2);
TEST_CHECK(!UTI_CompareIPs(&get_record(slot)->remote_addr->ip_addr,
&addr.ip_addr, NULL));
addr.port++;
find_slot(&addr, &slot, &found);
found = find_slot2(&addr, &slot);
TEST_CHECK(found == 1);
TEST_CHECK(!UTI_CompareIPs(&get_record(slot)->remote_addr->ip_addr,
&addr.ip_addr, NULL));
@@ -84,10 +84,10 @@ test_unit(void)
for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) {
DEBUG_LOG("removing source %s", UTI_IPToString(&addrs[j].ip_addr));
NSR_RemoveSource(&addrs[j]);
NSR_RemoveSource(&addrs[j].ip_addr);
for (k = 0; k < sizeof (addrs) / sizeof (addrs[0]); k++) {
find_slot(&addrs[k], &slot, &found);
found = find_slot2(&addrs[k], &slot);
TEST_CHECK(found == (k <= j ? 0 : 2));
}
}

View File

@@ -35,7 +35,7 @@ prepare_response(NKSN_Instance session, int valid)
if (valid)
index = -1;
else
index = random() % 9;
index = random() % 10;
DEBUG_LOG("index=%d", index);
NKSN_BeginMessage(session);
@@ -89,8 +89,12 @@ prepare_response(NKSN_Instance session, int valid)
}
if (index != 8) {
for (i = 0; i < NKE_MAX_COOKIES; i++)
for (i = 0; i < NKE_MAX_COOKIES; i++) {
length = (random() % sizeof (data) + 1) / 4 * 4;
if (index == 9)
length += (length < sizeof (data) ? 1 : -1) * (random() % 3 + 1);
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COOKIE, data, length));
}
}
TEST_CHECK(NKSN_EndMessage(session));
@@ -112,7 +116,6 @@ test_unit(void)
CNF_ParseLine(NULL, i + 1, conf[i]);
LCL_Initialise();
NKC_Initialise();
SCK_GetLoopbackIPAddress(AF_INET, &addr.ip_addr);
addr.port = 0;
@@ -129,7 +132,6 @@ test_unit(void)
NKC_DestroyInstance(inst);
NKC_Finalise();
LCL_Finalise();
CNF_Finalise();
}

View File

@@ -50,7 +50,7 @@ prepare_request(NKSN_Instance session, int valid)
if (valid)
index = -1;
else
index = random() % 7;
index = random() % 9;
DEBUG_LOG("index=%d", index);
NKSN_BeginMessage(session);
@@ -61,30 +61,34 @@ prepare_request(NKSN_Instance session, int valid)
if (index != 0) {
memset(data, NKE_NEXT_PROTOCOL_NTPV4 + 1, sizeof (data));
data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4);
if (index == 1)
data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4 + random() % 10 + 1);
else
data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4);
if (index == 2)
length = 0;
else if (index == 2)
length = 3 + random() % 15 * 2;
else
length = 2 + random() % 16 * 2;
TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length));
}
if (index != 3) {
if (index == 4)
data[0] = htons(AEAD_AES_SIV_CMAC_256 + random() % 10 + 1);
else
data[0] = htons(AEAD_AES_SIV_CMAC_256);
if (index == 3)
TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length));
if (index != 4) {
data[0] = htons(AEAD_AES_SIV_CMAC_256);
if (index == 5)
length = 0;
else if (index == 6)
length = 3 + random() % 15 * 2;
else
length = 2 + random() % 16 * 2;
TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length));
}
if (index == 6) {
if (index == 7)
TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length));
if (index == 8) {
length = random() % (sizeof (data) + 1);
TEST_CHECK(NKSN_AddRecord(session, 1, 1000 + random() % 1000, data, length));
}
@@ -154,7 +158,8 @@ test_unit(void)
SCH_Initialise();
unlink("ntskeys");
NKS_Initialise(0);
NKS_PreInitialise(0, 0, 0);
NKS_Initialise();
session = NKSN_CreateInstance(1, NULL, handle_message, NULL);

View File

@@ -53,22 +53,34 @@ send_message(NKSN_Instance inst)
NKSN_BeginMessage(inst);
TEST_CHECK(check_message_format(&inst->message, 0));
TEST_CHECK(!check_message_format(&inst->message, 1));
TEST_CHECK(!NKSN_AddRecord(inst, 0, 1, record, NKE_MAX_MESSAGE_LENGTH - 4 + 1));
TEST_CHECK(check_message_format(&inst->message, 0));
TEST_CHECK(!check_message_format(&inst->message, 1));
for (i = 0; i < records; i++) {
TEST_CHECK(NKSN_AddRecord(inst, critical, type_start + i, record, record_length));
TEST_CHECK(!NKSN_AddRecord(inst, 0, 1, &record,
NKE_MAX_MESSAGE_LENGTH - inst->message.length - 4 + 1));
TEST_CHECK(check_message_format(&inst->message, 0));
TEST_CHECK(!check_message_format(&inst->message, 1));
}
TEST_CHECK(NKSN_EndMessage(inst));
TEST_CHECK(check_message_format(&inst->message, 0));
TEST_CHECK(check_message_format(&inst->message, 1));
}
static void
verify_message(NKSN_Instance inst)
{
unsigned char buffer[NKE_MAX_MESSAGE_LENGTH];
int i, c, t, length, buffer_length;
int i, c, t, length, buffer_length, msg_length, prev_parsed;
NKE_Key c2s, s2c;
for (i = 0; i < records; i++) {
@@ -76,6 +88,9 @@ verify_message(NKSN_Instance inst)
buffer_length = random() % (record_length + 1);
assert(buffer_length <= sizeof (buffer));
prev_parsed = inst->message.parsed;
msg_length = inst->message.length;
TEST_CHECK(NKSN_GetRecord(inst, &c, &t, &length, buffer, buffer_length));
TEST_CHECK(c == critical);
TEST_CHECK(t == type_start + i);
@@ -83,6 +98,20 @@ verify_message(NKSN_Instance inst)
TEST_CHECK(memcmp(record, buffer, buffer_length) == 0);
if (buffer_length < record_length)
TEST_CHECK(buffer[buffer_length] == 0);
inst->message.length = inst->message.parsed - 1;
inst->message.parsed = prev_parsed;
TEST_CHECK(!get_record(&inst->message, NULL, NULL, NULL, buffer, buffer_length));
TEST_CHECK(inst->message.parsed == prev_parsed);
inst->message.length = msg_length;
if (msg_length < 0x8000) {
inst->message.data[prev_parsed + 2] ^= 0x80;
TEST_CHECK(!get_record(&inst->message, NULL, NULL, NULL, buffer, buffer_length));
TEST_CHECK(inst->message.parsed == prev_parsed);
inst->message.data[prev_parsed + 2] ^= 0x80;
}
TEST_CHECK(get_record(&inst->message, NULL, NULL, NULL, buffer, buffer_length));
TEST_CHECK(inst->message.parsed > prev_parsed);
}
TEST_CHECK(!NKSN_GetRecord(inst, &critical, &t, &length, buffer, sizeof (buffer)));

View File

@@ -82,6 +82,10 @@ test_unit(void)
TEST_CHECK(r);
TEST_CHECK(info.length - packet_length >= min_ef_length);
r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
-1, &plaintext2_length);
TEST_CHECK(!r);
r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
sizeof (plaintext2), &plaintext2_length);
TEST_CHECK(r);

View File

@@ -27,10 +27,11 @@
#include "ntp.h"
#include "nts_ke_client.h"
#define NKC_CreateInstance(address, name) NULL
#define NKC_DestroyInstance(inst)
#define NKC_CreateInstance(address, name) Malloc(1)
#define NKC_DestroyInstance(inst) Free(inst)
#define NKC_Start(inst) (random() % 2)
#define NKC_IsActive(inst) (random() % 2)
#define NKC_GetRetryFactor(inst) (1)
static int get_nts_data(NKC_Instance inst, NKE_Context *context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
@@ -59,6 +60,8 @@ get_nts_data(NKC_Instance inst, NKE_Context *context,
*num_cookies = random() % max_cookies + 1;
for (i = 0; i < *num_cookies; i++) {
cookies[i].length = random() % (sizeof (cookies[i].cookie) + 1);
if (random() % 4 != 0)
cookies[i].length = cookies[i].length / 4 * 4;
memset(cookies[i].cookie, random(), cookies[i].length);
}
@@ -81,10 +84,14 @@ get_request(NNC_Instance inst)
info.version = 4;
info.mode = MODE_CLIENT;
info.length = random() % (sizeof (packet) + 1);
if (random() % 4 != 0)
info.length = info.length / 4 * 4;
inst->num_cookies = 0;
if (inst->num_cookies > 0 && random() % 2) {
inst->num_cookies = 0;
TEST_CHECK(!NNC_GenerateRequestAuth(inst, &packet, &info));
TEST_CHECK(!NNC_GenerateRequestAuth(inst, &packet, &info));
}
while (!NNC_PrepareForAuth(inst)) {
inst->next_nke_attempt = SCH_GetLastEventMonoTime() + random() % 10 - 7;
@@ -124,9 +131,9 @@ get_request(NNC_Instance inst)
static void
prepare_response(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info, int valid, int nak)
{
unsigned char cookie[508], plaintext[512], nonce[512];
int nonce_length, cookie_length, plaintext_length, min_auth_length;
int index, auth_start;
unsigned char cookie[508], plaintext[528], nonce[448];
int nonce_length, ef_length, cookie_length, plaintext_length, min_auth_length;
int i, index, auth_start;
SIV_Instance siv;
memset(packet, 0, sizeof (*packet));
@@ -139,7 +146,7 @@ prepare_response(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info, in
if (valid)
index = -1;
else
index = random() % (nak ? 2 : 6);
index = random() % (nak ? 2 : 8);
DEBUG_LOG("index=%d nak=%d", index, nak);
@@ -168,16 +175,29 @@ prepare_response(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info, in
DEBUG_LOG("nonce_length=%d cookie_length=%d min_auth_length=%d",
nonce_length, cookie_length, min_auth_length);
UTI_GetRandomBytes(nonce, nonce_length);
UTI_GetRandomBytes(cookie, cookie_length);
if (cookie_length >= 12 && cookie_length <= 32 && random() % 2 == 0)
TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_COOKIE, cookie, cookie_length));
plaintext_length = 0;
if (index != 3)
TEST_CHECK(NEF_SetField(plaintext, sizeof (plaintext), 0, NTP_EF_NTS_COOKIE,
cookie, cookie_length, &plaintext_length));
if (index != 3) {
for (i = random() % ((sizeof (plaintext) - 16) / (cookie_length + 4)); i >= 0; i--) {
TEST_CHECK(NEF_SetField(plaintext, sizeof (plaintext), plaintext_length,
NTP_EF_NTS_COOKIE, cookie,
i == 0 ? cookie_length : random() % (cookie_length + 1) / 4 * 4,
&ef_length));
plaintext_length += ef_length;
}
}
auth_start = info->length;
if (index != 4) {
if (index == 5) {
assert(plaintext_length + 16 <= sizeof (plaintext));
memset(plaintext + plaintext_length, 0, 16);
plaintext_length += 16;
}
siv = SIV_CreateInstance(inst->context.algorithm);
TEST_CHECK(siv);
TEST_CHECK(SIV_SetKey(siv, inst->context.s2c.key, inst->context.s2c.length));
@@ -186,8 +206,10 @@ prepare_response(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info, in
min_auth_length));
SIV_DestroyInstance(siv);
}
if (index == 5)
if (index == 6)
((unsigned char *)packet)[auth_start + 8]++;
if (index == 7)
TEST_CHECK(NEF_AddField(packet, info, 0x7000, inst->uniq_id, sizeof (inst->uniq_id)));
}
void
@@ -200,6 +222,8 @@ test_unit(void)
IPAddr ip_addr;
int i, j, prev_num_cookies, valid;
TEST_CHECK(SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0);
SCK_GetLoopbackIPAddress(AF_INET, &addr.ip_addr);
addr.port = 0;
@@ -233,7 +257,7 @@ test_unit(void)
if (valid) {
TEST_CHECK(NNC_CheckResponseAuth(inst, &packet, &info));
TEST_CHECK(inst->num_cookies == MIN(NTS_MAX_COOKIES, prev_num_cookies + 1));
TEST_CHECK(inst->num_cookies >= MIN(NTS_MAX_COOKIES, prev_num_cookies + 1));
TEST_CHECK(inst->ok_response);
}

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